基于Vue3?+?TS?+?Ant?Design?Vue?實現(xiàn)精細(xì)化菜單按鈕權(quán)限授權(quán)組件功能
在前端開發(fā)中,RBAC(基于角色的訪問控制)是企業(yè)級應(yīng)用的核心需求。本文將分享如何使用 Vue3 + TypeScript + Vite + Ant Design Vue 實現(xiàn)一個功能完善的菜單與按鈕級別的權(quán)限授權(quán)組件,支持樹形菜單選擇、動態(tài)按鈕聯(lián)動、狀態(tài)持久化等高級特性。
?? 核心功能亮點
- ? 雙欄布局:左側(cè)樹形菜單選擇,右側(cè)動態(tài)按鈕列表
- ? 細(xì)粒度控制:支持菜單級和按鈕級雙重權(quán)限控制
- ? 狀態(tài)保持:切換菜單時自動保存/恢復(fù)按鈕選中狀態(tài)
- ? 父子聯(lián)動:自動處理菜單的半選/全選狀態(tài)邏輯
- ? 類型安全:完整的 TypeScript 類型定義
- ? 性能優(yōu)化:使用 Map 緩存菜單數(shù)據(jù),避免重復(fù)查詢
?? 項目結(jié)構(gòu)概覽
src/
├── components/
│ └── MenuAuthModal.vue # 菜單授權(quán)彈窗組件
├── store/
│ └── index.ts # Vuex Store,管理全局按鈕權(quán)限
└── api/
└── contract.ts # 權(quán)限相關(guān) API 接口?? 核心技術(shù)實現(xiàn)
1. 組件架構(gòu)設(shè)計
采用 Ant Design Vue 的 Modal + Tree + Checkbox 組合,實現(xiàn)左右分欄布局:
<template>
<a-modal :visible="visible" width="960px">
<div class="menu-auth-content">
<!-- 標(biāo)題行 -->
<div class="header-row">
<div class="header-left">可授權(quán)菜單</div>
<div class="header-right">可授權(quán)按鈕</div>
</div>
<!-- 內(nèi)容區(qū)域 -->
<div class="content-row">
<!-- 左側(cè):樹形菜單 -->
<div class="content-left">
<a-tree
v-model:checkedKeys="checkedMenuKeys"
v-model:halfCheckedKeys="halfCheckedMenuKeys"
checkable
:tree-data="menuTreeData"
@select="onMenuSelect"
/>
</div>
<!-- 右側(cè):按鈕列表 -->
<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. 狀態(tài)管理策略
使用 全局 Map 保存每個菜單的按鈕選中狀態(tài),解決切換菜單時狀態(tài)丟失問題:
// 全局按鈕選中狀態(tài)(菜單ID -> 按鈕ID數(shù)組)
const globalButtonStates = ref<Map<string, string[]>>(new Map())
// 監(jiān)聽按鈕變化,實時保存
watch(checkedButtons, (newValue) => {
if (isRestoringState.value) return // 避免恢復(fù)狀態(tài)時重復(fù)觸發(fā)
if (selectedMenuKeys.value.length > 0) {
const currentMenuId = selectedMenuKeys.value[0]
globalButtonStates.value.set(currentMenuId, [...newValue])
}
}, { deep: true })3. 菜單樹數(shù)據(jù)處理
將后端返回的嵌套菜單結(jié)構(gòu)轉(zhuǎn)換為 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. 權(quán)限收集邏輯
提交時遞歸收集所有選中菜單(含父級)和按鈕:
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. 全局權(quán)限存儲
在 Vuex Store 中構(gòu)建按鈕權(quán)限映射表,供全局使用:
// 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 用于權(quán)限判斷
hasButtonPermission: (state) => (menuActive: string, buttonId: number | string) => {
const buttonKey = `${menuActive}_${buttonId}`
return state.buttonPermissions.has(buttonKey)
}?? 使用示例
父組件調(diào)用
<template>
<a-button @click="openAuthModal">菜單授權(quán)</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('授權(quán)成功', { roleId, menuPermissions, buttonPermissions })
// 刷新列表或更新狀態(tài)
}
</script>頁面按鈕權(quán)限控制
<template>
<a-button
v-if="$store.getters.hasButtonPermission('ContractList', 1001)"
@click="handleEdit"
>
編輯
</a-button>
</template>?? 樣式優(yōu)化
使用 Less 實現(xiàn)美觀的雙欄布局和滾動條樣式:
.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;
}?? 性能優(yōu)化技巧
- Map 緩存:使用
Map<string, Menu>快速查找菜單對象,避免 O(n) 遍歷 - 防抖處理:狀態(tài)恢復(fù)時使用
isRestoringState標(biāo)志位,防止 watch 重復(fù)觸發(fā) - 按需渲染:僅當(dāng)選中菜單時才加載對應(yīng)的按鈕列表
- 深度克隆:使用
[...array]避免引用導(dǎo)致的意外修改
?? 總結(jié)
本方案實現(xiàn)了企業(yè)級應(yīng)用所需的完整權(quán)限授權(quán)功能,具有以下優(yōu)勢:
- 用戶體驗佳:直觀的樹形選擇 + 實時按鈕預(yù)覽
- 代碼可維護(hù):清晰的職責(zé)分離,TypeScript 類型保障
- 擴展性強:易于添加新的權(quán)限維度(如數(shù)據(jù)權(quán)限)
- 性能優(yōu)秀:合理的緩存策略和狀態(tài)管理
通過本文的實現(xiàn),你可以快速在自己的項目中集成類似的權(quán)限管理系統(tǒng),提升應(yīng)用的安全性和靈活性。
到此這篇關(guān)于基于 Vue3 + TS + Ant Design Vue 實現(xiàn)精細(xì)化菜單按鈕權(quán)限授權(quán)組件的文章就介紹到這了,更多相關(guān)基于 Vue3 + TS + Ant Design Vue 實現(xiàn)精細(xì)化菜單按鈕權(quán)限授權(quán)組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3使用watch監(jiān)聽props的值的注意事項及說明
這篇文章主要介紹了vue3使用watch監(jiān)聽props的值的注意事項及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06
詳解基于Vue2.0實現(xiàn)的移動端彈窗(Alert, Confirm, Toast)組件
這篇文章主要介紹了詳解基于Vue2.0實現(xiàn)的移動端彈窗(Alert, Confirm, Toast)組件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
解決vue js IOS H5focus無法自動彈出鍵盤的問題
今天小編就為大家分享一篇解決vue js IOS H5focus無法自動彈出鍵盤的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08

