vue使用Element的Tree树形控件实现拖动改变节点顺序方式
作者:hhtSeeTheWorld
项目完成后,产品又提新需求,
通过拖动能够改变下面的组织顺序,又给我增加了好大的工作量!
先吐槽产品一波,怎么早先不想好呢!
首先要想实现拖动改变顺序,那我从后端查询得来的数据
treeList
首先就必须有顺序,
后端为了实现节点有顺序—在实体类中又增加了一个字段
原先类实体
public class OrgNode { private String id; private String name; private String parentId; }
增加了一个brotherId
当前类实体
public class OrgNode { private String id; private String name; private String parentId; private String brotherId; }
后端
1.新增节点
每当我新增一个节点时,就把他的brotherId设为 “-1”,
如果新增节点所在的层级之前没有节点,那不需要处理;
如果新增节点所在的层级之前有节点,那么把之前brotherId为-1的节点的brotherId改为新增节点的id
2.删除节点
当删除节点时,为保证顺序,需要把 被删节点 后一个节点的brotherId 改为 被删节点的 brotherId,
这样这一层级的节点顺序才不会出错
以上写了一点后端的做法,下面来详细讲解前端实现
属性结构的代码
<el-tree :draggable="true" @node-drop="testTrop" :allow-drop="dropPosition" :data="organization" node-key="id" :current-node-key="currentNode" :default-expanded-keys="keys" :expand-on-click-node="false" @node-click="nodeClick" :props="defaultProps" ref='treeOrg' :filter-node-method='filterNode' highlight-current> <span class="custom-tree-node" slot-scope="{ node, data }"> <el-tooltip v-if="node.label.length>10" :content='node.label' placement="top-start" effect="dark" popper-class="atooltip"> <span>{{node.label | showTreeName}}</span> </el-tooltip> <span v-else>{{ node.label }}</span> <span v-if="node.data.id==selectId"> <el-button type="text" icon="el-icon-edit" size="mini" @click.stop=" () => updateOrganization(data)"></el-button> <el-button type="text" icon="el-icon-delete" size="mini" @click.stop="() => remove(node, data)"></el-button> </span> </span> </el-tree>
该组织树的使用
1.draggabl.是否开.拖拽功能
2.allow-dro.拖拽时判定目标节点能否被放置。
3.node-dro.拖拽成功触发的事. 主要是我们 把数据发.后端
4.dat.我们要展示的数. 从后端获取. treeList
5.node-ke.每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
6.current-node-ke.当前选中的节点
7.default-expanded-key.默认展开的节点.key 的数.. 就是我们 想展开节点.id 数组
8.expand-on-click-nod. 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。
9.node-clic.节点被点击时的回.. 节点被点击.我们想做什么. 一般是查询
10.prop...主要用.指定 我们想展示节点.的那两个属. 属性.必须和 从后端得到数据.属性名 相对应
11.re..ref被用来给元素或子组. 注. 引用信息.引用信息将会注册在父组件的 $refs对象上。
12.filter-node-metho. 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
13.highlight-curren. 是否高亮当前选中节点,默认值是 false。
14.el-toolti. 文字提. 用法 <el-tooltip> 要展示的内.</el-tooltip. 给这个 内容 做一些提示
. 当前我们前端要达到的效果.如果文字过. 很后面以省略号结. 但是有必须 让用.看.该条数据的全部内.所以.文字提示.方法去达到目的
. 当前这个文字提. 已经添..v-i.判断字数大于1.才会生效
........... v-els. 就显示原本的字数
这里主要讲解拖拽的两个属性,一个事件
- 属性 draggable 是否开启 拖拽功能
- 属性 allow-drop 拖拽时判定目标节点能否被放置。
- 事件 node-drop 拖拽成功触发的事件 主要是我们 把数据发给 后端
第一个draggable没什么好讲的
第三个node-drop用法也简单
本人主要是
我被该属性卡住了
因为我的是有一个限制,同级中不能有同名组织,
所以需要判定 被拖组织 能否置于 目标组织 前面,后面,或者内部
dropPosition(draggingNode, dropNode, type) { console.log(draggingNode); console.log(dropNode); console.log(type); //这三个参数 是我们在页面拖动时 我们停止拖动时 会自动生成的参数三个参数 // 转移节点draggingNode 目标节点dropNode 置于目标节点的位置 type='prev'、'inner' 和 'next' 三个中其中一个 if (type === "inner") { checkOrganizationName(draggingNode.data.id, draggingNode.data.name, dropNode.data.id).then(data => { if (data.data.data === true) { //说明目标节点内部存在 与转移节点 相同名称的 节点,不能转入 //那我们不设返回值 就不会触发移动事件 } else { return "inner"; } }); } else if (type === "prev" || type === "next") { //判断转移节点 能否放置于 目标节点的 前面或者后面 checkOrganizationName(draggingNode.data.id, draggingNode.data.name, dropNode.data.parentId).then(data => { if (data.data.data === true) { //说明 前后不能放 //那我们也不设返回值 就不会触发移动事件 } else { return type; } }); } },
所以不能通过向后端发请求的方式,判断能否放于 目标位置
应该通过比较前端已经有的数据,判断能否放于目标位置
processDataTree(data) { for (let index in data) { let obj = data[index]; this.organizationTemp.push({id: obj.id, name: obj.name, parentId: obj.parentId}); if (obj.children !== null && obj.children.length > 0) { this.processDataTree(obj.children); } } }, getOrganizationTemp() { getOrganization().then(data => { this.organizationAfterMove = data.data.data; this.organizationTemp = []; this.processDataTree(this.organizationAfterMove); }); }, dropPosition(draggingNode, dropNode, type) { //这三个参数 是我们在页面拖动时 我们停止拖动时 会自动生成的参数三个参数 // 转移节点draggingNode 目标节点dropNode 置于目标节点的位置 type='prev'、'inner' 和 'next' 三个中其中一个 if (type === "inner") { if (dropNode.data.children===null){ //目标节点没有子节点 可以直接放入 return "inner" }else { let childArray=dropNode.data.children; for (let i = 0; i < childArray.length; i++) { if (childArray[i].name===draggingNode.data.name){ return } } return "inner" } } else if (type === "prev" || type === "next") { let existObj; for (let i = 0; i <this.organizationTemp.length; i++) { let obj = this.organizationTemp[i]; if(obj.parentId === dropNode.data.parentId && obj.name === draggingNode.data.name && obj.id!==draggingNode.data.id){ existObj=obj; } } if (existObj) { } else { return type; } } }, testTrop(before,after,inner,event){ changeParentOrg(before.data.id,after.data.id,inner,this.$getCookie().getUserName()); setTimeout(()=>{this.getOrganizationTemp()},500); },
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。