由于下面几种方式都需要用到鉴权函数,所以将其放置在组件外面,供组件或其他文件调用。
// src/utils/hasPermission.js
import { usePermissionStore } from '@/stores'
import array from 'lodash/array'
export const hasPermission = (value, def = true) => {
// 不传值,默认视为有权限,不做鉴权
if (!value) {
return def
}
const allCodeList = usePermissionStore().getPermCodeList
// 如果不是数组,直接判断pinia里的权限数组有没有相同的元素即可
if (!Array.isArray(value)) {
return allCodeList.includes(value)
}
// intersection是lodash提供的一个方法,用于返回一个所有给定数组都存在的元素组成的数组
return array.intersection(value, allCodeList).length > 0
}
将可以操作按钮的权限码们以权限码数组的形式存放到了pinia中,所以要先获取pinia中的权限数组再进行判断。
Pinia:
// store/index.js
import { createPinia } from "pinia";
const pinia=createPinia()
export default pinia
export * from './modules/permission.js'
// store/modules/permission.js
import { defineStore } from 'pinia'
import { getPermissionList } from '@/api/permission'
export const usePermissionStore = defineStore('permission', {
state: () => ({
// 权限代码列表
permCodeList: [101, 102, 778, 779]
}),
getters: {
// 获取
getPermCodeList() {
return this.permCodeList
}
},
actions: {
// 存储
setPermCodeList(codeList) {
this.permCodeList = codeList
},
// 请求权限码
async getPermissionCode() {
const codeList = await getPermissionList()
this.setPermCodeList(codeList)
}
}
})
引入鉴权函数,将该能够操作该按钮的权限码以实参形式传入鉴权函数,v-if根据返回值决定是否加载该按钮结构。
将按钮以插槽形式放入组件中,给组件以props传递权限码数据,组件中使用鉴权函数来判断传入的权限码是否符合条件,符合则渲染插槽中的结构(按钮),否则不渲染。
自定义指令其实和第一种很像,就是我们自己出一个"v-if",只不过是专门用来做权限判断的指令。指令传入的数据即为权限码,指令内部根据指令绑定的数据来做鉴权判断。
注意点1:这里的mounted可能较容易实现,但需要注意updated的设计,考虑到有动态修改指令后面的权限码的可能,所以当涉及到指令后面的权限码修改后,要重新做鉴权判断。
注意点2:自定义指令在内部的鉴权判断后,如果不符合条件要移除指令所在的结构;如果符合条件的话,要把之前因为不符合条件被移除的结构要添加回来,也就是回添操作;回添操作为了避免不知道把原来的结构添加回来到哪个位置,这里使用了document.createComment() 即注释先做占位,等需要回添时使用replaceChild() 把注释替换为按钮结构。如果使用的是remove()或removeChild()可能再放回来就不知道放到哪个位置了。
// src/directs/judge-permission.js
import { hasPermission } from '@/utils/hasPermission'
// removeDom、addDom的存在是为了保证自定义指令绑定的权限码数据变化后可以不刷新页面动态添加/移除自定义指令所在的dom节点
// 移除dom节点
const removeDom = (el) => {
// 把注释点绑在元素上,方便后面使用
el._placeholderNode = document.createComment('permission-btn')
// 和父节点关联上,方便后面使用
el._parentNode = el.parentNode
// 父节点做子元素替换
el.parentNode.replaceChild(el._placeholderNode, el)
}
// 把移除的dom节点添加回来
const addDom = (el) => {
el._parentNode?.replaceChild(el, el._placeholderNode)
}
const mounted = (el, binding) => {
const permisList = binding.value
if (!permisList) return
if (!hasPermission(permisList)) {
// 移除当前dom节点
// el.parentNode?.removeChild(el) // 通过父节点删除子自己
// el.remove() // 自杀
/*
权限值会变,那就涉及到更新后要不要将移除的dom节点添加回来
为了能够知道添加回来的dom节点放到哪个地方,可以创建注释节点来占位
在添加时只对那个位置的子元素做替换操作即可
*/
removeDom(el)
}
}
const updated = (el, binding) => {
// 比对前后变化的值,相同不需要后续操作
let valDiff = binding.value === binding.oldValue
if (valDiff) return
// 重新判断改值前后的权限变化,相同不需要后续操作(如:修改前就没权限,修改后依旧没权限,无需操作)
let oldPermisStatus = hasPermission(binding.oldValue)
let nowPermisStatus = hasPermission(binding.value)
if (oldPermisStatus == nowPermisStatus) return
if (nowPermisStatus) {
addDom(el)
} else {
removeDom(el)
}
}
export const permisDirect = {
mounted,
updated
}
main.js中添加指令:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import pinia from './stores'
import {permisDirect} from '@/directs/judge-permission'
const app=createApp(App)
app.use(pinia)
app.directive('permis',permisDirect)
app.mount('#app')