feat: 菜单路由添加是否开启缓存和目录始终显示的设置

This commit is contained in:
hxr
2023-10-22 23:58:41 +08:00
parent c403c59107
commit a82ce8da7b
6 changed files with 180 additions and 116 deletions

View File

@@ -1,7 +1,5 @@
package com.youlai.system.model.bo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.youlai.system.common.enums.MenuTypeEnum;
import lombok.Data;
@@ -12,10 +10,7 @@ import java.util.List;
*/
@Data
public class RouteBO {
/**
*
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
@@ -73,4 +68,14 @@ public class RouteBO {
*/
private List<String> roles;
/**
* 【目录】只有一个子路由是否始终显示(1:是 0:否)
*/
private Integer alwaysShow;
/**
* 【菜单】是否开启页面缓存(1:是 0:否)
*/
private Integer keepAlive;
}

View File

@@ -78,4 +78,15 @@ public class SysMenu extends BaseEntity {
*/
private String treePath;
/**
* 【菜单】是否开启页面缓存(1:开启;0:关闭)
*/
private Integer keepAlive;
/**
* 【目录】只有一个子路由是否始终显示(1:是 0:否)
*/
private Integer alwaysShow;
}

View File

@@ -1,5 +1,6 @@
package com.youlai.system.model.form;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.youlai.system.common.enums.MenuTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -41,4 +42,11 @@ public class MenuForm {
@Schema(description = "跳转路径")
private String redirect;
@Schema(description = "【菜单】是否开启页面缓存", example = "1")
private Integer keepAlive;
@Schema(description = "【目录】只有一个子路由是否始终显示", example = "1")
private Integer alwaysShow;
}

View File

@@ -48,10 +48,12 @@ public class RouteVO {
@Schema(description = "拥有路由权限的角色编码", example = "['ADMIN','ROOT']")
private List<String> roles;
@Schema(description = "是否开启缓存", example = "true")
@Schema(description = "【菜单】是否开启页面缓存", example = "true")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean keepAlive;
@Schema(description = "是否一直显示根路由", example = "true")
@Schema(description = "【目录】只有一个子路由是否始终显示", example = "true")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean alwaysShow;
}

View File

