参考
Schema 与 Config
schema 来自你的服务端,决定什么可以改;config 归你自己,决定它长什么样。包里的任何一处都不知道你的接口和图床。
两个输入
左边由服务端下发,右边由站点提供,两者用字段 key 对齐。
来自你的接口
// 你的接口按字段下发的内容 —— @nextmoe/edit-ui-core
interface EditSchemaField {
key: string
kind: string // text | int | enum | bool | date | imagehash | list | ref
diff_hint: string // inline | lines | items | image
deprecated?: boolean
locked: boolean
can_propose: boolean
can_review: boolean
would_automerge: boolean
// 以下来自另一张脸 GET /v2/catalog/schemas/{object},
// 用 mergeSchemaFaces(capsFields, valueFields) 合并进来
vocabulary?: string // /v2/vocabularies 里的词表名
encoding?: 'int' | 'token' // 有词表时必有:写入发整数码还是发 token
base?: number // int 编码:码 = base + 词表发布序下标
nullable?: boolean // null 会清除已存的值
element?: EditSchemaElement | null
}SchemaForm
<EditSchemaForm
:fields="fields"
:values="values"
:config="config"
:group-order="['基础', '媒体']"
:tabbed-groups="['关系']"
layout="tabs"
:disabled="isReviewer"
@update:patch="onPatch"
@update:valid="(ok) => (valid = ok)"
/>SchemaForm 的属性与事件
表单本身不发请求,也不保存——它只告诉你哪些字段变了。
| fields | EditSchemaField[] | 必填。服务端下发的 schema。 |
| values | Record<string, unknown> | 必填。服务端当前值,同时作为计算差异的基线。 |
| config | EditFieldConfigMap | 必填。标签、控件、分组,以及注入的函数。 |
| vocabularies | EditVocabularyMap | 可选。GET /v2/vocabularies 的结果。config 没写 options 的枚举字段会用它推导选项(config 写了则以 config 为准);不传时这类字段降级为只读。 |
| group-order | string[] | 分组顺序。没写进来的分组按自然顺序排在其后。 |
| tabbed-groups | string[] | 这些分组内部的字段渲染成二级标签页,而不是纵向堆叠。 |
| layout | 'stack' | 'tabs' | 默认 'stack'。'tabs' 把分组列表放到字段区域旁边:窄屏横向、md 以上纵向。两份 DOM 用 CSS 切换,不靠 JS media query。 |
| disabled | boolean | 把全部控件渲染为只读——同一个表单的审阅形态。 |
| @update:patch | Record<string, unknown> | 每次编辑都会触发:只包含与 values 深比较后确实不同的字段。锁定、已废弃与无提案权限的字段永远不会出现在里面。 |
| @update:valid | boolean | 提交闸门:为 false 时表单里至少有一个字段现在提交必被引擎拒绝——对象行缺必填列、列类型不对、超出 max_elements。用它禁用提交按钮,不必等 422。上一次提交的 errors 不算在内,那是给用户改的,不是拦他的。 |
| ref.valid / ref.invalidFields | boolean / Record<string, string[]> | 同一个信号的模板引用形态。invalidFields 按字段 key 给出待修的中文消息,可以直接列在提交按钮旁边。另有 dirtyCount 与 reset()。 |
可以指定的控件
config 里 control 的取值范围。
inputnumbertextareaselectswitchdatestring-listnumber-listobject-listentity-pickerentity-kind-pickerimageimage-listreadonly
不写 control 时,它由 schema 的 kind 与 diff_hint 经 resolveControl() 推导得出。写了不在上表中的值,字段会降级为只读展示,而不会抛错。
凡是要碰后端的,都是你注入的函数
这是包与站点之间唯一的边界。
| EditFieldConfig 键 | 签名 | 你要做的事 |
|---|---|---|
| uploadImage | (file, currentItems) => Promise<item | null> | 上传一张图片,返回要存进值里的那个条目。没有提供时,图片字段渲染为只读。 |
| resolveImage | (value) => string | 把存下来的图片值(哈希、一行记录、URL)变成 <img> 能显示的地址。 |
| searchEntities | (keyword) => Promise<EditSelectOption[]> | 为实体选择器搜索你的条目库。没有提供时,该字段不会渲染成选择器。 |
| resolveEntities | (ids) => EditSelectOption[] | Promise<…> | 首屏时把已存下来的 id 还原成可读的名称。 |
没有默认实现,没有兜底请求,也没有内置的 endpoint。