通过el-select选择器和el-tree树形结构二次封装(vue2+elementUI),开发element-plus的TreeSelect树形选择器

需求:

领导看我在另一个vue3项目的el-tree-select挺好的,叫我移入vue2的项目中。
但是vue2版本的elementUI,并没有这个组件,所以只能自己找,找半天找不到和它差不多的,通过网友写的组件改写的

参考链接:

利用 el-select 和 el-tree 实现树形结构多选框联动功能

组件示例照片:

照片1
照片2

使用:

<elTreeSelect
    :props="{
    value: 'id',
    label: 'label',
    children: 'children',
  }"
    :options="deptOptions"
    :value="deptId"
    :clearable="false"
    :accordion="true"
    @getValue="handleNodeClick"
    height="200"
/>



  data() {
    return {
    	deptId: '', // 牧场id
    	deptOptions: [
        {
            "id": 201,
            "label": "突泉县",
            "children": [
                {
                    "id": 202,
                    "label": "东杜尔基镇",
                    "children": [
                        {
                            "id": 12,
                            "label": "东方镜养殖户",
                            "farmFlag": "1",
                            "level": null
                        }
                    ],
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 203,
                    "label": "六户镇",
                    "children": [
                        {
                            "id": 14,
                            "label": "澜养殖户",
                            "farmFlag": "1",
                            "level": null
                        }
                    ],
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 204,
                    "label": "永安镇",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 205,
                    "label": "学田乡",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 206,
                    "label": "宝石镇",
                    "children": [
                        {
                            "id": 3,
                            "label": "张三养殖户",
                            "farmFlag": "1",
                            "level": null
                        }
                    ],
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 207,
                    "label": "太平乡",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 208,
                    "label": "突泉镇",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 209,
                    "label": "水泉镇",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 210,
                    "label": "内蒙古自治区国营杜尔基农场",
                    "farmFlag": null,
                    "level": "4"
                },
                {
                    "id": 211,
                    "label": "九龙乡",
                    "farmFlag": null,
                    "level": "4"
                }
            ],
            "farmFlag": null,
            "level": "3"
        }
    ], // tree数据
    },
    methods: {
    	// 节点单击事件
    	handleNodeClick(data) {
    		console.log('选择的数据', data);
   		}
    }

组件代码:

<template>
  <el-select
    v-model="valueTitle"
    :clearable="clearable"
    @clear="clearHandle"
    ref="treeSelect"
    filterable
    :filter-method="filterSelect"
    @visible-change="clearSelect"
    placeholder="请选择"
  >
    <!-- <el-input
      class="selectInput"
      :placeholder="placeholder"
      v-model="filterText"
    >
    </el-input> -->

    <el-option :value="valueTitle" :label="valueTitle" class="options">
      <el-tree
        id="tree-option"
        ref="selectTree"
        :accordion="accordion"
        :data="treeOptions"
        :props="props"
        :node-key="props.value"
        :default-expanded-keys="defaultExpandedKey"
        :filter-node-method="filterNode"
        @node-click="handleNodeClick"
      >
      </el-tree>
    </el-option>
  </el-select>
</template>

<script>
export default {
  name: "el-tree-select",
  props: {
    /* 配置项 */
    props: {
      type: Object,
      default: () => {
        return {
          value: "id", // ID字段名
          label: "title", // 显示名称
          children: "children", // 子级字段名
        };
      },
    },
    /* 选项列表数据(树形结构的对象数组) */
    options: {
      type: Array,
      default: () => {
        return [];
      },
    },
    /* 初始值 */
    value: {
      type: Number | String,
      default: () => {
        return null;
      },
    },
    /* 可清空选项 */
    clearable: {
      type: Boolean,
      default: () => {
        return true;
      },
    },
    /* 自动收起 */
    accordion: {
      type: Boolean,
      default: () => {
        return false;
      },
    },
    placeholder: {
      type: String,
      default: () => {
        return "检索关键字";
      },
    },
  },
  data() {
    return {
      filterText: "",
      valueId: this.value, // 初始值
      valueTitle: "",
      defaultExpandedKey: [],
      treeOptions: [],
    };
  },
  mounted() {
    this.initHandle();
  },
  methods: {
    // 初始化值
    initHandle() {
      if (this.valueId && this.options && this.options.length > 0) {
        let info = this.findFirstDeptFarm2(this.options);
        this.valueTitle = info.label; // 初始化显示
        // this.$refs.selectTree.getNode(this.valueId).data[this.props.label]
        this.$refs.selectTree.setCurrentKey(this.valueId); // 设置默认选中
        this.defaultExpandedKey = [this.valueId]; // 设置默认展开

        this.treeOptions = this.options;
      }
      this.initScroll();
    },
    // 初始化滚动条
    initScroll() {
      this.$nextTick(() => {
        let scrollWrap = document.querySelectorAll(
          ".el-scrollbar .el-select-dropdown__wrap"
        )[0];
        let scrollBar = document.querySelectorAll(
          ".el-scrollbar .el-scrollbar__bar"
        );
        scrollWrap.style.cssText =
          "margin: 0px; max-height: none; overflow: hidden;";
        scrollBar.forEach((ele) => (ele.style.width = 0));
      });
    },
    // 切换选项
    handleNodeClick(node) {
      this.valueTitle = node[this.props.label];
      this.valueId = node[this.props.value];
      this.$emit("getValue", {
        id: this.valueId,
        label: this.valueTitle,
        farmFlag: node.farmFlag,
      });
      this.defaultExpandedKey = [];
      // if (!node.children || !node.children.length) {
      //补录选择完选项弹框不能收起
      this.$refs.treeSelect.blur();
      // }
    },
    // 清除选中
    clearHandle() {
      this.valueTitle = "";
      this.valueId = null;
      this.defaultExpandedKey = [];
      this.clearSelected();
      this.$emit("getValue", null);
    },
    /* 清空选中样式 */
    clearSelected() {
      let allNode = document.querySelectorAll("#tree-option .el-tree-node");
      allNode.forEach((element) => element.classList.remove("is-current"));
    },
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    },
    findFirstDeptFarm2(arr) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i].id === this.valueId) {
          return arr[i];
        } else if (
          Array.isArray(arr[i].children) &&
          arr[i].children.length > 0
        ) {
          const result = this.findFirstDeptFarm2(arr[i].children);
          if (result) {
            return result;
          }
        }
      }
      return null;
    },
    clearSelect(show) {
      if (!show) {
        this.treeOptions = this.options;
      }
    },
    filterSelect(val) {
      if (val) {
        this.treeOptions = this.filterTreeByName(this.options, val);
      } else {
        this.treeOptions = this.options;
      }
    },
    filterTreeByName(tree, nameToMatch) {
      // 递归函数,用于遍历树并找到匹配的节点及其父节点
      function traverseAndFilter(node, filteredTree = []) {
        // 如果节点的name匹配,则将其添加到filteredTree中
        if (node.label.indexOf(nameToMatch) !== -1) {
          filteredTree.push(node);
        }

        // 遍历节点的子节点
        if (node.children) {
          for (let child of node.children) {
            // 递归调用traverseAndFilter来检查子节点
            traverseAndFilter(child, filteredTree);
          }
        }

        // 如果filteredTree不为空,说明找到了匹配项,返回filteredTree
        // 否则返回null,表示当前节点及其子节点中都没有匹配项
        return filteredTree.length ? filteredTree : null;
      }

      // 初始化结果树
      let resultTree = [];

      // 遍历树的根节点
      for (let rootNode of tree) {
        let filteredNodes = traverseAndFilter(rootNode);
        // 如果filteredNodes不为空,说明找到了匹配项,将其添加到结果树中
        if (filteredNodes) {
          resultTree = resultTree.concat(filteredNodes);
        }
      }

      // 去除重复的节点(如果有的话),因为可能通过多个路径找到了相同的节点
      resultTree = [
        ...new Map(resultTree.map((node) => [node.id || node, node])).values(),
      ];

      // 如果没有找到任何匹配的节点,返回null
      if (resultTree.length === 0) {
        return null;
      }

      // 构建最终的树形结构,只包含匹配的节点及其父节点
      function buildFilteredTree(nodes) {
        const filteredTree = [];
        const nodeMap = new Map();

        for (const node of nodes) {
          nodeMap.set(node.id || node, node);
        }

        for (const node of nodes) {
          const parent = node.parent;
          if (!parent || nodeMap.has(parent)) {
            let currentNode = nodeMap.get(node.id || node);
            let parentNode = parent ? nodeMap.get(parent) : null;

            if (parentNode) {
              if (!parentNode.children) {
                parentNode.children = [];
              }
              parentNode.children.push(currentNode);
              filteredTree.push(parentNode);
            } else {
              filteredTree.push(currentNode);
            }
          }
        }

        return filteredTree;
      }

      // 如果找到了匹配的节点,则构建最终的树形结构
      if (resultTree.length > 0) {
        return buildFilteredTree(resultTree);
      }

      // 如果没有找到匹配的节点,返回null
      return null;
    },
  },
  watch: {
    value() {
      this.valueId = this.value;
      this.initHandle();
    },
    filterText(val) {
      if (val) {
        try {
          this.$refs.selectTree.filter(val);
        } catch (error) {}
      }
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
  height: auto;
  max-height: 274px;
  padding: 0;
  overflow: hidden;
  overflow-y: auto;
}
.el-select-dropdown__item.selected {
  font-weight: normal;
}
ul li >>> .el-tree .el-tree-node__content {
  height: auto;
  padding: 0 20px;
}
.el-tree-node__label {
  font-weight: normal;
}
.el-tree >>> .is-current .el-tree-node__label {
  color: #409eff;
  font-weight: 700;
}
.el-tree >>> .is-current .el-tree-node__children .el-tree-node__label {
  color: #606266;
  font-weight: normal;
}
.selectInput {
  padding: 0 5px;
  box-sizing: border-box;
}
</style>

最近更新

  1. TCP协议是安全的吗?

    2024-03-14 06:04:03       17 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-14 06:04:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-14 06:04:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-14 06:04:03       18 阅读

热门阅读

  1. 数字媒体专业AR增强现实仿真模拟实训

    2024-03-14 06:04:03       21 阅读
  2. 关于k8s中 storageclass 的 is-default-sc 默认存储设置

    2024-03-14 06:04:03       18 阅读
  3. k8s rancher开源平台(概念,部署示例,)

    2024-03-14 06:04:03       18 阅读
  4. k8s HPA 自动伸缩机制 (配置,资源限制,)

    2024-03-14 06:04:03       20 阅读
  5. Android 辅助功能 -抢红包

    2024-03-14 06:04:03       15 阅读
  6. C#+datax实现sql server数据同步到redis

    2024-03-14 06:04:03       16 阅读