项目

Blazor UI:导航 / 菜单

每个应用程序都有一个主菜单,允许用户导航到应用程序的页面/屏幕。某些应用程序可能在不同UI部分包含多个菜单。

ABP 是一个模块化应用开发框架。每个模块都可能需要向菜单添加项目

因此,ABP提供了一个菜单基础架构,其中:

  • 应用程序或模块可以向菜单添加项目,而无需知道菜单是如何渲染的。
  • 主题能够正确地渲染菜单。

添加菜单项

为了添加菜单项(或操作现有项),您需要创建一个实现 IMenuContributor 接口的类。

应用程序启动模板 已经包含了一个 IMenuContributor 的实现。因此,您可以在该类内部添加项目,而无需创建新类。

示例:添加一个包含 客户订单 子菜单项的 CRM 菜单项

using System.Threading.Tasks;
using MyProject.Localization;
using Volo.Abp.UI.Navigation;

namespace MyProject.Web.Menus
{
    public class MyProjectMenuContributor : IMenuContributor
    {
        public async Task ConfigureMenuAsync(MenuConfigurationContext context)
        {
            if (context.Menu.Name == StandardMenus.Main)
            {
                await ConfigureMainMenuAsync(context);
            }
        }

        private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
        {
            var l = context.GetLocalizer<MyProjectResource>();

            context.Menu.AddItem(
                new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"])
                    .AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Customers", 
                        displayName: l["Menu:Customers"], 
                        url: "/crm/customers")
                    ).AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Orders", 
                        displayName: l["Menu:Orders"],
                        url: "/crm/orders")
                     )
            );
        }
    }
}
  • 此示例仅向主菜单(StandardMenus.Main:参见下面的标准菜单部分)添加项目。
  • 它从 context 获取一个 IStringLocalizer本地化菜单项的显示名称。
  • 将“客户”和“订单”添加为CRM菜单的子项。

创建菜单贡献者后,您需要将其添加到模块的 ConfigureServices 方法中的 AbpNavigationOptions

Configure<AbpNavigationOptions>(options =>
{
    options.MenuContributors.Add(new MyProjectMenuContributor());
});

此示例使用了一些本地化键作为显示名称,这些键应在本地化文件中定义:

"Menu:CRM": "CRM",
"Menu:Orders": "订单",
"Menu:Customers": "客户"

有关本地化的更多信息,请参阅本地化文档

运行应用程序时,您将看到添加到主菜单的菜单项:

nav-main-menu

菜单由当前UI主题渲染。因此,根据您的主题,主菜单的外观可能完全不同。

关于菜单贡献者,需要注意以下几点:

  • ABP在需要渲染菜单时会调用 ConfigureMenuAsync 方法。
  • 每个菜单项都可以有子项。因此,您可以添加无限深度的菜单项(但是,您的UI主题可能不支持无限深度)。
  • 通常,只有叶子菜单项才有 url。当您单击父菜单时,会打开或关闭其子菜单,您不会导航到父菜单项的 url
  • 如果一个菜单项没有子项且未定义 url,则它不会在UI上渲染。这简化了菜单项的授权:您只需要授权子项(参见下一节)。如果没有任何子项被授权,则父项会自动消失。

菜单项属性

菜单项有更多选项(ApplicationMenuItem 类的构造函数)。以下是所有可用选项的列表:

  • name (string, 必需):菜单项的唯一名称
  • displayName (string, 必需):菜单项的显示名称/文本。您可以像之前所示那样本地化它。
  • url (string):菜单项的URL。
  • icon (string):图标名称。默认支持免费的 Font Awesome 图标类。例如:fa fa-book。只要您将必要的CSS文件包含到应用程序中,您可以使用任何CSS字体图标类。
  • order (int):菜单项的顺序。默认值为 1000。除非您指定顺序值,否则项目按添加顺序排序。
  • customData (object):一个可以关联到菜单项并在渲染菜单项时使用的自定义对象。
  • target (string):菜单项的目标。可以为 null(默认)、"_blank"、"_" 或 Web 应用程序的框架名称。
  • elementId (string):可用于渲染具有特定 HTML id 属性的元素。
  • cssClass (string):菜单项的附加字符串类。
  • groupName (string):可用于对菜单项进行分组。

授权

如上所示,菜单贡献者动态地贡献菜单。因此,您可以执行任何自定义逻辑或从任何源获取菜单项。

