基于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) // 递归转换
}
})
}
}
热门推荐
设计师必备思维模型
微创手术需住院吗
求职必看!三个公式学会高效表达
摘获2024诺贝尔奖!miRNA何以站上基因调控C位?
年假可以跨年休吗?三倍工资是另外支付300%还是200%?
水泥基防水涂料技术交底(水泥基防水涂料施工工艺要求)
翻墙行为是否合法:法律视角下的全面解析
昆明盘龙区:激活人才引擎 赋能乡村振兴
责任与担当 新时代高质量发展的原动力
本田思域的保养维护成本高吗?本田日常需要保养哪些项目?
探秘!南昌这些村落藏着乡愁记忆
2025新高考投档录取原则:志愿录取顺序是怎么样的?
贝叶斯定理及其在人工智能中的应用
【禁毒科普】传统毒品与新型毒品全解析:从危害到预防
女儿墙的由来和构造规范及防水施工注意事项
硼砂的性质、用途及危害:从化工原料到医疗应用的全面解析
60句一眼惊艳的对偶诗词,意境绝美,冠绝古今
电机转速与性能全解析:从基础概念到实际应用
视觉定位在焊接机器人中的作用
结婚戒指戴哪只手
丙酮:一种重要的有机化合物
厨房下水道疏通全攻略:物理、化学方法和预防保养技巧
合金钢同步带轮的详细应用领域?
智能车竞赛入门指南:从零开始打造参赛作品
2024年理工大学排行榜最新版(含全国100所学校排名)
冬天煮老白茶的6大好处
成都全市义务教育优质资源覆盖率达65% 成都各区(县)教育有何新变化?
如何选择适合的地板颜色以提升家居美感?这种选择对室内设计有何影响?
以案释法:购买到“三无”减肥产品,消费者如何维权?
女性低血压的症状及应对方法