时代在变化
- 按钮级权限建议按照这种方式走:以自定义指令的形式,简单快捷粗暴。
- 自定义指令形式目前我理解的有两种模式:
- 第一种模式为权限不跟角色挂钩(前端自己控制):执行v-power=“xxxxx”
- 第二种模式为权限跟角色挂钩;一切权限全靠配置,在页面内需要权限的按钮只需要执行(假设我们设置自定义指令为v-power)v-power就行了。
新款权限组件
第一种权限:自定义传值校验是否有权限(如果之前使用的本文权限可直接复制以下代码增加指令就行了)
- 自定义指令;(然后main.js引入)
import Vue from 'vue';
import store from '@/store';
/**权限指令**/
Vue.directive('power', {
bind: function (el, binding, vnode) {
// 获取页面按钮权限;
const powerList = store.getters['user/power'][vnode.context.$route.fullPath].map((v) => v.buttonName);
if (!powerList.includes(binding.value)) {
// 这块等待页面加载出来后去删除节点
vnode.context.$nextTick(() => {
el.parentNode && el.parentNode.removeChild(el);
})
}
}
});
- 接口返回参数(与路由相对应,本文以hash来举例子的)
power: {
'/goods/detail': [
{
buttonName: 'search',
},
{
buttonName: 'edit',
},
{
buttonName: 'delete',
},
{
buttonName: 'export',
}
]
}
- vuex代码
// axios 的接口封装;测试的时候可不用。以下数据支持测试。
import { funcButtonList } from '@/api/user.js'
export default {
// vuex 开启命名空间模式。方便使用mapGetters('user', { powers: 'power' });我这里界面名字是user.js;
namespaced: true,
state: {
// 初始测试数据
power: {
'/goods/detail': [
{
buttonName: 'search',
},
{
buttonName: 'edit',
},
{
buttonName: 'delete',
},
{
buttonName: 'export',
}
]
}
},
mutations: {
updatePower(state, d) {
state.power = d
}
},
actions: {
funcButtonList({commit}) {
funcButtonList().then(({data})=> {
commit('updatePower', data)
})
}
},
// 让 power 变为可监听的(方便 watch )
getters: {
power: state => state.power
}
}
- 使用
<el-button v-power="'export'">导出</el-button>
<el-button v-power="'search'">查询</el-button>
第二种权限:跟角色挂钩模式【我自己项目不适用于跟角色挂钩,所以说我使用的是另一种】
- 自定义指令代码
import Vue from 'vue';
/**权限指令**/
Vue.directive('power', {
bind: function (el, binding, vnode) {
// 获取页面按钮权限
let btnPermissionsArr = [];
if(binding.value){
// 如果指令传值,获取指令参数,根据指令参数和当前登录人按钮权限做比较。
btnPermissionsArr = Array.of(binding.value);
}else{
// 否则获取路由中的参数,根据路由的btnPermissionsArr和当前登录人按钮权限做比较。
btnPermissionsArr = vnode.context.$route.meta.btnPermissions;
}
if (!Vue.prototype.$_powe(btnPermissionsArr)) {
el.parentNode.removeChild(el);
}
}
});
- 挂载在vue原型上的方法(
$_power
)
import Vue from 'vue'
Vue.prototype.$_power = function (value) {
let isExist = false;
// 获取用户按钮权限
let btnPermissionsStr = sessionStorage.getItem("btnPermissions");
if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
return false;
}
if (value.indexOf(btnPermissionsStr) > -1) {
isExist = true;
}
return isExist;
};
- 使用
<el-button @click='handleClick' type="primary" v-power>编辑</el-button>
老旧式权限组件(不建议走了)
- 本文按钮级权限逻辑介绍:
1)因为项目的页面级权限已经弄完善了,但是呢,这个时候用户又提出了需要按钮级权限。哎
2)用户登录后去请求一个接口btnList。以每个页面的路由路径为key,值为这个页面的所有按钮级权限
3)此时咱们就可以在各个页面或者组件里面判断 查询、编辑、添加、删除(crud)等权限了
4)需要用到的有 vuex,provide-inject【重新渲染】,mixin
- 接口返回参数
power: {
'/goods/detail': [
{
buttonName: 'search',
},
{
buttonName: 'edit',
},
{
buttonName: 'delete',
},
{
buttonName: 'export',
}
]
}
- vuex代码
// axios 的接口封装;测试的时候可不用。以下数据支持测试。
import { funcButtonList } from '@/api/user.js'
export default {
// vuex 开启命名空间模式。方便使用mapGetters('user', { powers: 'power' });我这里界面名字是user.js;
namespaced: true,
state: {
// 初始测试数据
power: {
'/goods/detail': [
{
buttonName: 'search',
},
{
buttonName: 'edit',
},
{
buttonName: 'delete',
},
{
buttonName: 'export',
}
]
}
},
mutations: {
updatePower(state, d) {
state.power = d
}
},
actions: {
funcButtonList({commit}) {
funcButtonList().then(({data})=> {
commit('updatePower', data)
})
}
},
// 让 power 变为可监听的(方便 watch )
getters: {
power: state => state.power
}
}
- 封装 filterPower 方法
import store from '@/store'
/**
*
* @param {param1 url} 地址栏url参数
* @param {param1 power} 权限列表 ['edit', 'delete', 'search' ...]
* @param {param2 flag} 当为true的时候判断为空的情况全部默认返回,否则置空返回
* @returns 过滤掉在当前权限列表中没有的按钮 ['edit', 'delete' ...]
*/
export const filterPower = ({
url,
power
}, flag = false) => power.filter(
item =>
// user/power 这个是读取的 getters 暴露出来的属性
!!(store.getters['user/power'][url] ?
store.getters['user/power'][url].filter((val) => val.buttonName === item).length :
flag)
)
-
在页面的app.vue【这个得看你,我自己加到app那个最顶级要出问题,所以说我加到app子级的】添加 能让页面重新加载的方法
1)这里有两种情况可以考虑使用,我自己选的provide+inject方法
2)第一种是添加一个空白页面的路由,然后那个页面里面开始创建的时候(created里面写如果效果不好则改为data函数里面)写入this.$router.replace(xxxx)替换当前路由回之前路由
3)provide+inject
4)为什么要这个呢?因为是为了兼容用户刷新页面后重新渲染当前页面的按钮(无权限,按钮不显示出来)以下js代码为你的父级页面(你要确保这个v-if不会让你进入死循环【重复请求按钮权限接口】的父级页面,provide可以任意往父级套,引用的时候只会找最近的一个provide)
<template>
<div>
<router-view v-if="isRouterAlive"></router-view>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
name: "xxxx",
provide() {
return {
reload: this.reload
}
},
data() {
return {
isRouterAlive: true,
}
},
created() {
// 请求按钮权限接口-页面刷新则去调用vuex中actions的funcButtonList方法
// 防止用户刷新界面权限按钮消失bug
this.funcButtonList()
},
methods: {
...mapActions('user', ['funcButtonList']),
reload() {
this.isRouterAlive = false;
this.$nextTick(()=> {
this.isRouterAlive = true
})
}
}
}
</script>
- mxin封装的方法 @/mixin/power.js
import { mapGetters } from "vuex";
export default {
inject: ['reload'],
computed: {
// 这个看不懂的去看看vuex的命名空间下mapGetters写法
...mapGetters('user', {
powers: 'power'
})
},
watch: {
powers() {
// 重新加载 - 看不懂这个 去看看vue的provide和inject
this.reload()
}
},
}
- 到了此时准备工作已经做完了,正式开工,以下为一个组件页面改出来的0.0,功能目测没问题
主要是两步,一个引入mixin 监听接口请求刷新页面,然后就是filterPower 过滤出当前页面涵盖的功能了
<template>
<el-row class="param-search" v-if="isShow">
<!-- xxxxxx-->
<el-col :span="4" class="flex-acenter" style="justify-content: flex-end">
<el-button
type="primary"
v-if="btnPower.includes('search')"
>查询</el-button
>
<el-button
type="primary"
v-if="btnPower.includes('export')"
>导出</el-button
>
</el-col>
</el-row>
</template>
<script>
import PowerMixin from '@/mixin/power.js'
export default {
// 这个是负责刷新时候界面更新
mixins: [PowerMixin],
inject: ["tableInfo"],
data() {
return {
btnPower: [],
};
},
computed: {
isShow() {
return !!this.btnPower.length
}
},
created() {
this.init();
},
methods: {
init() {
// 初始化按钮级权限-this.$lh.filterPower 我把filterPower挂载在自己的方法上面的,这里你们自己引用上面的那个函数吧。或者跟我一起挂载在vue原型上面(下面会介绍)
this.btnPower = this.$lh.filterPower({
url: this.$route.path,
power: ["search", "export"],
});
},
},
};
</script>
- 按文件夹的形式读取挂载到vue的原型上,首先创建一个libs文件夹,再在libs中创建一个vuePrototype文件夹
此处为 main.js 文件地址(vue唯一的入口文件)
import Vue from 'vue'
// 会默认直接读取文件夹内所有的js文件 (记住 使用 export default;不然你自己改下面代码去。下面取的是的value.default)
const modulesFiles = require.context('@/libs/vuePrototype', true, /\.js$/)
modulesFiles.keys().reduce((modules, modulePath) => {
const name = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[name] = value.default
Vue.prototype[name] = modules[name]
return modules
}, {})
new Vue({
render: h => h(App),
data: {
eventHub: new Vue()
}
}).$mount('#app');
- $lh.js代码
import { filterPower } from '@/libs/power.js'
export default { filterPower }
有更好的见解,咱们交流交流哇