行业知识
day06
文章目录
- 1 菜单管理
- 1.1 表结构介绍
- 1.2 查询菜单
- 1.2.1 需求说明
- 1.2.2 页面制作
- 1.2.3 后端接口
- SysMenu
- SysMenuController
- SysMenuService
- MenuHelper
- SysMenuMapper
- SysMenuMapper.xml
- 1.2.4 前端对接
- sysMenu.js
- sysMenu.vue
- 1.3 添加菜单
- 1.3.1 需求说明
- 1.3.3 页面制作
- 1.3.3 后端接口
- SysMenuController
- SysMenuService
- SysMenuMapper
- SysMenuMapper.xml
- 1.3.4 前端对接
- 实现思路
- sysMenu.js
- sysMenu.vue
- 1.4 添加子菜单
- 1.4.1 需求说明
- 1.4.2 代码实现
- 1.5 修改菜单
- 1.5.1 需求说明
- 1.5.2 数据回显
- 1.5.3 提交修改
- 后端接口
- SysMenuController
- SysMenuService
- SysMenuMapper
- SysMenuMapper.xml
- 前端对接
- sysMenu.js
- sysMenu.vue
- 1.6 删除菜单
- 1.6.1 需求说明
- 1.6.2 后端接口
- SysMenuController
- SysMenuService
- SysMenuMapper
- SysMenuMapper.xml
- 1.6.3 前端对接
- sysMenu.js
- sysMenu.vue
- 2 角色分配菜单
- 2.1 需求说明
- 2.2 页面制作
- 2.3 查询菜单
- 2.3.1 后端接口
- SysRoleMenuController
- SysRoleMenuService
- SysRoleMenuMapper
- SysRoleMenuMapper.xml
- 2.3.2 前端对接
- sysRole.js
- sysRole.vue
- 2.4 保存菜单
- 2.4.1 后端接口
- AssginMenuDto
- SysRoleMenuController
- SysRoleMenuService
- SysRoleMenuMapper
- SysRoleMenuMapper.xml
- 2.4.2 前端对接
- sysRole.js
- sysRole.vue
- 3 动态菜单
- 3.1 需求说明
- 3.2 后端接口
- 3.2.1 SysMenuVo
- 3.2.1 IndexController
- 3.2.2 SysMenuService
- 3.2.3 SysMenuMapper
- 3.2.4 SysMenuMapper.xml
- 3.3 前端对接
- 3.3.1 前端加载流程
- 3.3.2 menu.js
- 3.3.3 index.js
- 3.3.4 menu.js
- 3.4 代码测试
- 3.4.1 测试内容说明
- 3.4.2 bug解决
- SysMenuServiceImpl
- SysMenuMapper
- SysMenuMapper.xml
- SysRoleMenuMapper
- SysRoleMenuMapper.xml
1 菜单管理
菜单���理就是对系统中首页中的左侧菜单进行维护。
1.1 表结构介绍
菜单表结果如下所示:
CREATE TABLE `sys_menu` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', `parent_id` bigint NOT NULL DEFAULT '0' COMMENT '所属上级', `title` varchar(20) NOT NULL DEFAULT '' COMMENT '菜单标题', `component` varchar(100) DEFAULT NULL COMMENT '组件名称', `sort_value` int NOT NULL DEFAULT '1' COMMENT '排序', `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(0:禁止,1:正常)', `has_children` tinyint NOT NULL COMMENT '是否存在在子节点', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)', PRIMARY KEY (`id`), KEY `idx_parent_id` (`parent_id`)) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='菜单表';
1.2 查询菜单
1.2.1 需求说明
由于菜单表结构是一种树形结构,因此在进行数据展示的时候需要按照树形表格的方式进行数据展示。
效果图如下所示:
1.2.2 页面制作
具体代码如下所示:
添 加 {{ scope.row.status == 1 ? '正常' : '停用' }} 添加下级节点 修改 删除 import { ref } from "vue"// 定义表格数据模型const list = ref([ { "id": 1, "title": "系统管理", "component": "system", "sortValue": 1, "status": 1, "createTime": "2023-05-04", "children":[ {"id":2, "title": "用户管理" , "component":"sysUser" , "sortValue":1 ,"status":1 , "createTime":"2023-05-04"}, {"id":3, "title": "角色管理" , "component":"sysRole" , "sortValue":2 ,"status":1 , "createTime":"2023-05-04"}, {"id":4, "title": "菜单管理" , "component":"sysMenu" , "sortValue":3 ,"status":1 , "createTime":"2023-05-04"} ] }, { "id": 5, "title": "基础数据管理", "component": "base", "sortValue": 2, "status": 1, "createTime": "2023-05-04", "children":[ {"id":6, "title": "商品单位" , "component":"productUnit" , "sortValue":2 ,"status":1 , "createTime":"2023-05-04"}, {"id":7, "title": "地区管理" , "component":"region" , "sortValue":1 ,"status":1 , "createTime":"2023-05-04"} ] }]).search-p { margin-bottom: 10px; padding: 10px; border: 1px solid #ebeef5; border-radius: 3px; background-color: #fff;}.tools-p { margin: 10px 0; padding: 10px; border: 1px solid #ebeef5; border-radius: 3px; background-color: #fff;}1.2.3 后端接口
SysMenu
创建与数据库表相对应的实体类:
// com.atguigu.spzx.model.entity.system@Datapublic class SysMenu extends BaseEntity {private Long parentId;private String title;private String component;private Integer sortValue;private Integer status;// 下级列表private List children;}SysMenuController
表现层代码实现
// com.atguigu.spzx.manager.controller@RestController@RequestMapping(value="/admin/system/sysMenu")public class SysMenuController { @Autowired private SysMenuService sysMenuService; @GetMapping("/findNodes") public Result findNodes() { List list = sysMenuService.findNodes(); return Result.build(list , ResultCodeEnum.SUCCESS) ; }}SysMenuService
业务层代码实现
// com.atguigu.spzx.manager.service.impl@Servicepublic class SysMenuServiceImpl implements SysMenuService { @Autowired private SysMenuMapper sysMenuMapper ; @Override public List findNodes() { List sysMenuList = sysMenuMapper.selectAll() ; if (CollectionUtils.isEmpty(sysMenuList)) return null; List treeList = MenuHelper.buildTree(sysMenuList); //构建树形数据 return treeList; }}MenuHelper
构建树形菜单的工具类:
// com.atguigu.spzx.manager.helperpublic class MenuHelper { /** * 使用递归方法建菜单 * @param sysMenuList * @return */ public static List buildTree(List sysMenuList) { List trees = new ArrayList(); for (SysMenu sysMenu : sysMenuList) { if (sysMenu.getParentId().longValue() == 0) { trees.add(findChildren(sysMenu,sysMenuList)); } } return trees; } /** * 递归查找子节点 * @param treeNodes * @return */ public static SysMenu findChildren(SysMenu sysMenu, List treeNodes) { sysMenu.setChildren(new ArrayList()); for (SysMenu it : treeNodes) { if(sysMenu.getId().longValue() == it.getParentId().longValue()) { if (sysMenu.getChildren() == null) { sysMenu.setChildren(new ArrayList()); } sysMenu.getChildren().add(findChildren(it,treeNodes)); } } return sysMenu; }}SysMenuMapper
持久层代码实现:
// com.atguigu.spzx.manager.mapper@Mapperpublic interface SysMenuMapper { public abstract List selectAll();}SysMenuMapper.xml
在映射文件中添加如下sql语句
id,parent_id,title,component,sort_value,status,create_time,update_time,is_deletedselect from sys_menuwhereis_deleted = 0order by sort_value
1.2.4 前端对接
sysMenu.js
在api文件夹下创建一个sysMenu.js文件,在该文件中添加如下代码:
import request from '@/utils/request'const api_name = '/admin/system/sysMenu'// 分页列表export const FindNodes = () => { return request({ url: `${api_name}/findNodes`, method: 'get', })}sysMenu.vue
更改sysMenu.vue的代码如下所示:
import { ref , onMounted } from "vue"import { FindNodes } from '@/api/sysMenu'// 定义表格数据模型const list = ref([])// 加载数据onMounted(() => { fetchData()})//分页列表const fetchData = async () => { const { code, data, message } = await FindNodes() list.value = data}1.3 添加菜单
1.3.1 需求说明
当用户点击添加按钮的时候,那么此时就弹出对话框,在该对话框中需要展示添加菜单表单。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要将提交过来的表单数据保存到数据库中即可。页面效果如下所示:
1.3.3 页面制作
具体代码如下所示:
添 加 正常 停用 提交 取消 // 定义添加表单菜单表单相关数据模型const dialogTitle = ref('添加')const dialogVisible = ref(false)//进入添加const addShow = () => { dialogVisible.value = true dialogTitle.value = '添加'}1.3.3 后端接口
SysMenuController
表现层代码实现
// com.atguigu.spzx.manager.controller.SysMenuController@PostMapping("/save")public Result save(@RequestBody SysMenu sysMenu) { sysMenuService.save(sysMenu); return Result.build(null , ResultCodeEnum.SUCCESS) ;}SysMenuService
业务层代码实现
// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl@Overridepublic void save(SysMenu sysMenu) { sysMenuMapper.insert(sysMenu) ;}SysMenuMapper
持久层代码实现:
// com.atguigu.spzx.manager.mapper#SysMenuMapperpublic abstract void insert(SysMenu sysMenu);
SysMenuMapper.xml
在映射文件中添加如下sql语句
insert into sys_menu ( id, parent_id, title, component, sort_value, status ) values ( #{id}, #{parentId}, #{title}, #{component}, #{sortValue}, #{status} )1.3.4 前端对接
实现思路
1、给表单绑定数据模型
2、给提交按钮绑定点击事件
3、点击按钮请求后端地址
sysMenu.js
在api文件夹下创建一个sysMenu.js文件,在该文件中添加如下代码:
// 保存信息export const save = sysMenu => { return request({ url: `${api_name}/save`, method: 'post', data: sysMenu, })}sysMenu.vue
更改sysMenu.vue的代码如下所示:
正常 停用 提交 取消 import { FindNodes , SaveMenu} from '@/api/sysMenu'import { ElMessage, ElMessageBox } from 'element-plus' //页面表单数据const defaultForm = { id: '', parentId: 0, title: '', url: '', component: '', icon: '', sortValue: 1, status: 1,}// 表单响应式数据模型对象const sysMenu = ref(defaultForm)//提交保存与修改const saveOrUpdate = () => { if (!sysMenu.value.id) { sysMenu.value.parentId = 0 saveData() } }// 新增const saveData = async () => { await SaveMenu(sysMenu.value) dialogVisible.value = false ElMessage.success('操作成功') fetchData()}1.4 添加子菜单
1.4.1 需求说明
当用户点击添加按钮的时候,那么此时就弹出对话框,在该对话框中需要展示添加子菜单表单。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要将提交过来的表单数据保存到数据库中即可。页面效果如下所示:
可以使用添加菜单的表单。
1.4.2 代码实现
只需要更改前端代码即可,如下所示:
添加下级节点 //进入添加const addShow = (row) => { sysMenu.value = {} dialogVisible.value = true if(!row.id) { dialogTitle.value = '添加' }else { dialogTitle.value = '添加下级节点' sysMenu.value.parentId = row.id }}1.5 修改菜单
1.5.1 需求说明
当用户点击修改按钮的时候,那么此时就弹出对话框,在该对话框中需要将当前行所对应的菜单数据在该表单页面进行展示。当用户在该表单中点击提交按钮的时候那么此时就需要将表单进行提交,在后端需要提交过来的表单数据修改数据库中的即可。页面效果如下所示:
1.5.2 数据回显
分析:
1、使用添加数据的表单即可
2、要将当前操作行的数据展示在表单中,那么此时需要用到插槽
代码如下所示:
修改 //进入修改const editShow = row => { sysMenu.value = row dialogVisible.value = true}1.5.3 提交修改
后端接口
SysMenuController
表现层代码实现
// com.atguigu.spzx.manager.controller#SysMenuController@PutMapping("/updateById")public Result updateById(@RequestBody SysMenu sysMenu) { sysMenuService.updateById(sysMenu); return Result.build(null , ResultCodeEnum.SUCCESS) ;}SysMenuService
业务层代码实现
// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl@Overridepublic void updateById(SysMenu sysMenu) { sysMenuMapper.updateById(sysMenu) ;}SysMenuMapper
持久层代码实现
// com.atguigu.spzx.manager.mapper.SysMenuMapperpublic abstract void updateById(SysMenu sysMenu);
SysMenuMapper.xml
映射文件中添加如下sql语句
update sys_menu set parent_id = #{parentId}, title = #{title}, component = #{component}, sort_value = #{sortValue}, status = #{status}, update_time = now() where id = #{id}前端对接
sysMenu.js
在api目录下创建一个sysMenu.js文件,文件的内容如下所示:
// 修改信息export const UpdateSysMenuById = sysMenu => { return request({ url: `${api_name}/updateById`, method: 'put', data: sysMenu, })}sysMenu.vue
更改sysMenu.vue的代码如下所示:
import { FindNodes , SaveMenu , UpdateSysMenuById } from '@/api/sysMenu' //提交保存与修改const saveOrUpdate = () => { if (!sysMenu.value.id) { saveData() } else { updateData() }}// 修改const updateData = async () => { await UpdateSysMenuById(sysMenu.value) dialogVisible.value = false ElMessage.success('操作成功') fetchData()}>1.6 删除菜单
1.6.1 需求说明
当点击删除按钮的时候此时需要弹出一个提示框,询问是否需要删除数据?如果用户点击是,那么此时向后端发送请求传递id参数,后端接收id参数进
行逻辑删除。页面效果如下所示:
1.6.2 后端接口
SysMenuController
表现层代码实现
// com.atguigu.spzx.manager.controller#SysMenuController@DeleteMapping("/removeById/{id}")public Result removeById(@PathVariable Long id) { sysMenuService.removeById(id); return Result.build(null , ResultCodeEnum.SUCCESS) ;}SysMenuService
业务层代码实现
// com.atguigu.spzx.manager.service.impl#SysMenuServiceImpl@Overridepublic void removeById(Long id) { int count = sysMenuMapper.countByParentId(id); // 先查询是否存在子菜单,如果存在不允许进行删除 if (count > 0) { throw new GuiguException(ResultCodeEnum.NODE_ERROR); } sysMenuMapper.deleteById(id);// 不存在子菜单直接删除}SysMenuMapper
持久层代码实现
// com.atguigu.spzx.manager.mapper#SysMenuMapper@Mapperpublic interface SysMenuMapper { public abstract int countByParentId(Long id); public abstract void deleteById(Long id);}SysMenuMapper.xml
映射文件中添加如下sql语句
select count(id) from sys_menu where parent_id = #{parentId} and is_deleted = 0 update sys_menu set update_time = now() , is_deleted = 1 where id = #{id}1.6.3 前端对接
sysMenu.js
在api目录下创建一个sysMenu.js文件,文件的内容如下所示:
// 根据id删除数据export const RemoveSysMenuById = id => { return request({ url: `${api_name}/removeById/${id}`, method: 'delete', })}sysMenu.vue
更改sysMenu.vue的代码如下所示:
删除 import { RemoveSysMenuById } from '@/api/sysMenu' //删除const remove = async id => { console.log('removeDataById:' + id) ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) .then(async () => { const { code , message } = await RemoveSysMenuById(id) if(code === 200) { ElMessage.success('删除成功') fetchData() }else { ElMessage.error(message) } }) .catch(() => { ElMessage.info('取消删除') })}2 角色分配菜单
2.1 需求说明
在角色列表页面,当用户点击分配菜单按钮的时候,此时就会弹出一个对话框。在该对话框中会将系统中所涉及到的所有的菜单都展示出来。并且将当
前角色所对应的菜单进行选中。效果如下图所示:
用户选择对应的菜单以后,点击提交按钮,此时请求后端接口,后端将选中的菜单数据保存到sys_role_menu表中。
2.2 页面制作
页面代码如下所示:
分配菜单 提交 取消 //分配菜单const defaultProps = { children: 'children', label: 'title',}const dialogMenuVisible = ref(false)const sysMenuTreeList = ref([ { "id": 1, "title": "系统管理", "children":[ {"id":2, "title": "用户管理" }, {"id":3, "title": "角色管理" }, {"id":4, "title": "菜单管理" } ] }, { "id": 5, "title": "数据管理", "component": "base", "sortValue": 2, "status": 1, "createTime": "2023-05-04", "children":[ {"id":6, "title": "商品单位" }, {"id":7, "title": "地区管理" } ] }])const showAssignMenu = async row => { dialogMenuVisible.value = true}2.3 查询菜单
2.3.1 后端接口
需要:根据角色的id查询出其对应的菜单id,并且需要将系统中所有的菜单数据查询出来。
SysRoleMenuController
表现层代码实现
// com.atguigu.spzx.manager.controller@RestController@RequestMapping(value = "/admin/system/sysRoleMenu")public class SysRoleMenuController { @Autowired private SysRoleMenuService sysRoleMenuService ; @GetMapping(value = "/findSysRoleMenuByRoleId/{roleId}") public Result findSysRoleMenuByRoleId(@PathVariable(value = "roleId") Long roleId) { Map sysRoleMenuList = sysRoleMenuService.findSysRoleMenuByRoleId(roleId) ; return Result.ok().data(sysRoleMenuList); }}SysRoleMenuService
业务层代码实现
// com.atguigu.spzx.manager.service.impl#SysRoleMenuServiceImpl@Overridepublic Map findSysRoleMenuByRoleId(Long roleId) { // 查询所有的菜单数据 List sysMenuList = sysMenuService.findNodes() ; // 查询当前角色的菜单数据 List roleMenuIds = sysRoleMenuMapper.findSysRoleMenuByRoleId(roleId) ; // 将数据存储到Map中进行返回 Map result = new HashMap() ; result.put("sysMenuList" , sysMenuList) ; result.put("roleMenuIds" , roleMenuIds) ; // 返回 return result;}SysRoleMenuMapper
持久层代码实现
@Mapperpublic interface SysRoleMenuMapper { public abstract List findSysRoleMenuByRoleId(Long roleId);}SysRoleMenuMapper.xml
映射文件添加如下sql语句
SELECT t1.menu_id FROM sys_role_menu t1 LEFT JOIN sys_menu t2 ON t1.menu_id = t2.id WHERE t1.role_id = #{roleId} AND t2.parent_id !=02.3.2 前端对接
sysRole.js
在src/api/sysRole.js文件中添加如下方法:
// 查询指定角色所对应的菜单idexport const GetSysRoleMenuIds = (roleId) => { return request({ url: "/admin/system/sysRoleMenu/findSysRoleMenuByRoleId/"+ roleId, method: 'get' })}sysRole.vue
修改sysRole.vue文件中的代码,如下所示:
import { GetSysRoleMenuIds } from '@/api/sysRole'; // 树对象变量const tree = ref() // 默认选中的菜单数据集合const showAssignMenu = async row => { dialogMenuVisible.value = true const { data } = await GetSysRoleMenuIds(row.id) // 请求后端地址获取所有的菜单数据,以及当前角色所对应的菜单数据 sysMenuTreeList.value = data.sysMenuList tree.value.setCheckedKeys(data.roleMenuIds) // 进行数据回显}2.4 保存菜单
2.4.1 后端接口
思路分析:前端请求后端接口的时候需要将角色的id和用户所选中的菜单id传递到后端。后端需要先根据角色的id从sys_role_menu表中删除其所对应的菜单数据,然后添加新的菜单数据到sys_role_menu表中。
AssginMenuDto
定义一个实体类封装前端所传递过来的两部分数据:
// com.atguigu.spzx.model.dto.system@Datapublic class AssginMenuDto { private Long roleId;// 角色id private List menuIdList;// 选中的菜单id的集合 , Map中包含了2部分的数据:全选菜单id,半选菜单id}SysRoleMenuController
表现层代码实现:
// com.atguigu.spzx.manager.controller@PostMapping("/doAssign")public Result doAssign(@RequestBody AssginMenuDto assginMenuDto) { sysRoleMenuService.doAssign(assginMenuDto); return Result.build(null , ResultCodeEnum.SUCCESS) ;}SysRoleMenuService
业务层代码实现:
// com.atguigu.spzx.manager.service.impl#SysRoleMenuServiceImpl@Transactional@Overridepublic void doAssign(AssginMenuDto assginMenuDto) { // 根据角色的id删除其所对应的菜单数据 sysRoleMenuMapper.deleteByRoleId(assginMenuDto.getRoleId()); // 获取菜单的id List menuIds = assginMenuDto.getMenuIdList(); if(CollectionUtils.isEmpty(menuIds)){ //如果menuIds为空 表示删除角色所有的菜单即可 不需要新分配 return; } sysRoleMenuMapper.doAssign(assginMenuDto) ;}SysRoleMenuMapper
持久层代码实现:
// com.atguigu.spzx.manager.mapper@Mapperpublic interface SysRoleMenuMapper { public abstract void deleteByRoleId(Long roleId); public abstract void doAssign(AssginMenuDto assginMenuDto);}SysRoleMenuMapper.xml
映射文件中添加如下sql语句:
delete from sys_role_menu where role_id = #{roleId} insert into sys_role_menu ( role_id, menu_id, create_time , update_time , is_deleted , is_half ) values (#{roleId} , #{menuId} , now() , now() , 0 , 02.4.2 前端对接
sysRole.js
在src/api/sysRole.js文件中添加如下方法:
// 根据角色分配菜单请求方法export const DoAssignMenuIdToSysRole = (assignMenuDto) => { return request({ url: "/admin/system/sysRoleMenu/doAssign", method: 'post', data: assignMenuDto })}sysRole.vue
修改sysRole.vue文件中的代码,思路如下:
1、给提交按钮绑定点击事件,并提供对应的事件处理函数
2、在单机事件处理函数中执行的代码逻辑如下所示:
- 获取tree组件对象中选中的节点
- 获取tree组件对象中半选中的节点
- 将两组节点数据进行组合
- 构建请求参数
- 发送请求
具体代码如下所示:
const doAssign = async () => { // 将选中的节点id和半选中的节点的id进行合并 const menuIds = [ ...tree.value.getCheckedKeys(), ...tree.value.getHalfCheckedKeys() ] console.log(menuIds); // 构建请求数据 const assignMenuDto = { roleId: roleId, menuIdList: menuIds } // 发送请求 await DoAssignMenuIdToSysRole(assignMenuDto) ; ElMessage.success('菜单分配成功') dialogMenuVisible.value = false}3 动态菜单
3.1 需求说明
现在首页的左侧菜单是直接写***在前端系统中的,这里很显然是不合适的。不同用户其所对应的权限是不同的,因此关于左侧菜单需要根据当前登录的用户所对应的角色动态进行获取。
前端所需要的菜单数据结构如下所示:
[{"title": "系统管理","name": "system","children": [{"title": "用户管理","name": "sysUser","children": null},{"title": "角色管理","name": "sysRole","children": null},{"title": "菜单管理","name": "sysMenu","children": null}]},{"title": "数据管理","name": "base","children": [{"title": "地区管理","name": "region","children": null},{"title": "商品单位","name": "productUnit","children": null}]}]3.2 后端接口
3.2.1 SysMenuVo
定义一个实体类,来封装前端所需要的菜单数据。如下所示:
// com.atguigu.spzx.model.vo.system@Datapublic class SysMenuVo { private String title; private String name; private List children;}3.2.1 IndexController
表现层代码实现:
// com.atguigu.spzx.system.controller#IndexController@GetMapping("/menus")public Result menus() { List sysMenuVoList = sysMenuService.findUserMenuList() ; return Result.build(sysMenuVoList , ResultCodeEnum.SUCCESS) ;}3.2.2 SysMenuService
业务层代码实现:
@Overridepublic List findUserMenuList() { SysUser sysUser = AuthContextUtil.get(); Long userId = sysUser.getId(); // 获取当前登录用户的id List sysMenuList = sysMenuMapper.selectListByUserId(userId) ; //构建树形数据 List sysMenuTreeList = MenuHelper.buildTree(sysMenuList); return this.buildMenus(sysMenuTreeList);}// 将List对象转换成List对象private List buildMenus(List menus) { List sysMenuVoList = new LinkedList(); for (SysMenu sysMenu : menus) { SysMenuVo sysMenuVo = new SysMenuVo(); sysMenuVo.setTitle(sysMenu.getTitle()); sysMenuVo.setName(sysMenu.getComponent()); List children = sysMenu.getChildren(); if (!CollectionUtils.isEmpty(children)) { sysMenuVo.setChildren(buildMenus(children)); } sysMenuVoList.add(sysMenuVo); } return sysMenuVoList;}3.2.3 SysMenuMapper
持久层代码实现:
@Mapperpublic interface SysMenuMapper { public abstract List selectListByUserId(Long userId);}3.2.4 SysMenuMapper.xml
在SysMenuMapper.xml文件中添加如下的sql语句
select * from sys_menu sm where sm.id in ( select DISTINCT srm.menu_id from sys_role_menu srm where srm.role_id in ( select sr.id from sys_role sr where sr.id in (select sur.role_id from sys_user_role sur where sur.user_id = #{userId} and is_deleted = 0) and sr.is_deleted = 0 ) and srm.is_deleted = 0 ) and sm.is_deleted = 03.3 前端对接
3.3.1 前端加载流程
前端加载动态菜单的流程如下图所示:
通过源码的查看得出结论:要求在添加菜单的时候,菜单的名称和前端路由的名称保存一致。
3.3.2 menu.js
修改/src/api/menu.js中的请求地址,如下所示:
// 获取菜单export const GetMenus = params => { return request({ url: '/admin/system/index/menus', method: 'get', params, })}3.3.3 index.js
更改src/router/index.js固定菜单和异步菜单的路由加载:
// 固定菜单export const fixedRoutes = [...home]// 动态菜单export const asyncRoutes = [...system]
3.3.4 menu.js
修改pinia/modules/menu.js中的generateMenus方法,注释掉方式一菜单加载,打开方式二菜单加载。
const generateMenus = async () => { // // 方式一:只有固定菜单 // const menus = getFilterMenus(fixedRoutes) // setMenus(menus) // 方式二:有动态菜单 // 从后台获取菜单 const { code, data } = await GetMenus() if (+code === 200) { // 添加路由之前先删除所有动态路由 asyncRoutes.forEach(item => { router.removeRoute(item.name) }) // 过滤出需要添加的动态路由 const filterRoutes = getFilterRoutes(asyncRoutes, data) filterRoutes.forEach(route => router.addRoute(route)) // 生成菜单 const menus = getFilterMenus([...fixedRoutes, ...filterRoutes]) setMenus(menus) }}3.4 代码测试
3.4.1 测试内容说明
1、给管理员角色分配访问所有的菜单权限,创建新的测试角色进行测试。
2、添加新的菜单测试角色分配菜单数据回显
3.4.2 bug解决
测试内容二的时候会存在bug【新添加的子菜单也会被选中】,解决bug。代码如下所示:
SysMenuServiceImpl
更改添加菜单方法
// com.atguigu.spzx.manager.service.impl.SysMenuServiceImpl@Transactional@Overridepublic void save(SysMenu sysMenu) { // 添加新的节点 sysMenuMapper.insert(sysMenu) ; // 新添加一个菜单,那么此时就需要将该菜单所对应的父级菜单设置为半开 updateSysRoleMenuIsHalf(sysMenu) ;}private void updateSysRoleMenuIsHalf(SysMenu sysMenu) { // 查询是否存在父节点 SysMenu parentMenu = sysMenuMapper.selectById(sysMenu.getParentId()); if(parentMenu != null) { // 将该id的菜单设置为半开 sysRoleMenuMapper.updateSysRoleMenuIsHalf(parentMenu.getId()) ; // 递归调用 updateSysRoleMenuIsHalf(parentMenu) ; }}SysMenuMapper
持久层代码
// com.atguigu.spzx.manager.mapper@Mapperpublic interface SysMenuMapper { public abstract SysMenu selectById(Long id) ;}SysMenuMapper.xml
在SysMenuMapper.xml映射文件中添加如下的sql语句
select from sys_menu where id = #{id}SysRoleMenuMapper
角色菜单持久层代码:
// com.atguigu.spzx.manager.mapper@Mapperpublic interface SysRoleMenuMapper { public abstract void updateSysRoleMenuIsHalf(Long menuId);}SysRoleMenuMapper.xml
在SysRoleMenuMapper.xml映射文件中添加如下的sql语句
update sys_role_menu srm set srm.is_half = 1 where menu_id = #{menuId}
下一篇:服务器中毒后怎么处理?