utils.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import {
  2. RouterHistory,
  3. RouteRecordRaw,
  4. RouteComponent,
  5. createWebHistory,
  6. createWebHashHistory,
  7. RouteRecordNormalized
  8. } from "vue-router";
  9. import { router } from "./index";
  10. import { loadEnv } from "../../build";
  11. import Layout from "/@/layout/index.vue";
  12. import { useTimeoutFn } from "@vueuse/core";
  13. import { RouteConfigs } from "/@/layout/types";
  14. import { usePermissionStoreHook } from "/@/store/modules/permission";
  15. // https://cn.vitejs.dev/guide/features.html#glob-import
  16. const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
  17. // 动态路由
  18. import { getAsyncRoutes } from "/@/api/routes";
  19. // 按照路由中meta下的rank等级升序来排序路由
  20. const ascending = (arr: any[]) => {
  21. return arr.sort(
  22. (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
  23. return a?.meta?.rank - b?.meta?.rank;
  24. }
  25. );
  26. };
  27. // 过滤meta中showLink为false的路由
  28. const filterTree = (data: RouteComponent[]) => {
  29. const newTree = data.filter(
  30. (v: { meta: { showLink: boolean } }) => v.meta.showLink
  31. );
  32. newTree.forEach(
  33. (v: { children }) => v.children && (v.children = filterTree(v.children))
  34. );
  35. return newTree;
  36. };
  37. // 批量删除缓存路由(keepalive)
  38. const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => {
  39. delAliveRouteList.forEach(route => {
  40. usePermissionStoreHook().cacheOperate({
  41. mode: "delete",
  42. name: route?.name
  43. });
  44. });
  45. };
  46. // 通过path获取父级路径
  47. const getParentPaths = (path: string, routes: RouteRecordRaw[]) => {
  48. // 深度遍历查找
  49. function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
  50. for (let i = 0; i < routes.length; i++) {
  51. const item = routes[i];
  52. // 找到path则返回父级path
  53. if (item.path === path) return parents;
  54. // children不存在或为空则不递归
  55. if (!item.children || !item.children.length) continue;
  56. // 往下查找时将当前path入栈
  57. parents.push(item.path);
  58. if (dfs(item.children, path, parents).length) return parents;
  59. // 深度遍历查找未找到时当前path 出栈
  60. parents.pop();
  61. }
  62. // 未找到时返回空数组
  63. return [];
  64. }
  65. return dfs(routes, path, []);
  66. };
  67. // 查找对应path的路由信息
  68. const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => {
  69. let res = routes.find((item: { path: string }) => item.path == path);
  70. if (res) {
  71. return res;
  72. } else {
  73. for (let i = 0; i < routes.length; i++) {
  74. if (
  75. routes[i].children instanceof Array &&
  76. routes[i].children.length > 0
  77. ) {
  78. res = findRouteByPath(path, routes[i].children);
  79. if (res) {
  80. return res;
  81. }
  82. }
  83. }
  84. return null;
  85. }
  86. };
  87. // 重置路由
  88. const resetRouter = (): void => {
  89. router.getRoutes().forEach(route => {
  90. const { name } = route;
  91. if (name) {
  92. router.hasRoute(name) && router.removeRoute(name);
  93. }
  94. });
  95. };
  96. // 初始化路由
  97. const initRouter = (name: string) => {
  98. return new Promise(resolve => {
  99. getAsyncRoutes({ name }).then(({ info }) => {
  100. if (info.length === 0) {
  101. usePermissionStoreHook().changeSetting(info);
  102. } else {
  103. formatFlatteningRoutes(addAsyncRoutes(info)).map(
  104. (v: RouteRecordRaw) => {
  105. // 防止重复添加路由
  106. if (
  107. router.options.routes[0].children.findIndex(
  108. value => value.path === v.path
  109. ) !== -1
  110. ) {
  111. return;
  112. } else {
  113. // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
  114. router.options.routes[0].children.push(v);
  115. // 最终路由进行升序
  116. ascending(router.options.routes[0].children);
  117. if (!router.hasRoute(v?.name)) router.addRoute(v);
  118. }
  119. resolve(router);
  120. }
  121. );
  122. usePermissionStoreHook().changeSetting(info);
  123. }
  124. router.addRoute({
  125. path: "/:pathMatch(.*)",
  126. redirect: "/error/404"
  127. });
  128. });
  129. });
  130. };
  131. /**
  132. * 将多级嵌套路由处理成一维数组
  133. * @param routesList 传入路由
  134. * @returns 返回处理后的一维路由
  135. */
  136. const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
  137. if (routesList.length <= 0) return routesList;
  138. for (let i = 0; i < routesList.length; i++) {
  139. if (routesList[i].children) {
  140. routesList = routesList
  141. .slice(0, i + 1)
  142. .concat(routesList[i].children, routesList.slice(i + 1));
  143. }
  144. }
  145. return routesList;
  146. };
  147. /**
  148. * 一维数组处理成多级嵌套数组(三级及以上的路由全部拍成二级,keep-alive 只支持到二级缓存)
  149. * https://github.com/xiaoxian521/vue-pure-admin/issues/67
  150. * @param routesList 处理后的一维路由菜单数组
  151. * @returns 返回将一维数组重新处理成规定路由的格式
  152. */
  153. const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => {
  154. if (routesList.length <= 0) return routesList;
  155. const newRoutesList: RouteRecordRaw[] = [];
  156. routesList.forEach((v: RouteRecordRaw) => {
  157. if (v.path === "/") {
  158. newRoutesList.push({
  159. component: v.component,
  160. name: v.name,
  161. path: v.path,
  162. redirect: v.redirect,
  163. meta: v.meta,
  164. children: []
  165. });
  166. } else {
  167. newRoutesList[0].children.push({ ...v });
  168. }
  169. });
  170. return newRoutesList;
  171. };
  172. // 处理缓存路由(添加、删除、刷新)
  173. const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
  174. switch (mode) {
  175. case "add":
  176. matched.forEach(v => {
  177. usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
  178. });
  179. break;
  180. case "delete":
  181. usePermissionStoreHook().cacheOperate({
  182. mode: "delete",
  183. name: matched[matched.length - 1].name
  184. });
  185. break;
  186. default:
  187. usePermissionStoreHook().cacheOperate({
  188. mode: "delete",
  189. name: matched[matched.length - 1].name
  190. });
  191. useTimeoutFn(() => {
  192. matched.forEach(v => {
  193. usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
  194. });
  195. }, 100);
  196. }
  197. };
  198. // 过滤后端传来的动态路由 重新生成规范路由
  199. const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
  200. if (!arrRoutes || !arrRoutes.length) return;
  201. const modulesRoutesKeys = Object.keys(modulesRoutes);
  202. arrRoutes.forEach((v: RouteRecordRaw) => {
  203. if (v.redirect) {
  204. v.component = Layout;
  205. } else {
  206. const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
  207. v.component = modulesRoutes[modulesRoutesKeys[index]];
  208. }
  209. if (v.children) {
  210. addAsyncRoutes(v.children);
  211. }
  212. });
  213. return arrRoutes;
  214. };
  215. // 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html
  216. const getHistoryMode = (): RouterHistory => {
  217. const routerHistory = loadEnv().VITE_ROUTER_HISTORY;
  218. // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
  219. const historyMode = routerHistory.split(",");
  220. const leftMode = historyMode[0];
  221. const rightMode = historyMode[1];
  222. // no param
  223. if (historyMode.length === 1) {
  224. if (leftMode === "hash") {
  225. return createWebHashHistory("");
  226. } else if (leftMode === "h5") {
  227. return createWebHistory("");
  228. }
  229. } //has param
  230. else if (historyMode.length === 2) {
  231. if (leftMode === "hash") {
  232. return createWebHashHistory(rightMode);
  233. } else if (leftMode === "h5") {
  234. return createWebHistory(rightMode);
  235. }
  236. }
  237. };
  238. // 是否有权限
  239. const hasPermissions = (value: Array<string>): boolean => {
  240. if (value && value instanceof Array && value.length > 0) {
  241. const roles = usePermissionStoreHook().buttonAuth;
  242. const permissionRoles = value;
  243. const hasPermission = roles.some(role => {
  244. return permissionRoles.includes(role);
  245. });
  246. if (!hasPermission) {
  247. return false;
  248. }
  249. return true;
  250. } else {
  251. return false;
  252. }
  253. };
  254. export {
  255. ascending,
  256. filterTree,
  257. initRouter,
  258. resetRouter,
  259. hasPermissions,
  260. getHistoryMode,
  261. addAsyncRoutes,
  262. delAliveRoutes,
  263. getParentPaths,
  264. findRouteByPath,
  265. handleAliveRoute,
  266. formatTwoStageRoutes,
  267. formatFlatteningRoutes
  268. };