index.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import {
  2. Router,
  3. createRouter,
  4. RouteComponent,
  5. createWebHashHistory,
  6. RouteRecordNormalized
  7. } from "vue-router";
  8. import { RouteConfigs } from "/@/layout/types";
  9. import { split, uniqBy } from "lodash-es";
  10. import { openLink } from "/@/utils/link";
  11. import NProgress from "/@/utils/progress";
  12. import { useTimeoutFn } from "@vueuse/core";
  13. import { storageSession, storageLocal } from "/@/utils/storage";
  14. import { usePermissionStoreHook } from "/@/store/modules/permission";
  15. import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
  16. // 静态路由
  17. import tabsRouter from "./modules/tabs";
  18. import homeRouter from "./modules/home";
  19. import Layout from "/@/layout/index.vue";
  20. import errorRouter from "./modules/error";
  21. import editorRouter from "./modules/editor";
  22. import nestedRouter from "./modules/nested";
  23. import externalLink from "./modules/externalLink";
  24. import remainingRouter from "./modules/remaining";
  25. import flowChartRouter from "./modules/flowchart";
  26. import componentsRouter from "./modules/components";
  27. // 动态路由
  28. import { getAsyncRoutes } from "/@/api/routes";
  29. import { transformI18n } from "../utils/i18n";
  30. // https://cn.vitejs.dev/guide/features.html#glob-import
  31. const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
  32. const constantRoutes: Array<RouteComponent> = [
  33. tabsRouter,
  34. homeRouter,
  35. flowChartRouter,
  36. editorRouter,
  37. componentsRouter,
  38. nestedRouter,
  39. externalLink,
  40. errorRouter
  41. ];
  42. // 按照路由中meta下的rank等级升序来排序路由
  43. export const ascending = arr => {
  44. return arr.sort((a: any, b: any) => {
  45. return a?.meta?.rank - b?.meta?.rank;
  46. });
  47. };
  48. // 将所有静态路由导出
  49. export const constantRoutesArr: Array<RouteComponent> = ascending(
  50. constantRoutes
  51. ).concat(...remainingRouter);
  52. // 过滤meta中showLink为false的路由
  53. export const filterTree = data => {
  54. const newTree = data.filter(v => v.meta.showLink);
  55. newTree.forEach(v => v.children && (v.children = filterTree(v.children)));
  56. return newTree;
  57. };
  58. // 从路由中提取keepAlive为true的name组成数组(此处本项目中并没有用到,只是暴露个方法)
  59. export const getAliveRoute = () => {
  60. const alivePageList = [];
  61. const recursiveSearch = treeLists => {
  62. if (!treeLists || !treeLists.length) {
  63. return;
  64. }
  65. for (let i = 0; i < treeLists.length; i++) {
  66. if (treeLists[i]?.meta?.keepAlive) alivePageList.push(treeLists[i].name);
  67. recursiveSearch(treeLists[i].children);
  68. }
  69. };
  70. recursiveSearch(router.options.routes);
  71. return alivePageList;
  72. };
  73. // 批量删除缓存路由
  74. export const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => {
  75. delAliveRouteList.forEach(route => {
  76. usePermissionStoreHook().cacheOperate({
  77. mode: "delete",
  78. name: route?.name
  79. });
  80. });
  81. };
  82. // 处理缓存路由(添加、删除、刷新)
  83. export const handleAliveRoute = (
  84. matched: RouteRecordNormalized[],
  85. mode?: string
  86. ) => {
  87. switch (mode) {
  88. case "add":
  89. matched.forEach(v => {
  90. usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
  91. });
  92. break;
  93. case "delete":
  94. usePermissionStoreHook().cacheOperate({
  95. mode: "delete",
  96. name: matched[matched.length - 1].name
  97. });
  98. break;
  99. default:
  100. usePermissionStoreHook().cacheOperate({
  101. mode: "delete",
  102. name: matched[matched.length - 1].name
  103. });
  104. useTimeoutFn(() => {
  105. matched.forEach(v => {
  106. usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
  107. });
  108. }, 100);
  109. }
  110. };
  111. // 过滤后端传来的动态路由 重新生成规范路由
  112. export const addAsyncRoutes = (arrRoutes: Array<RouteComponent>) => {
  113. if (!arrRoutes || !arrRoutes.length) return;
  114. arrRoutes.forEach((v: any) => {
  115. if (v.redirect) {
  116. v.component = Layout;
  117. } else {
  118. v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
  119. }
  120. if (v.children) {
  121. addAsyncRoutes(v.children);
  122. }
  123. });
  124. return arrRoutes;
  125. };
  126. // 创建路由实例
  127. export const router: Router = createRouter({
  128. history: createWebHashHistory(),
  129. routes: ascending(constantRoutes).concat(...remainingRouter),
  130. scrollBehavior(to, from, savedPosition) {
  131. return new Promise(resolve => {
  132. if (savedPosition) {
  133. return savedPosition;
  134. } else {
  135. if (from.meta.saveSrollTop) {
  136. const top: number =
  137. document.documentElement.scrollTop || document.body.scrollTop;
  138. resolve({ left: 0, top });
  139. }
  140. }
  141. });
  142. }
  143. });
  144. // 初始化路由
  145. export const initRouter = name => {
  146. return new Promise(resolve => {
  147. getAsyncRoutes({ name }).then(({ info }) => {
  148. if (info.length === 0) {
  149. usePermissionStoreHook().changeSetting(info);
  150. } else {
  151. addAsyncRoutes(info).map((v: any) => {
  152. // 防止重复添加路由
  153. if (
  154. router.options.routes.findIndex(value => value.path === v.path) !==
  155. -1
  156. ) {
  157. return;
  158. } else {
  159. // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
  160. router.options.routes.push(v);
  161. // 最终路由进行升序
  162. ascending(router.options.routes);
  163. router.addRoute(v.name, v);
  164. usePermissionStoreHook().changeSetting(info);
  165. }
  166. resolve(router);
  167. });
  168. }
  169. router.addRoute({
  170. path: "/:pathMatch(.*)",
  171. redirect: "/error/404"
  172. });
  173. });
  174. });
  175. };
  176. // 重置路由
  177. export function resetRouter() {
  178. router.getRoutes().forEach(route => {
  179. const { name } = route;
  180. if (name) {
  181. router.hasRoute(name) && router.removeRoute(name);
  182. }
  183. });
  184. }
  185. // 路由白名单
  186. const whiteList = ["/login"];
  187. router.beforeEach((to, _from, next) => {
  188. if (to.meta?.keepAlive) {
  189. const newMatched = to.matched;
  190. handleAliveRoute(newMatched, "add");
  191. // 页面整体刷新和点击标签页刷新
  192. if (_from.name === undefined || _from.name === "redirect") {
  193. handleAliveRoute(newMatched);
  194. }
  195. }
  196. const name = storageSession.getItem("info");
  197. NProgress.start();
  198. const externalLink = to?.redirectedFrom?.fullPath;
  199. if (!externalLink)
  200. to.matched.some(item => {
  201. item.meta.title
  202. ? (document.title = transformI18n(
  203. item.meta.title as string,
  204. item.meta?.i18n as boolean
  205. ))
  206. : "";
  207. });
  208. if (name) {
  209. if (_from?.name) {
  210. // 如果路由包含http 则是超链接 反之是普通路由
  211. if (externalLink && externalLink.includes("http")) {
  212. openLink(`http${split(externalLink, "http")[1]}`);
  213. NProgress.done();
  214. } else {
  215. next();
  216. }
  217. } else {
  218. // 刷新
  219. if (usePermissionStoreHook().wholeRoutes.length === 0)
  220. initRouter(name.username).then((router: Router) => {
  221. if (!useMultiTagsStoreHook().getMultiTagsCache) {
  222. return router.push("/");
  223. }
  224. router.push(to.path);
  225. // 刷新页面更新标签栏与页面路由匹配
  226. const localRoutes = storageLocal.getItem("responsive-tags");
  227. const optionsRoutes = router.options?.routes;
  228. const newLocalRoutes = [];
  229. optionsRoutes.forEach(ors => {
  230. localRoutes.forEach(lrs => {
  231. if (ors.path === lrs.parentPath) {
  232. newLocalRoutes.push(lrs);
  233. }
  234. });
  235. });
  236. storageLocal.setItem(
  237. "responsive-tags",
  238. uniqBy(newLocalRoutes, "path")
  239. );
  240. });
  241. next();
  242. }
  243. } else {
  244. if (to.path !== "/login") {
  245. if (whiteList.indexOf(to.path) !== -1) {
  246. next();
  247. } else {
  248. next({ path: "/login" });
  249. }
  250. } else {
  251. next();
  252. }
  253. }
  254. });
  255. router.afterEach(() => {
  256. NProgress.done();
  257. });
  258. export default router;