From 31b2a5ba82d7f89f6505692df445a7c6d9a5b4d1 Mon Sep 17 00:00:00 2001 From: pany <939630029@qq.com> Date: Tue, 26 Nov 2024 14:45:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=20router=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 1 - src/pinia/stores/permission.ts | 4 +- src/pinia/stores/user.ts | 4 +- src/{config/route.ts => router/config.ts} | 17 ++++++- src/router/guard.ts | 58 +++++++++++++++++++++++ src/router/helper.ts | 10 ++-- src/router/index.ts | 13 +++-- src/router/permission.ts | 57 ---------------------- 8 files changed, 89 insertions(+), 75 deletions(-) rename src/{config/route.ts => router/config.ts} (68%) create mode 100644 src/router/guard.ts delete mode 100644 src/router/permission.ts diff --git a/src/main.ts b/src/main.ts index 7aa1f57..575e352 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,6 @@ import App from "@/App.vue" import { pinia } from "@/pinia" import { router } from "@/router" import { createApp } from "vue" -import "@/router/permission" // load import { loadDirectives } from "@/directives" import { loadPlugins } from "@/plugins" diff --git a/src/pinia/stores/permission.ts b/src/pinia/stores/permission.ts index ac8e155..dead596 100644 --- a/src/pinia/stores/permission.ts +++ b/src/pinia/stores/permission.ts @@ -1,7 +1,7 @@ import type { RouteRecordRaw } from "vue-router" -import { routeSettings } from "@/config/route" import { pinia } from "@/pinia" import { constantRoutes, dynamicRoutes } from "@/router" +import { routerConfig } from "@/router/config" import { flatMultiLevelRoutes } from "@/router/helper" import { defineStore } from "pinia" import { ref } from "vue" @@ -46,7 +46,7 @@ export const usePermissionStore = defineStore("permission", () => { // 统一设置 const set = (accessedRoutes: RouteRecordRaw[]) => { routes.value = constantRoutes.concat(accessedRoutes) - addRoutes.value = routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(accessedRoutes) : accessedRoutes + addRoutes.value = routerConfig.thirdLevelRouteCache ? flatMultiLevelRoutes(accessedRoutes) : accessedRoutes } return { routes, addRoutes, setRoutes, setAllRoutes } diff --git a/src/pinia/stores/user.ts b/src/pinia/stores/user.ts index ea9aa28..9371fc5 100644 --- a/src/pinia/stores/user.ts +++ b/src/pinia/stores/user.ts @@ -1,8 +1,8 @@ import type { LoginRequestData } from "@/http/apis/login/type" -import { routeSettings } from "@/config/route" import { getUserInfoApi, loginApi } from "@/http/apis/login" import { pinia } from "@/pinia" import { resetRouter } from "@/router" +import { routerConfig } from "@/router/config" import { getToken, removeToken, setToken } from "@/utils/cache/cookies" import { defineStore } from "pinia" import { ref } from "vue" @@ -29,7 +29,7 @@ export const useUserStore = defineStore("user", () => { const { data } = await getUserInfoApi() username.value = data.username // 验证返回的 roles 是否为一个非空数组,否则塞入一个没有任何作用的默认角色,防止路由守卫逻辑进入无限循环 - roles.value = data.roles?.length > 0 ? data.roles : routeSettings.defaultRoles + roles.value = data.roles?.length > 0 ? data.roles : routerConfig.defaultRoles } // 模拟角色变化 diff --git a/src/config/route.ts b/src/router/config.ts similarity index 68% rename from src/config/route.ts rename to src/router/config.ts index 3b896cb..c87ee2f 100644 --- a/src/config/route.ts +++ b/src/router/config.ts @@ -1,5 +1,13 @@ +import type { RouterHistory } from "vue-router" +import { createWebHashHistory, createWebHistory } from "vue-router" + /** 路由配置 */ -interface RouteSettings { +interface RouterConfig { + /** + * @name 路由模式 + * @description hash 模式和 html5 模式 + */ + history: RouterHistory /** * @name 是否开启动态路由功能 * @description 1. 开启后需要后端配合,在查询用户详情接口返回当前用户可以用来判断并加载动态路由的字段(该项目用的是角色 roles 字段) @@ -21,7 +29,12 @@ interface RouteSettings { thirdLevelRouteCache: boolean } -export const routeSettings: RouteSettings = { +const VITE_ROUTER_HISTORY = import.meta.env.VITE_ROUTER_HISTORY + +const VITE_PUBLIC_PATH = import.meta.env.VITE_PUBLIC_PATH + +export const routerConfig: RouterConfig = { + history: VITE_ROUTER_HISTORY === "hash" ? createWebHashHistory(VITE_PUBLIC_PATH) : createWebHistory(VITE_PUBLIC_PATH), dynamic: true, defaultRoles: ["DEFAULT_ROLE"], thirdLevelRouteCache: false diff --git a/src/router/guard.ts b/src/router/guard.ts new file mode 100644 index 0000000..eacb0e5 --- /dev/null +++ b/src/router/guard.ts @@ -0,0 +1,58 @@ +import type { Router } from "vue-router" +import { setRouteChange } from "@/composables/useRouteListener" +import { useTitle } from "@/composables/useTitle" +import { isWhiteList } from "@/config/white-list" +import { usePermissionStore } from "@/pinia/stores/permission" +import { useUserStore } from "@/pinia/stores/user" +import { routerConfig } from "@/router/config" +import { getToken } from "@/utils/cache/cookies" +import { ElMessage } from "element-plus" +import NProgress from "nprogress" +import "nprogress/nprogress.css" + +NProgress.configure({ showSpinner: false }) +const { setTitle } = useTitle() + +export function registerNavigationGuard(router: Router) { + // 全局前置守卫 + router.beforeEach(async (to, _from, next) => { + NProgress.start() + const userStore = useUserStore() + const permissionStore = usePermissionStore() + // 如果没有登陆 + if (!getToken()) { + // 如果在免登录的白名单中,则直接进入 + if (isWhiteList(to)) return next() + // 其他没有访问权限的页面将被重定向到登录页面 + return next("/login") + } + // 如果已经登录,并准备进入 Login 页面,则重定向到主页 + if (to.path === "/login") return next({ path: "/" }) + // 如果用户已经获得其权限角色 + if (userStore.roles.length !== 0) return next() + // 否则要重新获取权限角色 + try { + await userStore.getInfo() + // 注意:角色必须是一个数组! 例如: ["admin"] 或 ["developer", "editor"] + const roles = userStore.roles + // 生成可访问的 Routes + routerConfig.dynamic ? permissionStore.setRoutes(roles) : permissionStore.setAllRoutes() + // 将 "有访问权限的动态路由" 添加到 Router 中 + permissionStore.addRoutes.forEach(route => router.addRoute(route)) + // 设置 replace: true, 因此导航将不会留下历史记录 + next({ ...to, replace: true }) + } catch (error) { + // 过程中发生任何错误,都直接重置 Token,并重定向到登录页面 + userStore.resetToken() + ElMessage.error((error as Error).message || "路由守卫发生错误") + next("/login") + } + }) + + // 全局后置钩子 + router.afterEach((to) => { + setRouteChange(to) + setTitle(to.meta.title) + NProgress.done() + }) +} diff --git a/src/router/helper.ts b/src/router/helper.ts index aa00d2d..383fd36 100644 --- a/src/router/helper.ts +++ b/src/router/helper.ts @@ -1,11 +1,7 @@ import type { Router, RouteRecordNormalized, RouteRecordRaw } from "vue-router" import { cloneDeep, omit } from "lodash-es" -import { createRouter, createWebHashHistory, createWebHistory } from "vue-router" - -/** 路由模式 */ -export const history = import.meta.env.VITE_ROUTER_HISTORY === "hash" - ? createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH) - : createWebHistory(import.meta.env.VITE_PUBLIC_PATH) +import { createRouter } from "vue-router" +import { routerConfig } from "./config" /** 路由降级(把三级及其以上的路由转化为二级路由) */ export function flatMultiLevelRoutes(routes: RouteRecordRaw[]) { @@ -29,7 +25,7 @@ function isMultipleRoute(route: RouteRecordRaw) { function promoteRouteLevel(route: RouteRecordRaw) { // 创建 router 实例是为了获取到当前传入的 route 的所有路由信息 let router: Router | null = createRouter({ - history, + history: routerConfig.history, routes: [route] }) const routes = router.getRoutes() diff --git a/src/router/index.ts b/src/router/index.ts index 20a2d56..2e39026 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,7 +1,8 @@ import type { RouteRecordRaw } from "vue-router" -import { routeSettings } from "@/config/route" +import { routerConfig } from "@/router/config" +import { registerNavigationGuard } from "@/router/guard" import { createRouter } from "vue-router" -import { flatMultiLevelRoutes, history } from "./helper" +import { flatMultiLevelRoutes } from "./helper" const Layouts = () => import("@/layouts/index.vue") @@ -290,9 +291,10 @@ export const dynamicRoutes: RouteRecordRaw[] = [ } ] +/** 路由实例 */ export const router = createRouter({ - history, - routes: routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(constantRoutes) : constantRoutes + history: routerConfig.history, + routes: routerConfig.thirdLevelRouteCache ? flatMultiLevelRoutes(constantRoutes) : constantRoutes }) /** 重置路由 */ @@ -310,3 +312,6 @@ export function resetRouter() { window.location.reload() } } + +// 注册路由导航守卫 +registerNavigationGuard(router) diff --git a/src/router/permission.ts b/src/router/permission.ts deleted file mode 100644 index d16ff4a..0000000 --- a/src/router/permission.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { setRouteChange } from "@/composables/useRouteListener" -import { useTitle } from "@/composables/useTitle" -import { routeSettings } from "@/config/route" -import { isWhiteList } from "@/config/white-list" -import { usePermissionStore } from "@/pinia/stores/permission" -import { useUserStore } from "@/pinia/stores/user" -import { router } from "@/router" -import { getToken } from "@/utils/cache/cookies" -import { ElMessage } from "element-plus" -import NProgress from "nprogress" -import "nprogress/nprogress.css" - -NProgress.configure({ showSpinner: false }) -const { setTitle } = useTitle() - -router.beforeEach(async (to, _from, next) => { - NProgress.start() - const userStore = useUserStore() - const permissionStore = usePermissionStore() - // 如果没有登陆 - if (!getToken()) { - // 如果在免登录的白名单中,则直接进入 - if (isWhiteList(to)) return next() - // 其他没有访问权限的页面将被重定向到登录页面 - return next("/login") - } - - // 如果已经登录,并准备进入 Login 页面,则重定向到主页 - if (to.path === "/login") return next({ path: "/" }) - - // 如果用户已经获得其权限角色 - if (userStore.roles.length !== 0) return next() - - // 否则要重新获取权限角色 - try { - await userStore.getInfo() - // 注意:角色必须是一个数组! 例如: ["admin"] 或 ["developer", "editor"] - const roles = userStore.roles - // 生成可访问的 Routes - routeSettings.dynamic ? permissionStore.setRoutes(roles) : permissionStore.setAllRoutes() - // 将 "有访问权限的动态路由" 添加到 Router 中 - permissionStore.addRoutes.forEach(route => router.addRoute(route)) - // 设置 replace: true, 因此导航将不会留下历史记录 - next({ ...to, replace: true }) - } catch (error) { - // 过程中发生任何错误,都直接重置 Token,并重定向到登录页面 - userStore.resetToken() - ElMessage.error((error as Error).message || "路由守卫发生错误") - next("/login") - } -}) - -router.afterEach((to) => { - setRouteChange(to) - setTitle(to.meta.title) - NProgress.done() -})