chore: 并入 iotlcm-admin-ui 历史到 admin-ui/(原提交 f305eab)
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import type {
|
||||
ComponentRecordType,
|
||||
GenerateMenuAndRoutesOptions,
|
||||
RouteRecordStringComponent,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
import { mapTree } from '@vben-core/shared/utils';
|
||||
|
||||
/**
|
||||
* 动态生成路由 - 后端方式
|
||||
*/
|
||||
async function generateRoutesByBackend(
|
||||
options: GenerateMenuAndRoutesOptions,
|
||||
): Promise<RouteRecordRaw[]> {
|
||||
const { fetchMenuListAsync, layoutMap = {}, pageMap = {} } = options;
|
||||
|
||||
try {
|
||||
const menuRoutes = await fetchMenuListAsync?.();
|
||||
if (!menuRoutes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const normalizePageMap: ComponentRecordType = {};
|
||||
|
||||
for (const [key, value] of Object.entries(pageMap)) {
|
||||
normalizePageMap[normalizeViewPath(key)] = value;
|
||||
}
|
||||
|
||||
const routes = convertRoutes(
|
||||
removePathlessRoutes(menuRoutes),
|
||||
layoutMap,
|
||||
normalizePageMap,
|
||||
);
|
||||
|
||||
return routes;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function removePathlessRoutes(
|
||||
routes: RouteRecordStringComponent[],
|
||||
): RouteRecordStringComponent[] {
|
||||
return routes.flatMap((route) => {
|
||||
const children = removePathlessRoutes(route.children ?? []);
|
||||
if (!route.path?.trim()) {
|
||||
console.warn('Skipping backend menu route with an empty path', {
|
||||
name: route.name,
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
return [{ ...route, children }];
|
||||
});
|
||||
}
|
||||
|
||||
function convertRoutes(
|
||||
routes: RouteRecordStringComponent[],
|
||||
layoutMap: ComponentRecordType,
|
||||
pageMap: ComponentRecordType,
|
||||
): RouteRecordRaw[] {
|
||||
// 前端无对应视图、需丢弃的路由
|
||||
const invalidRoutes = new WeakSet<object>();
|
||||
|
||||
const converted = mapTree(routes, (node) => {
|
||||
const route = node as unknown as RouteRecordRaw;
|
||||
const { component, name } = node;
|
||||
|
||||
if (!name) {
|
||||
console.error('route name is required', route);
|
||||
}
|
||||
|
||||
// layout转换
|
||||
if (component && layoutMap[component]) {
|
||||
route.component = layoutMap[component];
|
||||
// 页面组件转换
|
||||
} else if (component) {
|
||||
const normalizePath = normalizeViewPath(component);
|
||||
const pageKey = normalizePath.endsWith('.vue')
|
||||
? normalizePath
|
||||
: `${normalizePath}.vue`;
|
||||
if (pageMap[pageKey]) {
|
||||
route.component = pageMap[pageKey];
|
||||
} else {
|
||||
// 前端没有对应视图(后端菜单指向了已不存在的页面)。
|
||||
// 这里不再替换成 not-found 组件——那样菜单还在、点进去却是 404 页面。
|
||||
// 直接标记丢弃:路由不注册、菜单里也不出现这个死链。
|
||||
console.warn(`route component is invalid, route skipped: ${pageKey}`, {
|
||||
name,
|
||||
path: node.path,
|
||||
});
|
||||
invalidRoutes.add(route);
|
||||
}
|
||||
}
|
||||
|
||||
return route;
|
||||
});
|
||||
|
||||
return pruneInvalidRoutes(converted, layoutMap, invalidRoutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 丢弃解析不到视图的路由;并去掉因此变空的目录。
|
||||
*/
|
||||
function pruneInvalidRoutes(
|
||||
routes: RouteRecordRaw[],
|
||||
layoutMap: ComponentRecordType,
|
||||
invalidRoutes: WeakSet<object>,
|
||||
): RouteRecordRaw[] {
|
||||
return routes
|
||||
.filter((route) => !invalidRoutes.has(route))
|
||||
.map((route) => {
|
||||
if (!route.children) {
|
||||
return route;
|
||||
}
|
||||
return {
|
||||
...route,
|
||||
children: pruneInvalidRoutes(route.children, layoutMap, invalidRoutes),
|
||||
};
|
||||
})
|
||||
.filter((route) => {
|
||||
const children = route.children ?? [];
|
||||
if (children.length > 0 || route.redirect) {
|
||||
return true;
|
||||
}
|
||||
const meta = route.meta as Record<string, unknown> | undefined;
|
||||
// 外链菜单(新窗口打开)没有组件是正常的,不能丢
|
||||
if (meta?.openInNewWindow || meta?.link) {
|
||||
return true;
|
||||
}
|
||||
// 只剩布局组件 = 变成了空目录,丢掉
|
||||
const isLayout = Object.values(layoutMap).includes(
|
||||
route.component as never,
|
||||
);
|
||||
if (isLayout) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(route.component);
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeViewPath(path: string): string {
|
||||
// 去除相对路径前缀
|
||||
const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, '');
|
||||
|
||||
// 确保路径以 '/' 开头
|
||||
const viewPath = normalizedPath.startsWith('/')
|
||||
? normalizedPath
|
||||
: `/${normalizedPath}`;
|
||||
|
||||
// 这里耦合了vben-admin的目录结构
|
||||
return viewPath.replace(/^\/views/, '');
|
||||
}
|
||||
export { generateRoutesByBackend };
|
||||
Reference in New Issue
Block a user