index.ts 6.4 KB

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