- 1 三级分类
- 1.1 数据准备
- 1.2 查询功能实现
- 1.2.1 controller
- 1.2.2 entity
- 1.2.2 service
- 1.2.3 测试
- 1.3 配置网关路由与路径重写
- 1.3.2 配置renren-fast
- 1.4 跨域
- 1.4.1 什么是跨域
- 1.4.2 跨域流程
- 1.4.3 解决跨域的方法
- 1.4.4 网关配置跨域
- 1.5 树形展示分类数据
- 1.5.1 添加网关路由规则
- 1.5.2 配置注册nacos
- 1.5.3 前端页面
- 1.5.4 测试
- 1.6 删除分类实现
- 1.6.1 前端页面
- 1.6.2 测试删除逻辑
- 1.6.3 实现逻辑删除
- 1.6.4 删除效果细化
- 1.7 新增分类实现
- 1.8 修改效果实现
- 1.9 拖拽效果实现
- 1.9.1 前端效果实现
- 1.9.2 前端数据更新
- 1.9.3 后端数据更新
- 1.9.4 批量保存拖拽效果
- 1.10 批量删除节点实现
导入数据pms_catelog.sql到表 psm_category
文件下载地址:https://share.weiyun.com/bO0OZMCv
连接失效可以到尚硅谷微信公众号输入“谷粒商城”获取
1.2 查询功能实现 1.2.1 controller修改CategoryController.java里的list方法
@RequestMapping("/list/tree")
// @RequiresPermissions("product:category:list")
public R list(){
List entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
1.2.2 entity
修改CategoryEntity.java,添加 children属性
@TableField(exist = false) private List1.2.2 servicechildren;
在CategoryService.java声明方法
在CategoryServiceImpl.java实现方法
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl implements CategoryService {
@Override
public PageUtils queryPage(Map params) {
IPage page = this.page(
new Query().getPage(params),
new QueryWrapper()
);
return new PageUtils(page);
}
@Override
public List listWithTree() {
//1、查出所有分类
List entities = baseMapper.selectList(null);
//2、组装成父子的树形结构
List level1Menus = entities.stream()
// 过滤,取出所有1级分类的记录
.filter(categoryEntity -> categoryEntity.getParentCid() == 0)
// 递归映射子分类
.map((menu) -> {
menu.setChildren(getChildrens(menu, entities));
return menu;
})
.sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
.collect(Collectors.toList());
return level1Menus;
}
//递归查找所有菜单的子菜单
private List getChildrens(CategoryEntity root, List all) {
List children = all.stream()
// 过滤出分类的父级与传入参数的等级一样的记录,级1级分类找2级分类
.filter(categoryEntity -> categoryEntity.getParentCid().equals(root.getCatId()))
.map(categoryEntity -> {
//1、递归找子菜单
categoryEntity.setChildren(getChildrens(categoryEntity, all));
return categoryEntity;
})
.sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
.collect(Collectors.toList());
return children;
}
}
1.2.3 测试
启动服务,测试,注意,先启动nacos
访问http://localhost:10000/product/category/list/tree
1.3 配置网关路由与路径重写1、idea启动renren-fast
2、vscode打开renren-fast-vue启动
3、进入前端开发平台,进入菜单管理新增菜单
可以在gulimall-admin数据库里的sys_menu表里看到
4、新增分类维护
角色管理视图对应renren-fast-vue里的src/views/modules/sys/role.vue文件
5、在modules文件夹下创建product文件夹
6、新建编写category.vue
7、staticconfigindex.js里定义了api接口请求地址,改写成全部给网关地址发请求
// api接口请求地址 window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';
验证码失效:需要将renren-fast注册进nacos
1.3.2 配置renren-fast1、让renren-fast依赖gulimall-common模块
坑:各种报错
**问题原因:**可能是因为renren-fast各种依赖包的版本和common里包的版本不同
**解决方法:**单独导入依赖包,例如我的renren-fast的springboot是2.6.6版本
只导入nacos-discover
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 2021.0.1.0 com.netflix.ribbon ribbon
2、在application.yml添加配置
spring:
application:
name: renren-fast
cloud:
nacos:
discovery:
server-addr: localhost:8848
3、开启服务的注册发现功能
@SpringBootApplication
@EnableDiscoveryClient
public class RenrenApplication {
public static void main(String[] args) {
SpringApplication.run(RenrenApplication.class, args);
}
}
4、重启服务
坑:启动服务报错java: 找不到符号 符号: 方法setOperation(java.lang.String)
解决方法:
将renren-fast里的的lombok依赖的版本改为
5、到nacos查看是否注册成功
6、配置网关规则
spring:
cloud:
gateway:
routes:
- id: baidu_route
uri: https://www.baidu.com
predicates:
- Query=url, baidu # 如果参数url的值为baidu,则页面跳转至https://www.baidu.com
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url, qq
- id: admin_route
uri: lb://renren-fast
predicates:
# 设定前端项目发送请求都带上api前缀
- Path=/api
@Configuration
public class GulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1、配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("*.xml
global-config:
db-config:
id-type: auto # 自增主键
server:
port: 10000
4、开启服务发现注解
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.atguigu.gulimall.product.dao")
public class GulimallProductApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallProductApplication.class, args);
}
}
1.5.3 前端页面
1、修改category.vue页面
1.5.4 测试
1、启动服务
- vue
- gulimall-gateway
- gulimall-product
- renren-fast
- nacos
访问http://localhost:8001/
登陆后进入分类维护页面即可看到效果
1.6 删除分类实现 1.6.1 前端页面修改category.vue
实现最末尾的分类可以删除,其他有子节点的节点不能被删除
末尾节点不能添加,其他节点可以添加
添加选项框
1.6.2 测试删除逻辑{{ node.label }} append(data)" > Append remove(node, data)" > Delete
使用postman测试删除逻辑
1.6.3 实现逻辑删除1、修改CategoryController里原来的删除逻辑
@RequestMapping("/delete")
//@RequiresPermissions("product:category:delete")
public R delete(@RequestBody Long[] catIds){
//categoryService.removeByIds(Arrays.asList(catIds));
categoryService.removeMenuByIds(Arrays.asList(catIds));
return R.ok();
}
2、创建removeMenuByIds接口并实现
CategoryServiceImpl:
@Override public void removeMenuByIds(ListasList) { //TODO 1、检查当前删除的菜单,是否被别的地方引用 //逻辑删除 baseMapper.deleteBatchIds(asList); }
3、配置全局的逻辑删除规则并调整日志打印级别,gulimall-productsrcmainresourcesapplication.yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.56.10:3306/gulimall_pms
driver-class-name: com.mysql.jdbc.Driver
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-product
mybatis-plus:
mapper-locations: classpath:/mapper*.xml
global-config:
db-config:
id-type: auto # 自增主键
logic-delete-value: 0 # 删除逻辑为0
logic-not-delete-value: 1 # 不删除逻辑为1
server:
port: 10000
4、实体类字段上加上@TableLogic注解,逻辑不删除用value,逻辑删除用delval,gulimall-productsrcmainjavacomatguigugulimallproductentityCategoryEntity.java
@TableLogic private Integer showStatus;
或
@TableLogic(value = "1", delval = "0") private Integer showStatus;
6、重启服务,用postman测试逻辑删除,查看打印的日志
1.6.4 删除效果细化1、前端发送删除请求
2、删除确认
3、消息提示
4、依旧展开父节点
修改后的category.vue代码
{{ node.label }} append(data)" > Append remove(node, data)" > Delete
设置数据库所有数据的show_status为1
UPDATE pms_category SET show_status=11.7 新增分类实现
1、点击新增弹出对话框
2、填写表单数据
3、发送post请求保存分类
4、关闭对话框
5、刷新菜单并展开
修改后的category.vue
{{ node.label }}
append(data)"
>
Append
remove(node, data)"
>
Delete
取 消
确 定
1.8 修改效果实现
1、添加修改按钮
2、区分对话框功能
3、不同提示信息
4、新增表单填写选项
5、回显图标和计量单位
6、回显最新的数据
7、修改后端的info方法,改完后记得重启
@RequestMapping("/info/{catId}")
// @RequiresPermissions("product:category:info")
public R info(@PathVariable("catId") Long catId) {
CategoryEntity category = categoryService.getById(catId);
return R.ok().put("data", category);
}
修改后的category.vue:
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定
1.9 拖拽效果实现
1.9.1 前端效果实现
1、在树形结构中允许拖拽
2、编写拖拽函数
修改后的category.vue:
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定
1.9.2 前端数据更新
1、影响的数据有parent_cid、cat_level、sort
2、添加拖拽结束函数
修改后的category.vue
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定
1.9.3 后端数据更新
1、后端编写批量修改方法,改完后记得重启
@RequestMapping("/update/sort")
// @RequiresPermissions("product:category:update")
public R updateSort(@RequestBody CategoryEntity[] category) {
categoryService.updateBatchById(Arrays.asList(category));
return R.ok();
}
2、用postman测试方法
3、前端发送请求
4、拖拽完成后初始化updateNodes和maxLevel
修改后的category.vue:
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定
1.9.4 批量保存拖拽效果
1、添加可选拖拽功能
2、统一提交拖拽结果
3、这里我做了一下修改,在点击保存拖拽结果后不展开最后的拖拽的父节点,就不设置pCid了
4、修改计算深度的代码逻辑
修改后的category.vue:
批量保存
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定
1.10 批量删除节点实现
1、添加删除按钮
2、给tree添加引用名
3、编写删除函数
改写后的category.vue:
批量保存
批量删除
{{ node.label }}
append(data)"
>
Append
edit(data)">
Edit
remove(node, data)"
>
Delete
取 消
确 定



