index.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // import "@/utils/sso";
  2. import Cookies from "js-cookie";
  3. import { getConfig } from "@/config";
  4. import NProgress from "@/utils/progress";
  5. import { buildHierarchyTree } from "@/utils/tree";
  6. import remainingRouter from "./modules/remaining";
  7. import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
  8. import { usePermissionStoreHook } from "@/store/modules/permission";
  9. import {
  10. isUrl,
  11. openLink,
  12. cloneDeep,
  13. isAllEmpty,
  14. storageLocal
  15. } from "@pureadmin/utils";
  16. import {
  17. ascending,
  18. getTopMenu,
  19. initRouter,
  20. isOneOfArray,
  21. getHistoryMode,
  22. findRouteByPath,
  23. handleAliveRoute,
  24. formatTwoStageRoutes,
  25. formatFlatteningRoutes
  26. } from "./utils";
  27. import {
  28. type Router,
  29. type RouteRecordRaw,
  30. type RouteComponent,
  31. createRouter
  32. } from "vue-router";
  33. import {
  34. type DataInfo,
  35. userKey,
  36. removeToken,
  37. multipleTabsKey
  38. } from "@/utils/auth";
  39. /** 自动导入全部静态路由,无需再手动引入!匹配 src/router/modules 目录(任何嵌套级别)中具有 .ts 扩展名的所有文件,除了 remaining.ts 文件
  40. * 如何匹配所有文件请看:https://github.com/mrmlnc/fast-glob#basic-syntax
  41. * 如何排除文件请看:https://cn.vitejs.dev/guide/features.html#negative-patterns
  42. */
  43. const modules: Record<string, any> = import.meta.glob(
  44. ["./modules/**/*.ts", "!./modules/**/remaining.ts"],
  45. {
  46. eager: true
  47. }
  48. );
  49. /** 原始静态路由(未做任何处理) */
  50. const routes = [];
  51. Object.keys(modules).forEach(key => {
  52. routes.push(modules[key].default);
  53. });
  54. /** 导出处理后的静态路由(三级及以上的路由全部拍成二级) */
  55. export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
  56. formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
  57. );
  58. /** 初始的静态路由,用于退出登录时重置路由 */
  59. const initConstantRoutes: Array<RouteRecordRaw> = cloneDeep(constantRoutes);
  60. /** 用于渲染菜单,保持原始层级 */
  61. export const constantMenus: Array<RouteComponent> = ascending(
  62. routes.flat(Infinity)
  63. ).concat(...remainingRouter);
  64. /** 不参与菜单的路由 */
  65. export const remainingPaths = Object.keys(remainingRouter).map(v => {
  66. return remainingRouter[v].path;
  67. });
  68. /** 创建路由实例 */
  69. export const router: Router = createRouter({
  70. history: getHistoryMode(import.meta.env.VITE_ROUTER_HISTORY),
  71. routes: constantRoutes.concat(...(remainingRouter as any)),
  72. strict: true,
  73. scrollBehavior(to, from, savedPosition) {
  74. return new Promise(resolve => {
  75. if (savedPosition) {
  76. return savedPosition;
  77. } else {
  78. if (from.meta.saveSrollTop) {
  79. const top: number =
  80. document.documentElement.scrollTop || document.body.scrollTop;
  81. resolve({ left: 0, top });
  82. }
  83. }
  84. });
  85. }
  86. });
  87. /** 重置路由 */
  88. export function resetRouter() {
  89. router.clearRoutes();
  90. for (const route of initConstantRoutes.concat(...(remainingRouter as any))) {
  91. router.addRoute(route);
  92. }
  93. router.options.routes = formatTwoStageRoutes(
  94. formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
  95. );
  96. usePermissionStoreHook().clearAllCachePage();
  97. }
  98. /** 路由白名单 */
  99. const whiteList = ["/login"];
  100. const { VITE_HIDE_HOME } = import.meta.env;
  101. router.beforeEach((to: ToRouteType, _from, next) => {
  102. if (to.meta?.keepAlive) {
  103. handleAliveRoute(to, "add");
  104. // 页面整体刷新和点击标签页刷新
  105. if (_from.name === undefined || _from.name === "Redirect") {
  106. handleAliveRoute(to);
  107. }
  108. }
  109. const userInfo = storageLocal().getItem<DataInfo<number>>(userKey);
  110. NProgress.start();
  111. const externalLink = isUrl(to?.name as string);
  112. if (!externalLink) {
  113. to.matched.some(item => {
  114. if (!item.meta.title) return "";
  115. const Title = getConfig().Title;
  116. if (Title) document.title = `${item.meta.title} | ${Title}`;
  117. else document.title = item.meta.title as string;
  118. });
  119. }
  120. /** 如果已经登录并存在登录信息后不能跳转到路由白名单,而是继续保持在当前页面 */
  121. function toCorrectRoute() {
  122. whiteList.includes(to.fullPath) ? next(_from.fullPath) : next();
  123. }
  124. if (Cookies.get(multipleTabsKey) && userInfo) {
  125. // 无权限跳转403页面
  126. if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
  127. next({ path: "/error/403" });
  128. }
  129. // 开启隐藏首页后在浏览器地址栏手动输入首页welcome路由则跳转到404页面
  130. if (VITE_HIDE_HOME === "true" && to.fullPath === "/welcome") {
  131. next({ path: "/error/404" });
  132. }
  133. if (_from?.name) {
  134. // name为超链接
  135. if (externalLink) {
  136. openLink(to?.name as string);
  137. NProgress.done();
  138. } else {
  139. toCorrectRoute();
  140. }
  141. } else {
  142. // 刷新
  143. if (
  144. usePermissionStoreHook().wholeMenus.length === 0 &&
  145. to.path !== "/login"
  146. ) {
  147. initRouter().then((router: Router) => {
  148. /*// 使用下面方法替换initRouter
  149. usePermissionStoreHook().handleWholeMenus([]);
  150. addPathMatch();*/
  151. if (!useMultiTagsStoreHook().getMultiTagsCache) {
  152. const { path } = to;
  153. const route = findRouteByPath(
  154. path,
  155. router.options.routes[0].children
  156. );
  157. getTopMenu(true);
  158. // query、params模式路由传参数的标签页不在此处处理
  159. if (route && route.meta?.title) {
  160. if (isAllEmpty(route.parentId) && route.meta?.backstage) {
  161. // 此处为动态顶级路由(目录)
  162. const { path, name, meta } = route.children[0];
  163. useMultiTagsStoreHook().handleTags("push", {
  164. path,
  165. name,
  166. meta
  167. });
  168. } else {
  169. const { path, name, meta } = route;
  170. useMultiTagsStoreHook().handleTags("push", {
  171. path,
  172. name,
  173. meta
  174. });
  175. }
  176. }
  177. }
  178. // 确保动态路由完全加入路由列表并且不影响静态路由(注意:动态路由刷新时router.beforeEach可能会触发两次,第一次触发动态路由还未完全添加,第二次动态路由才完全添加到路由列表,如果需要在router.beforeEach做一些判断可以在to.name存在的条件下去判断,这样就只会触发一次)
  179. if (isAllEmpty(to.name)) router.push(to.fullPath);
  180. });
  181. }
  182. toCorrectRoute();
  183. }
  184. } else {
  185. if (to.path !== "/login") {
  186. if (whiteList.indexOf(to.path) !== -1) {
  187. next();
  188. } else {
  189. removeToken();
  190. next({ path: "/login" });
  191. }
  192. } else {
  193. next();
  194. }
  195. }
  196. });
  197. router.afterEach(() => {
  198. NProgress.done();
  199. });
  200. export default router;