Skip to content

Commit

Permalink
feat: Add dynamic route
Browse files Browse the repository at this point in the history
  • Loading branch information
kailong321200875 committed Feb 19, 2022
1 parent d5b6e2a commit 9d926b2
Show file tree
Hide file tree
Showing 15 changed files with 852 additions and 67 deletions.
475 changes: 475 additions & 0 deletions mock/role/index.ts

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions mock/user/index.ts
Expand Up @@ -26,6 +26,30 @@ const List: {
]

export default [
// 列表接口
{
url: '/user/list',
method: 'get',
response: ({ query }) => {
const { username, pageIndex, pageSize } = query

const mockList = List.filter((item) => {
if (username && item.username.indexOf(username) < 0) return false
return true
})
const pageList = mockList.filter(
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
)

return {
code: result_code,
data: {
total: mockList.length,
list: pageList
}
}
}
},
// 登录接口
{
url: '/user/login',
Expand Down
21 changes: 20 additions & 1 deletion src/api/login/index.ts
@@ -1,5 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios'
import type { UserLoginType } from './types'
import type { UserLoginType, UserType } from './types'

const { request } = useAxios()

Expand All @@ -13,3 +13,22 @@ export const loginApi = (data: UserLoginType) => {
export const loginOutApi = () => {
return request({ url: '/user/loginOut', method: 'get' })
}

export const getUserListApi = ({ params }: AxiosConfig) => {
return request<{
total: number
list: UserType[]
}>({ url: '/user/list', method: 'get', params })
}

export const getAdminRoleApi = ({ params }: AxiosConfig) => {
return request<{
list: AppCustomRouteRecordRaw[]
}>({ url: '/role/list', method: 'get', params })
}

export const getTestRoleApi = ({ params }: AxiosConfig) => {
return request<{
list: string[]
}>({ url: '/role/list', method: 'get', params })
}
7 changes: 7 additions & 0 deletions src/api/login/types.ts
Expand Up @@ -2,3 +2,10 @@ export type UserLoginType = {
username: string
password: string
}

export type UserType = {
username: string
password: string
role: string
roleId: string
}
18 changes: 17 additions & 1 deletion src/locales/en.ts
Expand Up @@ -126,7 +126,10 @@ export default {
exampleAdd: 'Example page - add',
exampleEdit: 'Example page - edit',
exampleDetail: 'Example page - detail',
errorPage: 'Error page'
errorPage: 'Error page',
authorization: 'Authorization',
user: 'User management',
role: 'Role management'
},
analysis: {
newUser: 'New user',
Expand Down Expand Up @@ -393,5 +396,18 @@ export default {
content: 'Content',
save: 'Save',
detail: 'Detail'
},
userDemo: {
title: 'User management',
message:
'Because it is simulated data, only two accounts with different permissions are provided, which can be modified and combined by developers according to the actual situation.',
index: 'Index',
action: 'Action',
username: 'Username',
password: 'Password',
role: 'Role',
remark: 'Remark',
remarkMessage1: 'Back end control routing permission',
remarkMessage2: 'Front end control routing permission'
}
}
17 changes: 16 additions & 1 deletion src/locales/zh-CN.ts
Expand Up @@ -126,7 +126,10 @@ export default {
exampleAdd: '综合示例 - 新增',
exampleEdit: '综合示例 - 编辑',
exampleDetail: '综合示例 - 详情',
errorPage: '错误页面'
errorPage: '错误页面',
authorization: '权限管理',
user: '用户管理',
role: '角色管理'
},
analysis: {
newUser: '新增用户',
Expand Down Expand Up @@ -390,5 +393,17 @@ export default {
content: '内容',
save: '保存',
detail: '详情'
},
userDemo: {
title: '用户管理',
message: '由于是模拟数据,所以只提供了两种不同权限的帐号,开发者可根据实际情况自行改造结合。',
index: '序号',
action: '操作',
username: '用户名',
password: '密码',
role: '角色',
remark: '备注',
remarkMessage1: '后端控制路由权限',
remarkMessage2: '前端控制路由权限'
}
}
10 changes: 9 additions & 1 deletion src/permission.ts
Expand Up @@ -30,7 +30,15 @@ router.beforeEach(async (to, from, next) => {
to.path === '/' ? next({ path: permissionStore.addRouters[0]?.path as string }) : next()
return
}
await permissionStore.generateRoutes()

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

userInfo.role === 'admin'
? await permissionStore.generateRoutes('admin', roleRouters as AppCustomRouteRecordRaw[])
: await permissionStore.generateRoutes('test', roleRouters as string[])

permissionStore.getAddRouters.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
})
Expand Down
43 changes: 36 additions & 7 deletions src/router/index.ts
Expand Up @@ -424,31 +424,60 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
},
children: [
{
path: '404',
path: '404-demo',
component: () => import('@/views/Error/404.vue'),
name: '404',
name: '404Demo',
meta: {
title: '404'
}
},
{
path: '403',
path: '403-demo',
component: () => import('@/views/Error/403.vue'),
name: '403',
name: '403Demo',
meta: {
title: '403'
}
},
{
path: '500',
path: '500-demo',
component: () => import('@/views/Error/500.vue'),
name: '500',
name: '500Demo',
meta: {
title: '500'
}
}
]
}
// {
// path: '/authorization',
// component: Layout,
// redirect: '/authorization/user',
// name: 'Authorization',
// meta: {
// title: t('router.authorization'),
// icon: 'eos-icons:role-binding',
// alwaysShow: true
// },
// children: [
// {
// path: 'user',
// component: () => import('@/views/Authorization/User.vue'),
// name: 'User',
// meta: {
// title: t('router.user')
// }
// },
// {
// path: 'role',
// component: () => import('@/views/Authorization/Role.vue'),
// name: 'Role',
// meta: {
// title: t('router.role')
// }
// }
// ]
// }
]

