vue2结合element-ui实现TreeSelect 树选择功能

news2025/2/24 11:01:36
需求背景

在日常开发中,我们会遇见很多不同的业务需求。如果让你用element-ui实现一个 tree-select 组件,你会怎么做?

这个组件在 element-plus 中是有这个组件存在的,但是在 element-ui 中是没有的。

可能你会直接使用 element-plus 组件库,或者其他组件库。但是若你的项目目前的基于vue2和element-ui进行开发的呢?

下面这种思路利用 el-tree 和 el-select 进行嵌套从而实现我们想要的 tree-select 组件

最终效果

大致思路:

el-select和el-tree进行嵌套,将el-tree放到el-option里,循环遍历el-option,同时定义一个方法比如:formatData,对树形数据进行递归处理,这样就可以实现无论嵌套的层级有几层都可以正常渲染在界面上
利用 v-model 和 update:selectValue 实现父子组件之间的双向通信,同时利用computed进行监听以实现实时更新

组件中的 v-model

我们在input中可以使用v-model来完成双向绑定:

  •  这个时候往往会非常方便,因为v-model默认会帮助我们完成两件事:
  •  v-bind:value的数据绑定和@input的事件监听;

如果我们现在封装了一个组件,其他地方在使用这个组件时,是否也可以使用v-model来同时完成这两个功能呢?

当我们在组件上使用的时候,等价于如下的操作:

  •  我们会发现和input元素不同的只是属性的名称和事件触发的名称而已;

如果我们希望绑定多个属性呢?

  •  也就是我们希望在一个组件上使用多个v-model是否可以实现呢?
  •  我们知道,默认情况下的v-model其实是绑定了 modelValue 属性和 @update:modelValue的事件;
  •  如果我们希望绑定更多,可以给v-model传入一个参数,那么这个参数的名称就是我们绑定属性的名称;

实现代码示例

子组件:

<template>
  <div>
    <h4>Counter: {{modelValue}}</h4>
    <button @click="changeCounter">修改数据</button>
  </div>
</template>

<script>
export default {
  props: {
    modelValue:  {
      type: Number
    },
  },
  emits: ['update:modelValue'],
  methods: {
    changeCounter(){
      this.$emit('update:modelValue',101)
    }
  }
}
</script>

父组件:

<template>
  <!-- <child v-model="appCounter" /> -->
  <!-- 等同于如下做法:modelValue--默认
    可以自定义名称,通过 v-model:counter 类似于这种格式
   -->
  <child :modelValue="appCounter" @update:modelValue="appCounter = $event" />
</template>

<script>
import child from '@/components/child.vue'
export default {
  components: {
    child
  },
  data() {
    return {
      appCounter: 100,
    };
  },
  methods: {
    
  },
};
</script>

有了上面的知识,那么下面实现就很简单了,这里直接上代码 

组件封装

子组件:TreeSelect.vue

<template>
  <div class="app-container" style="padding: 0">
    <el-select
      class="main-select-tree"
      ref="selectTree"
      v-model="value"
      style="width: 240px"
      clearable
      @clear="clearSelectInput"
    >
      <el-input
        style="width: 220px; margin-left: 10px; margin-bottom: 10px"
        placeholder="输入关键字进行过滤"
        v-model="filterText"
        clearable
      >
      </el-input>
      <el-option
        v-for="item in formatData(data)"
        :key="item.value"
        :label="item.label"
        :value="item.value"
        style="display: none"
      />
      <el-tree
        class="main-select-el-tree"
        ref="selecteltree"
        :data="data"
        node-key="id"
        highlight-current
        :props="defaultProps"
        @node-click="handleNodeClick"
        :current-node-key="value"
        :expand-on-click-node="true"
        default-expand-all
        :filter-node-method="filterNode"
      />
    </el-select>
  </div>
</template>
    
