Skip to content

Commit

Permalink
perf: 优化动态路由
Browse files Browse the repository at this point in the history
  • Loading branch information
kailong321200875 committed Aug 27, 2023
1 parent 1452a1a commit 8793588
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 98 deletions.
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ If you need a basic template, please switch to the `mini` branch. `mini` simply
- [vue-element-plus-admin](https://element-plus-admin.cn/) - Full version of the github site
- [vue-element-plus-admin](https://kailong110120130.gitee.io/vue-element-plus-admin) - Full version of the gitee site

account: **admin/admin test/test**

`admin` account is used to simulate the control permission of the server, and render whatever the server returns

`test` account is used to simulate the front-end control authority. The server only returns the menu key to be displayed, and the front-end performs matching rendering
account: **admin/admin**

Online examples do not apply to menu filtering by default, but directly use Static routing

Expand Down
6 changes: 1 addition & 5 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ vue-element-plus-admin 的定位是后台集成方案,不太适合当基础模
- [vue-element-plus-admin](https://element-plus-admin.cn/) - 完整版 github 站点
- [vue-element-plus-admin](https://kailong110120130.gitee.io/vue-element-plus-admin) - 完整版 gitee 站点

帐号:**admin/admin test/test**

`admin` 帐号用于模拟服务端控制权限,服务端返回什么就渲染什么

`test` 帐号用于模拟前端控制权限,服务端只返回需要显示的菜单 key,前端进行匹配渲染
帐号:**admin/admin**

在线例子默认不适用菜单过滤,而是直接使用静态路由表

Expand Down
35 changes: 32 additions & 3 deletions mock/role/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1070,12 +1070,41 @@ export default [
url: '/role/list',
method: 'get',
timeout,
response: ({ query }) => {
const { roleName } = query
response: () => {
return {
data: {
code: code,
data: adminList
}
}
}
},
{
url: '/role/table',
method: 'get',
timeout,
response: () => {
return {
data: {
code: code,
data: {
list: List,
total: 4
}
}
}
}
},
// 列表接口
{
url: '/role/list2',
method: 'get',
timeout,
response: () => {
return {
data: {
code: code,
data: roleName === 'admin' ? adminList : testList
data: testList
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/api/login/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ export const getAdminRoleApi = (
}

export const getTestRoleApi = (params: RoleParams): Promise<IResponse<string[]>> => {
return request.get({ url: '/role/list', params })
return request.get({ url: '/role/list2', params })
}
13 changes: 13 additions & 0 deletions src/components/Setting/src/components/InterfaceDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ const dynamicRouterChange = (show: boolean) => {
appStore.setDynamicRouter(show)
}
// 服务端动态路由
const serverDynamicRouter = ref(appStore.getServerDynamicRouter)
const serverDynamicRouterChange = (show: boolean) => {
ElMessage.info(t('setting.reExperienced'))
appStore.setServerDynamicRouter(show)
}
// 固定菜单
const fixedMenu = ref(appStore.getFixedMenu)
Expand Down Expand Up @@ -206,6 +214,11 @@ watch(
<ElSwitch v-model="dynamicRouter" @change="dynamicRouterChange" />
</div>

<div class="flex justify-between items-center">
<span class="text-14px">{{ t('setting.serverDynamicRouter') }}</span>
<ElSwitch v-model="serverDynamicRouter" @change="serverDynamicRouterChange" />
</div>

<div class="flex justify-between items-center">
<span class="text-14px">{{ t('setting.fixedMenu') }}</span>
<ElSwitch v-model="fixedMenu" @change="fixedMenuChange" />
Expand Down
29 changes: 22 additions & 7 deletions src/hooks/web/useStorage.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { isArray, isObject } from '@/utils/is'
// 获取传入的值的类型
const getValueType = (value: any) => {
const type = Object.prototype.toString.call(value)
return type.slice(8, -1)
}

export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionStorage') => {
const setStorage = (key: string, value: any) => {
window[type].setItem(key, isArray(value) || isObject(value) ? JSON.stringify(value) : value)
const valueType = getValueType(value)
window[type].setItem(key, JSON.stringify({ type: valueType, value }))
}

const getStorage = (key: string) => {
const value = window[type].getItem(key)
try {
return JSON.parse(value || '')
} catch (error) {
if (value) {
const { value: val } = JSON.parse(value)
return val
} else {
return value
}
}
Expand All @@ -18,8 +24,17 @@ export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionSto
window[type].removeItem(key)
}

const clear = () => {
window[type].clear()
const clear = (excludes?: string[]) => {
// 获取排除项
const keys = Object.keys(window[type])
const defaultExcludes = ['dynamicRouter', 'serverDynamicRouter']
const excludesArr = excludes ? [...excludes, ...defaultExcludes] : defaultExcludes
const excludesKeys = excludesArr ? keys.filter((key) => !excludesArr.includes(key)) : keys
// 排除项不清除
excludesKeys.forEach((key) => {
window[type].removeItem(key)
})
// window[type].clear()
}

return {
Expand Down
4 changes: 3 additions & 1 deletion src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ export default {
footer: 'Footer',
uniqueOpened: 'Unique opened',
tagsViewIcon: 'Tags view icon',
dynamicRouter: 'Dynamic router',
// 开启动态路由
dynamicRouter: 'Enable dynamic router',
serverDynamicRouter: 'Server dynamic router',
reExperienced: 'Please exit the login experience again',
fixedMenu: 'Fixed menu'
},
Expand Down
3 changes: 2 additions & 1 deletion src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export default {
footer: '页脚',
uniqueOpened: '菜单手风琴',
tagsViewIcon: '标签页图标',
dynamicRouter: '动态路由',
dynamicRouter: '开启动态路由',
serverDynamicRouter: '服务端动态路由',
reExperienced: '请重新退出登录体验',
fixedMenu: '固定菜单'
},
Expand Down
21 changes: 4 additions & 17 deletions src/permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ import type { RouteRecordRaw } from 'vue-router'
import { useTitle } from '@/hooks/web/useTitle'
import { useNProgress } from '@/hooks/web/useNProgress'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
// import { useDictStoreWithOut } from '@/store/modules/dict'
import { usePageLoading } from '@/hooks/web/usePageLoading'
// import { getDictApi } from '@/api/common'

const permissionStore = usePermissionStoreWithOut()

const appStore = useAppStoreWithOut()

// const dictStore = useDictStoreWithOut()

const { getStorage } = useStorage()

const { start, done } = useNProgress()
Expand All @@ -30,30 +26,21 @@ router.beforeEach(async (to, from, next) => {
if (to.path === '/login') {
next({ path: '/' })
} else {
// if (!dictStore.getIsSetDict) {
// // 获取所有字典
// const res = await getDictApi()
// if (res) {
// dictStore.setDictObj(res.data)
// dictStore.setIsSetDict(true)
// }
// }
if (permissionStore.getIsAddRouters) {
next()
return
}

// 开发者可根据实际情况进行修改
const roleRouters = getStorage('roleRouters') || []
const userInfo = getStorage(appStore.getUserInfo)

// 是否使用动态路由
if (appStore.getDynamicRouter) {
userInfo.role === 'admin'
? await permissionStore.generateRoutes('admin', roleRouters as AppCustomRouteRecordRaw[])
: await permissionStore.generateRoutes('test', roleRouters as string[])
appStore.serverDynamicRouter
? await permissionStore.generateRoutes('server', roleRouters as AppCustomRouteRecordRaw[])
: await permissionStore.generateRoutes('frontEnd', roleRouters as string[])
} else {
await permissionStore.generateRoutes('none')
await permissionStore.generateRoutes('static')
}

permissionStore.getAddRouters.forEach((route) => {
Expand Down
16 changes: 12 additions & 4 deletions src/store/modules/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface AppState {
fixedHeader: boolean
greyMode: boolean
dynamicRouter: boolean
serverDynamicRouter: boolean
pageLoading: boolean
layout: LayoutType
title: string
Expand All @@ -42,7 +43,6 @@ export const useAppStore = defineStore('app', {
mobile: false, // 是否是移动端
title: import.meta.env.VITE_APP_TITLE, // 标题
pageLoading: false, // 路由跳转loading

breadcrumb: true, // 面包屑
breadcrumbIcon: true, // 面包屑图标
collapse: false, // 折叠菜单
Expand All @@ -57,11 +57,12 @@ export const useAppStore = defineStore('app', {
fixedHeader: true, // 固定toolheader
footer: true, // 显示页脚
greyMode: false, // 是否开始灰色模式,用于特殊悼念日
dynamicRouter: getStorage('dynamicRouter') || true, // 是否动态路由
fixedMenu: getStorage('fixedMenu') || false, // 是否固定菜单
dynamicRouter: getStorage('dynamicRouter'), // 是否动态路由
serverDynamicRouter: getStorage('serverDynamicRouter'), // 是否服务端渲染动态路由
fixedMenu: getStorage('fixedMenu'), // 是否固定菜单

layout: getStorage('layout') || 'classic', // layout布局
isDark: getStorage('isDark') || false, // 是否是暗黑模式
isDark: getStorage('isDark'), // 是否是暗黑模式
currentSize: getStorage('default') || 'default', // 组件尺寸
theme: getStorage('theme') || {
// 主题色
Expand Down Expand Up @@ -138,6 +139,9 @@ export const useAppStore = defineStore('app', {
getDynamicRouter(): boolean {
return this.dynamicRouter
},
getServerDynamicRouter(): boolean {
return this.serverDynamicRouter
},
getFixedMenu(): boolean {
return this.fixedMenu
},
Expand Down Expand Up @@ -216,6 +220,10 @@ export const useAppStore = defineStore('app', {
setStorage('dynamicRouter', dynamicRouter)
this.dynamicRouter = dynamicRouter
},
setServerDynamicRouter(serverDynamicRouter: boolean) {
setStorage('serverDynamicRouter', serverDynamicRouter)
this.serverDynamicRouter = serverDynamicRouter
},
setFixedMenu(fixedMenu: boolean) {
setStorage('fixedMenu', fixedMenu)
this.fixedMenu = fixedMenu
Expand Down
34 changes: 0 additions & 34 deletions src/store/modules/dict.ts

This file was deleted.

16 changes: 10 additions & 6 deletions src/store/modules/permission.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { defineStore } from 'pinia'
import { asyncRouterMap, constantRouterMap } from '@/router'
import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import {
generateRoutesByFrontEnd,
generateRoutesByServer,
flatMultiLevelRoutes
} from '@/utils/routerHelper'
import { store } from '../index'
import { cloneDeep } from 'lodash-es'

Expand Down Expand Up @@ -34,17 +38,17 @@ export const usePermissionStore = defineStore('permission', {
},
actions: {
generateRoutes(
type: 'admin' | 'test' | 'none',
type: 'server' | 'frontEnd' | 'static',
routers?: AppCustomRouteRecordRaw[] | string[]
): Promise<unknown> {
return new Promise<void>((resolve) => {
let routerMap: AppRouteRecordRaw[] = []
if (type === 'admin') {
if (type === 'server') {
// 模拟后端过滤菜单
routerMap = generateRoutesFn2(routers as AppCustomRouteRecordRaw[])
} else if (type === 'test') {
routerMap = generateRoutesByServer(routers as AppCustomRouteRecordRaw[])
} else if (type === 'frontEnd') {
// 模拟前端过滤菜单
routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap), routers as string[])
routerMap = generateRoutesByFrontEnd(cloneDeep(asyncRouterMap), routers as string[])
} else {
// 直接读取静态路由表
routerMap = cloneDeep(asyncRouterMap)
Expand Down
Loading

0 comments on commit 8793588

Please sign in to comment.