menu.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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 { defineStore } from 'pinia'
  12. import tool from '@/utils/tool'
  13. import { cloneDeep } from 'lodash-es'
  14. import userRoutes from '@/config/route'
  15. import { searchStore } from '@/store/search'
  16. import router from '@/router'
  17. import userCenterApi from '@/api/sys/userCenterApi'
  18. import whiteList from '@/router/whiteList'
  19. import routesData from '@/router/systemRouter'
  20. // findPwd和login路由组件已静态加载,此处不在进行异步加载
  21. const modules = import.meta.glob([
  22. '/src/views/**/**.vue',
  23. '!/src/views/auth/findPwd/**.vue',
  24. '!/src/views/auth/login/**.vue'
  25. ])
  26. export const useMenuStore = defineStore('menuStore', () => {
  27. const menuData = ref([])
  28. const refreshFlag = ref(false)
  29. // 改变刷新标志
  30. const changeRefreshFlag = (flag) => {
  31. refreshFlag.value = flag
  32. }
  33. // 加载菜单
  34. const loadMenu = () => {
  35. // 获取用户菜单
  36. const apiMenu = tool.data.get('MENU') || []
  37. if (apiMenu.length === 0) {
  38. // 创建默认模块,显示默认菜单
  39. apiMenu[0] = cloneDeep(userRoutes.module[0])
  40. }
  41. const childrenApiMenu = apiMenu[0].children
  42. apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
  43. let menuRouter = filterAsyncRouter(apiMenu)
  44. menuRouter = flatAsyncRoutes(menuRouter)
  45. menuData.value = menuRouter
  46. // 初始化搜索
  47. const search_store = searchStore()
  48. search_store.init(menuRouter)
  49. }
  50. // 过滤异步路由
  51. const filterAsyncRouter = (routerMap) => {
  52. const accessedRouters = []
  53. routerMap.forEach((item) => {
  54. item.meta = item.meta ? item.meta : {}
  55. // 处理外部链接特殊路由
  56. if (item.meta.type === 'iframe') {
  57. item.meta.url = item.path
  58. item.path = `/${item.name}`
  59. }
  60. // MAP转路由对象
  61. const route = {
  62. path: item.path,
  63. name: item.name,
  64. meta: item.meta,
  65. redirect: item.redirect,
  66. children: item.children ? filterAsyncRouter(item.children) : null,
  67. component: loadComponent(item.component)
  68. }
  69. accessedRouters.push(route)
  70. })
  71. return accessedRouters
  72. }
  73. // 将异步路由扁平化
  74. const flatAsyncRoutes = (routes, breadcrumb = []) => {
  75. const res = []
  76. routes.forEach((route) => {
  77. const tmp = { ...route }
  78. if (tmp.children) {
  79. const childrenBreadcrumb = [...breadcrumb]
  80. childrenBreadcrumb.push(route)
  81. const tmpRoute = { ...route }
  82. tmpRoute.meta.breadcrumb = childrenBreadcrumb
  83. delete tmpRoute.children
  84. res.push(tmpRoute)
  85. const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
  86. childrenRoutes.map((item) => {
  87. res.push(item)
  88. })
  89. } else {
  90. const tmpBreadcrumb = [...breadcrumb]
  91. tmpBreadcrumb.push(tmp)
  92. tmp.meta.breadcrumb = tmpBreadcrumb
  93. res.push(tmp)
  94. }
  95. })
  96. return res
  97. }
  98. // 动态加载组件
  99. const loadComponent = (component) => {
  100. if (component) {
  101. if (component.includes('/')) {
  102. return modules[`/src/views/${component}.vue`]
  103. }
  104. return modules[`/src/views/${component}/index.vue`]
  105. } else {
  106. return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`)
  107. }
  108. }
  109. // 从路由中移除菜单
  110. const removeFromRouter = () => {
  111. const routes = router.getRoutes()
  112. // 遍历所有路由
  113. routes.forEach((route) => {
  114. // 过滤白名单
  115. if (
  116. whiteList.filter((e) => e.path === route.path).length > 0 ||
  117. routesData.filter((e) => e.path === route.path).length > 0
  118. ) {
  119. return
  120. }
  121. if (route.name && route.name !== 'layout') {
  122. router.removeRoute(route.name)
  123. }
  124. })
  125. }
  126. // 获取用户菜单(通过API重新初始化菜单,用于界面实时响应)
  127. const fetchMenu = async () => {
  128. const menu = await userCenterApi.userLoginMenu()
  129. tool.data.set('MENU', menu)
  130. refreshMenu()
  131. }
  132. // 刷新菜单(非API刷新,用于路由守卫内使用)
  133. const refreshMenu = () => {
  134. loadMenu()
  135. removeFromRouter()
  136. addToRouter()
  137. changeRefreshFlag(true)
  138. }
  139. // 通过API刷新菜单(仅在layout的onMounted内使用,浏览器刷新只刷新一次)
  140. const refreshApiMenu = () => {
  141. userCenterApi.userLoginMenu().then((data) => {
  142. tool.data.set('MENU', data)
  143. nextTick(() => {
  144. refreshMenu()
  145. })
  146. })
  147. }
  148. // 将菜单添加到路由
  149. const addToRouter = () => {
  150. menuData.value.forEach((item) => {
  151. router.addRoute('layout', item)
  152. })
  153. }
  154. return { menuData, loadMenu, addToRouter, refreshMenu, changeRefreshFlag, refreshFlag, fetchMenu, refreshApiMenu }
  155. })