大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说树形结构穿梭框_html5treetable,希望您对编程的造诣更进一步.
最近遇到个需求,穿梭框中的内容是树形结构的数据。查看elementUI的transfer组件是不支持树形结构的数据,也就不能直接使用了。但是el-tree组件支持啊,那如果让tree组件和transfer组件结合一下岂不完美🤔。
准备
clone
一份element
的源码,找到package/tansfer/src
文件夹,里面的vue文件就是我们的目标。复制一份出来到自己的项目(同时复制mixins
里的emitter.js
和migrating.js
),接下来就对它进行改造。
|-- components
| |-- mixins
| | |-- emitter.js
| | |-- migraing.js
| |-- main.vue
| |-- transfer-panel.vue
使用
使用之前先对main.vue
,transfer-panel.vue
两个文件进行简单的修改。删除locale
相关代码。emitter.js
和migrating.js
因为路径变化需要重新引入。然后在其它组件中引入main.vue
就可以使用啦(和直接使用el-transfer是一样的)。
<i-el-transfer v-model="value" :data="data"></i-el-transfer>
...
import iElTransfer from '../components/main'
export default {
name: 'home',
components: {
iElTransfer
},
data () {
return {
data: [
{ key: 1, label: '备选项1', disabled: false },
{ key: 2, label: '备选项2', disabled: false }
...
]
}
}
...
}
改造
将原来的单层结构转换成树形结构,我们有现成的el-tree
使用,所以将原来显示内容的区域换成el-tree
组件看看。
# transfer-panel.vue
...
<el-checkbox-group
v-model="checked"
v-show="!hasNoMatch && data.length > 0"
:class="{ 'is-filterable': filterable }"
class="el-transfer-panel__list">
<el-checkbox
class="el-transfer-panel__item"
:label="item[keyProp]"
:disabled="item[disabledProp]"
:key="item[keyProp]"
v-for="item in filteredData">
<option-content :option="item"></option-content>
</el-checkbox>
</el-checkbox-group>
...
先不管那么多,直接替换成el-tree
...
<div v-show="data.length > 0"
:class="{ 'is-filterable': filterable }"
class="el-transfer-panel__list">
<el-tree ref="tree" :data="filteredData"
:node-key="keyProp"
show-checkbox
default-expand-all
:default-checked-keys="checked">
</el-tree> </div>
...
将data
变为树形结构,回过来看页面
是我们像要的结果,这时候虽然变成了我们想要的树形结构,但是操作一下就能发现问题。继续看代码。
sourceData() {
return this.data.filter(item => this.value.indexOf(item[this.props.key]) === -1);
},
targetData() {
if (this.targetOrder === 'original') {
return this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1);
} else {
return this.value.reduce((arr, cur) => {
const val = this.dataObj[cur];
if (val) {
arr.push(val);
}
return arr;
}, []);
}
},
sourceData
和targetData
分别用来展示左右两侧的数据,this.value
是使用组件时通过v-model
绑定的数据。这里通过this.value
筛选出左右两边的数据。那我们就在这里做一下修改:
sourceData () {
const data = deepCopy(this.data)
// 有children继续筛选
const filterData = (list) => {
return list.filter(item => {
if (item[this.props.children] && item[this.props.children].length > 0) {
item[this.props.children] = filterData(item.children)
}
return this.value.indexOf(item[this.props.key]) === -1
})
}
return filterData(data)
},
targetData () {
const data = deepCopy(this.data)
const filterData = (list) => {
const res = []
list.forEach(item => {
if (this.value.indexOf(item[this.props.key]) > -1) {
res.push(item)
}
if (item[this.props.children] && item[this.props.children].length > 0) {
const result = filterData(item[this.props.children])
if (result.length > 0) {
item[this.props.children] = result
const find = res.find(i => i.key === item.key)
// res 中没有找到 item,需将 item 加入 res。
// 避免当选中子元素,父元素未选中的时候出现问题
if (find === undefined) {
res.push(item)
}
}
}
})
return res
}
return filterData(data)
},
我这里希望添加到右边的数据和左边的数据保持同样的结构,所以删除了targetOrder
选项。我们试着改变v-model
绑定的值来看下效果。
有那感觉了,但是现在向左向右添加的功能还是不行。下面就修改这部分的功能。找到addToRight
函数,该函数筛选出用户左侧选中要添加到右侧的数据和已经存在右侧的数据,并通过$emit()
修改this.value
的值。
addToRight () {
let currentValue = this.value.slice()
const itemsToBeMoved = []
const findSelectkey = (list) => {
const key = this.props.key
const itemsToBeMoved = []
list.forEach(item => {
const itemKey = item[key]
if (
this.leftChecked.indexOf(itemKey) > -1 &&
this.value.indexOf(itemKey) === -1
) {
itemsToBeMoved.push(itemKey)
}
if (item[this.props.children] && item[this.props.children].length > 0) {
itemsToBeMoved.push(...findSelectkey(item[this.props.children]))
}
})
return itemsToBeMoved
}
itemsToBeMoved.push(...findSelectkey(this.data))
currentValue = currentValue.concat(itemsToBeMoved)
this.$emit('input', currentValue)
this.$emit('change', currentValue, 'right', this.leftChecked)
},
其实思路都是一样的,如果存在children
就继续查找下层数据。接着修改addToLeft
函数。
addToLeft () {
let currentValue = this.value.slice()
const list = this.rightChecked.concat(this.rightFalfChecked)
list.forEach(item => {
const index = currentValue.indexOf(item)
if (index > -1) {
currentValue.splice(index, 1)
}
})
this.$emit('input', currentValue)
this.$emit('change', currentValue, 'left', this.rightChecked)
},
这里我想的是如果元素的子元素没有被全选,那么该元素不是选中状态,所以要从右侧删除该元素的key
。这里新加了一个rightFalfChecked
用来表示右侧半选状态的数据。
上面对main.vue
文件的修改基本就算完了。我们发现还需要获取到rightChecked
、leftChecked
、rightFalfChecke
的值。修改transfer-panel.vue
文件来获取它们。el-tree
组件有个check
事件,通过它我们可以获取当前el-tree
哪些被选中了。
check:当复选框被点击的时候触发;共两个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、树目前的选中状态对象,包含 checkedNodes、checkedKeys、halfCheckedNodes、halfCheckedKeys 四个属性
<el-tree ref="tree" :data="filteredData"
:node-key="keyProp"
show-checkbox
default-expand-all
:default-checked-keys="checked"
@check="handleCheck">
</el-tree>
...
watch: {
checked (val, oldVal) {
this.updateAllChecked()
if (this.checkChangeByUser) {
const movedKeys = val.concat(oldVal)
.filter(v => val.indexOf(v) === -1 || oldVal.indexOf(v) === -1)
this.$emit('checked-change', val, movedKeys, this.halfChecked)
} else {
this.$emit('checked-change', val)
this.checkChangeByUser = true
}
}
}
...
handleCheck (cur, checkedInfo) {
const { checkedKeys, halfCheckedKeys } = checkedInfo
this.checked = checkedKeys
this.halfChecked = halfCheckedKeys
}
总结
到这里基本功能实现都已经完成了,但是还有一些细节问题,比如全选、筛选等,这里就不再描述。相比原来的transfer
组件,这里去掉了targetOrder
,format
属性,其它的则做了保留或修改。希望这篇文章能帮到您,您可以在下面的链接查看完整代码。欢迎交流🎉️。
Github:链接
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/12918.html