这些指南旨在创建一个协调一致的管理员界面,重点关注可用性、可访问性和结构化布局。请参阅目录以了解包含的内容并轻松导航到每个部分。
注意:此处使用的术语在管理员界面术语表中有定义。
0. 前言 - 配置页面结构和侧边栏链接
在管理员界面中添加新的配置页面时,每个页面都需要一个侧边栏链接,并且每个页面都需要一个标题和头部描述。这是为了确保我们保持一致,并且未来对管理员搜索的增强可以显示管理员界面的整个布局。
通常,管理员界面的结构如下所示:
- 管理员界面
- 配置页面(显示在侧边栏中)
- 设置选项卡
- 其他可选的第三级选项卡
- 资源的编辑/新建第三级页面
- 配置页面(显示在侧边栏中)
最终,将在根界面和配置页面之间插入一个“部分概览”。
侧边栏链接
所有管理员页面都应添加到 discourse/frontend/discourse/app/lib/sidebar/admin-nav-map.js at main · discourse/discourse · GitHub 中的 ADMIN_NAV_MAP。每个项至少应包含以下键:
name- 链接的唯一标识符,应为snake_caseroute或href-route是 Ember 路由标识符,例如adminUsers。对于管理员,这些在管理员路由映射中定义。也可以使用href,但推荐使用route。label或text- Label 是 I18n 键,通常应为admin.config.page_name.title(见下文翻译部分)。如果使用text,则将是已翻译的文本。
还可以提供以下可选键:
description- 建议你也提供这个。它是一个 I18n 键,通常应为admin.config.page_name.header_description。icon- 也建议,显示在侧边栏链接旁边。routeModels- 用于路由参数的 URL 数据数组。例如adminCustomizeThemes有一个:type路由参数,因此你可以传递routeModels: ["components"]。数组项按路由参数出现的顺序使用。moderator:如果版主应在侧边栏中看到此页面,请将其设置为true。keywords:一个 I18n 键,包含用|分隔的侧边栏链接关键词列表,用于在过滤/搜索页面时提供额外的“搜索权重”。links:位于侧边栏页面下方的第三级路由列表。这些不会直接显示在侧边栏中。这将用于未来的管理员搜索功能。settings_area和settings_category:如果页面仅显示过滤后的站点设置列表,则应填写其中之一。如果站点设置定义了area(在AdminAreaSettings中使用),则应使用settings_area。如果页面上显示整个设置类别,并且在AdminAreaSettings中也使用,则应使用settings_category。multi_tabbed:如果页面有设置选项卡和其他选项卡,则应将其设置为 true。它有助于为管理员搜索系统生成链接。
翻译
每个配置页面的标题和头部描述应位于:
- admin
- config
- page_name
- title: “页面标题”
- header_description: “此页面用于 xyz”
- page_name
- config
你可以在这里看到示例:
1. 面包屑导航
面包屑导航作为一种导航工具,帮助用户了解他们在管理员界面中的当前位置、内容结构和层级关系。
管理员 > 面包屑 > 路径
页面标题
设计
结构
- 管理员:固定前缀,出现在每个面包屑路径的开头,链接到
/admin - 链接:在同一窗口中打开页面
- 分隔符:
angle-right图标分隔每个链接
用法
何时使用:
- 出现在每个管理员页面上
- 位于内容(标题、描述、选项卡)上方
- 显示当前选中的页面
何时不使用:
- 访问新建或编辑路由时
内容
- 每个项包含指向其相关页面的链接
- 显示当前选中的页面
可访问性
- 带有
aria-label="Breadcrumb"的nav元素包裹有序列表,以提供导航地标 - 在最后一个链接上应用
aria-current="page"以指示它是当前页面 - 更多详情,请参阅 WAI-ARIA 作者实践面包屑示例
实现
DBreadcrumbsContainer 组件必须放置在页面的某个位置:
<DBreadcrumbsContainer />
然后,添加到路由或子路由上任何组件的每个 DBreadcrumbsItem 元素都将渲染到这个容器中。每个 DBreadcrumbsItem 必须提供 @label 和 @path:
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
<DBreadcrumbsItem
@path="/admin/plugins"
@label={{i18n "admin.plugins.title"}}
/>
<DBreadcrumbsItem
@path="/admin/plugins/{{@plugin.name}}"
@label={{@plugin.nameTitleized}}
/>
```\n
使用 Discourse AI 插件的视觉示例如下所示:

# 2. 页面头部和标题
管理员页面的顶部部分,包含页面标题以及可选的操作和描述。

## :art: 设计
**结构**
* **页面标题:** 页面的标题
* **页面描述:** 内容涵盖内容的简介或描述 *(可选)*
* **主要操作:** 页面标题主要操作 *(可选)*
* **次要操作:** 页面标题次要操作按钮设置 *(可选)*
**用法和内容**
* **页面标题:** 使用一级标题以句子大小写解释页面的主要主题。通常 I18n 翻译应位于 `admin.config.your_page.title` 下。
* **页面描述:** 支持基本 markdown 节点,如 `_斜体_`、`**粗体**` 和 `[链接名称](url)`
* **主要操作:** 使用 `btn-primary`。不要包含图标。通常 I18n 翻译应位于 `admin.config.your_page.header_description` 下。
* **次要操作:** 使用 `btn-default` 按钮设置,仅当存在主要操作时才可见。不要包含图标。
> :point_right: 操作按钮要清晰。例如,使用描述性标签如“添加表情符号”而不是简单的“添加”以减少歧义。
## :hammer_and_wrench: 实现
这里使用 `DPageHeader` 组件。它接受 `@titleLabel`、`@descriptionLabel`、`@learnMoreUrl` 和 `@shouldDisplay` 参数。这使用 Ember 中的命名 `yields` 为内容提供 5 个命名块:
1. `breadcrumbs` - 页面的任何额外 `DBreadcrumbsItem` 组件应放置在此处。
2. `actions` - 用于定义标题右侧的按钮。这会产生一个名为 `actions` 的对象,可用于渲染 `Default`、`Primary`、`Danger` 和 `Wrapped` 按钮。
3. `title` - `@titleLabel` 的替代方案,允许在标题中使用自定义标记。
4. `drawer` - 一个可选的可折叠抽屉部分,当 `@showDrawer` 为 true 时显示。
5. `tabs` - 使用 `NavItem` 组件定义页面的选项卡。`@hideTabs` 可用于在不需要的情况下删除头部的这部分。
完整示例如下:
```gjs
<DPageHeader
@titleLabel={{i18n "admin.config.backups.title"}}
@descriptionLabel={{i18n "admin.config.backups.header_description"}}
@learnMoreUrl="https://meta.discourse.org/t/create-download-and-restore-a-backup-of-your-discourse-database/122710"
>
<:breadcrumbs>
<DBreadcrumbsItem
@path="/admin/backups"
@label={{i18n "admin.backups.title"}}
/>
</:breadcrumbs>
<:actions as |actions|>
<actions.Primary
@action={{routeAction "showStartBackupModal"}}
@title="admin.backups.operations.backup.title"
@label="admin.backups.operations.backup.label"
class="admin-backups__start"
/>
</:actions>
<:tabs>
<NavItem
@route="admin.backups.settings"
@label="settings"
class="admin-backups-tabs__settings"
/>
<NavItem
@route="admin.backups.index"
@label="admin.backups.menu.backup_files"
class="admin-backups-tabs__files"
/>
<NavItem
@route="admin.backups.logs"
@label="admin.backups.menu.logs"
class="admin-backups-tabs__logs"
/>
<PluginOutlet @name="downloader" @connectorTagName="div" />
</:tabs>
</DPageHeader>
浏览器选项卡的页面标题在 Ember 路由中使用 titleToken 功能处理。每次在路由中使用它时,它都会将令牌添加到浏览器选项卡标题的末尾。请注意,你必须使用 DiscourseRoute 类来扩展你的路由,而不是 Ember 中的普通 Route,才能使其工作:
titleToken() {
return i18n("admin.config.backups.title");
}
页面头部对于
/new和/edit路径自动隐藏,以支持第三级路由。可以通过使用@shouldDisplay参数来覆盖。
3. 选项卡
一个可选的导航,提供对更深层设置或功能的访问。我们也将其称为“第三级”页面或导航。
设计
我们使用选项卡在相同上下文中的不同但相关的视图之间切换。
用法
- 不用于主要导航
- 一次只有一个处于活动状态
实现
参见页面头部详细信息,选项卡在 DPageHeader 组件中定义。
![]()
4. 概览/部分着陆页
允许用户查看部分的内容,特别是在侧边栏折叠或在移动设备上时。
设计
结构
使用网格系统使用三个相等的列布局。在小屏幕上,这些列将垂直堆叠。
设计和用法
- 可以通过面包屑访问(管理员 > 社区 > 概览)
- 每个部分应该有一个,除了插件(显示已安装的)和报告(只有一页)
- 项具有:
- 名称 - 与部分链接相同
- 描述 - 关于页面内容的简短描述
- 图标 - 与侧边栏使用的相同图标
实现
代码片段或链接到主题/GitHub
5. 页面内容
管理员页面的主要区域,在此显示和交互设置、配置和其他内容。
设计
结构
使用网格系统使用 2/3 + 1/3 布局。主要部分占据三分之二,次要部分占据三分之一的空间。在小屏幕上,这些列将垂直堆叠。
- 配置区域: 页面内容中专门用于设置和配置的部分。
- 帮助/参考/插入: 页面内容中提供指南、文档或额外上下文信息的区域。(可选)
设计和用法
- 将类似的设置和操作分组在卡片中
- 构建主要/次要布局,以便主要(2/3)部分用于主要设置,次要(1/3)部分用于额外信息或有帮助的上下文
- 如果次要部分不可用,保持主要部分的宽度相同
内容
实现
代码片段或 GitHub 链接
5.a. 副标题
副标题是用于分割部分下内容的次要标题,通常在选项卡下方。
结构
- 副标题: 内容涵盖的副标题 (可选)
- 主要操作: 副标题主要操作 (可选)
- 次要操作: 副标题次要操作按钮设置 (可选)
用法和内容
-
副标题: 使用二级标题解释相关内容的主要主题。仅当:
- 有主要操作按钮,或
- 有描述解释该部分。
-
主要操作: 使用
btn-primary。不要包含图标。 -
次要操作: 使用
btn-default按钮设置,仅当存在主要操作时才可见。不要包含图标。
操作按钮要清晰。例如,使用描述性标签如“添加表情符号”而不是简单的“添加”以减少歧义。
实现
这与 DPageHeader 类似,有一个 DPageSubheader 组件。主要区别是只有一个用于 actions 的命名 yield。
actions- 用于定义标题右侧的按钮。这会产生一个名为actions的对象,可用于渲染Default、Primary、Danger和Wrapped按钮。
<DPageSubheader @titleLabel="admin.config.backups.subheader.title">
<:actions>
<actions.Primary
@action={{routeAction "showStartBackupModal"}}
@title="admin.backups.operations.backup.title"
@label="admin.backups.operations.backup.label"
class="admin-backups__start"
/>
</:actions>
</DPageSubheader>
5.b. 配置区域
配置区域由卡片或部分构成。卡片非常适合分组相关信息和任务,帮助用户更轻松地扫描和优先处理内容。
设计
卡片
卡片设置为 2px 边框半径,并使用 --secondary 背景。它还有一个 1px 实线边框,带有 --primary-low 和内容周围 20px 的内边距。
默认变体
手风琴变体
设计和用法
- 分组相关信息
- 显示信息,以便管理员和版主首先看到最重要的内容
- 使用清晰解释卡片用途的标题
- 如有必要,将复杂的卡片分成多个部分
默认变体
- 每张卡片坚持一个主要号召性用语
- 将主要号召性用语放在卡片底部以进行下一步
手风琴变体
- 使用卡片的右上角进行可选操作,如“查看全部”
内容
-
所有表单应使用核心中描述的 FormKit ember 组件,详见文档
-
卡片标题应为句子大小写
做
不做常规设置 常规设置 联系信息 联系信息
实现
我们有一个 AdminConfigAreaCard 组件,应用于所有这些卡片。目前这只有 @translatedHeading 和 @heading 参数,未来我们可以添加操作并使它们可折叠等:
<AdminConfigAreaCard
@heading="admin.config_areas.about.general_settings"
class="admin-config-area-about__general-settings-section"
>
<AdminConfigAreasAboutGeneralSettings
@generalSettings={{this.generalSettings}}
@setGlobalSavingStatus={{this.setSavingStatus}}
@globalSavingStatus={{this.saving}}
/>
</AdminConfigAreaCard>
嵌入式站点设置
此部分仍在进行中。
5.c. 帮助插入
此部分在页面内容中提供额外的指导、文档或上下文。
v1
设计
设计和用法
- 显示与页面内容相关的文档或指南,以提供有用信息
- 在标题中包含图标,使其易于识别
- 将此部分放置在次要(1/3)布局区域
内容
- 标题应为句子大小写
实现
代码片段或链接到主题/GitHub
5.d. 表格
表格以单元格、列和行的网格形式显示信息,使管理员能够快速扫描项目并采取行动。
设计
用法
- 使用表格显示结构化内容,其中每个条目共享相同的属性。
- 允许管理员查看、启用/禁用、编辑和删除数据集。
- 适用于随时间继续增长的数据集。
设计
- 在行之间使用水平线以视觉上分隔内容,包括最后一行。避免在表格周围使用边框或框架,以防止其看起来像网。
- 不要在列之间应用垂直线。没有垂直线的表格通常更容易扫描和阅读。
额外操作
- 行操作: 在每个表格行的最右列中包含额外操作。
- 如果有两个或更多交互元素,主要操作(例如,“编辑”)应为文本按钮,所有其他行操作包括“删除”应分组在
[...]下拉菜单中。鼓励在下拉菜单中使用图标以在视觉上打破内容。 - 如果只有“删除”操作且没有主要操作,请使用内联“删除”文本按钮,样式为
btn-default。 - 你应该将主列的文本(通常是
d-table__cell --overview)包装在链接中,将管理员直接带到对应行的显示/编辑页面,以便快速访问。
- 如果有两个或更多交互元素,主要操作(例如,“编辑”)应为文本按钮,所有其他行操作包括“删除”应分组在
- 删除确认:所有“删除”按钮应在执行操作前显示确认。
内容
- 标题: 表格标题是识别下方列的顶行。它提供清晰度,特别是如果数据不具描述性或模糊。标题应简短、描述性和相关,使用标题大小写。避免标题对于下方行中的内容来说太长。
- 列: 按优先级或以一种讲述连贯数据故事的方式排序列。根据内容调整列大小,小内容使用窄列,段落使用宽列。
- 行: 行应支持文本、按钮、链接和图标,以增强数据呈现。
- 无数据: 空列表应使用
AdminConfigAreaEmptyList组件,带有 CTA 按钮和标签,引导用户创建新记录
实现
有一小集合 CSS 类必须与表格一起使用,以使其在移动设备和桌面上正常工作。
<table> 元素应应用 d-table 类。
<thead> 元素应应用 d-table__header 类。
<tr> 元素应应用 d-table__row 类。
<td> 元素包含大量描述性文本(通常是最左边的列)应使用 d-table__cell --overview 类。所有其他单元格应使用 d-table__cell --detail。
带有 d-table__cell --overview 类的 <td> 元素可以将内部行内容包装在链接中,将管理员直接带到行的编辑/显示页面。此链接应遵循此结构并应用 d-table__overview-link CSS 类。理想情况下应使用 LinkTo 组件,但 <a> 也可以,只要使用 getURL 即可。
d-table__overview-name 类应应用于此处的名称部分,但不应用于描述。
<td class="d-table__cell --overview">
<LinkTo
class="d-table__overview-link"
@route="adminPlugins.show.explorer.details"
@model={{query.id}}
>
<strong class="query-name d-table__overview-name">{{query.name}}</strong>
{{#if query.is_default}}
<span class="query-badge">{{i18n
"explorer.default_query"
}}</span>
{{/if}}
<div class="query-desc">{{query.description}}</div>
</LinkTo>
</td>
<td class="d-table__cell --overview">
<a class="d-table__overview-name admin-flag-item__name d-table__overview-link" href={{this.editUrl}}>
{{@flag.name}}
</a>
</td>
包装每行按钮的 <td> 元素应应用 d-table-cell --controls CSS 类。这确保按钮对齐。每个按钮也应应用 btn-small 类。
对于移动设备,每个 <td> 元素除了 d-table-cell --overview 还应包含一个带有类 d-table__mobile-label 的 <div>,其中包含与 <th> 中相同的 I18n 标签:
<td class="d-table__cell --detail">
<div class="d-table__mobile-label">
{{i18n "chat.incoming_webhooks.emoji"}}
</div>
{{replaceEmoji webhook.emoji}}
</td>
这在移动设备上以更易读的卡片格式显示表格行:
对于 [...] 下拉菜单,应使用 DMenu 与 DropdownMenu,这里是一个示例:
<DMenu
@identifier="backup-item-menu"
@title={{i18n "more_options"}}
@icon="ellipsis-vertical"
class="btn-small"
>
<:content>
<DropdownMenu as |dropdown|>
<dropdown.item>
<DButton ...[button args here] />
</dropdown.item>
<dropdown.item>
<DButton ...[button args here] />
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
表格行中的切换使用 DToggleSwitch 组件处理:
<DToggleSwitch
@state={{this.enabled}}
class="admin-flag-item__toggle {{@flag.name_key}}"
{{on "click" (fn this.toggleFlagEnabled @flag)}}
/>
将所有内容放在一起,这里是一个管理员表格的最小示例:
<table class="d-table">
<thead class="d-table__header">
<tr>
<th>Name</th>
<th>Description</th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="d-table__row">
<td class="d-table__cell --overview">
<LinkTo @route="admin.exampleRoute" class="d-table__overview-link">
<span class="d-table__overview-name">Example Item</span>
<span class="d-table__overview-about">A short description</span>
</LinkTo>
</td>
<td class="d-table__cell --detail">
<span class="d-table__mobile-label">Description</span>
Some detail content here
</td>
<td class="d-table__cell --controls">
<div class="d-table__cell-actions">
<button class="btn btn-default btn-small">Edit</button>
</div>
</td>
</tr>
</tbody>
</table>
5.e 第三级路由
第三级路由是只能从配置区域到达的路由。这些通常以编辑/新建路由的形式出现,如下面的标志示例:
在大多数情况下,使用 FormKit 的表单将放置在这里。
使用标准的 RESTful 路由:
| 操作 | 路径 |
|---|---|
| 新建 | <resource>/new |
| 编辑 | <resource>/:id/edit |
并确保路由也在后端路由。(重新加载新建或编辑页面不应导致错误。)
设计
用法
- 优先使用这些第三级路由,而不是在主路由或表格中使用内联表单。独立的编辑和新建路由最好,因为它们可以轻松链接。
- 不要显示页面 UI 的顶部部分(面包屑、页面头部和副标题)
- 相反,显示一个单一的“返回 X”链接,允许管理员返回主配置区域
- 页面内容应至少包裹在一个
AdminConfigAreaCard中 - 页面上的任何副标题应使用配置区域卡片完成
实现
有一个简单的 BackButton 组件,可用于页面顶部返回:
<BackButton
@route="adminConfig.flags"
@label="admin.config_areas.flags.back"
/>
6. 过滤设置配置页面
我们的许多管理员界面配置页面都是简单的过滤站点设置列表。这允许管理员找到相关的设置组,而不会被完整的“所有站点设置”列表所淹没,直到我们创建更多专门的配置页面,如 /admin/config/about/。
实现
添加这些路由之一需要几件事。首先,你可以显示整个 category 的站点设置,这些是 site_settings.yml 中的顶级键(例如 branding:),或者你可以使用设置 area。
站点设置可以存在于多个 areas 中,你可以在同一页面上显示一个或多个。
- 在
adminConfig下方的管理员路由映射中添加路由,例如:
this.route("trustLevels", { path: "/trust-levels" }, function () {
this.route("settings", {
path: "/",
});
});
- 添加一个新的路由 .js 文件,文件将匹配类似
frontend/discourse/admin/routes/admin-config/localization.js的路径,具体取决于你的新路由名称。这应继承自AdminConfigWithSettingsRoute并包含titleToken()。
import { i18n } from "discourse-i18n";
import AdminConfigWithSettingsRoute from "../admin-config-with-settings-route";
export default class AdminConfigLocalizationRoute extends AdminConfigWithSettingsRoute {
titleToken() {
return i18n("admin.config.localization.title");
}
}
- 添加一个控制器,这主要是为了启用设置搜索和过滤。它必须继承自
AdminAreaSettingsBaseController:
import AdminAreaSettingsBaseController from "discourse/admin/controllers/admin-area-settings-base";
export default class AdminConfigLocalizationSettingsController extends AdminAreaSettingsBaseController {}
- 最后,添加一个
.gjs格式的路由模板文件,路径类似frontend/discourse/admin/templates/admin-config/localization/settings.gjs。这应包含正常的DPageHeader和面包屑,但要显示设置,你需要AdminAreaSettings。
<div class="admin-config-page__main-area">
<AdminAreaSettings
@showBreadcrumb={{false}}
@area="localization"
@path="/admin/config/localization"
@filter={{@controller.filter}}
@adminSettingsFilterChangedCallback={{@controller.adminSettingsFilterChangedCallback}}
/>
</div>
这里需要更改的重要事项是 @path 和 @area(或者使用 @categories)。如前所述,填写你想要显示的站点设置区域或类别。
7. 一般指导
-
URL slugs 必须使用连字符 (
-) 来表示单词中的空格,而不是下划线 (_)。 -
管理员界面中的所有文本应遵循此处概述的文本格式指南:
8. 插件
一些插件需要为其插件进行深入的配置 UI(例如 AI、自动化、游戏化),而不仅仅是拥有一系列站点设置。例如,这里是 Discourse AI:
一些使用此的插件示例:
- Discourse AI GitHub - discourse/discourse-ai: Discourse AI now lives in the discourse/discourse repo · GitHub
- Discourse Gamification GitHub - discourse/discourse-gamification · GitHub
- Discourse Chat (核心)
设计
用法
- 制作独立插件 UI 时应遵循一般管理员 UI 指南。
实现
Ember 路由
- 所有路由模板将位于
admin/assets/javascripts/discourse/templates/admin-plugins/show/ - 所有路由 js 文件将位于
admin/assets/javascripts/discourse/routes/并
以admin-plugins-show-为前缀 - 管理员路由映射应在类似
admin-PLUGIN-NAME-plugin-route-map.js的文件中 - 路由映射应具有如下结构。重要的是我们使用
admin.adminPlugins.show作为resource。
export default {
resource: "admin.adminPlugins.show",
path: "/plugins",
map() {
this.route("discourse-ai-personas", { path: "ai-personas" }, function () {
this.route("new");
this.route("show", { path: "/:id" });
});
},
};
- 当前所有工作的示例可以在 Discourse AI 插件中看到,如果你转到
/admin/plugins/discourse-ai/ai-personas - 如果你只有一个“顶级”路由,例如不定义子路由的路由,那么模板路径将是类似
admin/assets/javascripts/discourse/templates/admin-plugins/show/your-route-name.gjs。如果有子路由,那么你就进入了需要index.gjs、show.gjs和new.gjs模板等的领域。
导航
插件可以在内部侧边栏或顶部选项卡导航栏中显示其导航。后者高度推荐,未来可能会放弃内部侧边栏支持。
- 任何将在顶部栏或插件显示页面的内部侧边栏中显示的链接应在初始化程序(例如
assets/javascripts/initializers/admin-plugin-configuration-nav.js)中使用
api.addAdminPluginConfigurationNav定义。链接需要label、route和description(用于管理员搜索) - 此初始化程序仅应在用户是管理员时运行。
- 插件的站点设置链接是自动生成的,无需在此处包含。
- 示例可见于 discourse-ai/assets/javascripts/initializers/admin-plugin-configuration-nav.js at ab4544d8977ec0e9d6aa42b4551df8317aa9b365 · discourse/discourse-ai · GitHub 。
服务器端
add_admin_route仍用于在管理员侧边栏和带有顶部选项卡的 /plugins 索引中显示自定义管理员路由。基本上,这定义了你的插件 UI 的根页面。- 应传递
use_new_show_route: true作为额外参数,以便使用新的插件显示页面。
- 应传递
UI 约定
- 插件的每个索引路由应显示
DPageSubheader组件,以描述该路由的意图并添加任何相关的操作按钮。 - 需要渲染到主插件页面头部的操作按钮必须使用
admin-plugin-config-page-actions出口和专用组件。最佳位置是在使用addAdminPluginConfigurationNav的同一初始化程序中。plugin和actions作为outletArgs传递。plugin是当前插件的模型表示,因此可以访问插件名称和其他内容,actions是来自DPageHeader的 yielded 操作按钮组件。
api.renderInOutlet(
"admin-plugin-config-page-actions",
ChatAdminPluginActions
);
相关主题:













