vue实现页面权限显示_vue实现菜单权限控制
大家在做后臺管理系統(tǒng)時一般都會涉及到菜單的權限控制問題。當然解決問題的方法無非兩種——前端控制和后端控制。我們公司這邊的產(chǎn)品迭代速度較快,所以我們是從前端控制路由迭代到后端控制路由。下面我會分別介紹這兩種方法的優(yōu)缺點以及如何實現(xiàn)(不熟悉vue-router API的同學可以先去官網(wǎng)看一波API哈)。
我先簡單說下項目的需求:如下圖所示,有一級菜單和二級菜單,然后不同的人登錄進去會展示不同的菜單。
前端控制路由的思路:將所有的路由映射表都拿到前端來維護,就是我的router.js里面將所有的菜單path與對應的components全部寫進去,后面我會提到全部寫進去的弊端。然后我的左側菜單寫成一個組件(sidebar.vue),在這個組件里寫一份類似這樣的data數(shù)據(jù),然后通過登錄時獲取的level值來給data中固定的菜單加hidden,然后前端就根據(jù)hidden展示菜單。
//router.js 偽代碼
const Login= r => require.ensure([],()=>r(require('../page/login/Login.vue')),'login');
const Home= r => require.ensure([],()=>r(require('../page/Home.vue')),'home');
const Forbidden= r => require.ensure([],()=>r(require('../page/403.vue')),'forbidden');
const NotFound= r => require.ensure([],()=>r(require('../page/404.vue')),'notfound');
const Dashboard= r => require.ensure([],()=>r(require('../page/dashboard/Dashboard.vue')),'dashboard');
const SplashScreen= r => require.ensure([],()=>r(require('../page/splashScreen/SplashScreen.vue')),'splashScreen');
const AddSplashScreen= r => require.ensure([],()=>r(require('../page/splashScreen/AddSplashScreen.vue')),'addSplashScreen');
const routes=[
{
path:'/',
redirect:'/login'},{
path:'/login',
component: Login
},{
path:'/404',
component: NotFound
},{
path:'/home',
component: Home,
redirect:'/home/splashScreen',
children: [
{
path:'/home/splashScreen',
component: SplashScreen,
meta: {
title:'國服李白
}
},{
path:'/home/addSplashScreen',
component: AddSplashScreen,
meta: {
title: 國服呂布'}
}
]
}
];
下面是菜單組件的偽代碼
//sidebar.vue
...
data() {return{
routes: [
{
index:'1',
title:'國服打野',
icon:'iconfont icon-guanggao',
children: [
{
index:'splashScreen',
title:'李白',
children: []
},
]
},
{
index:'2',
title:'國服中單',
icon:'iconfont icon-tuisongguanli-',
}
]
}
},
methods: {
getLevel(){
const level= sessionStorage.getItem('level');if(level === '0'){this.routes.forEach(function(value){if(value.title == "國服上單"){
value.hidden= true;
value.children.forEach(function(value){if(value.title=="國服關羽"){
value.hidden= true;
}
})
}
})
}else if(level === '1'){this.routes.forEach(function(value){
value.hidden= truevalue.children.forEach(function(value){
value.hidden= true;
})
})
}
}
},
created(){this.getLevel();
}
}
雖然說這樣可以實現(xiàn)權限功能,但有兩個問題。
session里存的是level,我們可以打開瀏覽器控制臺人為控制level,這樣就失去了權限的意義。
我們?nèi)绻涀×藀ath,可以直接在瀏覽器網(wǎng)址欄中手動輸入path,然后回車就可以看到任何頁面。這也是前端router.js寫死所有路由的弊端。
在這里面前端只是通過后端傳回的level來給router顯示/隱藏,這樣前端維護整個路由是比較復雜的而且是有重大隱患的。
現(xiàn)在呢我們來講講后端控制路由。先從操作流程來說,我們這邊加入了一個dashboard中間頁,這個頁面只展示不同level下的一級路由,通過點擊相應的一級路由進到對應的Page頁面,該page頁面也只展示相對應的所有的二級路由。
這里面出現(xiàn)了兩個個新的概念叫 “動態(tài)添加路由”和“導航守衛(wèi)”,就是我前端router.js中只寫所有人可以訪問的路由表,比如login和404頁面等。其他所有的組件資源全部寫到一個新的components.js文件中,然后通過后端返回的menuData去映射符合components.js中的key,如果有對應的,就把它動態(tài)添加到router中,通過addRoutes添加。動態(tài)添加路由這個方法要寫到導航守衛(wèi)beforeEach這個鉤子函數(shù)中。導航守衛(wèi)的意思是我路由跳轉到下個頁面之前要做些什么。就是說我們登錄后會跳到dashboard頁面,在進到這個頁面之前我們需要將后端請求回來的menuData進行二次封裝,把他根據(jù)權限返回回來的data與我們前端components.js去做map匹配,將最終的數(shù)據(jù)通過addRoutes來push到我們的路由中,之后才能進到我們的dashborad頁面,再通過dashborad頁面進到對應的page頁面,就是說我們把所有的權限控制全在dashboard頁面進入之前就做完了。這里面還有一個小的優(yōu)化的點:當我們通過前面說的瀏覽器菜單欄訪問到非權限頁面或者不存在的頁面時,需要根據(jù)vue-router中的匹配優(yōu)先級來最后addRoutes 404和*這個頁面,這樣就可以直接到達404頁面而非空頁面。
//components.js 所有的頁面資源
const home = () => import('../page/Home.vue');
const splashScreen= () => import('../page/splashScreen/SplashScreen.vue');
const addSplashScreen= () => import('../page/splashScreen/AddSplashScreen.vue');
const editSplashScreen= () => import('../page/splashScreen/EditSplashScreen.vue');
exportdefault{
home,
splashScreen,
addSplashScreen,
editSplashScreen,
};
//router.js 看,只寫通用的頁面是不是很清爽
import Vue from 'vue';
import Router from'vue-router';
Vue.use(Router);
const Login= () => import('../page/login/Login.vue');
const Home= () => import('../page/Home.vue');
const Forbidden= () => import('../page/403.vue');
const Dashboard= () => import('../page/dashboard/Dashboard.vue');
const routes=[
{
path:'/',
redirect:'/login'},{
path:'/login',
component: Login
},{
path:'/403',
component: Forbidden
},
{
path:'/dashboard',
component: Dashboard,
},
];
exportdefault newRouter({
mode:'history',
routes: routes,
base: __dirname,
linkActiveClass:'link-active'})
//main.js 偽代碼 只保留具體的相關邏輯
import routeMap from './router/component.js';
const NotFound= () => import('./page/404.vue');
const formatRoutes= function(routes, routeData) {if (!routeData) {
routeData={
name:'home',
path:'/home',//組件匹配成功的話才可以訪問具體的頁面
component: routeMap['home'],
children: [],
};
}
routes.length&& routes.forEach(route =>{if(route.component) {
route.component=routeMap[route.component];
routeData.children.push({
path: route.path,
name: route.index,
component: route.component,
meta: {
title: route.title,
},
})
}if (route.children &&route.children.length) {
formatRoutes(route.children, routeData);
}
});returnrouteData;
};
let isFetchRemote= true;//使用鉤子函數(shù)對路由進行權限跳轉
router.beforeEach((to, from, next) =>{
const username= sessionStorage.getItem('username');if(!username && to.path !== '/login'){
next({path:'/login'});
}else if (isFetchRemote && to.path !== '/login') {
ajaxPost('/resourceAPI/getMenuData').then(res =>{if (res.status === 200 && res.data.errno === 0) {
isFetchRemote= false;
const menuData=res.data.result;
localStorage.setItem('menudata', JSON.stringify(menuData));
const routeData=formatRoutes(menuData);
resourceApp.$router.addRoutes([routeData].concat([
{name:'404',path:'/404',component:NotFound},
{path:'*',redirect: '/404'}]));
resourceApp.$router.push({
path: to.path,
query: to.query
});
}else{
isFetchRemote= true;
}
next();
})
.catch(err =>{
console.log(err);
});
}else{
next();
}
});
const resourceApp= newVue({
router,
render: h=>h(App)
}).$mount('#app');
//menuData請求數(shù)據(jù)//一級菜單與二級菜單的區(qū)別是一級菜單帶有component這個值,比如下面的短信管理就是只有一級菜單
{"errno": 0,"errmsg": "獲取權限成功","result": [
{"index": "1","title": "打野位置","icon": "iconfont icon-guanggao","children": [
{"index": "splashScreen","icon": "","title": "娜可露露","path": "/home/splashAdverse","component": "splashAdverse","isShow": true},
{"index": "addSplashScreen","icon": "","title": "李白","path": "/home/addAdverse","component": "addAdverse","isShow": false},
]
},
{"index": "message","title": "國服上單","icon": "iconfont icon-duanxinguanli","path": "/home/message","component": "message","children": [
{"index": "addMessage","title": "國服第一關羽","icon": "","path": "/home/addMessage","component": "addMessage","isShow": false}
]
}
]
}
而sidebar和dashboard這兩個組件都只需要通過session拿到后端的menudate就可以。
//dashboard 偽代碼
{{item.title}}
const routeArr= JSON.parse(localStorage.getItem('menudata'));this.navList =routeArr;
},
methods: {
goPage(item){//只有一級菜單
if(item.component){this.$router.push(item.path);
}else{//二級菜單的數(shù)據(jù)結構中只在children中有path
this.$router.push(item.children[0]['path']);
}
}
}
//sidebar 偽代碼
data() {return{
routes: [],
}
},
methods: {
bouncer(arr){return arr.filter(function(val){return !(!val || val === "");
});
}
},
created(){
const menuData= JSON.parse(localStorage.getItem('menudata'));//通過當前router的path來map對應的整個路由數(shù)組
let routes = menuData.map((item)=>{//只有一級路由
if(item.component && item.path == this.$route.path){
console.log(item)returnitem;
}else{if(item.children[0]['path'] == this.$route.path){
console.log(item)returnitem;
}
}
})//去掉數(shù)組中的undefined、null 等空值 假值
this.routes = this.bouncer(routes);
}
}
通過這種方式來控制權限,我們?nèi)绻跒g覽器控制臺改了session中的level或者在瀏覽器導航欄改path,都會回歸到導航守衛(wèi)中,就是發(fā)請求重新獲取menuData,當我addRoutes后如果沒有匹配到這個值就回到404,當然通過改level也不會達到修改權限的控制,因為我們是動態(tài)獲取路由,不是之前的前端控制路由。
目前為止,我感覺通過后端控制權限這種實現(xiàn)方式應該是最理想的一種吧,當然大家有更好的方法或者對此文有任何問題,歡迎大家留言哈。
總結
以上是生活随笔為你收集整理的vue实现页面权限显示_vue实现菜单权限控制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python数据处理不用编程_用Pyth
- 下一篇: python url编码 空格_使用请求