通过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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/452946.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手势学习

1. 点击手势 Composable fun ClickableSample() {val number remember {mutableStateOf(0)}Text(text number.value.toString(),textAlign TextAlign.Center,modifier Modifier.wrapContentSize().clickable {number.value}.background(Color.LightGray).padding(horizonta…

软件I2C读写MPU6050

文章目录 前言本次线路图封装I2C时序封装MPU6050,配置寄存器最后在主函数进行显示 前言 本片文章开始进行I2C在STM32的直接操作&#xff0c;理解时序的代码实现&#xff0c;理解对寄存器的配置&#xff0c;使用I2C读写MPU6050&#xff0c;读取MPU6050的各轴数据。 MPU6050详解…

vue 自定义组件绑定model+弹出选择支持上下按键选择

参考地址v-modelhttps://v2.cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model 原文代码 Vue.component(base-checkbox, {model: {prop: checked,event: change},props: {checked: Boolean},template: `…

Java异常分类(二)

RuntimeException 运行时异常&#xff1a; 派生于 RuntimeException 的异常&#xff0c;如被 0 除、数组下标越界、空指针等&#xff0c;其产生比较频繁&#xff0c;处理麻烦&#xff0c;如果显式的声明或捕获将会对程序可读性和运行效率影响很大。因此由系统自动检测并将它们交…

Caffeine缓存

本地缓存基于本地环境的内存&#xff0c;访问速度非常快&#xff0c;对于一些变更频率低、实时性要求低的数据&#xff0c;可以放在本地缓存中&#xff0c;提升访问速度 使用本地缓存能够减少和Redis类的远程缓存间的数据交互&#xff0c;减少网络 I/O 开销&#xff0c;降低这…

使用EasyRec快速构建推荐模型

随着移动app的普及&#xff0c;个性化推荐和广告成为很多app不可或缺的一部分。他们在改善用户体验和提升app的收益方面带来了巨大的提升。深度学习在搜广推领域的应用也已经非常深入&#xff0c;并且给各种场景的效果带来了巨大的提升。针对推荐流程的各个阶段&#xff0c;业界…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的石头剪刀布手势识别系统详解(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;本篇博客深入探讨了使用深度学习技术开发石头剪刀布手势识别系统的过程&#xff0c;并分享了完整代码。该系统利用先进的YOLOv8、YOLOv7、YOLOv6、YOLOv5算法&#xff0c;并对这几个版本进行性能对比&#xff0c;如mAP、F1 Score等关键指标。文章详细阐述了YOL…

【算法题解】Java算法题目解析

1. 数字三角形 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int n scan.nextInt();int[][] dp new int[n 1][n 1];int max 0;for (int i 1; i < dp.length; i) {for (int j 1; j &l…

Android 生成SO - 基础工程创建

最近需要给小伙伴扫盲一下如何使用Android Studio 生成一个SO文件&#xff0c;网上找了很多都没有合适的样例&#xff0c;那只能自己来写一个了。 原先生成SO是一个很麻烦的事情&#xff0c;现在Android Studio帮忙做了很多的事情&#xff0c;基本只要管好自己的C代码即可。 …

第2篇【Docker项目实战】使用Docker部署Raneto知识库平台(转载)

【Docker项目实战】使用Docker部署Raneto知识库平台 一、Raneto介绍 1.1 Raneto简介 Raneto是一个免费、开放、简单的 Markdown 支持的 Node.js 知识库。 1.2 知识库介绍 知识库 知识库是指存储和组织知识的系统或库&#xff0c;它包括了各种类型的信息和知识&#xff0c;如…

【数学建模】熵权法 Python代码

熵权法是一种客观的赋权方法&#xff0c;它可以靠数据本身得出权重。 依据的原理&#xff1a;指标的变异程度越小&#xff0c;所反映的信息量也越少&#xff0c;其对应的权值也应该越低。 import numpy as np#自定义对数函数mylog&#xff0c;用于处理输入数组中的0元素 def m…

OpenCV和Mediapipe实现摸嘴或鼻检测

目录 引言 1.过程简介 2. 代码结构 2.1 导入库 2.2 初始化模型 2.3 读取视频流或摄像头 2.4 初始化FPS计算 2.5 主循环 2.6 转换BGR图像为RGB图像 2.7 运行姿势检测模型和手部检测模型 2.8 绘制姿势关键点及连接线 2.9 检测手部关键点 2.10 判断手部与鼻子、嘴的相对…

300分钟吃透分布式缓存-28讲:如何构建一个高性能、易扩展的Redis集群?

Redis 集群的分布式方案主要有 3 种。分别是 Client 端分区方案&#xff0c;Proxy 分区方案&#xff0c;以及原生的 Redis Cluster 分区方案。 Client 端分区 Client 端分区方案就是由 Client 决定数据被存储到哪个 Redis 分片&#xff0c;或者由哪个 Redis 分片来获取数据。…

DVWA靶场-暴力破解

DVWA是一个适合新手锻炼的靶机&#xff0c;是由PHP/MySQL组成的 Web应用程序&#xff0c;帮助大家了解web应用的攻击手段 DVWA大致能分成以下几个模块&#xff0c;包含了OWASP Top 10大主流漏洞环境。 Brute Force——暴力破解 Command Injection——命令注入 CSRF——跨站请…

职场中的创新思维与执行力

在职场中&#xff0c;创新思维和执行力是两个关键要素。创新思维能够帮助员工在工作中找到更好的解决方案&#xff0c;而执行力则是将想法付诸实践的能力。本文将探讨如何在职场中培养创新思维和提升执行力。 一、创新思维的重要性 在职场中&#xff0c;创新思维是推动企业发展…

Docker容器化技术(互联机制实现便捷互访)

容器的互联是一种让多个容器中的应用进行快速交互的方式。它会在源和接收容器之间创建连接关系&#xff0c;接收容器可以通过容器名快速访问到源容器&#xff0c;而不用指定具体的IP地址。 1.自定义容器命名 连接系统依据容器的名称来执行。因此&#xff0c;首先需要自定义一…

Django 模版基本语法

Django学习笔记 模版语法 本质&#xff1a;在HTML中写一些占位符&#xff0c;由数据对这些占位符进行替换和处理。 views.py def page2(request):#定义一些变量将变量传送给templates中的html文件name1 sallyname2 yingyinghobbys [swimming,badminton,reading]person {…

惬意上手Redis

Redis介绍 Redis&#xff08;全称为REmote Dictionary Server&#xff09;是一个开源的、内存中的数据存储结构&#xff0c;主要用作应用程序缓存或快速相应数据库。 REmote Dictionary Server: 有道翻译Redis是“远程字典服务”&#xff0c;远程不过是远程访问&#xff0c;而…

Ingress 实战:从零到一构建高可用服务

Ingress 是 Kubernetes 中一种用于控制流量进入集群的资源。它可以为集群内的服务提供统一的访问入口&#xff0c;并提供一些额外的功能&#xff0c;例如&#xff1a; 路由流量到不同的服务 提供基于路径的路由 提供基于主机的路由 提供 TLS 加密 使用身份验证和授权 Ing…

SQL: 触发器/存储过程/游标的操作

目录 触发器存储过程创建存储过程修改存储过程删除存储过程执行存储过程 游标待续、更新中 触发器 待更新存储过程 定义 是一组TSQL语句的预编译集合&#xff0c;能实现特定的功能 是一种独立的数据库对象&#xff0c;在服务器上创建和运行 类似于编程语言中的过程或函数分类…