import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types'; import type { Router, RouteRecordNormalized } from 'vue-router'; import { getParentLayout, LAYOUT } from '/@/router/constant'; import { cloneDeep } from 'lodash-es'; import { warn } from '/@/utils/log'; import { createRouter, createWebHashHistory } from 'vue-router'; export type LayoutMapKey = 'LAYOUT'; const LayoutMap = new Map Promise>(); let dynamicViewsModules: Record Promise>; // Dynamic introduction function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) { dynamicViewsModules = dynamicViewsModules || import.meta.glob('../../views/**/*.{vue,tsx}'); if (!routes) return; routes.forEach((item) => { const { component, name } = item; const { children } = item; if (component) { item.component = dynamicImport(dynamicViewsModules, component as string); } else if (name) { item.component = getParentLayout(); } children && asyncImportRoute(children); }); } function dynamicImport( dynamicViewsModules: Record Promise>, component: string ) { const keys = Object.keys(dynamicViewsModules); const matchKeys = keys.filter((key) => { let k = key.replace('../../views', ''); const lastIndex = k.lastIndexOf('.'); k = k.substring(0, lastIndex); return k === component; }); if (matchKeys?.length === 1) { const matchKey = matchKeys[0]; return dynamicViewsModules[matchKey]; } if (matchKeys?.length > 1) { warn( 'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure' ); return; } } // Turn background objects into routing objects export function transformObjToRoute(routeList: AppRouteModule[]): T[] { LayoutMap.set('LAYOUT', LAYOUT); routeList.forEach((route) => { if (route.component) { if ((route.component as string).toUpperCase() === 'LAYOUT') { //route.component = LayoutMap.get(route.component as LayoutMapKey); route.component = LayoutMap.get((route.component as string).toUpperCase() as LayoutMapKey); } else { route.children = [cloneDeep(route)]; route.component = LAYOUT; route.name = `${route.name}Parent`; route.path = ''; const meta = route.meta || {}; meta.single = true; meta.affix = false; route.meta = meta; } } route.children && asyncImportRoute(route.children); }); return (routeList as unknown) as T[]; } /** * Convert multi-level routing to level 2 routing */ export function flatRoutes(routeModules: AppRouteModule[]) { for (let index = 0; index < routeModules.length; index++) { const routeModule = routeModules[index]; if (!isMultipleRoute(routeModule)) { continue; } promoteRouteLevel(routeModule); } } // Routing level upgrade function promoteRouteLevel(routeModule: AppRouteModule) { // Use vue-router to splice menus let router: Router | null = createRouter({ routes: [routeModule as any], history: createWebHashHistory(), }); const routes = router.getRoutes(); const children = cloneDeep(routeModule.children); addToChildren(routes, children || [], routeModule); router = null; routeModule.children = routeModule.children?.filter((item) => !item.children?.length); } // Add all sub-routes to the secondary route function addToChildren( routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule ) { for (let index = 0; index < children.length; index++) { const child = children[index]; const route = routes.find((item) => item.name === child.name); if (route) { routeModule.children = routeModule.children || []; routeModule.children?.push(route as any); if (child.children?.length) { addToChildren(routes, child.children, routeModule); } } } } // Determine whether the level exceeds 2 levels function isMultipleRoute(routeModule: AppRouteModule) { if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) { return false; } const children = routeModule.children; let flag = false; for (let index = 0; index < children.length; index++) { const child = children[index]; if (child.children?.length) { flag = true; break; } } return flag; }