Template
1
0
mirror of https://github.com/un-pany/v3-admin-vite.git synced 2025-04-20 19:09:21 +08:00

feat: 支持三级及其以上路由的 keep-alive 缓存 (#93)

This commit is contained in:
ClariS 2023-08-07 15:14:15 +08:00 committed by GitHub
parent 45b0bea731
commit ad9ff59a40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 176 additions and 35 deletions

View File

@ -1,21 +1,28 @@
/** 动态路由配置 */
interface AsyncRouteSettings {
interface RouteSettings {
/**
*
* 1. roles
* 2. open: false
* 2. async: false
*/
open: boolean
async: boolean
/**
* 1. 访
* 2.
*/
defaultRoles: Array<string>
/**
*
* 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

69
src/router/helper.ts Normal file
View File

@ -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)
}
}
})
}

View File

@ -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
})
/** 重置路由 */

View File

@ -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) => {

View File

@ -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<RouteRecordRaw[]>([])
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 }

View File

@ -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<string>(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) => {

View File

@ -1,6 +1,6 @@
<template>
<div class="app-container">
<el-card header="menu 1">
<el-card header="二级路由 - menu1">
<router-view />
</el-card>
</div>

View File

@ -1,5 +1,17 @@
<script lang="ts" setup>
import { ref } from "vue"
defineOptions({
name: "Menu1-1"
})
const text = ref("")
</script>
<template>
<div class="app-container">
<el-card> menu 1-1 </el-card>
<el-card header="三级路由缓存 - menu1-1">
<el-input v-model="text" />
</el-card>
</div>
</template>

View File

@ -1,6 +1,6 @@
<template>
<div class="app-container">
<el-card header="menu 1-2">
<el-card header="三级路由 - menu1-2">
<router-view />
</el-card>
</div>

View File

@ -1,5 +1,17 @@
<script lang="ts" setup>
import { ref } from "vue"
defineOptions({
name: "Menu1-2-1"
})
const text = ref("")
</script>
<template>
<div class="app-container">
<el-card> menu 1-2-1 </el-card>
<el-card header="四级路由缓存 - menu1-2-1">
<el-input v-model="text" />
</el-card>
</div>
</template>

View File

@ -1,5 +1,17 @@
<script lang="ts" setup>
import { ref } from "vue"
defineOptions({
name: "Menu1-2-2"
})
const text = ref("")
</script>
<template>
<div class="app-container">
<el-card> menu 1-2-2 </el-card>
<el-card header="四级路由缓存 - menu1-2-2">
<el-input v-model="text" />
</el-card>
</div>
</template>

View File

@ -1,5 +1,17 @@
<script lang="ts" setup>
import { ref } from "vue"
defineOptions({
name: "Menu1-3"
})
const text = ref("")
</script>
<template>
<div class="app-container">
<el-card> menu 1-3 </el-card>
<el-card header="三级路由缓存 - menu1-3">
<el-input v-model="text" />
</el-card>
</div>
</template>

View File

@ -1,5 +1,17 @@
<script lang="ts" setup>
import { ref } from "vue"
defineOptions({
name: "Menu2"
})
const text = ref("")
</script>
<template>
<div class="app-container">
<el-card> menu 2 </el-card>
<el-card header="二级路由缓存 - menu2">
<el-input v-model="text" />
</el-card>
</div>
</template>