手把手教學vue的路由權限問題
后臺管理類系統(tǒng)大多都涉及權限管理,菜單權限,按鈕權限。
菜單權限
菜單權限對應 - 路由。菜單權限 - 根據(jù)用戶角色不同,路由文件動態(tài)配置。
相關知識點了解
vue-router是vue項目在進行開發(fā)過程中必不可少缺少的插件,目前vue2依賴的是vue-router3,vue3依賴的vue-router4
在進行權限控制之前一定要了解哪些路由需要權限哪些不需要
知識點集結(jié)
router.addRoutes()
動態(tài)添加更多的路由規(guī)則。參數(shù)必須是一個符合 routes 選項要求的數(shù)組。
已廢棄目前版本再使用該api會被提示已經(jīng)廢棄,但是暫時依舊可以使用
router.addRoutes(routes: Array<RouteConfig>)
router.addRoute()
添加一條新路由規(guī)則。如果該路由規(guī)則有 name,并且已經(jīng)存在一個與之相同的名字,則會覆蓋它。
addRoute(route: RouteConfig)
router.getRoutes()
獲取所有活躍的路由記錄列表。注意只有文檔中記錄下來的 property 才被視為公共 API,避免使用任何其它 property,例如 regex,因為它在 Vue Router 4 中不存在。
路由導航守衛(wèi) - beforeEach
router.beforeEach((to, from, next) => {
/* 必須調(diào)用 `next` */
})全局前置守衛(wèi) - 跳轉(zhuǎn)一個路由之前都會執(zhí)行.
3個參數(shù):
to: 即將進入的目標的路由對象from:當前導航正在離開的路由next: 是個函數(shù),進入下一個鉤子
next(): 進行管道中的下一個鉤子。如果全部鉤子執(zhí)行完了,則導航的狀態(tài)就是 confirmed (確認的)。next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。next('/')或者next({ path: '/' }): 跳轉(zhuǎn)到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。next(error): (2.4.0+) 如果傳入 next 的參數(shù)是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調(diào)。
功能實現(xiàn)過程
路由權限第一種可以是后端全部返回,直接調(diào)用接口使用后端的可以使用的路由,但是這種情況一般較少。
第二種前端有一個份完整路由,根據(jù)后端接口進行篩選,這里講解第二種情況。
(1)定義路由文件
router -> index.js
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'
Vue.use(Router)
//沒有權限的路由
export const constantRoutes = [
{ path: '/', name: 'index', redirect: '/login' },
{ path: '/login', name: 'login', component: () => import('@/views/login') },
{ path: '/register', name: 'register', component: () => import('@/views/register') },
{ path: '/forget', name: 'forget', component: () => import('@/views/forget') },
{ path: '/404', name: 'notfing', component: () => import('@/views/404')}
]
//有權限的路由
export const myAsyncRoute = [{
path: '/portal',
name: 'portal',
component: LayoutPortal,
redirect: '/portal/home',
meta: {id: 'no'},
children: [
{
path: '/portal/home',
name: 'portal-home',
component: () => import('@/views/portal/home/index.vue'),
meta: {id: 100100, title: '首頁', show: true}
},
{
path: '/portal/user',
name: 'portal-user',
component: () => import('@/views/layout/submenu'),
meta: {id: 100200, title: '統(tǒng)一身份認證', show: true},
redirect: '/portal/user/userInfo',
children: [
{
path: '/portal/user/userInfo',
name: 'portal-user-userInfo',
component: () => import('@/views/portal/userInfo/index.vue'),
meta: {id: 100201, title: '統(tǒng)一用戶管理', show: true}
},
{
path: '/portal/user/userInfo/detial',
name: 'portal-userInfo-detial',
component: () => import('@/views/portal/userInfo/detial.vue'),
meta: { id: 100201, activeMenu: '/portal/user/userInfo', title: '統(tǒng)一用戶管理', show: false},
},
{
path: '/portal/user/userAuth',
name: 'portal-user-userAuth',
component: () => import('@/views/portal/userAuth/index.vue'),
meta: {id: 100202, title: '用戶認證', show: true}
},
]
},
{
path: '/portal/journal',
name: 'portal-journal',
component: () => import('@/views/portal/journal/index.vue'),
meta: {id: 100303, title: '統(tǒng)一日志管理', show: true},
},
{
path: 'personal',
name: 'portal-personal',
component: () => import('@/views/portal/personal/index.vue'),
meta: {
id: 'no',
activeMenu: '/portal/home',
show: false
},
},
],
}]
export default new Router({
routes: constantRoutes,
})(2)注冊路由
main.js
import router from './router'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})正常注冊完路由就可以開始進行權限設置了
(3)獲取有權限路由
不同的項目獲取路由權限并不相同,大多數(shù) - 登錄接口返回/單獨接口進行獲取
登錄獲取權限保存
let res = await this.$api.user.login(data);
if(res.token) {
this.$store.commit('setToken', res.token);
this.$store.commit('setUsername', res.nickName);
this.$store.commit('setUserInfo', res);
//存儲權限
localStorage.setItem('menuIdList', JSON.stringify(res.menuIdList))
this.$message({
message: '登錄成功',
type: 'success'
});
setTimeout(() => {
this.loading = false;
this.$router.push('/portal')
}, 1000);
}在main.js中攔截
獲取權限進行匹配 - beforeEach一定要有一個next()的出口,不然會陷入死循環(huán)
let flag = true;
router.beforeEach((to, from, next) => {
if (['/login', '/forget', '/register'].includes(to.path)) {
next();
} else {
let token = localStorage.getItem("token");
if (token) {
if(flag) {
try {
//獲取有權限的路由進行組裝
let route = asyncRoute() || [];
router.addRoutes(route)
router.addRoute({
path: '*',
redirect: '/404'
})
flag = false
next({...to, replace:true})
}catch(e) {
next('/login')
}
}else {
next();
}
}else {
next({ path: '/login' })
}
}
}
);注意: addRoute之后,打印router是看不見的,要獲取所有的權限,必須使用router.getRoute()進行查看
(4)路由權限匹配
router.js-根據(jù)后端返回的權限,和自己的路由權限匹配,組織成新的路由,和自己想要的格式
export const asyncRoute = function() {
let menuIdList = localStorage.getItem('menuIdList') ? JSON.parse(localStorage.getItem('menuIdList')) : [];
let tempArr = filterRoute(myAsyncRoute, menuIdList);
store.dispatch('getRoute', tempArr)
let showRoute = [];
let nowRoute =JSON.parse(JSON.stringify(tempArr))
if(nowRoute[0].children && nowRoute[0].children.length){
nowRoute[0].children.forEach(item => {
let arr = [];
if(item.children && item.children.length){
arr = item.children.filter(obj => obj.meta.show)
}
if(item.meta.show){
item['showRouteChildren'] = arr;
showRoute.push(item)
}
})
}
store.dispatch('getShowRoute', showRoute)
return tempArr
}
function filterRoute(arr, menuIdList) {
if(!arr.length) return [];
return arr.filter(item => {
if(item.children && item.children.length) {
item.children = filterRoute(item.children, menuIdList);
}
return (item.meta && item.meta.id && menuIdList.includes(item.meta.id)) || (item.meta && item.meta.id == 'no') || (item.children && item.children.length > 0)
})
}在這個過程中,在store存儲了路由
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
routes: JSON.parse(localStorage.getItem('routes')) || [],
addRoutes: JSON.parse(localStorage.getItem('addRoutes')) ||[],
showRoutes: []
},
mutations: {
setRoutes(state, routes) {
state.addRoutes = routes;
state.routes = constantRoutes.concat(routes)
localStorage.setItem('routes', JSON.stringify(state.routes))
localStorage.setItem('addRoutes', JSON.stringify(state.addRoutes))
},
setShowRoutes(state, routes) {
state.showRoutes = routes;
}
},
actions: {
getRoute({commit}, list) {
return new Promise(resolve => {
commit('setRoutes', list)
resolve(list)
})
},
getShowRoute({commit}, list) {
return new Promise(resolve => {
commit('setShowRoutes', list)
resolve(list)
})
}
}
})總結(jié): 最后在理一下整個過程 - 存儲權限路由數(shù)據(jù),在進行跳轉(zhuǎn)的時候進行篩選組合路由。其實篩選路由不一定要寫在router.js,只要是你認為合適的地方都可以,在權限控制過程中最重要的是路由攔截。
模擬一下路由攔截的過程
假設login之后進入的/index,路由攔截的過程
/index - > token -> flag(true) ->獲取路由權限 -> next('/index') -> 重新進入beforeEach-> token->flag(false) -> next() ->結(jié)束
按鈕權限 - 操作(自定義指令)
按鈕權限主要涉及的知識點就是全局自定義指令
寫在main.js?;蛘邌为歫s文件,main.js進行引入
import Vue from "vue"
import store from "../store"
//自定義指令 v-has進行權限判斷
Vue.directive("has",{
inserted : function (el,binding){
//按鈕權限
const data = store.state.buttons;
//按鈕的值 <el-button v-has>aa</el-button>
const value = binding.value; //a
const hasPermissions = data.includes(value);
if(!hasPermissions){
//隱藏按鈕
el.style.display = "none";
setTimeout(()=>{
el.parentNode.removeChild(el)
},0)
}
}
})總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Vue3+El-Plus實現(xiàn)表格行拖拽功能完整代碼
在vue3+elementPlus網(wǎng)站開發(fā)中,詳細完成el-table表格的鼠標拖拽/拖曳/拖動排序,下面這篇文章主要給大家介紹了關于Vue3+El-Plus實現(xiàn)表格行拖拽功能的相關資料,需要的朋友可以參考下2024-05-05
vue-quill-editor實現(xiàn)圖片上傳功能
這篇文章主要為大家詳細介紹了vue-quill-editor實現(xiàn)圖片上傳功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
vue實現(xiàn)拖動調(diào)整左右兩側(cè)容器大小
這篇文章主要為大家詳細介紹了vue實現(xiàn)拖動調(diào)整左右兩側(cè)容器大小,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
vue@cli3項目模板怎么使用public目錄下的靜態(tài)文件
這篇文章主要介紹了vue@cli3項目模板怎么使用public目錄下的靜態(tài)文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07

