哈嘍大家好,今天我們來探討vue開發(fā)中不可避免的動(dòng)態(tài)路由權(quán)限。尤其是開發(fā)后臺(tái)管理系統(tǒng),其中有很多的角色需要用來做判斷。不同的角色有著不同的權(quán)限。作為管理員還必須擁有修改這些權(quán)限的能力。
這樣的操作無疑會(huì)使得前端做路由的加載時(shí)顯得更為復(fù)雜。我司的角色有包括管理員在內(nèi)總共6種。所以我將自己在開發(fā)過程中遇到的問題整理起來。
僅做參考,希望對(duì)大家有幫助!
思路
1.先從最高權(quán)限下手,把項(xiàng)目所有需要做動(dòng)態(tài)分配的路由都保存到后臺(tái)服務(wù)器。(可能會(huì)有人說放在后端就遏制住前端的處理了,但是根據(jù)我們目前的情況,放在后臺(tái)更方便處理)。
2.利用后臺(tái)存放的數(shù)據(jù),做成菜單樹。然后給每一個(gè)想要獲取權(quán)限的角色分配權(quán)限,達(dá)到每一個(gè)角色都擁有自定義權(quán)限。
3.給創(chuàng)建成的不同用戶分配角色,達(dá)到權(quán)限的動(dòng)態(tài)分配。
我司的處理情況特殊,也就是給不同的角色來劃分權(quán)限
4.當(dāng)用戶登錄時(shí),獲取當(dāng)前用戶所在角色組,利用角色返回配置的路由信息,然后將信息加載到vue-router中,實(shí)現(xiàn)不同用戶||角色登錄進(jìn)系統(tǒng)后,顯示不同的路由菜單。
業(yè)務(wù)代碼
1.用戶登錄以后,在cookie存放后臺(tái)返回的Token||sessionID。
login(userInfo).then(res => {
if (res.code == 600) {
resolve(res)
return
}
commit('SET_TOKEN', res.res);
setToken(res.res);
resolve(res)
})
2.在路由守衛(wèi)中,我們做判斷。如果有我們要的cookie,那么就讓繼續(xù)加載路由。處理相關(guān)事宜,這塊如有不懂。后期我會(huì)單獨(dú)抽出來做一次vue路由守衛(wèi)的理解。
// 確定用戶是否已登錄
const hasToken = store.getters.token;
if (hasToken) {
if (to.path === '/login') {
// 如果已登錄,則重定向到主頁
next({
path: '/'
})
NProgress.done()
} else {
// 確定用戶是否通過getInfo獲得了他的權(quán)限角色
const hasRoles = store.getters.roles && store.getters.roles.length > 0;
if (hasRoles) {
next()
} else {
try {
/*
主要的操作在第三步
*/
} catch (error) {
// 刪除token,進(jìn)入登錄頁面重新登錄
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* 沒有token */
if (whiteList.indexOf(to.path) !== -1) {
// 在免費(fèi)登錄白名單,直接去查看
next()
} else {
// 沒有訪問權(quán)限的其他頁面被重定向到登錄頁面。
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
3.當(dāng)路由守衛(wèi)判斷這是第一次進(jìn)入系統(tǒng),沒有角色。所以就要開始獲取角色。利用在vuex中寫的異步方法請(qǐng)求到這一角色信息,以及對(duì)應(yīng)的路由信息。
getInfo({
commit,
state
}) {
return new Promise((resolve, reject) => {
getInfo().then(e => {
// 因?yàn)檫@里測(cè)試時(shí)只返回了路由信息,我在這里自定義了roles和name.
var data = {
roles: ['admin'],
name: '長得丑活得久,長得帥老的快',
list: e.res
}
const {
roles,
name,
} = data;
commit('SET_ROLES', roles)
commit('SET_NAME', name)
resolve(data)
})
})
}
獲取當(dāng)前角色的信息,并拋出
↓↓↓↓↓ 需要放入第二步空出來的區(qū)域
// 獲取路由列表
const {
list
} = await store.dispatch('user/getInfo');
//如果返回的數(shù)據(jù)列表為空,讓重新登錄
if (list == undefined) {
await store.dispatch('user/resetToken')
next(`/${to.path}`)
NProgress.done()
} else {
const accessRoutes = await store.dispatch('permission/generateRoutes', list);
// 根據(jù)角色生成可訪問路由映射
// 動(dòng)態(tài)添加可訪問路由
router.addRoutes(accessRoutes)
// 設(shè)置replace: true,這樣導(dǎo)航就不會(huì)留下歷史記錄
next({
...to,
replace: true
})
}
此時(shí)拋出的data被獲取到,我們拿到想要的list。通過已經(jīng)定義好的遞歸方法,把我們傳進(jìn)去的list生成vue的路由,并將生成的數(shù)據(jù)返回。我們使用router.addRoutes()方法將路由加載。
4.接第三步路由信息的處理方法。
import {
constantRoutes
} from '@/router'
import Layout from '@/layout'
/**
* 加載其他路由
* @param {Object} route
*/
function makeOtherRouter(route) {
var res = [];
route.children.forEach(i => {
const item = {
...i
}
var Unified = item.router.split('/').pop();
var next = {
path: Unified,
component: () => import(`@/views${item.router}`),
name: Unified,
meta: {
title: Unified
}
};
// 是否可見
if (item.visible == '0') {
next.hidden = true;
}
// 子路由
if (item.children) {
next = {};
next = {
path: Unified,
component: () => import(`@/views${item.router}/index`),
name: Unified,
meta: {
title: Unified
}
};
if (item.children.length == 1) {
next.alwaysShow = true
}
next.children = [];
next.children = makeRouter(item)
}
res.push(next)
})
return res;
}
//加載第一層路由
export function makeFirstRouter(routes) {
const res = [];
routes.forEach(route => {
const tmp = {
...route
}
var Unified = tmp.router.split('/').pop();
if (tmp.icon) {
var first = {
path: tmp.router,
component: Layout,
name: Unified,
meta: {
title: Unified,
icon: tmp.icon
}
}
}
// 第一級(jí)路由是否可見
if (tmp.visible == '0') {
first.hidden = true;
}
if (tmp.children) {
first.children = [];
first.children = makeOtherRouter(tmp)
}
if (tmp.children.length == 1) {
first.alwaysShow = true;
}
res.push(first)
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({
commit
}, list) {
return new Promise(resolve => {
let accessedRoutes = makeFirstRouter(list);
//異步路由要把404放在最后加載。
accessedRoutes.push({
path: '*',
redirect: '/404',
hidden: true
})
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
此時(shí)我們的路由就已經(jīng)處理完成。大致的流程走到這里就結(jié)束了。因?yàn)閿?shù)據(jù)為公司內(nèi)部數(shù)據(jù)在這里就沒有引用真實(shí)的數(shù)據(jù),所以只是用語言組織的。
后面如果找到合適的數(shù)據(jù)會(huì)再次提出來,希望對(duì)正在處理這個(gè)問題的朋友提供一點(diǎn)點(diǎn)思路和幫助。希望支持~
未完待續(xù)!
- 本人博客地址:https://reinness.com 站點(diǎn)名稱:平凡的你我。 歡迎大家的到來!