管理面板API Admin Panel API for plugins

管理面板是一个React应用程序,可以嵌入其他 React 应用程序。这些其他 React 应用程序是每个 Strapi 插件的管理部分。

Strapi 插件 可以与 Strapi 应用程序的 后端 和前端交互。管理面板 API 是关于前端部分的,即它允许插件自定义 Strapi 的 管理面板

管理面板是一个 React 应用程序,可以嵌入其他 React 应用程序。这些其他 React 应用程序是每个 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)。

在注册函数中,插件可以:

registerPlugin()

类型:Function

注册插件以使其在管理面板中可用。

此函数返回具有以下参数的对象:

参数类型说明
id字符串插件 ID
name字符串插件名称
injectionZones对象可用 注入区域 的声明

可以从 package.json 文件导入一些参数。

示例:

my-plugin/admin/src/index.js
js
// 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

公开引导函数,在所有插件注册 后执行。

在引导函数中,插件可以:

示例:

js
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 个语言环境(enfr)。registerTrads() 函数用于注册插件的翻译文件并为应用程序翻译创建单独的块。它不需要修改。

示例:注册插件的翻译文件

jsx
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要使用的函数相关生命周期函数
向主导航添加新链接菜单 APIaddMenuLink()register()
创建新的设置部分设置 APIcreateSettingSection()register()
声明注入区域注入区域 APIregisterPlugin()register()
添加减速器Reducers APIaddReducers()register()
创建一个钩子Hooks APIcreateHook()register()
向设置部分添加单个链接Settings APIaddSettingsLink()bootstrap()
向设置部分添加多个链接Settings APIaddSettingsLinks()bootstrap()
在注入区域中注入组件注入区域 APIinjectComponent()bootstrap()
注册一个钩子Hooks APIregisterHook()bootstrap()
替换 WYSIWYG

可以利用 自定义字段 替换 WYSIWYG 编辑器,例如使用 CKEditor 自定义字段插件

管理面板支持 dotenv 变量。

通过 process.env 自定义管理面板时,可以使用 .env 文件中定义的所有变量,这些变量以 STRAPI_ADMIN_ 为前缀。

菜单 API

菜单 API 允许插件通过 addMenuLink() 函数使用以下参数向主导航添加新链接:

参数类型说明
to字符串链接应指向的路径
iconReact 组件主导航中显示的图标
intlLabel对象链接的标签,遵循 React Int'l 约定,其中:
  • id:用于插入本地化标签的 id
  • defaultMessage:链接的默认标签
Component异步函数返回插件入口点的动态导入
permissions对象数组在插件的 permissions.js 文件中声明的权限

intlLabel.id are ids used in translation files ([plugin-name]/admin/src/translations/[language].json)

Example:

my-plugin/admin/src/index.js
jsx
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:

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 约定,其中:
  • id:用于插入本地化标签的 id
  • defaultMessage:链接的默认标签
Component异步函数返回插件入口点的动态导入
permissions对象数组在插件的 permissions.js 文件中声明的权限

createSettingSection()

类型Function

创建一个新的设置部分。

该函数接受 2 个参数:

参数类型描述
第一个参数对象部分标签:
  • id (字符串):部分 id
  • intlLabel (对象):部分的本地化标签,遵循 React Int'l 约定,其中:
    • id:用于插入本地化标签的 id
    • defaultMessage:部分的默认标签
第二个参数对象数组部分中包含的链接

intlLabel.id are ids used in translation files ([plugin-name]/admin/src/translations/[language].json)

Example:

my-plugin/admin/src/index.js
jsx
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],
        },
      ]
    )
  },
}

类型Function

向现有设置部分添加唯一链接。

Example:

my-plugin/admin/src/index.js
jsx
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]
      }
    )
  }
}

Type: Function

向现有设置部分添加多个链接。

Example:

my-plugin/admin/src/index.js
jsx
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 元素)。

插件可以使用:

注入区域在 register() 生命周期中定义,但组件在 bootstrap() 生命周期中注入。

使用预定义注入区域

Strapi 管理面板带有预定义注入区域,因此可以将组件添加到 内容管理器 的 UI 中:

查看注入区域名称和位置
列表视图
  • actions:位于过滤器和齿轮图标之间
  • deleteModalAdditionalInfos():位于删除项目时显示的模式底部
编辑视图
  • informations:位于编辑视图的右上角
  • right-links:位于“配置视图”和“编辑”按钮之间

创建自定义注入区域

要创建自定义注入区域,请将其声明为 <InjectionZone /> React 组件,并使用 area prop,该 prop 采用以下命名约定的字符串:plugin-name.viewName.injectionZoneName

注入组件

插件有两种不同的组件注入方式:

  • 要将插件中的组件注入另一个插件的注入区域,请使用 injectComponent() 函数
  • 要将组件专门注入内容管理器的 预定义注入区域 之一,请使用 injectContentManagerComponent() 函数

injectComponent()injectContentManagerComponent() 方法都接受以下参数:

参数类型说明
第一个参数字符串注入组件的视图
第二个参数字符串组件注入的区域
第三个参数对象具有以下键的对象:
  • name(字符串):组件的名称
  • Component(函数或类):要注入的 React 组件

示例:在内容管理器的编辑视图的信息框中注入组件:

my-plugin/admin/src/index.js
jsx
export default {
  bootstrap(app) {
    app.injectContentManagerComponent('editView', 'informations', {
      name: 'my-plugin-my-compo',
      Component: () => 'my-compo',
    })
  }
}

示例:创建一个新的注入区域并将其从一个插件注入到另一个插件:

my-plugin/admin/src/injectionZones.js
jsx
// 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>
  )
}
my-plugin/admin/src/index.js
jsx
// 在插件的注册生命周期中声明此注入区域

export default {
  register() {
    app.registerPlugin({
      // ...
      injectionZones: {
        homePage: {
          right: []
        }
      }
    })
  },
}
my-other-plugin/admin/src/index.js
jsx
// 将一个插件中的组件注入另一个插件中

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”钩子的基本组件示例

js
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() 函数将减速器添加到插件接口。

使用以下语法将减速器声明为对象:

示例:

my-plugin/admin/src/index.js
js
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 开始。

示例:在插件中创建钩子并在另一个插件中使用它

my-plugin/admin/src/index.js
jsx
// 在插件中创建钩子
export default {
  register(app) {
    app.createHook('My-PLUGIN/MY_HOOK')
  }
}
my-other-plugin/admin/src/index.js
jsx
// 在另一个插件中使用钩子
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 钩子,国际化插件使用它来添加'可用内容'列

./plugins/my-plugin/admin/src/index.js
jsx
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,
            },
          ],
        }
      }
    )
  },
}