EmDash 的 json 字段类型可存储任意结构化数据,但默认编辑器是单行文本输入,需要手动输入原始 JSON。Field Kit 是一个第一方插件,为 json 字段提供四个可组合的小部件,完全通过 seed options 配置 — 网站构建者无需使用 React。
安装
npm i @emdash-cms/plugin-field-kit
在 astro.config.mjs 中注册插件:
import { defineConfig } from "astro/config";
import emdash from "emdash";
import { fieldKitPlugin } from "@emdash-cms/plugin-field-kit";
export default defineConfig({
integrations: [
emdash({
plugins: [fieldKitPlugin()],
}),
],
});
然后通过将 widget 设置为 field-kit:<name> 来为任何 json 字段附加小部件:
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": { "fields": [...] }
}
小部件
| 小部件 | 用途 | 存储值 |
|---|---|---|
object-form | 扁平 JSON 对象的内联表单 | { key: value, ... } |
list | 带有添加/删除/重排功能的有序数组编辑器 | [{ ... }, ...] |
grid | 行 × 列矩阵 | { rowKey: { colKey: value } } |
tags | 自由形式的标签/芯片输入 | ["tag1", "tag2"] |
如果小部件缺少必需的 options(例如 object-form/list 的 fields,或 grid 的 rows/columns),编辑器会渲染内联警告”小部件配置错误”而不是损坏的输入 — 这在迭代 seed 架构时很有用。
object-form
渲染一组类型化的子字段,作为单个 JSON 对象存储。适用于固定形状的结构化数据,如营养成分或联系信息。
{
"slug": "nutrition",
"type": "json",
"widget": "field-kit:object-form",
"options": {
"collapsed": false,
"fields": [
{ "key": "calories", "label": "Calories", "type": "number", "suffix": "kcal" },
{ "key": "protein", "label": "Protein", "type": "number", "suffix": "g" },
{ "key": "fat", "label": "Fat", "type": "number", "suffix": "g" },
{ "key": "carbs", "label": "Carbs", "type": "number", "suffix": "g" }
]
}
}
存储值:{ "calories": 250, "protein": 12.5, "fat": 8, "carbs": 30 }。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
fields | SubFieldDef[] | (必需) | 子字段定义 — 参见子字段。 |
collapsed | boolean | false | 默认折叠渲染组。 |
helpText | string | — | 小部件下方显示的帮助文本。 |
list
带有添加、删除和重新排序控件的有序数组编辑器。每一行是一个 JSON 对象,其形状由 fields 定义。行标题显示从 Mustache 风格模板渲染的摘要。
{
"slug": "ingredients",
"type": "json",
"widget": "field-kit:list",
"options": {
"itemLabel": "Ingredient",
"min": 1,
"max": 50,
"sortable": true,
"summary": "{{name}} — {{amount}}",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "amount", "label": "Amount", "type": "text" },
{ "key": "optional", "label": "Optional", "type": "boolean" }
]
}
}
存储值:
[
{ "name": "Flour", "amount": "500g", "optional": false },
{ "name": "Butter", "amount": "200g", "optional": false }
]
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
fields | SubFieldDef[] | (必需) | 每行的子字段定义。 |
itemLabel | string | "Item" | 行的单数标签(用于”添加”按钮和后备行标题)。 |
min | number | — | 最小项目数。低于此数时,删除按钮隐藏。 |
max | number | — | 最大项目数。达到此数时,添加按钮隐藏。 |
sortable | boolean | true | 显示上/下重排按钮。 |
summary | string | — | 作为折叠行标题渲染的 Mustache 模板。参见摘要模板。 |
helpText | string | — | 小部件下方显示的帮助文本。 |
grid
行 × 列的二维矩阵。每个单元格可以是开关、文本输入、数字输入或选择。适用于季节性可用性、价格表或功能比较等矩阵。
{
"slug": "availability",
"type": "json",
"widget": "field-kit:grid",
"options": {
"cell": "toggle",
"rows": [
{ "key": "berries", "label": "Berries" },
{ "key": "stoneFruit", "label": "Stone fruit" },
{ "key": "citrus", "label": "Citrus" }
],
"columns": [
{ "key": "spring", "label": "Spring" },
{ "key": "summer", "label": "Summer" },
{ "key": "autumn", "label": "Autumn" },
{ "key": "winter", "label": "Winter" }
]
}
}
存储值:
{
"berries": { "spring": false, "summer": true, "autumn": false, "winter": false },
"stoneFruit": { "spring": false, "summer": true, "autumn": true, "winter": false },
"citrus": { "spring": false, "summer": false, "autumn": true, "winter": true }
}
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
rows | GridAxisDef[] | (必需) | 行定义:{ key, label, image? }。 |
columns | GridAxisDef[] | (必需) | 列定义:{ key, label, image? }。 |
cell | "toggle" | "text" | "number" | "select" | "toggle" | 单元格输入类型,统一应用于每个单元格。 |
cellOptions | string[] | Array<{ label, value }> | [] | 当 cell 为 "select" 时必需。 |
helpText | string | — | 小部件下方显示的帮助文本。 |
tags
用于字符串数组的芯片样式输入。支持固定的 suggestions 列表、自由形式的自定义值(可切换)、大小写转换和可选的 max。
{
"slug": "keywords",
"type": "json",
"widget": "field-kit:tags",
"options": {
"placeholder": "Add a keyword…",
"max": 10,
"transform": "lowercase",
"allowCustom": true,
"suggestions": ["vegan", "vegetarian", "gluten-free", "dairy-free", "nut-free"]
}
}
存储值:["vegan", "gluten-free"]。
按 Enter 或 , 提交标签。在空输入上按 Backspace 删除最后一个标签。重复标签会被静默忽略。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
placeholder | string | "Add..." | 没有标签时显示的输入占位符。 |
max | number | — | 最大标签数。达到限制时输入隐藏。 |
suggestions | string[] | [] | 通过 <datalist> 显示的自动完成建议。 |
allowCustom | boolean | true | 当为 false 时,只能添加来自 suggestions 的值。 |
transform | "none" | "lowercase" | "uppercase" | "trim" | "none" | 添加标签时进行规范化。 |
helpText | string | — | 小部件下方显示的帮助文本。 |
子字段
object-form 和 list 接受类型化子字段定义的 options.fields 数组。每个条目都有一个 key(它写入的 JSON 对象键)、一个 label、一个 type 和特定于类型的额外内容。
| 子字段类型 | 渲染为 | 值得注意的额外内容 |
|---|---|---|
text | 单行输入 | placeholder |
textarea | 多行输入 | rows(默认 3)、placeholder |
number | 数字输入 | min、max、step、prefix、suffix、placeholder |
boolean | 切换开关 | — |
select | 下拉菜单 | options: string[] | Array<{ label, value }>、placeholder |
date | 日期输入 | — |
color | 配对十六进制文本输入的原生颜色选择器 | — |
url | URL 输入(HTML5 type="url") | placeholder |
每个子字段的通用属性:required、helpText、defaultValue。
摘要模板
list 小部件使用 options.summary 中的 Mustache 风格模板渲染每个折叠行。{{key}} 被替换为该键的行值(强制转换为字符串)。假值回退到 "{itemLabel} {n}"。
"summary": "{{name}} — {{amount}}"
渲染类似 Flour — 500g 的行。模板是简单的字符串替换 — 没有 HTML,没有嵌套表达式。
数据持久性
Field Kit 小部件在字段的现有列中存储纯 JSON。没有特定于插件的表,没有外键,没有架构变更。如果您从配置中删除 @emdash-cms/plugin-field-kit,数据仍然有效 — 只有编辑 UI 会恢复到默认的 json 文本输入。
即使更改小部件形状也适用:存储对象上的未知键在下次写入时保留,因此您可以在不丢失在旧字段集下捕获的数据的情况下演变架构。
另请参阅
- 插件概述 — EmDash 插件的工作原理。
- 选择插件格式 — 如果 Field Kit 不合适,编写您自己的字段小部件。
- Discussion #571 — 促成此插件的提案。