Angular UI:主题系统
引言
ABP 提供了一套完整的 UI 主题系统,旨在实现以下目标:
- 可复用的应用模块以与主题无关的方式进行开发,从而可以与任何 UI 主题协同工作。
- UI 主题由最终应用程序决定。
- 主题通过 NPM 包分发,便于轻松升级。
- 最终应用程序可以自定义所选的主题。
为了实现这些目标,ABP 采取了以下措施:
- 确定了一套被所有主题使用和适配的基础库。这样,模块和应用开发者可以依赖并使用这些库,而无需依赖特定的主题。
- 提供了一套由所有主题实现的布局部分(如导航菜单和工具栏)组成的系统。因此,模块和应用程序可以共同作用于布局,从而构建出一致的应用 UI。
当前提供的主题
目前,官方提供了三个主题:
- 基础主题 是采用原生 Bootstrap 风格的极简主题。它是开源且免费的。
- Lepton 主题 是由 ABP 核心团队开发的商业主题,是 ABP 许可证的一部分。
- LeptonX 主题 是一个同时提供商业版和精简版选择的主题。
概述
基础库
所有主题都必须依赖 @abp/ng.theme.shared NuGet 包,因此它们间接依赖以下库:
- Twitter Bootstrap 作为基础的 HTML/CSS 框架。
- FontAwesome 作为基础的 CSS 字体库。
- NG Bootstrap 作为支持 Bootstrap 并添加了模态框和日期选择器等额外组件的组件库。
- Ngx-Datatable 作为数据表格库。
- ngx-validate 一个用于响应式表单的动态验证库。
- Chart.js 作为图表库。
这些库被选为基础库,可供应用程序和模块使用。
由于 NG Bootstrap 库已经以原生方式为 Bootstrap 组件提供了必要的功能,因此 Bootstrap 的 JavaScript 部分未被使用。
布局
所有主题都必须为应用程序定义一个布局。下图展示了基础主题应用布局中的用户管理页面:
而同一页面在 Lepton 主题 应用布局下的展示如下:
正如您所见,页面内容相同,但在上述主题中外观完全不同。
应用程序布局通常包含以下部分:
- 主菜单
- 导航项区域,包含以下组件:
- 用户菜单
- 语言切换下拉菜单
- 页面提示
- 页面内容(即
<router-outlet>)
实现一个主题
一个主题本质上是一个 NPM 包,并随启动模板一同提供。
简单方法
创建新主题最简单的方法是通过 ABP CLI 命令将基础主题源代码添加到您的项目中并进行自定义。
您可以在 Angular 项目目录中运行以下命令,将源代码复制到您的解决方案中:
abp add-package @abp/ng.theme.basic --with-source-code
全局/组件样式
Angular 可以将全局样式文件和组件样式与组件打包在一起。 有关更多信息,请参阅 Angular 文档中的组件样式指南。
布局部分
典型的布局由若干部分组成。主题应在每个布局中包含必要的部分。
示例:基础主题的应用布局包含以下部分
应用程序代码和模块只能在"页面内容"部分显示内容。如果它们需要更改其他部分(例如添加菜单项、添加导航项、更改徽标区域的应用名称...),则应使用 ABP 提供的 API。
以下部分解释了由 ABP 预定义、可由主题实现的基本部分。
将布局拆分为组件/部分是一个好的做法,这样最终应用程序可以为了自定义目的而部分覆盖它们。
徽标
应在环境文件的 application 对象中进行配置,以获取要在徽标部分渲染的应用程序名称和徽标 URL。此外,可以替换 LogoComponent。有关更多信息,请参阅组件替换文档。
应用程序启动模板 实现了此接口,供应用程序开发者设置这些值。
主菜单 / 路由
RoutesService 服务用于管理主菜单项并在布局上渲染它们。
示例:向主菜单添加路由
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { Component, inject } from '@angular/core';
@Component(/* 组件元数据 */)
export class AppComponent {
private routes = inject(RoutesService);
constructor() {
this.routes.add([
{
path: '/your-path',
name: 'Your navigation',
order: 101,
iconClass: 'fas fa-question-circle',
requiredPolicy: 'permission key here',
layout: eLayoutType.application,
},
{
path: '/your-path/child',
name: 'Your child navigation',
parentName: 'Your navigation',
order: 1,
requiredPolicy: 'permission key here',
},
]);
}
}
有关导航系统的更多信息,请参阅修改菜单文档。
工具栏 / 导航项
NavItemsService 服务用于获取菜单右侧部分的项并在布局上渲染。您可以添加 HTML 内容或 Angular 组件作为要渲染的元素。
示例:向菜单右侧部分添加元素
import { NavItemsService } from '@abp/ng.theme.shared';
import { Component, inject } from '@angular/core';
@Component({
template: `
<input type="search" placeholder="Search" class="bg-transparent border-0 color-white" />
`,
})
export class MySearchInputComponent {}
@Component(/* 组件元数据 */)
export class AppComponent {
private navItems = inject(NavItemsService);
constructor() {
this.navItems.addItems([
{
id: 'MySearchInput',
order: 1,
component: MySearchInputComponent,
},
{
id: 'SignOutIcon',
html: '<i class="fas fa-sign-out-alt fa-lg text-white m-2"><i>',
action: () => console.log('Clicked the sign out icon'),
order: 101, // 作为最后一个元素放置
},
]);
}
}
有关导航项系统的更多信息,请参阅 如何向菜单右侧部分添加元素 文档。
主题有责任向工具栏添加两个预定义的项:语言选择器和用户菜单。
语言选择
语言选择器工具栏项通常是一个下拉菜单,用于在语言之间切换。ConfigStateService 用于获取可用语言列表,SessionStateService 用于获取当前语言。
SessionStateService 用于获取和设置当前语言。
示例:获取当前选定的语言
import { SessionStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
//...
const sessionState = inject(SessionStateService);
const lang = sessionState.getLanguage();
示例:设置选定语言
import { SessionStateService } from '@abp/ng.core';
import { inject } from '@angular/core';
//...
const sessionState = inject(SessionStateService);
sessionState.setLanguage('en');
用户菜单
UserMenuService 用于获取用户菜单项并在下拉菜单中渲染。您可以使用该服务添加/更新/删除菜单项。
您可以通过传递一个 component、一段 html 或一个 textTemplate 来渲染一个项。
所有选项如下所示。您可以选择其中任意一种。
示例:添加/更新/删除用户菜单项
import { eUserMenuItems } from '@abp/ng.theme.basic';
import { UserMenuService } from '@abp/ng.theme.shared';
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
// 确保在 NgModule 中导入此组件
@Component({
selector: 'abp-current-user-test',
template: `
<a class="dropdown-item pointer" (click)="data.action()">
<i *ngIf="data.textTemplate.icon" [class]="data.textTemplate.icon"></i>
{{ data.textTemplate.text | abpLocalization }}
</a>
`,
})
export class UserMenuItemComponent {
// 您可以通过 `INJECTOR_PIPE_DATA_TOKEN` 令牌注入数据
public data = inject<UserMenu>(INJECTOR_PIPE_DATA_TOKEN);
}
@Component({/* 组件元数据 */})
export class AppComponent {
private userMenu = inject(UserMenuService);
private router = inject(Router);
constructor() {
this.userMenu.addItems([
{
id: 'UserMenu.MyAccount',
order: 1,
// HTML 示例
html: `<a class="dropdown-item pointer">My account</a>`,
// 文本模板示例
textTemplate: {
text: 'AbpAccount::MyAccount',
icon: 'fa fa-cog',
},
// 组件示例
component: UserMenuItemComponent,
action: () => {
this.router.navigateByUrl('/account/manage');
},
},
]);
this.userMenu.patchItem(eUserMenuItems.MyAccount, {
html: `<a class="dropdown-item pointer">My profile</a>`,
});
this.userMenu.removeItem(eUserMenuItems.Logout);
}
}
在上面的示例中,添加了一个标记为"报告"的新用户菜单项,更新了"我的账户"项的 HTML 内容,并移除了"注销"项。
查看结果:
页面提示
PageAlertService 服务用于获取当前页面提示并在布局上渲染。有关更多信息,请参阅页面提示文档。
抠丁客






