1.项目中要使用到权限管理及左侧菜单动态加载 基于此。
2.项目模板使用的是vue-admin-template
这个模板比较干净,只有框架的实现,要添加权限可以参考 github上vue-element-admin项目
首先路由页面router:
有2个参数
export const constantRouterMap = [] 为初始路由参数,如登录 首页 404等共有页面 不需要权限控制的路由
export const asyncRouterMap = []为动态路由 登录成功后 在router.beforeEach中根据后端权限 加载不同路由 已展示不同的左侧菜单
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress 页面导航
// 验证是否有权限
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/auth-redirect']//白名单
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === '/login') { //如果是进入登录页面 则不需要权限 直接进入
next({ path: '/' })
NProgress.done() // 页面导航结束
} else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(res => { // 拉取user_info
store.dispatch('GenerateRoutes', res).then(() => { // 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
} else {
// next()
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.perms, to.meta.perms)) {
next()
} else {
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
// 可删 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
下面为加载路由代码
import { asyncRouterMap, constantRouterMap } from '@/router'
// import store from '@/store'
/**
* 通过meta.role判断是否与当前用户权限匹配
* @param roles
* @param route
*/
function hasPermission(perms, route) { //判断是否有权限
if (route.meta && route.meta.perms) {
return perms.some(perm => route.meta.perms.includes(perm))
} else {
return true
}
}
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(routes, perms) { //过滤没有权限的列表 循环过滤 直到没有子路由
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(perms, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, perms)
}
res.push(tmp)
}
})
return res
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => { //保存动态路由时 将静态路由和动态路由合并
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const roles = data.roles
const perms = data.perms
let accessedRouters
if (perms) {
if (roles.includes(1)) { //如果未admin角色 加载所有动态路由
accessedRouters = asyncRouterMap
} else { //如果不是admin角色 则加载过滤后的动态路由
accessedRouters = filterAsyncRouter(asyncRouterMap, perms)
}
commit('SET_ROUTERS', accessedRouters) //保存路由
}
resolve()
})
}
}
}
export default permission
此时已经获得了想要的路由列表,定义一个获取路由的getter方法
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
userInfo: state => state.user.userInfo,
roles: state => state.user.roles,
perms: state => state.user.perms,
permission_routers: state => state.permission.routers, //路由列表
addRouters: state => state.permission.addRouters
}
export default getters
目录layout/sidebar/index.vue 这里是展示左侧列表的页面
<template>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="$route.path"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/> //加载这个路由就行
</el-menu>
</el-scrollbar>
</template>
<script>
import { mapGetters } from 'vuex'
import variables from '@/styles/variables.scss'
import SidebarItem from './SidebarItem'
import store from '@/store'
export default {
components: { SidebarItem },
created(){
this.perms = JSON.stringify(this.$store.state.user.perms);
},
data(){
return {
perms: ''
};
},
computed: {
...mapGetters([
'permission_routers',
'sidebar'
]),
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
},
methods: {
}
}
</script>
最后 来个router页面
{
path: '/customerInfo',
component: Layout,
meta: { title: '客户信息管理', perms: ['customerPage'] }, //perms是后端返回的权限标识 写到这里可以使其所有子类都加上权限 也可以分别给子类添加
children: [
{
path: '/customer',
name: 'customer', //name必须有 面包屑导航的显示
component: () => import('@/views/page/customerInfo/index'), //懒加载
meta: { title: '客户信息管理', icon: 'customerInfo' } //title左侧菜单与面包屑导航显示文字 icon为左侧菜单图标
},
{
path: '/customeraddressInfo',
hidden: true,
name: 'customeraddress',
component: () => import('@/views/page/customerInfo/customeraddress'),
meta: { title: '客户公司管理', icon: 'customerInfo' }
}
]
},
{
path: '/fleetGroup',
component: Layout,
meta: { title: '车队编组管理', perms: ['fleetgroupPage'] },
children: [
{
path: '',
name: 'fleet',
component: () => import('@/views/page/fleetGroup/index'),
meta: { title: '车队编组管理', icon: 'fleetGroup' }
}
]
},