在我们做一个管理系统的时候,一般比较重要也是最基础的地方就是权限管控这部分。
权限管理在来说基本有两个方面:
一、控制页面级访问权限;
页面访问权限控制实质上是控制页面是否显示,这里有两种方法:
1、添加所有路由和菜单,当用户在浏览器输入菜单路径时,不在权限内的显示无权限;
2、动态添加只在权限内的菜单和路由,当用户输入url绕过菜单时,显示404页面,提示无权限。
二、控制数据操作权限。
比如页面上的增删改查等一系列按钮的控制。
当然做权限管控,一般我们都会有专门的权限维护页面,包括菜单、角色、用户管理。
本文一切从简,就不详细介绍维护页面了,这里mock出所需的经过分配权限后的菜单数据如下:
// 正常来说这里是后台根据权限维护页,返回给前台的数据
// menuName 是菜单名
// menuUrl 是菜单对应的路由
// buttonList 是按钮的集合,按钮分为两种,1.点击按钮跳转到一个页面(需要添加路由) 2.点击按钮做数据操作(不需添加路由,menuUrl为空)
export const navData = [
{
menuName: '系统管理',
childrenMenu: [
{
menuName: '消息管理',
menuUrl: 'xxxx/xxxxx'
// 此处菜单添加按钮
buttonList: [
{
menuName: '新增消息',
menuUrl: ''
},
{
menuName: '查询',
menuUrl: ''
}
]
},
{
menuName: '数据管理',
menuUrl: 'xxxx/xxxx'
}
]
},
{
menuName: '任务计算',
menuUrl: 'xxxxxxxxx/xxxxxx/xxxx',
// 此处菜单添加按钮
buttonList: [
{
menuName: '新建',
menuUrl: 'xxxxxx'
}
]
},
{
menuName: '任务发布',
childrenMenu: [
{
menuName: '定时发布',
menuUrl: 'xxxxx'
},
{
menuName: '立即发布',
menuUrl: 'xxxx'
},
{
menuName: '发布历史',
menuUrl: 'xxxx'
}
]
},
{
menuName: '任务更新',
menuUrl: 'xxxx',
// 此处菜单添加按钮
buttonList: [
{
menuName: '新建',
menuUrl: 'xxxx'
}
]
}
]
mock出的数据只是一种,实际数据是根据分配的权限不一样,后台返回的不一样的。
根据element-ui的菜单组件,改写项目的菜单组件。(本文主要讲权限控制这里就不详细说明了)
首先我们构思是在构建vue对象的时候,去调取后台查询菜单权限的接口,然后把获取的菜单存放在vuex中,最后经过处理成router,用router.addRoutes方法去动态添加路由。然后根据菜单id,去查询每个页面的按钮权限,通过自定义指令控制按钮的显示隐藏。
1.首先在vuex中构建menu.js
import http from '@/js/http.js'
import apiPath from '@/api/api-path'
const menu = {
namespaced: true,
state: {
menus: [],
permissionButton: []
},
mutations: {
update (state, data) {
state.menus= data
},
clear (state) {
state.menus= []
},
changButtonList (state, data) {
state.permissionButton = data
}
},
actions: {
load ({ commit }) {
return http.getRequest(apiPath.getUserInfo, {}, (res) => {
const items = res.data // 此处为后台返回的权限集合
commit('update', items)
return res.data
})
},
getButton ({ commit }, data) {
return http.getRequest(apiPath.getButtonByMenuId, {menuId: data.id}, (res) => {
let buttonList = []
if (res.data && res.data.buttonList) {
res.data.buttonList.forEach(item => {
buttonList.push(item.menuName)
})
}
commit('changButtonList', buttonList)
})
}
}
}
export default menu
2.在vuex的index.js注册挂载模块
import Vue from 'vue'
import Vuex from 'vuex'
import menu from './menu.js'
Vue.use(Vuex)
const store = new Vuex.Store({
actions: {
init ({ dispatch, commit }) {
return dispatch('menu/load').then((data) => {
// 做一些自己想做的操作
return data
})
}
}
})
store.registerModule('menu', menu)
3.在router中构建index.js动态添加路由
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const createRouter = () =>
new Router({
routes: [],
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
},
strict: process.env.NODE_ENV !== 'production'
})
const router = createRouter()
export default router
export const addRoutes = (routes) => {
router.addRoutes([
...routes,
{
path: '/',
redirect: '首页的路由地址'
},
{
path: '*',
component: () => import('@/components/common/UnAuthorized.vue'),
meta: { title: '页面找不到' }
}
])
}
4.编写路由处理文件getrouter.js (根据配置路由处理文件加载路径和路由参数等)
function getPaths (menus) {
let arr = []
menus.forEach((item) => {
const { childrenMenu } = item
if (item.menuUrl) {
arr.push({
path: item.menuUrl,
menuName: item.menuName,
id: item.id
})
}
if (childrenMenu && childrenMenu.length > 0) {
const result = getPaths(childrenMenu)
arr = arr.concat(result)
}
})
return arr
}
const importFile = (path) => {
const componentName = path.split('/').map((str) => {
if (str && str.indexOf('-') > -1) {
let strs = ""
str.split('-').map((item) => {
return item && item.replace(item[0], item[0].toUpperCase())
}).forEach((items) => {
strs += items
})
str = strs
}
return str
}).join('/')
const fileUrl= () => import(/* webpackChunkName: "[request]" */`@/components${componentName}.vue`)
return fileUrl
}
const getName = (path) => {
const name = path.split('/').map((str) => {
return str
}).filter((item) => {
if (item) return item
}).join('-')
return name
}
export default function getRoutes (data) {
return getPaths(data).map(({ path, menuName, id }) => ({
path,
name: getName(path),
component: importFile(path),
meta: {
title: menuName,
id
}
}))
}
5.new vue添加处理逻辑
import getRoutes from 'getrouter.js'
new Vue({
router,
store,
methods: {
// 初始化获取菜单并添加路由
init () {
store.dispatch('init').then((menus) => {
addRoutes(getRoutes(menus))
})
}
},
created () {
this.init()
},
render: h => h(App)
}).$mount('#app')
6.添加路由监控查询菜单按钮权限
router.beforeEach((to, from, next) => {
if (to.meta.id) {
store.dispatch('menu/getButton', {
id: to.meta.id
})
}
next()
})
7.最后我们编写全局指令去控制按钮显示隐藏
/* 使用方法:v-hasPermission="'按钮名'" */
Vue.directive('hasPermission', {
update (el, binding, vnode) {
let permissionList = vnode.context.$store.state.menu.permissionButton
if (permissionList && permissionList.length
&&!permissionList.includes(binding.value)) {
el.style.display = 'none'
} else {
el.style.display = 'inline-block'
}
}
})
至此,我们的权限管控就到此结束了!