赞
踩
所有这些数据都用收集到,然后发送给后台更新。
拖拽结束后,触发拖拽结束事件,所以要在el-tree上绑定这个事件。
事件函数nodeDrop定义如下。
然后定义一个全局数组,用来收集需要更新的节点数据。
var draggingNodeNewPId = 0;
// 1,更新draggingNode的parentCid,根据dropPosition的不同值,分为两种情况
if (dropPosition === "before" || dropPosition === "after") {
draggingNodeNewPId = dropNode.data.parentCid;
} else {
draggingNodeNewPId = dropNode.data.cid;
}
这段代码是在处理 el-tree
组件中拖拽结束后的逻辑,特别是更新被拖动节点的 parentCid
字段,以反映新的父节点关系。parentCid
假设是数据库中用来标识父节点的一个字段,通常在树形结构的数据模型中使用。
代码中的逻辑分为两种情况:
当拖动节点放置在目标节点之前 (prev
) 或之后 (next
):
if (dropPosition === "prev" || dropPosition === "next") {
draggingNode.parentCid = dropNode.data.parentCid;
}
在这种情况下,拖动的节点将变为与目标节点同级,但它们共同的父节点会成为新的父节点。因此,draggingNode
的 parentCid
被设置为 dropNode.data.parentCid
,即目标节点的父节点ID。这种写法还兼容了没有父节点的情况,没有父节点时,parentCid为0。
当拖动节点放置在目标节点内部 (inner
):
else if (dropPosition === "inner") {
draggingNode.parentCid = dropNode.data.cid;
}
如果拖动的节点被放置在目标节点内部,那么它将成为目标节点的子节点。因此,draggingNode
的 parentCid
被设置为 dropNode.data.cid
,即目标节点自身的ID。
这段代码确保了在拖拽结束后,树形结构在逻辑上保持一致,parentCid
的更新反映了新的父子关系。这在维护树形数据结构的完整性方面非常重要,特别是在需要同步这些变化到后端数据库的场景下。
// 2,更新draggingNode及其子节点的sort // 2.1如果draggingNode的parentCid没有更新,只需要重排draggingNode的兄弟结点的sort; // 2.2如果draggingNode的parentCid有更新,不仅需要重排draggingNode的原来的兄弟结点的sort,还需要重排draggingNode新的兄弟结点的sort var siblingNodesOld = []; var siblingNodesNew = []; // draggingNode.parent为空,所以要根据parentCid找到其parent。 // 写一个根据cid从menus中查询结点的函数 let draggingNodeOldParentNode = this.getNodeByCid(draggingNode.data.parentCid, this.getLevel1Nodes(dropNode)); console.log("draggingNodeOldParentNode:", draggingNodeOldParentNode); siblingNodesOld = draggingNodeOldParentNode.childNodes;; console.log("siblingNodesOld:", siblingNodesOld); if (draggingNode.data.parentCid !== draggingNodeNewPId) { if (dropPosition === "before" || dropPosition === "after") { siblingNodesNew = dropNode.parent.childNodes; } else { siblingNodesNew = dropNode.childNodes; } } for (var i = 0; i < siblingNodesOld.length; i++) { this.nodesInfoAfterDrop.push({catId: siblingNodesOld[i].data.catId, sort: i}); } console.log("update sortu0....", siblingNodesNew, siblingNodesOld, dropNode, dropPosition); for (var i = 0; i < siblingNodesNew.length; i++) { this.nodesInfoAfterDrop.push({catId: siblingNodesNew[i].data.catId, sort: i}); } console.log("update sort....", siblingNodesNew, siblingNodesOld, dropNode, dropPosition);
这段代码涉及到了在 el-tree
拖拽操作后更新节点排序(sort
属性)的逻辑,以便反映节点在树结构中的新位置。这里主要关注两个方面:更新拖动节点 draggingNode
及其子节点的排序,以及根据 draggingNode
的父节点是否改变来决定哪些节点的排序需要被更新。
确定旧的和新的兄弟节点集合:
siblingNodesOld
是拖动前 draggingNode
的兄弟节点集合,即它原本的同级节点。siblingNodesNew
是拖动后 draggingNode
的兄弟节点集合,这取决于拖动的目标位置(放置在目标节点之前、之后还是内部)。判断是否需要更新新的兄弟节点集合:
draggingNode
的父节点ID(parentCid
)没有改变,意味着拖动只改变了顺序而没有改变层级,此时只需要更新旧的兄弟节点集合的排序。draggingNode
的父节点ID改变了,意味着节点的层级发生了变化,此时需要更新旧的和新的兄弟节点集合的排序。更新兄弟节点的排序:
siblingNodesOld
和 siblingNodesNew
集合,将每个节点的 sort
属性设置为其在集合中的索引。这是因为索引反映了节点在数组中的位置,可以作为排序的依据。在 siblingNodesNew
的赋值中,对于放置在目标节点之前或之后的情况,代码似乎直接将 dropNode.data.parentCid
赋值给了 siblingNodesNew
,但实际上 dropNode.data.parentCid
不应直接作为节点集合,这看起来像是一个错误或代码片段的不完整展示。正确的做法应该是获取 dropNode
的父节点的子节点集合。
对于放置在目标节点内部的情况,代码正确地从 dropNode
的父节点获取了子节点集合。
// 3,更新draggingNode及其子节点的catLevel var catLevelNeedUpdate = true; if (draggingNode.data.catLevel === dropNode.data.catLevel && (dropPosition === "before" || dropPosition === "after")) { catLevelNeedUpdate = false; } var draggingNodeAfterDrop = {}; if (catLevelNeedUpdate) { var draggingParentNodeAfterDrop = {}; if (dropPosition === "before" || dropPosition === "after") { draggingParentNodeAfterDrop = dropNode.parent; } else { draggingParentNodeAfterDrop = dropNode; } draggingParentNodeAfterDrop.childNodes.forEach((child) => { if (child.cid === draggingNode.data.cid) { draggingNodeAfterDrop = child; } }) // 递归变量draggingNodeAfterDrop及其childNodes,将其data属性的cateLevel置为level属性值 this.updateCatLevel(draggingNodeAfterDrop); }
updateCatLevel的定义如下:
// 递归更新draggingNode及其子节点的catLevel
updateCatLevel(node) {
node.catLevel = node.level;
if (node.childNodes && node.childNodes.length > 0) {
node.childNodes.forEach((child) => {
this.updateCatLevel(child);
});
}
},
这段代码的主要作用是,在 el-tree
组件的拖拽操作完成后,更新拖动节点 draggingNode
及其所有子节点的 catLevel
属性,以反映新的层级关系。catLevel
通常用于表示节点在树结构中的层级深度,这对于保持树结构的一致性和完整性是非常重要的。
catLevel
首先,代码检查是否需要更新 catLevel
:
var catLevelNeedUpdate = true;
if (draggingNode.data.catLevel === dropNode.data.catLevel && (dropPosition === "prev" || dropPosition === "next")) {
catLevelNeedUpdate = false;
}
如果拖动节点和目标节点的层级相同,并且拖动位置是目标节点的前后,那么 catLevel
不需要更新,因为拖动操作不会改变节点的层级深度。
接下来,代码尝试获取拖动操作完成后的拖动节点 draggingNodeAfterDrop
,这通常是通过查找父节点的子节点列表来完成的:
var draggingNodeAfterDrop = {};
if (catLevelNeedUpdate) {
var draggingParentNodeAfterDrop = {};
if (dropPosition === "prev" || dropPosition === "next") {
draggingParentNodeAfterDrop = dropNode.parent;
} else {
draggingParentNodeAfterDrop = dropNode;
}
draggingParentNodeAfterDrop.childNodes.forEach((child) => {
if (child.cid === draggingNode.data.cid) {
draggingNodeAfterDrop = child;
}
})
}
这里,draggingParentNodeAfterDrop
根据拖动位置确定,然后遍历其子节点找到具有相同 cid
的节点,即拖动后的拖动节点。
catLevel
最后,如果需要更新 catLevel
,调用 updateCatLevel
方法来递归更新拖动节点及其所有子节点的 catLevel
:
this.updateCatLevel(draggingNodeAfterDrop, dropNode.data.catLevel + deep);
updateCatLevel
方法如下:
// 递归更新draggingNode及其子节点的catLevel
updateCatLevel(node) {
if (!node) {
return;
}
this.nodesInfoAfterDrop.push({catId: node.data.catId, catLevel: node.level});
if (node.childNodes && node.childNodes.length > 0) {
node.childNodes.forEach((child) => {
this.updateCatLevel(child);
});
}
},
在这个方法中,node.catLevel
被设置为 node.level
,后者通常反映了节点的实际层级深度。如果节点有子节点,递归地调用 updateCatLevel
方法来更新所有子节点的 catLevel
。
<template> <div> <el-tree node-key="catId" :data="menus" :props="defaultProps" :expand-on-click-node="false" show-checkbox :default-expanded-keys="expandedKeys" :allow-drop="allowDrag" @node-drop="nodeDrop" draggable > <span class="custom-tree-node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <span> <el-button v-if="node.level <= 2" size="mini" @click="() => append(data)" > Append </el-button> <el-button size="mini" @click="() => edit(data)"> Edit </el-button> <el-button v-if="node.childNodes.length == 0" type="text" size="mini" @click="() => remove(node, data)" > Delete </el-button> </span> </span> </el-tree> <el-dialog :title="dialogTitle" :visible.sync="dialogFormVisible" :close-on-click-modal="false" > <el-form :model="category"> <el-form-item label="分类名称"> <el-input v-model="category.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="图标"> <el-input v-model="category.icon" autocomplete="off"></el-input> </el-form-item> <el-form-item label="计量单位"> <el-input v-model="category.productUnit" autocomplete="off" ></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="submitCategory">确 定</el-button> </div> </el-dialog> </div> </template> <script> export default { components: {}, props: {}, data() { return { nodesInfoAfterDrop: [], dialogTitle: "", // 编辑窗口标题,新增分类,修改分类 dialogType: "", // 编辑窗口类型,create表示append,edit表示edit dialogFormVisible: false, menus: [], category: { name: "", parentCid: 0, catLevel: 0, sort: 0, showStatus: 1, icon: "", productUnit: "", catId: null, }, expandedKeys: [], defaultProps: { children: "children", label: "name", }, }; }, methods: { nodeDrop(draggingNode, dropNode, dropPosition) { console.log("draggingNode:", draggingNode, dropNode, dropPosition); var draggingNodeNewPId = 0; // 1,更新draggingNode的parentCid,根据dropPosition的不同值,分为两种情况 if (dropPosition === "before" || dropPosition === "after") { draggingNodeNewPId = dropNode.data.parentCid; } else { draggingNodeNewPId = dropNode.data.cid; } // 2,更新draggingNode及其子节点的sort // 2.1如果draggingNode的parentCid没有更新,只需要重排draggingNode的兄弟结点的sort; // 2.2如果draggingNode的parentCid有更新,不仅需要重排draggingNode的原来的兄弟结点的sort,还需要重排draggingNode新的兄弟结点的sort var siblingNodesOld = []; var siblingNodesNew = []; // draggingNode.parent为空,所以要根据parentCid找到其parent。 // 写一个根据cid从menus中查询结点的函数 let draggingNodeOldParentNode = this.getNodeByCid(draggingNode.data.parentCid, this.getLevel1Nodes(dropNode)); console.log("draggingNodeOldParentNode:", draggingNodeOldParentNode); siblingNodesOld = draggingNodeOldParentNode.childNodes;; console.log("siblingNodesOld:", siblingNodesOld); if (draggingNode.data.parentCid !== draggingNodeNewPId) { if (dropPosition === "before" || dropPosition === "after") { siblingNodesNew = dropNode.parent.childNodes; } else { siblingNodesNew = dropNode.childNodes; } } for (var i = 0; i < siblingNodesOld.length; i++) { this.nodesInfoAfterDrop.push({catId: siblingNodesOld[i].data.catId, sort: i}); } console.log("update sortu0....", siblingNodesNew, siblingNodesOld, dropNode, dropPosition); for (var i = 0; i < siblingNodesNew.length; i++) { this.nodesInfoAfterDrop.push({catId: siblingNodesNew[i].data.catId, sort: i}); } console.log("update sort....", siblingNodesNew, siblingNodesOld, dropNode, dropPosition); // 3,更新draggingNode及其子节点的catLevel var catLevelNeedUpdate = true; if (draggingNode.data.catLevel === dropNode.data.catLevel && (dropPosition === "before" || dropPosition === "after")) { catLevelNeedUpdate = false; } var draggingNodeAfterDrop = null; if (catLevelNeedUpdate) { var draggingParentNodeAfterDrop = {}; if (dropPosition === "before" || dropPosition === "after") { draggingParentNodeAfterDrop = dropNode.parent; } else { draggingParentNodeAfterDrop = dropNode; } draggingParentNodeAfterDrop.childNodes.forEach((child) => { if (child.data.catId === draggingNode.data.catId) { draggingNodeAfterDrop = child; } }) // 递归变量draggingNodeAfterDrop及其childNodes,将其data属性的cateLevel置为level属性值 this.updateCatLevel(draggingNodeAfterDrop); } this.nodesInfoAfterDrop.push({catId: draggingNode.data.catId, parentCid: draggingNodeNewPId}); console.log(this.nodesInfoAfterDrop); // 4,调用后端接口,将数组nodesInfoAfterDrop发送给后端 this.$http({ url: this.$http.adornUrl("/product/category/update/sort"), method: "post", data: this.$http.adornData(this.nodesInfoAfterDrop, false), }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "操作成功", type: "success", duration: 1500, onClose: () => { console.log("删除成功,关闭消息提示"); this.getMenus(); // 重新获取数据 this.expandedKeys = [draggingNodeNewPId]; // 重置展开节点 }, }); } else { this.$message.error(data.msg); } }); }, getLevel1Nodes(node) { var tmpNode = node; while(tmpNode.parent) { tmpNode = tmpNode.parent; } return tmpNode.childNodes; }, // 写一个根据cid从menus中查询结点的函数 getNodeByCid(cid, nodes) { if (cid === 0) { return null; } // 递归查询 for (var i = 0; i < nodes.length; i++) { if (nodes[i].data.catId === cid) { return nodes[i]; } if (nodes[i].childNodes && nodes[i].childNodes.length > 0) { var node = this.getNodeByCid(cid, nodes[i].childNodes); if (node) { return node; } } } return null; }, // 递归更新draggingNode及其子节点的catLevel updateCatLevel(node) { if (!node) { return; } this.nodesInfoAfterDrop.push({catId: node.data.catId, catLevel: node.level}); if (node.childNodes && node.childNodes.length > 0) { node.childNodes.forEach((child) => { this.updateCatLevel(child); }); } }, allowDrag(draggingNode, dropNode, dropPosition) { var deep = this.countDraggingNodeDeep(draggingNode); // 根据dropPosition结合dropNode.catLevel来判断draggingNode新位置的位置是否合法 if (dropPosition === "prev" || dropPosition === "next") { return dropNode.data.catLevel + deep - 1 <= 3; } else if (dropPosition === "inner") { return dropNode.data.catLevel + deep <= 3; } }, // 递归计算draggingNode子树的深度 countDraggingNodeDeep(draggingNode) { var deep = 0; if (draggingNode.childNodes && draggingNode.childNodes.length > 0) { debugger; draggingNode.childNodes.forEach((child) => { deep = Math.max(deep, this.countDraggingNodeDeep(child)); }); } return deep + 1; }, append(data) { console.log(data); this.dialogType = "create"; this.dialogTitle = "新增分类"; this.dialogFormVisible = true; this.category = { name: "", parentCid: data.catId, catLevel: data.level + 1, sort: 0, showStatus: 1, }; }, edit(data) { console.log(data); this.dialogType = "edit"; this.dialogTitle = "修改分类"; this.dialogFormVisible = true; // 根据catId查询最新数据 this.$http({ url: this.$http.adornUrl(`/product/category/info/${data.catId}`), method: "get", data: this.$http.adornData({ catId: data.catId }, false), }).then(({ data }) => { if (data && data.code === 0) { this.category = { ...data.data }; } else { this.$message.error(data.msg); } }); }, submitCategory() { if (this.dialogType === "create") { this.addCategory(); } else if (this.dialogType === "edit") { this.updateCategory(); } }, updateCategory() { var { catId, name, icon, productUnit } = this.category; console.log(this.category); this.$http({ url: this.$http.adornUrl("/product/category/update"), method: "post", data: this.$http.adornData({ catId, name, icon, productUnit }, false), }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "修改成功", type: "success", duration: 1500, onClose: () => { console.log("修改成功,关闭消息提示"); this.dialogFormVisible = false; this.getMenus(); // 重新获取数据 this.expandedKeys = [ this.category.parentCid == 0 ? this.category.catId : this.category.parentCid, ]; // 重置展开节点 console.log(this.expandedKeys); }, }); } else { this.$message.error(data.msg); } }); }, addCategory() { this.$http({ url: this.$http.adornUrl("/product/category/save"), method: "post", data: this.$http.adornData(this.category, false), }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "添加成功", type: "success", duration: 1500, onClose: () => { console.log("添加成功,关闭消息提示"); this.dialogFormVisible = false; this.getMenus(); // 重新获取数据 this.expandedKeys = [this.category.parentCid]; // 重置展开节点 }, }); } else { this.$message.error(data.msg); } }); }, remove(node, data) { console.log(node, data); var ids = [node.data.catId]; this.$confirm( `确定对[id=${ids.join(",")}]进行[${ ids.length == 1 ? "删除" : "批量删除" }]操作?`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", } ) .then(() => { this.$http({ url: this.$http.adornUrl("/product/category/delete"), method: "post", data: this.$http.adornData(ids, false), }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "操作成功", type: "success", duration: 1500, onClose: () => { console.log("删除成功,关闭消息提示"); this.getMenus(); // 重新获取数据 this.expandedKeys = [node.parent.data.catId]; // 重置展开节点 }, }); } else { this.$message.error(data.msg); } }); }) .catch(() => {}); }, // 获取分类数据 getMenus() { this.dataListLoading = true; this.$http({ url: this.$http.adornUrl("/product/category/list/tree"), method: "get", }).then(({ data }) => { console.log(data); this.dataListLoading = false; this.menus = data.data; }); }, }, created() { this.getMenus(); // 获取分类数据 }, }; </script> <style scoped> </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。