Files
dysync/app/src/router/dynamicRoutes.ts
T
lijianyou b4e759cf69 init
2025-09-30 15:00:32 +08:00

249 lines
6.7 KiB
TypeScript

// 引入 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<string, string>) => {
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<string, LazyRouteComponent>);
}
};
/**
* 解析路由
* @param routes
* @returns
*/
function parseRoutes(routes: RouteOption[]): RouteRecordRaw[] {
return routes.map<RouteRecordRaw>((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<RouteRecordRaw, 'children'> {
children?: Map<string, RouteRecordMap>;
}
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<string, RouteRecordMap> => {
parentPath = parentPath ?? '';
const _map = new Map<string, RouteRecordMap>();
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<string, RouteRecordMap>,
source?: Map<string, RouteRecordMap>
): Map<string, RouteRecordMap> | undefined => {
if (!target || !source) {
return target ?? source;
}
const resultMap = new Map<string, RouteRecordMap>();
// 保证新路由数据顺序
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<string, RouteRecordMap>): 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<RouteRecordRaw[]>, 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));
}