文章目录
- 一,54-商品服务-API-三级分类-修改-拖拽效果
- 1,el-tree控件加上允许拖拽的属性
- 2,是否允许拖拽
- 3,完整代码
一,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>