<script>
export default {
  props: {
    selectValue: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      filterText: "",
      value: "",
      data: [
        {
          id: 1,
          label: "云南",
          children: [
            {
              id: 2,
              label: "昆明",
              children: [
                {
                  id: 3,
                  label: "五华区",
                  children: [
                    {
                      id: 8,
                      label: "xx街道",
                      children: [
                        {
                          id: 81,
                          label: "yy社区",
                          children: [{ id: 82, label: "北辰小区" }],
                        },
                      ],
                    },
                  ],
                },
                { id: 4, label: "盘龙区" },
              ],
            },
          ],
        },
        {
          id: 5,
          label: "湖南",
          children: [
            { id: 6, label: "长沙" },
            { id: 7, label: "永州" },
          ],
        },
        {
          id: 12,
          label: "重庆",
          children: [
            { id: 10, label: "渝北" },
            { id: 9, label: "合川" },
          ],
        },
        {
          id: 13,
          label: "江苏",
          children: [{ id: 14, label: "盐城" }],
        },
      ],
      defaultProps: {
        children: "children",
        label: "label",
      },
    };
  },
  watch: {
    filterText(val) {
      this.$refs.selecteltree.filter(val);
    },
  },
  methods: {
    filterNode(value, data) {
      if (!value) return true;
      return data.label.indexOf(value) !== -1;
    },
    // 递归遍历数据
    formatData(data) {
      let options = [];
      const formatDataRecursive = (data) => {
        data.forEach((item) => {
          options.push({ label: item.label, value: item.id });
          if (item.children && item.children.length > 0) {
            formatDataRecursive(item.children);
          }
        });
      };
      formatDataRecursive(data);
      return options;
    },
    // 点击事件
    handleNodeClick(node) {
      this.value = node.id;
      this.$refs.selectTree.blur();
      this.$emit('update:selectValue', node.label);
    },
    // 清空事件
    clearSelectInput() {
        this.$emit('update:selectValue', '');
        // 获取 el-tree 实例的引用
        const elTree = this.$refs.selecteltree;
       // 将当前选中的节点设置为 null
       elTree.setCurrentKey(null);
    },
  },
};
</script>

<style>
.main-select-el-tree .el-tree-node .is-current > .el-tree-node__content {
  font-weight: bold;
  color: #409eff;
}
.main-select-el-tree .el-tree-node.is-current > .el-tree-node__content {
  font-weight: bold;
  color: #409eff;
}
</style>
使用方式
<TreeSelect v-model="selectedValue" @update:selectValue="handleSelectValueChange"></TreeSelect>

<el-button size="medium" :disabled="todoIsTotal">交接当前{{ tableData.length }}条任务</el-button>
                
import TreeSelect from "./TreeSelect.vue";

