【vue】el-tree的新增/编辑/删除节点

1、概述

关于树形结构的新增同级节点新增子级节点修改节点名称删除节点等四种操作,各种参数配置完全继承el-tree,本篇使用vue2 + element-ui

2、效果图展示

3、调用方式

<template><Tree:data="treeData":props="defaultProps":default-expanded-keys="expandedKeys"node-key="id"highlight-current:highligh-color="highlighColor":show-btn-group="showBtnGroup":draggable="true"@node-click="nodeClick"@editNodeSubmit="addNode"@deleteNodeSubmit="deleteNode"@node-drag-end="handleDragEnd"/>
</template>      <script>
export default {name: 'Tree',data() {return {defaultProps: {children: 'children',label: 'label'},expandedKeys: [], // 展开节点highlighColor: {color: '#FFAE0D',bgColor: 'rgba(255, 174, 13, .1)'},showBtnGroup: true,treeData: []}}
}
</script>

4、源码解析

<template><el-treeref="tree":class="['menu-el-tree',iconStyle.src && 'menu-expand-icon',highlighColor && 'menu-node-highligh']":style="{'--bgUrl': 'url(' + iconStyle.src + ')','--iconWidth': iconStyle.width,'--iconHeight': iconStyle.height,'--iconTransform': iconStyle.transform,'--hoverColor': hoverStyle.color,'--hoverBgColor': hoverStyle.bgColor,'--highlighColor': highlighColor.color,'--highlighBgColor': highlighColor.bgColor}"node-key="id":draggable="draggable"v-bind="$attrs"v-on="$listeners"><template v-slot="{ node, data }"><div v-if="!data.isEdit" class="custom-tree-node-root" @mouseenter="nodeMouseEnter(node)" @mouseleave="nodeMouseLeve(node)"><span class="custom-tree-node">{{ node.label }}</span><section v-show="node.showBtn" class="tree-btn-group"><el-tooltipv-for="item in btnGroup":key="item"effect="light"placement="top"popper-class="common-tooltip-primary":content="{'addChild': '新增子级','delete': '删除','edit': '修改','addSibling': '新增同级'}[item]"><i:class="{'addChild': 'el-icon-circle-plus-outline','delete': 'el-icon-circle-close','edit': 'el-icon-edit','addSibling': 'el-icon-plus'}[item]"@click.stop="() => handleOperaion(item, node, data)"/></el-tooltip></section></div><div v-else class="custom-tree-node-root-edit"><el-input v-model="inputValue" :size="editInputStyle.size" :style="{ width: editInputStyle.width }" /><section :id="data.id"><el-tooltipcontent="确定"popper-class="common-tooltip-primary"effect="light"placement="top"><i class="el-icon-success" @click.stop="() => editMenuSubmit(node, data)" /></el-tooltip><el-tooltipcontent="取消"popper-class="common-tooltip-primary"effect="light"placement="top"><i class="el-icon-error" @click.stop="() => addSiblingCancel(node, data)" /></el-tooltip></section></div></template></el-tree>
</template>
<script>
export default {name: 'MenuTree',inheritAttrs: true,props: {hoverStyle: {type: Object,default: () => ({color: '#606266',bgColor: '#F5F7FA'})},/*** @description 当前行选中高亮颜色配置*/highlighColor: {type: Object,default: () => ({color: '#606266',bgColor: '#fff9ec'})},/*** @description 动态左侧图标配置(图片格式)*/iconStyle: {type: Object,default: () => ({src: '',width: '20px',height: '17px',transform: 'rotate(270deg)'})},/*** @description 操作按钮组* addChild => 新增子级,delete => 删除, edit => 修改, addSibling => 新增同级*/btnGroup: {type: Array,default: () => ['edit', 'addSibling', 'addChild', 'delete']},/*** @description 是否展示操作按钮*/showBtnGroup: {type: Boolean,default: false},/*** @description 编辑模式输入框样式*/editInputStyle: {type: Object,default: () => ({size: 'mini',width: '200px'})},/*** 是否可拖拽*/draggable: {type: Boolean,default: false}},data() {return {inputValue: ''}},methods: {/*** @description 鼠标移入目录层级*/nodeMouseEnter(data) {if (!this.showBtnGroup) return;this.$set(data, 'showBtn', true)},/*** @description 鼠标移除目录层级*/nodeMouseLeve(data) {if (!this.showBtnGroup) return;this.$set(data, 'showBtn', false)},/*** 当前目录操作分发* @param {string} eventKey 事件名称* @param {object} currentNode 当前Node节点内容* @param {object} data 当前节点数据*/handleOperaion(eventKey, currentNode, data) {console.log(currentNode, data, 'menuRoot');switch (eventKey) {case 'addSibling':this.addSibling(currentNode, data);break;case 'edit':this.editTreeItem(data);break;case 'addChild':this.addChild(currentNode, data);break;case 'delete':this.deleteTreeItem(currentNode, data);break;}},/*** @description 编辑当前节点*/editTreeItem(data) {this.$set(data, 'isEdit', true);this.inputValue = data.label; // 当前正在编辑内容赋值this.$nextTick(() => {document.getElementById(data.id).previousElementSibling.firstElementChild.focus();})},/*** @description 添加同级节点*/addSibling(currentNode, data) {const treeDOM = this.$refs.tree;const id = Math.ceil(Math.random() * 100)const newData = { id: id, pId: data.pId, label: '', isEdit: true, isNew: true, children: [] };treeDOM.insertAfter(newData, currentNode);// 聚焦当前新增目录this.$nextTick(() => {document.getElementById(newData.id).previousElementSibling.firstElementChild.focus();})},/*** @description 添加子级节点*/addChild(currentNode, data) {const treeDOM = this.$refs.tree;const id = Math.ceil(Math.random() * 100)const newData = { id: id, pId: data.id, label: '', isEdit: true, isNew: true, children: [] };treeDOM.append(newData, currentNode);// 展开子节点后才能获取DOM聚焦treeDOM.store.nodesMap[data.id].expanded = true;setTimeout(() => {document.getElementById(newData.id).previousElementSibling.firstElementChild.focus();}, 500)},/*** @description 编辑模式修改确认*/editMenuSubmit(node, data) {this.$emit('editNodeSubmit', {node,data,currentLabel: this.inputValue,// 新增同级/子级节点接口调用成功的话,即新增同级/子级节点callback: (status) => {if (status) {this.resetNode()}}});},/*** @description 删除当前节点*/deleteTreeItem(node, data) {this.$emit('deleteNodeSubmit', {node,data,callback: (status) => {// 删除接口调用成功的话,即删除节点if (status) {const treeDOM = this.$refs.tree;treeDOM.remove(node);}}});},/*** @description 取消同级节点添加*/addSiblingCancel(node, data) {// 如果是新增的节点,取消即是删除if (data.isNew) {const treeDOM = this.$refs.tree;treeDOM.remove(node);} else {// 重置修改内容this.inputValue = '';data.isEdit = false;}},/*** 寻找第一个叶子节点及叶子节点的父节点* @param {*} tree 平铺数组*/findFirstChildAndParent(tree) {let firstChild = null;let parentOfFirstChild = null;const dfs = (node, parent) => {if (firstChild !== null) {return; // 如果已经找到了第一个子节点,则不再继续搜索}if (node.children && node.children.length > 0) {// eslint-disable-next-linefor (const child of node.children) {dfs(child, node);}} else {firstChild = node;parentOfFirstChild = parent;}}// eslint-disable-next-linefor (const node of tree) {dfs(node, null);}return {firstChild,parentOfFirstChild};},/*** 获取自身树结构实例*/getTree() {return this.$refs.tree;},/*** 重置节点数据*/resetNode() {this.inputValue = '';},/*** 寻找节点对应的父级节点* @param {*} tree* @param {*} nodeId*/findParentByChildId(tree, nodeId) {let parentOfFirstChild = null;const dfs = (node, parent) => {if (parentOfFirstChild !== null) {return;}if (node.children && node.children.length > 0) {// eslint-disable-next-linefor (const child of node.children) {dfs(child, node);}} else {// 找到对应节点后,返回其父节点if (node.id === nodeId) {parentOfFirstChild = parent;}}}// eslint-disable-next-linefor (const node of tree) {dfs(node, null);}return parentOfFirstChild}}
}
</script>
<style scoped lang="scss">
// 动态配置右侧图标
.menu-expand-icon {::v-deep .el-tree-node__expand-icon:not(.is-leaf) {&::before {background: var(--bgUrl);background-size: contain;background-repeat: no-repeat;background-position: center;content: '';width: var(--iconWidth);height: var(--iconHeight);display: inline-block;transform: var(--iconTransform);}}
}
// 动态配置hover样式
.menu-el-tree {::v-deep .el-tree-node__content {&:hover {background: var(--hoverBgColor);color: var(--hoverColor);}}.custom-tree-node-root {display: flex;align-items: center;flex: 1;.tree-btn-group {margin-left: 15px;display: flex;align-items: center;column-gap: 8px;i {font-size: 15px;&:hover {color: $primary}}}}
}
.menu-node-highligh.el-tree--highlight-current {::v-deep .el-tree-node.is-current>.el-tree-node__content {background: var(--highlighBgColor);color: var(--highlighColor);}
}
.custom-tree-node-root-edit {display: flex;align-items: center;.el-input, ::v-deep .el-input .el-input__inner {height: 26px;}section {i {margin-left: 10px;font-size: 15px;color: $primary;}}
}
</style>

5、疑难解答

1、拖拽节点时,如何监听和区分拖拽事件?

使用的是el-tree内置的node-drag-end事件,当拖拽完成时触发,抛出四个参数

* @param {*} draggingNode 被拖拽的节点

 * @param {*} dropNode 最后进入的节点

 * @param {*} dropType 相对比的位置

 * @param {*} ev 事件

2、新增和删除节点,如何再触发后端接口后再执行节点的新增和删除?

节点新增和删除执行下列两个事件

@editNodeSubmit="addNode"

@deleteNodeSubmit="deleteNode"

他们回调的最后一个参数都是callback,当callback(true)传入为true时,则代表接口成功,则进行后续的树节点操作。

------有不懂的或建议欢迎直接评论噢~------

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

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

相关文章

椋鸟数据结构笔记#11:排序·下

文章目录 外排序&#xff08;外部排序&#xff09;文件拆分并排序归并文件两个文件归并多文件归并优化 萌新的学习笔记&#xff0c;写错了恳请斧正。 外排序&#xff08;外部排序&#xff09; 当数据量非常庞大以至于无法全部写入内存时&#xff0c;我们应该怎么排序这些数据呢…

Route Discovery Route Repair

1. Route Discovery 当单播消息从一台设备发送到另一台设备&#xff0c;并且没有预先存在的路由时&#xff0c;会发起路由发现。 我们假设没有现有的路由&#xff0c;因此网络软件将开始路由发现过程。为简单起见&#xff0c;假设所有设备的路由表都为空。 以设备A向设备C发送…

[lesson49]多态的概念和意义

多态的概念和意义 函数重写回顾 父类中被重写的函数依然会继承给子类 子类中重写的函数将覆盖父类中的函数 通过作用域分辨符(::)访问父类中的同名成员 多态的概念和意义 面向对象中期望的行为 根据实际的对象类型判断如何调用重写函数父类指针(引用)指向 父类对象则调用…

【LeetCode】---118.杨辉三角

一、题目解析&#xff1a; 二、知识回顾&#xff1a; 1.二维数组&#xff1a; 2. C语言中的二维数组访问方式和vector二维数组的访问&#xff0c; 不同区别&#xff1a; &#xff08;1&#xff09;表面是一样的&#xff0c;但底层不同&#xff01; &#xff08;2&#xff09;静…

JAVA 项目<果园之窗>_2

上节主要是理论流程&#xff0c;这次直接用实际例子过一遍整个流程 目标是向数据库添加一个员工 上述是前端页面&#xff0c;点击保存 浏览器向我后端发送http请求 后端这一部分专门接收employee请求 在这里对http post请求进行转换成JAVA数据&#xff0c;并处理数据&#xff…

在linux系统中启动pycharm

1.找到pycharm的安装路径&#xff0c;一般在下载文件夹中 2.进入pycharm的安装路径&#xff0c;进入bin目录 3.右击&#xff0c;打开终端&#xff0c;输入./pycharm.sh

奇妙的探索——偶然发现的bug

今天想在腾讯招聘官网找几个前端的岗位投一下&#xff0c;最近自己也在找工作&#xff0c;结果简历还没有投出去&#xff0c;就发现了腾旭招聘官网的3个前端bug。 1.有时候鼠标hover还没有滑倒下拉选框的菜单上&#xff0c;就消失了&#xff0c;消失的太快了&#xff0c;根本点…

为智算产业高质量发展探寻路径,又一重要生态合作启动

4月22日&#xff0c;由中国工业经济联合会主办的“2024中国工业经济高峰论坛智能算力产业高质量发展论坛”落幕。院士专家、研究机构、以及来自智能算力产业上下游企业代表近180人出席&#xff0c;围绕完善算力基础设施、深化算力赋能行业应用、推动区域数字化发展等热点议题展…

Linux加强篇-Vim编辑器

目录 ⛳️推荐 Vim文本编辑器 编写简单文档 配置主机名称 配置网卡信息 配置软件仓库 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Vim文本编辑器 在Linux系统中一切都…

重磅发布 | 《网络安全专用产品指南》(第一版)

2017年6月1日&#xff0c;《中华人民共和国网络安全法》正式实施&#xff0c;明确规定“网络关键设备和网络安全专用产品应当按照相关国家标准的强制性要求&#xff0c;由具备资格的机构安全认证合格或者安全检测符合要求后&#xff0c;方可销售或者提供。国家网信部门会同国务…

区区几行Python代码,就能实现全面自动探索性数据分析!

探索性数据分析是数据科学模型开发和数据集研究的重要组成部分之一。在拿到一个新数据集时首先就需要花费大量时间进行EDA来研究数据集中内在的信息。自动化的EDA Python包可以用几行Python代码执行EDA。在本文中整理了10个可以自动执行EDA并生成有关数据的见解的Python包&…

ROM修改进阶教程------安卓7_____安卓13去除签名验证操作步骤解析

同类博文: 安卓玩机搞机技巧综合资源-----修改rom 制作rom 解包rom的一些问题解析【二十一】_qcn改区域锁-CSDN博客 安卓系列机型rom修改。如果你删减了系统相关的app。那么严重会导致开机系统卡米 定屏等问题。这类一般都是系统签名验证导致的。而破解签名验证一般都是修改…

API接口的用途以及接入示例

API接口的主要用途是允许不同的软件系统之间进行通信和数据交换。具体来说&#xff0c;API接口可以用于以下几个方面&#xff1a; 数据传输和交换&#xff1a;API接口可以用于不同系统之间的数据传输和交换&#xff0c;例如将数据从一个系统传递到另一个系统&#xff0c;或者从…

【office安装错误1402或1406】

office安装错误1402或1406 错误如图 解决方法 打开autoremove&#xff0c;点击扩展&#xff0c;输入1402&#xff0c;点击搜索 等待修复成功&#xff0c;再尝试安装office 软件每周六选择其他登录方式可以免费使用

Python学习1--变量和简单数据类型

本章练习&#xff1a; Python之禅&#xff1a;

RocketMQ快速入门:namesrv、broker、dashboard的作用及消息发送、消费流程(三)

0. 引言 接触rocketmq之后&#xff0c;大家首当其冲的就会发现需要安装3个组件&#xff1a;namesrv, broker, dashboard&#xff0c;其中dashboard也叫console&#xff0c;为选装。而这几个组件之前的关系是什么呢&#xff0c;消息发送和接收的过程是如何传递的呢&#xff0c;…

如何在Windows 10中打开和自定义搜索?这里提供详细步骤

使用Windows 10中的搜索功能&#xff0c;你可以快速查找计算机上的文件、应用程序或设置。在本文&#xff0c;你可以学习如何在Windows 10中打开和控制搜索。 打开Windows 10搜索面板 打开Windows 10搜索面板很容易。通常&#xff0c;你可以在任务栏上找到搜索图标。只需单击…

如何在PostgreSQL中创建一个新的数据库,并指定所有者?

文章目录 解决方案示例代码 PostgreSQL是一个强大的开源关系型数据库管理系统&#xff0c;它允许用户创建和管理多个数据库。在PostgreSQL中创建一个新的数据库并指定所有者是一个常见的操作。下面&#xff0c;我们将详细解释如何执行这一操作&#xff0c;并提供示例代码。 解…

灭火器检查记录卡模板如何制作

灭火器是常见的消防设备&#xff0c;为确保灭火器正常使用&#xff0c;需要定期对灭火器进行检查和维护&#xff1b;而灭火器检查记录卡就是用来记录灭火器检查的重要工具。然而传统的灭火器检查记录卡都是纸质的&#xff0c;哪怕我们采购多好多贵材质做的检查卡终归记录有限、…

Midjourney如何利用chaos控制生成图片的差异化

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词常用参数——chaos&#xff0c;话不多说&#xff0c;直接开始~ chaos参数什么意思呢&#xff1f; 它可以用来控制我们生成图片之间的差异化程度的一个参数 通常我们在用Midjourney生…