index.ts 6.7 KB

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