import type { LocationQueryRaw, NavigationGuardNext, RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router'; import type { RouteKey, RoutePath } from '@elegant-router/types'; import { useAuthStore } from '@/store/modules/auth'; import { useObjectContextStore } from '@/store/modules/object-context'; import { useRouteStore } from '@/store/modules/route'; import { localStg } from '@/utils/storage'; import { getRouteName } from '@/router/elegant/transform'; /** * 创建全局路由守卫 * * @param router 路由实例 */ export function createRouteGuard(router: Router) { router.beforeEach(async (to, from, next) => { const location = await initRoute(to); if (location) { next(location); return; } const authStore = useAuthStore(); const rootRoute: RouteKey = 'root'; const loginRoute: RouteKey = 'login'; const noAuthorizationRoute: RouteKey = '403'; const isLogin = Boolean(localStg.get('token')); const needLogin = !to.meta.constant; const routeRoles = to.meta.roles || []; const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role)); const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole; // 已登录状态下访问登录页时,直接跳到首页 if (to.name === loginRoute && isLogin) { const redirect = to.query?.redirect as string; if (redirect) { next({ path: redirect }); } else { next({ name: rootRoute }); } return; } // 不需要登录的路由允许直接访问 if (!needLogin) { const objectContextLocation = await handleObjectContextSwitch(to); if (objectContextLocation) { next(objectContextLocation); return; } handleRouteSwitch(to, from, next); return; } // 需要登录但当前未登录时,跳转到登录页 if (!isLogin) { next({ name: loginRoute, query: { redirect: to.fullPath } }); return; } // 已登录但没有权限时,跳转到 403 页面 if (!hasAuth) { next({ name: noAuthorizationRoute }); return; } // 正常放行 const objectContextLocation = await handleObjectContextSwitch(to); if (objectContextLocation) { next(objectContextLocation); return; } handleRouteSwitch(to, from, next); }); } /** * 初始化路由 * * @param to 目标路由 */ async function initRoute(to: RouteLocationNormalized): Promise { const routeStore = useRouteStore(); const notFoundRoute: RouteKey = 'not-found'; const isNotFoundRoute = to.name === notFoundRoute; // 常量路由还没初始化时,先初始化常量路由 if (!routeStore.isInitConstantRoute) { await routeStore.initConstantRoute(); // 初始化前可能先被 not-found 捕获,初始化完成后重新回到原目标路由 const path = to.fullPath; const location: RouteLocationRaw = { path, replace: true, query: to.query, hash: to.hash }; return location; } const isLogin = Boolean(localStg.get('token')); if (!isLogin) { // 未登录时,如果访问的是常量路由且不是 not-found,则允许访问 if (to.meta.constant && !isNotFoundRoute) { routeStore.onRouteSwitchWhenNotLoggedIn(); return null; } // 未登录时跳到登录页 const loginRoute: RouteKey = 'login'; const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome); const location: RouteLocationRaw = { name: loginRoute, query }; return location; } if (!routeStore.isInitAuthRoute) { // 初始化权限路由 await routeStore.initAuthRoute(); // 初始化前可能先被 not-found 捕获,初始化完成后重新回到原目标路由 if (isNotFoundRoute) { const rootRoute: RouteKey = 'root'; const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath; const location: RouteLocationRaw = { path, replace: true, query: to.query, hash: to.hash }; return location; } } routeStore.onRouteSwitchWhenLoggedIn(); // 权限路由已初始化,且当前不是 not-found,直接放行 if (!isNotFoundRoute) { return null; } // 如果被 not-found 捕获,再判断是否其实是“存在但无权限”的路由 const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath); const noPermissionRoute: RouteKey = '403'; if (exist) { const location: RouteLocationRaw = { name: noPermissionRoute }; return location; } return null; } function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) { // 带 href 的路由直接新开窗口 if (to.meta.href) { window.open(to.meta.href, '_blank'); next({ path: from.fullPath, replace: true, query: from.query, hash: from.hash }); return; } next(); } async function handleObjectContextSwitch(to: RouteLocationNormalized) { const objectContextStore = useObjectContextStore(); return objectContextStore.ensureContextByRoute(to); } function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) { const loginRoute: RouteKey = 'login'; const redirect = to.fullPath; const [redirectPath, redirectQuery] = redirect.split('?'); const redirectName = getRouteName(redirectPath as RoutePath); const isRedirectHome = routeHome === redirectName; const query: LocationQueryRaw = to.name !== loginRoute && !isRedirectHome ? { redirect } : {}; if (isRedirectHome && redirectQuery) { query.redirect = `/?${redirectQuery}`; } return query; }