export default {
    components: {
        TreeSelect,
    },
    data() {
        selectedValue: "",
    },
    computed: {
        todoIsTotal() {
            return this.selectedValue === "";
        },
    },
    methods: {
        handleSelectValueChange(value) {
            if (value && value.length > 0) {
                this.selectedValue = value;
            } else {
                this.selectedValue = "";
            }
        },
    },
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nginx配置文件和配置命令详解案例

一.nginx.conf配置结构 1.1配置结构图 1.2 nginx中配置nginx.conf配置内容 #user nobody; user root; # 表示worker进程是有root用户去执行的 worker_processes 2; events {# 默认使用epolluse epoll;# 每个worker链接最大数据worker_connections 1024; } http {include …

Java入门基础学习笔记14——数据类型转换

类型转换&#xff1a; 1、存在某种类型的变量赋值给另一种类型的变量&#xff1b; 2、存在不同类型的数据一起运算。 自动类型转换&#xff1a; 类型范围小的变量&#xff0c;可以直接赋值给类型范围大的变量。 byte类型赋值给int类型&#xff0c;就是自动类型转换。 pack…

【MySQL基本查询(下)】

文章目录 一、update案例 二、Delete案例注意&#xff1a;delete 全表数据的行为慎用&#xff01;truncate 三、插入查询结果案例 四、了解一些函数1.count函数2.sum函数3. avg函数4.max函数5. min函数 五、group by子句的使用案例having和where 一、update 该关键字的功能就是…

基于SpringBoot的全国风景区WebGIS按省展示实践

目录 前言 一、全国风景区信息介绍 1、全国范围内数据分布 2、全国风景区分布 3、PostGIS空间关联查询 二、后台查询的设计与实现 1、Model和Mapper层 2、业务层和控制层设计 三、WebGIS可视化 1、省份范围可视化 2、省级风景区可视化展示 3、成果展示 总结 前…

C++ 抽象与封装

一 抽象 抽象实例&#xff1a;时钟 数据抽象&#xff1a; 具有表面当前时间的时、分、秒 行为抽象&#xff1a; 具有设置时间和显示时间两个最基本的功能。 抽象实例&#xff1a;人 数据抽象&#xff1a;姓名、年龄、性别等。 行为抽象&#xff1a; 生物属性&#xff1a;吃…

Cocos creator实现《战机长空》关卡本地存储功能

Cocos creator实现《战机长空》关卡本地存储功能 Cocos creator在开放小游戏过程中&#xff0c;经常会出现设置关卡&#xff0c;这里记录一下关卡数据本地存储功能。 一、关卡设置数据 假如我们有关卡数据如下&#xff0c; let settings [ { level: 1, // 第1关 score: 0,…

Java类加载器介绍

在Java中&#xff0c;类加载器是一种动态加载类的机制&#xff0c;它负责在运行时查找、加载和链接类文件。当Java应用程序需要创建某个类的对象时&#xff0c;类加载器会在运行时查找该类对应的.class文件&#xff0c;并将其加载到Java虚拟机中。Java类加载器通常分为三层&…

【MQTT】mosquitto 的 “下载、交叉编译、使用” 详细教程,手把手搭建一个MQTT Broker

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-05-11 0…

系统架构设计师 - 计算机组成与体系结构(1)

计算机组成与体系结构 计算机组成与体系结构计算机结构 ★CPU 组成结构运算器组成控制器组成 计算机体系结构冯诺依曼结构哈弗结构 嵌入式芯片&#xff08;了解&#xff09; 存储系统 ★★★★概述Cache主存编址磁盘管理磁盘基本结构与存取过程磁盘优化分布存储磁盘管理 大家好…

C++:多态-重写和重载

重写&#xff08;Override&#xff09;和重载&#xff08;Overload&#xff09;是面向对象编程中常用的两个概念&#xff0c;它们虽然都涉及到方法的定义&#xff0c;但是在实现和使用上有着不同的特点。 重写&#xff08;Override&#xff09;&#xff1a; 重写是指在子类中重…

部署xwiki服务需要配置 hibernate.cfg.xml如何配置?

1. 定位 hibernate.cfg.xml 文件 首先&#xff0c;确保您可以在 Tomcat 的 XWiki 部署目录中找到 hibernate.cfg.xml 文件&#xff1a; cd /opt/tomcat/latest/webapps/xwiki/WEB-INF ls -l hibernate.cfg.xml如果文件存在&#xff0c;您可以继续编辑它。如果不存在&#xff…

Go语言系统学习笔记(一):基础篇

1. 写在前面 公司的新业务开发需要用到go语言&#xff0c;虽然之前没接触过这门语言&#xff0c;但在大模型的帮助下&#xff0c;边看项目边写代码也能进行go的项目开发&#xff0c;不过&#xff0c;写了一段时间代码之后&#xff0c;总感觉对go语言本身&#xff0c;我的知识体…

【SRC实战】合成类小游戏外挂漏洞

挖个洞先 https://mp.weixin.qq.com/s/ZnaRn222xJU0MQxWoRaiJg “以下漏洞均为实验靶场&#xff0c;如有雷同&#xff0c;纯属巧合” 合成类小游戏三个特点&#xff1a; 1、一关比一关难&#xff0c;可以参考“羊了个羊” 2、无限关卡无限奖励&#xff0c;可以参考“消灭星星…

BBS客户端服务器的编写

根据网络编程中的内容&#xff0c;我们本篇文章将讲解一个bbs通信的项目&#xff0c;首先让我们了解一下什么是bbs. 一、bbs介绍 BBS&#xff0c;即Bulletin Board System的缩写&#xff0c;中文译为“电子公告板系统”或“网络论坛”。它是一个在网络上进行信息交流和讨论的…

【科研】常用的实验结果评价指标(1) —— R2(R-square)是什么?

常用的实验结果评价指标&#xff08;1&#xff09; —— R2(R-square)&#xff0c;可能为负数吗&#xff1f;&#xff01; 提示&#xff1a;先说概念&#xff0c;后续再陆续上代码 文章目录 常用的实验结果评价指标&#xff08;1&#xff09; —— R2(R-square)&#xff0c;可能…

基于WPF的DynamicDataDisplay曲线显示

一、DynamicDataDisplay下载和引用 1.新建项目,下载DynamicDataDisplay引用: 如下图: 二、前端开发: <Border Grid.Row="0" Grid.Column="2" BorderBrush="Purple" BorderThickness="1" Margin="2"><Grid>…

【智能算法应用】基于麻雀搜索算法-支持向量回归预测(SSA-SVR)

目录 1.算法原理2.数学模型3.结果展示4.调试记录5.参考文献6.代码获取 1.算法原理 【智能算法】麻雀搜索算法&#xff08;SSA&#xff09;原理及实现 2.数学模型 支持向量机(SVM)是针对二分类问题&#xff0c;支持向量回归(SVR)基于SVM应用与回归问题。SVR回归与SVM分类的区…

【JVM】了解JVM规范中的虚拟机结构

目录 JVM规范的主要内容 1&#xff09;字节码指令集(相当于中央处理器CPU) JVM指令分类 2&#xff09;Class文件的格式 3&#xff09;数据类型和值 4&#xff09;运行时数据区 5&#xff09;栈帧 6&#xff09;特殊方法 7&#xff09;类库 JVM规范的主要内容 1&#…

Paddle 实现DCGAN

传统GAN 传统的GAN可以看我的这篇文章&#xff1a;Paddle 基于ANN&#xff08;全连接神经网络&#xff09;的GAN&#xff08;生成对抗网络&#xff09;实现-CSDN博客 DCGAN DCGAN是适用于图像生成的GAN&#xff0c;它的特点是&#xff1a; 只采用卷积层和转置卷积层&#x…

如何编译不同目录下的两个文件

1.直接编译 2.打包成动静态库进行链接