动态菜单的说明
什么叫做动态菜单?动态菜单就是根据用户属于不同的角色,每个角色还有不同的菜单,左侧菜单栏会有不同的显示
前端加载流程
menu.js
修改/src/api/menu.js中的请求地址,如下所示:
// 获取菜单
export const GetMenus = params => {
return request({
url: '/admin/system/index/menus',
method: 'get',
params,
})
}
index.js
更改src/router/index.js固定参数和异步菜单的路由加载:
// 固定菜单
export const fixedRoutes = [...home]
// 动态菜单
export const asyncRoutes = [...system]
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)
}
}
后端接口
SysMenuVo
定义一个实体类,来封装前端所需要的菜单数据。如下所示:
// com.atguigu.spzx.model.vo.system
@Data
public class SysMenuVo {
private String title;
private String name;
private List<SysMenuVo> children;
}
IndexController
表现层代码实现:
@GetMapping("/menus")
public Result menus() {
List<SysMenuVo> sysMenuVoList = sysMenuService.findUserMenuList() ;
return Result.build(sysMenuVoList , ResultCodeEnum.SUCCESS) ;
}
SysMenuService
业务层代码实现:
@Override
public List<SysMenuVo> findUserMenuList() {
SysUser sysUser = AuthContextUtil.get();
Long userId = sysUser.getId(); // 获取当前登录用户的id
List<SysMenu> sysMenuList = sysMenuMapper.selectListByUserId(userId) ;
//构建树形数据
List<SysMenu> sysMenuTreeList = MenuHelper.buildTree(sysMenuList);
return this.buildMenus(sysMenuTreeList);
}
// 将List<SysMenu>对象转换成List<SysMenuVo>对象
private List<SysMenuVo> buildMenus(List<SysMenu> menus) {
List<SysMenuVo> sysMenuVoList = new LinkedList<SysMenuVo>();
for (SysMenu sysMenu : menus) {
SysMenuVo sysMenuVo = new SysMenuVo();
sysMenuVo.setTitle(sysMenu.getTitle());
sysMenuVo.setName(sysMenu.getComponent());
List<SysMenu> children = sysMenu.getChildren();
if (!CollectionUtils.isEmpty(children)) {
sysMenuVo.setChildren(buildMenus(children));
}
sysMenuVoList.add(sysMenuVo);
}
return sysMenuVoList;
}
SysMenuMapper
持久层代码实现:
@Mapper
public interface SysMenuMapper {
public abstract List<SysMenu> selectListByUserId(Long userId);
}
SysMenuMapper.xml
在SysMenuMapper.xml文件中添加如下的sql语句
<select id="selectListByUserId" resultMap="sysMenuMap">
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 = 0
</select>
👆上面的sql可以优化
SELECT DISTINCT
sm.*
FROM
sys_menu sm
JOIN
sys_role_menu srm ON sm.id = srm.menu_id
JOIN
sys_user_role sur ON srm.role_id = sur.role_id
WHERE
sur.user_id = 1
AND sm.is_deleted = 0
AND srm.is_deleted = 0
AND sur.is_deleted = 0;