文章目录
一,54-商品服务-API-三级分类-修改-拖拽效果
本节的主要内容是给三级分类树形结构加上拖拽功能,并且根据分类不能大于三级的规则判断是否能拖拽。
1,el-tree控件加上允许拖拽的属性
el-tree控件加上允许拖拽的属性draggable,此外还需要根据层级判断是否能拖动,通过给allow-drop绑定事件allowDrag实现这个需求。
allowDrag(draggingNode, dropNode, dropPosition) { console.log(draggingNode, dropNode, dropPosition); return true },
在 Element UI(El-UI)的树组件 el-tree
中,allow-drop
事件是一个自定义槽函数,用于控制是否允许将一个节点拖放到另一个节点上。这个函数接受三个参数,分别代表正在拖动的节点、可能的放置目标节点以及放置位置。这三个参数具体如下:
draggingNode:
这个参数是TreeNode
类型的对象,表示当前正在被拖动的节点。它包含了关于拖动节点的所有信息,如节点的数据、状态等。dropNode:
同样是TreeNode
类型的对象,表示潜在的放置目标节点。这是你可能要将draggingNode
放置到的节点。如果拖动过程中没有特定的放置目标(例如,拖动到树的空白区域),这个参数可能是undefined
或者不适用。dropPosition:
表示相对于dropNode
的放置位置。这是一个字符串,可以是'before'
、'after'
或'inner'
,分别表示拖动的节点将放置在目标节点之前、之后或内部。如果dropNode
是undefined
,则这个参数可能表示放置在树的顶部或底部。
allow-drop
函数应该返回一个布尔值,指示是否允许进行拖放操作。如果返回 true
,则允许拖放;如果返回 false
,则阻止拖放操作。例如,在你的代码中:
接下来实现这个函数的逻辑。
原则是当前拖动的阶段到达要放置的位置后,层级数不能超过3,所以核心有3点:
- ①计算出以拖动结点为根结点的子树的深度deep。
- ②结合目标结点的深度及放置位置的类型,判断新位置的层级level。
- ③deep + level <=3 时允许拖动。
关于第②点,新位置的类型可能有三种:
- prev,目标节点的前面
- inner,目标节点的子节点
- next,目标节点的后面
2,是否允许拖拽
①
递归统计draggingNode子树的深度。
// 递归计算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; },
②
结合draggingNode子树的深度和位置判断是否能拖动。
allowDrag(draggingNode, dropNode, dropPosition) { console.log(draggingNode, dropNode, dropPosition); var deep = this.countDraggingNodeDeep(draggingNode); console.log(deep, dropNode.data.catLevel + deep); // 根据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; } },
3,完整代码
<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" 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 { 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: { allowDrag(draggingNode, dropNode, dropPosition) { console.log(draggingNode, dropNode, dropPosition); var deep = this.countDraggingNodeDeep(draggingNode); console.log(deep, dropNode.data.catLevel + deep); // 根据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>