You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
7.4 KiB

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