generator-routers.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { getRouters } from '@/api/system/menu';
  2. import { constantRouterIcon } from './router-icons';
  3. import { RouteRecordRaw } from 'vue-router';
  4. import { Layout, ParentLayout, HomeLayout, MenuLayout, FixedScreenLayout } from '@/router/constant';
  5. import type { AppRouteRecordRaw } from '@/router/types';
  6. const Iframe = () => import('@/views/iframe/index.vue');
  7. const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
  8. LayoutMap.set('LAYOUT', Layout);
  9. LayoutMap.set('IFRAME', Iframe);
  10. LayoutMap.set('HOME_LAYOUT', HomeLayout);
  11. LayoutMap.set('MENU_LAYOUT', MenuLayout);
  12. LayoutMap.set('FIXED_SCREEN_LAYOUT', FixedScreenLayout);
  13. /**
  14. * 格式化 后端 结构信息并递归生成层级路由表
  15. * @param routerMap
  16. * @param parent
  17. * @returns {*}
  18. */
  19. export const routerGenerator = (routerMap, parent?): any[] => {
  20. return routerMap.map((item) => {
  21. const currentRouter: any = {
  22. // 路由地址 动态拼接生成如 /dashboard/workplace
  23. path: `${(parent && parent.path) || ''}/${item.path}`,
  24. // 路由名称,建议唯一
  25. name: item.name || '',
  26. // 该路由对应页面的 组件
  27. component: item.component,
  28. // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
  29. meta: {
  30. ...item.meta,
  31. label: item.meta.title,
  32. icon: constantRouterIcon[item.meta.icon] || item.meta.icon || null,
  33. permissions: item.meta.permissions || null,
  34. },
  35. };
  36. // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
  37. currentRouter.path = currentRouter.path.replace('//', '/');
  38. // 重定向
  39. item.redirect && (currentRouter.redirect = item.redirect);
  40. // 是否有子菜单,并递归处理
  41. if (item.children && item.children.length > 0) {
  42. //如果未定义 redirect 默认第一个子路由为 redirect
  43. !item.redirect &&
  44. (currentRouter.redirect = `${parent?.path ? parent?.path + '/' : ''}${item.path}/${item.children[0].path}`);
  45. // Recursion
  46. currentRouter.children = routerGenerator(item.children, currentRouter);
  47. }
  48. return currentRouter;
  49. });
  50. };
  51. /**
  52. * 动态生成菜单
  53. * @returns {Promise<Router>}
  54. */
  55. export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
  56. return new Promise((resolve, reject) => {
  57. getRouters()
  58. .then((result) => {
  59. const routeList = routerGenerator(result);
  60. asyncImportRoute(routeList);
  61. resolve(routeList);
  62. })
  63. .catch((err) => {
  64. reject(err);
  65. });
  66. });
  67. };
  68. /**
  69. * 查找views中对应的组件文件
  70. * */
  71. let viewsModules: Record<string, () => Promise<Recordable>>;
  72. export const asyncImportRoute = (routes: AppRouteRecordRaw[] | undefined): void => {
  73. viewsModules = viewsModules || import.meta.glob('../views/**/*.{vue,tsx}');
  74. if (!routes) return;
  75. routes.forEach((item) => {
  76. if (!item.component && item.meta?.frameSrc) {
  77. item.component = 'IFRAME';
  78. }
  79. const { component, name } = item;
  80. const { children } = item;
  81. if (component) {
  82. const layoutFound = LayoutMap.get(component as string);
  83. if (layoutFound) {
  84. item.component = layoutFound;
  85. } else {
  86. item.component = dynamicImport(viewsModules, component as string);
  87. }
  88. } else if (name) {
  89. item.component = ParentLayout;
  90. }
  91. children && asyncImportRoute(children);
  92. });
  93. };
  94. /**
  95. * 动态导入
  96. * */
  97. export const dynamicImport = (viewsModules: Record<string, () => Promise<Recordable>>, component: string) => {
  98. const keys = Object.keys(viewsModules);
  99. const matchKeys = keys.filter((key) => {
  100. let k = key.replace('../views', '');
  101. const lastIndex = k.lastIndexOf('.');
  102. k = k.substring(0, lastIndex);
  103. return k === component;
  104. });
  105. if (matchKeys?.length === 1) {
  106. const matchKey = matchKeys[0];
  107. return viewsModules[matchKey];
  108. }
  109. if (matchKeys?.length > 1) {
  110. console.warn(
  111. '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',
  112. );
  113. return;
  114. }
  115. };