基于Vue3 + TS + Ant Design Vue 实现精细化菜单按钮权限授权组件功能

 更新时间:2026年03月21日 08:55:40   作者:奔跑的小蚂蚁9538  
本文将分享如何使用Vue3 + TypeScript + Vite + Ant Design Vue实现一个功能完善的菜单与按钮级别的权限授权组件,支持树形菜单选择、动态按钮联动、状态持久化等高级特性,感兴趣的朋友一起看看吧

在前端开发中,RBAC(基于角色的访问控制)是企业级应用的核心需求。本文将分享如何使用 Vue3 + TypeScript + Vite + Ant Design Vue 实现一个功能完善的菜单与按钮级别的权限授权组件,支持树形菜单选择、动态按钮联动、状态持久化等高级特性。

🎯 核心功能亮点

  • ✅ 双栏布局:左侧树形菜单选择,右侧动态按钮列表
  • ✅ 细粒度控制:支持菜单级和按钮级双重权限控制
  • ✅ 状态保持:切换菜单时自动保存/恢复按钮选中状态
  • ✅ 父子联动:自动处理菜单的半选/全选状态逻辑
  • ✅ 类型安全:完整的 TypeScript 类型定义
  • ✅ 性能优化:使用 Map 缓存菜单数据,避免重复查询

📁 项目结构概览

src/
├── components/
│   └── MenuAuthModal.vue      # 菜单授权弹窗组件
├── store/
│   └── index.ts               # Vuex Store,管理全局按钮权限
└── api/
    └── contract.ts            # 权限相关 API 接口

🔧 核心技术实现

1. 组件架构设计

采用 Ant Design Vue 的 Modal + Tree + Checkbox 组合,实现左右分栏布局:

<template>
  <a-modal :visible="visible" width="960px">
    <div class="menu-auth-content">
      <!-- 标题行 -->
      <div class="header-row">
        <div class="header-left">可授权菜单</div>
        <div class="header-right">可授权按钮</div>
      </div>
      <!-- 内容区域 -->
      <div class="content-row">
        <!-- 左侧:树形菜单 -->
        <div class="content-left">
          <a-tree
            v-model:checkedKeys="checkedMenuKeys"
            v-model:halfCheckedKeys="halfCheckedMenuKeys"
            checkable
            :tree-data="menuTreeData"
            @select="onMenuSelect"
          />
        </div>
        <!-- 右侧:按钮列表 -->
        <div class="content-right">
          <a-checkbox-group v-model:value="checkedButtons">
            <div v-for="button in buttonList" :key="button.key">
              <a-checkbox :value="button.key" :disabled="button.disabled">
                {{ button.label }}
              </a-checkbox>
            </div>
          </a-checkbox-group>
        </div>
      </div>
    </div>
  </a-modal>
</template>

2. 状态管理策略

使用 全局 Map 保存每个菜单的按钮选中状态,解决切换菜单时状态丢失问题:

// 全局按钮选中状态(菜单ID -> 按钮ID数组)
const globalButtonStates = ref<Map<string, string[]>>(new Map())
// 监听按钮变化,实时保存
watch(checkedButtons, (newValue) => {
  if (isRestoringState.value) return // 避免恢复状态时重复触发
  if (selectedMenuKeys.value.length > 0) {
    const currentMenuId = selectedMenuKeys.value[0]
    globalButtonStates.value.set(currentMenuId, [...newValue])
  }
}, { deep: true })

3. 菜单树数据处理

将后端返回的嵌套菜单结构转换为 Ant Design Tree 所需格式:

const convertMenuTree = (menu: FindMenuButtonTreeByRoleInfo): any => {
  return {
    key: String(menu.id),
    name: menu.name,
    icon: menu.icon || '',
    children: menu.children?.map(child => convertMenuTree(child)) || []
  }
}

4. 权限收集逻辑

提交时递归收集所有选中菜单(含父级)和按钮:

const collectAllMenuIdsWithParents = (): string[] => {
  const allMenuIds = new Set<string>()
  // 添加完全选中和半勾选的菜单
  checkedMenuKeys.value.forEach(id => allMenuIds.add(id))
  halfCheckedMenuKeys.value.forEach(id => allMenuIds.add(id))
  // 递归查找所有父级菜单
  checkedMenuKeys.value.forEach(menuId => {
    const parentIds = findParentMenuIds(menuId)
    parentIds.forEach(parentId => allMenuIds.add(parentId))
  })
  return Array.from(allMenuIds)
}

5. 全局权限存储

在 Vuex Store 中构建按钮权限映射表,供全局使用:

// store/index.ts
function buildButtonPermissionMap(menuList: MenuButtonTree[], buttonMap: Map<string, boolean>) {
  menuList.forEach((menu: MenuButtonTree) => {
    const menuKey = menu.active || (menu as any).key || ''
    if (menuKey && menu.buttons) {
      menu.buttons.forEach((button: any) => {
        const buttonKey = `${menuKey}_${button.id}`
        buttonMap.set(buttonKey, true)
      })
    }
    menu.children?.forEach(child => 
      buildButtonPermissionMap([child], buttonMap)
    )
  })
}
// Getter 用于权限判断
hasButtonPermission: (state) => (menuActive: string, buttonId: number | string) => {
  const buttonKey = `${menuActive}_${buttonId}`
  return state.buttonPermissions.has(buttonKey)
}

