permission.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import {defineStore} from 'pinia';
  2. import router, {constantRoutes, dynamicRoutes} from '@/router';
  3. import store from '@/store';
  4. import {getRouters} from '@/api/menu';
  5. import auth from '@/plugins/auth';
  6. import {RouteRecordRaw} from 'vue-router';
  7. import Layout from '@/layout/index.vue';
  8. import ParentView from '@/components/ParentView/index.vue';
  9. import InnerLink from '@/layout/components/InnerLink/index.vue';
  10. import {ref} from 'vue';
  11. import {createCustomNameComponent} from '@/utils/createCustomNameComponent';
  12. // 匹配views里面所有的.vue文件
  13. const modules = import.meta.glob('./../../views/**/*.vue');
  14. export const usePermissionStore = defineStore('permission', () => {
  15. const routes = ref<RouteRecordRaw[]>([]);
  16. const addRoutes = ref<RouteRecordRaw[]>([]);
  17. const defaultRoutes = ref<RouteRecordRaw[]>([]);
  18. const topbarRouters = ref<RouteRecordRaw[]>([]);
  19. const sidebarRouters = ref<RouteRecordRaw[]>([]);
  20. const getRoutes = (): RouteRecordRaw[] => {
  21. return routes.value as RouteRecordRaw[];
  22. };
  23. const getDefaultRoutes = (): RouteRecordRaw[] => {
  24. return defaultRoutes.value as RouteRecordRaw[];
  25. };
  26. const getSidebarRoutes = (): RouteRecordRaw[] => {
  27. return sidebarRouters.value as RouteRecordRaw[];
  28. };
  29. const getTopbarRoutes = (): RouteRecordRaw[] => {
  30. return topbarRouters.value as RouteRecordRaw[];
  31. };
  32. const setRoutes = (newRoutes: RouteRecordRaw[]): void => {
  33. addRoutes.value = newRoutes;
  34. routes.value = constantRoutes.concat(newRoutes);
  35. };
  36. const setDefaultRoutes = (routes: RouteRecordRaw[]): void => {
  37. defaultRoutes.value = constantRoutes.concat(routes);
  38. };
  39. const setTopbarRoutes = (routes: RouteRecordRaw[]): void => {
  40. topbarRouters.value = routes;
  41. };
  42. const setSidebarRouters = (routes: RouteRecordRaw[]): void => {
  43. sidebarRouters.value = routes;
  44. };
  45. const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
  46. const res = await getRouters();
  47. const {data} = res;
  48. const sdata = JSON.parse(JSON.stringify(data));
  49. const rdata = JSON.parse(JSON.stringify(data));
  50. const defaultData = JSON.parse(JSON.stringify(data));
  51. const sidebarRoutes = filterAsyncRouter(sdata);
  52. const rewriteRoutes = filterAsyncRouter(rdata, undefined, true);
  53. const defaultRoutes = filterAsyncRouter(defaultData);
  54. const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
  55. asyncRoutes.forEach((route) => {
  56. router.addRoute(route);
  57. });
  58. setRoutes(rewriteRoutes);
  59. setSidebarRouters(constantRoutes.concat(sidebarRoutes));
  60. setDefaultRoutes(sidebarRoutes);
  61. setTopbarRoutes(defaultRoutes);
  62. // 路由name重复检查
  63. duplicateRouteChecker(asyncRoutes, sidebarRoutes);
  64. return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
  65. };
  66. /**
  67. * 遍历后台传来的路由字符串,转换为组件对象
  68. * @param asyncRouterMap 后台传来的路由字符串
  69. * @param lastRouter 上一级路由
  70. * @param type 是否是重写路由
  71. */
  72. const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
  73. return asyncRouterMap.filter((route) => {
  74. if (type && route.children) {
  75. route.children = filterChildren(route.children, undefined);
  76. }
  77. // Layout ParentView 组件特殊处理
  78. if (route.component?.toString() === 'Layout') {
  79. route.component = Layout;
  80. } else if (route.component?.toString() === 'ParentView') {
  81. route.component = ParentView;
  82. } else if (route.component?.toString() === 'InnerLink') {
  83. route.component = InnerLink;
  84. } else {
  85. route.component = loadView(route.component, route.name as string);
  86. }
  87. if (route.children != null && route.children && route.children.length) {
  88. route.children = filterAsyncRouter(route.children, route, type);
  89. } else {
  90. delete route.children;
  91. delete route.redirect;
  92. }
  93. return true;
  94. });
  95. };
  96. const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
  97. let children: RouteRecordRaw[] = [];
  98. childrenMap.forEach((el) => {
  99. el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path;
  100. if (el.children && el.children.length && el.component?.toString() === 'ParentView') {
  101. children = children.concat(filterChildren(el.children, el));
  102. } else {
  103. children.push(el);
  104. }
  105. });
  106. return children;
  107. };
  108. return {
  109. routes,
  110. topbarRouters,
  111. sidebarRouters,
  112. defaultRoutes,
  113. getRoutes,
  114. getDefaultRoutes,
  115. getSidebarRoutes,
  116. getTopbarRoutes,
  117. setRoutes,
  118. generateRoutes,
  119. setSidebarRouters
  120. };
  121. });
  122. // 动态路由遍历,验证是否具备权限
  123. export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
  124. const res: RouteRecordRaw[] = [];
  125. routes.forEach((route) => {
  126. if (route.permissions) {
  127. if (auth.hasPermiOr(route.permissions)) {
  128. res.push(route);
  129. }
  130. } else if (route.roles) {
  131. if (auth.hasRoleOr(route.roles)) {
  132. res.push(route);
  133. }
  134. }
  135. });
  136. return res;
  137. };
  138. export const loadView = (view: any, name: string) => {
  139. let res;
  140. for (const path in modules) {
  141. const viewsIndex = path.indexOf('/views/');
  142. let dir = path.substring(viewsIndex + 7);
  143. dir = dir.substring(0, dir.lastIndexOf('.vue'));
  144. if (dir === view) {
  145. res = createCustomNameComponent(modules[path], {name});
  146. return res;
  147. }
  148. }
  149. return res;
  150. };
  151. // 非setup
  152. export const usePermissionStoreHook = () => {
  153. return usePermissionStore(store);
  154. };
  155. interface Route {
  156. name?: string | symbol;
  157. path: string;
  158. children?: Route[];
  159. }
  160. /**
  161. * 检查路由name是否重复
  162. * @param localRoutes 本地路由
  163. * @param routes 动态路由
  164. */
  165. function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) {
  166. // 展平
  167. function flatRoutes(routes: Route[]) {
  168. const res: Route[] = [];
  169. routes.forEach((route) => {
  170. if (route.children) {
  171. res.push(...flatRoutes(route.children));
  172. } else {
  173. res.push(route);
  174. }
  175. });
  176. return res;
  177. }
  178. const allRoutes = flatRoutes([...localRoutes, ...routes]);
  179. const nameList: string[] = [];
  180. allRoutes.forEach((route) => {
  181. const name = route.name.toString();
  182. if (name && nameList.includes(name)) {
  183. const message = `路由名称: [${name}] 重复, 会造成 404`;
  184. console.error(message);
  185. ElNotification({
  186. title: '路由名称重复',
  187. message,
  188. type: 'error'
  189. });
  190. return;
  191. }
  192. nameList.push(route.name.toString());
  193. });
  194. }