测试首页 / FORM GALLERY

表单活样板 · 字段类型与状态画廊

系统表单元件的活模板:14 个字段类型 × 六种状态 × 只读三模式,全部由 assets/form-kit.js 真实渲染。

i

本页是 docs/form-rendering-standard.md 的活样板,规范以文档为准。 第一重定位:文档里每一型的输入态 / 显示态契约,在这里就是一个能点的实例;form-kit 后续改动(含 picker 形态重构)在本页即时反映。 第二重定位:本页是表单重构的试验场——形态先在这里试、并排比、拍板,再向业务页推广,业务页重构与系统整体重构因此解耦;路线选择(如 picker 的 A+flip 与 B 原地展开两种候选形态)就在试验场一节并排挂载。 演示契约:工具页不是业务演示页,本页不注册进 guided-demo 场景体系(无 contract、无 adapter、无 data-guide-id)。

READ-ONLY PRESENTATION

一、只读呈现:只读展示不用表单控件

三档显示模式里「只读」这一档的呈现方式。字段显示模式只有三档:可编辑 = 可填可改;隐藏 = hidden 或不渲染;只读一律退化为纯文本呈现。新建只读呈现只能从下面三种模式里挑。

文档锚点 §2.7R-1 默认R-2 三条例外R-3 判别口诀规范源 docs/form-rendering-standard.md(仓库内,未随站点发布)

R-1 三种既有模式

新建只读呈现只许从这里挑
不得自创第四种

模式一 · 键值块(默认)

合同编号HT-2026-0043
委托单位无锡精密制造有限公司
合同金额¥ 128,600.00
检测周期15 个工作日

