vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3  TS Ant Design Vue菜单按钮

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

作者:奔跑的小蚂蚁9538

本文将分享如何使用Vue3 + TypeScript + Vite + Ant Design Vue实现一个功能完善的菜单与按钮级别的权限授权组件,支持树形菜单选择、动态按钮联动、状态持久化等高级特性,感兴趣的朋友一起看看吧

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

🎯 核心功能亮点

📁 项目结构概览

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;
}

🚀 性能优化技巧

📝 总结

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

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

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

您可能感兴趣的文章:
阅读全文