routerHelper.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
  2. import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
  3. import { isUrl } from '@/utils/is'
  4. import { omit, cloneDeep } from 'lodash-es'
  5. const modules = import.meta.glob('../views/**/*.{vue,tsx}')
  6. /* Layout */
  7. export const Layout = () => import('@/layout/Layout.vue')
  8. export const getParentLayout = () => {
  9. return () =>
  10. new Promise((resolve) => {
  11. resolve({
  12. name: 'ParentLayout'
  13. })
  14. })
  15. }
  16. // 按照路由中meta下的rank等级升序来排序路由
  17. export const ascending = (arr: any[]) => {
  18. arr.forEach((v) => {
  19. if (v?.meta?.rank === null) v.meta.rank = undefined
  20. if (v?.meta?.rank === 0) {
  21. if (v.name !== 'home' && v.path !== '/') {
  22. console.warn('rank only the home page can be 0')
  23. }
  24. }
  25. })
  26. return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
  27. return a?.meta?.rank - b?.meta?.rank
  28. })
  29. }
  30. export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => {
  31. if (!route) return route
  32. const { matched, ...opt } = route
  33. return {
  34. ...opt,
  35. matched: (matched
  36. ? matched.map((item) => ({
  37. meta: item.meta,
  38. name: item.name,
  39. path: item.path
  40. }))
  41. : undefined) as RouteRecordNormalized[]
  42. }
  43. }
  44. // 后端控制路由生成
  45. export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => {
  46. const res: AppRouteRecordRaw[] = []
  47. const modulesRoutesKeys = Object.keys(modules)
  48. for (const route of routes) {
  49. const meta = {
  50. title: route.name,
  51. icon: route.icon,
  52. hidden: !route.visible,
  53. noCache: !route.keepAlive,
  54. alwaysShow:
  55. route.children &&
  56. route.children.length === 1 &&
  57. import.meta.env.VITE_ROUTE_ALWAYSSHOW_ENABLE === 'true'
  58. ? true
  59. : false
  60. }
  61. // 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive
  62. let data: AppRouteRecordRaw = {
  63. path: route.path,
  64. name: toCamelCase(route.path, true),
  65. redirect: route.redirect,
  66. meta: meta
  67. }
  68. //处理顶级非目录路由
  69. if (!route.children && route.parentId == 0 && route.component) {
  70. data.component = Layout
  71. data.meta = {}
  72. data.name = toCamelCase(route.path, true) + 'Parent'
  73. data.redirect = ''
  74. meta.alwaysShow = true
  75. const childrenData: AppRouteRecordRaw = {
  76. path: '',
  77. name: toCamelCase(route.path, true),
  78. redirect: route.redirect,
  79. meta: meta
  80. }
  81. const index = route?.component
  82. ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
  83. : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
  84. childrenData.component = modules[modulesRoutesKeys[index]]
  85. data.children = [childrenData]
  86. } else {
  87. // 目录
  88. if (route.children) {
  89. data.component = Layout
  90. data.redirect = getRedirect(route.path, route.children)
  91. // 外链
  92. } else if (isUrl(route.path)) {
  93. data = {
  94. path: '/external-link',
  95. component: Layout,
  96. meta: {
  97. name: route.name
  98. },
  99. children: [data]
  100. } as AppRouteRecordRaw
  101. // 菜单
  102. } else {
  103. // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会根path保持一致)
  104. const index = route?.component
  105. ? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
  106. : modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
  107. data.component = modules[modulesRoutesKeys[index]]
  108. }
  109. if (route.children) {
  110. data.children = generateRoute(route.children)
  111. }
  112. }
  113. res.push(data as AppRouteRecordRaw)
  114. }
  115. return res
  116. }
  117. export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => {
  118. if (!children || children.length == 0) {
  119. return parentPath
  120. }
  121. const path = generateRoutePath(parentPath, children[0].path)
  122. // 递归子节点
  123. if (children[0].children) return getRedirect(path, children[0].children)
  124. }
  125. const generateRoutePath = (parentPath: string, path: string) => {
  126. if (parentPath.endsWith('/')) {
  127. parentPath = parentPath.slice(0, -1) // 移除默认的 /
  128. }
  129. if (!path.startsWith('/')) {
  130. path = '/' + path
  131. }
  132. return parentPath + path
  133. }
  134. export const pathResolve = (parentPath: string, path: string) => {
  135. if (isUrl(path)) return path
  136. const childPath = path.startsWith('/') || !path ? path : `/${path}`
  137. return `${parentPath}${childPath}`.replace(/\/\//g, '/')
  138. }
  139. // 路由降级
  140. export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => {
  141. const modules: AppRouteRecordRaw[] = cloneDeep(routes)
  142. for (let index = 0; index < modules.length; index++) {
  143. const route = modules[index]
  144. if (!isMultipleRoute(route)) {
  145. continue
  146. }
  147. promoteRouteLevel(route)
  148. }
  149. return modules
  150. }
  151. // 层级是否大于2
  152. const isMultipleRoute = (route: AppRouteRecordRaw) => {
  153. if (!route || !Reflect.has(route, 'children') || !route.children?.length) {
  154. return false
  155. }
  156. const children = route.children
  157. let flag = false
  158. for (let index = 0; index < children.length; index++) {
  159. const child = children[index]
  160. if (child.children?.length) {
  161. flag = true
  162. break
  163. }
  164. }
  165. return flag
  166. }
  167. // 生成二级路由
  168. const promoteRouteLevel = (route: AppRouteRecordRaw) => {
  169. let router: Router | null = createRouter({
  170. routes: [route as RouteRecordRaw],
  171. history: createWebHashHistory()
  172. })
  173. const routes = router.getRoutes()
  174. addToChildren(routes, route.children || [], route)
  175. router = null
  176. route.children = route.children?.map((item) => omit(item, 'children'))
  177. }
  178. // 添加所有子菜单
  179. const addToChildren = (
  180. routes: RouteRecordNormalized[],
  181. children: AppRouteRecordRaw[],
  182. routeModule: AppRouteRecordRaw
  183. ) => {
  184. for (let index = 0; index < children.length; index++) {
  185. const child = children[index]
  186. const route = routes.find((item) => item.name === child.name)
  187. if (!route) {
  188. continue
  189. }
  190. routeModule.children = routeModule.children || []
  191. if (!routeModule.children.find((item) => item.name === route.name)) {
  192. routeModule.children?.push(route as unknown as AppRouteRecordRaw)
  193. }
  194. if (child.children?.length) {
  195. addToChildren(routes, child.children, routeModule)
  196. }
  197. }
  198. }
  199. const toCamelCase = (str: string, upperCaseFirst: boolean) => {
  200. str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) {
  201. return group1.toUpperCase()
  202. })
  203. if (upperCaseFirst && str) {
  204. str = str.charAt(0).toUpperCase() + str.slice(1)
  205. }
  206. return str
  207. }