index.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /**
  2. * Copyright [2022] [https://www.xiaonuo.vip]
  3. * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
  4. * 1.请不要删除和修改根目录下的LICENSE文件。
  5. * 2.请不要删除和修改Snowy源码头部的版权声明。
  6. * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
  7. * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
  8. * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
  9. * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
  10. */
  11. import { createRouter, createWebHistory } from 'vue-router'
  12. import { notification } from 'ant-design-vue'
  13. import NProgress from 'nprogress'
  14. import 'nprogress/nprogress.css'
  15. import systemRouter from './systemRouter'
  16. import { afterEach, beforeEach } from './scrollBehavior'
  17. import whiteListRouters from './whiteList'
  18. import userRoutes from '@/config/route'
  19. import tool from '@/utils/tool'
  20. import { cloneDeep } from 'lodash-es'
  21. const modules = import.meta.glob('/src/views/**/**.vue')
  22. import { globalStore, searchStore } from '@/store'
  23. // 进度条配置
  24. NProgress.configure({ showSpinner: false, speed: 500 })
  25. // 系统特殊路由
  26. const routes_404 = [
  27. {
  28. path: '/:pathMatch(.*)*',
  29. hidden: true,
  30. component: () => import('@/layout/other/404.vue')
  31. }
  32. ]
  33. // 系统路由
  34. const routes = [...systemRouter, ...whiteListRouters, ...routes_404]
  35. const router = createRouter({
  36. history: createWebHistory(import.meta.env.BASE_URL),
  37. routes
  38. })
  39. // 设置标题
  40. // document.title = sysBaseConfig.SNOWY_SYS_NAME
  41. // 判断是否已加载过动态/静态路由
  42. const isGetRouter = ref(false)
  43. // 白名单校验
  44. const exportWhiteListFromRouter = (router) => {
  45. const res = []
  46. for (const item of router) res.push(item.path)
  47. return res
  48. }
  49. const whiteList = exportWhiteListFromRouter(whiteListRouters)
  50. router.beforeEach(async (to, from, next) => {
  51. NProgress.start()
  52. const store = globalStore()
  53. const sysBaseConfig = tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.sysBaseConfig
  54. // 动态标题
  55. document.title = to.meta.title
  56. ? `${to.meta.title} - ${sysBaseConfig.SNOWY_SYS_NAME}`
  57. : `${sysBaseConfig.SNOWY_SYS_NAME}`
  58. // 过滤白名单
  59. if (whiteList.includes(to.path)) {
  60. next()
  61. // NProgress.done()
  62. return false
  63. }
  64. const token = tool.data.get('TOKEN')
  65. if (to.path === '/login') {
  66. // 当用户输入了login路由,将其跳转首页即可
  67. if (token) {
  68. next({
  69. path: '/'
  70. })
  71. return false
  72. }
  73. // 删除路由(替换当前layout路由)
  74. router.addRoute(routes[0])
  75. isGetRouter.value = false
  76. next()
  77. return false
  78. } else {
  79. if (token) {
  80. // 有token的时候才保存登录之前要访问的页面
  81. tool.data.set('LAST_VIEWS_PATH', to.fullPath)
  82. }
  83. }
  84. if (!token) {
  85. next({
  86. path: '/login'
  87. })
  88. return false
  89. }
  90. // 整页路由处理
  91. if (to.meta.fullpage) {
  92. to.matched = [to.matched[to.matched.length - 1]]
  93. }
  94. // 加载动态/静态路由
  95. if (!isGetRouter.value) {
  96. const apiMenu = tool.data.get('MENU') || []
  97. if (apiMenu.length === 0) {
  98. // 创建默认模块,显示默认菜单
  99. apiMenu[0] = cloneDeep(userRoutes.module[0])
  100. }
  101. const childrenApiMenu = apiMenu[0].children
  102. // apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
  103. apiMenu.push(...userRoutes.menu)
  104. let menuRouter = filterAsyncRouter(apiMenu)
  105. menuRouter = flatAsyncRoutes(menuRouter)
  106. menuRouter.forEach((item) => {
  107. router.addRoute('layout', item)
  108. })
  109. const search_store = searchStore()
  110. search_store.init(menuRouter)
  111. isGetRouter.value = true
  112. next({ ...to, replace: true })
  113. return false
  114. }
  115. beforeEach(to, from)
  116. next()
  117. })
  118. router.afterEach((to, from) => {
  119. afterEach(to, from)
  120. NProgress.done()
  121. })
  122. router.onError((error) => {
  123. NProgress.done()
  124. notification.error({
  125. message: '路由错误',
  126. description: error.message
  127. })
  128. })
  129. // 入侵追加自定义方法、对象
  130. router.getMenu = () => {
  131. let apiMenu = tool.data.get('MENU') || []
  132. // 增加固定路由
  133. if (apiMenu.length === 0) {
  134. // 创建默认模块,显示默认菜单
  135. apiMenu[0] = cloneDeep(userRoutes.module[0])
  136. }
  137. const childrenApiMenu = apiMenu[0].children
  138. apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
  139. return filterUrl(apiMenu)
  140. }
  141. const filterUrl = (map) => {
  142. const newMap = []
  143. const traverse = (maps) => {
  144. maps &&
  145. maps.forEach((item) => {
  146. item.meta = item.meta ? item.meta : {}
  147. // 处理iframe
  148. if (item.meta.type === 'iframe') {
  149. item.path = `/${item.name}`
  150. }
  151. // 递归循环
  152. if (item.children && item.children.length > 0) {
  153. item.children = filterUrl(item.children)
  154. }
  155. newMap.push(item)
  156. })
  157. }
  158. traverse(map)
  159. return newMap
  160. }
  161. // 转换
  162. const filterAsyncRouter = (routerMap) => {
  163. const accessedRouters = []
  164. routerMap.forEach((item) => {
  165. item.meta = item.meta ? item.meta : {}
  166. // 处理外部链接特殊路由
  167. if (item.meta.type === 'iframe') {
  168. item.meta.url = item.path
  169. item.path = `/${item.name}`
  170. }
  171. // MAP转路由对象
  172. const route = {
  173. path: item.path,
  174. name: item.id,
  175. meta: item.meta,
  176. redirect: item.redirect,
  177. children: item.children ? filterAsyncRouter(item.children) : null,
  178. component: loadComponent(item.component)
  179. }
  180. accessedRouters.push(route)
  181. })
  182. return accessedRouters
  183. }
  184. const loadComponent = (component) => {
  185. if (component) {
  186. if (component.includes('/')) {
  187. return modules[`/src/views/${component}.vue`]
  188. }
  189. return modules[`/src/views/${component}/index.vue`]
  190. } else {
  191. return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`)
  192. }
  193. }
  194. // 路由扁平化
  195. const flatAsyncRoutes = (routes, breadcrumb = []) => {
  196. const res = []
  197. routes.forEach((route) => {
  198. const tmp = { ...route }
  199. if (tmp.children) {
  200. const childrenBreadcrumb = [...breadcrumb]
  201. childrenBreadcrumb.push(route)
  202. const tmpRoute = { ...route }
  203. tmpRoute.meta.breadcrumb = childrenBreadcrumb
  204. delete tmpRoute.children
  205. res.push(tmpRoute)
  206. const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
  207. childrenRoutes.map((item) => {
  208. res.push(item)
  209. })
  210. } else {
  211. const tmpBreadcrumb = [...breadcrumb]
  212. tmpBreadcrumb.push(tmp)
  213. tmp.meta.breadcrumb = tmpBreadcrumb
  214. res.push(tmp)
  215. }
  216. })
  217. return res
  218. }
  219. export default router