From ad9ff59a4033f114ff847cdb70dc3153f338f7eb Mon Sep 17 00:00:00 2001 From: ClariS <1457715339@qq.com> Date: Mon, 7 Aug 2023 15:14:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=89=E7=BA=A7?= =?UTF-8?q?=E5=8F=8A=E5=85=B6=E4=BB=A5=E4=B8=8A=E8=B7=AF=E7=94=B1=E7=9A=84?= =?UTF-8?q?=20keep-alive=20=E7=BC=93=E5=AD=98=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/{async-route.ts => route.ts} | 21 ++++-- src/router/helper.ts | 69 +++++++++++++++++++ src/router/index.ts | 28 ++++---- src/router/permission.ts | 8 +-- src/store/modules/permission.ts | 7 +- src/store/modules/user.ts | 4 +- src/views/menu/menu1/index.vue | 2 +- src/views/menu/menu1/menu1-1/index.vue | 14 +++- src/views/menu/menu1/menu1-2/index.vue | 2 +- .../menu/menu1/menu1-2/menu1-2-1/index.vue | 14 +++- .../menu/menu1/menu1-2/menu1-2-2/index.vue | 14 +++- src/views/menu/menu1/menu1-3/index.vue | 14 +++- src/views/menu/menu2/index.vue | 14 +++- 13 files changed, 176 insertions(+), 35 deletions(-) rename src/config/{async-route.ts => route.ts} (53%) create mode 100644 src/router/helper.ts diff --git a/src/config/async-route.ts b/src/config/route.ts similarity index 53% rename from src/config/async-route.ts rename to src/config/route.ts index 7191026..3475024 100644 --- a/src/config/async-route.ts +++ b/src/config/route.ts @@ -1,21 +1,28 @@ /** 动态路由配置 */ -interface AsyncRouteSettings { +interface RouteSettings { /** * 是否开启动态路由功能? * 1. 开启后需要后端配合,在查询用户详情接口返回当前用户可以用来判断并加载动态路由的字段(该项目用的是角色 roles 字段) - * 2. 假如项目不需要根据不同的用户来显示不同的页面,则应该将 open: false + * 2. 假如项目不需要根据不同的用户来显示不同的页面,则应该将 async: false */ - open: boolean + async: boolean /** 当动态路由功能关闭时: * 1. 应该将所有路由都写到常驻路由里面(表明所有登陆的用户能访问的页面都是一样的) * 2. 系统自动给当前登录用户赋值一个没有任何作用的默认角色 */ defaultRoles: Array + /** + * 是否开启三级及其以上路由缓存功能? + * 1. 开启后会进行路由降级(把三级及其以上的路由转化为二级路由) + * 2. 由于都会转成二级路由,所以二级及其以上路由有内嵌子路由将会失效 + */ + thirdLevelRouteCache: boolean } -const asyncRouteSettings: AsyncRouteSettings = { - open: true, - defaultRoles: ["DEFAULT_ROLE"] +const routeSettings: RouteSettings = { + async: true, + defaultRoles: ["DEFAULT_ROLE"], + thirdLevelRouteCache: false } -export default asyncRouteSettings +export default routeSettings diff --git a/src/router/helper.ts b/src/router/helper.ts new file mode 100644 index 0000000..4b10648 --- /dev/null +++ b/src/router/helper.ts @@ -0,0 +1,69 @@ +import { + type Router, + type RouteRecordNormalized, + type RouteRecordRaw, + createRouter, + createWebHashHistory, + createWebHistory +} from "vue-router" +import { cloneDeep, omit } from "lodash-es" + +/** 路由模式 */ +export const history = + import.meta.env.VITE_ROUTER_HISTORY === "hash" + ? createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH) + : createWebHistory(import.meta.env.VITE_PUBLIC_PATH) + +/** 路由降级(把三级及其以上的路由转化为二级路由) */ +export const flatMultiLevelRoutes = (routes: RouteRecordRaw[]) => { + const routesMirror = cloneDeep(routes) + routesMirror.forEach((route) => { + // 如果路由是三级及其以上路由,对其进行降级处理 + isMultipleRoute(route) && promoteRouteLevel(route) + }) + return routesMirror +} + +/** 判断路由层级是否大于 2 */ +const isMultipleRoute = (route: RouteRecordRaw) => { + const children = route.children + if (children?.length) { + // 只要有一个子路由的 children 长度大于 0,就说明是三级及其以上路由 + return children.some((child) => child.children?.length) + } + return false +} + +/** 生成二级路由 */ +const promoteRouteLevel = (route: RouteRecordRaw) => { + // 创建 router 实例是为了获取到当前传入的 route 的所有路由信息 + let router: Router | null = createRouter({ + history, + routes: [route] + }) + const routes = router.getRoutes() + // 在 addToChildren 函数中使用上面获取到的路由信息来更新 route 的 children + addToChildren(routes, route.children || [], route) + router = null + // 转为二级路由后,去除所有子路由中的 children + route.children = route.children?.map((item) => omit(item, "children") as RouteRecordRaw) +} + +/** 将给定的子路由添加到指定的路由模块中 */ +const addToChildren = (routes: RouteRecordNormalized[], children: RouteRecordRaw[], routeModule: RouteRecordRaw) => { + children.forEach((child) => { + const route = routes.find((item) => item.name === child.name) + if (route) { + // 初始化 routeModule 的 children + routeModule.children = routeModule.children || [] + // 如果 routeModule 的 children 属性中不包含该路由,则将其添加进去 + if (!routeModule.children.includes(route)) { + routeModule.children.push(route) + } + // 如果该子路由还有自己的子路由,则递归调用此函数将它们也添加进去 + if (child.children?.length) { + addToChildren(routes, child.children, routeModule) + } + } + }) +} diff --git a/src/router/index.ts b/src/router/index.ts index b732a0f..ececcd5 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,4 +1,6 @@ -import { type RouteRecordRaw, createRouter, createWebHashHistory, createWebHistory } from "vue-router" +import { type RouteRecordRaw, createRouter } from "vue-router" +import { history, flatMultiLevelRoutes } from "./helper" +import routeSettings from "@/config/route" const Layouts = () => import("@/layouts/index.vue") @@ -123,7 +125,7 @@ export const constantRoutes: RouteRecordRaw[] = [ redirect: "/menu/menu1", name: "Menu", meta: { - title: "多级菜单", + title: "多级路由", svgIcon: "menu" }, children: [ @@ -141,7 +143,8 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import("@/views/menu/menu1/menu1-1/index.vue"), name: "Menu1-1", meta: { - title: "menu1-1" + title: "menu1-1", + keepAlive: true } }, { @@ -158,7 +161,8 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import("@/views/menu/menu1/menu1-2/menu1-2-1/index.vue"), name: "Menu1-2-1", meta: { - title: "menu1-2-1" + title: "menu1-2-1", + keepAlive: true } }, { @@ -166,7 +170,8 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import("@/views/menu/menu1/menu1-2/menu1-2-2/index.vue"), name: "Menu1-2-2", meta: { - title: "menu1-2-2" + title: "menu1-2-2", + keepAlive: true } } ] @@ -176,7 +181,8 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import("@/views/menu/menu1/menu1-3/index.vue"), name: "Menu1-3", meta: { - title: "menu1-3" + title: "menu1-3", + keepAlive: true } } ] @@ -186,7 +192,8 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import("@/views/menu/menu2/index.vue"), name: "Menu2", meta: { - title: "menu2" + title: "menu2", + keepAlive: true } } ] @@ -270,11 +277,8 @@ export const asyncRoutes: RouteRecordRaw[] = [ ] const router = createRouter({ - history: - import.meta.env.VITE_ROUTER_HISTORY === "hash" - ? createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH) - : createWebHistory(import.meta.env.VITE_PUBLIC_PATH), - routes: constantRoutes + history, + routes: routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(constantRoutes) : constantRoutes }) /** 重置路由 */ diff --git a/src/router/permission.ts b/src/router/permission.ts index 7f87227..8d0d145 100644 --- a/src/router/permission.ts +++ b/src/router/permission.ts @@ -3,7 +3,7 @@ import { useUserStoreHook } from "@/store/modules/user" import { usePermissionStoreHook } from "@/store/modules/permission" import { ElMessage } from "element-plus" import { getToken } from "@/utils/cache/cookies" -import asyncRouteSettings from "@/config/async-route" +import routeSettings from "@/config/route" import isWhiteList from "@/config/white-list" import NProgress from "nprogress" import "nprogress/nprogress.css" @@ -24,7 +24,7 @@ router.beforeEach(async (to, _from, next) => { // 检查用户是否已获得其权限角色 if (userStore.roles.length === 0) { try { - if (asyncRouteSettings.open) { + if (routeSettings.async) { // 注意:角色必须是一个数组! 例如: ['admin'] 或 ['developer', 'editor'] await userStore.getInfo() const roles = userStore.roles @@ -32,8 +32,8 @@ router.beforeEach(async (to, _from, next) => { permissionStore.setRoutes(roles) } else { // 没有开启动态路由功能,则启用默认角色 - userStore.setRoles(asyncRouteSettings.defaultRoles) - permissionStore.setRoutes(asyncRouteSettings.defaultRoles) + userStore.setRoles(routeSettings.defaultRoles) + permissionStore.setRoutes(routeSettings.defaultRoles) } // 将'有访问权限的动态路由' 添加到 Router 中 permissionStore.dynamicRoutes.forEach((route) => { diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index 276752f..a7c364e 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -3,7 +3,8 @@ import store from "@/store" import { defineStore } from "pinia" import { type RouteRecordRaw } from "vue-router" import { constantRoutes, asyncRoutes } from "@/router" -import asyncRouteSettings from "@/config/async-route" +import { flatMultiLevelRoutes } from "@/router/helper" +import routeSettings from "@/config/route" const hasPermission = (roles: string[], route: RouteRecordRaw) => { const routeRoles = route.meta?.roles @@ -29,9 +30,9 @@ export const usePermissionStore = defineStore("permission", () => { const dynamicRoutes = ref([]) const setRoutes = (roles: string[]) => { - const accessedRoutes = asyncRouteSettings.open ? filterAsyncRoutes(asyncRoutes, roles) : asyncRoutes + const accessedRoutes = routeSettings.async ? filterAsyncRoutes(asyncRoutes, roles) : asyncRoutes routes.value = constantRoutes.concat(accessedRoutes) - dynamicRoutes.value = accessedRoutes + dynamicRoutes.value = routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(accessedRoutes) : accessedRoutes } return { routes, dynamicRoutes, setRoutes } diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index 70ac62b..74d84a0 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -9,7 +9,7 @@ import router, { resetRouter } from "@/router" import { loginApi, getUserInfoApi } from "@/api/login" import { type LoginRequestData } from "@/api/login/types/login" import { type RouteRecordRaw } from "vue-router" -import asyncRouteSettings from "@/config/async-route" +import routeSettings from "@/config/route" export const useUserStore = defineStore("user", () => { const token = ref(getToken() || "") @@ -35,7 +35,7 @@ export const useUserStore = defineStore("user", () => { const { data } = await getUserInfoApi() username.value = data.username // 验证返回的 roles 是否为一个非空数组,否则塞入一个没有任何作用的默认角色,防止路由守卫逻辑进入无限循环 - roles.value = data.roles?.length > 0 ? data.roles : asyncRouteSettings.defaultRoles + roles.value = data.roles?.length > 0 ? data.roles : routeSettings.defaultRoles } /** 切换角色 */ const changeRoles = async (role: string) => { diff --git a/src/views/menu/menu1/index.vue b/src/views/menu/menu1/index.vue index ccb9e66..9b2df12 100644 --- a/src/views/menu/menu1/index.vue +++ b/src/views/menu/menu1/index.vue @@ -1,6 +1,6 @@