ElementUI-tree拖拽功能与节点自定义

前言

在管理端会遇到多分类时,要求有层次展示出来,并且每个分类有额外的操作。例如:添加分类、编辑分类、删除、拖到分类等。

下面将会记录这样的一个需求实习过程。

了解需求

  1. 分类展示按层级展示
  2. 分类根据特定的参数展示可以操作的按钮,分类的操作有增、删、改
  3. 分类还支持拖拽功能,并不是所有的分类都支持拖拽
  4. 点分类去执行别的操作。例如:刷新数据(不实现)
  5. 增加分类之后刷新分类数据,当前选择的分类为增加的分类
  6. 删除分类后回到上一级分类
  7. 右击分类和点击操作按钮均可以弹出操作弹窗
  8. 点击分类前的箭头可展开和折叠分类

效果图

  • 分类展示

  • 分类操作的弹窗

组件库

采用ElementUI 中的 Tree树形控件、Dropdown下拉菜单

  • Tree树形控件:Element - The world's most popular Vue UI framework
  • Dropdown下拉菜单dropdown:Element - The world's most popular Vue UI framework

开始编码

搭建tree 组件

  •   html 部分:
<el-tree :data="classifyData" node-key="id" draggable ref="tree" :accordion="false" auto-expand-parent :default-expanded-keys="[checkedId]" :props="defaultProps":allow-drop="allowDrop" :allow-drag="allowDrag"@node-drag-start="handleDragStart" @node-drop="handleDrop"@node-click="nodeClick" @node-contextmenu="rightClick":show-checkbox="false" :check-strictly="true"  ><div class="custom-tree-node" slot-scope="{ node, data }"><span>{{ data.name }}</span> <span><el-dropdown type="primary" trigger="click" :ref="'messageDrop'+data.id" @visible-change="controlCheckedKeys"><span class="el-dropdown-link" @click.stop="setSeletKey(data.id)">  <img src="~@/../more-active.png" v-if="checkedKeys == data.id"  class="myicon-opt" /> <img src="~@/../more.png" v-else class="myicon-opt" /></span><el-dropdown-menu slot="dropdown"><el-dropdown-item v-if="data.is_add_classify"><div @click="openClassify(data.id,'新增子分类')"><img src="~@/../add.png" class="myicon-opt"/> 新增子分类</div></el-dropdown-item><el-dropdown-item v-if="data.is_edit_sort"><div @click="editClassify(data)"> <img src="~@/../edit.png" class="myicon-opt" /> 修改</div></el-dropdown-item><el-dropdown-item v-if="data.is_edit_sort"><div @click="delBefore(data.id,data.parent_id)"><img src="~@/../del.png" class="myicon-opt" />删除</div></el-dropdown-item></el-dropdown-menu></el-dropdown></span></div></el-tree>
  • css
