贞丰县建设局网站,网站流程,wordpress 分表,平面设计师和网页设计师哪个好概述#xff1a;
本三级联动分类服务端使用的是: Springboot MyBatis-plus#xff0c;前端使用的是#xff1a;VueElementUI#xff0c;树形控件使用的是el-tree。本三级联动分类可以把任一子项拖拽到其它目录#xff0c;可以添加、编辑、删除分类。
效果图#xff1a…概述
本三级联动分类服务端使用的是: Springboot MyBatis-plus前端使用的是VueElementUI树形控件使用的是el-tree。本三级联动分类可以把任一子项拖拽到其它目录可以添加、编辑、删除分类。
效果图 实现流程
一、树形数据展示实现
1、创建表
cat_id使用bigint类型目的是为了使用雪花算法加快查询的速度parent_cid父ID用于子节点指向父亲节点同理我们可根据当前节点等于子节点点父ID然后递归找到所属的所有子节点cat_level层级用于识别当前节点是一级、二级还是三级节点show_status是否显示控制是否显示使用了逻辑删除功能product_unit计量单位使用什么单位统计当前分类下产品的数量product_count产品数量在添加商品时进行统计更新sort排序进行分类排序icon图表类别的图片使用iconfont
CREATE TABLE kmall_product.pro_category (cat_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 分类id,name char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 分类名称,parent_cid bigint(20) NULL DEFAULT NULL COMMENT 父分类id,cat_level int(4) NULL DEFAULT NULL COMMENT 层级,show_status tinyint(2) NULL DEFAULT NULL COMMENT 是否显示[0-不显示1显示],sort int(4) NULL DEFAULT NULL COMMENT 排序,icon char(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 图标地址,product_unit char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 计量单位,product_count int(11) NULL DEFAULT NULL COMMENT 商品数量,PRIMARY KEY (cat_id) USING BTREE,INDEX parent_cid(parent_cid) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 1433 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT 商品三级分类 ROW_FORMAT Dynamic;2、生成实体类
把上面点表内能映射为实体类便于java中操作
Data
TableName(pro_category)
public class CategoryEntity implements Serializable {private static final long serialVersionUID 1L;/*** 分类id*/TableIdprivate Long catId;/*** 分类名称*/private String name;/*** 父分类id*/private Long parentCid;/*** 层级*/private Integer catLevel;/*** 是否显示[0-不显示1显示]*/TableLogic(value 1, delval 0)private Integer showStatus;/*** 排序*/private Integer sort;/*** 图标地址*/private String icon;/*** 计量单位*/private String productUnit;/*** 商品数量*/private Integer productCount;JsonInclude(value JsonInclude.Include.NON_EMPTY)TableField(exist false)private ListCategoryEntity children;}说明下面的children是用来保存子节点数据是暂存在内存中所以要加上TableField(exist false)注解 JsonInclude(value JsonInclude.Include.NON_EMPTY)TableField(exist false)private ListCategoryEntity children;3、获取树形数据
创建CategoryController控制层添加请求方法使用的是get请求查询到结果后以data为名称返回给前端
/*** 查出所有分类以及子分类以树形结构组装起来*/RequestMapping(/list/tree)public R listWithTree(){ListCategoryEntity entities categoryService.listWithTree();return R.ok().put(data, entities);}4、查询封装树形数据
创建CategoryServiceImpl实现层实现listWithTree方法
首先查出所有分类选出一级分类递归获取一级分类的所有子节点设置到children属性中
Overridepublic ListCategoryEntity listWithTree() {//查出所有分类ListCategoryEntity entities baseMapper.selectList(null);//组装成父子树形结构//1级分类ListCategoryEntity menus entities.stream().filter(entity - entity.getParentCid() 0).map(entity - {entity.setChildren(getChildren(entity, entities));return entity;}).sorted((menu1, menu2) -(menu1.getSort() null ? 0 : menu1.getSort()) -(menu2.getSort() null ? 0 : menu2.getSort())).collect(Collectors.toList());return menus;}/*** 递归查找所有菜单的子菜单*/private ListCategoryEntity getChildren(CategoryEntity root, ListCategoryEntity all) {ListCategoryEntity children all.stream().filter(entity - entity.getParentCid().equals(root.getCatId())).map(entity - {entity.setChildren(getChildren(entity, all)); //递归设置return entity;}).sorted((menu1, menu2) -(menu1.getSort() null ? 0 : menu1.getSort()) -(menu2.getSort() null ? 0 : menu2.getSort())).collect(Collectors.toList());return children;}5、前端展现实现
5.1、树形控件el-tree设置
:data“menus”绑定数据menus:props“defaultProps” 设置默认属性defaultProps便于与后端数据转换expand-on-click-node设置为false表示需要单击才展开show-checkbox显示checkbox复选框node-key“catId” 绑定主键:default-expanded-keys“expandedKey” 设置默认展开分类:draggable“draggable” 是否能拖拽节点数据:allow-drop“allowDrop”拖拽时判定目标节点能否被放置。type 参数有三种情况‘prev’、‘inner’ 和 ‘next’分别表示放置在目标节点前、插入至目标节点和放置在目标节点后node-drop“handleDrop”拖拽成功完成时触发的事件
el-tree :datamenus :propsdefaultProps :expand-on-click-nodefalse show-checkbox node-keycatId:default-expanded-keysexpandedKey :draggabledraggable :allow-dropallowDrop node-drophandleDroprefmenuTree /el-tree5.2、树形控件el-tree数据初始化
data() {return {pCid: [],draggable: false,updateNodes: [],maxLevel: 0,title: ,dialogType: , //edit,addcategory: {name: ,parentCid: 0,catLevel: 0,showStatus: 1,sort: 0,productUnit: ,icon: ,catId: null},dialogVisible: false,menus: [],expandedKey: [],defaultProps: {children: children,label: name}};},5.3、加载menus数据绑定到树形控件
说明/product/category/list/tree此路径对应的是controller里查找树形数据的方法
getMenus() {this.$http({url: this.$http.adornUrl(/product/category/list/tree),method: get}).then(({data}) {console.log(成功获取到菜单数据..., data.data);this.menus data.data;});},二、添加编辑删除树形控件实现
1、前端添加、编辑、删除实现
1.1、 html内容
只有叶子节点才显示删除功能所有的分类都可以编辑只有一级、二级分类有添加功能
el-button v-ifdraggable clickbatchSave批量保存/el-button
el-button sizemini typedanger clickbatchDelete批量删除/el-button
el-tree :datamenus :propsdefaultProps :expand-on-click-nodefalse show-checkbox node-keycatId:default-expanded-keysexpandedKey :draggabledraggable :allow-dropallowDrop node-drophandleDroprefmenuTreespan classcustom-tree-node slot-scope{ node, data }span{{ node.label }}/spanspanel-button v-ifnode.level 2 typetext sizemini click() append(data)添加/el-buttonel-button typetext sizemini clickedit(data)编辑/el-buttonel-button v-ifnode.childNodes.length0 typetext sizeminiclick() remove(node, data)删除/el-button/span/span/el-tree1.2、js实现
添加
append(data) {console.log(append, data);this.dialogType add;this.title 添加分类;this.dialogVisible true;this.category.parentCid data.catId;this.category.catLevel data.catLevel * 1 1;this.category.catId null;this.category.name ;this.category.icon ;this.category.productUnit ;this.category.sort 0;this.category.showStatus 1;},//添加三级分类addCategory() {console.log(提交的三级分类数据, this.category);this.$http({url: this.$http.adornUrl(/product/category/save),method: post,data: this.$http.adornData(this.category, false)}).then(({data}) {this.$message({message: 菜单保存成功,type: success});//关闭对话框this.dialogVisible false;//刷新出新的菜单this.getMenus();//设置需要默认展开的菜单this.expandedKey [this.category.parentCid];});},submitData() {if (this.dialogType add) {this.addCategory();}if (this.dialogType edit) {this.editCategory();}},批量添加
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; });},编辑
edit(data) {console.log(要修改的数据, data);this.dialogType edit;this.title 修改分类;this.dialogVisible true;//发送请求获取当前节点最新的数据this.$http({url: this.$http.adornUrl(/product/category/info/${data.catId}),method: get}).then(({data}) {//请求成功console.log(要回显的数据, data);this.category.name data.data.name;this.category.catId data.data.catId;this.category.icon data.data.icon;this.category.productUnit data.data.productUnit;this.category.parentCid data.data.parentCid;this.category.catLevel data.data.catLevel;this.category.sort data.data.sort;this.category.showStatus data.data.showStatus; });},//修改三级分类数据editCategory() {var {catId,name,icon,productUnit} this.category;this.$http({url: this.$http.adornUrl(/product/category/update),method: post,data: this.$http.adornData({catId,name,icon,productUnit}, false)}).then(({data}) {this.$message({message: 菜单修改成功,type: success});//关闭对话框this.dialogVisible false;//刷新出新的菜单this.getMenus();//设置需要默认展开的菜单this.expandedKey [this.category.parentCid];});},删除单个节点
remove(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({message: 菜单删除成功,type: success});//刷新出新的菜单this.getMenus();//设置需要默认展开的菜单this.expandedKey [node.parent.data.catId];});}).catch(() {});console.log(remove, node, data);}批量删除 1首先根据选中的节点获取选中的节点 catId 2提示是否删除 3如果确定删除则调用服务层的批量删除方法 4删除成功后提示并重新加载菜单 batchDelete() {let catIds [];let checkedNodes this.$refs.menuTree.getCheckedNodes();console.log(被选中的元素, checkedNodes);for (let i 0; i checkedNodes.length; i) {catIds.push(checkedNodes[i].catId);}this.$confirm(是否批量删除【${catIds}】菜单?, 提示, {confirmButtonText: 确定,cancelButtonText: 取消,type: warning}).then(() {this.$http({url: this.$http.adornUrl(/product/category/delete),method: post,data: this.$http.adornData(catIds, false)}).then(({data}) {this.$message({message: 菜单批量删除成功,type: success});this.getMenus();});}).catch(() {});},2、服务端添加、编辑、删除实现
添加 RequestMapping(/save)public R save(RequestBody CategoryEntity category){categoryService.save(category);return R.ok();}编辑 RequestMapping(/info/{catId})public R info(PathVariable(catId) Long catId){CategoryEntity category categoryService.getById(catId);return R.ok().put(data, category);}RequestMapping(/update)public R update(RequestBody CategoryEntity category){categoryService.updateCascade(category);return R.ok();}删除 RequestMapping(/delete)public R delete(RequestBody Long[] catIds){//1.检查当前删除的分类是否被别的地方引用categoryService.removeMenuByIds(Arrays.asList(catIds));return R.ok();}Overridepublic void removeMenuByIds(ListLong asList) { //逻辑删除baseMapper.deleteBatchIds(asList);}
三、树形控件拖拽功能实现
1、前端实现
1.1、html部分添加拖拽控制开关 el-switch v-modeldraggable active-text开启拖拽 inactive-text关闭拖拽/el-switchel-button v-ifdraggable clickbatchSave批量保存/el-buttonel-button sizemini typedanger clickbatchDelete批量删除/el-buttonel-tree :datamenus :propsdefaultProps :expand-on-click-nodefalse show-checkbox node-keycatId:default-expanded-keysexpandedKey :draggabledraggable :allow-dropallowDrop node-drophandleDroprefmenuTreespan classcustom-tree-node slot-scope{ node, data }span{{ node.label }}/spanspanel-button v-ifnode.level 2 typetext sizemini click() append(data)添加/el-buttonel-button typetext sizemini clickedit(data)编辑/el-buttonel-button v-ifnode.childNodes.length0 typetext sizeminiclick() remove(node, data)Delete/el-button/span/span/el-tree1.2、js部分
计算可拖拽的信息找到所有子节点求出最大深度 1被拖动的当前节点以及所在的父节点总层数不能大于3 2计算当前节点所在的最大层级一般是 3 , 3计算当前节点所在的最大深度一般是 3 , 公式let deep Math.abs(this.maxLevel - draggingNode.level) 1; 4判断拖拽是在内部还是前后如果是拖拽到某节点的内部必须deep dropNode.level 3; 5否则 deep dropNode.parent.level 3; 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.maxLevelif (type inner) { 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]);}}else{this.maxLevel node.level}},处理拖拽的节点排序
handleDrop(draggingNode, dropNode, dropType, ev) {console.log(handleDrop: , draggingNode, dropNode, dropType);//1、当前节点最新的父节点idlet pCid 0;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 ! draggingNode.level) {//当前节点的层级发生变化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);},2、服务端实现 RequestMapping(/update/sort)public R updateSort(RequestBody ListCategoryEntity categorys){categoryService.updateBatchById(categorys);return R.ok();}源码下载 https://gitee.com/charlinchenlin/koo-erp