mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-22 20:54:26 +08:00
feat: 菜单路由优化
This commit is contained in:
parent
8ee68bf898
commit
9f30696a5e
@ -64,13 +64,23 @@ public class RouteBO {
|
|||||||
private String icon;
|
private String icon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 外链路径
|
* 跳转路径
|
||||||
*/
|
*/
|
||||||
private String redirectUrl;
|
private String redirect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拥有路由的权限
|
* 拥有路由的权限
|
||||||
*/
|
*/
|
||||||
private List<String> roles;
|
private List<String> roles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【目录】只有一个子路由是否始终显示(1:是 0:否)
|
||||||
|
*/
|
||||||
|
private Integer alwaysShow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【菜单】是否开启页面缓存(1:是 0:否)
|
||||||
|
*/
|
||||||
|
private Integer keepAlive;
|
||||||
|
|
||||||
}
|
}
|
@ -77,4 +77,14 @@ public class SysMenu extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private String treePath;
|
private String treePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【菜单】是否开启页面缓存(1:开启;0:关闭)
|
||||||
|
*/
|
||||||
|
private Integer keepAlive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【目录】只有一个子路由是否始终显示(1:是 0:否)
|
||||||
|
*/
|
||||||
|
private Integer alwaysShow;
|
||||||
|
|
||||||
}
|
}
|
@ -41,4 +41,10 @@ public class MenuForm {
|
|||||||
@Schema(description = "跳转路径")
|
@Schema(description = "跳转路径")
|
||||||
private String redirect;
|
private String redirect;
|
||||||
|
|
||||||
|
@Schema(description = "【菜单】是否开启页面缓存", example = "1")
|
||||||
|
private Integer keepAlive;
|
||||||
|
|
||||||
|
@Schema(description = "【目录】只有一个子路由是否始终显示", example = "1")
|
||||||
|
private Integer alwaysShow;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,14 +42,19 @@ public class RouteVO {
|
|||||||
@Schema(description = "ICON")
|
@Schema(description = "ICON")
|
||||||
private String icon;
|
private String icon;
|
||||||
|
|
||||||
@Schema(description = "是否隐藏", example = "true")
|
@Schema(description = "是否隐藏(true-是 false-否)", example = "true")
|
||||||
private Boolean hidden;
|
private Boolean hidden;
|
||||||
|
|
||||||
@Schema(description = "拥有路由权限的角色编码", example = "['ADMIN','ROOT']")
|
@Schema(description = "拥有路由权限的角色编码", example = "['ADMIN','ROOT']")
|
||||||
private List<String> roles;
|
private List<String> roles;
|
||||||
|
|
||||||
@Schema(description = "是否开启缓存", example = "true")
|
@Schema(description = "【菜单】是否开启页面缓存", example = "true")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private Boolean keepAlive;
|
private Boolean keepAlive;
|
||||||
|
|
||||||
|
@Schema(description = "【目录】只有一个子路由是否始终显示", example = "true")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
private Boolean alwaysShow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "子路由列表")
|
@Schema(description = "子路由列表")
|
||||||
|
@ -39,7 +39,6 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
|
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
|
||||||
|
|
||||||
private final MenuConverter menuConverter;
|
private final MenuConverter menuConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,43 +56,40 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
Set<Long> parentIds = menus.stream()
|
Set<Long> parentIds = menus.stream()
|
||||||
.map(SysMenu::getParentId)
|
.map(SysMenu::getParentId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
Set<Long> menuIds = menus.stream()
|
Set<Long> menuIds = menus.stream()
|
||||||
.map(SysMenu::getId)
|
.map(SysMenu::getId)
|
||||||
.collect(Collectors.toSet());
|
.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<MenuVO> menuList = rootIds.stream()
|
||||||
list.addAll(recurMenus(rootId, menus)); // 递归
|
.flatMap(rootId -> buildMenuTree(rootId, menus).stream())
|
||||||
}
|
.collect(Collectors.toList());
|
||||||
return list;
|
|
||||||
|
return menuList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存菜单
|
* 递归生成菜单列表
|
||||||
|
*
|
||||||
|
* @param parentId 父级ID
|
||||||
|
* @param menuList 菜单列表
|
||||||
|
* @return 菜单列表
|
||||||
*/
|
*/
|
||||||
@Override
|
private List<MenuVO> buildMenuTree(Long parentId, List<SysMenu> menuList) {
|
||||||
public boolean saveMenu(MenuForm menuForm) {
|
return CollectionUtil.emptyIfNull(menuList)
|
||||||
String path = menuForm.getPath();
|
.stream()
|
||||||
|
.filter(menu -> menu.getParentId().equals(parentId))
|
||||||
MenuTypeEnum menuType = menuForm.getType(); // 菜单类型
|
.map(entity -> {
|
||||||
switch (menuType) {
|
MenuVO menuVO = menuConverter.entity2Vo(entity);
|
||||||
case CATALOG -> { // 目录
|
List<MenuVO> children = buildMenuTree(entity.getId(), menuList);
|
||||||
if (ObjectUtil.equal(menuForm.getParentId(), 0) && !path.startsWith("/")) {
|
menuVO.setChildren(children);
|
||||||
menuForm.setPath("/" + path); // 一级目录需以 / 开头
|
return menuVO;
|
||||||
}
|
}).toList();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,17 +98,41 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
@Override
|
@Override
|
||||||
public List<Option> listMenuOptions() {
|
public List<Option> listMenuOptions() {
|
||||||
List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>().orderByAsc(SysMenu::getSort));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路由列表
|
* 路由列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
//@Cacheable(cacheNames = "system", key = "'routes'")
|
@Cacheable(cacheNames = "system", key = "'routes'")
|
||||||
public List<RouteVO> listRoutes() {
|
public List<RouteVO> listRoutes() {
|
||||||
List<RouteBO> menuList = this.baseMapper.listRoutes();
|
List<RouteBO> menuList = this.baseMapper.listRoutes();
|
||||||
return recurRoutes(SystemConstants.ROOT_NODE_ID, menuList);
|
return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,34 +140,96 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
*
|
*
|
||||||
* @param parentId 父级ID
|
* @param parentId 父级ID
|
||||||
* @param menuList 菜单列表
|
* @param menuList 菜单列表
|
||||||
* @return
|
* @return 路由层级列表
|
||||||
*/
|
*/
|
||||||
private List<RouteVO> recurRoutes(Long parentId, List<RouteBO> menuList) {
|
private List<RouteVO> buildRoutes(Long parentId, List<RouteBO> menuList) {
|
||||||
return CollectionUtil.emptyIfNull(menuList).stream()
|
List<RouteVO> routeList = new ArrayList<>();
|
||||||
.filter(menu -> menu.getParentId().equals(parentId))
|
|
||||||
.map(menu -> {
|
for (RouteBO menu : menuList) {
|
||||||
RouteVO routeVO = new RouteVO();
|
if (menu.getParentId().equals(parentId)) {
|
||||||
MenuTypeEnum menuTypeEnum = menu.getType();
|
RouteVO routeVO = toRouteVo(menu);
|
||||||
if (MenuTypeEnum.MENU.equals(menuTypeEnum)) {
|
List<RouteVO> children = buildRoutes(menu.getId(), menuList);
|
||||||
String routeName = StringUtils.capitalize(StrUtil.toCamelCase(menu.getPath(), '-')); // 路由 name 需要驼峰,首字母大写
|
if (!children.isEmpty()) {
|
||||||
routeVO.setName(routeName); // 根据name路由跳转 this.$router.push({name:xxx})
|
routeVO.setChildren(children);
|
||||||
}
|
}
|
||||||
routeVO.setPath(menu.getPath()); // 根据path路由跳转 this.$router.push({path:xxx})
|
routeList.add(routeVO);
|
||||||
routeVO.setRedirect(menu.getRedirectUrl());
|
}
|
||||||
routeVO.setComponent(menu.getComponent());
|
}
|
||||||
|
|
||||||
|
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();
|
RouteVO.Meta meta = new RouteVO.Meta();
|
||||||
meta.setTitle(menu.getName());
|
meta.setTitle(routeBO.getName());
|
||||||
meta.setIcon(menu.getIcon());
|
meta.setIcon(routeBO.getIcon());
|
||||||
meta.setRoles(menu.getRoles());
|
meta.setRoles(routeBO.getRoles());
|
||||||
meta.setHidden(StatusEnum.DISABLE.getValue().equals(menu.getVisible()));
|
meta.setHidden(StatusEnum.DISABLE.getValue().equals(routeBO.getVisible()));
|
||||||
|
// 【菜单】是否开启页面缓存
|
||||||
|
if (MenuTypeEnum.MENU.equals(routeBO.getType())
|
||||||
|
&& ObjectUtil.equals(routeBO.getKeepAlive(), 1)) {
|
||||||
meta.setKeepAlive(true);
|
meta.setKeepAlive(true);
|
||||||
routeVO.setMeta(meta);
|
}
|
||||||
|
// 【目录】只有一个子路由是否始终显示
|
||||||
|
if (MenuTypeEnum.CATALOG.equals(routeBO.getType())
|
||||||
|
&& ObjectUtil.equals(routeBO.getAlwaysShow(), 1)) {
|
||||||
|
meta.setAlwaysShow(true);
|
||||||
|
}
|
||||||
|
|
||||||
List<RouteVO> children = recurRoutes(menu.getId(), menuList);
|
routeVO.setMeta(meta);
|
||||||
routeVO.setChildren(children);
|
|
||||||
return routeVO;
|
return routeVO;
|
||||||
}).toList();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存菜单
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -208,59 +290,5 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
|
|||||||
return true;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<result property="parentId" column="parent_id" jdbcType="BIGINT"/>
|
<result property="parentId" column="parent_id" jdbcType="BIGINT"/>
|
||||||
<result property="path" column="path" jdbcType="VARCHAR"/>
|
<result property="path" column="path" jdbcType="VARCHAR"/>
|
||||||
<result property="component" column="component" jdbcType="VARCHAR"/>
|
<result property="component" column="component" jdbcType="VARCHAR"/>
|
||||||
<result property="redirectUrl" column="redirect_url" jdbcType="VARCHAR"/>
|
<result property="redirect" column="redirect" jdbcType="VARCHAR"/>
|
||||||
<result property="icon" column="icon" jdbcType="VARCHAR"/>
|
<result property="icon" column="icon" jdbcType="VARCHAR"/>
|
||||||
<result property="sort" column="sort" jdbcType="INTEGER"/>
|
<result property="sort" column="sort" jdbcType="INTEGER"/>
|
||||||
<result property="visible" column="visible" jdbcType="BOOLEAN"/>
|
<result property="visible" column="visible" jdbcType="BOOLEAN"/>
|
||||||
@ -32,9 +32,11 @@
|
|||||||
t1.icon,
|
t1.icon,
|
||||||
t1.sort,
|
t1.sort,
|
||||||
t1.visible,
|
t1.visible,
|
||||||
t1.redirect_url,
|
t1.redirect,
|
||||||
t1.type,
|
t1.type,
|
||||||
t3.code
|
t3.code,
|
||||||
|
t1.always_show,
|
||||||
|
t1.keep_alive
|
||||||
FROM
|
FROM
|
||||||
sys_menu t1
|
sys_menu t1
|
||||||
LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id
|
||||||
|
Loading…
Reference in New Issue
Block a user