参考

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 的属性与事件

表单本身不发请求,也不保存——它只告诉你哪些字段变了。

fieldsEditSchemaField[]必填。服务端下发的 schema。
valuesRecord<string, unknown>必填。服务端当前值,同时作为计算差异的基线。
configEditFieldConfigMap必填。标签、控件、分组,以及注入的函数。
vocabulariesEditVocabularyMap可选。GET /v2/vocabularies 的结果。config 没写 options 的枚举字段会用它推导选项(config 写了则以 config 为准);不传时这类字段降级为只读。
group-orderstring[]分组顺序。没写进来的分组按自然顺序排在其后。
tabbed-groupsstring[]这些分组内部的字段渲染成二级标签页,而不是纵向堆叠。
layout'stack' | 'tabs'默认 'stack'。'tabs' 把分组列表放到字段区域旁边:窄屏横向、md 以上纵向。两份 DOM 用 CSS 切换,不靠 JS media query。
disabledboolean把全部控件渲染为只读——同一个表单的审阅形态。
@update:patchRecord<string, unknown>每次编辑都会触发:只包含与 values 深比较后确实不同的字段。锁定、已废弃与无提案权限的字段永远不会出现在里面。
@update:validboolean提交闸门:为 false 时表单里至少有一个字段现在提交必被引擎拒绝——对象行缺必填列、列类型不对、超出 max_elements。用它禁用提交按钮,不必等 422。上一次提交的 errors 不算在内,那是给用户改的,不是拦他的。
ref.valid / ref.invalidFieldsboolean / 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。