You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于用户权限条件构建/修改嵌套菜单对象数组?

基于用户权限动态构建嵌套菜单的解决方案

我需要基于用户权限构建可无限嵌套的菜单,现有菜单数组和模板代码如下,sessionStorage中存储着布尔型权限字段(如canCreateClients),希望根据权限自动增删菜单项,但嵌套三元运算符太繁琐,手动删除又不实用,求合适方案。

菜单数组示例

this.model = [
    {
        label: "Admin",
        items: [
            {
                label: "Users",
                icon: "fas fa-list",
                routerLink: ["/admin/users/"],
            },
            {
                label: "Clients",
                icon: "fas fa-list",
                items: [
                    {
                        label: "List",
                        icon: "fas fa-list",
                        routerLink: ["/client/list"],
                    },
                    {
                        label: "Create",
                        icon: "fas fa-list",
                        routerLink: ["/client/create/"],
                    },
                ],
            },
        ],
    },
];

菜单模板代码

<ul class="layout-menu">
    <ng-container *ngFor="let item of model; let i = index;">
        <li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
        <li *ngIf="item.separator" class="menu-separator"></li>
    </ng-container>
</ul>

方法1:权限标识+递归过滤函数

给每个需要权限控制的菜单项新增permission字段,然后写一个递归函数一次性过滤整个菜单数组,逻辑集中且适合深嵌套场景。

步骤1:给菜单项添加权限标识

this.model = [
    {
        label: "Admin",
        items: [
            {
                label: "Users",
                icon: "fas fa-list",
                routerLink: ["/admin/users/"],
                permission: "canViewUsers" // 绑定对应权限字段
            },
            {
                label: "Clients",
                icon: "fas fa-list",
                permission: "canViewClientsModule", // 父菜单权限,控制整个模块显示
                items: [
                    {
                        label: "List",
                        icon: "fas fa-list",
                        routerLink: ["/client/list"],
                        permission: "canViewClients"
                    },
                    {
                        label: "Create",
                        icon: "fas fa-list",
                        routerLink: ["/client/create/"],
                        permission: "canCreateClients"
                    },
                ],
            },
        ],
    },
];

步骤2:实现递归过滤函数

// 从sessionStorage读取用户权限
const getUserPermissions = () => {
    const permissions = sessionStorage.getItem('userPermissions');
    return permissions ? JSON.parse(permissions) : {};
};

// 递归过滤菜单,保留有权限的项
const filterMenuByPermissions = (menuItems: any[], permissions: any): any[] => {
    return menuItems.filter(item => {
        // 无权限标识的菜单项默认显示
        if (!item.permission) return true;
        
        const hasPermission = permissions[item.permission];
        // 递归处理子项
        if (item.items?.length) {
            item.items = filterMenuByPermissions(item.items, permissions);
            // 可选逻辑:父菜单有权限但子项全被过滤时,隐藏父菜单
            return hasPermission && item.items.length > 0;
        }
        return hasPermission;
    });
};

// 在组件初始化时执行过滤
const userPermissions = getUserPermissions();
this.model = filterMenuByPermissions(this.model, userPermissions);

方法2:递归组件内处理权限判断

如果不想修改原菜单数组,可以把权限判断逻辑放到app-menuitem组件内部,利用Angular递归组件特性渲染子项。

步骤1:改造app-menuitem组件

// app-menuitem.component.ts
import { Component, Input } from '@angular/core';

@Component({
    selector: 'app-menuitem',
    templateUrl: './app-menuitem.component.html'
})
export class AppMenuitemComponent {
    @Input() item: any;
    @Input() index: number;
    @Input() root: boolean;

    // 计算属性:判断当前菜单项是否有权限显示
    get hasPermission(): boolean {
        if (!this.item.permission) return true;
        const permissions = JSON.parse(sessionStorage.getItem('userPermissions') || '{}');
        return permissions[this.item.permission];
    }
}

步骤2:修改app-menuitem模板

<!-- app-menuitem.component.html -->
<li *ngIf="hasPermission">
    <!-- 无下拉的菜单项 -->
    <a [routerLink]="item.routerLink" *ngIf="!item.items">
        <i [class]="item.icon"></i>
        <span>{{ item.label }}</span>
    </a>
    <!-- 带下拉的父菜单项 -->
    <div *ngIf="item.items?.length">
        <i [class]="item.icon"></i>
        <span>{{ item.label }}</span>
        <ul>
            <ng-container *ngFor="let subItem of item.items; let i = index;">
                <app-menuitem [item]="subItem" [index]="i" [root]="false"></app-menuitem>
            </ng-container>
        </ul>
    </div>
</li>

父模板无需改动,所有权限控制逻辑都在子组件内完成,适合希望保持原菜单数组纯净的场景。

方法3:权限映射表+递归过滤

若不想给菜单项加permission字段,可以单独维护一个权限映射表,将菜单标识(路由/路径拼接)与权限字段绑定,实现无侵入式过滤。

// 权限映射表:菜单标识 => 对应权限字段
const permissionMap = new Map([
    ['/admin/users/', 'canViewUsers'],
    ['/client/list', 'canViewClients'],
    ['/client/create/', 'canCreateClients'],
    ['Admin/Clients', 'canViewClientsModule'] // 父菜单用"父标签/子标签"作为标识
]);

// 递归过滤函数
const filterMenuByMap = (menuItems: any[], permissions: any): any[] => {
    return menuItems.filter(item => {
        let hasPermission = true;
        // 生成当前项的标识:有路由用路由,无路由用标签拼接
        const itemKey = item.routerLink 
            ? item.routerLink.join('') 
            : `${item.label}/${(item.items?.[0]?.label || '')}`;
        
        if (permissionMap.has(itemKey)) {
            hasPermission = permissions[permissionMap.get(itemKey)];
        }
        // 递归处理子项
        if (item.items?.length) {
            item.items = filterMenuByMap(item.items, permissions);
            hasPermission = hasPermission && item.items.length > 0;
        }
        return hasPermission;
    });
};

// 使用方式同方法1
const userPermissions = getUserPermissions();
this.model = filterMenuByMap(this.model, userPermissions);

这种方式的权限配置集中在映射表,便于统一维护,适合菜单结构固定的场景。


内容的提问来源于stack exchange,提问作者cwmd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 10:07:02