import type { RouteKey, RoutePath } from '@elegant-router/types' import type { LocationQueryRaw, NavigationGuardNext, RouteLocationNormalized, RouteLocationRaw, Router, } from 'vue-router' import { getRouteName } from '@/router/elegant/transform' import { useAuthStore } from '@/store/modules/auth' import { useRouteStore } from '@/store/modules/route' import { localStg } from '@/utils/storage' /** * 创建路由守卫 * * @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) { next({ name: rootRoute }) return } // 如果路由不需要登录,则直接允许访问 if (!needLogin) { handleRouteSwitch(to, from, next) return } // 如果路由需要登录但用户未登录,则跳转到登录页 const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y' const whiteList: string[] = ['user'] const isInWhiteList = whiteList.includes(to.name as string) if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) { next({ name: loginRoute, query: { redirect: to.fullPath } }) return } // 如果用户已登录但没有权限,则跳转到 403 页面 if (!hasAuth) { next({ name: noAuthorizationRoute }) 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() // 由于常量路由未初始化,路由被“未找到”路由捕获 // 常量路由初始化后,重定向到原始路由 const path = to.fullPath const location: RouteLocationRaw = { path, replace: true, query: to.query, hash: to.hash, } return location } const isLogin = Boolean(localStg.get('token')) const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y' const whiteList: string[] = ['user'] const isInWhiteList = whiteList.includes(to.name as string) if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) { // 如果用户未登录且路由是常量路由但不是“未找到”路由,则允许访问。 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() // 由于权限路由未初始化,路由被“未找到”路由捕获 // 权限路由初始化后,重定向到原始路由 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() // 权限路由已初始化 // 如果不是“未找到”路由,则允许访问 if (!isNotFoundRoute) { return null } // 如果被“未找到”路由捕获,则检查路由是否存在 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) { // 外链路由 if (to.meta.href) { window.open(to.meta.href, '_blank') next({ path: from.fullPath, replace: true, query: from.query, hash: to.hash }) return } next() } 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 }