Vue后臺(tái)管理系統(tǒng)權(quán)限控制與動(dòng)態(tài)路由的實(shí)現(xiàn)
在開(kāi)發(fā)后臺(tái)管理系統(tǒng)時(shí),權(quán)限控制是不可或缺的一部分。本文將詳細(xì)介紹如何使用Vue實(shí)現(xiàn)根據(jù)不同角色動(dòng)態(tài)生成路由,從而控制用戶訪問(wèn)權(quán)限。
步驟一:基礎(chǔ)路由配置
首先,我們需要配置基礎(chǔ)路由,這些路由是所有用戶共有的,例如登錄頁(yè)和404頁(yè)。
const allRoutes = [
// 基礎(chǔ)路由
{
path: '/',
name: 'login',
component: () => import("@/views/LoginView.vue")
},
{
path: '/404',
name: '404',
component: () => import('@/views/Error/404View.vue')
},
// 動(dòng)態(tài)路由容器(登錄后內(nèi)容)
{
path: '/layout',
name: 'layout',
component: () => import('@/Layout/MainLayout.vue'),
children: []
},
]步驟二:完整路由數(shù)據(jù)
接下來(lái),創(chuàng)建一個(gè)包含所有可能路由的數(shù)組。這個(gè)數(shù)組將用于與后端返回的權(quán)限數(shù)據(jù)對(duì)比,以篩選出當(dāng)前用戶有權(quán)訪問(wèn)的路由。
// 所有路由
const totalRoute = [
{
path: '/',
name: 'login',
component: () => import("@/views/LoginView.vue")
},
{
path: '/404',
name: '404',
component: () => import('@/views/Error/404View.vue')
},
{
path: '/layout',
name: 'layout',
component: () => import('@/Layout/MainLayout.vue'),
},
{
path: '/home',
name: '首頁(yè)',
id: 1,
pid: 0,
},
{
id: 2,
pid: 0,
redirect: null,
path: '/comp',
name: '門(mén)診',
children: [{
id: 3,
pid: 2,
redirect: null,
component: () => import('@/views/payView.vue'),
path: "/pay",
name: "門(mén)診繳費(fèi)記錄",
},{
id: 4,
pid: 2,
redirect: null,
component: () => import('@/views/reservationView.vue'),
path: "/reservation",
name: "預(yù)約記錄",
}]
},
{
id: 8,
pid: 0,
path: '/hospital',
name: '住院',
children: [{
id: 9,
pid: 8,
redirect: null,
component: () => import('@/views/hospitalView.vue'),
path: "/hospitalpay",
name: "住院繳費(fèi)記錄",
},{
id: 10,
pid: 8,
redirect: null,
component: () => import('@/views/roomView.vue'),
path: "/room",
name: "房間管理",
},{
id: 11,
pid: 8,
redirect: null,
component: () => import('@/views/inpatientView.vue'),
path: "/inpatient",
name: "住院人",
}]
}
];步驟三:模擬后端權(quán)限數(shù)據(jù)
為了演示,我們使用模擬數(shù)據(jù)來(lái)代表后端返回的權(quán)限信息。
然后是后端接口返回的數(shù)據(jù),我就先用假數(shù)據(jù)進(jìn)行代替了。配置的基礎(chǔ)路由和假數(shù)據(jù)要一致就行。假數(shù)據(jù)就是動(dòng)態(tài)路由(會(huì)發(fā)生改變的)
// 動(dòng)態(tài)訪問(wèn)
const dynamicRouteConfigs = [
// 動(dòng)態(tài)路由容器(登錄后內(nèi)容)
{
path: '/home',
name: '首頁(yè)',
id: 1,
pid: 0,
},
{
id: 2,
pid: 0,
redirect: null,
path: '/comp',
name: '門(mén)診',
children: [{
id: 3,
pid: 2,
redirect: null,
path: "/pay",
name: "門(mén)診繳費(fèi)記錄",
},{
id: 4,
pid: 2,
redirect: null,
path: "/reservation",
name: "預(yù)約記錄",
}]
},
{
id: 8,
pid: 0,
path: '/hospital',
name: '住院',
children: [{
id: 9,
pid: 8,
redirect: null,
path: "/hospitalpay",
name: "住院繳費(fèi)記錄",
},{
id: 10,
pid: 8,
redirect: null,
path: "/room",
name: "房間管理",
},{
id: 11,
pid: 8,
redirect: null,
path: "/inpatient",
name: "住院人",
}]
},
{
id: 12,
pid: 0,
redirect: null,
path: '/userinfor',
name: '用戶管理',
children: [{
id: 13,
pid: 12,
redirect: null,
path: "/user",
name: "用戶管理",
},{
id: 14,
pid: 12,
redirect: null,
path: "/role",
name: "角色管理",
},{
id: 15,
pid: 12,
redirect: null,
path: "/admin",
name: "管理員管理",
},{
id: 27,
pid: 12,
redirect: null,
path: "/visit",
name: "就診卡",
}]
},
{
id: 16,
pid: 0,
path: '/department',
name: '科室管理',
children: [{
id: 24,
pid: 16,
redirect: null,
path: "/departments",
name: "科室管理",
}]
},
{
id: 17,
pid: 0,
path: '/hospitals',
name: '醫(yī)院信息',
redirect: null,
children: [{
id: 18,
pid: 17,
redirect: null,
path: "/hospitalList",
name: "醫(yī)院信息",
},{
id: 19,
pid: 17,
redirect: null,
path: "/doctor",
name: "醫(yī)生管理",
},{
id: 20,
pid: 17,
redirect: null,
path: "/scheduling",
name: "醫(yī)生排班管理",
},{
id: 21,
pid: 17,
redirect: null,
path: "/label",
name: "標(biāo)簽管理",
},{
id: 22,
pid: 17,
redirect: null,
path: "/drug",
name: "藥品管理",
},
{
id: 23,
pid: 17,
redirect: null,
path: "/physical",
name: "體檢管理",
},{
id: 26,
pid: 17,
redirect: null,
path: "/trends",
name: "醫(yī)院動(dòng)態(tài)",
}]
}
];步驟四:創(chuàng)建路由實(shí)例
使用Vue Router創(chuàng)建路由實(shí)例,并初始化注冊(cè)基礎(chǔ)路由。
// 創(chuàng)建路由實(shí)例
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: allRoutes
})步驟五:篩選匹配路由
編寫(xiě)一個(gè)函數(shù)來(lái)篩選出匹配的路由。這個(gè)函數(shù)將遍歷后端返回的權(quán)限數(shù)據(jù)和完整路由數(shù)據(jù),找出匹配的路由并添加組件。
- 然后就進(jìn)行改變判斷,通過(guò)遍歷兩個(gè)數(shù)組,判斷兩者的name或者path相同的時(shí)候?qū)⑵浞湃胍粋€(gè)新數(shù)組中。
- 然后再聲明一個(gè)變量等于component,就是添加基礎(chǔ)路由的component組件
const filterRoutes = (dynamicRouteConfigs, totalRoute) => {
// 打印動(dòng)態(tài)路由配置
console.log('動(dòng)態(tài)路由', dynamicRouteConfigs);
// 打印總路由
console.log('基礎(chǔ)路由', totalRoute);
// 定義一個(gè)遞歸函數(shù)findMatchedRoute,用于在路由數(shù)組中查找匹配的目標(biāo)路由
const findMatchedRoute = (routes, target) => {
// 遍歷路由數(shù)組
for (const route of routes) {
// 如果路由名稱(chēng)在排除列表中,則跳過(guò)
if (['404', 'home'].includes(route.name)) continue;
// 如果路由名稱(chēng)或路徑與目標(biāo)匹配,則返回該路由
if (route.name === target.name || route.path === target.path) {
return route;
}
// 如果當(dāng)前路由有子路由,則遞歸查找匹配的子路由
if (route.children) {
const matchedChild = findMatchedRoute(route.children, target);
// 如果找到匹配的子路由,則返回
if (matchedChild) return matchedChild;
}
}
// 如果沒(méi)有找到匹配的路由,則返回null
return null;
};
// 定義一個(gè)函數(shù)mergeRoutes,用于合并源路由和匹配的路由
const mergeRoutes = (source, target) => {
// 返回一個(gè)新的路由對(duì)象,包含源路由的所有屬性,以及匹配路由的component屬性
return {
...source,
component: target.component,
// 如果源路由有子路由,則遞歸合并子路由
children: source.children ? source.children.map(child => mergeRoutes(child, findMatchedRoute([target], child) || {})) : undefined,
// 可以在這里合并其他可能需要的內(nèi)容,例如meta屬性
};
};
// 映射動(dòng)態(tài)路由配置,對(duì)每個(gè)路由進(jìn)行匹配和合并操作
const result = dynamicRouteConfigs
.map(route => {
// 查找匹配的路由
const matchedRoute = findMatchedRoute(totalRoute, route);
// 打印匹配的路由
console.log(matchedRoute);
// 如果找到匹配的路由,則合并路由,否則返回null
return matchedRoute
? mergeRoutes(route, matchedRoute)
: null;
})
// 過(guò)濾掉結(jié)果中的null值
.filter(route => route !== null);
// 打印過(guò)濾后的路由數(shù)組
console.log('過(guò)濾后的路由數(shù)組:', result);
// 返回過(guò)濾后的路由數(shù)組
return result;
};
步驟六:動(dòng)態(tài)添加路由
編寫(xiě)一個(gè)函數(shù)來(lái)動(dòng)態(tài)添加篩選后的路由到基礎(chǔ)路由的layout容器中。
然后后臺(tái)控制路由就完成了。主要和起那段控制是一樣的,都是需要一個(gè)基礎(chǔ)的路由數(shù)組,然后我們將數(shù)組對(duì)比過(guò)濾過(guò)之后,需要把過(guò)濾好的數(shù)組添加注冊(cè)到路由中。邏輯都是一樣的,疏通邏輯就可以了。
只不過(guò)前后端控制路由的接口返回不一樣而已。
// 動(dòng)態(tài)添加路由到layout
const addDynamicRoutes = (roles) => {
// 清空已有動(dòng)態(tài)路由
const layout = router.getRoutes().find(r => r.name === 'layout')
layout.children.forEach(child => {
router.removeRoute(child.name)
})
// 過(guò)濾并添加新路由
const allowedRoutes = filterRoutes(dynamicRouteConfigs, allRoutes);
allowedRoutes.forEach(route => { router.addRoute('layout', route); });
console.log(allowedRoutes);
sessionStorage.setItem('menuPath',JSON.stringify(allowedRoutes));//存儲(chǔ)的篩選過(guò)的動(dòng)態(tài)路由
// 確保404最后處理
router.addRoute({
path: '/:pathMatch(.*)*',
redirect: '/404'
})
}步驟七:存儲(chǔ)和渲染菜單
將篩選后的路由存儲(chǔ)在會(huì)話存儲(chǔ)中,并在菜單頁(yè)面進(jìn)行渲染。
通過(guò)以上步驟,我們完成了基于Vue的后臺(tái)管理系統(tǒng)權(quán)限控制與動(dòng)態(tài)路由的設(shè)置。不同角色的用戶將看到不同的菜單和頁(yè)面,實(shí)現(xiàn)了權(quán)限的精細(xì)化管理。
最后路由導(dǎo)航守衛(wèi)加上去就ok了。
// 路由守衛(wèi)修改部分(router/index.ts)
router.beforeEach(async (to, from, next) => {
const store = loginStore()
const isLogin = !!store.id
// 未登錄狀態(tài)處理
if (!isLogin) {
return to.path === '/' ? next() : next('/')
}
// 已登錄但訪問(wèn)登錄頁(yè)時(shí)重定向
if (to.path === '/') {
return next('/home')
}
// 動(dòng)態(tài)路由加載邏輯
if (!store.routesLoaded) {
try {
// 直接從store獲取已保存的角色信息
const userRoles = store.roles;
// 如果角色信息不存在則拋出錯(cuò)誤
if (!userRoles || userRoles.length === 0) {
throw new Error('用戶角色信息未獲取')
}
// 添加動(dòng)態(tài)路由
addDynamicRoutes(dynamicRouteConfigs)
// 在添加路由后打印
console.log('當(dāng)前所有路由:', router.getRoutes().map(r => r.path))
// 更新加載狀態(tài)
store.setRoutesLoaded(true)
// 使用replace方式跳轉(zhuǎn)避免重復(fù)記錄歷史
return next({ ...to, replace: true })
} catch (error) {
console.error('路由加載失敗:', error)
// 清除用戶狀態(tài)并跳轉(zhuǎn)登錄頁(yè)
store.$reset()
return next('/')
}
}
next();
})總結(jié)
本文介紹了如何使用Vue和Vue Router實(shí)現(xiàn)后臺(tái)管理系統(tǒng)的權(quán)限控制。通過(guò)動(dòng)態(tài)生成路由,我們可以根據(jù)用戶的角色靈活控制頁(yè)面訪問(wèn)權(quán)限,提高系統(tǒng)的安全性和用戶體驗(yàn)。
到此這篇關(guān)于Vue后臺(tái)管理系統(tǒng)權(quán)限控制與動(dòng)態(tài)路由的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue 權(quán)限控制與動(dòng)態(tài)路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
極速上手 VUE 3 teleport傳送門(mén)組件及使用語(yǔ)法
teleport 傳送門(mén)組件,提供一種簡(jiǎn)潔的方式,可以指定它里面的內(nèi)容的父元素,也就是說(shuō)teleport 中的內(nèi)容允許我們控制在任意的DOM中,使用簡(jiǎn)單,對(duì)VUE 3 teleport傳送門(mén)相關(guān)知識(shí)感興趣的朋友一起看看吧2021-10-10
使用Webstorm調(diào)試Vue代碼詳細(xì)圖文教程
WebStorm是一款優(yōu)秀的前端開(kāi)發(fā)IDE,之前一直可以調(diào)試Vue項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于使用Webstorm調(diào)試Vue代碼的詳細(xì)圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
vue發(fā)布到nginx下請(qǐng)求后臺(tái)404問(wèn)題及解決
這篇文章主要介紹了vue發(fā)布到nginx下請(qǐng)求后臺(tái)404問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Vue3中使用vue3-print-nb實(shí)現(xiàn)打印功能
這篇文章主要為大家詳細(xì)介紹了Vue3中如何使用vue3-print-nb實(shí)現(xiàn)打印功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起參考一下2025-02-02
Vue實(shí)現(xiàn)簡(jiǎn)單網(wǎng)頁(yè)計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)簡(jiǎn)單網(wǎng)頁(yè)計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04

