树状的拖拽功能分析
1.Vue的树状已经组装好AP并支持拖拽,其中有node,data,type
组件部分
<el-tree
:data="menus"
:props="defaultProps"
:expand-on-click-node="false"
show-checkbox
node-key="catId"
:default-expanded-keys="expandekey"
:draggable="draggable"
:allow-drop="allowDrop"
@node-drop="handleDrop"
ref="menuTree"
>
data–对应绑定的数据,props–绑默认属性的API,expand-on-click-node是否只能点击箭头上进行展开节点(另一种解释),show-checkbox 是否为每项展示勾选,node-key为当项的唯一标识(数据区没有),default-expanded-keys表示树中默认展示的节点,draggable是否可拖拽,allow-drop为 拖拽时判定目标节点能否被放置(默认为三种: ‘prev’、‘inner’ 和 ‘next’ ),固定绑定的方法携带的参数(draggingNode, dropNode, type) ,node-drop为拖拽成功后触发事件,handleDrop也为以上三个
数据部分
data () {
return {
pCid: [],
maxLevel: 0,
draggable: true,
updateNodes: [],
dialogType: "",
menus: [],
expandekey: [],
defaultProps: {
children: "childrens",
label: "name"
}
}
}
方法部分
methods: {
allowDrop(draggingNode, dropNode, type) {
//1、被拖动的当前节点以及所在的父节点总层数不能大于3
//1)、被拖动的当前节点总层数
console.log("allowDrop:", draggingNode, dropNode, type);
//
this.countNodeLevel(draggingNode);
//当前正在拖动的节点+父节点所在的深度不大于3即可
let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;
console.log("深度:", deep);
// this.maxLevel
if (type == "inner") {
// console.log(
// `this.maxLevel:${this.maxLevel};draggingNode.data.catLevel:${draggingNode.data.catLevel};dropNode.level:${dropNode.level}`
// );
return deep + dropNode.level <= 3;
} else {
return deep + dropNode.parent.level <= 3;
}
},
countNodeLevel(node) {
//找到所有子节点,求出最大深度
if (node.childNodes != null && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].level > this.maxLevel) {
this.maxLevel = node.childNodes[i].level;
}
this.countNodeLevel(node.childNodes[i]);
}
}
},
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log("handleDrop: ", draggingNode, dropNode, dropType);
//1、当前节点最新的父节点id
let pCid = 0;
//2. 兄弟数组们
let siblings = null;
if (dropType == "before" || dropType == "after") {
pCid = dropNode.parent.data.catId ==
undefined? 0: dropNode.parent.data.catId;
siblings = dropNode.parent.childNodes;//平级为父节点下的子节点数组
} else {
pCid = dropNode.data.catId;
siblings = dropNode.childNodes;//嵌入类为目标节点的下的所有子节点
}
this.pCid.push(pCid);//存入单个最新父节点到内存
//2、当前拖拽节点的最新顺序,
for (let i = 0; i < siblings.length; i++) {
if (siblings[i].data.catId == draggingNode.data.catId) {
//到正在拖拽的节点时
let catLevel = draggingNode.level;
if (siblings[i].level != catLevel) {
//当前节点的层级发生变化
catLevel = siblings[i].level;
//修改他子节点的层级
this.updateChildNodeLevel(siblings[i]);
}
this.updateNodes.push({
catId: siblings[i].data.catId,
sort: i,
parentCid: pCid,
catLevel: catLevel
});
} else {
this.updateNodes.push({ catId: siblings[i].data.catId, sort: i });
}
}
//3、当前拖拽节点的最新层级
console.log("updateNodes", this.updateNodes);
},
batchSave() {
this.$http({
url: this.$http.adornUrl("/product/category/update/sort"),
method: "post",
data: this.$http.adornData(this.updateNodes, false)
}).then(({ data }) => {
this.$message({
message: "菜单顺序等修改成功",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = this.pCid;
this.updateNodes = [];
this.maxLevel = 0;
// this.pCid = 0;
});
},
updateChildNodeLevel(node) {
if (node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
var cNode = node.childNodes[i].data;
this.updateNodes.push({
catId: cNode.catId,
catLevel: node.childNodes[i].level
});
this.updateChildNodeLevel(node.childNodes[i]);
}
}
},
},
最终实现:完成拖拽限制,上传所有修改后数据,并展示当前展示的节点
流程分析
1.但用户拖拽并放在某个节点时,会先触发allowDrop,若判断为成功后触发handleDrop。
2.allowDrop解析:1.获得当前拖拽节点的最大深度+目标节点深度的两者之和,并根据拖拽的类型(内,并集,顺序调换)判断是否大于3
3.若深度没问题
静不下来,等上班再分析这段前端代码