@ foundrykit/ 高级形式- 插件
多步骤窗体插件 有效载荷CMS 3。在您的管理面板上添加一个窗体构建器,其中包含11个内置的字段类型,可配置的提交动作,以及用于前端的即时反应组件.
特征
- 多步骤形式 ——将字段组织成有可选图标和进度指标的已命名步骤
- reCAPTCHA v3 (英语) · 每种形式的可选、无形的垃圾邮件保护
- 12个外地类型 ——文本,电子邮件,电话,文本区域,复选框,收音机组,复选框组,选取,编号,日期,文件上传,地址(并带有后码查询)
- 提交行动 ——发送电子邮件,显示确认消息,或提交后重定向(每个表单多动作,按顺序执行)
- 两集 —— 说
forms财务报告和审定财务报表form-submissions自动添加到您的有效载荷配置 - (原始内容存档于2013-10-12) (英语). REST API ——通过有效载荷自定义终点获取窗体数据并提交表格
- 前端组件 —— 说
<EnquiryForm>以内置步骤导航和验证来反应客户端组件 - 完全定制 ——禁用、替换或添加字段块类型;重命名收藏 satters
安装
pnpm add @foundrykit/advanced-forms-plugin
同伴依赖关系 (如尚未安装)
pnpm add payload react react-hook-form @radix-ui/react-popover
快开始
// payload.config.ts
import { formPlugin } from '@foundrykit/advanced-forms-plugin'
import { buildConfig } from 'payload'
export default buildConfig({
// Configure your email adapter here — the plugin uses payload.sendEmail
// for the `sendEmail` submission action.
// email: nodemailerAdapter({ ... }) or resendAdapter({ ... }), etc.
plugins: [formPlugin({})],
// ...rest of your config
})
这个 sendEmail 提交动作使用有效载荷内置 payload.sendEmail,哪个
常驻裁军谈判会议代表 电子邮件适配器 你们
在有效载荷配置上配置 。 不需要插件级电子邮件处理器 .
插件选项
formPlugin(options: FormPluginConfig)
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
disabled | boolean | false | 注册收藏但跳过添加端点 |
baseUrl | string | '' | 在构建时将基础 URL 准备形成 API URL(例如. https://example.com(中文(简体) ) |
mediaCollection | string | 'media' | 显示您收藏的媒体, 用于步骤图标上传 |
collections.forms | string | 'forms' | 窗体收藏的标签 |
collections.submissions | string | 'form-submissions' | 提交文件收集的屏障 |
labels.forms | string | 'Form' | 表单的 Singula 管理标签 |
labels.submissions | string | 'Form Submission' | 提交文件的 Singula 管理标签 |
fields | FieldsConfig | 全部内置 | 控制窗体构建器中可用的字段块类型——见 自定义字段块 |
垃圾邮件保护(ReCAPTCHA v3)
传出 a captcha 配置和每个表单的提交端点都需要验证
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}是CAPTCHA v3 代币 别管它了,或者把钥匙放出去,什么也没有
更改: 没有装入脚本, 不需要符号 .
formPlugin({
captcha: {
siteKey: process.env.RECAPTCHA_SITE_KEY,
secretKey: process.env.RECAPTCHA_SECRET_KEY,
minScore: 0.5, // optional, default 0.5
action: 'form_submit' // optional, default 'form_submit'
},
})
没有别的东西可以窃听. GET /api/form-data/:slug 广告 站点密钥
(按设计公开)浏览器, EnquiryForm 薄荷每服一剂,
财务报告和审定财务报表 POST /api/form-submit/:formSlug 在表单平之前验证一下
装入。 这个 密钥永不离开服务器 - 综合测试
声称表格答复中未提及.
得分,不是挑战. v3 是隐形的: 它返回 0.0- 1. 0 而您选择
截断。 调色板 minScore 从reCAPTCHA控制台 一旦你能看到真实
交通;太高无声地拒绝真正的人.
故意没打开. 如果Google无法访问, 或秘密是
错误, 允许提交, 原因会被记录为错误 。 封锁
将会丢弃真实的询问 时间长度的断电或
错位,比垃圾邮件还要贵 失踪或
拒绝的代号仍然很困难 403 仅限 基础设施 失败是
原谅我 注意日志 [forms] captcha could not be verified。 。 。 .
将站点密钥与每个为表格服务的域进行注册,包括
localhost 或每个当地提交文件都无法核实.
收款
表单( E)forms(中文(简体) )
存储构成定义. 可公开读取;创建/更新/删除需要认证.
每个表格文档有:
- 标题 — 显示名称
- 弹片 ——API中使用的独特标识符(例如.
contact-form(中文(简体) ) - 步骤 ——一个或多个步骤,每个步骤都有标题,可选图标,以及字段块列表
- 额外内容 ——表上或表下可选的丰富文本
- 行动 - 在成功提交后执行的一项或多项行动
提交资料form-submissions(中文(简体) )
每个划界案一份。 存储 :
- 标题 ——谁寄出:答案取名
name, (中文(简体) ),full-name或者说first-name+ 键last-name,或者电子邮件,或者日期。 用作页面标题. - 表单 ——与父母形式的关系
- 提交日期 — ISO 时间戳
- 数据 ——提交字段值的密钥/值对
- 元数据 —— 说
userAgent, (中文(简体) ),ip, (中文(简体) ),referrer从请求中捕获
在管理员中,划界案的答案在表格本身的问题下显示,按步骤分组, 选项值显示为标签和电话、计数器和地址答案. 编辑 在Answers卡上将文本解答变为输入,选择解答变为下拉,所以 工作人员可以在重发前修补错误的电子邮件;更改保存时保留. 电话柜台 和地址答案不能在那里编辑。 表格、日期和a 细节 纸牌( 页面) 来源,IP,浏览器和提交上下文)是只读的.
站点可以以相同的卡片样式显示自己的提交字段 SubmissionPanel 从
@foundrykit/advanced-forms-plugin/rsc作为田园 admin.components.Field数字 :
import { SubmissionPanel } from '@foundrykit/advanced-forms-plugin/rsc'
export const NotesField = ({ data }) => (
<SubmissionPanel
heading="Notes"
id="notes"
groups={[{ items: [{ label: 'Follow-up', value: data?.followUp ?? '' }] }]}
/>
)
升级到 Postgres 的提交视图 : 页:1 title 列是新建的,所以添加一个
移徙(ALTER TABLE "form_submissions" ADD COLUMN "title" varchar;重新生成导入
地图。 旧的呈文读取标题直到下次保存为止.
字段类型
| 块状弹片 | 说明 |
|---|---|
text | 单行文本输入( 支持) half 宽度) |
email | 电子邮件地址输入 |
phone | 电话号码输入 |
textarea | 多行文本( 可配置) rows(中文(简体) ) |
checkbox | 单个布尔复选框 |
radioGroup | 从列表中选择单一选项( :row 或者说 grid (布局) |
checkboxGroup | 从列表中选择多个选项( E)row 或者说 grid (布局) |
select | 下拉选择 |
number | 可选数字输入 min, (中文(简体) ), max, (中文(简体) ), step |
date | 可选取日期者 min编号 :max |
file | 文件上传到有效载荷收藏( 可配置) accept, (中文(简体) ), maxSizeMB(中文(简体) ) |
address | 英国邮政编码查询地址( 可配置) defaultCountry, (中文(简体) ), showLine2, (中文(简体) ), lookupLabel(中文(简体) ) |
所有字段块共享这些基属性: name, (中文(简体) ), label, (中文(简体) ), required, (中文(简体) ), tooltip。 。 。 .
旅行日期(可选入)
"你想什么时候旅行?":一个日历面板,游客在那里挑选 一个月 (单位:一年)
月按钮)或 确切的日期 (日历,通过 react-day-picker) (中文(简体) ). 这是我们自己的
面板而不是本地选择和日期输入,所以网站可以标出它:
enquiry-field__calendar* 课程。 它不在默认的集中, 所以添加到插件中不会
更改任何现有网站的计划。 登记,然后在邮递表中添加一个迁移:
import { formPlugin, TravelDateBlock } from '@foundrykit/advanced-forms-plugin'
formPlugin({ fields: { travelDate: { block: TravelDateBlock } } })
答案为 2027-08 或者说 2027-08-14 ,并在提交页面上显示为
"2027年8月"或"2027年8月14日". parseTravelDate(value) 给它覆盖的第一天和最后一天
(单位:千美元)from编号 :to,准确日期的同一天)和那个标签,用于映射一个客户关系管理系统.
要问“你灵活吗?” 只要不灵活就需要约会,就加上一个是 / 无字段和
给予旅行日期可见度规则: 规则匹配时需要此字段,灵活
等于 no。 。 。 .
地址字段
存储一个复合值 - { line1, line2, city, county, postcode, country } —— 说
在呈文中连载到JSON,同样的方式 phone 存储
{ country, number, e164 }。 。 。 .
检查由 邮政编码.io:免费,没有 API 密钥, 没有人每通电话开账单,所以球场可以不看任何公开形式 一个配额。 它只解决其城镇、县和国家的邮政编码问题——它确实如此 不返回一个房地列表,所以街道线总是被打入. 手动条目是 从未被屏蔽,使字段能够用于非英国地址和何时 寻取相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相.
A 类 所需经费 地址需要街道行和邮政编码:单是邮政编码 无法交付, 和搜索永远无法提供街道。 重用浏览 与导出助手的表格外 :
import { lookupPostcode } from '@foundrykit/advanced-forms-plugin/client'
const result = await lookupPostcode('SW1A 1AA')
// { status: 'ok', address: { postcode, city, county, country } }
// | { status: 'notFound' } | { status: 'error' }
提交行动
每个窗体配置一个或多个动作。 他们按出现顺序执行.
| 块 | 说明 |
|---|---|
sendEmail | 通过电子邮件发送邮件 payload.sendEmail (使用您的有效载荷) 电子邮件适配器) (中文(简体) ). 主题支持 {{fieldName}} 内插. |
confirmationMessage | 返回 API 响应中的 Lexical 丰富文本消息,用于显示前端 . |
redirect | 回返a redirectUrl 在API响应中。 前端组件处理导航. |
(原始内容存档于2013-10-12) (英语). REST API
两个端点都登记在有效载荷 API 根上(/api (默认情况下).
GET /api/form-data/:slug
以弹片取出窗体文档.
回应: 全文 FormDocument 贾森.
POST /api/form-submit/:formSlug
提交窗体的字段数据.
请求机构 :
{
"data": { "name": "Alice", "email": "[email protected]" },
"metadata": { "referrer": "https://example.com/about" }
}
成功反应200:
{
"success": true,
"actions": {
"confirmationMessage": { /* Lexical rich-text node */ },
"redirectUrl": "/thank-you"
}
}
校验出错的答复( E)422:
{
"success": false,
"errors": [{ "field": "email", "message": "Email is required" }]
}
前端用途
服务器组件——获取窗体数据
从 @foundrykit/advanced-forms-plugin/rsc数字 :
import { fetchForm } from '@foundrykit/advanced-forms-plugin/rsc'
export default async function ContactPage() {
const form = await fetchForm({ slug: 'contact-form' })
return <ContactForm form={form} />
}
fetchForm 选项:
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
slug | string | 所需经费 | 要获取的窗体 sattle |
baseUrl | string | '' | 覆盖 API 基础 URL |
formsSlug | string | 'forms' | 覆盖 URL 路径中使用的表单收藏 sattle |
客户部分——提供表格
从 @foundrykit/advanced-forms-plugin/client数字 :
'use client'
import { EnquiryForm } from '@foundrykit/advanced-forms-plugin/client'
import type { FormDocument } from '@foundrykit/advanced-forms-plugin'
export function ContactForm({ form }: { form: FormDocument }) {
return (
<EnquiryForm
form={form}
onSuccess={(result) => console.log('submitted', result)}
onError={(err) => console.error(err)}
/>
)
}
EnquiryForm 道具 :
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
form | FormDocument | 所需经费 | 表单数据由 fetchForm |
apiBase | string | '' | 提交端点的基础 URL |
className | string | —— 说 | 适用于根元素的 CSS 类 |
onSuccess | (result: SubmitResult) => void | —— 说 | 在成功提交后调用 |
onError | (error: SubmitError) => void | —— 说 | 当服务器返回验证错误时调用 |
additionalContent | ReactNode | —— 说 | 表格上或下的额外内容(尊重 additionalContent.position 设置) |
resolver | Resolver<Record<string, unknown>> | —— 说 | 可选反应-hook-form 解析器(例如. zodResolver(schema)) (中文(简体) ). 与每个外地管理员的规则合并. |
验证
每个字段都会暴露一个管理员 验证 组 : requiredMessage, (中文(简体) ),
minLength 编号 : maxLength 编号 : pattern 编号 : patternMessage (用于文本形状)
字段)和 min 编号 : max 编号 : minMessage 编号 : maxMessage (用于数字字段)
和多场比赛总数。 这些规则通过
buildFieldRules 帮助者,也从 /client 用于:
自定义字段组件.
对于更丰富的跨域逻辑,通过形式级解析器. 插件确实如此
没有 捆绑 zod 或者说 @hookform/resolvers 安装在您的应用程序中:
pnpm add zod @hookform/resolvers
'use client'
import { EnquiryForm } from '@foundrykit/advanced-forms-plugin/client'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z
.object({
travelKnown: z.enum(['yes', 'no']).optional(),
travelPlans: z.string().optional(),
})
.passthrough()
.superRefine((data, ctx) => {
if (data.travelKnown === 'no' && !data.travelPlans) {
ctx.addIssue({
path: ['travelPlans'],
code: 'custom',
message: 'Tell us a bit about what you’re looking for',
})
}
})
export function ContactForm({ form }) {
return <EnquiryForm form={form} resolver={zodResolver(schema)} />
}
每个外地管理员的规则和解决者一起运行——任一来源都可以标出
无效的字段,并逐个进行验证(goNext)尊重两者.
Hook — 自定义格式 UI
如果您需要完全控制窗体 UI, 请使用 useEnquiryForm 直接 :
'use client'
import { useEnquiryForm } from '@foundrykit/advanced-forms-plugin/client'
export function MyCustomForm({ form }) {
const { currentStep, totalSteps, stepData, form: rhf, goNext, goBack, submit, isSubmitting } =
useEnquiryForm({ form, apiBase: '' })
// render whatever you like
}
自定义字段块
这个 fields 选项:
formPlugin({
fields: {
// disable a built-in type
file: false,
// replace a built-in type with your own block
text: { block: myCustomTextBlock },
// add a new type
budgetRange: { block: BudgetRangeBlock },
},
})
内置弹道: text, (中文(简体) ), email, (中文(简体) ), phone, (中文(简体) ), textarea, (中文(简体) ), checkbox, (中文(简体) ), radioGroup, (中文(简体) ), checkboxGroup, (中文(简体) ), select, (中文(简体) ), number, (中文(简体) ), date, (中文(简体) ), file, (中文(简体) ), address。 。 。 .
结构概览
src/
├── index.ts # Plugin entry — merges collections and endpoints into Payload config
├── types.ts # All shared TypeScript types (field blocks, FormDocument, plugin config)
├── collections/
│ ├── Forms.ts # createFormsCollection factory
│ └── Submissions.ts # createSubmissionsCollection factory
├── blocks/
│ ├── fields/ # One file per built-in field block type
│ └── submissionActions/ # SendEmail, ConfirmationMessage, Redirect blocks
├── endpoints/
│ ├── fetchFormHandler.ts # GET /api/form-data/:slug
│ └── submitFormHandler.ts # POST /api/form-submit/:formSlug — validates, stores, fires actions
├── components/
│ └── EnquiryForm/ # React client component and useEnquiryForm hook
├── utilities/
│ ├── fetchEnquiryForm.ts # fetchForm — RSC utility wrapping the fetch endpoint
│ ├── buildFormURL.ts # URL builder for the form-data endpoint
│ └── sanitizeSubmission.ts # Sanitises raw form data before storage
└── exports/
├── client.ts # @foundrykit/advanced-forms-plugin/client exports
└── rsc.ts # @foundrykit/advanced-forms-plugin/rsc exports
插件遵循标准的有效载荷插件模式 : formPlugin(options) 返回接收并返回有效载荷的函数 Config 对象。 集合和端点的添加使用分布式语法来避免覆盖已存在的配置.
发展
pnpm dev # Start the dev app at http://localhost:3000
pnpm test # Run unit + integration + e2e tests
pnpm build # Compile to dist/
复制 dev/.env.example 改为 dev/.env 设置 DATABASE_URL 财务报告和审定财务报表 PAYLOAD_SECRET 运行前 dev。 。 。 .
释放
运行 pnpm release 打开 prod。它撞倒了版本,更新了更改日志并推出一个 v* 标签.
标签触发器 .github/workflows/publish.yml,通过信任的方式发布到 npm
发布,所以没有npm令牌在当地或GitHub存储.