效果图如下
主要实现
与正常的 el-select 不同的是,el-select 的 el-option 需要放入树结构,但是依靠单纯的 slot 无法解决。本文的实现为异步树,主要的获取树结构代码与饿了么官网相同。
html 部分
本文的实现方式是,将树结构包裹到 el-select 标签内,el-option 写入样式为 display:none
。如此便使得,原本的下拉框内容不展示,而只展示树形控件。el-select 的 label、value 与 el-tree 的数据结构对应即可。本文的 value 对应 el-tree 的 node-key。
<el-select
v-model="form.resourceId"
placeholder="请选择"
class="resource-select-tree"
multiple
@change="changeTreeTag"
@remove-tag="removeTreeTag"
>
<el-option
v-for="item in treeOptions"
:key="item.externalCode"
style="display: none"
:label="item.name"
:value="item.externalCode"
></el-option>
<el-tree
ref="lazyTree"
:data="dataAsync"
:props="defaultProps"
:load="loadNode"
:node-key="nodeKey"
:parent-key="parentKey"
show-checkbox
lazy
:default-checked-keys="defaultChecked"
@check-change="handleCheckChange"
></el-tree>
</el-select>
el-tree -> el-select
主要的数据初始化
data(){
return {
form:{
resourceId:[] // el-select value
},
// 树
treeOptions: ['a'], // 下拉框列表,给任意值,防止出现“暂无数据”
dataAsync: [],
nodeKey: 'externalCode',
parentKey: 'parentExternalCode',
defaultChecked: [],
defaultProps: {
children: 'children',
label: 'name',
svgIconColorActive: '#fff',
icon: (data, node) => {
if (data.hasLeaf) {
return 'h-icon-internet';
}
},
svgIcon: (data, node) => {
if (!data.hasLeaf) {
return 'svg-box-camera';
}
},
svgIconColor: (data, node) => {
if (!data.hasLeaf) {
return '#3399ff';
}
},
isLeaf: data => {
return !data.hasLeaf;
}
}
}
}
树结构数据绑定到下拉框,主要是在 check-change
事件被触发时,更新 el-select 绑定的 value、el-select的下拉框列表。并且下拉框列表 与 值 一一对应,才可以显示准确。
需要注意的是,异步树在未展开某节点时,使用 getCheckedNodes
方法无法获得未展开的节点选中的数据,只能获取 已展开过的。因此在编辑回显时,操作时要将未展开的节点的选中状态保持。
handleCheckChange(data, isChecked, isLeafChecked) {
const checked = this.$refs.lazyTree.getCheckedNodes(true); // 获取选中的子节点
const selected = [];
const obj = {};
checked.map(item => {
obj[item.externalCode] = true;
});
// 该步骤得目的是获得 treeOptions 中有,但是树选中节点没有得节点
this.treeOptions.map(item => {
if (!obj[item.externalCode]) {
selected.push(item);
}
});
// 在编辑回显时,置入了编辑标志位isEdit,如果树选中节点没有但是是编辑回显的且不是该次事件取消选中的,push入选中列表
selected.map(item => {
if (
item.isEdit&&
!(data.externalCode === item.externalCode && !isChecked)
) {
checked.push(item);
}
});
const labelArr = [];
checked.forEach(item => {
item.externalCode && labelArr.push(item.externalCode); // 获取选中的key值
});
this.form.ResourceURI = labelArr; // 选中key值 就是 el-select 的值
// 选中的节点就是下拉框列表,如果节点个数为 0,则塞入一个随意的值,防止“暂无数据”
this.treeOptions = checked.length === 0 ? ['a12_'] : checked;
},
编辑回显
this.form.ResourceURI = detail.ResourceURI.split(',');
detail.ResourceURI && (this.treeOptions = []);
this.form.ResourceURI.map(item => {
this.treeOptions.push({
name: item.name,
externalCode: item.externalCode,
isEdit: true // 置入编辑标志位
});
el-tree <- el-select
下拉框的操作响应到树结构,需要在 change
事件内,利用到根据 key 值选中的 setCheckedKeys
函数更新树结构的选中状态,这里只需要将当前已经更改的 下拉框绑定的值 入参到函数即可。
change
事件被触发后,树结构的 check-change
事件被触发,更新下拉框列表值。同样check-change
事件被触发,也会导致下拉框的 change
触发,因此形成了一个闭环。
changeTreeTag() {
this.$refs.lazyTree.setCheckedKeys(this.form.resourceId);
}
// 直接删除多选 tag 时,需要将其从treeOptions 中剔除
removeTreeTag(e) {
const newTreeOptions = [];
this.treeOptions.map(item => {
if (this.form.ResourceURI.indexOf(item.externalCode) !== -1) {
newTreeOptions.push(item);
}
});
this.treeOptions = newTreeOptions;
},