表单构建器插件

https://www.npmjs.com/package/@payloadcms/plugin-form-builder

该插件允许你直接在 Admin Panel 中构建和管理自定义表单。无需每次需要新表单时都将其硬编码到网站或应用中,管理员可以即时定义每个所需表单的结构,而你的前端可以遍历这个结构,渲染自己的 UI 组件,并匹配你的品牌设计系统。

所有表单提交都会直接存储在你的数据库中,并可直接通过 Admin Panel 进行管理。当表单提交时,你可以向用户显示自定义的屏幕确认信息,或者将他们重定向到专门的确认页面。你甚至可以根据表单数据发送动态的个性化邮件。例如,你可能想向提交表单的用户发送确认邮件,同时也给你的团队发送通知邮件。

表单可以根据需要简单或复杂,从基本的联系表单,到多步骤的潜在客户生成引擎,甚至是处理付款的捐赠表单。你可能不需要为此使用 HubSpot 或 Mailchimp 等第三方服务,而是直接使用内置于你自己应用中的第一方工具。

该插件完全开源,源代码可以在此找到。 如需帮助,请查看我们的 社区帮助。如果认为发现了 bug, 请提交新 issue 并提供尽可能多的细节。

核心功能

  • 直接从管理面板构建完全动态的表单,适用于多种使用场景
  • 使用自定义 UI 组件在前端渲染表单,匹配品牌设计系统
  • 表单提交时发送动态个性化邮件给多个收件人,邮件内容基于表单数据生成
  • 显示自定义确认消息或在表单提交后自动重定向
  • 基于表单输入构建动态价格,用于支付处理(可选)

安装

使用任意 JavaScript 包管理器(如 pnpmnpmYarn)安装插件:

pnpm add @payloadcms/plugin-form-builder

基础用法

Payload 配置plugins 数组中,调用插件并传入 选项

import { buildConfig } from 'payload'
import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'

const config = buildConfig({
  collections: [
    {
      slug: 'pages',
      fields: [],
    },
  ],
  plugins: [
    formBuilderPlugin({
      // 可用选项见下文
    }),
  ],
})

export default config

选项

fields (可选)

fields 属性是一个字段类型对象,允许管理员编辑者构建表单。要覆盖默认设置,可以传入布尔值或部分 Payload Block(以块的 slug 为键)。详情请参阅 字段

// payload.config.ts
formBuilderPlugin({
  // ...
  fields: {
    text: true,
    textarea: true,
    select: true,
    email: true,
    state: true,
    country: true,
    checkbox: true,
    number: true,
    message: true,
    date: false,
    payment: false,
  },
})

redirectRelationships

redirectRelationships 属性是一个 collection slug 数组,启用后会在表单的 redirect 字段中作为选项填充。该字段用于在表单提交后将用户重定向到指定的确认页面(可选)。

// payload.config.ts
formBuilderPlugin({
  // ...
  redirectRelationships: ['pages'],
})

beforeEmail

beforeEmail 属性是一个 beforeChange 钩子,在邮件准备就绪后、发送前调用。这是注入自定义 HTML 模板以添加样式的理想位置。

// payload.config.ts
formBuilderPlugin({
  // ...
  beforeEmail: (emailsToSend, beforeChangeParams) => {
    // 在邮件发送前以任何方式修改邮件
    return emails.map((email) => ({
      ...email,
      html: email.html, // 以任何你想要的方式转换 HTML(比如将其包装在 HTML 模板中?)
    }))
  },
})

要获取完整的 beforeChangeParams 类型,你可以从插件中导入类型:

import type { BeforeEmail } from '@payloadcms/plugin-form-builder'
// 你生成的 FormSubmission 类型
import type { FormSubmission } from '@payload-types'

// 传入后,'data' 或 'originalDoc' 将被类型化
const beforeEmail: BeforeEmail<FormSubmission> = (
  emailsToSend,
  beforeChangeParams,
) => {
  // 在邮件发送前以任何方式修改邮件
  return emails.map((email) => ({
    ...email,
    html: email.html, // 以任何你想要的方式转换 HTML(比如将其包装在 HTML 模板中?)
  }))
}

defaultToEmail

提供一个备用的电子邮件地址,用于接收表单提交。如果表单配置中没有设置收件人邮箱,将使用此邮箱地址。如果此处也未提供,则会回退到 email 配置 中的 defaultFromAddress

// payload.config.ts
formBuilderPlugin({
  // ...
  defaultToEmail: 'test@example.com',
})

formOverrides

通过向 formOverrides 属性传递一个 Payload Collection 配置,可以覆盖 forms collection 的任何设置。

注意:fields 属性是一个接收默认字段并返回字段数组的函数。这是因为 fields 属性是一个特殊情况,它会与默认字段合并而不是替换它们。这允许你遍历默认字段并根据需要进行修改。

