准备
需求:点击拖拽功能后,拖拽树形数据,拖拽数据后,点击提交就会将拖拽完毕的信息自动保存到数据库
需求分析:
拖拽
有三种情况
- inner: 拖拽到目的元素中
- prev: 拖拽到目的元素前
- next: 拖拽到目的元素后
判断
树形表只有三层,合并后如果大于三层,此操作应该不被允许
数据更新
- 父节点不需要更新
- 子节点需要更新 自己所在层数,父节点id,排序,以及它的子节点层数
- 因为存在多次拖拽,更新和判断时需要以当前页面的数据为准,也就是Node数据
提交
因为是批量提交,所以需要修改的数据应该储存到一个全局数组中,在点击批量提交按钮后,会将所有节点数据一次性提交。
elementui组件
draggable
true 为支持拖拽:allow-drop="allowDrop"
是否允许被拖拽到当前位置 返回true表示不允许@node-drop="handleDrop"
拖拽成功后调用
判断
最后的层数 = 被拖拽的节点总深度+拖入后父节点所在层数
- 拖入后父节点
inner: 目标节点
else: 目标节点的父节点 - 计算被拖拽元素的总层数,这里参考二叉树深度,用bfs
getDepth(data) {
let max = 0;
if (data.children != null && data.children.length > 0) {
for (let index = 0; index < data.children.length; index++) {
max = Math.max(max, this.getDepth(data.children[index]));
}
return max + 1;
}
return 1;
- 计算
allowDrop(draggingNode, dropNode, type) { // 控制节点是否可以被拖拽
let depth = this.getDepth(draggingNode.data); // 拖拽节点的总深度
let level = dropNode.level; // 目的节点在第几层,这里的level使用node动态更新的level,因为存在批量拖拽,数据库中的数据不准确
let sum = level + depth; // 保证构建完毕后,总深度不大于3
if (type == 'inner') {
return sum <= 3;
} else {
return sum <= 4;// 并列关系是在父节点下层,所以是4
}
},
参数更新
动态加载的对象
- Node.level 表示当前层级
- Node.childNodes 表示当前节点的子节点
- Node.parent 表示当前节点的父节点
我们需要做的就是根据动态加载的参数,将data中的属性赋值,最后加载到全局数组updateNodes中去。之后点击批量修改按钮,就可以将通过后端修改数据库。
层级和父节点id
inner 针对目的节点,其他的是目的节点的父节点。
排序
因为在拖动时Node中的children已经动态加载了,我们要做的就是将拖动节点
的父节点
的孩子
打印出来,然后sort属性就是遍历的索引值(因为childNodes是按照当前页面的数据打印的)
注意:这次的更新要将目的节点的同级节点全部更新,因为目的节点的位置和同级节点是相关的
sort(siblings) {
for (let i = 0; i < siblings.length; i++) {
const node = siblings[i];
this.updateNodes.push({ catId: node.data.catId, parentCid: node.data.parentCid, catLevel: node.data.catLevel, sort: i });
}
},
递归修改目标节点的子节点
子节点也要添加到updateNodes中
changeChildren(children) { // 修改被拖拽的节点及其子节点 parentCid catLevel,将子节点添加到需要修改的节点中
if (children != null && children.length > 0) {
for (let i = 0; i < children.length; i++) {
let node = children[i];
this.updateNodes.push({ catId: node.data.catId, parentCid: node.data.parentCid, catLevel: node.data.catLevel });
node.data.parentCid = node.parent.data.catId;
node.data.catLevel = node.parent.data.catLevel + 1;
this.changeChildren(node.childern);
}
}
},
流程
代码
allowDrop
allowDrop(draggingNode, dropNode, type) { // 控制节点是否可以被拖拽
let depth = this.getDepth(draggingNode.data); // 拖拽节点的总深度
let level = dropNode.level; // 目的节点在第几层,这里的level使用node动态更新的level,因为存在批量拖拽,数据库中的数据不准确
let sum = level + depth; // 保证构建完毕后,总深度不大于3
if (type == 'inner') {
return sum <= 3;
} else {
return sum <= 4;// 并列关系是在父节点下层,所以是4
}
},
handleDrop
handleDrop(draggingNode, dropNode, dropType, ev) {// 拖拽时改变的值,parentCid,catLevel,sort
// 更新parentCid catLevel
let siblings = dropNode.childNodes;
if (dropType == 'inner') {
draggingNode.data.parentCid = dropNode.data.catId;
draggingNode.data.catLevel = dropNode.level + 1;;
} else {
draggingNode.data.parentCid = dropNode.data.parentCid;
draggingNode.data.catLevel = dropNode.level;
siblings = dropNode.parent.childNodes;
}
// 排序
this.sort(siblings, draggingNode);
// 更新子节点
this.changeChildren(draggingNode.childNodes);
// 记录父节点的id,方便提交后展开
this.pcids.push(draggingNode.data.parentCid);
// 修改数据库
// this.editCategories(this.updateNodes);
// this.extendKey = [draggingNode.data.parentCid];
},
editCategories
editCategories() {
this.$http({
url: this.$http.adornUrl('/product/category/update/list'),
method: 'post',
data: this.$http.adornData(this.updateNodes, false)
}).then(({ data }) => {
this.$message({
message: "拖拽成功",
type: "success",
});
this.getMenu();
this.extendKey = this.pcids;
this.updateNodes = []; //清空数据
this.pcids = [];
});
},