guard.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import { AxiosWrapper } from '@/utils/axios-wrapper'
  2. import { UserStore } from '@/store/user'
  3. import { cloneDeep, isArray, omit } from 'lodash-es'
  4. import NProgress from 'nprogress'
  5. import 'nprogress/nprogress.css'
  6. import { ElMessageBox } from 'element-plus'
  7. import i18n from '@/lang'
  8. const whiteList = ['/redirect', '/login', '/401', '/404']
  9. NProgress.configure({ showSpinner: false }) // 进度条
  10. // 检查路由是否存在
  11. const hasNecessaryRoute = (to, router) => {
  12. // 将默认注册的路由平铺成一维数组
  13. const routesOneDimensional = toOneDimensional(router.getRoutes())
  14. return routesOneDimensional.find((f) => f.path === to.path)
  15. }
  16. // 获取用户信息,返回菜单
  17. const getUserMeta = async (token) => {
  18. const userStore = UserStore()
  19. const axios = new AxiosWrapper()
  20. const result = await axios.$get(`/token/tokenView`, null, {
  21. headers: {
  22. token: token
  23. }
  24. })
  25. if (result.errcode === 0) {
  26. userStore.setUser(result.data)
  27. const resetMenusResult = resetMenus(result.data.menus)
  28. const storeMenus = toRaw(userStore.menus)
  29. if (storeMenus.length <= 0) {
  30. userStore.setMenus(resetMenusResult)
  31. }
  32. return { menus: result.data.menus, errcode: 0 }
  33. }
  34. return { errmsg: result.errmsg, errcode: result.errcode }
  35. }
  36. /**
  37. * 将路由数组一维化
  38. * @param {Array} routes 路由数组
  39. * @returns 一维路由数组
  40. */
  41. const toOneDimensional = (routes) => {
  42. const result = []
  43. for (const r of routes) {
  44. const { children = [], ...others } = r
  45. result.push(others)
  46. if (children.length > 0) result.push(...toOneDimensional(children))
  47. }
  48. return result
  49. }
  50. // 添加路由
  51. const addUserRoutes = async (menus, router) => {
  52. return new Promise((resolve, reject) => {
  53. // 将用户菜单转换成普通对象
  54. const menuArr = toRaw(menus)
  55. // 将用户菜单平铺成一维数组,并将目录过滤出去.目录不需要注册,不是组件
  56. const menuOneDimensional = toOneDimensional(menuArr)
  57. // 将默认注册的路由平铺成一维数组
  58. const routesOneDimensional = toOneDimensional(router.getRoutes())
  59. routesRegister(menuOneDimensional, routesOneDimensional, router)
  60. resolve()
  61. })
  62. }
  63. /**
  64. * 注册路由
  65. * @param {Array} menus 一维数组菜单
  66. * @param {Array} defaultRoutes 默认路由一维数组
  67. * @param {*} router 路由实例
  68. */
  69. const routesRegister = (menus, defaultRoutes, router) => {
  70. // 默认注册位置
  71. const __def = 'Layout'
  72. const loadComponent = import.meta.glob('../views/**/*.vue')
  73. for (const route of menus) {
  74. const { type, route_name, path, component, parent_id } = route
  75. if (!path) {
  76. console.log(route)
  77. }
  78. // 检查路由是否已存在,存在跳过
  79. const hasRoute = defaultRoutes.find((f) => f.path === path)
  80. if (hasRoute) continue
  81. if (type === '0') {
  82. // 目录, 默认使用 `/${路由名称}`
  83. const route = {
  84. path: `/${route_name}`,
  85. name: route_name,
  86. meta: { title: route_name, type }
  87. }
  88. router.addRoute(__def, route)
  89. } else if (type === '1' || type === '2') {
  90. // 菜单 或 子菜单
  91. const route = {
  92. path: path,
  93. name: route_name,
  94. meta: {
  95. title: route_name,
  96. type
  97. },
  98. component: loadComponent[`../views${component}.vue`]
  99. }
  100. if (parent_id) {
  101. const parent = menus.find((f) => f._id === parent_id)
  102. if (!parent) continue
  103. const pos = parent.route_name
  104. router.addRoute(pos, route)
  105. // try {
  106. // } catch (error) {
  107. // console.log(pos, route)
  108. // console.error(error)
  109. // }
  110. } else {
  111. router.addRoute(__def, route)
  112. }
  113. }
  114. }
  115. }
  116. const resetMenus = (menus) => {
  117. if (!isArray(menus) || menus.length <= 0) return []
  118. const cMenus = cloneDeep(menus)
  119. const result = []
  120. for (const m of cMenus) {
  121. const mid = omit(m, ['is_use', 'order_num', 'in_admin_frame'])
  122. const { children } = mid
  123. if (children) mid.children = resetMenus(children)
  124. result.push(mid)
  125. }
  126. return result
  127. }
  128. // 注册前置守卫
  129. export const registerBeforeRouter = async (router) => {
  130. router.beforeEach(async (to, from, next) => {
  131. NProgress.start() //开启进度条
  132. const token = localStorage.getItem('token')
  133. NProgress.inc()
  134. if (whiteList.includes(to.path)) {
  135. next()
  136. return
  137. }
  138. if (token) {
  139. NProgress.inc()
  140. if (to.path === '/login') next()
  141. NProgress.inc()
  142. const { menus, errcode, errmsg } = await getUserMeta(token)
  143. // 登录信息有问题
  144. if (errcode !== 0) {
  145. if (errcode.includes('FRAMEERROR_401')) {
  146. await ElMessageBox.alert(errmsg, i18n.global.t('common.user_confirm'), {
  147. confirmButtonText: i18n.global.t('common.re_login'),
  148. type: 'error'
  149. })
  150. next('/login')
  151. return
  152. } else {
  153. await ElMessageBox.alert(errmsg, i18n.global.t('common.user_confirm'), {
  154. confirmButtonText: i18n.global.t('common.re_login'),
  155. type: 'error'
  156. })
  157. location.reload()
  158. }
  159. }
  160. // 菜单格式不正确
  161. if (!menus) {
  162. next('/401')
  163. return
  164. }
  165. // 检查目的地路由是否注册
  166. const hasRoute = hasNecessaryRoute(to, router)
  167. NProgress.inc()
  168. if (hasRoute || to.meta.hidden) {
  169. // 注册了直接进入
  170. next()
  171. } else {
  172. // 没注册就先注册再重定向进入直到进入为止
  173. await addUserRoutes(menus, router)
  174. NProgress.inc()
  175. next({ ...to, replace: true })
  176. }
  177. } else {
  178. next('/login')
  179. }
  180. })
  181. }
  182. // 注册路由后置守卫
  183. export const registerAfterRouter = async (router) => {
  184. router.afterEach(async (to, form) => {
  185. NProgress.done() //完成进度条
  186. if (to.path === '/login') {
  187. return
  188. }
  189. // 请求该页面的权限
  190. })
  191. }