feat: support the dynamic introduction and sorting of routes

This commit is contained in:
vben
2024-06-02 21:33:31 +08:00
parent c5eb0841a5
commit 30f7472d26
15 changed files with 257 additions and 93 deletions

View File

@@ -1,41 +1,46 @@
import { describe, expect, it, vi } from 'vitest';
import { generatorMenus } from './generator-menus'; // 替换为您的实际路径
import type { RouteRecordRaw } from 'vue-router';
// 模拟路由数据
const mockRoutes = [
{
meta: { icon: 'home-icon', title: '首页' },
name: 'home',
path: '/home',
},
{
meta: { hideChildrenInMenu: true, icon: 'about-icon', title: '关于' },
name: 'about',
path: '/about',
children: [
{
path: 'team',
name: 'team',
meta: { icon: 'team-icon', title: '团队' },
},
],
},
] as RouteRecordRaw[];
// 模拟 Vue 路由器实例
const mockRouter = {
getRoutes: vi.fn(() => [
{ name: 'home', path: '/home' },
{ name: 'about', path: '/about' },
{ name: 'team', path: '/about/team' },
]),
};
import {
type RouteRecordRaw,
type Router,
createRouter,
createWebHistory,
} from 'vue-router';
// Nested route setup to test child inclusion and hideChildrenInMenu functionality
describe('generatorMenus', () => {
// 模拟路由数据
const mockRoutes = [
{
meta: { icon: 'home-icon', title: '首页' },
name: 'home',
path: '/home',
},
{
meta: { hideChildrenInMenu: true, icon: 'about-icon', title: '关于' },
name: 'about',
path: '/about',
children: [
{
path: 'team',
name: 'team',
meta: { icon: 'team-icon', title: '团队' },
},
],
},
] as RouteRecordRaw[];
// 模拟 Vue 路由器实例
const mockRouter = {
getRoutes: vi.fn(() => [
{ name: 'home', path: '/home' },
{ name: 'about', path: '/about' },
{ name: 'team', path: '/about/team' },
]),
};
it('the correct menu list should be generated according to the route', async () => {
const expectedMenus = [
{
@@ -138,8 +143,6 @@ describe('generatorMenus', () => {
mockRoutesWithRedirect,
mockRouter as any,
);
console.log(111, menus);
expect(menus).toEqual([
// Assuming your generatorMenus function excludes redirect routes from the menu
{
@@ -168,4 +171,60 @@ describe('generatorMenus', () => {
},
]);
});
const routes: any = [
{
meta: { orderNo: 2, title: 'Home' },
name: 'home',
path: '/',
},
{
meta: { orderNo: 1, title: 'About' },
name: 'about',
path: '/about',
},
];
const router: Router = createRouter({
history: createWebHistory(),
routes,
});
it('should generate menu list with correct order', async () => {
const menus = await generatorMenus(routes, router);
const expectedMenus = [
{
badge: undefined,
badgeType: undefined,
badgeVariants: undefined,
icon: undefined,
name: 'About',
orderNo: 1,
parent: undefined,
parents: undefined,
path: '/about',
children: [],
},
{
badge: undefined,
badgeType: undefined,
badgeVariants: undefined,
icon: undefined,
name: 'Home',
orderNo: 2,
parent: undefined,
parents: undefined,
path: '/',
children: [],
},
];
expect(menus).toEqual(expectedMenus);
});
it('should handle empty routes', async () => {
const emptyRoutes: any[] = [];
const menus = await generatorMenus(emptyRoutes, router);
expect(menus).toEqual([]);
});
});

View File

@@ -17,7 +17,7 @@ async function generatorMenus(
router.getRoutes().map(({ name, path }) => [name, path]),
);
const menus = mapTree<ExRouteRecordRaw, MenuRecordRaw>(routes, (route) => {
let menus = mapTree<ExRouteRecordRaw, MenuRecordRaw>(routes, (route) => {
// 路由表的路径写法有多种这里从router获取到最终的path并赋值
const path = finalRoutesMap[route.name as string] ?? route.path;
@@ -65,6 +65,8 @@ async function generatorMenus(
};
});
// 对菜单进行排序
menus = menus.sort((a, b) => (a.orderNo || 999) - (b.orderNo || 999));
return menus;
}

View File

@@ -1,4 +1,5 @@
export * from './flatten-object';
export * from './generator-menus';
export * from './generator-routes';
export * from './merge-route-modules';
export * from './nested-object';

View File

@@ -0,0 +1,68 @@
import type { RouteRecordRaw } from 'vue-router';
import { describe, expect, it } from 'vitest';
import { mergeRouteModules } from './merge-route-modules';
import type { RouteModuleType } from './merge-route-modules';
describe('mergeRouteModules', () => {
it('should merge route modules correctly', () => {
const routeModules: Record<string, RouteModuleType> = {
'./dynamic-routes/about.ts': {
default: [
{
component: () => Promise.resolve({ template: '<div>About</div>' }),
name: 'About',
path: '/about',
},
],
},
'./dynamic-routes/home.ts': {
default: [
{
component: () => Promise.resolve({ template: '<div>Home</div>' }),
name: 'Home',
path: '/',
},
],
},
};
const expectedRoutes: RouteRecordRaw[] = [
{
component: expect.any(Function),
name: 'About',
path: '/about',
},
{
component: expect.any(Function),
name: 'Home',
path: '/',
},
];
const mergedRoutes = mergeRouteModules(routeModules);
expect(mergedRoutes).toEqual(expectedRoutes);
});
it('should handle empty modules', () => {
const routeModules: Record<string, RouteModuleType> = {};
const expectedRoutes: RouteRecordRaw[] = [];
const mergedRoutes = mergeRouteModules(routeModules);
expect(mergedRoutes).toEqual(expectedRoutes);
});
it('should handle modules with no default export', () => {
const routeModules: Record<string, RouteModuleType> = {
'./dynamic-routes/empty.ts': {
default: [],
},
};
const expectedRoutes: RouteRecordRaw[] = [];
const mergedRoutes = mergeRouteModules(routeModules);
expect(mergedRoutes).toEqual(expectedRoutes);
});
});

View File

@@ -0,0 +1,28 @@
import type { RouteRecordRaw } from 'vue-router';
// 定义模块类型
interface RouteModuleType {
default: RouteRecordRaw[];
}
/**
* 合并动态路由模块的默认导出
* @param routeModules 动态导入的路由模块对象
* @returns 合并后的路由配置数组
*/
function mergeRouteModules(
routeModules: Record<string, unknown>,
): RouteRecordRaw[] {
const mergedRoutes: RouteRecordRaw[] = [];
for (const routeModule of Object.values(routeModules)) {
const moduleRoutes = (routeModule as RouteModuleType)?.default ?? [];
mergedRoutes.push(...moduleRoutes);
}
return mergedRoutes;
}
export { mergeRouteModules };
export type { RouteModuleType };

View File

@@ -42,7 +42,6 @@ interface RouteMeta {
* @default false
*/
hideInMenu?: boolean;
/**
* 当前路由在标签页不展现
* @default false