routeHelper.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /**
  2. * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
  3. * No deletion without permission, or be held responsible to law.
  4. * @author Vben、ThinkGem
  5. */
  6. import type { AppRouteModule, AppRouteRecordRaw } from '@jeesite/core/router/types';
  7. import type { Router, RouteRecordNormalized } from 'vue-router';
  8. import { cloneDeep, omit } from 'lodash-es';
  9. import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
  10. import { LAYOUT, IFRAME_BLANK, IFRAME_SIMPLE, EXCEPTION_COMPONENT } from '@jeesite/core/router/constant';
  11. import { warn, env } from '@jeesite/core/utils/log';
  12. // Dynamic introduction
  13. function asyncImportRoute(
  14. routes: AppRouteRecordRaw[] | undefined,
  15. parent: AppRouteRecordRaw | undefined,
  16. root: AppRouteRecordRaw | undefined,
  17. ) {
  18. if (!routes) return;
  19. routes.forEach((item) => {
  20. item.meta = item.meta || {};
  21. if (!item.meta.icon) {
  22. item.meta.icon = 'bx:bx-circle';
  23. }
  24. if (parent && item.meta.hideMenu) {
  25. item.meta.currentActiveMenu = parent.path;
  26. }
  27. const rawComponent = item.component;
  28. const component = typeof rawComponent === 'string' ? rawComponent.toUpperCase() : '';
  29. if (rawComponent && typeof rawComponent !== 'string') {
  30. item.children && asyncImportRoute(item.children, item, root);
  31. return;
  32. }
  33. if (!component || component === 'LAYOUT') {
  34. item.component = LAYOUT;
  35. } else if (component === 'IFRAME') {
  36. item.component = root?.component ? IFRAME_BLANK : IFRAME_SIMPLE;
  37. } else {
  38. item.component = dynamicImport(item.component as string);
  39. }
  40. if (!item.component) {
  41. item.component = EXCEPTION_COMPONENT;
  42. item.props = item.props || {};
  43. item.props.status = 404;
  44. }
  45. item.children && asyncImportRoute(item.children, item, root);
  46. });
  47. }
  48. let dynamicViewsModules: Record<string, () => Promise<Recordable>>;
  49. const explicitDynamicViewMap: Record<string, () => Promise<Recordable>> = {
  50. '/graph/index': () => import('@jeesite/graph/views/graph/index.vue'),
  51. '/graph/g6-editor': () => import('@jeesite/graph/views/g6Editor/index.vue'),
  52. '/graph/relationGraphTest': () => import('@jeesite/graph/views/relationGraphTest/index.vue'),
  53. '/aiBaseStation/index': () => import('@jeesite/trans/views/aiBaseStation/index.vue'),
  54. };
  55. function normalizeViewModuleKey(key: string) {
  56. return key.replace(/\\/g, '/');
  57. }
  58. export function dynamicImport(component: string) {
  59. const explicitImport = explicitDynamicViewMap[component];
  60. if (explicitImport) {
  61. return explicitImport;
  62. }
  63. if (!dynamicViewsModules) {
  64. dynamicViewsModules = import.meta.glob('../../../../**/views/**/*.{vue,tsx}');
  65. }
  66. const keys = Object.keys(dynamicViewsModules);
  67. const matchKeys = keys.filter((key) => {
  68. const viewsPath = '/views',
  69. l = viewsPath.length,
  70. index = key.indexOf(viewsPath);
  71. let k = key.substring(index + l);
  72. const lastIndex = k.lastIndexOf('.');
  73. k = k.substring(0, lastIndex);
  74. return k === component;
  75. });
  76. if (matchKeys?.length === 1) {
  77. const matchKey = matchKeys[0];
  78. return dynamicViewsModules[matchKey];
  79. }
  80. if (matchKeys?.length > 1) {
  81. const preferCaseDmPortrait =
  82. component === '/dm/portrait' &&
  83. matchKeys.find((key) => normalizeViewModuleKey(key).endsWith('/case/views/dm/portrait.vue'));
  84. if (preferCaseDmPortrait) {
  85. return dynamicViewsModules[preferCaseDmPortrait];
  86. }
  87. const workspaceMatchKeys = matchKeys.filter((key) => !normalizeViewModuleKey(key).includes('/node_modules/'));
  88. const preferWorkspaceView = workspaceMatchKeys.length === 1 ? workspaceMatchKeys[0] : undefined;
  89. if (preferWorkspaceView) {
  90. warn(`Found duplicate view components for route "${component}", prefer workspace file: ${preferWorkspaceView}`);
  91. return dynamicViewsModules[preferWorkspaceView];
  92. }
  93. warn(`Found duplicate view components for route "${component}", fallback to first matched file: ${matchKeys[0]}`);
  94. return dynamicViewsModules[matchKeys[0]];
  95. }
  96. }
  97. // Turn background objects into routing objects
  98. export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
  99. routeList.forEach((item) => {
  100. const rawComponent = item.component;
  101. const component = typeof rawComponent === 'string' ? rawComponent.toUpperCase() : '';
  102. if (component === 'BLANK') {
  103. item.component = item.path;
  104. item.children = [cloneDeep(item)];
  105. item.component = undefined;
  106. } else {
  107. item.children = [cloneDeep(item)];
  108. item.component = LAYOUT;
  109. }
  110. item.path = '';
  111. item.name = `${item.name}Parent`;
  112. item.meta = item.meta || {};
  113. item.meta.single = true;
  114. item.meta.affix = false;
  115. item.children && asyncImportRoute(item.children, item, item);
  116. });
  117. return routeList as unknown as T[];
  118. }
  119. /**
  120. * Convert multi-level routing to level 2 routing
  121. */
  122. export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) {
  123. const modules: AppRouteModule[] = cloneDeep(routeModules);
  124. for (let index = 0; index < modules.length; index++) {
  125. const routeModule = modules[index];
  126. if (!isMultipleRoute(routeModule)) {
  127. continue;
  128. }
  129. promoteRouteLevel(routeModule);
  130. }
  131. return modules;
  132. }
  133. export function createRouteHistory() {
  134. if (env.VITE_ROUTE_WEB_HISTORY == 'true') {
  135. return createWebHistory(env.VITE_PUBLIC_PATH);
  136. } else {
  137. return createWebHashHistory(env.VITE_PUBLIC_PATH);
  138. }
  139. }
  140. // Routing level upgrade
  141. function promoteRouteLevel(routeModule: AppRouteModule) {
  142. // Use vue-router to splice menus
  143. let router: Router | null = createRouter({
  144. routes: [routeModule as unknown as RouteRecordNormalized],
  145. history: createRouteHistory(),
  146. });
  147. const routes = router.getRoutes();
  148. addToChildren(routes, routeModule.children || [], routeModule);
  149. router = null;
  150. routeModule.children = routeModule.children?.map((item) => omit(item, 'children'));
  151. }
  152. // Add all sub-routes to the secondary route
  153. function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) {
  154. for (let index = 0; index < children.length; index++) {
  155. const child = children[index];
  156. const route = routes.find((item) => item.name === child.name);
  157. if (!route) {
  158. continue;
  159. }
  160. routeModule.children = routeModule.children || [];
  161. if (!routeModule.children.find((item) => item.name === route.name)) {
  162. routeModule.children?.push(route as unknown as AppRouteModule);
  163. }
  164. if (child.children?.length) {
  165. addToChildren(routes, child.children, routeModule);
  166. }
  167. }
  168. }
  169. // Determine whether the level exceeds 2 levels
  170. function isMultipleRoute(routeModule: AppRouteModule) {
  171. if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) {
  172. return false;
  173. }
  174. const children = routeModule.children;
  175. let flag = false;
  176. for (let index = 0; index < children.length; index++) {
  177. const child = children[index];
  178. if (child.children?.length) {
  179. flag = true;
  180. break;
  181. }
  182. }
  183. return flag;
  184. }