@@ -2,6 +2,7 @@ package com.youlai.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -29,6 +30,8 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path;
/**
* 菜单业务实现类
*
@@ -55,43 +58,40 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
Set<Long> parentIds = menus.stream()
.map(SysMenu::getParentId)
.collect(Collectors.toSet());
Set<Long> menuIds = menus.stream()
.map(SysMenu::getId)
.collect(Collectors.toSet());
List<Long> rootIds = CollectionUtil.subtractToList(parentIds, menuIds); // 求差集,得到 parentIds 中 menuIds 没有的元素
// 获取根节点ID
List<Long> rootIds = parentIds.stream()
.filter(id -> !menuIds.contains(id))
.toList();
List<MenuVO> list = new ArrayList<>();
for (Long rootId : rootIds) {
list.addAll(recurMenus(rootId, menus)); // 递归
}
return list;
// 使用递归函数来构建菜单树
List<MenuVO> menuList = rootIds.stream()
.flatMap(rootId -> buildMenuTree(rootId, menus).stream())
.collect(Collectors.toList());
return menuList;
}
/**
* 保存菜单
* 递归生成菜单列表
*
* @param parentId 父级ID
* @param menuList 菜单列表
* @return 菜单列表
*/
@Override
public boolean saveMenu(MenuForm menuForm) {
String path = menuForm.getPath();
MenuTypeEnum menuType = menuForm.getType(); // 菜单类型
switch (menuType) {
case CATALOG -> { // 目录
if (NumberUtil.equals(menuForm.getParentId(), 0) && !path.startsWith("/")) {
menuForm.setPath("/" + path); // 一级目录需以 / 开头
}
menuForm.setComponent("Layout");
}
case EXTLINK -> // 外链
menuForm.setComponent(null);
}
SysMenu entity = menuConverter.form2Entity(menuForm);
String treePath = generateMenuTreePath(menuForm.getParentId());
entity.setTreePath(treePath);
return this.saveOrUpdate(entity);
private List<MenuVO> buildMenuTree(Long parentId, List<SysMenu> menuList) {
return CollectionUtil.emptyIfNull(menuList)
.stream()
.filter(menu -> menu.getParentId().equals(parentId))
.map(entity -> {
MenuVO menuVO = menuConverter.entity2Vo(entity);
List<MenuVO> children = buildMenuTree(entity.getId(), menuList);
menuVO.setChildren(children);
return menuVO;
}).toList();
}
/**
@@ -100,7 +100,31 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
@Override
public List<Option> listMenuOptions() {
List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>().orderByAsc(SysMenu::getSort));
return recurMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
}
/**
* 递归生成菜单下拉层级列表
*
* @param parentId 父级ID
* @param menuList 菜单列表
* @return 菜单下拉列表
*/
private List<Option> buildMenuOptions(Long parentId, List<SysMenu> menuList) {
List<Option> menuOptions = new ArrayList<>();
for (SysMenu menu : menuList) {
if (menu.getParentId().equals(parentId)) {
Option option = new Option(menu.getId(), menu.getName());
List<Option> subMenuOptions = buildMenuOptions(menu.getId(), menuList);
if (!subMenuOptions.isEmpty()) {
option.setChildren(subMenuOptions);
}
menuOptions.add(option);
}
}
return menuOptions;
}
/**
@@ -110,7 +134,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
@Cacheable(cacheNames = "system", key = "'routes'")
public List<RouteVO> listRoutes() {
List<RouteBO> menuList = this.baseMapper.listRoutes();
return recurRoutes(SystemConstants.ROOT_NODE_ID, menuList);
return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
}
/**
@@ -118,35 +142,96 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
*
* @param parentId 父级ID
* @param menuList 菜单列表
* @return
* @return 路由层级列表
*/
private List<RouteVO> recurRoutes(Long parentId, List<RouteBO> menuList) {
return CollectionUtil.emptyIfNull(menuList).stream()
.filter(menu -> menu.getParentId().equals(parentId))
.map(menu -> {
RouteVO routeVO = new RouteVO();
MenuTypeEnum menuTypeEnum = menu.getType();
if (MenuTypeEnum.MENU.equals(menuTypeEnum)) {
String routeName = StringUtils.capitalize(StrUtil.toCamelCase(menu.getPath(), '-')); // 路由 name 需要驼峰,首字母大写
routeVO.setName(routeName); // 根据name路由跳转 this.$router.push({name:xxx})
}
routeVO.setPath(menu.getPath()); // 根据path路由跳转 this.$router.push({path:xxx})
routeVO.setRedirect(menu.getRedirect());
routeVO.setComponent(menu.getComponent());
private List<RouteVO> buildRoutes(Long parentId, List<RouteBO> menuList) {
List<RouteVO> routeList = new ArrayList<>();
RouteVO.Meta meta = new RouteVO.Meta();
meta.setTitle(menu.getName());
meta.setIcon(menu.getIcon());
meta.setRoles(menu.getRoles());
meta.setHidden(StatusEnum.DISABLE.getValue().equals(menu.getVisible()));
meta.setKeepAlive(true);
meta.setAlwaysShow(true);
routeVO.setMeta(meta);
List<RouteVO> children = recurRoutes(menu.getId(), menuList);
for (RouteBO menu : menuList) {
if (menu.getParentId().equals(parentId)) {
RouteVO routeVO = toRouteVo(menu);
List<RouteVO> children = buildRoutes(menu.getId(), menuList);
if (!children.isEmpty()) {
routeVO.setChildren(children);
return routeVO;
}).toList();
}
routeList.add(routeVO);
}
}
return routeList;
}
/**
* 根据RouteBO创建RouteVO
*/
private RouteVO toRouteVo(RouteBO routeBO) {
RouteVO routeVO = new RouteVO();
String routeName = StringUtils.capitalize(StrUtil.toCamelCase(routeBO.getPath(), '-')); // 路由 name 需要驼峰,首字母大写
routeVO.setName(routeName); // 根据name路由跳转 this.$router.push({name:xxx})
routeVO.setPath(routeBO.getPath()); // 根据path路由跳转 this.$router.push({path:xxx})
routeVO.setRedirect(routeBO.getRedirect());
routeVO.setComponent(routeBO.getComponent());
RouteVO.Meta meta = new RouteVO.Meta();
meta.setTitle(routeBO.getName());
meta.setIcon(routeBO.getIcon());
meta.setRoles(routeBO.getRoles());
meta.setHidden(StatusEnum.DISABLE.getValue().equals(routeBO.getVisible()));
// 【菜单】是否开启页面缓存
if (MenuTypeEnum.MENU.equals(routeBO.getType())
&& ObjectUtil.equals(routeBO.getKeepAlive(), 1)) {
meta.setKeepAlive(true);
}
// 【目录】只有一个子路由是否始终显示
if (MenuTypeEnum.CATALOG.equals(routeBO.getType())
&& ObjectUtil.equals(routeBO.getAlwaysShow(), 1)) {
meta.setAlwaysShow(true);
}
routeVO.setMeta(meta);
return routeVO;
}
/**
* 保存菜单
*/
@Override
public boolean saveMenu(MenuForm menuForm) {
String path = menuForm.getPath();
MenuTypeEnum menuType = menuForm.getType();
// 如果是目录
if (menuType == MenuTypeEnum.CATALOG) {
if (menuForm.getParentId() == 0 && !path.startsWith("/")) {
menuForm.setPath("/" + path); // 一级目录需以 / 开头
}
menuForm.setComponent("Layout");
}
// 如果是外链
else if (menuType == MenuTypeEnum.EXTLINK) {
menuForm.setComponent(null);
}
SysMenu entity = menuConverter.form2Entity(menuForm);
String treePath = generateMenuTreePath(menuForm.getParentId());
entity.setTreePath(treePath);
return this.saveOrUpdate(entity);
}
/**
* 部门路径生成
*
* @param parentId 父ID
* @return 父节点路径以英文逗号(, )分割eg: 1,2,3
*/
private String generateMenuTreePath(Long parentId) {
if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
return String.valueOf(parentId);
} else {
SysMenu parent = this.getById(parentId);
return parent != null ? parent.getTreePath() + "," + parent.getId() : null;
}
}
@@ -207,59 +292,8 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
return true;
}
/**
* 递归生成菜单列表
*
* @param parentId 父级ID
* @param menuList 菜单列表
* @return
*/
private List<MenuVO> recurMenus(Long parentId, List<SysMenu> menuList) {
return CollectionUtil.emptyIfNull(menuList)
.stream()
.filter(menu -> menu.getParentId().equals(parentId))
.map(entity -> {
MenuVO menuVO = menuConverter.entity2Vo(entity);
List<MenuVO> children = recurMenus(entity.getId(), menuList);
menuVO.setChildren(children);
return menuVO;
}).toList();
}
/**
* 递归生成菜单下拉层级列表
*
* @param parentId 父级ID
* @param menuList 菜单列表
* @return
*/
private static List<Option> recurMenuOptions(Long parentId, List<SysMenu> menuList) {
List<Option> menus = CollectionUtil.emptyIfNull(menuList).stream()
.filter(menu -> menu.getParentId().equals(parentId))
.map(menu -> new Option(menu.getId(), menu.getName(), recurMenuOptions(menu.getId(), menuList)))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
return menus;
}
/**
* 部门路径生成
*
* @param parentId 父ID
* @return 父节点路径以英文逗号(, )分割eg: 1,2,3
*/
private String generateMenuTreePath(Long parentId) {
String treePath = null;
if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
treePath = String.valueOf(parentId);
} else {
SysMenu parent = this.getById(parentId);
if (parent != null) {
treePath = parent.getTreePath() + "," + parent.getId();
}
}
return treePath;
}
}