Vue3 自定义指令实现按钮权限控制

kkcode
kkcode
2026-03-24阅读 701

Vue3 自定义指令,用来根据用户权限动态控制按钮/元素的显示/禁用,这是后台管理系统最常用的权限方案。
一套完整的完整可直接使用的实现,包含:指令注册、权限判断、使用方式、全局权限管理。

核心思路

  1. 定义全局自定义指令 v-permission
  2. 指令接收权限标识(如 user:add、role:edit)
  3. 从全局状态(Pinia/Vuex)获取用户拥有的权限列表
  4. 判断当前元素所需权限是否在用户权限内,不符合则移除元素/禁用按钮

完整实现步骤

1. 先创建权限状态管理(Pinia)

存储用户的权限列表,全局共享(如果用Vuex,逻辑完全一致)

// src/stores/permission.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'

// 权限Store
export const usePermissionStore = defineStore('permission', () => {
  // 用户拥有的权限标识列表(登录后从接口获取)
  const permissions = ref<string[]>([])

  // 设置权限
  const setPermissions = (list: string[]) => {
    permissions.value = list
  }

  return { permissions, setPermissions }
})

2. 注册自定义指令 v-permission

创建指令文件,支持两种模式:

  • disabled:权限不足时禁用按钮
  • 默认:权限不足时直接隐藏元素
// src/directives/permission.ts
import type { Directive } from 'vue'
import { usePermissionStore } from '@/stores/permission'

// 定义指令配置
const permissionDirective: Directive = {
  mounted(el, binding) {
    const permissionStore = usePermissionStore()
    // 获取指令传入的权限值
    const { value } = binding
    
    if (!value || !Array.isArray(value)) {
      console.warn('v-permission 必须传入权限标识数组,如 v-permission="[\'user:add\']"')
      return
    }

    // 判断用户是否拥有该权限
    const hasPermission = permissionStore.permissions.some(perm => value.includes(perm))

    if (!hasPermission) {
      // 支持两种模式:禁用 / 隐藏
      if (binding.modifiers.disabled) {
        // 禁用按钮
        el.disabled = true
        el.classList.add('is-disabled')
      } else {
        // 直接隐藏元素
        el.parentNode?.removeChild(el)
      }
    }
  }
}

export default permissionDirective

3. 全局注册指令

在 main.ts 中注册,全局可用:

// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import permissionDirective from './directives/permission'

const app = createApp(App)
// 注册全局指令 v-permission
app.directive('permission', permissionDirective)

app.mount('#app')

使用方式(超简单)

① 隐藏按钮(默认)

<!-- 只有拥有 user:add 权限才显示 -->
<button v-permission="['user:add']">新增用户</button>

<!-- 满足任意一个权限就显示 -->
<button v-permission="['user:edit', 'user:delete']">编辑/删除</button>

② 禁用按钮(加 .disabled 修饰符)

<!-- 权限不足时禁用按钮,而不是隐藏 -->
<button v-permission.disabled="['role:edit']">编辑角色</button>

测试效果

在登录后/页面初始化时,给用户设置权限:

import { usePermissionStore } from '@/stores/permission'

const permissionStore = usePermissionStore()
// 模拟接口返回的用户权限
permissionStore.setPermissions(['user:list', 'user:add', 'role:list'])

此时:

  • v-permission="['user:add']" → 显示
  • v-permission="['user:delete']" → 隐藏
  • v-permission.disabled="['role:edit']" → 禁用

扩展优化(可选)

支持单个字符串权限

修改指令,支持 v-permission="'user:add'":

// 处理权限值
const needPermissions = Array.isArray(value) ? value : [value]
const hasPermission = permissionStore.permissions.some(perm => needPermissions.includes(perm))

支持所有权限必须满足

加 .all 修饰符:

if (binding.modifiers.all) {
  // 必须拥有所有权限
  hasPermission = value.every(perm => permissionStore.permissions.includes(perm))
} else {
  // 拥有任意一个即可
  hasPermission = value.some(perm => permissionStore.permissions.includes(perm))
}

使用:

<button v-permission.all="['user:edit', 'user:delete']">删除</button>

总结

  1. 用 Pinia 全局存储用户权限列表
  2. 自定义指令 v-permission 做权限判断
  3. 支持隐藏/禁用两种交互方式
  4. 全局注册后,所有页面直接使用,零业务代码侵入
评论数量:0