Strapi 插件 可以与 Strapi 应用程序的 后端 和前端交互。管理面板 API 是关于前端部分的,即它允许插件自定义 Strapi 的 管理面板。
管理面板是一个 React 应用程序,可以嵌入其他 React 应用程序。这些其他 React 应用程序是每个 Strapi 插件的管理部分。
您已 创建 Strapi 插件。
管理面板 API 包括:
插件的管理面板部分的全部代码可以位于 /strapi-admin.js|ts
或 /admin/src/index.js|ts
文件中。但是,建议将代码拆分到不同的文件夹中,就像 strapi generate plugin
CLI 生成器命令创建的 结构 一样。
入口文件
管理面板 API 的入口文件是 [plugin-name]/admin/src/index.js
。该文件导出所需的接口,提供以下功能:
函数类型 | 可用函数 |
---|---|
生命周期函数 | |
异步函数 | registerTrads |
生命周期函数
register()
类型:Function
此函数被调用来加载插件,甚至在应用程序实际引导之前。它将正在运行的 Strapi 应用程序作为参数(app
)。
在注册函数中,插件可以:
- 注册自身,以便管理面板可以使用
- 向主导航添加新链接(参见 菜单 API)
- 创建新的设置部分
- 定义 注入区域
- 添加 Reducer
registerPlugin()
类型:Function
注册插件以使其在管理面板中可用。
此函数返回具有以下参数的对象:
参数 | 类型 | 说明 |
---|---|---|
id | 字符串 | 插件 ID |
name | 字符串 | 插件名称 |
injectionZones | 对象 | 可用 注入区域 的声明 |
可以从 package.json
文件导入一些参数。
示例:
// Auto-generated component
import PluginIcon from './components/PluginIcon'
import pluginId from './pluginId'
export default {
register(app) {
app.addMenuLink({
to: `/plugins/${pluginId}`,
icon: PluginIcon,
intlLabel: {
id: `${pluginId}.plugin.name`,
defaultMessage: 'My plugin',
},
Component: async () => {
const component = await import(/* webpackChunkName: "my-plugin" */ './pages/App')
return component
},
permissions: [], // 权限数组(对象),允许用户根据其权限访问插件
})
app.registerPlugin({
id: pluginId,
name,
})
},
}
bootstrap()
类型:Function
公开引导函数,在所有插件注册 后执行。
在引导函数中,插件可以:
- 使用
getPlugin('plugin-name')
扩展另一个插件, - 注册钩子(参见 Hooks API)
- 添加指向设置部分的链接
示例:
module.exports = () => {
return {
// ...
bootstrap(app) {
// execute some bootstrap code
app.injectContentManagerComponent('editView', 'right-links', { name: 'my-compo', Component: () => 'my-compo' })
},
}
}
Async function
虽然 register()
和 bootstrap()
是生命周期函数,但 registerTrads()
是一个异步函数。
registerTrads()
类型:Function
为了减少构建大小,管理面板默认仅附带 2 个语言环境(en
和 fr
)。registerTrads()
函数用于注册插件的翻译文件并为应用程序翻译创建单独的块。它不需要修改。
示例:注册插件的翻译文件
export default {
async registerTrads({ locales }) {
const importedTrads = await Promise.all(
locales.map((locale) => {
return import(
/* webpackChunkName: "[pluginId]-[request]" */ `./translations/${locale}.json`
)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
}
})
.catch(() => {
return {
data: {},
locale,
}
})
})
)
return Promise.resolve(importedTrads)
},
}
Available actions
管理面板 API 允许插件利用几个小 API 来执行操作。请使用此表作为参考:
操作 | 要使用的 API | 要使用的函数 | 相关生命周期函数 |
---|---|---|---|
向主导航添加新链接 | 菜单 API | addMenuLink() | register() |
创建新的设置部分 | 设置 API | createSettingSection() | register() |
声明注入区域 | 注入区域 API | registerPlugin() | register() |
添加减速器 | Reducers API | addReducers() | register() |
创建一个钩子 | Hooks API | createHook() | register() |
向设置部分添加单个链接 | Settings API | addSettingsLink() | bootstrap() |
向设置部分添加多个链接 | Settings API | addSettingsLinks() | bootstrap() |
在注入区域中注入组件 | 注入区域 API | injectComponent() | bootstrap() |
注册一个钩子 | Hooks API | registerHook() | bootstrap() |
可以利用 自定义字段 替换 WYSIWYG 编辑器,例如使用 CKEditor 自定义字段插件。
通过 process.env
自定义管理面板时,可以使用 .env
文件中定义的所有变量,这些变量以 STRAPI_ADMIN_
为前缀。
菜单 API
菜单 API 允许插件通过 addMenuLink()
函数使用以下参数向主导航添加新链接:
参数 | 类型 | 说明 |
---|---|---|
to | 字符串 | 链接应指向的路径 |
icon | React 组件 | 主导航中显示的图标 |
intlLabel | 对象 | 链接的标签,遵循 React Int'l 约定,其中:
|
Component | 异步函数 | 返回插件入口点的动态导入 |
permissions | 对象数组 | 在插件的 permissions.js 文件中声明的权限 |
intlLabel.id
are ids used in translation files ([plugin-name]/admin/src/translations/[language].json
)
Example:
import PluginIcon from './components/PluginIcon'
export default {
register(app) {
app.addMenuLink({
to: '/plugins/my-plugin',
icon: PluginIcon,
intlLabel: {
id: 'my-plugin.plugin.name',
defaultMessage: 'My plugin',
},
Component: () => 'My plugin',
permissions: [], // permissions to apply to the link
})
app.registerPlugin({
// ...
})
},
bootstrap() {},
}
Settings API
The Settings API allows:
- creating a new setting section
- adding a single link or multiple links at once to existing settings sections
Adding a new section happens in the register lifecycle while adding links happens during the bootstrap lifecycle.
所有函数都接受具有以下参数的对象形式的链接:
参数 | 类型 | 说明 |
---|---|---|
id | 字符串 | React id |
to | 字符串 | 链接应指向的路径 |
intlLabel | 对象 | 链接的标签,遵循 React Int'l 约定,其中:
|
Component | 异步函数 | 返回插件入口点的动态导入 |
permissions | 对象数组 | 在插件的 permissions.js 文件中声明的权限 |
createSettingSection()
类型:Function
创建一个新的设置部分。
该函数接受 2 个参数:
参数 | 类型 | 描述 |
---|---|---|
第一个参数 | 对象 | 部分标签:
|
第二个参数 | 对象数组 | 部分中包含的链接 |
intlLabel.id
are ids used in translation files ([plugin-name]/admin/src/translations/[language].json
)
Example:
async function myComponent() {
const component = await import(
/* webpackChunkName: "users-providers-settings-page" */ './pages/Providers'
)
return component
}
export default {
register(app) {
app.createSettingSection(
{ id: String, intlLabel: { id: String, defaultMessage: String } }, // Section to create
[
// links
{
intlLabel: { id: String, defaultMessage: String },
id: String,
to: String,
Component: myComponent,
permissions: Object[0],
},
]
)
},
}
addSettingsLink()
类型:Function
向现有设置部分添加唯一链接。
Example:
async function myComponent() {
const component = await import(
/* webpackChunkName: "users-providers-settings-page" */
'./pages/Providers'
)
return component
}
export default {
bootstrap(app) {
// Adding a single link
app.addSettingsLink(
'global', // id of the section to add the link to
{
intlLabel: { id: String, defaultMessage: String },
id: String,
to: String,
Component: myComponent,
permissions: Object[0]
}
)
}
}
addSettingsLinks()
Type: Function
向现有设置部分添加多个链接。
Example:
async function myComponent() {
const component = await import(
/* webpackChunkName: "users-providers-settings-page" */ './pages/Providers'
)
return component
}
export default {
bootstrap(app) {
// Adding several links at once
app.addSettingsLinks(
'global', // id of the section to add the link in
[{
intlLabel: { id: String, defaultMessage: String },
id: String,
to: String,
Component: myComponent,
permissions: Object[0]
}]
)
}
}
注入区域 API
注入区域是指视图布局中的某个区域,插件允许另一个插件注入自定义 React 组件(例如按钮之类的 UI 元素)。
插件可以使用:
- Strapi 的内容管理器的 预定义注入区域,
- 或插件创建的自定义注入区域
注入区域在 register() 生命周期中定义,但组件在 bootstrap() 生命周期中注入。
使用预定义注入区域
Strapi 管理面板带有预定义注入区域,因此可以将组件添加到 内容管理器 的 UI 中:
查看 | 注入区域名称和位置 |
---|---|
列表视图 |
|
编辑视图 |
|
创建自定义注入区域
要创建自定义注入区域,请将其声明为 <InjectionZone />
React 组件,并使用 area
prop,该 prop 采用以下命名约定的字符串:plugin-name.viewName.injectionZoneName
。
注入组件
插件有两种不同的组件注入方式:
- 要将插件中的组件注入另一个插件的注入区域,请使用
injectComponent()
函数 - 要将组件专门注入内容管理器的 预定义注入区域 之一,请使用
injectContentManagerComponent()
函数
injectComponent()
和 injectContentManagerComponent()
方法都接受以下参数:
参数 | 类型 | 说明 |
---|---|---|
第一个参数 | 字符串 | 注入组件的视图 |
第二个参数 | 字符串 | 组件注入的区域 |
第三个参数 | 对象 | 具有以下键的对象:
|
示例:在内容管理器的编辑视图的信息框中注入组件:
export default {
bootstrap(app) {
app.injectContentManagerComponent('editView', 'informations', {
name: 'my-plugin-my-compo',
Component: () => 'my-compo',
})
}
}
示例:创建一个新的注入区域并将其从一个插件注入到另一个插件:
// Use the injection zone in a view
import { InjectionZone } from '@strapi/helper-plugin'
function HomePage() {
return (
<main>
<h1>This is the homepage</h1>
<InjectionZone area="my-plugin.homePage.right" />
</main>
)
}
// 在插件的注册生命周期中声明此注入区域
export default {
register() {
app.registerPlugin({
// ...
injectionZones: {
homePage: {
right: []
}
}
})
},
}
// 将一个插件中的组件注入另一个插件中
export default {
register() {
// ...
},
bootstrap(app) {
app.getPlugin('my-plugin').injectComponent('homePage', 'right', {
name: 'my-other-plugin-component',
Component: () => 'This component is injected',
})
}
}
使用 useCMEditViewDataManager
React 钩子访问数据
一旦定义了注入区域,内容管理器中要注入的组件就可以通过 useCMEditViewDataManager
React 钩子访问编辑视图的所有数据。
使用“useCMEditViewDataManager”钩子的基本组件示例
import { useCMEditViewDataManager } from '@strapi/helper-plugin'
function MyCompo() {
const {
createActionAllowedFields = [], // 允许用户编辑的字段数组
formErrors = {}, // 对象错误
readActionAllowedFields = [], // 允许用户编辑的字段数组
slug = 'api::address.address', // 内容类型的 Slug
updateActionAllowedFields = [],
allLayoutData: {
components = {}, // 组件布局
contentType = {}, // 内容类型布局
} = {}, // 为 allLayoutData 设置默认值
initialData = {},
isCreatingEntry = true,
isSingleType = true,
status = 'resolved',
layout = {}, // 当前内容类型布局
hasDraftAndPublish = true,
modifiedData = {},
onPublish = () => {},
onUnpublish = () => {},
addComponentToDynamicZone = () => {},
addNonRepeatableComponentToField = () => {},
addRelation = () => {},
addRepeatableComponentToField = () => {},
moveComponentDown = () => {},
moveComponentField = () => {},
moveComponentUp = () => {},
moveRelation = () => {},
onChange = () => {},
onRemoveRelation = () => {},
removeComponentFromDynamicZone = () => {},
removeComponentFromField = () => {},
removeRepeatableField = () => {},
} = useCMEditViewDataManager()
return null
}
Reducers API
Reducers 是 Redux 减速器,可用于在组件之间共享状态。减速器在以下情况下很有用:
- 应用程序的许多地方都需要大量应用程序状态。
- 应用程序状态经常更新。
- 更新该状态的逻辑可能很复杂。
可以在 register
生命周期中使用 addReducers()
函数将减速器添加到插件接口。
使用以下语法将减速器声明为对象:
示例:
import { exampleReducer } from './reducers'
const reducers = {
// Reducer Syntax
[`${pluginId}_exampleReducer`]: exampleReducer
}
export default {
register(app) {
app.addReducers(reducers)
},
bootstrap() {},
}
Hooks API
Hooks API 允许插件创建和注册钩子,即应用程序中插件可以添加个性化行为的位置。
钩子应该在插件的 bootstrap 生命周期内注册。
然后可以按顺序、瀑布式或并行运行钩子:
runHookSeries
返回一个对应于每个执行函数结果的数组,按顺序排列runHookParallel
返回一个对应于执行函数解析的承诺结果的数组,按顺序排列runHookWaterfall
返回一个对应于不同函数应用的所有转换的单个值,从初始值args
开始。
示例:在插件中创建钩子并在另一个插件中使用它
// 在插件中创建钩子
export default {
register(app) {
app.createHook('My-PLUGIN/MY_HOOK')
}
}
// 在另一个插件中使用钩子
export default {
bootstrap(app) {
app.registerHook('My-PLUGIN/MY_HOOK', (...args) => {
console.log(args)
// 重要:返回变异数据
return args
})
app.registerPlugin({})
}
}
Predefined hook
Strapi 包含一个预定义的 Admin/CM/pages/ListView/inject-column-in-table
钩子,可用于添加或改变 内容管理器 列表视图的列。
示例:
Admin/CM/pages/ListView/inject-column-in-table
钩子,国际化插件使用它来添加'可用内容'列
import get from 'lodash/get'
import cellFormatter from './components/cellFormatter'
export default {
bootstrap(app) {
app.registerHook(
'Admin/CM/pages/ListView/inject-column-in-table',
({ displayedHeaders, layout }) => {
const isFieldLocalized = get(
layout,
'contentType.pluginOptions.i18n.localized',
false
)
if (!isFieldLocalized) {
return { displayedHeaders, layout }
}
return {
layout,
displayedHeaders: [
...displayedHeaders,
{
key: '__locale_key__', // 表格所需
fieldSchema: { type: 'string' }, // 属性的架构
metadatas: {
label: 'Content available in', // 标题的标签,
sortable: true | false, // 定义列是否可排序
}, // 标签的元数据
// 我们将显示的数据中的键的名称
name: 'locales',
// 自定义渲染器:props => Object.keys(props).map(key => <p key={key}>key</p>)
cellFormatter,
},
],
}
}
)
},
}