一个用例是授权。您通常希望通过检查权限来添加菜单项。

示例:检查当前用户是否拥有权限

if (await context.IsGrantedAsync("MyPermissionName"))
{
    //...添加菜单项
}

您可以使用 context.AuthorizationService 直接访问 IAuthorizationService

自定义组件

可以使用自定义组件来渲染菜单项,而不是使用主题的默认组件。Volo.Abp.UI.Navigation 命名空间中的 UseComponent 扩展方法可用于为菜单项设置自定义组件。

示例:为菜单项设置自定义组件

context.Menu.Items.Add(
    new ApplicationMenuItem("MyProject.Crm.About", "关于", "/about")
        .UseComponent<AboutMenuItemComponent>()
        );

菜单项将渲染 _AboutMenuItemComponent.razor 组件。

解析依赖关系

可以使用 context.ServiceProvider 来解析任何服务依赖。

示例:获取服务

var myService = context.ServiceProvider.GetRequiredService<IMyService>();
//...使用服务

您无需关心释放/处置服务。ABP会处理它。

管理菜单

菜单中有一个由ABP添加的特殊菜单项:管理菜单。它通常由预构建的管理 应用程序模块 使用:

nav-main-menu-administration

如果您想向管理菜单项下添加菜单项,可以使用 context.Menu.GetAdministration() 扩展方法:

context.Menu.GetAdministration().AddItem(...)

操作现有菜单项

ABP 按照模块依赖顺序执行菜单贡献者。因此,您可以操作您的应用程序或模块(直接或间接)所依赖的菜单项。

示例:为身份模块添加的 Users 菜单项设置图标

var userMenu = context.Menu.FindMenuItem(IdentityMenuNames.Users);
userMenu.Icon = "fa fa-users";

context.Menu 使您能够访问所有先前菜单贡献者添加的菜单项。

菜单组

您可以定义组,并将菜单项与组关联。

示例:

using System.Threading.Tasks;
using MyProject.Localization;
using Volo.Abp.UI.Navigation;

namespace MyProject.Web.Menus
{
    public class MyProjectMenuContributor : IMenuContributor
    {
        public async Task ConfigureMenuAsync(MenuConfigurationContext context)
        {
            if (context.Menu.Name == StandardMenus.Main)
            {
                await ConfigureMainMenuAsync(context);
            }
        }

        private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
        {
            var l = context.GetLocalizer<MyProjectResource>();

            context.Menu.AddGroup(
                new ApplicationMenuGroup(
                    name: "Main",
                    displayName: l["Main"]
                )
            )
            context.Menu.AddItem(
                new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"], groupName: "Main")
                    .AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Customers", 
                        displayName: l["Menu:Customers"], 
                        url: "/crm/customers")
                    ).AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Orders", 
                        displayName: l["Menu:Orders"],
                        url: "/crm/orders")
                     )
            );      
        }
    }
}

UI主题将决定是否渲染组,如果决定渲染,渲染方式由主题决定。目前只有LeptonX主题实现了菜单组。

标准菜单

菜单是一个命名组件。一个应用程序可以包含多个具有不同唯一名称的菜单。有两个预定义的标准菜单:

  • Main:应用程序的主菜单。包含指向应用程序页面的链接。定义为常量:Volo.Abp.UI.Navigation.StandardMenus.Main
  • User:用户资料菜单。定义为常量:Volo.Abp.UI.Navigation.StandardMenus.User

上面的内容已经涵盖了 Main 菜单。当用户登录时,User 菜单可用:

user-menu

您可以通过检查 context.Menu.Name 来向 User 菜单添加项目,如下所示:

if (context.Menu.Name == StandardMenus.User)
{
    //...添加项目
}

IMenuManager

IMenuManager 通常由 UI 主题 用于在 UI 上渲染菜单项。因此,通常不需要直接使用 IMenuManager

示例:获取主菜单以在 Razor 组件中渲染

// Razor 组件的代码后置文件
public partial class NavMenu
{
    private readonly IMenuManager _menuManager;

    public NavMenu(IMenuManager menuManager)
    {
        _menuManager = menuManager;
    }
    
    protected override async Task OnInitializedAsync()
    {
        var menu = await _menuManager.GetAsync(StandardMenus.Main);
        //...
    }
}
在本文档中