当前位置: 首页 > article >正文

elementplus el-tree 二次封装支持配置删除后展示展开或折叠编辑复选框懒加载功能

本文介绍了基于 ElementPlus 的 el-tree 组件进行二次封装的 TreeView 组件,使用 Vue3 和 JavaScript 实现。TreeView 组件通过 props 接收树形数据、配置项等,支持懒加载、节点展开/收起、节点点击、删除、编辑等操作。组件内部通过 ref 管理树实例,并提供了 clearCurrentNode、setCurrentKey、setExpandedKeys 等方法供父组件调用。renderContent 方法用于自定义节点内容,支持根据配置显示删除和编辑按钮。事件处理函数通过 emit 将节点操作传递给父组件,实现了组件与父组件的交互。样式部分通过 scoped 样式隔离,确保组件样式独立。

准备组件 TreeView treeUtils方法

  1. TreeView组件
<template><div class="tree-container"><div v-if="isShowHeader" class="tree-header"><slot name="header"></slot></div><el-tree ref="treeRef" :data="treeData" :props="treeProps" highlight-current node-key="id":render-content="renderContent" :lazy="lazy" :load="lazy ? loadNode : undefined":default-expanded-keys="expandedKeys" :show-checkbox="showCheckbox" :check-strictly="checkStrictly"@node-click="handleNodeClick" @node-expand="handleNodeExpand" @node-collapse="handleNodeCollapse"@check="handleCheck" /></div>
</template><script setup>
import { defineProps, defineEmits, ref } from 'vue'
import { Delete, Edit } from '@element-plus/icons-vue'
import { handleNodeExpand as handleNodeExpandUtil, handleNodeCollapse as handleNodeCollapseUtil } from '@/utils/treeUtils'// 接收父组件传来的数据
const props = defineProps({treeData: {type: Array,required: true,},treeProps: {type: Object,default: () => ({children: 'children',label: 'label',isLeaf: 'isLeaf'})},showDelete: {type: Boolean,default: false},showEdit: {type: Boolean,default: false},lazy: {type: Boolean,default: false},isShowHeader: {type: Boolean,default: false},showCheckbox: {type: Boolean,default: false},checkStrictly: {type: Boolean,default: false}
})
const applicationYear = ref('')// 接收父组件传来的事件
const emit = defineEmits(['nodeClick', 'loadChildren', 'deleteNode', 'nodeExpand', 'nodeCollapse','check'
])// 使用props中的treeProps
const { treeProps } = props// 添加treeRef
const treeRef = ref(null)// 展开的节点keys
const expandedKeys = ref([])// 添加取消选中节点的方法
const clearCurrentNode = () => {if (treeRef.value) {treeRef.value.setCurrentKey(null)}
}// 设置当前选中的节点
const setCurrentKey = (key) => {if (treeRef.value) {treeRef.value.setCurrentKey(key)}
}// 设置展开的节点
const setExpandedKeys = (keys) => {expandedKeys.value = [...keys]
}// 获取当前展开的节点
const getExpandedKeys = () => {return expandedKeys.value
}// 处理复选框选中事件
const handleCheck = (data, { checkedKeys, checkedNodes }) => {emit('check', {data,checkedKeys,checkedNodes})
}// 获取选中的节点
const getCheckedKeys = () => {return treeRef.value?.getCheckedKeys() || []
}// 获取半选中的节点
const getHalfCheckedKeys = () => {return treeRef.value?.getHalfCheckedKeys() || []
}// 设置选中的节点
const setCheckedKeys = (keys) => {treeRef.value?.setCheckedKeys(keys)
}// 暴露方法给父组件
defineExpose({clearCurrentNode,setCurrentKey,setExpandedKeys,getExpandedKeys,getCheckedKeys,getHalfCheckedKeys,setCheckedKeys
})const renderContent = (hFn, { node, data }) => {const content = [hFn('span', data[props.treeProps.label] || data.label)]// 根据showDelete配置决定是否显示删除按钮if (props.showDelete) {content.push(hFn('el-button',{type: 'danger',size: 'small',class: 'delete-btn',onClick: () => handleDeleteNode(node, data),},[hFn(Delete)]))}// 根据showDelete配置决定是否显示修改按钮if (props.showEdit) {content.push(hFn('el-button',{type: 'danger',size: 'small',class: 'edit-btn',onClick: () => handleEditNode(data),},[hFn(Edit)]))}return hFn('div',{ class: 'tree-node' },content)
}// 加载子节点数据
const loadNode = (node, resolve) => {if (!props.lazy) {return resolve([])}if (node.level === 0) {// 根节点直接返回初始数据return resolve(props.treeData)}// 触发父组件的事件来获取子节点数据emit('loadChildren', {node,resolve: (children) => {// 确保children是数组const childNodes = Array.isArray(children) ? children : []// 将子节点数据设置到当前节点的children属性中if (node.data) {node.data.children = childNodes}resolve(childNodes)}})
}// 处理节点点击事件
const handleNodeClick = (data, node) => {emit('nodeClick', data)
}// 处理删除节点事件
const handleDeleteNode = (node, data) => {emit('deleteNode', { node, data })
}// 处理修改节点事件
const handleEditNode = (nodeData) => {emit('editNode', nodeData)
}// 处理节点展开
const handleNodeExpand = (data, node) => {expandedKeys.value = handleNodeExpandUtil({data,node,expandedKeys: expandedKeys.value,onExpand: (data) => emit('nodeExpand', data)})
}// 处理节点收起
const handleNodeCollapse = (data, node) => {expandedKeys.value = handleNodeCollapseUtil({data,expandedKeys: expandedKeys.value,onCollapse: (data) => emit('nodeCollapse', data)})
}
</script><style scoped>
.tree-container {height: 100%;border: 1px solid #e4e7ed;padding: 10px;overflow: auto;
}::v-deep(.tree-node .delete-btn) {display: none !important;
}::v-deep(.tree-node .edit-btn) {display: none !important;
}::v-deep(.tree-node:hover) {color: skyblue;
}::v-deep(.tree-node:hover .delete-btn) {width: 14px;display: inline-block !important;color: red;margin-left: 5px;transform: translateY(2px);
}::v-deep(.tree-node:hover .edit-btn) {width: 14px;display: inline-block !important;color: rgb(17, 0, 255);margin-left: 5px;transform: translateY(2px);
}.tree-header {border-bottom: 1px solid #e4e7ed;margin-bottom: 10px;
}
</style>
  1. treeUtils.js文件
import { nextTick } from 'vue'/*** 处理树节点展开* @param {Object} options 配置选项* @param {Object} options.data 节点数据* @param {Object} options.node 节点对象* @param {Array} options.expandedKeys 展开节点数组* @param {Function} options.onExpand 展开回调函数* @returns {Array} 更新后的展开节点数组*/
export const handleNodeExpand = ({data,node,expandedKeys,onExpand
}) => {// 如果节点ID不在展开数组中,则添加if (!expandedKeys.includes(data.id)) {expandedKeys.push(data.id)}// 确保父节点也保持展开状态let parent = node.parentwhile (parent && parent.data && parent.data.id) {if (!expandedKeys.includes(parent.data.id)) {expandedKeys.push(parent.data.id)}parent = parent.parent}// 调用展开回调if (onExpand) {onExpand(data)}return expandedKeys
}/*** 处理树节点收起* @param {Object} options 配置选项* @param {Object} options.data 节点数据* @param {Array} options.expandedKeys 展开节点数组* @param {Function} options.onCollapse 收起回调函数* @returns {Array} 更新后的展开节点数组*/
export const handleNodeCollapse = ({data,expandedKeys,onCollapse
}) => {// 从展开数组中移除节点IDconst index = expandedKeys.indexOf(data.id)if (index > -1) {expandedKeys.splice(index, 1)}// 调用收起回调if (onCollapse) {onCollapse(data)}return expandedKeys
}/*** 处理树节点删除后的展开状态* @param {Object} options 配置选项* @param {Object} options.node 要删除的节点* @param {Object} options.data 节点数据* @param {Array} options.treeData 树数据* @param {Function} options.getExpandedKeys 获取展开节点的方法* @param {Function} options.setExpandedKeys 设置展开节点的方法* @param {Function} options.clearCurrentNode 清除当前选中节点的方法* @returns {Promise<void>}*/
export const handleTreeDelete = async ({node,data,treeData,getExpandedKeys,setExpandedKeys,clearCurrentNode
}) => {const parent = node.parentconst children = parent.data.children || parent.dataconst index = children.findIndex((d) => d.id === data.id)// 获取当前展开的节点const currentExpandedKeys = getExpandedKeys()// 删除节点children.splice(index, 1)// 强制刷新treeDatatreeData.value = JSON.parse(JSON.stringify(treeData.value))// 重新设置展开状态await nextTick()// 确保父节点保持展开状态if (parent && parent.data && parent.data.id) {if (!currentExpandedKeys.includes(parent.data.id)) {currentExpandedKeys.push(parent.data.id)}}clearCurrentNode()setExpandedKeys(currentExpandedKeys)return currentExpandedKeys
} 

父组件使用

  1. 导入组件
import TreeView from '@/components/basicComponents/TreeView'
  1. 使用组件
<TreeView ref="treeViewRef":treeData="treeData" :treeProps="customTreeProps" :showDelete="true" :lazy="true":default-expanded-keys="expandedKeys"@nodeClick="handleNodeClick" @deleteNode="handleNodeDelete"@loadChildren="handleLoadChildren"@nodeExpand="handleNodeExpand"@nodeCollapse="handleNodeCollapse"/>
  1. 父组件里使用方法
// 定义treeViewRef
const treeViewRef = ref(null)
const treeData = ref([]) //树数据
const expandedKeys = ref([]) // 添加展开节点的key数组
// 自定义树形配置
const customTreeProps = {children: 'children',  // 子节点字段名label: 'label',        // 使用label字段作为显示文本isLeaf: 'isLeaf'       // 是否为叶子节点字段名
}
const handleLoadChildren = async ({ node, resolve }) => {try {const children = await fetchTreeChildrenData(node.data.id)resolve(children)} catch (error) {console.error('加载子节点失败:', error)resolve([]) // 加载失败时返回空数组}
}
// 获取树子节点数据 懒加载 格式化数据
const fetchTreeChildrenData = async (id = '') => {const { data } = await getZhuangBeiCategory( id )const formattedChildren = data.map(item => ({id: item.id,label: item.label,  // 添加label字段isLeaf: item.sonNum > 0 ? false : true,  // 修正isLeaf的逻辑children: [] // 初始化为空数组,等待后续加载}))if(id) return formattedChildrentreeData.value = formattedChildren
}
//删除子节点
const handleNodeDelete = ({node, data}) => {ElMessageBox.confirm(`<div style="text-align: center;">确定要删除【${data.label}】吗?</div>'提示',{dangerouslyUseHTMLString: true,confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(async() => {try{await deleteZhuangBeiCategory(data.id)ElMessage({  type: 'success',  message: '删除成功!'})await handleTreeDelete({node,data,treeData,getExpandedKeys: () => treeViewRef.value.getExpandedKeys(),setExpandedKeys: (keys) => treeViewRef.value.setExpandedKeys(keys),clearCurrentNode: () => treeViewRef.value.clearCurrentNode()})}catch{ElMessage({  type: 'error',  message: '删除失败!'})}}).catch(() => {// 取消了,不做处理})
}
// 处理节点展开
const handleNodeExpand = (data) => {if (!expandedKeys.value.includes(data.id)) {expandedKeys.value.push(data.id)}
}// 处理节点收起
const handleNodeCollapse = (data) => {const index = expandedKeys.value.indexOf(data.id)if (index > -1) {expandedKeys.value.splice(index, 1)}
}// 处理节点点击
const handleNodeClick = (nodeData) => {
}
  • 其他方法比如复选框,编辑不在示例,感兴趣的可以去试试

相关文章:

elementplus el-tree 二次封装支持配置删除后展示展开或折叠编辑复选框懒加载功能

本文介绍了基于 ElementPlus 的 el-tree 组件进行二次封装的 TreeView 组件&#xff0c;使用 Vue3 和 JavaScript 实现。TreeView 组件通过 props 接收树形数据、配置项等&#xff0c;支持懒加载、节点展开/收起、节点点击、删除、编辑等操作。组件内部通过 ref 管理树实例&…...

Pycharm IDEA加载大文件时报错:The file size exceeds configured limit

解决方案&#xff1a;配置一下idea.properties文件 文件里面写入代码&#xff1a; idea.max.intellisense.filesize50000重启IDEA即可&#xff1b;...

free void* 指令

https://stackoverflow.com/questions/2182103/is-it-ok-to-free-void free(ptr) 仅释放指针指向的内存&#xff0c;不会修改指针变量本身的值。调用后&#xff0c;ptr 仍然指向原来的地址&#xff08;称为 "悬空指针"&#xff09;&#xff0c;但该地址对应的内存已…...

PDA手持终端应用有哪些?

随着技术进步不断拓展&#xff0c;PDA手持终端其便携性与多功能特性使其成为多行业数字化转型的核心工具。主要包括物流与仓储管理、零售行业、医疗行业以及制造业等。 1.物流与仓储管理 在物流与仓储管理中&#xff0c;PDA手持终端主要用于物品的实时跟踪、库存管理和拣货作业…...

Python模块化编程

Python模块化编程 记得我刚学Python那会儿&#xff0c;特别喜欢把所有代码都写在一个文件里。直到有一天&#xff0c;我的项目膨胀到了2000多行代码&#xff0c;每次修改都要翻半天…这才痛定思痛&#xff0c;开始研究模块化编程。今天就跟大家聊聊这个让代码变得优雅的魔法。…...

Linux性能分析工具perf

perf 工具详解 perf&#xff08;Performance Counters for Linux&#xff09;是 Linux 系统上的一个强大的性能分析工具&#xff0c;用于监控和分析系统及应用程序的性能。它基于 Linux 内核的 Performance Event Subsystem&#xff08;perf_events&#xff09;&#xff0c;能…...

Android开发-使用内容组件获取通讯信息

在Android开发中&#xff0c;访问和处理用户的通讯信息&#xff08;如联系人、通话记录等&#xff09;是一项常见的需求。通过使用Android的内容提供者&#xff08;ContentProvider&#xff09;&#xff0c;开发者可以方便地查询这些数据&#xff0c;并将其集成到自己的应用中。…...

文件目录与检索综合练习题

文章目录 前言一、基础部分二、参数应用三、参数进阶四、组合应用五、归档压缩六、统计与分析总结 前言 这部分练习题帮助大家更好的掌握命令 一、基础部分 1.用grep在error.log中查找所有含"Timeout"的行 2.使用find在/var/log下搜索7天内修改过的.log文件 3.对da…...

Python+Selenium爬虫:豆瓣登录反反爬策略解析

1. 引言 在当今互联网时代&#xff0c;数据抓取&#xff08;爬虫&#xff09;技术广泛应用于数据分析、市场调研、自动化测试等领域。然而&#xff0c;许多网站采用动态加载技术&#xff08;如Ajax、React、Vue.js等框架&#xff09;来渲染页面&#xff0c;传统的**<font s…...

信息系统运行管理员:临阵磨枪版

信息系统运行管理员考试 - 全覆盖详细背诵大纲 (根据考情分析和原始材料&#xff0c;力求完整覆盖考点细节) 第一部分&#xff1a;基础知识与运维概览 Chapter 1: 信息系统运维概述 (上午题 5分) 信息&#xff1a; 含义&#xff1a;香农 - 减少随机不确定性的东西&#xff1b…...

电总协议调试助手更新-PowerBus-v1.0.5

电总协议调试助手&#xff0c;该工具主要是用于打包电总协议&#xff0c;用于电总协议的设备调试&#xff08;精密空调、UPS、基站电源等等&#xff09;。电总协议校验计算、编码转换比较麻烦&#xff0c;手动组包困难&#xff0c;使用该工具可以大大提高调试效率。 Ver1.0.5版…...

技术文档:变频器干扰问题与解决方案

1. 引言 在现代工业自动化系统中&#xff0c;变频器&#xff08;Variable Frequency Drive, VFD&#xff09;因其高效节能和精确调速的特点被广泛应用于电机控制。然而&#xff0c;变频器在运行过程中会产生高频电磁干扰&#xff08;EMI&#xff09;&#xff0c;对周边设备如P…...

2025认证杯数学建模C题思路+代码+模型:化工厂生产流程的预测和控制

2025认证杯数学建模C题思路代码模型&#xff0c;详细内容见文末名片 在化工厂的生产流程中&#xff0c;往往涉及到多个反应釜、管道和储罐等设备。在 流水线上也有每个位置的温度、压力、流量等诸多参数。只有参数处于正常范 围时&#xff0c;最终的产物才是合格的。这些参数…...

亚马逊,temu测评采购低成本养号策略:如何用一台设备安全批量管理买家账号

只要能够巧妙规避平台的检测和风控措施&#xff0c;测评便可安全进行。 自养号测评&#xff0c;它更便于卖家掌控&#xff0c;且能降低风险。现在很多卖家都是自己养号&#xff0c;自己养号都是精养&#xff0c;不是自动的机刷&#xff0c;买家账号掌握在自己手里&#xff0c;更…...

SiFli-SDK 编译

1.编译报错 scons: *** No SConstruct file found. 出现这个错误是没有正确进入到工程目录执行编译命令&#xff0c;例如应该进入project目录中。 2.scons: *** [build_em-lb525_hcpu\src\resource\strings\en_us.c] AttributeError : dict object has no attribute iteritem…...

React 第四十一节Router 中 useActionData 使用方法案例以及注意事项

一、useActionData前言 useActionData 是 React Router 提供的一个钩子函数&#xff0c;用于获取在路由的 action 函数中返回的数据。它通常与表单提交&#xff08;通过 <Form> 组件&#xff09;配合使用&#xff0c;用于处理表单提交后的服务器响应数据&#xff08;如错…...

C++多态实现的必要条件剖析

在C中&#xff0c;多态的一个必要条件确实是通过基类的指针或引用调用虚函数。这一要求背后的原因与C如何实现动态绑定&#xff08;运行时多态&#xff09;密切相关。下面详细解释了为什么需要使用基类的指针或引用来实现多态。 动态绑定与静态绑定 静态绑定&#xff08;编译期…...

C语言_自动义类型:联合和枚举

1. 联合体 1.1 联合体类型的声明 与结构体相似&#xff0c;联合体也是有一个或多个成员&#xff08;可以是不同类型&#xff09;构成&#xff1b;但是编译器只为最大的成员分配足够的内存空间 联合体的特点是所有成员共用同一块内存空间&#xff0c;所以联合体也叫&#xff…...

汽车紧固件涂层18问:看敦普无铬锌铝涂料如何为螺丝防锈防腐

导读 在汽车紧固件防锈涂装领域&#xff0c;敦普牌紧固件无铬锌铝涂料&#xff0c;是专为汽车紧固件打造的水性涂料&#xff0c;集防锈、环保、高性价比于一体。它有何独特之处&#xff1f;让我们一探究竟。​ 1、敦普紧固件无铬锌铝涂料是什么产品&#xff1f; 敦普紧固件无铬…...

掘金中亚货代蓝海,易境通货代系统解锁数字化制胜密码!

2025年&#xff0c;中亚地区正成为全球物流行业的新蓝海。中亚五国因其独特的地缘位置和“一带一路”倡议的深化推进&#xff0c;正逐渐成为全球物流行业的战略要地。 在政策红利、基建升级与市场需求的叠加效应下&#xff0c;中亚物流市场预计在2025年迎来爆发式增长。但传统…...

Python内存管理:赋值、浅拷贝与深拷贝解析

赋值与共享资源 在Python中&#xff0c;直接赋值操作&#xff08;如 list2 list1&#xff09;会导致两个变量共享同一个内存地址。这意味着对 list1 的修改会直接影响到 list2&#xff0c;因为它们指向同一个对象。 注意: 赋值等于完全共享资源 如果我们不希望这样完全共享&…...

W1R3S: 1.0.1靶场

W1R3S: 1.0.1 来自 <W1R3S: 1.0.1 ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.249 3&#xff0c;对靶机进行端口…...

深度学习-分布式训练机制

1、分布式训练时&#xff0c;包括train.py的全部的代码都会在每个gpu上运行吗&#xff1f; 在分布式训练&#xff08;如使用 PyTorch 的 DistributedDataParallel&#xff0c;DDP&#xff09;时&#xff0c;每个 GPU 上运行的进程会执行 train.py 的全部代码&#xff0c;但通过…...

[Mamba轻量化]DefMamba: Deformable Visual State Space Model,CVPR2025

paper 文章目录 AbstractMethod整体模型架构可变形状态空间模型 Experiments Abstract 然而&#xff0c;大多数现有的视觉Mamba方法使用预定义的扫描顺序将图像展平为1D序列&#xff0c;导致模型在特征提取过程中对图像空间结构信息的利用能力减弱。为解决这一问题&#xff0…...

CSRF 和 XSS 攻击分析与防范

CSRF 和 XSS 攻击分析与防范 CSRF (跨站请求伪造) 什么是 CSRF&#xff1f; CSRF (Cross-Site Request Forgery) 是一种攻击方式&#xff0c;攻击者诱使用户在已登录目标网站的情况下&#xff0c;执行非预期的操作。 攻击流程&#xff1a; 用户登录可信网站 A在不登出 A 的…...

找银子 题解(c++)

题目 思路 首先&#xff0c;这道题乍一看&#xff0c;应该可以用搜索来做。 但是&#xff0c;搜索会不会超时间限制呢&#xff1f; 为了防止时间超限,我们可以换一种做法。 先创立两个二维数组&#xff0c;一个是输入的数组a&#xff0c;一个是数组b。 假设 i 行 j 列的数…...

中国版 Cursor?腾讯推出 AI 编程助手 CodeBuddy,重新定义编程体验

人工智能&#xff08;AI&#xff09;技术的迅猛发展正深刻地变革着各个行业&#xff0c;编程领域也不例外。以原生 IDE 体验见长的 Cursor&#xff0c;凭借其 Agent 模式在 AI 编程领域脱颖而出&#xff0c;对 GitHub Copilot 以及 VS Code 与 JetBrains 全家桶的地位发起挑战。…...

JVM学习专题(二)内存模型深度剖析

目录 1.JVM结构体系 ​编辑 2.跨平台特性 3.JVM整体结构及内存模型 1.栈内存 1、栈帧&#xff1a; 1.局部变量表 2.操作数栈 3.动态链接 4.方法出口 2、创建对象 2.程序计数器&#xff1a; 3.方法区 ​4.堆 5.本地方法区 6.总结 1.JVM结构体系 JDK、JRE 和 JVM…...

密码学实验:凯撒密码

密码学实验&#xff1a;凯撒密码 一、实验目的 掌握凯撒密码的数学原理&#xff1a;理解字符移位与模运算的结合&#xff0c;实现加解密算法。理解暴力破解本质&#xff1a;通过穷举有限密钥空间&#xff0c;掌握利用语言特征破解密文的方法。编程实践&#xff1a;用Python实…...

linux备份与同步工具rsync

版权声明&#xff1a;原创作品&#xff0c;请勿转载&#xff01; 文章目录 版权声明&#xff1a;原创作品&#xff0c;请勿转载&#xff01; 实验环境介绍&#xff1a; 1.工具介绍 2.详细介绍 2.1 本地模式&#xff08;用得少&#xff09; 2.2 远程模式 2.3 守护进程模式…...