vue的下拉框(多选)嵌套异步树

1,896 阅读1分钟

效果图如下

主要实现

与正常的 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;
},