一、关键代码
<template>
<div>
<!-- 左侧待选列表 -->
<div class="left-box">
<p>待选列表</p>
<el-input placeholder="输入关键词过滤" v-model="leftFilterText" clearable/>
<el-tree
ref="treeLeft"
:data="leftData"
show-checkbox
node-key="id"
props="defaultProps"
:load="loadNode"
lazy
:filter-node-method="filterNode"
>
</el-tree>
</div>
<!-- 穿梭按钮 -->
<div class="oper-box">
<el-button circle type="primary" icon="el-icon-arrow-left" @click="removeData"></el-button>
<el-button circle type="primary" icon="el-icon-arrow-right" @click="addData"></el-button>
</div>
<div class="right-box">
<p>已选列表</p>
<el-input placeholder="输入关键词过滤" v-model="rightFilterText" clearable/>
<el-tree
ref="treeRight"
:data="rightData"
show-checkbox
node-key="id"
props="defaultProps"
:filter-node-method="filterNode"
>
</el-tree>
</div>
</div>
</template>
<script>
data(){
return {
checkAll: false,
leftFilterText: '',
rightFilterText: '',
defaultProps: {
chilren: 'children',
label: 'labelName', // 适配后端下发的数据字段名
isLeaf: 'leaf', // leaf 字段判断节点是否为叶子节点
// 配置禁选的节点
disabled: function(data, node) {
// 如这里配置父节点、带有disable属性的节点禁选
if('children' in data || data.disable) {
return true;
} else {
return false;
}
}
},
leftData: [],
rightData: []
}
},
watch: {
leftFilterText(val) {
this.$refs.treeLeft.filter(val);
},
rightFilterText(val) {
this.$refs.treeRight.filter(val);
}
},
methods: {
// 根据关键词过滤节点
filterNode(value, data) {
if(!value) return true;
// labeName 为defaultProps中配置的label值,未配置默认为label
return data.labeName.indexOf(value) !== -1;
},
// 懒加载出树结构的最后一层节点
async loadNode(node, resolve) {
if(node.level === 0) {
return resolve(node.data); // 顶层数据默认展示
} else {
if(node.data.children && node.data.children.length > 0) {
return resolve(node.data.children);
} else { // 最后一层数据,异步懒加载
let tempData = await this.getDynamicData(node.data.id);
return resolve(tempData);
}
}
},
// 获取数据接口
getDynamicData(id) {
},
// 移除节点
removeData() {
// 右侧选中节点
let removeKeys = this.$refs.treeRight.getCheckedKeys();
this.rightData = this.rightData.filter(item => !removeKeys.includes(item.id));
// 左侧:仅保留右侧列表中有的数据为勾选状态
let leftCheckKeys = this.rightData.map(item => item.id);
this.$refs.treeLeft.setCheckedKeys(leftCheckKeys);
},
// 添加节点
removeData() {
// 获取左侧选中节点,作为右侧的数据
let checkNodes = this.$refs.treeLeft.getCheckedNodes();
let checkKeys = this.$refs.treeLeft.getCheckedKeys();
this.rightData = checkNodes;
},
}
</script>
🎨 过滤节点函数:filterNode
1、watch 监听关键词;filterNode 必须有返回值,否则数据显示不出来;
2、关键词不为空时,函数的返回值 data.labeName.indexOf(value) !== -1; 其中 labeName 为defaultProps中配置的label值,未配置默认为label
🎨 异步加载函数:loadNode
根据 node.level 去匹配数据层级,判断是否需要调用接口获取数据
🎨 样式自定义
二、最终效果:(效果图仅供参考)
(1) 左侧列表为树形结构,且最后一级节点懒加载;(数据量大时,可以有效提高加载速度)
(2)右侧选中的列表无树形结构,为左侧选中的所有节点