根據(jù)登錄狀態(tài)和用戶的角色設(shè)置路由攔截是控制臺的基本功能,本文主要是對這部分內(nèi)容的總結(jié),閱讀這篇文章之后,你可以構(gòu)建一個基本功能完整的控制臺。
路由守衛(wèi)
登錄狀態(tài)的處理
用戶未登錄時,攔截用戶的所有路由,跳轉(zhuǎn)登錄頁面,而在登錄之后,用戶不應(yīng)該再進入登錄頁面,將頁面重定向到首頁
Const whiteList = [‘/login’] // 使用白名單放置不登錄也可以訪問的頁面,現(xiàn)在只有登錄頁
router.beforeEach((to, from, next) => {
const hasToken = getToken()
if (hasToken) {
if (to.path === ‘/login’) {
next({ path: ‘/‘ })
} else {
next()
}
} else {
if (whiteList) {
if (whiteList.includes(to.path)) {
next()
} else {
next('/login')
}
}
}
})
用戶角色的處理
一般我們項目中不但要根據(jù)用戶的登錄狀態(tài)限制路由,也存在根據(jù)用戶角色限制路由的場景。
首先要在路由的meta中設(shè)置roles屬性,roles設(shè)置為進入路由需要的用戶角色,然后再在路由跳轉(zhuǎn)時,添加用戶角色的處理邏輯:
const needRoles = to.meta && to.meta.roles && to.meta.roles.length > 0
if (needRoles) {
const hasRoles = store.state.user.roles.some(role => to.meta.roles.includes(role))
if (hasRoles) {
next()
} else {
next('/403')
}
} else {
next()
}
菜單欄實現(xiàn)
不同角色的用戶登錄控制臺,因為角色擁有的權(quán)限不同我們應(yīng)該展示給用戶不同的菜單,在實現(xiàn)路由守衛(wèi)的時候,我們在路由的meta上通過設(shè)置roles屬性限制了不同角色的權(quán)限,這個權(quán)限的限制與菜單欄的顯示權(quán)限的限制應(yīng)該是一致的。所以我們可以使用路由和獲取的用戶角色來生成菜單欄菜單。
/**
* 根據(jù)路由meta.role確定是否當前用戶擁有訪問權(quán)限
* @roles 用戶擁有角色
* @route 待判定路由
*/
function hasPermission (roles, route) {
// 如果當前路由有roles字段則需判斷用戶訪問權(quán)限
if (route.meta && route.meta.roles) {
// 若用戶擁有的角色中有被包含在待判定路由角色表中的則擁有訪問權(quán)
return roles.some(role => route.meta.roles.includes(role))
} else {
// 沒有設(shè)置roles則無需判定即可訪問
return true
}
}
/**
* 遞歸過濾AsyncRoutes路由表
* @routes 待過濾路由表,首次傳入的就是AsyncRoutes
* @roles 用戶擁有角色
*/
export function filterAsyncRoutes (routes, roles) {
const res = []
routes.forEach(route => {
// 復(fù)制一份
const tmp = { ...route }
// 如果用戶有訪問權(quán)則加入結(jié)果路由表
if (hasPermission(roles, tmp)) {
// 如果存在子路由則遞歸過濾之
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
filterAsyncRoutes(asyncRoutes, state.roles)
我們使用roles過濾生成新的路由后會發(fā)現(xiàn),生成的路由直接作為菜單項還是存在一些問題,例如數(shù)據(jù)詳情頁面往往是不在菜單欄中展示的,所以過濾后的路由直接作為菜單項依舊不是很合適,所以需要再進行處理,通過路由的hidden屬性再次進行過濾。
const filterMenu = (route) => {
const children = route.children ? route.children.filter(item => !item.hidden) : []
if (children.length === 0) {
return {
icon: route.meta.icon, name: route.name, title: route.meta.title
}
}
if (children.length === 1) {
// 單只有一個子菜單的時候,子菜單會被提升
return filterMenu(children[0])
}
if (children.length > 1) {
return {
icon: route.meta.icon, name: route.name, title: route.meta.title, subs: children.map(filterMenu)
}
}
}
請求處理
發(fā)送請求也是項目中必不可少的的功能,在項目中我們通常使用axios來發(fā)送請求,針對請求我們在請求發(fā)送之前和接受請求時進行處理。
http.interceptors.request.use(
config => {
config.headers['token'] = getToken()
return config
},
error => {
return Promise.reject(error)
}
)
http.interceptors.response.use(
response => {
if (response.status !== 200) {
return Promise.reject(response)
}
// 處理狀態(tài)碼
const data = response.data
if (data.code === 10000) {
// setToken(cookies)
return data
}
// 1006為用戶未登錄
if (data.code === 10006) {
MessageBox.alert('登錄狀態(tài)異常,請重新登錄', '確認登錄信息', {
confirmButtonText: '重新登錄',
type: 'warning',
callback: () => {
removeToken()
router.replace({ name: 'login' })
}
})
return Promise.reject(new Error(data))
}
// 其他錯誤碼,提示信息并返回信息
Message({
message: data,
type: 'error',
duration: 3 * 1000
})
return Promise.reject(new Error(data))
},
error => {
Message({
message: error.message,
type: 'error',
duration: 3 * 1000
})
return Promise.reject(error)
}
)
這里主要是對返回的結(jié)果進行處理,根據(jù)不同的錯誤類型進行分類處理,比較要代表性的是登錄狀態(tài)失效的處理,和其他錯誤不同,登錄失效會直接讓用戶進入登錄頁面。
其他
- 菜單欄的收起與展開:在store的state中添加collapse進行控制,在Header組件中根據(jù)頁面寬度進行collapse的初始設(shè)置。
if (document.body.clientWidth < 980) {
this.$store.commit('changeCollapse', true)
}
- 標簽頁的控制:標簽頁的標簽頁列表也放到store中,這樣用戶在頁面里需要關(guān)閉標簽頁時,就可以調(diào)用方法進行關(guān)閉了。