2024-07-23 00:03:59 +08:00
|
|
|
|
import type { Router, RouteRecordRaw } from 'vue-router';
|
2024-06-02 15:04:37 +08:00
|
|
|
|
|
2025-03-20 12:34:02 +08:00
|
|
|
|
import type { ExRouteRecordRaw, MenuRecordRaw, RouteRecordStringComponent } from '@vben-core/typings';
|
2025-01-01 11:39:49 +08:00
|
|
|
|
|
2025-03-20 12:34:02 +08:00
|
|
|
|
import { filterTree, mapTree, isHttpUrl } from '@vben-core/shared/utils';
|
|
|
|
|
|
import type { AppRouteRecordRaw } from '@vben/types'; // TODO @芋艿:这里的报错,解决
|
2024-06-02 15:04:37 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据 routes 生成菜单列表
|
|
|
|
|
|
* @param routes
|
|
|
|
|
|
*/
|
2024-06-30 15:03:37 +08:00
|
|
|
|
async function generateMenus(
|
2024-06-02 15:04:37 +08:00
|
|
|
|
routes: RouteRecordRaw[],
|
|
|
|
|
|
router: Router,
|
|
|
|
|
|
): Promise<MenuRecordRaw[]> {
|
|
|
|
|
|
// 将路由列表转换为一个以 name 为键的对象映射
|
|
|
|
|
|
// 获取所有router最终的path及name
|
|
|
|
|
|
const finalRoutesMap: { [key: string]: string } = Object.fromEntries(
|
|
|
|
|
|
router.getRoutes().map(({ name, path }) => [name, path]),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2024-06-02 21:33:31 +08:00
|
|
|
|
let menus = mapTree<ExRouteRecordRaw, MenuRecordRaw>(routes, (route) => {
|
2024-06-02 15:04:37 +08:00
|
|
|
|
// 路由表的路径写法有多种,这里从router获取到最终的path并赋值
|
|
|
|
|
|
const path = finalRoutesMap[route.name as string] ?? route.path;
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为菜单结构
|
|
|
|
|
|
// const path = matchRoute?.path ?? route.path;
|
|
|
|
|
|
const { meta, name: routeName, redirect, children } = route;
|
|
|
|
|
|
const {
|
2024-07-28 14:29:05 +08:00
|
|
|
|
activeIcon,
|
2024-06-02 15:04:37 +08:00
|
|
|
|
badge,
|
|
|
|
|
|
badgeType,
|
|
|
|
|
|
badgeVariants,
|
|
|
|
|
|
hideChildrenInMenu = false,
|
|
|
|
|
|
icon,
|
2024-06-08 20:14:04 +08:00
|
|
|
|
link,
|
2024-06-02 23:46:18 +08:00
|
|
|
|
order,
|
2024-06-02 15:04:37 +08:00
|
|
|
|
title = '',
|
|
|
|
|
|
} = meta || {};
|
|
|
|
|
|
|
|
|
|
|
|
const name = (title || routeName || '') as string;
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏子菜单
|
|
|
|
|
|
const resultChildren = hideChildrenInMenu
|
|
|
|
|
|
? []
|
|
|
|
|
|
: (children as MenuRecordRaw[]);
|
|
|
|
|
|
|
|
|
|
|
|
// 将菜单的所有父级和父级菜单记录到菜单项内
|
|
|
|
|
|
if (resultChildren && resultChildren.length > 0) {
|
|
|
|
|
|
resultChildren.forEach((child) => {
|
|
|
|
|
|
child.parents = [...(route.parents || []), path];
|
|
|
|
|
|
child.parent = path;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
// 隐藏子菜单
|
2024-06-08 20:14:04 +08:00
|
|
|
|
const resultPath = hideChildrenInMenu ? redirect || path : link || path;
|
2024-06-02 15:04:37 +08:00
|
|
|
|
return {
|
2024-07-28 14:29:05 +08:00
|
|
|
|
activeIcon,
|
2024-06-02 15:04:37 +08:00
|
|
|
|
badge,
|
|
|
|
|
|
badgeType,
|
|
|
|
|
|
badgeVariants,
|
|
|
|
|
|
icon,
|
|
|
|
|
|
name,
|
2024-06-02 23:46:18 +08:00
|
|
|
|
order,
|
2024-06-02 15:04:37 +08:00
|
|
|
|
parent: route.parent,
|
|
|
|
|
|
parents: route.parents,
|
|
|
|
|
|
path: resultPath as string,
|
2024-07-19 01:26:13 +08:00
|
|
|
|
show: !route?.meta?.hideInMenu,
|
2024-06-02 15:04:37 +08:00
|
|
|
|
children: resultChildren || [],
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2024-06-02 21:33:31 +08:00
|
|
|
|
// 对菜单进行排序
|
2024-06-02 23:46:18 +08:00
|
|
|
|
menus = menus.sort((a, b) => (a.order || 999) - (b.order || 999));
|
2024-07-19 01:26:13 +08:00
|
|
|
|
|
|
|
|
|
|
const finalMenus = filterTree(menus, (menu) => {
|
|
|
|
|
|
return !!menu.show;
|
|
|
|
|
|
});
|
|
|
|
|
|
return finalMenus;
|
2024-06-02 15:04:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-20 12:34:02 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 转换后端菜单数据为路由数据
|
|
|
|
|
|
* @param menuList 后端菜单数据
|
|
|
|
|
|
* @param parent 父级菜单
|
|
|
|
|
|
* @returns 路由数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
function convertServerMenuToRouteRecordStringComponent(
|
|
|
|
|
|
menuList: AppRouteRecordRaw[],
|
|
|
|
|
|
parent = '',
|
|
|
|
|
|
): RouteRecordStringComponent[] {
|
|
|
|
|
|
const menus: RouteRecordStringComponent[] = [];
|
|
|
|
|
|
menuList.forEach((menu) => {
|
|
|
|
|
|
// 处理顶级链接菜单
|
|
|
|
|
|
if (isHttpUrl(menu.path) && menu.parentId === 0) {
|
|
|
|
|
|
const urlMenu: RouteRecordStringComponent = {
|
|
|
|
|
|
component: 'IFrameView',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
hideInMenu: !menu.visible,
|
|
|
|
|
|
icon: menu.icon,
|
|
|
|
|
|
link: menu.path,
|
|
|
|
|
|
orderNo: menu.sort,
|
|
|
|
|
|
title: menu.name,
|
|
|
|
|
|
},
|
|
|
|
|
|
name: menu.name,
|
|
|
|
|
|
path: `/${menu.path}/index`,
|
|
|
|
|
|
};
|
|
|
|
|
|
menus.push(urlMenu);
|
|
|
|
|
|
return;
|
|
|
|
|
|
} else if (menu.children && menu.parentId === 0) {
|
|
|
|
|
|
menu.component = 'BasicLayout';
|
|
|
|
|
|
} else if (!menu.children) {
|
|
|
|
|
|
menu.component = menu.component as string;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (menu.component === 'Layout') {
|
|
|
|
|
|
menu.component = 'BasicLayout';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (menu.children && menu.parentId !== 0) {
|
|
|
|
|
|
menu.component = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// path
|
|
|
|
|
|
if (parent) {
|
|
|
|
|
|
menu.path = `${parent}/${menu.path}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!menu.path.startsWith('/')) {
|
|
|
|
|
|
menu.path = `/${menu.path}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const buildMenu: RouteRecordStringComponent = {
|
|
|
|
|
|
component: menu.component,
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
hideInMenu: !menu.visible,
|
|
|
|
|
|
icon: menu.icon,
|
|
|
|
|
|
keepAlive: menu.keepAlive,
|
|
|
|
|
|
orderNo: menu.sort,
|
|
|
|
|
|
title: menu.name,
|
|
|
|
|
|
},
|
|
|
|
|
|
name: menu.name,
|
|
|
|
|
|
path: menu.path,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (menu.children && menu.children.length > 0) {
|
|
|
|
|
|
buildMenu.children = convertServerMenuToRouteRecordStringComponent(menu.children, menu.path);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
menus.push(buildMenu);
|
|
|
|
|
|
});
|
|
|
|
|
|
return menus;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export { generateMenus, convertServerMenuToRouteRecordStringComponent };
|