💡 使用示例

父组件调用

<template>
  <a-button @click="openAuthModal">菜单授权</a-button>
  <MenuAuthModal ref="authModalRef" @ok="handleAuthSuccess" />
</template>
<script setup lang="ts">
const authModalRef = ref()
const openAuthModal = () => {
  authModalRef.value.open({
    id: '123',
    roleName: '超级管理员'
  })
}
const handleAuthSuccess = ({ roleId, menuPermissions, buttonPermissions }) => {
  console.log('授权成功', { roleId, menuPermissions, buttonPermissions })
  // 刷新列表或更新状态
}
</script>

页面按钮权限控制

<template>
  <a-button 
    v-if="$store.getters.hasButtonPermission('ContractList', 1001)"
    @click="handleEdit"
  >
    编辑
  </a-button>
</template>

🎨 样式优化

使用 Less 实现美观的双栏布局和滚动条样式:

.menu-auth-content {
  .header-row {
    display: flex;
    border-bottom: 1px solid #f0f0f0;
    .header-left {
      width: 30%;
      border-right: 1px solid #f0f0f0;
    }
    .header-right {
      width: 70%;
    }
  }
  .content-row {
    display: flex;
    .content-left, .content-right {
      max-height: 500px;
      overflow-y: auto;
    }
    .content-left {
      width: 30%;
      border-right: 1px solid #f0f0f0;
    }
    .content-right {
      width: 70%;
      .button-checkbox-item:nth-child(even) {
        background: #FAFAFA;
      }
    }
  }
}
// 自定义滚动条
.content-left::-webkit-scrollbar {
  width: 6px;
}
.content-left::-webkit-scrollbar-thumb {
  background: #d9d9d9;
  border-radius: 3px;
}

🚀 性能优化技巧

  • Map 缓存:使用 Map<string, Menu> 快速查找菜单对象,避免 O(n) 遍历
  • 防抖处理:状态恢复时使用 isRestoringState 标志位,防止 watch 重复触发
  • 按需渲染:仅当选中菜单时才加载对应的按钮列表
  • 深度克隆:使用 [...array] 避免引用导致的意外修改

📝 总结

本方案实现了企业级应用所需的完整权限授权功能,具有以下优势:

  • 用户体验佳:直观的树形选择 + 实时按钮预览
  • 代码可维护:清晰的职责分离,TypeScript 类型保障
  • 扩展性强:易于添加新的权限维度(如数据权限)
  • 性能优秀:合理的缓存策略和状态管理

通过本文的实现,你可以快速在自己的项目中集成类似的权限管理系统,提升应用的安全性和灵活性。

到此这篇关于基于 Vue3 + TS + Ant Design Vue 实现精细化菜单按钮权限授权组件的文章就介绍到这了,更多相关基于 Vue3 + TS + Ant Design Vue 实现精细化菜单按钮权限授权组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue3使用watch监听props的值的注意事项及说明

    vue3使用watch监听props的值的注意事项及说明

    这篇文章主要介绍了vue3使用watch监听props的值的注意事项及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • 详解基于Vue2.0实现的移动端弹窗(Alert, Confirm, Toast)组件

    详解基于Vue2.0实现的移动端弹窗(Alert, Confirm, Toast)组件

    这篇文章主要介绍了详解基于Vue2.0实现的移动端弹窗(Alert, Confirm, Toast)组件,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 详解用vue-cli来搭建vue项目和webpack

    详解用vue-cli来搭建vue项目和webpack

    本篇文章主要介绍了详解用vue-cli来搭建vue项目和webpack,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • vue项目中路由懒加载的三种方式(简洁易懂)

    vue项目中路由懒加载的三种方式(简洁易懂)

    本文主要介绍了vue项目中路由懒加载的三种方式,主要包括vue异步组件,组件懒加载,webpack的require.ensure(),具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • vue3+axios封装拦截器方式

    vue3+axios封装拦截器方式

    介绍了如何在Vue项目中使用Axios封装请求、配置拦截器,并在api.js中统一管理API接口,同时,也讲解了如何在vite.config.js中配置解决跨域问题,这些操作可以优化前端代码结构,提高开发效率
    2024-09-09
  • vue在组件中使用v-model的场景

    vue在组件中使用v-model的场景

    这篇文章主要介绍了vue在组件中使用v-model的场景,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • Vue基础配置讲解

    Vue基础配置讲解

    在本篇文章里小编给大家整理的是关于Vue基础配置讲解内容,有兴趣的朋友们可以学习下。
    2019-11-11
  • 解决vue js IOS H5focus无法自动弹出键盘的问题

    解决vue js IOS H5focus无法自动弹出键盘的问题

    今天小编就为大家分享一篇解决vue js IOS H5focus无法自动弹出键盘的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • vue.js实例todoList项目

    vue.js实例todoList项目

    本篇文章主要介绍了vue.js实例todoList项目,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • vue router demo详解

    vue router demo详解

    这篇文章主要为大家详细介绍了vue router demo演示代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10

最新评论