<template>
<div>
<el-switch
v-model="enabledrag"
active-text="开启拖拽"
inactive-text="关闭拖拽"
>
</el-switch>
<el-button type="danger" @click="deleteBatch">批量删除</el-button>
<el-tree
:data="menus"
:props="defaultProps"
show-checkbox
node-key="catId"
:expand-on-click-node="false"
:default-expanded-keys="defaultkeys"
:draggable="enabledrag"
:allow-drop="allowDrop"
@node-drop="handleDropSuccess"
ref="menuTree"
>
<!-- @node-drop 拖拽成功时触发的事件-->
<!--:allow-drop : v-bind 绑定属性 allowDrop不是data里面的值 而是一个方法 这个方法返回的值-->
<!--default-expanded-keys 表示默认展开的菜单id的数组-->
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span>
<!--可以在控制台打印 node data 两个对象的结构-->
<!--只有在node的等级是1 2 时才能有append操作 第三季分类不能有append-->
<!--node data对象都是elementui tree组件中封装好了的-->
<el-button
v-if="node.level <= 2"
type="text"
size="mini"
@click="() => append(data)"
>
Append
</el-button>
<el-button
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)"
>
Delete
</el-button>
<el-button type="text" size="mini" @click="() => edit(data)">
Edit
</el-button>
</span>
</span>
</el-tree>
<el-dialog :title="dialogtitle" :visible.sync="dialogFormVisible">
<el-form :model="categorymenu">
<el-form-item label="菜单名称">
<el-input v-model="categorymenu.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="菜单图表">
<el-input v-model="categorymenu.icon" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="计量单位(件、个、箱等)">
<el-input
v-model="categorymenu.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="submitData">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json 文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
export default {
//import 引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
return {
menus: [], //要显示在页面的分类数据
defaultProps: {
children: "children", // 表示子树是menus中的哪一个部分 打印menus到控制台可以发现子树是children属性
label: "name", //表示menus中的对象的哪一个属性 打印得到的menus数据 可以发现 我们希望把分类的
//name 作为树形显示的内容 所以这里写name
},
//默认展开的菜单id数组 每次删除节点后 展开被删除节点的父节点
defaultkeys: [],
//append菜单的对象 和后端的实体属性相对应
categorymenu: {
name: "",
parentCid: 0,
catLevel: 0,
showStatus: 1,
sort: 0,
catId: null,
icon: "",
productUnit: "",
},
dialogFormVisible: false, //append菜单的对话框是否显示
dialogtype: "", // append添加新菜单 edit编辑菜单
dialogtitle: "",
maxlevel: 0,
updataNodes: [],
enabledrag: false,
};
},
methods: {
deleteBatch() {
let nodes = this.$refs.menuTree.getCheckedNodes(); //所有选中的节点组成的数组
let deleteIds = [];
let deleteNames = [];
console.log("所有选中的节点:", nodes);
for (let i = 0; i < nodes.length; i++) {
deleteIds.push(nodes[i].catId); //取出所有选中节点的id值 把这些id值封装成一个数组
deleteNames.push(nodes[i].name);
}
this.$confirm(`确认删除, 是否继续?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/category/delete"),
method: "post",
data: this.$http.adornData(deleteIds, false),
}).then(({ data }) => {
this.$message({
type: "success",
message: "已成功删除",
});
this.getMenus()//删除成功后刷新菜单
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
},
//方法 判断是否允许拖拽放置
//只有三级菜单 在拖拽时要考虑菜单级数不能大于3
allowDrop(draggingNode, dropNode, type) {
//被拖动的当前节点的层数和放置这个节点的父节点的级数之和不能大于3
console.log("当前正在拖动的节点:", draggingNode, dropNode, type);
//判断被拖动节点的子节点的最大级数
this.countNodeDeep(draggingNode);
//被拖动的当前节点和他的子节点一共有几层 这个节点可能在二级 但这个二级节点没有子菜单节点 那么层数为1
// 用以下方式计算 比如被拖动的节点在第二级 子节点的最大级数为三 那么被拖动的节点一共有(3-2)+1 2层
console.log("被拖拽节点的最大级数:", this.maxlevel);
var deeps = this.maxlevel - draggingNode.level + 1;
console.log("被拖拽节点的层数:", deeps);
if (type == "inner") {
//被拖动的节点放在 dropNode的里面时
return dropNode.level + deeps <= 3;
} else {
return dropNode.parent.level + deeps <= 3;
//被拖动的节点放在 dropNode的前面或后面 type=="next" or "prev"
}
},
//方法 判断这个节点的最深子节点在第几级
countNodeDeep(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.countNodeDeep(node.childNodes[i]); //递归
}
} else {
this.maxlevel = node.level; //如果这个节点没有子节点 已经是最大节点
}
},
//方法 拖拽成功后触发的事件
// 拖拽成功后 要改变节点的排序信息 父节点信息 层级level信息 写入数据库中
handleDropSuccess(draggingNode, dropNode, dropType, ev) {
let pCid = 0; //拖拽成功后的节点的父节点
let siblings = null; //拖拽成功后的节点的兄弟节点(包括拖拽成功的节点自己)
// 也就是说 当拖拽成功以后 dropNode中的childNodes parent.childNodes就会自动添加被拖拽的节点
// node 中的值 是根据当前页面的 节点结构来的 data中的值是数据库 中的
//比如 把一个二级节点拖成一级节点 在其他一级节点的parent.childNodes中可以找到这个新一级节点
// 这个新一级节点的 level值就是 1 当前页面这个节点的实际层级数为 1
//而 这个新一级节点的 data里的catLevel 还是 2
if (dropType == "inner") {
pCid = dropNode.data.catId;
siblings = dropNode.childNodes;
} else {
pCid =
dropNode.parent.data.catId == undefined
? 0
: dropNode.parent.data.catId;
siblings = dropNode.parent.childNodes;
}
for (let i = 0; i < siblings.length; i++) {
if (siblings[i].data.catId == draggingNode.data.catId) {
//当遍历的节点就是被拖拽的节点时 要修改节点的排序 父节点 level值及所有子节点的level值
let nodelevel = draggingNode.level;
if (siblings[i].level != draggingNode.level) {
nodelevel = siblings[i].level;
//递归修改子节点的层级
this.updateNodeLevel(siblings[i]);
}
this.updataNodes.push({
catId: siblings[i].data.catId,
sort: i,
parentCid: pCid,
catLevel: nodelevel,
});
} else {
//当遍历的节点是其他兄弟节点只需修改排序顺序
this.updataNodes.push({ catId: siblings[i].data.catId, sort: i });
}
}
console.log(this.updataNodes);
this.$http({
url: this.$http.adornUrl("/product/category/update/sort"),
method: "post",
data: this.$http.adornData(this.updataNodes, false),
}).then(({ data }) => {
this.getMenus();
this.defaultkeys = [pCid];
this.$message({
type: "success",
message: "拖拽成功!",
});
});
this.maxlevel = 0;
this.updataNodes = [];
},
//递归修改子节点的层级
updateNodeLevel(node) {
if (node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
var subnodelevel = node.childNodes[i].level;
var subnodeCid = node.childNodes[i].data.catId;
this.updataNodes.push({ catId: subnodeCid, catLevel: subnodelevel });
this.updateNodeLevel(node.childNodes[i]);
}
}
},
//点击对话框的确认按钮时 先判断是 添加新节点 还是 编辑旧节点
submitData() {
if (this.dialogtype == "add") {
this.saveNewMenu();
}
if (this.dialogtype == "edit") {
this.editCategory();
}
},
//点击编辑节点时
//这里传的参数data 是由elementui tree组件封装好的data
edit(data) {
console.log(data);
this.dialogtitle = "编辑菜单";
this.dialogtype = "edit";
this.dialogFormVisible = true; //打开编辑框
//回显数据需要重新请求一次得到最新的 有可能多个人在管理页面编辑菜单 当你登上去过后页面请求
//得到菜单数据 过一段时间 在这段时间内你没有操作 而别人操作了菜单数据 此时页面上的数据就不是最新的
//页面加载完成之后只请求一次菜单数据 在编辑之前需要我们手动再请求一次 得到最新的数据
this.$http({
//这里的url 和 static/config/index.js中的baseUrl做拼接
url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
method: "get",
// params: this.$http.adornParams({})
}).then(({ data }) => {
//这里({data})相当于返回的那一大坨整体的里面的data 结构
console.log("获取回显的数据:", data); //注意这里想查看数据需要用逗号 不能用 + +号只显示 【Object object】
this.categorymenu.name = data.category.name; //回显名称
this.categorymenu.icon = data.category.icon; //回显图标
this.categorymenu.productUnit = data.category.productUnit; //回显计量单位
this.categorymenu.catId = data.category.catId;
this.categorymenu.parentCid = data.category.parentCid;
});
},
editCategory() {
var { catId, name, icon, productUnit } = this.categorymenu; //解构 直接得到对象中的部分属性值
this.dialogFormVisible = false;
this.$http({
//这里的 url 是和 static/config/index.js中的 baseUrl拼接起来的
url: this.$http.adornUrl("/product/category/update"),
method: "post",
// 相当于 var category={catId:catId,name:name,icon:icon,productUnit:productUnit}
//把这个category对象 传递到下面的请求中 当属性名和属性值同名时可以只写一个
data: this.$http.adornData({ catId, name, icon, productUnit }, false),
}).then(({ data }) => {
//1.提示信息
this.$message({
type: "success",
message: "编辑成功!",
});
//2.刷新菜单页面
this.getMenus();
//3.展开被编辑节点的父节点
this.defaultkeys = [this.categorymenu.parentCid];
});
},
//添加菜单节点
append(data) {
console.log(data);
this.dialogtitle = "添加菜单节点";
this.dialogtype = "add";
this.dialogFormVisible = true;
this.categorymenu.name = ""; //清空表单输入框
this.categorymenu.icon = "";
this.categorymenu.productUnit = "";
this.categorymenu.catId = null; //设置要添加的节点的部分值
this.categorymenu.parentCid = data.catId;
this.categorymenu.catLevel = data.catLevel * 1 + 1; //先乘1 表示把字符串变成数字
},
//确认添加新菜单
saveNewMenu() {
this.dialogFormVisible = false;
this.$http({
url: this.$http.adornUrl("/product/category/save"),
method: "post",
data: this.$http.adornData(this.categorymenu, false),
}).then(({ data }) => {
this.$message({
type: "success",
message: "添加成功!",
});
this.getMenus(); //添加成功后刷新菜单
this.defaultkeys = [this.categorymenu.parentCid]; //添加成功后 默认展开被添加节点的父节点
});
},
//删除菜单节点
remove(node, data) {
//获取要删除的节点的id值
console.log(node, data);
var ids = [data.catId];
this.$confirm(`确认删除【${data.name}】, 是否继续?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/product/category/delete"),
method: "post",
data: this.$http.adornData(ids, false),
}).then(({ data }) => {
this.$message({
type: "success",
message: "删除成功!",
});
console.log("删除成功...");
//删除成功后刷新菜单页面
this.getMenus();
//展开被删除节点的父节点 更好的看到删除后的效果
this.defaultkeys = [node.data.parentCid]; //可以看到打印后的node对象的结构
//this.defaultkeys = [node.parent.data.catId]; //这种好像也可以 不过都是用node 而不用data 是不是因为此时节点已删除 data没有数据了吗
});
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
},
//获取所有的商品分类菜单
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get",
}).then(({ data }) => {
//这里写 ({data}) 指的是提取出返回的response对象数据里的data
// 结构 相当于 得到 本来返回的resposne对象的data属性的值
console.log(data);
this.menus = data.categorylist;
});
},
},
//计算属性 类似于 data 概念
computed: {},
//监控 data 中的数据变化
watch: {},
//生命周期 - 创建完成(可以访问当前 this 实例)
created() {
this.getMenus();
},
//生命周期 - 挂载完成(可以访问 DOM 元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {}, //如果页面有 keep-alive 缓存功能,这个函数会触发 页面只要一激活就会调用这个方法
};
</script>
<style scoped>
</style>
category.vue备份
最新推荐文章于 2023-08-01 12:17:18 发布