重要提示:默认情况下,表单 collection 是公开可读的。 电子邮件字段仅对已认证用户锁定。如果你有前端用户, 应该覆盖 collection 和电子邮件字段的访问权限, 以确保不会泄露任何私人电子邮件。

// payload.config.ts
formBuilderPlugin({
  // ...
  formOverrides: {
    slug: 'contact-forms',
    access: {
      read: ({ req: { user } }) => !!user, // 仅限已认证用户
      update: () => false,
    },
    fields: ({ defaultFields }) => {
      return [
        ...defaultFields,
        {
          name: 'custom',
          type: 'text',
        },
      ]
    },
  },
})

formSubmissionOverrides

通过向 formSubmissionOverrides 属性传递一个 Payload Collection Config,可以覆盖 form-submissions collection 的任何配置。

默认情况下,该插件依赖 Payload 访问控制来限制对 form-submissions collection 的 updateread 操作。这是因为 任何人 都应该能够创建表单提交(即使是来自面向公众的网站),但 任何人 都不应该能够在提交创建后更新它,或者在没有权限的情况下读取提交。你可以根据需要覆盖此行为或任何其他属性。

// payload.config.ts
formBuilderPlugin({
  // ...
  formSubmissionOverrides: {
    slug: 'leads',
    fields: ({ defaultFields }) => {
      return [
        ...defaultFields,
        {
          name: 'custom',
          type: 'text',
        },
      ]
    },
  },
})

handlePayment

handlePayment 属性是一个 beforeChange 钩子,在表单提交时被调用。你可以在这里集成任何第三方支付处理 API,根据表单输入接收付款。可以使用 getPaymentTotal 函数在所有条件应用后计算总费用。这仅在表单启用了 payment 字段时适用。

首先导入实用函数。这将执行你在表单 payment 字段中设置的所有价格条件,并返回总价格。

// payload.config.ts
import { getPaymentTotal } from '@payloadcms/plugin-form-builder'

然后在你的插件配置中:

// payload.config.ts
formBuilderPlugin({
  // ...
  handlePayment: async ({ form, submissionData }) => {
    // 首先计算价格
    const paymentField = form.fields?.find(
      (field) => field.blockType === 'payment',
    )
    const price = getPaymentTotal({
      basePrice: paymentField.basePrice,
      priceConditions: paymentField.priceConditions,
      fieldValues: submissionData,
    })
    // 然后在这里异步处理支付
  },
})

字段

每个字段代表一个表单输入。要覆盖默认设置,可以传递布尔值或部分 Payload Block(以块的 slug 为键)。有关如何执行此操作的更多详细信息,请参阅字段覆盖

注意: 这里的"Fields"指的是_用于构建表单的字段_, 不要与_集合的字段_混淆,后者是通过 formOverrides.fields 设置的。

文本字段

对应前端中的 text 输入框。用于收集简单的字符串。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuestring字段默认值
widthstring字段在前端的宽度
requiredcheckbox提交时是否为必填字段

文本区域

对应前端中的 textarea 输入框。用于收集多行文本。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuestring字段默认值
widthstring字段在前端的宽度
requiredcheckbox提交时是否为必填字段

Select(选择框)

在前端映射为 select 输入框。用于显示选项列表。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuestring字段默认值
widthstring在前端显示的字段宽度
requiredcheckbox提交时该字段是否为必填项
optionsarray包含 labelvalue 属性的对象数组

Email(邮箱字段)

在前端映射为类型为 emailtext 输入框。用于收集电子邮件地址。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuestring字段默认值
widthstring在前端显示的字段宽度
requiredcheckbox提交时该字段是否为必填项

State

对应前端的一个 select 输入框。用于收集美国州名。

PropertyTypeDescription
namestring字段名称。
labelstring字段标签。
defaultValuestring字段默认值。
widthstring字段在前端的宽度。
requiredcheckbox提交时该字段是否为必填项。

Country

对应前端的一个 select 输入框。用于收集国家名称。

PropertyTypeDescription
namestring字段名称。
labelstring字段标签。
defaultValuestring字段默认值。
widthstring字段在前端的宽度。
requiredcheckbox提交时该字段是否为必填项。

复选框 (Checkbox)

对应前端 checkbox 输入类型。用于收集布尔值。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuecheckbox字段默认值
widthstring字段在前端的显示宽度
requiredcheckbox提交时是否为必填字段

日期 (Date)

对应前端 date 输入类型。用于收集日期值。

PropertyTypeDescription
namestring字段名称
labelstring字段标签
defaultValuedate字段默认值
widthstring字段在前端的显示宽度
requiredcheckbox提交时是否为必填字段

Number(数字)