权威定义:.kv-grid / .kv 的唯一定义是 public/lims2.css 里以 .kv-grid{ 起头的四条规则(4 列卡片式,窄视口在同文件媒体查询里收成 2 列)。public/assets/tokens.css:411-419 的 dl/dt/dd 两列变体属独立栈(contract-review 不 link lims2.css),不适用本条;tokens.css 与 tech-leader 共用,别据此推断 tech-leader 也在本规范内。

先例 business/contracts/page.js:293先例 assets/business-common.js:2075-2082先例 portal/index.html:112,122

栅格覆盖(沿用既有内联惯例,不新增栅格类)

默认 4 列

样品编号YP-2026-1187
样品名称一次性使用输液器
规格型号1.0 × 20 ml
批次B2609-04

两列 style="grid-template-columns:1fr 1fr"

送样日期2026-09-28
收样日期2026-09-29

先例 portal/changes/page.js:109先例 business/changes/page.js:82

单列长文本 style="grid-template-columns:1fr"

检测依据GB 15810—2019 一次性使用输液器 第 4 章 生物学试验 GB/T 1962.1—2003 输液器第 1 部分

先例 test-plans/page.js:214先例 assets/portal-common.js:911

全宽键 style="grid-column:1/-1"

备注客户要求加急,报告需随附偏离表; 附件为上批次报告复印件。

先例 portal/orders/page.js:835

模式二 · 只读文本行 / 长文本块

长文本走 <pre>

原始记录载荷(Base64 片段)
eyJ0ZW1wZXJhdHVyZSI6MjMuNSwic2FtcGxlbmFtZSI6Il...kZXQ=
提交人:zhangsan 审核人:lisi

先例 test-plans/page.js:214-216

弱化补充行走 <small class="muted">

报价备注:本批次按加急计费,报告寄顺丰。

商务专员 张三 · 最后修改 2026-09-30 14:22

先例 business/page.js:1202先例 assets/portal-common.js:915

模式三 · 表格单元格

样品编号检测项目判定实测值
YP-2026-1187一次性使用输液器细菌内毒素合格< 0.5 EU/mL
YP-2026-1188一次性使用输液器无菌不合格检出

为什么 disabled 更糟:.form-grid input:disabled 一律灰底灰字(public/lims2.css:290),select:disabled 还额外把下拉箭头换成停用态淡三角(:328、:331)。成片出现就是实测的「灰色方块堆积」——问题不只是难看,是整屏看起来像一张没人填完的表。
为什么 readonly 也不行:不命中 :disabled 所以没有灰底,但控件的几何、光标与边框和可填的输入框一模一样,用户第一反应就是去点它。M14 把报价只读合计从 input 改成键值块时的用户原话是「显示一个可编辑的输入框本身就不对」。

先例 business/page.js:1198-1204先例 business/page.js:1131-1133

R-2 三条例外:真正需要表单语义时才用控件

例外,不是默认
  1. 值必须能被 execCommand('copy') 一次性复制(门户账号一次性凭据)→ 用隐藏 textarea。这是全站唯一应当用控件承载只读值的场景。
    正例 public/business/customers/page.js:184-193:opacity:0;position:fixed + readonly + execCommand('copy')。
  2. 同一份 DOM 要在「只读 ↔ 可编辑」之间切换、切换时不重建子树、取值链又依赖控件 → 用 readonly 而非 disabled。注意 disabled 控件不参与表单取值(先例 public/testing/records/page.js:181 的注释踩过这个坑),readonly 则不影响取值与复制,也不命中 :disabled 的灰底。

    只读 ↔ 可编辑切换(readonly,不重建子树)

    取值链依赖控件:disabled 时 values() 会漏掉这两个字段,提交体随之缺项;readonly 不会。

  3. 条件必填 / 条件启用的可编辑控件 → disabled 是业务门控不是只读呈现,不受 R-1 约束。
    正例 public/samples/page.js:214-224(勾选后才可填,且同时 hidden)、public/test-plans/page.js:32(方案类型互斥)、public/business/page.js:521-524(阶梯首档 / 末档锁定)。

R-3 判别口诀

不会被用户改,且没有解锁入口

→ 只读展示,走 R-1。详情头、字段组摘要、财务快照。

不会被用户改,但提交链 / 取值链依赖控件,或同一 DOM 要切换

→ R-2 第 2 条,用 readonly。

会被用户改,只是此刻条件不满足 / 正在提交 / 门控没过

→ 保持现状,不属于本条。业务门控类 disabled 见附二黑名单,统一改造时不得连坐。

FIELD TYPOLOGY

二、字段类型学 M1–M14

一个字段类型 = 一对契约(输入态渲染器 + 显示态渲染器),再加一层与类型正交的修饰符。每型一节:输入态六种状态 + 显示态按 §2.7 三种模式呈现。表单层类型名(M1–M14)尚未进入 form-kit,各节「现形态」列的是今天真实可用的 spec 键;两者的差集就是类型学落地的路线图。

文档锚点 §2.8.3 类型清单文档锚点 §2.8.4 逐类型契约文档锚点 §2.8.5 野生类型收编账规范源 docs/form-rendering-standard.md(仓库内,未随站点发布)

MODIFIER LAYER

三、修饰符层

类型是「这是什么」,修饰符是「它有什么」,两者正交:required / placeholder / hint 之于 short-text,和之于 money,是同一件事。下表是修饰符全景,已定义未实现 的那些是 form-kit 的路线图。

文档锚点 §2.9.2 数值域文档锚点 §2.9.3 文本域文档锚点 §2.9.4 通用域文档锚点 §2.9.5 枚举域规范源 docs/form-rendering-standard.md(仓库内,未随站点发布)

SUBMIT & VALIDATION

四、提交与校验契约(L1 硬约束)

这一节把 §1.1–§1.6 六条硬约束做成活的:真 <form>、一个提交入口、统一提交守卫、字段级错误、必填唯一事实源。校验失败一律字段级呈现(控件 aria-invalid + 行内 .field-error 红字 + 表单顶部 .form-errors 列表),toast 不承担字段校验提示。

文档锚点 §1.1文档锚点 §1.2文档锚点 §1.4文档锚点 §1.5文档锚点 §1.6

页内综合表单

真 form + submitButton 插槽
留空必填项再提交,看错误怎么落到字段旁

提交守卫:首选 form-kit 的 submitButton 插槽自动挂 withSubmitGuard(§1.2)。本页不接 modal-busy:它的 toast 是必填注入项,而 toast 尚未并入 form-kit(§2.6 记债),注入一份就等于新增第 6 份同构复制,违反 §2.6「新增代码一律引 form-kit」。真要发请求的表单走 modal-busy,本页不发请求。

弹窗内表单

footer 按钮用 form= 关联
§1.1:弹窗内和页内都算

弹窗 footer 的提交按钮用 form="表单id" 属性关联(先例 public/testing/training/index.html:53,102,113,124、public/testing/tasks/index.html:61)。

五种状态在本页怎么来的

不是贴图,是真实通道
状态怎么来的对应约束
默认spec 无修饰符;placeholder 按类型适用§2.8.1 A-1(省略即视为缺陷)
必填 + 校验错误required + 「触发校验」按钮 = 真 <form> 的 type="submit";由 form-kit 收集全部错误后逐字段 setFieldError§1.1 / §1.4 / §1.5
禁用disabled:业务门控语义,不是只读呈现§2.9.4 / §2.7 R-2 第 3 条
只读不用控件,走 §2.7 R-1 纯文本呈现§2.7 R-1
预填value / checked / 已选 option§2.8 A-1(默认值须声明)
hinthint 渲染成 .hint;不得用 placeholder 写「(必填)/(选填)」§2.9.4 hint 行;§2.9.3 placeholder 禁区;§1.5

FORM SHAPE LAB

五、试验场:增强态与退出态并排

表单重构先在这里试形态:并排挂多个候选形态、看效果、拍板,再向业务页推广。picker 的检索增强层当前形态由 §2.4 定死为「原生 <select> 保底 + 增强层,引擎 choices.js 11.2.4」(M26 已拍板、M38 已换芯,§2.9.6 的形态占位段随之补齐)——本页只呈现已落地的现状,不预先发明候选形态。下面 A / B 并排挂的就是本次翻转后的默认态与显式退出态。

文档锚点 §2.4 复合选择器文档锚点 §2.9.6规范源 docs/form-rendering-standard.md(仓库内,未随站点发布)

形态 A · 显式退出增强(enhance:false)

唯一退出键
§2.4:增强前 / 无 JS 就是普通 select,可用、可测

默认增强(2026-10-01 SLDEFP01 翻转,取代 2026-09-28 的「默认不增强」):单行下拉一律默认可检索,type:'select' 与别名 type:'picker' 走同一条判据——不写 enhance 就是增强态,只有显式写 enhance:false 才退回原生外观(就是下面这个字段)。存量 spec 里的 enhance:true 从此冗余,保留无害。

≤3 个固定值优先 radio / checkbox 组(与旧文案的「两三个选项」同口径,阈值可调):全部选项一眼看全、无需点开。「优先」是规范层引导、不是强制——引擎不改写声明类型,写 select 就渲染下拉、写 radio 就渲染单选组,两者都是合法选择,只是本型要多付一次「点开面板再点选」。列表框(size)已降为 choiceStyle:'listbox' 的显式特例、且与默认增强互斥(size 写在增强态 select 上不产生可见效果,要列表框必须同时写 enhance:false),不进默认陈列(M4 一节「输入态变体」里有实例)。

窄视口(innerWidth < 768)一律不初始化增强层,原生 select 接管,resize 实时切换、值不丢——把窗口拖窄就能看到下面 B 变成 A。全站 5 份下拉库的横向对比不在本页(那是选型用的 select-lab,与本页互补不重复)。

形态 B · 默认增强(不写 enhance)

单行 + 可检索
§2.4:选中值写回原生 select

两条形态的提交都走同一条 submitButton 插槽:取值、校验、setFieldError 只认原生 select,增强层属展示细节。必填为什么还成立:choices 把 select 置 hidden 并自己加 .choices__input 类,顶回 display 的是 vendor CSS 里那两条 .choices__input{display:…} 作者规则(压过 UA 的 [hidden]{display:none}),实测 checkValidity()===false、focus() 成功——详见 §2.4 的机制记档。type:'picker' 是待废弃别名(§2.9.6),保留可用,新增字段不得再写。

HEADER FILTER ROW

六、表头筛选行形态与筛选模型

表头第二行筛选(form-rendering-standard §2.5.1)在本页是活的:控件渲染进 <thead> 的第二行、与数据列同一网格,筛选与列严格 1:1。每个控件配一枚 「× 清除」——默认态不出现,非默认态才出现,点它恢复该列默认并立刻重新取数,不必再点「筛选」或「刷新」。

文档锚点 form-rendering-standard §2.5.1 表头第二行筛选行文档锚点 form-rendering-standard §2.5.2 筛选模型§2.5.1 标签去重(M91)三角命中条带 §2.4(M96)规范源 docs/form-rendering-standard.md(仓库内,未随站点发布)

筛选与列 1:1 · 每格一枚「× 清除」

真实 hydrateFilter layout:'header-row'
改任一格看 × 冒出来,点 × 看列表立刻重取

1:1 由构造保证:筛选行第 N 格就是第 N 列的筛选,columns 写错(越界 / 交叠 / 漏声明)一律抛错,摊不出一个「无家可归」的筛选。不设「更多筛选」面板——那正是无家可归筛选的藏身处。

单号客户状态金额(元)
正在读取…
取数次数0
最近一次条件—
命中行数—
非默认态的列—

× 的落点分两种:输入框内嵌右端(控件自己给右端让出 28px);下拉那支排在三角命中条带左侧(条带占右沿 30px,× 再往左 6px),点 × 不会顺手把面板开出来——按钮挂在 <td> 上而不是字段外层那个 <label> 里,正是为了躲开 label 的激活转发。键盘可达:原生 <button>,aria-label 是「清除该列筛选」,Tab 能走到、Enter / 空格能触发。

默认态不用页面声明:默认 = 水合那一刻的值——下拉是首条空选项(全部)、输入框是空串。所以「非默认」永远是事实判断,不是页面自己喊的。