backEnd.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import { RouteRecordRaw } from 'vue-router'
  2. // import { storeToRefs } from 'pinia'
  3. import pinia from '/@/stores/index'
  4. import { useUserInfo } from '/@/stores/userInfo'
  5. import { useRequestOldRoutes } from '/@/stores/requestOldRoutes'
  6. import { NextLoading } from '/@/utils/loading'
  7. import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route'
  8. import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index'
  9. import { useRoutesList } from '/@/stores/routesList'
  10. import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'
  11. // import { useMenuApi } from '/@/api/menu/index'
  12. import { AuthApi } from '/@/api/admin/Auth'
  13. import { listToTree } from '/@/utils/tree'
  14. import { getToken } from '/@/api/admin/http-client'
  15. // 后端控制路由
  16. // 引入 api 请求接口
  17. // const menuApi = useMenuApi()
  18. /**
  19. * 获取目录下的 .vue、.tsx 全部文件
  20. * @method import.meta.glob
  21. * @link 参考:https://cn.vitejs.dev/guide/features.html#json
  22. */
  23. const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}')
  24. const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}')
  25. const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules })
  26. /**
  27. * 后端控制路由:初始化方法,防止刷新时路由丢失
  28. * @method NextLoading 界面 loading 动画开始执行
  29. * @method useUserInfo().setUserInfos() 触发初始化用户信息 pinia
  30. * @method useRequestOldRoutes().setRequestOldRoutes() 存储接口原始路由(未处理component),根据需求选择使用
  31. * @method setAddRoute 添加动态路由
  32. * @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
  33. */
  34. export async function initBackEndControlRoutes() {
  35. // 界面 loading 动画开始执行
  36. if (window.nextLoading === undefined) NextLoading.start()
  37. // 无 token 停止执行下一步
  38. if (!getToken()) return false
  39. // 触发初始化用户信息 pinia
  40. // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
  41. await useUserInfo().setUserInfos()
  42. // 获取路由菜单数据
  43. const menus = await getBackEndControlRoutes()
  44. if (!(menus?.length > 0)) return Promise.resolve(true)
  45. // 存储接口原始路由(未处理component),根据需求选择使用
  46. useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(menus)))
  47. // 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
  48. const routes = await backEndComponent(menus)
  49. const isIncludeExample = true
  50. if (isIncludeExample) {
  51. //包含样例
  52. dynamicRoutes[0].children?.unshift(...routes)
  53. } else {
  54. dynamicRoutes[0].children = routes
  55. }
  56. // 添加动态路由
  57. await setAddRoute()
  58. // 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
  59. await setFilterMenuAndCacheTagsViewRoutes()
  60. }
  61. /**
  62. * 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
  63. * @description 用于左侧菜单、横向菜单的显示
  64. * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
  65. */
  66. export function setFilterMenuAndCacheTagsViewRoutes() {
  67. const storesRoutesList = useRoutesList(pinia)
  68. storesRoutesList.setRoutesList(dynamicRoutes[0].children as any)
  69. setCacheTagsViewRoutes()
  70. }
  71. /**
  72. * 缓存多级嵌套数组处理后的一维数组
  73. * @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
  74. */
  75. export function setCacheTagsViewRoutes() {
  76. const storesTagsView = useTagsViewRoutes(pinia)
  77. storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children)
  78. }
  79. /**
  80. * 处理路由格式及添加捕获所有路由或 404 Not found 路由
  81. * @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
  82. * @returns 返回替换后的路由数组
  83. */
  84. export function setFilterRouteEnd() {
  85. let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))
  86. // notFoundAndNoPower 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
  87. // 关联问题 No match found for location with path 'xxx'
  88. filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower]
  89. return filterRouteEnd
  90. }
  91. /**
  92. * 添加动态路由
  93. * @method router.addRoute
  94. * @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套
  95. * @link 参考:https://next.router.vuejs.org/zh/api/#addroute
  96. */
  97. export async function setAddRoute() {
  98. await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
  99. router.addRoute(route)
  100. })
  101. }
  102. /**
  103. * 请求后端路由菜单接口
  104. * @description isRequestRoutes 为 true,则开启后端控制路由
  105. * @returns 返回后端路由菜单数据
  106. */
  107. export async function getBackEndControlRoutes() {
  108. const res = await new AuthApi().getUserInfo()
  109. if (res?.success && (res?.data?.menus?.length as number) > 0) {
  110. const menus = [] as any
  111. res.data?.menus?.forEach((menu) => {
  112. menus.push({
  113. id: menu.id,
  114. parentId: menu.parentId,
  115. name: menu.name ? menu.name : menu.id + '',
  116. path: menu.path ? menu.path : menu.id + '',
  117. redirect: menu.redirect,
  118. component: menu.viewPath ? menu.viewPath : 'Layout',
  119. meta: {
  120. title: menu.label as string,
  121. icon: menu.icon,
  122. isAffix: menu.isAffix,
  123. isHide: menu.hidden,
  124. isIframe: menu.isIframe,
  125. isKeepAlive: menu.isKeepAlive,
  126. isLink: menu.link,
  127. status: 1,
  128. remark: null,
  129. order: menu.sort,
  130. },
  131. })
  132. })
  133. const menuTree = listToTree(menus)
  134. return menuTree
  135. } else {
  136. return []
  137. }
  138. /*
  139. // 模拟 admin 与 test
  140. const stores = useUserInfo(pinia)
  141. const { userInfos } = storeToRefs(stores)
  142. const auth = userInfos.value.roles[0]
  143. // 管理员 admin
  144. if (auth === 'admin') return menuApi.getAdminMenu()
  145. // 其它用户 test
  146. else return menuApi.getTestMenu()
  147. */
  148. }
  149. /**
  150. * 重新请求后端路由菜单接口
  151. * @description 用于菜单管理界面刷新菜单(未进行测试)
  152. * @description 路径:/src/views/system/menu/component/addMenu.vue
  153. */
  154. export function setBackEndControlRefreshRoutes() {
  155. getBackEndControlRoutes()
  156. }
  157. /**
  158. * 后端路由 component 转换
  159. * @param routes 后端返回的路由表数组
  160. * @returns 返回处理成函数后的 component
  161. */
  162. export function backEndComponent(routes: any) {
  163. if (!routes) return
  164. return routes.map((item: any) => {
  165. if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string)
  166. item.children && backEndComponent(item.children)
  167. return item
  168. })
  169. }
  170. /**
  171. * 后端路由 component 转换函数
  172. * @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
  173. * @param component 当前要处理项 component
  174. * @returns 返回处理成函数后的 component
  175. */
  176. export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
  177. const keys = Object.keys(dynamicViewsModules)
  178. const matchKeys = keys.filter((key) => {
  179. const k = key.replace(/..\/views|../, '')
  180. return k.startsWith(`${component}`) || k.startsWith(`/${component}`)
  181. })
  182. if (matchKeys?.length === 1) {
  183. const matchKey = matchKeys[0]
  184. return dynamicViewsModules[matchKey]
  185. }
  186. if (matchKeys?.length > 1) {
  187. return false
  188. }
  189. }