permission.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /* eslint-disable no-case-declarations */
  2. import { toRaw } from 'vue'
  3. import { defineStore } from 'pinia'
  4. import { useUserStore } from './user'
  5. import { useAppStoreWithOut } from './app'
  6. import { store } from '@/store'
  7. import type { AppRouteRecordRaw, Menu } from '@/router/types'
  8. import { asyncRoutes } from '@/router/routes'
  9. import about from '@/router/routes/modules/about'
  10. import dashboard from '@/router/routes/modules/dashboard'
  11. import { PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic'
  12. import { transformRouteToMenu } from '@/router/helper/menuHelper'
  13. import { flatMultiLevelRoutes, transformObjToRoute } from '@/router/helper/routeHelper'
  14. import { useI18n } from '@/hooks/web/useI18n'
  15. import { useMessage } from '@/hooks/web/useMessage'
  16. import { filter } from '@/utils/helper/treeHelper'
  17. import projectSetting from '@/settings/projectSetting'
  18. import { PageEnum } from '@/enums/pageEnum'
  19. import { PermissionModeEnum } from '@/enums/appEnum'
  20. interface PermissionState {
  21. // Permission code list
  22. // 权限代码列表
  23. permCodeList: string[] | number[]
  24. // Whether the route has been dynamically added
  25. // 路由是否动态添加
  26. isDynamicAddedRoute: boolean
  27. // To trigger a menu update
  28. // 触发菜单更新
  29. lastBuildMenuTime: number
  30. // Backstage menu list
  31. // 后台菜单列表
  32. backMenuList: Menu[]
  33. // 菜单列表
  34. frontMenuList: Menu[]
  35. }
  36. export const usePermissionStore = defineStore('app-permission', {
  37. state: (): PermissionState => ({
  38. // 权限代码列表
  39. permCodeList: [],
  40. // Whether the route has been dynamically added
  41. // 路由是否动态添加
  42. isDynamicAddedRoute: false,
  43. // To trigger a menu update
  44. // 触发菜单更新
  45. lastBuildMenuTime: 0,
  46. // Backstage menu list
  47. // 后台菜单列表
  48. backMenuList: [],
  49. // menu List
  50. // 菜单列表
  51. frontMenuList: [],
  52. }),
  53. getters: {
  54. getPermCodeList(state): string[] | number[] {
  55. return state.permCodeList
  56. },
  57. getBackMenuList(state): Menu[] {
  58. return state.backMenuList
  59. },
  60. getFrontMenuList(state): Menu[] {
  61. return state.frontMenuList
  62. },
  63. getLastBuildMenuTime(state): number {
  64. return state.lastBuildMenuTime
  65. },
  66. getIsDynamicAddedRoute(state): boolean {
  67. return state.isDynamicAddedRoute
  68. },
  69. },
  70. actions: {
  71. setPermCodeList(codeList: string[]) {
  72. this.permCodeList = codeList
  73. },
  74. setBackMenuList(list: Menu[]) {
  75. this.backMenuList = list
  76. list?.length > 0 && this.setLastBuildMenuTime()
  77. },
  78. setFrontMenuList(list: Menu[]) {
  79. this.frontMenuList = list
  80. },
  81. setLastBuildMenuTime() {
  82. this.lastBuildMenuTime = new Date().getTime()
  83. },
  84. setDynamicAddedRoute(added: boolean) {
  85. this.isDynamicAddedRoute = added
  86. },
  87. resetState(): void {
  88. this.isDynamicAddedRoute = false
  89. this.permCodeList = []
  90. this.backMenuList = []
  91. this.lastBuildMenuTime = 0
  92. },
  93. changePermissionCode(codeList: string[]) {
  94. this.setPermCodeList(codeList)
  95. },
  96. // 构建路由
  97. buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
  98. const { t } = useI18n()
  99. const userStore = useUserStore()
  100. const appStore = useAppStoreWithOut()
  101. let routes: AppRouteRecordRaw[] = []
  102. const roleList = toRaw(userStore.getRoleList) || []
  103. const userInfo = toRaw(userStore.getUserInfo) || {}
  104. const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig
  105. // 路由过滤器 在 函数filter 作为回调传入遍历使用
  106. const routeFilter = (route: AppRouteRecordRaw) => {
  107. const { meta } = route
  108. // 抽出角色
  109. const { roles } = meta || {}
  110. if (!roles)
  111. return true
  112. // 进行角色权限判断
  113. return roleList.some(role => roles.includes(role))
  114. }
  115. const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
  116. const { meta } = route
  117. // ignoreRoute 为true 则路由仅用于菜单生成,不会在实际的路由表中出现
  118. const { ignoreRoute } = meta || {}
  119. // arr.filter 返回 true 表示该元素通过测试
  120. return !ignoreRoute
  121. }
  122. /**
  123. * @description 根据设置的首页path,修正routes中的affix标记(固定首页)
  124. */
  125. const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
  126. if (!routes || routes.length === 0)
  127. return
  128. let homePath: string = PageEnum.BASE_HOME
  129. function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
  130. if (parentPath)
  131. parentPath = `${parentPath}/`
  132. routes.forEach((route: AppRouteRecordRaw) => {
  133. const { path, children, redirect } = route
  134. const currentPath = path.startsWith('/') ? path : parentPath + path
  135. if (currentPath === homePath) {
  136. if (redirect) {
  137. homePath = route.redirect! as string
  138. }
  139. else {
  140. route.meta = Object.assign({}, route.meta, { affix: true })
  141. throw new Error('end')
  142. }
  143. }
  144. children && children.length > 0 && patcher(children, currentPath)
  145. })
  146. }
  147. try {
  148. patcher(routes)
  149. }
  150. catch (e) {
  151. // 已处理完毕跳出循环
  152. }
  153. }
  154. switch (permissionMode) {
  155. // 角色权限
  156. case PermissionModeEnum.ROLE:
  157. // 对非一级路由进行过滤
  158. routes = filter(asyncRoutes, routeFilter)
  159. // 对一级路由根据角色权限过滤
  160. routes = routes.filter(routeFilter)
  161. // Convert multi-level routing to level 2 routing
  162. // 将多级路由转换为 2 级路由
  163. routes = flatMultiLevelRoutes(routes)
  164. break
  165. // 路由映射, 默认进入该case
  166. case PermissionModeEnum.ROUTE_MAPPING:
  167. // 对非一级路由进行过滤
  168. routes = filter(asyncRoutes, routeFilter)
  169. // 对一级路由再次根据角色权限过滤
  170. routes = routes.filter(routeFilter)
  171. // 将路由转换成菜单
  172. const menuList = transformRouteToMenu(routes, true)
  173. // 移除掉 ignoreRoute: true 的路由 非一级路由
  174. routes = filter(routes, routeRemoveIgnoreFilter)
  175. // 移除掉 ignoreRoute: true 的路由 一级路由;
  176. routes = routes.filter(routeRemoveIgnoreFilter)
  177. // 对菜单进行排序
  178. menuList.sort((a, b) => {
  179. return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0)
  180. })
  181. // 设置菜单列表
  182. this.setFrontMenuList(menuList)
  183. // Convert multi-level routing to level 2 routing
  184. // 将多级路由转换为 2 级路由
  185. routes = flatMultiLevelRoutes(routes)
  186. break
  187. // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
  188. // 如果确定不需要做后台动态权限,请在下方注释整个判断
  189. case PermissionModeEnum.BACK:
  190. const { createMessage } = useMessage()
  191. createMessage.loading({ content: t('sys.app.menuLoading'), duration: 1 })
  192. // !Simulate to obtain permission codes from the background,
  193. // 模拟从后台获取权限码,
  194. // this function may only need to be executed once, and the actual project can be put at the right time by itself
  195. // 这个功能可能只需要执行一次,实际项目可以自己放在合适的时间
  196. let routeList: AppRouteRecordRaw[] = []
  197. try {
  198. routeList = userInfo.menus as AppRouteRecordRaw[]
  199. }
  200. catch (error) {
  201. console.error(error)
  202. }
  203. // Dynamically introduce components
  204. // 动态引入组件
  205. routeList = transformObjToRoute(routeList)
  206. // Background routing to menu structure
  207. // 后台路由到菜单结构
  208. const backMenuList = transformRouteToMenu([dashboard, ...routeList, about])
  209. this.setBackMenuList(backMenuList)
  210. // remove meta.ignoreRoute item
  211. // 删除 meta.ignoreRoute 项
  212. routeList = filter(routeList, routeRemoveIgnoreFilter)
  213. routeList = routeList.filter(routeRemoveIgnoreFilter)
  214. routeList = flatMultiLevelRoutes(routeList)
  215. routes = [PAGE_NOT_FOUND_ROUTE, dashboard, ...routeList, about]
  216. break
  217. }
  218. // 从用户中获取权限
  219. if (userInfo)
  220. this.setPermCodeList(userInfo.permissions)
  221. patchHomeAffix(routes)
  222. return routes
  223. },
  224. },
  225. })
  226. // Need to be used outside the setup
  227. // 需要在设置之外使用
  228. export function usePermissionStoreWithOut() {
  229. return usePermissionStore(store)
  230. }