vue2 , el-select 多选树结构,可重名

人家antd都支持,elementplus 也支持,vue2的没有,很烦。

网上其实可以搜到各种的,不过大部分不支持重名,在删除的时候可能会删错,比如树结构1F的1楼啊,2F的1楼啊这种同时勾选的情况。。

可以全路径

干净点不要全路径也可以,

一股脑全放,可能有一点无效代码,懒得删了,等出bug再说

<template><!-- <t-tree-select:options="treeList"placeholder="请选择tree结构"width="50%":defaultData="defaultValue":treeProps="treeProps"@handleNodeClick="selectDrop"
/> --><el-selectref="select"v-model="displayValues":multiple="multiple":filter-method="dataFilter"@remove-tag="removeTag"@clear="clearAll"popper-class="t-tree-select":style="{width: width||'100%'}"v-bind="attrs"v-on="$listeners" popper-append-to-bodyclass="select-tree"><el-option v-model="selectTree" class="option-style" disabled  ><div class="check-box" v-if="multiple&&checkBoxBtn"><el-button type="text" @click="handlecheckAll">{{checkAllText}}</el-button><el-button type="text" @click="handleReset">{{resetText}}</el-button><el-button type="text" @click="handleReverseCheck">{{reverseCheckText}}</el-button></div> <el-tree:data="options":props="treeProps"class="tree-style"ref="treeNode":check-strictly="true":show-checkbox="multiple":node-key="treeProps.value":filter-node-method="filterNode":default-checked-keys="defaultValue":current-node-key="currentKey"@node-click="handleTreeClick"@check-change="handleNodeChange"v-bind="treeAttrs"v-on="$listeners"></el-tree></el-option></el-select>
</template><script>
export default {name: 'TTreeSelect',props: {// 多选默认值数组defaultValue: {type: Array,default: () => []},// 单选默认展示数据必须是{id:***,label:***}格式defaultData: {type: Object},// 全选文字checkAllText: {type: String,default: '全选'},// 清空文字resetText: {type: String,default: '清空'},// 反选文字reverseCheckText: {type: String,default: '反选'},// 可用选项的数组options: {type: Array,default: () => []},// 配置选项——>属性值为后端返回的对应的字段名treeProps: {type: Object,default: () => ({value: 'value', // ID字段名label: 'title', // 显示名称children: 'children' // 子级字段名})},// 是否显示全选、反选、清空操作checkBoxBtn: {type: Boolean,default: false},// 是否多选multiple: {type: Boolean,default: true},// 选择框宽度width: {type: String},// 是否显示完整路径, 如 1楼-1f-101showFullPath: {type: Boolean,default: true}},data() {return {selectTree: this.multiple ? [] : '', // 绑定el-option的值currentKey: null, // 当前选中的节点filterText: null, // 筛选值VALUE_NAME: this.treeProps.value, // value转换后的字段VALUE_TEXT: this.treeProps.label, // label转换后的字段selectedNodes: [] // 存储选中的完整节点信息}},computed: {attrs() {return {'popper-append-to-body': false,clearable: true,filterable: true,...this.$attrs}},// tree属性treeAttrs() {return {'default-expand-all': true,...this.$attrs}},// 显示值:根据showFullPath决定显示内容displayValues() {if (this.multiple) {return this.selectedNodes.map(node => this.showFullPath ? this.getNodePath(node) : node[this.VALUE_TEXT])}const firstNode = this.selectedNodes[0]if (!firstNode) return ''return this.showFullPath ? this.getNodePath(firstNode) : firstNode[this.VALUE_TEXT]}},watch: {defaultValue: {handler() {this.$nextTick(() => {// 多选if (this.multiple) {let datalist = this.$refs.treeNode.getCheckedNodes()this.selectTree = datalistthis.selectedNodes = [...datalist]}})},deep: true},// 对树节点进行筛选操作filterText(val) {this.$refs.treeNode.filter(val)}},mounted() {this.$nextTick(() => {const scrollWrap = document.querySelectorAll(".el-scrollbar .el-select-dropdown__wrap")[0];const 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;});});if (this.multiple) {let datalist = this.$refs.treeNode.getCheckedNodes()this.selectTree = datalistthis.selectedNodes = [...datalist]}// 有defaultData值才回显默认值if (this.defaultData?.id) {this.setDefaultValue(this.defaultData)}},methods: {// 获取节点的完整路径getNodePath(node) {const path = []let currentNode = node// 向上查找父节点,构建路径while (currentNode) {path.unshift(currentNode[this.VALUE_TEXT])currentNode = this.findParentNode(currentNode, this.options)}return path.join('-')},// 查找父节点findParentNode(targetNode, nodes, parent = null) {for (let node of nodes) {if (node[this.VALUE_NAME] === targetNode[this.VALUE_NAME]) {return parent}if (node.children && node.children.length > 0) {const found = this.findParentNode(targetNode, node.children, node)if (found !== null) {return found}}}return null},// 单选设置默认值setDefaultValue(obj) {if (obj.label !== '' && obj.id !== '') {this.selectTree = obj.idthis.selectedNodes = [{ [this.VALUE_NAME]: obj.id, [this.VALUE_TEXT]: obj.label }]this.$nextTick(() => {this.currentKey = this.selectTreethis.setTreeChecked(this.selectTree)})}},// 全选handlecheckAll() {setTimeout(() => {this.$refs.treeNode.setCheckedNodes(this.options)}, 200)},// 清空handleReset() {setTimeout(() => {this.$refs.treeNode.setCheckedNodes([])}, 200)},/*** @description: 反选处理方法* @param {*} nodes 整个tree的数据* @param {*} refs  this.$refs.treeNode* @param {*} flag  选中状态* @param {*} seleteds 当前选中的节点* @return {*}*/batchSelect(nodes, refs, flag, seleteds) {if (Array.isArray(nodes)) {nodes.forEach(element => {refs.setChecked(element, flag, true)})}if (Array.isArray(seleteds)) {seleteds.forEach(node => {refs.setChecked(node, !flag, true)})}},// 反选handleReverseCheck() {setTimeout(() => {let res = this.$refs.treeNodelet nodes = res.getCheckedNodes(true, true)this.batchSelect(this.options, res, true, nodes)}, 200)},// 输入框关键字dataFilter(val) {setTimeout(() => {this.filterText = val}, 100)},/*** @description: tree搜索过滤* @param {*} value 搜索的关键字* @param {*} data  筛选到的节点* @return {*}*/filterNode(value, data) {if (!value) return truereturn data[this.treeProps.label].toLowerCase().indexOf(value.toLowerCase()) !== -1},/*** @description: 勾选树形选项* @param {*} data 该节点所对应的对象* @param {*} self 节点本身是否被选中* @param {*} child 节点的子树中是否有被选中的节点* @return {*}*/// 多选赋值组件handleNodeChange(data, self, child) {let datalist = this.$refs.treeNode.getCheckedNodes()this.$nextTick(() => {this.selectTree = datalistthis.selectedNodes = [...datalist]this.$emit('handleNodeClick', this.selectTree)})},// 单选tree点击赋值handleTreeClick(data, node) {if (this.multiple) {} else {this.filterText = ''this.selectTree = data[this.VALUE_NAME]this.selectedNodes = [data]this.currentKey = this.selectTreethis.highlightNode = data[this.VALUE_NAME]this.$emit('handleNodeClick', { id: this.selectTree, label: data[this.VALUE_TEXT] }, node)this.setTreeChecked(this.highlightNode)this.$refs.select.blur()}},setTreeChecked(highlightNode) {if (this.treeAttrs.hasOwnProperty('show-checkbox')) {// 通过 keys 设置目前勾选的节点,使用此方法必须设置 node-key 属性this.$refs.treeNode.setCheckedKeys([highlightNode])} else {// 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性this.$refs.treeNode.setCurrentKey(highlightNode)}},// 移除单个标签removeTag(displayText) {let nodeIndex = -1if (this.showFullPath) {// 完整路径模式:根据完整路径精确匹配nodeIndex = this.selectedNodes.findIndex(node => this.getNodePath(node) === displayText)} else {// 普通模式:根据文本匹配,删除最后一个匹配项nodeIndex = this.selectedNodes.map(node => node[this.VALUE_TEXT]).lastIndexOf(displayText)}if (nodeIndex !== -1) {const nodeToRemove = this.selectedNodes[nodeIndex]// 从selectedNodes中移除this.selectedNodes.splice(nodeIndex, 1)// 从selectTree中移除对应的节点const treeNodeIndex = this.selectTree.findIndex(v => v[this.VALUE_NAME] === nodeToRemove[this.VALUE_NAME])if (treeNodeIndex !== -1) {this.selectTree.splice(treeNodeIndex, 1)}// 更新树的选中状态this.$nextTick(() => {this.$refs.treeNode.setCheckedNodes(this.selectTree)})this.$emit('handleNodeClick', this.selectTree)}},// 文本框清空clearAll() {this.selectTree = this.multiple ? [] : ''this.selectedNodes = []this.$refs.treeNode.setCheckedNodes([])this.$emit('handleNodeClick', this.selectTree)}}}
</script><style scoped lang="scss">
.t-tree-select {.check-box {padding: 0 20px;}.option-style {height: 100%;max-height: 300px;margin: 0;overflow-y: auto;cursor: default !important;}.tree-style {::v-deep .el-tree-node.is-current > .el-tree-node__content {color: #3370ff;}}.el-select-dropdown__item.selected {font-weight: 500;}.el-input__inner {height: 36px;line-height: 36px;}.el-input__icon {line-height: 36px;}.el-tree-node__content {height: 32px;}}
</style>
<style lang="scss" scoped>
::v-deep .el-tree{background: #262F40 !important;color: #FFFFFF;
}
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {height: auto;max-height: 300px;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;
}::v-deep .el-tree-node__content:hover,
::v-deep .el-tree-node__content:active,
::v-deep .is-current > div:first-child,
::v-deep .el-tree-node__content:focus {background-color: rgba(#333F52, 0.5);color: #409eff;
}
::v-deep .el-tree-node__content:hover {background-color: rgba(#333F52, 0.5);color: #409eff;
}::v-deep .el-tree-node:focus>.el-tree-node__content{background-color: rgba(#333F52, 0.5);}
.el-popper {z-index: 9999;
}.el-select-dropdown__item::-webkit-scrollbar {display: none !important;
}.el-select {::v-deep.el-tag__close {// display: none !important; //隐藏在下拉框多选时单个删除的按钮}
}
</style><style lang="scss"  >
.select-tree {.el-tag.el-tag--info{color: #fff;border-color:none;background: #273142 !important;}.el-icon-close:before{color: rgba(245, 63, 63, 1); }.el-tag__close.el-icon-close{background-color: transparent !important;}
}
</style>

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

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

相关文章

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…

【一文看懂Spring循环依赖】Spring循环依赖:从陷阱破局到架构涅槃

&#x1f32a;️ Spring Boot循环依赖&#xff1a;从陷阱破局到架构涅槃 循环依赖如同莫比乌斯环上的蚂蚁&#xff0c;看似前进却永远困在闭环中。本文将带你拆解Spring中这一经典难题&#xff0c;从临时救火到根治重构&#xff0c;构建无懈可击的依赖体系。 &#x1f525; 一、…

el-table封装自动滚动表格(适用大屏)

表格功能&#xff1a;自动滚动&#xff0c;鼠标移入停止滚动&#xff0c;移出继续滚动。如果想加触底加载新数据可以判断 scrollWrap.scrollTop和maxScrollTop大小来加载数据&#xff0c;另写逻辑。 <template><el-table ref"eltable" :data"tableDa…

Eureka REST 相关接口

可供非 Java 应用程序使用的 Eureka REST 操作。 appID 是应用程序的名称&#xff0c;instanceID 是与实例关联的唯一标识符。在 AWS 云中&#xff0c;instanceID 是实例的实例 ID&#xff1b;在其他数据中心&#xff0c;它是实例的主机名。 对于 XML/JSON&#xff0c;HTTP 的…

DSP——时钟树讲解

配置任何外设的第一步都要看一下时钟树,下图是DSP28377的时钟树: 由图所示DSP28377由4个时钟源,分别是INTOSC1、INTOSC2、XTAL、AUXCL INTOSC1:0M内部系统时钟,备用时钟,检测到系统时钟缺失自动连接到备用时钟,也作为看门狗时钟使用; INTOSC2:10M内部系统时钟,复位…

少量数据达到更好效果

九坤团队新作&#xff01;一条数据训练AI超越上万条数据 一 仅需一条无标签数据和10步优化 九坤团队训练了13,440个大模型&#xff0c;发现熵最小化 (EM) 仅需一条无标签数据和10步优化&#xff0c;就能实现与强化学习中使用成千上万条数据和精心设计的奖励机制所取得的性能提…

html - <mark>标签

<mark> 标签在HTML中用于高亮显示文本&#xff0c;通常用于突出显示某些重要的部分。它的默认样式通常是背景色为黄色&#xff0c;但你可以通过CSS自定义其外观。 1. 基本用法 <mark> 标签用于标记文本的高亮显示。它常用于搜索结果中&#xff0c;突出显示匹配的…

YOLOv8+ByteTrack:高精度人车过线统计系统搭建指南

文章目录 1. 引言2. YOLOv8简介3. 过线统计原理4. 代码实现4.1 环境准备4.2 基础检测代码4.3 过线统计实现4.4 完整代码示例5. 性能优化与改进5.1 多线程处理5.2 区域检测优化5.3 使用ByteTrack改进跟踪6. 实际应用中的挑战与解决方案7. 总结与展望1. 引言 目标检测是计算机视…

20、React常用API和Hook索引

这一小节中只给出一些API和Hook的索引&#xff0c;需要用到的时候可以去官网查询&#xff0c;如无必要此处不列出详细用法。React v1.19.1。 对Components的支持 以下是开发时通用的一些功能组件 APIdescription<Fragment>通常使用 <>…</> 代替&#xff0…

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…

HTML实现的2048游戏

以下是一个纯HTML实现的2048游戏代码&#xff0c;包含CSS和JavaScript&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>2048 Game</title><style>body {font-family: Arial, sans-serif;text-a…

使用Python 构建支持主流大模型与 Ollama 的统一接口平台

🧩 背景概述 近年来,随着大语言模型(LLM)的蓬勃发展,OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude、以及开源的 Ollama 本地模型等,逐渐成为自然语言处理、智能问答、AI 助手等应用的基础组件。 开发者在使用这些模型时常面临如下问题: 各模型接口不统…

计算机系统概述(4)

计算机系统层次结构&#xff1a;硬件层、系统层、应用层。 计算机的基本硬件系统由运算器、控制器、存储器、输入设备和输出设备5大部件组成。 运算器、控制器等部件被集成在一起统称为中央处理单元CPU。 存储器是计算机系统中的记忆设备&#xff0c;分为内部存储器和外部存…

Linux 下的COW机制(copy-on-write)

Linux通过MMU进行虚拟地址到物理地址的转换&#xff0c;当进程执行fork()后&#xff0c;会把页中的权限设置为RD-ONLY&#xff08;只读&#xff09;。 MMU&#xff08;内存管理单元&#xff09; MMU本质是一个集成在CPU核心的硬件电路模块&#xff0c;其核心任务是实现…

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践

01技术背景与业务挑战 某短视频点播企业深耕国内用户市场&#xff0c;但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大&#xff0c;传统架构已较难满足当前企业发展的需求&#xff0c;企业面临着三重挑战&#xff1a; ① 业务&#xff1a;国内用户访问海外服…

开发Vue.js组件的二三事

Vue.js作为一款渐进式JavaScript框架&#xff0c;其组件化开发模式是其核心优势之一。在多年的Vue开发实践中&#xff0c;我积累了一些组件开发的经验和思考&#xff0c;在此与大家分享。 组件设计原则 单一职责原则 每个组件应该只关注一个特定的功能或UI部分。如果一个组件…

实现多路视频截图预览之后上传到后台系统

********************父组件********************** <div class"camera-box" v-loading"i.loading"> <div class"camera-box-inner" v-for"(x, y) in i.children" :key"y children x.featureCode" v-show"…

分布式锁-Redisson实现

目录 本地锁的局限性 Redisson解决分布式锁问题 在分布式环境下&#xff0c;分布式锁可以保证在多个节点上的并发操作时数据的一致性和互斥性。分布式锁有多种实现方案&#xff0c;最常用的两种方案是&#xff1a;zookeeper和redis&#xff0c;本文介绍redis实现分布式锁方案…

【办公类-48-04】202506每月电子屏台账汇总成docx-5(问卷星下载5月范围内容,自动获取excel文件名,并转移处理)

背景需求&#xff1a; 1-4月电子屏表格&#xff0c;都是用这个代码将EXCEL数据整理成分类成3个WORD表格。 【办公类-48-04】20250118每月电子屏台账汇总成docx-4&#xff08;提取EXCLE里面1月份的内容&#xff0c;自制月份文件夹&#xff09;-CSDN博客文章浏览阅读1.2k次&…

【websocket】安装与使用

websocket安装与使用 1. 介绍2. 安装3. websocketpp常用接口4. Websocketpp使用4.1 服务端4.2 客户端 1. 介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的 消息推送机制。 传统的 web 程序都是属于 “一问一答” 的形式&#xff0c;即客户端给服务器发送…