基于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 实现精细化菜单按钮权限授权组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 手写vite插件教程示例

    手写vite插件教程示例

    这篇文章主要为大家介绍了手写一个vite插件教程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • VUE多层路由嵌套实现代码

    VUE多层路由嵌套实现代码

    这篇文章主要为大家详细介绍了VUE多层路由嵌套的实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • 使用github部署前端vue项目

    使用github部署前端vue项目

    这篇文章主要为大家介绍了使用github部署前端vue项目过程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 基于Vue实现本地消息队列MQ的示例详解

    基于Vue实现本地消息队列MQ的示例详解

    这篇文章为大家详细主要介绍了如何基于Vue实现本地消息队列MQ, 让消息延迟消费或者做缓存,文中的示例代码讲解详细,需要的可以参考一下
    2024-02-02
  • 小白教程|一小时上手最流行的前端框架vue(推荐)

    小白教程|一小时上手最流行的前端框架vue(推荐)

    这篇文章主要介绍了前端框架vue,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Vue如何获取new Date().getTime()时间戳

    Vue如何获取new Date().getTime()时间戳

    在Web开发中,前端使用Vue.js获取的是毫秒级时间戳,而PHP后端则是秒级时间戳,处理此类问题时,需要将PHP的时间戳乘以1000转换为毫秒级,以保证数据的一致性和正确的逻辑判断
    2024-10-10
  • websocket在vue2中的封装使用方式

    websocket在vue2中的封装使用方式

    这篇文章主要介绍了websocket在vue2中的封装使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 在VUE3中禁止网页返回到上一页的方法

    在VUE3中禁止网页返回到上一页的方法

    这篇文章主要介绍了在VUE3中如何禁止网页返回到上一页,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • vue在项目中实现base64加密解密的示例代码

    vue在项目中实现base64加密解密的示例代码

    这篇文章主要为大家详细介绍了vue在项目中实现base64加密解密的两种方法,文中的示例代码讲解详细,具有一定的参考价值,有需要的小伙伴可以了解一下
    2023-10-10
  • Vue3中watch无法监听的解决办法

    Vue3中watch无法监听的解决办法

    本文主要介绍了Vue3中watch无法监听的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05

最新评论