const router = createRouter({
Expand All @@ -459,7 +488,7 @@ const router = createRouter({
})

export const resetRouter = (): void => {
const resetWhiteNameList = ['RedirectRoot', 'Redirect', 'Login', 'Root', 'Dashboard', 'Page404']
const resetWhiteNameList = ['Redirect', 'Login', 'NoFind']
router.getRoutes().forEach((route) => {
const { name } = route
if (name && !resetWhiteNameList.includes(name as string)) {
Expand Down
39 changes: 17 additions & 22 deletions src/store/modules/permission.ts
@@ -1,16 +1,9 @@
import { defineStore } from 'pinia'
import { asyncRouterMap, constantRouterMap } from '@/router'
// import { useCache } from '@/hooks/web/useCache'
import { flatMultiLevelRoutes } from '@/utils/routerHelper'
// import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { store } from '../index'
// import { useAppStoreWithOut } from '@/store/modules/app'
import { cloneDeep } from 'lodash-es'

// const { wsCache } = useCache()

// const appStore = useAppStoreWithOut()

export interface PermissionState {
routers: AppRouteRecordRaw[]
addRouters: AppRouteRecordRaw[]
Expand Down Expand Up @@ -44,21 +37,23 @@ export const usePermissionStore = defineStore({
}
},
actions: {
generateRoutes(): Promise<unknown> {
generateRoutes(
type: 'admin' | 'test' | 'none',
routers?: AppCustomRouteRecordRaw[] | string[]
): Promise<unknown> {
return new Promise<void>((resolve) => {
// 路由权限控制,如果不需要权限控制,请注释
// let routerMap: AppRouteRecordRaw[] = []
// if (wsCache.get(appStore.getUserInfo).username === 'admin') {
// // 模拟前端控制权限
// routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap))
// } else {
// // 模拟后端控制权限
// routerMap = generateRoutesFn2(wsCache.get(appStore.getUserInfo).checkedNodes)
// }

// 不需要权限控制
const routerMap: AppRouteRecordRaw[] = cloneDeep(asyncRouterMap)

let routerMap: AppRouteRecordRaw[] = []
if (type === 'admin') {
// 模拟后端过滤菜单
routerMap = generateRoutesFn2(routers as AppCustomRouteRecordRaw[])
} else if (type === 'test') {
// 模拟前端过滤菜单
routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap), routers as string[])
} else {
// 直接读取静态路由表
routerMap = cloneDeep(asyncRouterMap)
}
console.log(routerMap)
// 动态路由,404一定要放到最后面
this.addRouters = routerMap.concat([
{
Expand Down
3 changes: 0 additions & 3 deletions src/store/modules/tagsView.ts
Expand Up @@ -16,9 +16,6 @@ export const useTagsViewStore = defineStore({
visitedViews: [],
cachedViews: new Set()
}),
persist: {
enabled: true
},
getters: {
getVisitedViews(): RouteLocationNormalizedLoaded[] {
return this.visitedViews
Expand Down
35 changes: 13 additions & 22 deletions src/utils/routerHelper.ts
@@ -1,15 +1,9 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import type { Router, RouteLocationNormalized, RouteRecordNormalized, RouteMeta } from 'vue-router'
import { isUrl } from '@/utils/is'
import { useCache } from '@/hooks/web/useCache'
import { useAppStoreWithOut } from '@/store/modules/app'
import { omit, cloneDeep } from 'lodash-es'

const appStore = useAppStoreWithOut()

const { wsCache } = useCache()

const modules = import.meta.glob('../../views/**/*.{vue,tsx}')
const modules = import.meta.glob('../views/**/*.{vue,tsx}')

/* Layout */
export const Layout = () => import('@/layout/Layout.vue')
Expand Down Expand Up @@ -41,6 +35,7 @@ export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormal
// 前端控制路由生成
export const generateRoutesFn1 = (
routes: AppRouteRecordRaw[],
keys: string[],
basePath = '/'
): AppRouteRecordRaw[] => {
const res: AppRouteRecordRaw[] = []
Expand All @@ -55,7 +50,6 @@ export const generateRoutesFn1 = (
let data: Nullable<AppRouteRecordRaw> = null

let onlyOneChild: Nullable<string> = null

if (route.children && route.children.length === 1 && !meta.alwaysShow) {
onlyOneChild = (
isUrl(route.children[0].path)
Expand All @@ -64,24 +58,22 @@ export const generateRoutesFn1 = (
) as string
}

// 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
const list = wsCache.get(appStore.getUserInfo).checkedNodes
// 开发者可以根据实际情况进行扩展
for (const item of list) {
for (const item of keys) {
// 通过路径去匹配
if (isUrl(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
if (isUrl(item) && (onlyOneChild === item || route.path === item)) {
data = Object.assign({}, route)
} else {
const routePath = pathResolve(basePath, onlyOneChild || route.path)
if (routePath === item.path || meta.followRoute === item.path) {
if (routePath === item || meta.followRoute === item) {
data = Object.assign({}, route)
}
}
}

// recursive child routes
if (route.children && data) {
data.children = generateRoutesFn1(route.children, pathResolve(basePath, data.path))
data.children = generateRoutesFn1(route.children, keys, pathResolve(basePath, data.path))
}
if (data) {
res.push(data as AppRouteRecordRaw)
Expand All @@ -91,7 +83,7 @@ export const generateRoutesFn1 = (
}

// 后端控制路由生成
export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] => {
export const generateRoutesFn2 = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => {
const res: AppRouteRecordRaw[] = []

for (const route of routes) {
Expand All @@ -102,15 +94,14 @@ export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRa
meta: route.meta
}
if (route.component) {
const comModule =
modules[`../../${route.component}.vue`] || modules[`../../${route.component}.tsx`]
if (comModule) {
const comModule = modules[`../${route.component}.vue`] || modules[`../${route.component}.tsx`]
const component = route.component as string
if (!comModule && !component.includes('#')) {
console.error(`未找到${route.component}.vue文件或${route.component}.tsx文件,请创建`)
} else {
// 动态加载路由文件,可根据实际情况进行自定义逻辑
const component = route.component as string
data.component =
component === '#' ? Layout : component.includes('##') ? getParentLayout() : comModule
} else {
console.error(`未找到${route.component}.vue文件或${route.component}.tsx文件,请创建`)
}
}
// recursive child routes
Expand All @@ -124,7 +115,7 @@ export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRa

export const pathResolve = (parentPath: string, path: string) => {
const childPath = path.startsWith('/') || !path ? path : `/${path}`
return `${parentPath}${childPath}`
return `${parentPath}${childPath}`.replace(/\/\//g, '/')
}

// 路由降级
Expand Down

0 comments on commit 9d926b2

Please sign in to comment.