如何基于用户权限条件构建/修改嵌套菜单对象数组?
基于用户权限动态构建嵌套菜单的解决方案
我需要基于用户权限构建可无限嵌套的菜单,现有菜单数组和模板代码如下,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
相关产品推荐
相关产品推荐

