vue运营端项目按钮权限的实现
公司的老项目,之前是只有页面权限的,是服务器端控制生成动态路由后发送给前端,前端整理完数据通过router.addRoutes动态给不同权限的用户添加不同的路由映射表,从而达到页面权限的差别控制。 后来用户权限具体到了按钮级别,开始比较少所以都是在具体的某个页面通过计算属性来判断是否显示按钮或者在js中判断是否有读取权限
computed: {
isRoles() {
const sllowRoles = ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN', 'ROLE_VIP_MANAGER']
const roles = this.$store.state.user.user.roles || []
let bool = false
if(roles.filter(item => sllowRoles.includes(item)).length) {
bool = true
}
return bool
},
},
随着需要按钮权限的页面越来越多,如果在每个页面都用计算属性来管理,就会比较繁琐不好维护容易出错。最后就是结合网上的优质想法最终把它通过自定义指令来实现按钮级别权限的控制。 具体做法如下:
1、首先创建一个directive目录,来存放相关自定义指令,新建permission目录存放权限的指令
2、在index.js中引入Vue,编写好自定义指令和相关方法后用Vue插件机制注入全局
import Vue from 'vue'
import permissionMap from './permissionMap'
import { createPermissionMap, judgeIsRoles } from './permissionTools'
// table中的el-table-column 和 <template>标签 不能使用简单的v-permission="$permissionMap.xxxxx" 来删除 只能借用v-if来判断是否显示
const directive = {
inserted(el, binding, vnode) {
let { value } = binding
if (value && permissionMap[value]) {
const hasPermission = judgeIsRoles(value)
if (!hasPermission) {
// el.parentNode && el.parentNode.removeChild(el) // 考虑到有些元素是可以来回切换显示隐藏的 所以没有使用removeChild
el.style.display = 'none'
} else {
el.style.display = 'inline-block' // 使用unset这个属性导致按钮布局混乱 最终使用inline-block
}
} else {
throw new Error(`need roles! Like v-permission="userQuery"`)
}
},
install(Vue) {
Vue.prototype.$judgeIsRoles = judgeIsRoles
Vue.prototype.$permissionMap = createPermissionMap()
Vue.directive('permission', directive)
}
}
Vue.use(directive)
main.js中引入即可
// ...
// 按钮级别的各种权限
import './directive/permission' // permission control
// ...
3、首先解释一下 permissionMap 文件,因为每个按钮权限不同,有操作权限的用户不止一个,所以需要用一个字符串数组来存取,如果每次使用自定义指令都传入一个数组,一是不美观二是太分散不好维护,所以我就提取到一个公用文件通过key来映射来存取相关的权限用户列表,
比如: myUsersQuery: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER'] 在使用中直接使用 v-permission="myUsersQuery"
myUsersQuery这个字符串值就是2步骤中的 let { value } = binding 的value,判断如果 permissionMap 对象中有这个值再进行相关操作
permissionMap.js
export default {
// APP页面获取当前会话权限
APPGetCurrentSession: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER', 'ROLE_GS_MANAGER', 'ROLE_GS_USER'], // APP页面获取当前会话权限
// 我的用户
myUsersQuery: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER'], // 查询
myUsersReAssign: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 重新分配
myUsersTransfer: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 按坐席转移
myUsersRoleDetail: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER' ], // 角色详情查看
myUsersRechargeDetail: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER' ], // 充值明细查看
myUsersTrack: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER' ], // 用户追踪查看
myUsersStartSession: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER', 'ROLE_VIP_USER' ], // 发起会话
myUsersDeleteSession: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 删除会话
// 待分配页面
assigningListQuery: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 查询
assigningListAssign: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 分配
assigningListDeleteBatch: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 批量删除
assigningListExportExcel: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 导出excel
assigningListImportBatch: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 批量导入
assigningListAssignBatch: ['ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_VIP_MANAGER' ], // 批量分配
// ....
}
4、这个步骤就是需要判断当前用户是否有操作某个按钮的权限,即 judgeIsRoles 函数。
permissionTools.js
import store from '@/store'
import permissionMap from './permissionMap'
export const createPermissionMap = () => {
const obj = {}
Object.keys(permissionMap).forEach(key => obj[key] = key)
return obj
}
export const judgeIsRoles = (mapKey) => {
if (mapKey && permissionMap[mapKey]) {
const sllowRoles = permissionMap[mapKey]
const roles = store.state.user.user.roles || []
if(roles.filter(item => sllowRoles.includes(item)).length) {
return true
}
}
return false
}
这个 judgeIsRoles 就是用的之前页面中的计算属性,拿到当前用户的角色列表(有可能是多个角色)和 当前按钮的有效权限列表,两者进行交集运算如果有重合则返回true表示有权限,否则取false表示没有权限
同时会在2步骤中把 judgeIsRoles 注册为全局方法在某些不能使用自定义指令的地方同样可以使用这个方法判断权限的展示与否
然后是 createPermissionMap 方法, 这个方法就是把 permissionMap 中大对象的所有key值取出来放到全局变量中, 这样在使用自定义指令的时候 可以这么使用 v-permission="$permissionMap.myUsersQuery", 传入的参数就必须是在 permissionMap 中的key值,否则将找不到相关数据做报错处理。 这样在后期维护中只要在 permissionMap 文件中添加相关的key值和权限列表即可。
最后就是有些坑需要解释一下
table中的el-table-column 和 template标签 不能使用简单的v-permission="$permissionMap.xxxxx" 来控制 只能借用v-if来判断是否显示
//比如: <el-table-column align="center" label="操作" width="300px" fixed="right" v-if="$judgeIsRoles($permissionMap.userMsgShowHandle)"></el-table-column>
// <template v-if="$judgeIsRoles($permissionMap.userMsgShowHandle)"></template>
个人理解是 template 在vue的render函数中会被和谐掉,最终是不产出真实DOM树的,既然没有真实DOM就不能在inserted的时候对其做相关DOM操作
而 el-table-column 是table中的某一列,这列中的每一行 又不在同一个元素中,所以也是很难在inserted的时候去操作DOM,都用v-if代替 说明v-if应该是在生成虚拟DOM之前就做好判断了,之后再去把VDOM渲染成真实DOM。