VUE3+Element+下拉树形多选组件

<template>
  <!-- 该下拉树形组件同时支持单选与多选 -->
  <!-- select内展示被选中数据modelValueLabel,单选为对象类型,多选为数组类型 -->
  <!-- 单选:{label:'',id:''},多选:[{label:'',id:''},{label:'',id:''}] -->
  <!-- select内value-key绑定的值必须与tree内node-key绑定的值为同一值,且在数据中唯一 -->
  <div>
    <el-select
      v-model="modelValueLabel"
      :filter-method="selectFilterMethod"
      style="min-width: 180px"
      :size="size"
      :value-key="valueKey"
      ref="selectTree"
      fit-input-width
      :multiple="!isSingleChoice"
      :placeholder="placeholderText"
      :filterable="isFilter"
      :collapse-tags="isTag"
      @change="selectChangeMethod"
      @visible-change="visibleChange"
    >
      <el-option
        v-for="item in optionList"
        :value="item"
        :label="item.label"
        :key="item[valueKey]"
        style="height: 0"
      >
      </el-option>
      <el-option style="height: auto; padding: 0" key="option2" value="option2">
        <el-tree
          :data="treeData"
          :default-expand-all="isDefaultAll"
          :expand-on-click-node="true"
          :filter-node-method="filterNode"
          :show-checkbox="!isSingleChoice"
          :node-key="valueKey"
          ref="tree"
          highlight-current
          :props="defaultProps"
          @node-click="nodeClick"
          @check-change="checkChange"
        >
        </el-tree>
      </el-option>
    </el-select>
  </div>
</template>

<script>
export default {
  name: "MetaSelectTree",
  props: {
    // 回显数据,单选值为对象,多选为数组
    // 单选:{label:'',id:''},多选:[{label:'',id:''},{label:'',id:''}]
    echoData: {
      type: [Array, Object],
      default: null,
    },
    // 是否开启搜索
    isFilter: {
      default: true,
    },
    // 尺寸
    size: {
      default: "default",
    },
    placeholderText: {
      default: "请选择...",
    },
    // 多选时是否将选中值按文字的形式展示
    isTag: {
      default: true,
    },
    // 是否默认展开所有节点
    isDefaultAll: {
      default: true,
    },
    isCheckParent: {
      default: false,
    },
    // 点击节点是否展开
    expandClickNode: {
      default: false,
    },
    // 绑定数据的唯一标识所使用的key
    valueKey: {
      type: String,
      default: "id",
    },
    // 树形结构渲染数据
    treeData: {
      default: [],
    },
    // 遍历时字段格式化
    defaultProps: {
      default: {
        children: "children",
        label: "label",
      },
    },
    // 是否单选
    isSingleChoice: {
      default: true,
    },
  },
  data() {
    return {
      // 实际选中值
      modelValue: [],
      // 实际选中值的id
      arrId: [],
      // 下拉框绑定值
      modelValueLabel: [],
      optionList: [],
    };
  },
  watch: {
    treeData(data) {
      // 对树形数据增加监听,确保option可被正常创建
      if (data && data.length) {
        this.optionList = this.getOptionList(this.treeData);
      }
    },
  },
  mounted() {
    if (this.echoData) {
      let echoData = this.echoData;
      let valueKey = this.valueKey;
      if (this.isSingleChoice) {
        this.modelValue = [echoData];
        this.modelValueLabel = echoData;
        this.arrId = [echoData[valueKey]];
      } else if (echoData.length > 0) {
        this.modelValue = echoData;
        this.modelValueLabel = echoData;
        let arrId = [];
        echoData.forEach((item) => {
          arrId.push(item[valueKey]);
        });
        this.arrId = arrId;
        this.$nextTick(() => {
          this.$refs.selectTree.setCheckedKeys(arrId);
        });
      }
    }
  },
  methods: {
    getOptionList(treeData, optionList = []) {
      // 递归树形数据,获取所有叶子节点,组成一维数组,作为渲染option的数据。
      // 请注意!该处不可省略,因该组件支持多选模式,
      // select回显数据配置的为对象数组,必须有相匹配的option才可显示正常
      if (treeData.length) {
        treeData.forEach((item) => {
          if (item.children && item.children.length) {
            this.getOptionList(item.children, optionList);
          } else {
            optionList.push(item);
          }
        });
        return optionList;
      }
      return [];
    },
    visibleChange(bool) {
      if (bool) {
        this.selectFilterMethod("");
      }
    },
    selectFilterMethod(val) {
      // 下拉框调用tree树筛选
      this.$refs.tree.filter(val);
    },
    selectChangeMethod(data) {
      // 下拉多选内从展示的被选中数据中删除被选中内容
      let arrNew = [];
      let arrId = [];
      data.forEach((item) => {
        arrNew.push(item);
        arrId.push(item[this.valueKey]);
      });
      this.modelValue = arrNew;
      this.arrId = arrId;
      // 设置勾选的值
      this.$refs.tree.setCheckedNodes(arrNew);
      this.$emit("input", arrId);
    },
    filterNode(value, data) {
      if (!value) return true;
      return data.label.indexOf(value) !== -1;
    },
    nodeClick(data, node) {
      // 树形结构点击节点
      let valueKey = this.valueKey;
      if (this.isSingleChoice) {
        // 单选
        if (this.isCheckParent) {
          // 允许选中父节点
          this.modelValueLabel = data;
          this.modelValue = [];
          this.arrId = [];
          this.modelValue.push(data);
          this.arrId.push(data[valueKey]);
          this.$emit("input", this.arrId);
          this.$emit("nodeClick", this.arrId);
          this.$refs.selectTree.blur();
        } else if (node.childNodes.length == 0) {
          // 选中的是末级节点
          this.modelValueLabel = data;
          this.modelValue = [];
          this.arrId = [];
          this.modelValue.push(data);
          this.arrId.push(data[valueKey]);
          this.$emit("callId", data[valueKey]); // 直接返回字符串
          this.$emit("callItem", data); // 直接返回选中对象
          this.$emit("input", this.arrId); // 直接返回选中id
          this.$refs.selectTree.blur();
        }
      }
    },
    checkChange(node, checkedType) {
      // 树形结构CheckBox勾选与取消
      // this.modelValue内部存储的数据顺序完全遵循勾选的顺序
      let nodeInDataIndex = -1;
      if (node.children && node.children.length) {
        // 当前节点有孩子节点,不是末端的叶子节点
      } else {
        if (checkedType) {
          // 当前节点被选中
          nodeInDataIndex = this.nodeInDataIndex(this.modelValue, node);
          if (nodeInDataIndex == -1) {
            // 该节点未被保存过
            this.modelValue.push(node);
            this.arrId.push(node[this.valueKey]);
          }
        } else {
          // 当前节点被取消选中
          nodeInDataIndex = this.nodeInDataIndex(this.modelValue, node);
          if (nodeInDataIndex > -1) {
            // 该节点被保存过
            this.modelValue.splice(nodeInDataIndex, 1);
            this.arrId.splice(nodeInDataIndex, 1);
          }
        }
        this.modelValueLabel = this.modelValue;
        this.$emit("input", this.arrId);
      }
    },
    nodeInDataIndex(data, node, key = this.valueKey) {
      if (data && data.length && node) {
        let dataLen = data.length;
        let nodeInDataIndex = -1;
        for (let i = 0; i < dataLen; i++) {
          if (data[i][key] == node[key]) {
            nodeInDataIndex = i;
            break;
          }
        }
        return nodeInDataIndex;
      }
      return -1;
    },
  },
};
</script>

<style lang="scss" scoped>
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值