generate-routes-backend.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import type { RouteRecordRaw } from 'vue-router';
  2. import type {
  3. ComponentRecordType,
  4. GenerateMenuAndRoutesOptions,
  5. RouteRecordStringComponent,
  6. } from '@vben-core/typings';
  7. import { mapTree } from '@vben-core/shared/utils';
  8. /**
  9. * 判断路由是否在菜单中显示但访问时展示 403(让用户知悉功能并申请权限)
  10. */
  11. function menuHasVisibleWithForbidden(route: RouteRecordRaw): boolean {
  12. return !!route.meta?.menuVisibleWithForbidden;
  13. }
  14. /**
  15. * 动态生成路由 - 后端方式
  16. * 对 meta.menuVisibleWithForbidden 为 true 的项直接替换为 403 组件,让用户知悉功能并申请权限。
  17. */
  18. async function generateRoutesByBackend(
  19. options: GenerateMenuAndRoutesOptions,
  20. ): Promise<RouteRecordRaw[]> {
  21. const {
  22. fetchMenuListAsync,
  23. layoutMap = {},
  24. pageMap = {},
  25. forbiddenComponent,
  26. } = options;
  27. try {
  28. const menuRoutes = await fetchMenuListAsync?.();
  29. if (!menuRoutes) {
  30. return [];
  31. }
  32. const normalizePageMap: ComponentRecordType = {};
  33. for (const [key, value] of Object.entries(pageMap)) {
  34. normalizePageMap[normalizeViewPath(key)] = value;
  35. }
  36. let routes = convertRoutes(menuRoutes, layoutMap, normalizePageMap);
  37. if (forbiddenComponent) {
  38. routes = mapTree(routes, (route) => {
  39. if (menuHasVisibleWithForbidden(route)) {
  40. route.component = forbiddenComponent;
  41. }
  42. return route;
  43. });
  44. }
  45. return routes;
  46. } catch (error) {
  47. console.error(error);
  48. throw error;
  49. }
  50. }
  51. function convertRoutes(
  52. routes: RouteRecordStringComponent[],
  53. layoutMap: ComponentRecordType,
  54. pageMap: ComponentRecordType,
  55. ): RouteRecordRaw[] {
  56. return mapTree(routes, (node) => {
  57. const route = node as unknown as RouteRecordRaw;
  58. const { component, name } = node;
  59. if (!name) {
  60. console.error('route name is required', route);
  61. }
  62. // layout转换
  63. if (component && layoutMap[component]) {
  64. route.component = layoutMap[component];
  65. // 页面组件转换
  66. } else if (component) {
  67. const normalizePath = normalizeViewPath(component);
  68. const pageKey = normalizePath.endsWith('.vue')
  69. ? normalizePath
  70. : `${normalizePath}.vue`;
  71. if (pageMap[pageKey]) {
  72. route.component = pageMap[pageKey];
  73. } else {
  74. console.error(`route component is invalid: ${pageKey}`, route);
  75. route.component = pageMap['/_core/fallback/not-found.vue'];
  76. }
  77. }
  78. return route;
  79. });
  80. }
  81. function normalizeViewPath(path: string): string {
  82. // 去除相对路径前缀
  83. const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, '');
  84. // 确保路径以 '/' 开头
  85. const viewPath = normalizedPath.startsWith('/')
  86. ? normalizedPath
  87. : `/${normalizedPath}`;
  88. // 这里耦合了vben-admin的目录结构
  89. return viewPath.replace(/^\/views/, '');
  90. }
  91. export { generateRoutesByBackend };