<style lang="stylus" scoped>
.active{background: #F2F6F9;color: #409EFF;
}
.classify{padding : 0 16px;height: 40px;font-family: PingFangSC-Medium;font-weight: 500;font-size: 15px; line-height:40px;
}.el-tree ::v-deep {.el-tree-node__content{ @extend .classify;&:hover{@extend .active; }.el-tree-node__expand-icon.is-leaf{// display:nonemargin-left:-12px}}.is-checked > .el-tree-node__content{@extend .active;} 
} 
.custom-tree-node{display: flex;justify-content: space-between;width: 100%;
}
.myicon-opt{vertical-align: middle;width: 16px;height: 16px;
} 
</style>
  • js
<script>export default {props:{activeId:{type:[String,Number],default:''},classifyData:{type:Array,default:[]}},watch:{activeId: {handler(v,o){// v 值为0时, 0 == '' 值为trueif (typeof v == 'number') {this.checkedId = v this.$nextTick(()=>{this.$refs.tree.setCheckedKeys([v])}) }},immediate:true,deep:true },},data() {return {checkedId:'', checkedKeys:'', defaultProps: {children: 'child',label: 'name'},classifyCofig:{flag:false,Id: '',title:'',value:''}, }},methods: { // 点击分类名称nodeClick(data,node){ this.checkedId = data.id  this.$refs.tree.setCheckedKeys([data.id]) node.expanded = truethis.$emit('selectId',data.id)// console.log('node',data.id,node.parent)let addId  = [ data.id]if(node.parent.parent != null)  this.selectNode(addId,node.parent)// console.log('addId',addId)this.$emit('selectaddId', addId)},// 获取多层级的父类id加入到数组下标为0的位置selectNode(id,node){ id.unshift(node.data.id)if(node.parent.parent != null){this.selectNode(id,node.parent)} },// 右击分类rightClick(event,data, Node, element){ setTimeout(()=>{this.checkedKeys = data.id this.$refs['messageDrop'+data.id].show() })},// 点击操作按钮setSeletKey(k){ setTimeout(()=>{this.checkedKeys = k})},// 下拉菜单的异步监听,打开(true)还是隐藏(flase)controlCheckedKeys(flag){  if(!flag){this.checkedKeys = ''}},// 节点开始拖拽时触发的事件handleDragStart(node) {if(!node.data.is_edit_sort){return false} }, // 拖拽成功完成时触发的事件handleDrop(draggingNode, dropNode, dropType) {if(dropType == 'none') return // 准备排序参数可自行更改let params = {pk1: draggingNode.data.id,pk2: dropNode.data.id,direction:dropType == 'before' ? -1 : 1}this.orderClassify(params)}, /** *  拖拽时判定目标节点能否被放置。* @param {*} draggingNode * @param {*} dropNode * @param {*} type 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后*/allowDrop(draggingNode, dropNode, type) {if (draggingNode.level === dropNode.level) { if (draggingNode.data.parent_id === dropNode.data.parent_id && dropNode.data.is_edit_sort) {// 向上拖拽 || 向下拖拽return type === "prev" || type === "next"}} else {// 不同级进行处理return false}},//判断节点能否被拖拽allowDrag(draggingNode) {if(!draggingNode.data.is_edit_sort){return false}return true }, async orderClassify(params){// 发送排序请求}, setClassCofig(flag,id,title,value){this.classifyCofig['flag'] = flagthis.classifyCofig['Id'] = idthis.classifyCofig['title'] = titlethis.classifyCofig['value'] = value},openClassify(pid,txt){this.setClassCofig(true,pid, txt ? txt : '新增分类','')   },editClassify(row){this.setClassCofig(true,row.id, '修改分类', row.name) }, closeAdd(){this.setClassCofig(false,'', '', '')},// 新增/修改分类async sureClassify(params){ let {value,Id} = this.classifyCofig// 通过value的值判断当前是新增还是修改// 刷新分类,cid 新分类的idlet refresh = { }if(value){ refresh.flag = false}else{ refresh.flag = true}  // 准备参数,发送请求// 请求成功后执行this.setClassCofig(false,'', '', '')refresh.cid = value? this.checkedId : res.data.data.idthis.$emit('refreshClass',refresh)},//判断分类是否可以删除async delBefore(id,pid){//1.自定义判断是否可以删除,//2.可以删去执行删除操作,this.sureDelete(id,pid)},//删除分类,删除后回到上一级async sureDelete(id,pid){//1.准备删除的接口使用数据//2.发起请求,请求成功后执行下面代码this.setClassCofig(false,'', '', '')let refresh = {flag: true,cid: pid}this.$emit('refreshClass',refresh)},}};
</script>

使用tree组件

  • html
 <PersonalTree :activeId="currentClassfiyId" :classifyData="classifyData"@selectId="changeSelectId" @selectaddId="setAddId" @refreshClass="refreshClass"/>
  • js
<script>
// 在此处引入tree组件命名为customTreeexport default{components:{customTree},data(){return{currentClassfiyId:'',addClassifyId:[],classifyData:[], }},mounted(){this.getClassList(true) },methods:{async getClassList(flagScene,cid){// console.log(flagScene,cid)// 发送请求,获取全部分类this.classifyData = res.data.data.classify this.currentClassfiyId = cid || this.classifyData?.[0].idif(flagScene){ // 可以去获取内容} }},refreshClass({flag,cid}){// 去刷新分类列表this.getClassList(flag,cid)},setAddId(val){this.addClassifyId = val},changeSelectId(id){this.currentClassfiyId = id// 可以去获取内容},}}
</script>   

classifyData的数据:

[{"id": 1033,"name": "一级分类","parent_id": 0, "level": 1,"child": [{"id": 1036,"name": "aaaaaaaaa","parent_id": 1033, "level": 2,"child": [],"is_edit_sort": true,"is_add_classify": true,"is_add_scene": true},{"id": 1035,"name": "aaaaa","parent_id": 1033,  "level": 2,"child": [{"id": 1037,"name": "a-1","parent_id": 1035, "level": 3,"child": [{"id": 1040,"name": "a-1-3","parent_id": 1037, "level": 4,"child": [],"is_edit_sort": true,"is_add_classify": false,"is_add_scene": true},{"id": 1038,"name": "a-1-1","parent_id": 1037, "level": 4,"child": [],"is_edit_sort": true,"is_add_classify": false,"is_add_scene": true}],"is_edit_sort": true,"is_add_classify": true,"is_add_scene": true}],"is_edit_sort": true,"is_add_classify": true,"is_add_scene": true}],"is_edit_sort": true,"is_add_classify": true,"is_add_scene": true
},{"id": 1032,"name": "测试分类b","parent_id": 0, "level": 1,"child": [],"is_edit_sort": true,"is_add_classify": true,"is_add_scene": true
},{"id": 1015,"name": "无操作区","parent_id": 0,"level": 1,"child": [],"is_edit_sort": false,"is_add_classify": false,"is_add_scene": false
}]

如有帮到您,请收藏+关注哦!!!

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

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

相关文章

详解 SpringMVC 的 HttpMessageConverter

文章目录 前言参考资料内容1、[RequestBody ](/RequestBody )2、RequestEntity3、[ResponseBody ](/ResponseBody )4、SpringMVC处理json5、SpringMVC处理ajax6、RestController注解7、ResponseEntity 推荐读物 《Spring Boot微服务实战(第2版)》内容简介目录 前言 HttpMessag…

【解密ChatGPT】:从过去到未来,揭示其发展与变革

&#x1f38a;专栏【ChatGPT】 &#x1f33a;每日一句&#xff1a;天行健,君子以自强不息,地势坤,君子以厚德载物 ⭐欢迎并且感谢大家指出我的问题 文章目录 一、ChatGPT的发展历程 二、ChatGPT的技术原理 三、ChatGPT的应用场景 四、ChatGPT的未来趋势 五、总结 引言:随着…

目标检测算法 - YOLOv1

文章目录 1. 作者简介2. 目标检测综述3. YOLOv1算法3.1 预测阶段3.2 预测阶段后处理3.3 训练阶段 YOLO的全称是you only look once&#xff0c;指只需要浏览一次就可以识别出图中的物体的类别和位置。 YOLO是目标检测模型。目标检测是计算机视觉中比较简单的任务&#xff0c;用…

“双11”近300万商家为消费者贴息,花呗分期免息成增长“利器”

今年双11&#xff0c;很多消费者发现&#xff0c;能用花呗分期免息的商品变多了&#xff0c;不光是品牌旗舰店&#xff0c;还有中小淘宝卖家&#xff0c;不少直播间里的商品&#xff0c;都能用花呗分期免息。这背后&#xff0c;是平台、商家对花呗分期免息的重视和需要。 数据…

70 内网安全-域横向内网漫游Socks代理隧道技术

目录 必要基础知识点:1.内外网简单知识2.内网1和内网2通信问题3.正向反向协议通信连接问题4.内网穿透代理隧道技术说明 演示案例:内网穿透Ngrok测试演示-两个内网通讯上线内网穿透Frp自建跳板测试-两个内网通讯上线CFS三层内网漫游安全测试演练-某CTF线下2019 涉及资源: 主要说…

数据结构:Map和Set(2):相关OJ题目

目录 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 771. 宝石与石头 - 力扣&#xff08;LeetCode&#xff09; 旧键盘 (20)__牛客网 (nowcoder.com) 138. 随机链表的复制 - 力扣&#xff08;LeetCode&#xff09; 692. 前K个高频单词 - 力扣&#xff08…

sd-wan专线异地组网|分支机构与总部间外贸MES系统高速访问解决方案

随着全球化的快速发展&#xff0c;国际市场对于企业的重要性日益增加&#xff0c;很多外贸企业都会选择在海外建立工厂&#xff0c;以求更好的发展国际市场&#xff0c;但是&#xff0c;海外建立工厂必然会在当地招聘新的公司人员&#xff0c;如何做好异地管理和监控海外工厂的…

Redis注解式开发结合SSM项目使用与Quartz框架介绍以及击穿、穿透、雪崩问题解决

目录 一、SSM项目整合Redis 1.1 导入pom依赖 1.2 spring-redis.xml 1.3 spring上下文配置 二、Redis注解式开发 2.1 Cacheable 注解 2.2 自定义策略 2.3 CachePut 注解 三、Redis中缓冲、击穿、穿透、雪崩问题解决 3.1 缓冲问题 —— Quartz 框架 3.2 常见的三种问题…

从vue源码中看diff算法

一、v-for必须要指定key&#xff0c;其作用是什么&#xff1f; 在源码中有一个函数为&#xff0c;其中就是通过判断两个vnode的type和key进行判断&#xff0c;如果这两个属性相同&#xff0c;那么这两个vnode就是相同&#xff0c;所以在设置key的时候也不可以设置为object等无…

Midway.js打通WebSocket前后端监听通道

您好&#xff0c; 如果喜欢我的文章或者想上岸大厂&#xff0c;可以关注公众号「量子前端」&#xff0c;将不定期关注推送前端好文、分享就业资料秘籍&#xff0c;也希望有机会一对一帮助你实现梦想 前言 WebSocket协议允许客户端和服务端持久化连接&#xff0c;这种可以持续…

STM32_project:led_beep

代码&#xff1a; 主要部分&#xff1a; #include "stm32f10x.h" // Device header #include "delay.h"// 给蜂鸣器IO口输出低电平&#xff0c;响&#xff0c;高&#xff0c;不向。 //int main (void) //{ // // 开启时钟 // RC…

接口自动化面试题

1.http请求都包含哪些内容&#xff0c;请求头和请求体有哪些内容 请求行/请求头/请求体/空行 请求行&#xff1a;请求方法字段、URL字段、http协议版本 例如&#xff1a;GET /index.html HTTP/1.1 请求方法&#xff1a;GET、POST、PUT、DELETE、OPTIONS、TRACE、CO…