在前端映射为 number 输入框。用于收集数字类型的数据。

属性类型描述
namestring字段名称
labelstring字段标签
defaultValuenumber字段的默认值
widthstring字段在前端的显示宽度
requiredcheckbox提交时该字段是否为必填项

Message(消息)

在前端映射为 RichText 组件。用于在表单任意位置向用户显示自定义消息。

属性类型描述
messagerichText要在表单上显示的消息内容

支付

如果表单需要收集支付信息,可以添加此字段。提交时,系统会执行 handlePayment 回调函数,传入表单和提交数据。你可以利用这个回调来集成任何第三方支付处理 API。

属性类型描述
namestring字段名称
labelstring字段标签
defaultValuenumber字段默认值
widthstring字段在前端的显示宽度
requiredcheckbox提交时该字段是否为必填项
priceConditionsarray定义价格条件的对象数组。详见下文获取更多细节。

价格条件

每个 priceConditions 都会由该插件提供的 getPaymentTotal 工具函数执行。你可以在 handlePayment 回调中调用此函数,根据用户输入动态计算表单提交时的总价。例如,你可以创建一个价格条件:"如果用户对此复选框选择'是',则在总价上增加 10 美元"。

propertytypedescription
fieldToUserelationship用于确定价格的字段。
conditionstring用于确定价格的条件。
valueForOperatorstring用于运算符的值。
operatorstring用于确定价格的运算符。
valueTypestring用于确定价格的值类型。
valuestring用于确定价格的值。

字段覆盖

你可以通过传入一个新的 Payload Block 对象到 fields 中,来提供自定义字段。你可以通过首先从插件导入 fields 来覆盖或扩展任何现有字段:

import { fields } from '@payloadcms/plugin-form-builder'

然后将其合并到你自己的自定义字段中:

// payload.config.ts
formBuilderPlugin({
  // ...
  fields: {
    text: {
      ...fields.text,
      labels: {
        singular: '自定义文本字段',
        plural: '自定义文本字段',
      },
    },
  },
})

自定义日期字段默认值

你可以通过以下方式自定义日期字段的默认值以及日期块的其他方面。请注意,最终提交来源将负责处理日期的时区。Payload 仅以 UTC 格式存储日期。

import { fields as formFields } from '@payloadcms/plugin-form-builder'

// payload.config.ts
formBuilderPlugin({
  fields: {
    // date: true, // 不进行任何自定义,直接启用
    date: {
      ...formFields.date,
      fields: [
        ...(formFields.date && 'fields' in formFields.date
          ? formFields.date.fields.map((field) => {
              if ('name' in field && field.name === 'defaultValue') {
                return {
                  ...field,
                  timezone: true, // 可选启用时区
                  admin: {
                    ...field.admin,
                    description: '这是一个日期字段',
                  },
                }
              }
              return field
            })
          : []),
      ],
    },
  },
})

电子邮件

该插件依赖于你在 Payload 配置中定义的 email 配置。它会读取你的配置并尝试使用提供的凭据发送电子邮件。

电子邮件格式

邮件内容支持富文本,这些内容会在服务器端被序列化为 HTML 后再发送。默认情况下,它会读取你富文本编辑器的全局配置。

邮件主题和正文支持使用 {{字段名}} 语法插入来自表单提交数据的动态字段。例如,如果你的表单中有一个名为 name 的字段,你可以像这样将其包含在邮件正文中:

感谢你的提交,{{name}}!

你还可以使用 {{*}} 作为通配符以键值格式输出所有数据,或使用 {{*:table}} 以表格格式输出所有数据。

TypeScript

所有类型都可以直接导入:

import type {
  PluginConfig,
  Form,
  FormSubmission,
  FieldsConfig,
  BeforeEmail,
  HandlePayment,
  ...
} from "@payloadcms/plugin-form-builder/types";

示例

示例目录中包含一个官方的表单构建插件示例,该示例展示了如何在 Payload 中配置此插件并在前端实现它。我们还在表单构建插件博客文章中详细介绍了如何从头开始构建表单。

故障排除

以下是一些常见的故障排除提示。为了帮助其他开发者,请在解决自己的应用问题时贡献到本节。

SendGrid 403 禁止访问错误

  • 如果你使用 SendGrid 链接品牌来移除电子邮件中的"via sendgrid.net"部分,则还必须设置域名认证。这意味着你只能从该域名下的地址发送电子邮件——因此表单提交邮件中的 from 地址不能是除 something@your_domain.com 之外的任何内容。这意味着 {{email}} 将不起作用,但 website@your_domain.com 可以。你仍然可以在邮件正文中发送表单的电子邮件地址。

屏幕截图

screenshot 1

screenshot 2

screenshot 3

screenshot 4

screenshot 5

screenshot 6