前言:
后台管理项目免不了要做权限控制,常见的分为路由级别和按钮级别,在此主要针对于按钮权限,比如说某个用户或者角色对数据有没有增删改查的权限,例如以下功能,巡查人员可以点击导入和新建,而一般用户只能选择下载模板。
一、创建
1.创建文件夹
项目是vue3版本,在项目的src文件夹下的shared文件下新建一个名为directives的文件夹,内置一个modules文件夹和index.ts文件,前者用于存放各个自定义指令业务逻辑便于区分和维护,后者用于集中整理注册所有的自定义指令。
2.创建自定义指令
在 directives文件夹下分别创建permission、debounce、throttle三个ts文件,分别用于存放权限控制,防抖和节流的业务逻辑,结构清晰,方便维护以及更低的耦合度。
①按钮权限:
实现逻辑是登陆后获取用户的按钮权限整理成一个数组buttonList存入store中,指令绑定按钮后传入binding参数,与buttonList中数据进行映射匹配,若找到则返回true,设置绑定元素显示,反之绑定元素设置隐藏。
import type { Directive, DirectiveBinding } from 'vue'
import { useRoutesStore } from '@/store/modules/routes'
import { BUTTON_PERMISSION_MAP } from '@/constants/button-permission'
const permission: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding): void {
const { value } = binding
if (!value)
return
const curValue = BUTTON_PERMISSION_MAP[binding.value]
const allBtnMap = useRoutesStore().buttonList
// 可根据自己的业务修改此处实现逻辑
if (!allBtnMap.includes(curValue))
el.style.display = 'none'
else
el.style.display = 'auto'
},
}
export default permission
②防抖函数
函数防抖是常见的项目交互优化方式,就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。此功能用法一致,也可根据自己需求稍作修改。
import type { Directive, DirectiveBinding } from 'vue'
interface ElType extends HTMLElement {
__handleClick__: () => any
}
const debounce: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
if (typeof binding.value !== 'function')
return
let timer: NodeJS.Timeout | null = null
el.__handleClick__ = function () {
if (timer)
clearInterval(timer)
timer = setTimeout(() => {
binding.value()
}, 500)
}
el.addEventListener('click', el.__handleClick__)
},
beforeUnmount(el: ElType) {
el.removeEventListener('click', el.__handleClick__)
},
}
export default debounce
③节流函数
与上面防抖函数一样,节流也是常见的项目交互优化方式,主要是指在事件被触发后,在规定时间内无论再怎么触发只会调用一次函数,直到时间结束。节流函数根据业务需求传入等待时间,不传默认设置2s。
import type { Directive, DirectiveBinding } from 'vue'
interface ElType extends HTMLElement {
__handleClick__: (event: MouseEvent) => any
disabled: boolean
}
const throttle: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
if (typeof binding.value !== 'function')
return
const duration = binding.arg ? parseInt(binding.arg) : 2000
let timer: NodeJS.Timeout | null = null
el.__handleClick__ = function (event: MouseEvent) {
if (timer)
clearTimeout(timer)
if (!el.disabled) {
el.disabled = true
el.classList.add('n-button--disabled')
binding.value(event)
timer = setTimeout(() => {
el.disabled = false
el.classList.remove('n-button--disabled')
}, duration)
}
}
el.addEventListener('click', el.__handleClick__)
},
beforeUnmount(el: ElType) {
el.removeEventListener('click', el.__handleClick__)
},
}
export default throttle
3.注册自定义指令
在index.ts文件中分别导入每个自定义指令对象,再遍历注册每一个指令。
import type { App } from 'vue'
import debounce from './modules/debounce'
import throttle from './modules/throttle'
import permission from './modules/permission'
const directivesList: any = {
debounce,
throttle,
permission,
}
const setDirectives = {
install(app: App<Element>) {
Object.keys(directivesList).forEach((key) => {
app.directive(key, directivesList[key])
})
},
}
export default setDirectives
4.挂载自定义指令
在根目录main.ts文件中导入index.ts中创建的指令setDirectives,在vue实例app上进行挂载,可在项目中任意位置提供全局使用。
二、使用
类似于vue的v-if,v-for等内置指令,在页面功能按钮上,直接通过v-前缀加上自定义指令名称,即可使用封装的自定义指令。