之前写了一篇 《vue 按钮级别权限控制实现》,里面介绍了前端页面对于按钮权限的控制,这一篇来说说前端页面对于菜单权限的控制。
前端菜单权限,就是根据用户的权限不同控制菜单的显示隐藏,不同的场景,实现的方式也有所不同。
一、前端控制路由
前端配置好一套通用路由表一套动态路由表,然后获取用户权限数据,根据权限数据对比动态路由表,生成具有用户权限的新路由表,然后使用 router.addRoutes方法将新路由表动态挂载到 router 上,最后根据路由表渲染菜单。
实现如下:
1、在 route.js 里面配置通用的路由(404,login 等)。
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
const router = new Router({
mode: 'hash',
routes:[{
path:'/',
redirect:'/login'
},{
path:'/login',
name:'login',
component: () => import('../view/login')
},{
path:'/404',
name:'404',
component: () => import('../view/404')
}]
})
export default router;
2、新建一个 routerList.js 文件,里面配置好动态路由表;通过 isShow 字段控制菜单的显示隐藏。
const routerList = [{
path:'/',
name:'home',
component: () => import('../view/index.vue'),
meta:{
title:'Home'
},
children:[{
path:'menu1',
name:'menu1',
isShow:true,
component: () => import('../view/menu1.vue'),
meta:{
title:'菜单1',
icon:'el-icon-info'
}
},{
path:'menu2',
name:'menu2',
isShow:true,
component: () => import('../view/menu2.vue'),
meta:{
title:'菜单2',
icon:'el-icon-info'
}
},{
path:'menu3',
name:'menu3',
isShow:true,
component: () => import('../view/menu3.vue'),
meta:{
title:'菜单3',
icon:'el-icon-info'
}
}]
}
]
3、获取接口返回权限数据。
//以数组为例
['menu1','menu3']
4、根据这个数组来处理已有的动态路由表,将数组里没有的菜单对应动态路由表里的路由的 isShow 属性值改为 false;生成的新路由表保存到 vuex 和 localStorage 里面。
const routerList = [{
path:'/',
name:'home',
component: () => import('../view/index.vue'),
meta:{
title:'Home'
},
children:[{
path:'menu1',
name:'menu1',
isShow:true,
component: () => import('../view/menu1.vue'),
meta:{
title:'菜单1',
icon:'el-icon-info'
}
},{
path:'menu2',
name:'menu2',
isShow:false,
component: () => import('../view/menu2.vue'),
meta:{
title:'菜单2',
icon:'el-icon-info'
}
},{
path:'menu3',
name:'menu3',
isShow:true,
component: () => import('../view/menu3.vue'),
meta:{
title:'菜单3',
icon:'el-icon-info'
}
}]
}
]
4、在 App.vue 文件的 created 生命周期使用 addRouters 方法将通过权限数据处理过的最新路由表加入到 router 里面去。
//处理好数据之后
this.$router.options.routes = routerList;
this.$router.addRoutes(routerList);
5、在路由切换时,通过路由守卫判断当前将要跳转的页面是否有权限,有正常跳转,没有则跳转到 404 页面。
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next();
} else if (to.path === '/404') {
next();
} else {
let resstr = ['menu1','menu3'];
if (resstr != 'null' && resstr != undefined) {
let hasPermiss = resstr.find(item => {
return item.includes(to.path.split('/')[to.path.split('/').length-1]);
})
if (hasPermiss) {
next();
} else {
next({
path: '/404'});
}
}
}
})
6、在菜单页面,使用 this.$router.options.routers 来获取最新路由对象,通过 v-for 循环渲染菜单栏,isShow 属性值为 false 的菜单不显示。
二、后端控制路由
路由表由后端维护好,通过接口前端拿到数据,然后使用 addRoutes方法添加到 router,最后根据路由表渲染菜单。
实现如下:
1、在 route.js 里面配置通用的路由(404,login 等)。
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
const router = new Router({
mode: 'hash',
routes:[{
path:'/',
redirect:'/login'
},{
path:'/login',
name:'login',
component: () => import('../view/login')
},{
path:'/404',
name:'404',
component: () => import('../view/404')
}]
})
export default router;
2、接口获取动态路由信息(与后端同时约定好数据的格式),这个时候只需要返回该用户有权限的路由表即可。
{
code:200
message:'查询成功'
success:true
data: {
router: [
{
path: "/",
component: "index",
name: "home",
meta: {
title: "Home"
},
children: [
{
path: "menu1",
name: "menu1",
component: "menu1",
meta: {
title: "菜单1",
icon: "el-icon-info"
}
}
]
}]
}
}
3、这里 component 是一个字符串,需要转换成路径,然后将处理好的数据保存到 vuex 和 localStorage 里面。
function filterRouter(routers) {
// 遍历后台传来的路由字符串,转换为组件对象
const accessedRouters = routers.filter(route => {
if (route.component) {
route.component = _import(route.component)
}
if (route.children && route.children.length) {
route.children = filterRouter(route.children)
}
return true
})
return accessedRouters
}
function _import (file) {
return () => import('../views/' + file)
}
4、在 App.vue 文件的 created 生命周期使用 addRoutes方法将通过权限数据处理过的最新路由表加入到 router 里面去。
//处理好数据之后
this.$router.options.routes = routerList;
this.$router.addRoutes(routerList);
5、在菜单页面,使用 this.$router.options.routers 来获取最新路由对象,通过 v-for 循环渲染菜单栏。
建议:处理好的路由表存到 localStorage 里的时候最好加密处理,防止用户通过本地修改。