需求描述:按钮 根据 “角色” 显示或隐藏。同时该按钮也可以有其他展示条件。
如:按钮 “开启彩虹桥”显示条件是,当账户角色为 Asgard 或 Avenger,且页面中存在 Bifrost。
注意点:
- 页面刚进入时,不展示按钮
- v-if
- 可以有多个角色
期望最终使用方式
<button
v-if="Bifrost"
v-btnPermit="roles"
>
开启彩虹桥
</button>
// 也可以只判断角色
<button
v-btnPermit="roles"
>
开启彩虹桥
</button>
// -----
{
roles: ['Asgard','Avenger'], // string|string[] 可以使用该按钮的角色列表
Bifrost: true // Boolean
}
遇到的问题与解决思路
我写的也迷糊=_=,你们大概看看,不对的地方请指正。
- 通过接口获取当前账户的角色,并存储到vuex中。
- 每次项目初始化(刷新)时调用
GetRoles
,防止角色被更新后页面逻辑不能及时更新。我放在了最外层的layout页面。 - 接口是异步进行的,会出现符合展示条件时,按钮并未展示或隐藏的问题。需要同步处理,等接口执行完毕再判断展示的逻辑
- 上面的处理按照正常登录进入系统的逻辑是没问题的。如果在有权限类按钮页面做了刷新,但此时还没获取到角色列表,自定义指令的钩子中,会去调用接口并等待处理。
这个逻辑就会有一种问题:页面有10个权限类的按钮,会同时调用10次接口,有好的解决方案的朋友们,可以留言说一下
- 角色权重大于if条件。如果角色不存在,直接移除节点。
- 默认先用样式控制隐藏 display:none。页面默认不展示有权限控制的节点。
- 使用
inserted
,主要是因为做了同步处理一步到位了。如果if条件发生更改,也会触发inserted
。
代码
// vuex
state: {
allRoles: null
}
mutations: {
SET_ROLE: (state, roles) => {
state.allRoles = roles
}
}
actions: {
GetRoles({commit}) {
return new Promise((resolve) => {
// axios 请求接口,并处理成数据 resolve([角色列表])
// 注意接口错误时,返回 空数组 []
})
}
}
Vue.directive('btnPermit', {
inserted: async(el, binding)=> {
el.style.display = 'none'
/**
* 判断该账户下的角色列表中是否包含
* role string[] | string
* list string[]
*/
const passRole = (role, list) => {
if(!list) return false // 没有获取到角色
if(role instanceof Array) {
return role.some(item => list.includes(item))
} else {
return list.includes(role)
}
}
const _roles = binding.value
if(!store.getters.allRoles) {
// _res 等待接口获取到该账户的所有角色,再进行处理
const _res = await store.dispatch('GetRoles')
if(!passRole(_roles, _res)) {
el.parentNode ? el.parentNode.removeChild(el) : el.remove()
} else {
el.style.display = 'unset' // 考虑到影响原有布局问题,block 改为 unset了
}
} else {
if(!passRole(_roles, _res)) {
el.parentNode ? el.parentNode.removeChild(el) : el.remove()
} else {
el.style.display = 'unset'
}
}
}
})
补充
不用v-show
是在想,如果他没这个权限,页面中还有节点,那有懂代码的手动给我block了咋整。
v-show场景的思路,仅供参考,欢迎提供方案
如果你们有v-show的需求,我之前的思路是,可以把条件的值传入自定义指令,在update函数中处理逻辑。比如:isShow=true
。
<button v-show="isShow" v-btnPermit:show="{ roles: ['Loki'], show: isShow }">
根据 binding.arg
判断是否有 show 条件,
binding.value
这时是 { roles: ['Loki'], show: true }
,
可以取到 v-show此时的值,结合passRole的判断角色存在不,处理下 el.style.display
我傻了
好不容易折腾完了这个功能,然后又吭哧写了博客,刚才突然想到:为啥我不直接在最外层加个div啥的,直接给个v-if 或者v-show…
想下班了… 我决定不改了.如果有帮助到你们,我很开心,没帮到的话,,,反正你也看到这里了。