【vue】封装树形下拉框组件 el-popover+el-tree+el-select
父组件使用
<template><div>{{ array }} 更多属性详见wgyTreeSelect组件<wgyTreeSelectv-model="array":list="list":multiple="true":disabled-ids="[111,113,2]"/></div>
</template><script>
/* 注意: 默认是这种结构id: 'id', // IDlabel: 'name', // 显示名称children: 'children', // 子级字段名path: 'path', // 路径content: 'content', // 描述pid: 'pid', // 父id如果不是这种结构传入obj定义
*/
export default {data() {return {array: [],//数据源list: [{id: '1', // IDname: '上海市', // 显示名称children: [{id: '2', // IDname: '嘉定区', // 显示名称children: [{id: '3', // IDname: '江桥镇11111111111111111111111111111', // 显示名称content: '嘉定', // 描述pid: '2', // 父id},{id: '4', // IDname: '安亭镇', // 显示名称content: '安亭', // 描述pid: '2', // 父id},], // 子级字段名content: '嘉定', // 描述pid: '1', // 父id},{id: '2000000', // IDname: '嘉定2区', // 显示名称children: [{id: '3876543', // IDname: '江桥2镇11111111111111111111111111111', // 显示名称content: '嘉定', // 描述pid: '2000000', // 父id},], // 子级字段名content: '嘉定', // 描述pid: '1', // 父id},], // 子级字段名content: '上海魔都', // 描述pid: '0', // 父id},{id: '11', // IDname: '北京市', // 显示名称children: [{id: '12', // IDname: '朝阳区', // 显示名称children: [{id: '13', // IDname: '三里屯', // 显示名称content: '三里屯', // 描述pid: '12', // 父id},{id: '111', // IDname: '四里屯--------------', // 显示名称pid: '12', // 父id},{id: '113', // IDname: '五里屯--------------', // 显示名称pid: '12', // 父id},{id: '123', // IDname: '六里屯--------------', // 显示名称pid: '12', // 父id},{id: '11111', // IDname: '七里屯--------------', // 显示名称pid: '12', // 父id},{id: '11311', // IDname: '八里屯--------------', // 显示名称pid: '12', // 父id},{id: '12312', // IDname: '九里屯--------------', // 显示名称pid: '12', // 父id},{id: '11113331', // IDname: '十里屯--------------', // 显示名称pid: '12', // 父id},{id: '11344411', // IDname: '十一里屯--------------', // 显示名称pid: '12', // 父id},{id: '12555312', // IDname: '十二里屯--------------', // 显示名称pid: '12', // 父id},{id: '14', // IDname: '左家庄--------------', // 显示名称content: '左家庄', // 描述pid: '12', // 父id},], // 子级字段名content: '朝阳', // 描述pid: '11', // 父id},], // 子级字段名content: '北京', // 描述pid: '0', // 父id},],};},
}
</script>
子组件
<template><div><el-popoverv-model="isShowSelect"placement="bottom-start":width="popoverWidth":close-on-click-modal="false"trigger="manual"@hide="popoverHide"><el-treeref="tree"v-bind="$attrs"class="common-tree":width="width":data="treeData":props="obj":show-checkbox="multiple":node-key="obj.id":check-strictly="checkStrictly":default-expanded-keys="defaultKeys":expand-on-click-node="multiple&&expandClickNode":check-on-click-node="checkClickNode":highlight-current="true"@check-change="nodeClick"@node-click="nodeClick"/><el-selectslot="reference"ref="select"v-bind="$attrs"v-model="returnDataKeys":size="size":width="width":multiple="multiple":clearable="clearable":collapse-tags="collapseTags"class="tree-select"@click.native="selectClick"@remove-tag="removeTag"@clear="clear"@mouseenter.native="showCloseIcon = true"@mouseleave.native="showCloseIcon = false"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"/><!-- 这里是删除整个下拉框的内容,多选的时候,是没有这个功能的,所有自己弄了一个icon --><templateslot="prefix"><iv-if="multiple && clearable && returnDataKeys.length && showCloseIcon"class="el-icon-close"@click.stop="clearSelectedNodes"></i><i></i></template></el-select></el-popover></div>
</template><script>
export default {name: 'TreeSelect',props: {// 绑定的值value: {tyep: Object,},// 树结构数据list: {type: Array,default: () => ([]),},obj: {type: Object,required: false,default: () => ({id: 'id', // IDlabel: 'name', // 显示名称children: 'children', // 子级字段名path: 'path', // 路径content: 'content', // 描述pid: 'pid', // 父id}),},// 配置是否可多选multiple: {type: Boolean,default: false,},// 配置是否可清空选择,只对单选时生效, 多选时自定义清空按钮clearable: {type: Boolean,default: true,},// 配置多选时是否将选中值按文字的形式展示collapseTags: {type: Boolean,default: false,},// 显示复选框情况下,是否严格遵循父子不互相关联checkStrictly: {type: Boolean,default: false,},// 多选时设置点击节点是否可以选中,false是只有点击多选框才选中,点击元素不选中checkClickNode: {type: Boolean,default: false,},// 多选时:点击节点展开还是点三角标,true是点击节点展开,false是点击三角形展开expandClickNode: {type: Boolean,default: false,},size: {type: String,default: 'small',},width: {type: String,default: '200px',},// 父节点全选时, 是否只展示子节点onlyLeaf: {type: Boolean,default: false,},// 父节点全选时,是否只展示父节点mergeTag: {type: Boolean,default: false,},// 外部点击是否收起树形, 默认收起closeOnOutsideClick: {type: Boolean,default: true,},// 禁止选中的数据disabledIds: {type: Array,default: () => ([]),},},// 上面是父组件可传入参数data() {return {defaultKeys: [], // 默认展开的节点defaultKey: '',first: false, //popoverWidth: '0px', // 下拉框大小isShowSelect: false, // 是否显示树状选择器options: [], // select option选项returnDatas: [], // 返回给父组件数组对象returnDataKeys: [], // 返回父组件数组主键值showCloseIcon: false, // 清空icon};},computed: {treeData() {// 判断传入的数据是不是树形结构const isTreeStructure = JSON.stringify(this.list).indexOf(this.obj.children) !== -1;// 是树形结构,就返回传入的数据, 不是的话格式化成树形结构return isTreeStructure ? this.list : this.toTreeStructure(this.list);},},watch: {// 是否显示树状选择器isShowSelect() {// 隐藏select自带的下拉框this.$refs.select.blur();},// 监听tree数据treeData() {this.$nextTick(() => {this.init();});},// 监听value从新赋值value: {handler(val) {this.$nextTick(() => {if (this.multiple) {this.defaultKeys = val;} else {this.defaultKey = val;}this.init();});},immediate: true,},// 监听选中的值returnDataKeys: {handler(val) {if (this.first || val) {this.first = true;this.$emit('input', val);} else {this.first = true;}},},// 监听禁用数组disabledIds: {handler(val) {console.log(val);this.addDisabledProperty(this.list, val);},},},mounted() {if (this.closeOnOutsideClick) {document.addEventListener('click', this.handleClickOutside);}},beforeDestroy() {if (this.closeOnOutsideClick) {document.removeEventListener('click', this.handleClickOutside);}},methods: {// 传入数据类型不是树形结构,转成树形结构toTreeStructure(list) {console.log(list);const map = {}; let node; const roots = []; leti;for (i = 0; i < list.length; i += 1) {map[list[i].id] = i; // 初始化maplist[i].children = []; // 初始化children}for (i = 0; i < list.length; i += 1) {node = list[i];if (node.pid !== '0') {// 如果有父级list[map[node.pid]].children.push(node);} else {// 如果没有父级,则为根节点roots.push(node);}}return roots;},// 点击其他元素, 树隐藏handleClickOutside(event) {if (!this.$refs.select?.$el?.contains(event.target)) {this.isShowSelect = false;}},init() {// 如果是多选,if (this.multiple) {// 且默认展开的节点大于0if (Array.isArray(this.defaultKeys) && this.defaultKeys.length > 0) {// 检测this.defaultKeys[0]是否是一个对象。if (Object.prototype.toString.call(this.defaultKeys[0]).indexOf('Object') !== -1) { // 对象this.setDatas(this.defaultKeys);// 检测this.defaultKeys[0]是否是一个数字或者字符串。} else if (Object.prototype.toString.call(this.defaultKeys[0]).indexOf('Number') !== -1|| Object.prototype.toString.call(this.defaultKeys[0]).indexOf('String') !== -1) {this.setKeys(this.defaultKeys);} else {console.log('多选:传入参数类型不匹配');}}} else {// 单选if (Object.prototype.toString.call(this.defaultKey).indexOf('Number') !== -1|| Object.prototype.toString.call(this.defaultKey).indexOf('String') !== -1|| Object.prototype.toString.call(this.defaultKey).indexOf('Object') !== -1) {this.setKey(this.defaultKey);} else {console.log('单选:传入参数类型不匹配');}}},// 下拉框select点击[入口]selectClick() {this.isShowSelect = !this.isShowSelect;},// 节点被点击 nodeClick(a, node) {// 单选if (!this.multiple) {this.isShowSelect = false;this.setKey(node.key);// 多选} else {// 所有被选中的节点的 key 所组成的数组数据const checkedKeys = this.$refs.tree.getCheckedKeys();let selectedNodes = checkedKeys.map((item) => {// 所有被选中的节点对应的nodeconst { data, label, key } = this.$refs.tree.getNode(item);return { label, value: key, data };});// 如果onlyLeaf为true,mergeTag为false只保留叶子节点if (this.onlyLeaf && !this.mergeTag) {selectedNodes = selectedNodes.filter((n) => !n.data.children);}// 如果mergeTag为true,onlyLeaf为false只保留父节点if (this.mergeTag && !this.onlyLeaf) {selectedNodes = selectedNodes.filter((n) => {// 判断当前节点是否有父节点if (n.data.pid) {// 判断当前节点的父节点是否也在selectedNodes中const parentInSelectedNodes = selectedNodes.some((upN) => upN.data.id === n.data.pid);// 如果父节点也在selectedNodes中,就将当前节点从selectedNodes中移除return !parentInSelectedNodes;}return true;});}// 设置option选项this.options = selectedNodes;this.returnDataKeys = selectedNodes.map((item) => item.value);this.returnDatas = selectedNodes.map((n) => n.data);}},// 单选:清空选中clear() {this.$refs.tree.setCurrentKey(null);// 清除树选中keythis.returnDatas = null;this.returnDataKeys = '';this.popoverHide();this.isShowSelect = false;},// 单选:设置、初始化值 keysetKey(thisKey) {if (thisKey) {// 设置当前选中的值, 获取当前值对应的节点, 设置当前节点this.$refs.tree.setCurrentKey(thisKey);const node = this.$refs.tree.getNode(thisKey);this.setData(node.data);}},// 单选:设置、初始化对象setData(data) {this.options = [];this.options.push({ label: data[this.obj.label], value: data[this.obj.id] });this.returnDatas = data;this.returnDataKeys = data[this.obj.id];},// 多选:设置、初始化值 keyssetKeys(checkedKeys) {// 给树状选择器设置选中的节点。 给select赋值this.$refs.tree.setCheckedKeys(checkedKeys);this.returnDataKeys = checkedKeys;const selectedNodes = checkedKeys.map((item) => {// 所有被选中的节点对应的nodeconst node = this.$refs.tree.getNode(item);return { label: node.label, value: node.key, data: node.data };});this.returnDatas = selectedNodes.map((node) => node.data);this.popoverHide();},// 多选:设置、初始化对象 setDatas(data) {// 获取选中节点的主键值const checkedKeys = data.map((item) => item[this.obj.id]);// 设置树状选择器的选中节点this.$refs.tree.setCheckedKeys(checkedKeys);// 设置返回给父组件的数组对象this.returnDatas = data;this.returnDataKeys = checkedKeys;this.popoverHide();},// 多选模式下,当删除选中的节点时,将该节点及其子节点设置为未选中状态,并更新选中的节点。 okremoveTag(val) {// 获取删除的节点const node = this.$refs.tree.getNode(val);// 判断获取的节点是否为叶子节点const isLeafNode = node.childNodes.length === 0;// 是叶子节点,那么只需要将该节点设置为未选中状态// 不是叶子节点,那么需要将该节点及其所有子节点设置为未选中状态const nodesToUncheck = isLeafNode ? [node] : this.treeToList(node);// 将每个节点设置为未选中状态。nodesToUncheck.forEach((n) => this.$refs.tree.setChecked(n, false));// 更新选中的节点。this.nodeClick();this.popoverHide();},// 下拉框关闭执行popoverHide() {this.$emit('getValue', this.returnDataKeys, this.returnDatas);},// 多选,清空所有被选中的节点clearSelectedNodes() {this.$refs.tree.setCheckedKeys([]);},// 树形转为集合treeToList(tree) {let queen = [];const out = [];queen = queen.concat(tree);while (queen.length) {const first = queen.shift();if (first.childNodes) {queen = queen.concat(first.childNodes);}out.push(first);}return out;},// 禁用某个节点addDisabledProperty(data, ids) {data.forEach((item) => {if (ids.includes(+item.id)) {item.disabled = true;}if (item.children) {this.addDisabledProperty(item.children, ids);}});},},
};
</script><style scoped lang="scss">
::v-deep .el-input__prefix{position: absolute;height: 100%;right: 5px;.el-icon-close {position: absolute;right: 6px;top: 11px;z-index: 1;border-radius:50%;color: #fff;background-color: #B0B3B8;}
}.mask{height: 100%;position: fixed;top: 0;left: 0;opacity: 0;z-index: 11;}.common-tree{overflow: auto;}.tree-select{position: relative;z-index: 111;}.ok{float: right;}.el-row{padding-top: 0px !important;}
</style>
相关文章:

【vue】封装树形下拉框组件 el-popover+el-tree+el-select
父组件使用 <template><div>{{ array }} 更多属性详见wgyTreeSelect组件<wgyTreeSelectv-model"array":list"list":multiple"true":disabled-ids"[111,113,2]"/></div> </template><script> /*…...

docker安装Kafka,SpringBoot整合Kafka
#拉取镜像 docker pull wurstmeister/zookeeper #运行容器 docker run --restartalways --name zookeeper -p 2181:2181 \ -v /etc/localtime:/etc/localtime -d wurstmeister/zookeeper#拉取镜像 docker pull wurstmeister/kafka#运行容器 docker run --restartalways --name …...

MySQL - InnoDB 的事务支持、锁机制
InnoDB 存储引擎实现了强大的事务支持和锁机制: 事务原理: ACID 属性:InnoDB 支持 ACID(原子性、一致性、隔离性、持久性)属性,确保数据的一致性和可靠性。事务是一组 SQL 操作,要么全部执行&…...

Android Studio的笔记--Module新建和使用
Module新建和使用 新建module使用module android studio 中module的建立和使用。比如修改工程为module的步骤,引用module的步骤。 新建module 1、新建android工程,New Project.包名为com.lxh.serialport 2、修改工程为module。 2.1、在app下的build.pro…...

Git统计个人提交代码行数
目录 一、git bash打开二、查看个人提交的代码行数统计三、查看项目每个人提交的代码行数统计四、查询所有用户的提交总次数五、统计用户一段时间内的提交代码量 在实际开发中,常常会想查看自己对于某个项目的贡献,管理者会查看项目下各成员的贡献&#…...

Collction的List方法,list特有方法,遍历方式,迭代器选择
[to] list特有方法 //插入指定元素//list.add(1,"ddd");//System.out.println(list);//[aaa, ddd, bbb, ccc]//这个表示在一索引的位置插入ddd//他会把原来一索引位置的元素往后移动一位在添加//删除指定元素//String remove list.remove(1);//System.out.println(…...

奇偶校验码和循环冗余码
在数据链路层的传输中,1可能变成0,0可能变成1,这是比特差错。 为了应对比特差错,有两种方式,即自动重传请求ARQ(Automatic Repeat-reQuest)和前向纠错FEC(Forward Error Correction&…...

Recommender System复习(考试向)
Recommender System Review OverviewCollaborative Filtering基于用户的CF(User CF)基于物品的CF(Item CF)Similarity CalculationBias in CF Evaluation of Recommender SystemFactorization MachinesLatent factor modelLFM算法…...

SpringBoot绑定配置文件中变量的四种方式-解析
当在Spring Boot中需要绑定配置文件中的变量时,可以使用以下注解: PropertySource:用于指定要加载的属性文件。可以将该注解放置在Configuration类上。 Configuration PropertySource("classpath:application.properties") public…...

linux 安装 elasticsearch 全教程
一、去 elasticsearch官网找到Linux版本的下载链接 地址https://www.elastic.co/cn/downloads/elasticsearch 二、在linux 中用wget下载 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.10.4-linux-x86_64.tar.gz三、下载成功后解压文件 tar -x…...

更快更准 | YOLOv3算法超详细解析(包括诞生背景+论文解析+技术原理等)
前言:Hello大家好,我是小哥谈。YOLOv3是一种基于深度学习的目标检测算法,它可以快速而准确地在图像中检测出多个目标。它是由Joseph Redmon和Ali Farhadi在2018年提出的,是YOLO(You Only Look Once)系列算法…...

【ChatGPT】教我 Flyweight(享元) 模式
文章目录 设计模式中 Flyweight 模式,实际应用场景有哪些?我需要画很多的树,以这个需求为例来教我 Flyweight 模式。好的,我大概明白了,我还有点疑惑,请问外在状态和内在状态是哪些?你可以讲一下…...

双十一越来越早,公域电商何去何从——深度解析
为什么双十一越来越早?传统的公域电商越来越饱和,某淘某东甚至和某79元网红打起了价格战 ,市面上标品越来越多,商家越来越多,买东西的变少了,商家越卖越便宜。 公域电商: 就像一个大市场。里面摆满了各种商…...

区块链中的 “不可能三角,技术性ppt突出关键技术点,数据交易-安全问题
目录 区块链中的 "不可能三角 技术性ppt突出关键技术点 数据交易-安全问题...

[Docker]五.Docker中Dockerfile详解
Dockerfile 就是用来构建 docker 镜像的构建文件 1.Dockerfile 构建一个自己的 centos 镜像 新建一个名为 Dockerfile 文件,并在文件内添加以下内容: #基于centos构建镜像 FROM centos #安装net-tools软件: RUN表示安装软件 RUN yum install -y net-tools…...

react使用react-sortable-hoc实现拖拽
react-sortable-hoc拖拽 安装 npm install react-sortable-hoc --save 代码如下(示例): import React, { useImperativeHandle, forwardRef, memo, useState } from react;import { DrawerForm } from ant-design/pro-form;import { messag…...

实习日常的点点滴滴记录(Spring Boot的核心注解)------慢慢积累,厚积薄发(要学的东西还好多,加油!))(知识和实践的结合)
Spring Boot的核心注解有: **SpringBootApplication:**这是Spring Boot最核心的注解,用于开启Spring Boot的各项能力。 它其实是SpringBootConfiguration、EnableAutoConfiguration、ComponentScan这三个注解的组合。 Configuration…...

第 04 章_逻辑架构
第 04 章_逻辑架构 1. 逻辑架构剖析 1. 1 服务器处理客户端请求 那服务器进程对客户端进程发送的请求做了什么处理,才能产生最后的处理结果呢?这里以查询请求为 例展示: 下面具体展开看一下: 1.2 Connectors 1.3 第 1 层&…...

免费API接口资源推荐
企业基本信息(含联系方式):通过公司名称/公司ID/注册号或社会统一信用代码获取企业基本信息和企业联系方式,包括公司名称或ID、类型、成立日期、电话、邮箱、网址等字段的详细信息。企业投资:获取企业对外投资信息,对外投资信息包…...

LuatOS-SOC接口文档(air780E)--miniz - 简易zlib压缩
示例 -- 准备好数据 local bigdata "123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw" -- 压缩之, 压缩得到的数据是zlib兼容的,其他语言可通过zlib相关的库进行解压 local cdata miniz.compress(bigdata) -- lua 的 字符串相当于有长度的cha…...

一整套智慧工地源码,劳务实名制、施工安全管理、绿色施工、危大工程设备监测、视频AI识别功能
智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术,以PC端,移动端,设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等…...

世微 DC-DC平均电流双路降压恒流驱动器 LED车灯AP2813
产品描述 AP2813 是一款双路降压恒流驱动器,高效率、外 围简单、内置功率管,适用于 5-80V 输入的高精度降 压 LED 恒流驱动芯片。内置功率管输出最大功率可达 12W,最大电流 1.2A。 AP2813 一路直亮,另外一路通过 MODE1 切换 全亮,…...

ES6~ES13新特性(一)
文章目录 一、ES6中对象的增强1.字面量的增强2.解构Destructuring3.解构的应用场景 二、手写实现apply、call、bind方法1.apply、call方法2.bind方法 三、ECMA新描述概念1.新的ECMA代码执行描述2.词法环境(Lexical Environments)3.词法环境和变量环境4.环…...

基于 Amazon EC2 和 Amazon Systems Manager Session Manager 的堡垒机的设计和自动化实现
文章目录 1. 背景2. 云上堡垒机设计2.1 安全设计2.2 高可用和弹性设计2.3 监控告警设计2.4 自动化部署设计2.4.1 堡垒机代码设计2.4.2 Session Manager 配置设计2.4.3 堡垒机 IAM 角色设计 3. 部署堡垒机3.1 堡垒机部署架构图3.2 堡垒机自动化部署 4. 堡垒机使用场景4.1 堡垒机…...

RFID管理方案有效提升电力物资管理效率与资产安全
在电力行业,电力资产的管理是一项重要的任务,为了实现对电力资产的精细化管理、入出库监控管理、盘点管理和巡查管理等,电力公司多采用电力资产RFID管理系统,该系统能够实时监控出入库过程,有效防止出入库错误…...

leetcode:389. 找不同
一、题目 函数原型:char findTheDifference(char * s, char * t) 二、思路 作者原先的思路是先将两个字符串从小到大排序,然后两个字符串依次比较。若出现字符串t中的元素和字符串s不相等,则说明该元素就是被添加的字母。 但是,该…...

c 函数调用过程中,调用函数的栈帧一旦被修改,被调用函数则无法正确返回。( X )
当一个函数被调用时,它的栈帧会被创建并压入调用栈中。栈帧包含了函数的局部变量、参数以及返回地址等信息。当函数执行完毕后,栈帧会被弹出,返回到调用函数的位置继续执行。 下面是一个示例代码,展示了调用函数栈帧被修改但不影…...

专为个人打造专注工作的便签APP工具推荐哪个
工作中很多人都比较懒散,工作起来动力不足,常常拖延消极怠工,等到一天结束后进行工作盘点时才发现很多项任务都没有处理完;这和日常工作不能专注于工作有很大的关系。 专注工作,在日常办公时可以选择一些好用的手机便…...

代码随想录算法训练营第四十二天 | LeetCode 1049. 最后一块石头的重量 II、494. 目标和、474. 一和零
代码随想录算法训练营第四十二天 | LeetCode 1049. 最后一块石头的重量 II、494. 目标和、474. 一和零 文章链接:最后一块石头的重量 II 目标和 一和零 视频链接:最后一块石头的重量 II 目标和 一和零 1. LeetCode 1049. 最后一块石头的重量 II 1.1 思路…...

Windows PowerShell 和 Linux BashShell 极简对比
声明:本文不会涉及原理,详细的介绍,也不是入门文章。仅仅从使用上进行简单比较 命令 在 bash 中,一个命令是一个单独的进程;而在 PowerShell 中,命令被称为 cmdlets,他们不是独立的可执行程序&…...