基于Vue Router的动态路由权限控制详解
创作时间:
作者:
@小白创作中心
基于Vue Router的动态路由权限控制详解
引用
CSDN
1.
https://blog.csdn.net/bingjilingvsh2o/article/details/145261451
本文详细介绍了基于Vue Router的动态路由权限控制的实现方法,包括用户登录、路由守卫和动态组件加载等关键步骤。通过本文,读者可以了解如何使用Pinia进行状态管理,以及如何实现基于菜单权限的动态路由加载。
1、用户登录
我们使用Pinia做状态管理,新建userStore.ts文件,用于处理用户登录、登出和储存用户信息。便于其他组件使用用户数据和方法。
主要定义两个方法,登录和登出。登录方法中,登录接口调用成功后,将用户名、token等信息存入store;登出方法,调用登出接口并清除store中的token。
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
import loginServer from '@/api/loginServer'
export const userStore = defineStore('user', () => {
const token = useLocalStorage('token', '')
const role = useLocalStorage('role', '')
const userName = useLocalStorage('userName', '')
const password = useLocalStorage('password', '')
const isRemember = useLocalStorage('remember', false)
const login = (param: any) => {
return new Promise<void>((resolve, reject) => {
loginServer
.login({
username: param.username,
password: param.password
})
.then((res) => {
token.value = res.token
userName.value = res.userName
role.value = res.role
isRemember.value = param.isRemember
password.value = ''
if (param.isRemember) {
//缓存密码
password.value = param.password
}
resolve()
})
.catch((error) => {
reject(error)
})
})
}
const logout = () => {
return new Promise((resolve, reject) => {
loginServer
.logout()
.then((res) => {
// 清除token
token.value = ''
resolve(res)
})
.catch((error) => {
reject(error)
})
})
}
return {
token,
userName,
password,
isRemember,
role,
login,
logout
}
})
用户输入用户名密码后,点击登录按钮,调用userStore的login方法,接口返回值存入userStore。在登录组件中使用userStore的代码如下。
import { userStore } from '@/store/userStore'
const userStore = userStore()
const login = async (values: any) => {
login_loading.value = true
try {
await userStore.login({
...values
})
router.push('/')
} catch (error) {
}
}
2、路由守卫
登录后进入到路由阶段。
2.1 动态引入组件
这里使用的是动态引入组件,根据菜单数据中配置好的component字段,其值为组件的路径字符串,从而实现动态加载组件。
首先加载views目录下所有vue文件,编写一个根据路径字符串转换为组件的方法,接收菜单数据参数。
// 使用 import.meta.glob 动态加载所有以.vue结尾的文件
const requireComponent = import.meta.glob(['@/views/**/*.vue'])
// 根据路径字符串转换为组件的方法
const convertPathToComponent = (limitMenus: any) => {
if (limitMenus && limitMenus.length > 0) {
limitMenus.forEach((item: any) => { // 遍历菜单数据
const componentPath = item.component // 菜单的component字段值为该组件的路径字符串
item.component = requireComponent[`/src/views/${componentPath}/index.vue`]
if (item.children && item.children.length > 0) {
convertPathToComponent(item.children) // 递归转换
}
})
}
}
菜单数据示例:
let menus = [
{
"menuId": "1",
"meta": {
"hasChildren": true,
"icon": "system",
"menuId": 1,
"title": "系统配置"
},
"path": "system",
"component": null,
"children": [
{
"menuId": "2",
"meta": {
"hasChildren": false,
"icon": null,
"menuId": 2,
"title": "配置列表"
},
"path": "setList",
"component": "system/setList"
},
{
"menuId": "3",
"meta": {
"hasChildren": false,
"icon": null,
"menuId": 3,
"title": "配置文件"
},
"path": "files",
"component": "system/files"
}
]
}
]
2.2 路由守卫逻辑
1)判断目标是否为登录页,是,就移除动态路由,清除权限,并放行;如果目标不是登录页,到2)
2)判断用户已经登录,可以通过token存储状态判断是否登录,是就到3);否就跳到登录页
3)根据menuID获取按钮权限,存入menuStore中
4)判断menuStore中是否有菜单权限数据,是,就放行;否,进入5)
5)获取菜单权限,存储在menuStore,并处理数据,转换组件,并设置重定向到首页
路由守卫中的判断流程图如下图所示。
route.ts关键代码如下:
const requireComponent = import.meta.glob(['@/views/**/*.vue'])
const loginPath = '/login'
const dynamicRoutes: RouteRecordRaw = {
path: '/',
name: 'root',
component: Layout,
children: []
}
let removeRoute: () => void
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: loginPath,
component: Login
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/notFound/index.vue')
}
]
})
router.beforeEach(async (to, from, next) => {
const userStore = userStore()
const menuStore = menuStore()
if (to.path === loginPath) {
removeRoute && removeRoute()
menuStore.limitMenus = []
next()
} else {
if (userStore.token) {
//已登录
const res = await getBtnLimit(to.meta.menuId)
res.forEach((item) => {
menuStore.btnPermissions.add(item)
})
if (menuStore.limitMenus && menuStore.limitMenus.length > 0) {
next()
} else {
try {
const res = await loginServer.getMenus()
userStore.role = res.role
if (res.menus && res.menus.length > 0) {
res.menus.forEach((item) => {
if (item.children && item.children.length > 0) {
const subItem = item.children[0]
item.redirect = item.path + '/' + subItem.path
}
})
//转换组件
convertPathToComponent(res.menus)
menuStore.limitMenus = [
...res.menus
]
dynamicRoutes.children = menuStore.limitMenus
//重定向第一个路由
const firstMenu = menuStore.limitMenus[0]
dynamicRoutes.redirect = firstMenu.path
removeRoute = router.addRoute(dynamicRoutes)
next({ ...to, replace: true })
} else {
menuStore.limitMenus = []
next({
path: loginPath
})
}
} catch (error) {
//权限获取失败跳转到登录页
next({
path: loginPath
})
}
}
} else {
//未登录
next({
path: loginPath
})
}
}
})
// 根据路径字符串转换为组件的方法
const convertPathToComponent = (limitMenus: any) => {
if (limitMenus && limitMenus.length > 0) {
limitMenus.forEach((item: any) => { // 遍历菜单数据
const componentPath = item.component // 菜单的component字段值为该组件的路径字符串
item.component = requireComponent[`/src/views/${componentPath}/index.vue`]
if (item.children && item.children.length > 0) {
convertPathToComponent(item.children) // 递归转换
}
})
}
}
热门推荐
量子计算如何在分子层面改变药物开发
公司没有债务问题可以注销吗
"Oh Yes, Minister": 政治讽刺喜剧的经典之作
Db2数据库如何查询字段名
海棠文学:揭示生活细腻情感与真实人际关系的魅力
一般民办本科一年的学费是多少钱
门冬胰岛素注射液使用说明指导
培养孩子情商的7个方法
浴室柜安装高度标准尺寸与材料选择
银行跨境汇款手续费及汇率计算指南
中国气象局将在山西岢岚建设国家气候观象台
积阴德最快最大的方法,这十件事很容易积阴德
2月份国内糖市供需形势分析解读!广西干旱影响几何?
不同季节丝袜怎么选?厚度背后的时尚与舒适平衡
破解“跨学科主题学习”两大难题(附实操案例)
牙线和牙缝刷哪个更适合正畸患者?
中药泡脚配方大全:从养生保健到疾病调理
1994年腊月初四出生属狗人八字五行缺吗?命运、婚姻、性格、事业财运!
全国多处历史文化景区可背诗文免门票 游客:书本上的诗词歌赋“走”出来融入到生活中
社保基数怎么计算?
行测类比推理的二次辨析如何快速解答?
让二手车商代销自己的车,如何签汽车代销电子合同
小孩舌苔白厚怎么治疗
二手品牌手表交易:收藏价值与商业模式深度解析
探索信仰的多样性:不同宗教对于人生意义的看法
Excel表格怎么设置分类组
如何分析亚盘市场并做出明智交易决策?亚盘分析对全球市场有何实际影响?
海南有什么特产值得带回家?本地人推荐这10样,物美价廉,不踩坑
连跨“高科技”“创业史”两道坎,《赤热》把行业剧拍出了共情感
RESP在医学领域是什么意思?