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

需求背景

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

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

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

最终效果

大致思路:

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 = "";
            }
        },
    },
}

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

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

相关文章

调用nvprof报错: No kernels were profiled. No API activities were profiled.

调用nvprof报错 1 nvprof介绍 nvprof 是 NVIDIA 提供的一款用于分析 CUDA 应用程序性能的命令行性能分析器。CUDA 是一种并行计算平台和编程模型&#xff0c;允许开发人员利用 NVIDIA GPU 进行通用处理。 nvprof 帮助开发人员分析其 CUDA 应用程序的性能&#xff0c;提供各种…

以gitee为例的git入门使用指北

安装git 在linux中我们首先需要使用 sudo apt install git来下载git 在windows中可以下载msysGit 链接&#xff1a;https://git-scm.com/download/win gitee准备 申请账号 建立仓库 ​ 点击新建仓库 这里一般是私有库&#xff0c;点击创建&#xff0c;这时你就拥有一个线上…

Freeswitch-mod开发

文章目录 一、Freeswitch-mod开发1.1 介绍1.2 实战1.2.1 新建一个mymod.c或者mymod.cpp1.2.2 新建一个Makefile1.2.3 编译 二、Freeswitch-mod-自定义Dialplan模块2.1 介绍2.2 实战2.2.1 改造mymod.c&#xff08;代码是完整的&#xff0c;自己做区别看一下&#xff09;2.2.2 编…

小白入门:创建一个SpringBoot项目

前言 我们在创建SpringBoot项目时候&#xff0c;会出现不确定和报错的情况很多&#xff0c;大家可以按照我的做法来简单创建一个SpringBoot项目 1.环境配置 下载安装并配置jdk1.8下载apache mavenidea软件 2.开始创建项目 Server URL&#xff1a;初始是start.spring.io,我…

ComfyUI中的图像稀释处理

用下面的节点对图片进行稀释处理&#xff0c;如下 为0表示不变&#xff0c;我设置大一点&#xff0c;设置为0.5看看&#xff0c;如下 图像就暗淡了一些&#xff0c;但是还是有一些彩色的&#xff0c;相当于把它放在水里浸泡了一样&#xff0c;掉色了&#xff0c;这就是稀释&…

美国访问学者J1签证申请,这些关键事项一定要注意!

美国访问学者准备踏上学术探索之旅&#xff0c;申请J1签证是决定性的一步。以下是美国J1签证申请的一些关键注意事项&#xff0c;帮助你顺利通过签证流程&#xff1a; 1、签证类型&#xff1a;确认J-1签证是适合您访问学者身份的正确签证类型。 2、DS-2019表&#xff1a;在申…

2024------MySQL数据库基础知识点总结

-- 最好的选择不是最明智的&#xff0c;而是最勇敢的&#xff0c;最能体现我们真实意愿的选择。 MySQL数据库基础知识点总结 一、概念 数据库&#xff1a;DataBase&#xff0c;简称DB。按照一定格式存储数据的一些文件的组合顾名思义: 存储数据的仓库&#xff0c;实际上就是一…

工业光源环形系列一高均匀条形光源特点

产品特点 ◆可以根据检测需求随意调整照射角度&#xff1a; ◆可以根据检测需求选择光源颜色&#xff1a; ◆多个条形光源可以自由组合&#xff1a; ◆使用贴片灯珠&#xff0c;均匀性更好。

ICDE2024 |VDTuner:向量数据库自动调优技术

在CodeFuse接入实际业务的过程中&#xff0c;大模型的推理成本以及生成内容的准确性是产品规模落地的两个核心考量因素。为了降低推理成本&#xff0c;我们研发了CodeFuse-ModelCache语义缓存加速功能&#xff0c;通过引入Cache机制&#xff0c;缓存已经计算的结果&#xff0c;…

Spring Security基础教程:从入门到实战

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

Windows系统安装MySQL数据库详细教程

【确认本地是否安装mysql】 &#xff08;1&#xff09;按【winr】快捷键打开运行&#xff1b; &#xff08;2&#xff09;输入services.msc&#xff0c;点击【确定】&#xff1b; &#xff08;3&#xff09;在打开的服务列表中查找mysql服务&#xff0c;如果没有mysql服务&am…

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第16课-跳转私信

【WEB前端2024】开源智体世界&#xff1a;乔布斯3D纪念馆-第16课-跳转私信 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&am…

猫咪没精神不吃饭?这招主食冻干喂养教你让猫咪恢复食欲

猫突然不吃东西没精神是生病了吗&#xff1f;当猫咪不吃东西、精神不振时&#xff0c;可能的原因有健康问题、环境因素或食物原因。首先应进行身体检查&#xff0c;观察是否有其他并发症&#xff0c;如无则可排除健康问题。还要给猫咪提供一个舒适的环境&#xff0c;多给它们一…

Elsevier旗下双1区TOP刊,8.8分影响因子加上超低自引率,各指标领跑计算机类SCI

【SciencePub学术】 今天小编给大家带来了一本计算机类的高分优刊解读&#xff0c;隶属于Elsevier出版社&#xff0c;JCR1区&#xff0c;中科院1区TOP&#xff0c;影响因子高达8.7&#xff0c;领域相符的学者可考虑&#xff01; APPLIED SOFT COMPUTING 1 期刊概况 【期刊简…

从零开始搭建Springboot项目脚手架2:配置文件、返回值、日志等

1、多个环境与配置文件 2、统一返回值 返回值包括两种场景&#xff1a;正常controller的返回、异常发生之后返回 正常controller的返回&#xff1a;通过在controller的默认返回Response实现 异常发生之后返回&#xff1a;通过全局异常处理统一捕获返回 首先创建类StatusCode…

windows安装ElasticSearch以及踩坑

1.下载 elasticsearch地址&#xff1a;Past Releases of Elastic Stack Software | Elastichttps://www.elastic.co/cn/downloads/past-releases#elasticsearch IK分析器地址&#xff1a;infinilabs/analysis-ik: &#x1f68c; The IK Analysis plugin integrates Lucene IK…

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

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

如何把视频转成文字稿?6个软件教你快速转换视频成文字

如何把视频转成文字稿&#xff1f;6个软件教你快速转换视频成文字 将视频转换为文字稿是一种有效的方式&#xff0c;可以帮助您提取视频中的信息并进行整理、编辑。以下是六个软件和工具&#xff0c;可以帮助您快速转换视频为文字稿&#xff1a; Google文档语音输入&#xf…

05-06 周一 Shell工程目录划分和开发最佳实践

05-06 周一 Shell工程目录划分和开发最佳实践 时间版本修改人描述2024年5月6日10:34:13V0.1宋全恒新建文档2024年5月6日11:07:12V1.0宋全恒完成 简介 之前楼主曾经完成过一个shell工程的开发&#xff0c;记得当时项目名称叫做campus-shell&#xff0c;主要是用来一键完成多个模…

高刚性滚柱直线导轨有哪些应用场景?

滚柱型滚动体取代了滚珠&#xff0c;由滚柱与导轨和滑块的线接触方式&#xff0c;滚柱导轨能在高负载的情况下仍然保持刚性值和高精度。为实现超高刚性和超重负荷能力而设计&#xff0c;整体滚柱导轨达到四方向等高刚性、高负载、高精度能力的特点&#xff0c;非常适合高速自动…