feat: support the dynamic introduction and sorting of routes
This commit is contained in:
@@ -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([]);
|
||||
});
|
||||
});
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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';
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
@@ -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 };
|
@@ -42,7 +42,6 @@ interface RouteMeta {
|
||||
* @default false
|
||||
*/
|
||||
hideInMenu?: boolean;
|
||||
|
||||
/**
|
||||
* 当前路由在标签页不展现
|
||||
* @default false
|
||||
|
Reference in New Issue
Block a user