// 引入 src/pages 文件夹下所有组件作为动态组件 import Pages from '@/pages'; import { RouteRecordRaw } from 'vue-router'; import { RouteOption, LazyRouteComponent, RouteRecordLink } from './interface'; import router from './index'; import { initUndefined } from '@/utils/helpers'; // 注册 IframeBox、BlankView 组件 Pages['iframe'] = () => import('stepin/es/iframe-box'); Pages['blankView'] = () => import('@/components/layout/BlankView.vue'); Pages['link'] = () => import('@/components/layout/LinkView.vue'); /** * 解析路由组件 * @param component * @returns */ const parseComponent = (component: null | undefined | string | Record) => { if (component === null || component === undefined) { return component; } if (typeof component === 'string') { return Pages[component]; } else { return Object.entries(component).reduce((p, [key, val]) => { p[key] = Pages[val]; return p; }, {} as Record); } }; /** * 解析路由 * @param routes * @returns */ function parseRoutes(routes: RouteOption[]): RouteRecordRaw[] { return routes.map((route) => { // 初始化meta route.meta = route.meta ?? {}; initUndefined(route.meta, { cacheable: true, renderMenu: true, link: (route as RouteRecordLink).link, }); // 解析组件 及 子路由 const _route = { ...route, children: route.children && parseRoutes(route.children), component: route.component && parseComponent(route.component), components: route.components && parseComponent(route.components), } as any; // 删除 undefined 属性 Object.keys(_route).forEach((key) => { if (_route[key] === undefined) { delete _route[key]; } }); return _route as RouteRecordRaw; }); } /** * 提取嵌套路由所有name * @param recordList * @returns */ const extractRouteNames = (recordList: RouteRecordRaw[]): string[] => { const result: string[] = []; recordList.forEach((record) => { if (typeof record.name === 'string') { result.push(record.name); } if (record.children) { result.push(...extractRouteNames(record.children)); } }); return result; }; /** * 合并路由 * @param target * @param source */ function mergeRoutes(target: readonly RouteRecordRaw[], source: RouteRecordRaw[]): RouteRecordRaw[] { interface RouteRecordMap extends Omit { children?: Map; } type Filter = (record: RouteRecordRaw) => Boolean; /** * 转换成 map, 不满足过滤条件的 route 值设置为 undefined * @param routes * @param filter 过滤器 * @param parentPath * @returns */ const toRoutesMap = ( routes: readonly RouteRecordRaw[], filter?: Filter, parentPath?: string ): Map => { parentPath = parentPath ?? ''; const _map = new Map(); routes.forEach((route) => { const fullPath = /^\//.test(route.path) ? route.path : `${parentPath}/${route.path}`; if (!filter || filter(route)) { _map.set(fullPath, { ...route, children: route.children && toRoutesMap(route.children, filter, fullPath), }); } else { _map.set(fullPath, undefined as never); } }); return _map; }; // 合并 const mergeMap = ( target?: Map, source?: Map ): Map | undefined => { if (!target || !source) { return target ?? source; } const resultMap = new Map(); // 保证新路由数据顺序 for (const key of source.keys()) { resultMap.set(key, void 0); } for (const key of target.keys()) { resultMap.set(key, void 0); } target.forEach((v, k) => { resultMap.set(k, v); }); source.forEach((v, k) => { const t = resultMap.get(k); if (t !== undefined) { v.children = mergeMap(t.children, v.children); } resultMap.set(k, v); }); return resultMap; }; // map 转换成 routes const toRoutes = (routesMap: Map): RouteRecordRaw[] => { const _routes: RouteRecordRaw[] = []; routesMap.forEach((record, path) => { if (record) { const _route = { ...record } as RouteRecordRaw; if (record.children) { _route.children = toRoutes(record.children); } else { delete _route.children; } _routes.push(_route); } }); return _routes; }; const names = extractRouteNames(source); const targetMap = toRoutesMap(target, (record) => !names.includes(record.name as string)); const sourceMap = toRoutesMap(source); const routesMap = mergeMap(targetMap, sourceMap); return toRoutes(routesMap); } /** * 查找符合条件的路由 * @param routes 路由集合 * @param filter 过滤器 * @returns */ function findRoute( routes: readonly RouteRecordRaw[], filter: (route: RouteRecordRaw) => boolean ): RouteRecordRaw | undefined { if (routes.length === 0) { return undefined; } return ( routes.find(filter) ?? findRoute( routes.flatMap((route) => route.children ?? []), filter ) ); } /** * 添加路由 * @param routes */ export function addRoutes(routes: RouteOption[]) { const routesRaw: RouteRecordRaw[] = parseRoutes(routes); routesRaw.forEach((routeRaw) => router.addRoute(routeRaw)); router.options.routes = mergeRoutes(router.options.routes, routesRaw); } /** * 过滤路由配置 * @param routes 路由配置数组 * @param filter 过滤条件 * @returns */ function filterRoutes(routes: Readonly, filter: (route: RouteRecordRaw) => boolean) { return routes.filter((route) => { if (route.children && route.children.length > 0) { route.children = filterRoutes(route.children, filter); } return filter(route); }); } /** * 移出路由 * @param routeName */ export function removeRoute(routeName: string) { router.removeRoute(routeName); router.options.routes = filterRoutes(router.options.routes, (route) => route.name !== routeName); } /** * 添加路由 * @param routes * @param parentName * @returns */ export function appendRoutes(routes: RouteOption[], parentName: string) { const parent = findRoute(router.options.routes, (route) => route.name === parentName); if (!parent) { console.error(`name为${parentName}的父级路由不存在,请检查`); return false; } const routesRaw: RouteRecordRaw[] = parseRoutes(routes); routesRaw.forEach((routeRaw) => router.addRoute(parentName, routeRaw)); parent.children = mergeRoutes(router.options.routes, mergeRoutes(parent.children ?? [], routesRaw)); }