diff --git a/.ua/backlog.json b/.ua/backlog.json index a585231..ffe0c7c 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -1436,6 +1436,14 @@ "acceptance": "新增 ua sync-counts(幂等,无变化不写盘)把三份文档的「N 个组件 / N 个 DOM 组件 / 共 N 个」统一改写为清单实际数量;verify 的计数正则覆盖 README 标题形态并提示修法;npm run check 全绿", "notes": "自补:构建/门禁工具链改进(实测把 README 标题改成 61 后 verify 能报行号,sync-counts 一键修回)", "status": "done" + }, + { + "id": "g135", + "title": "表单支持多列布局 columns", + "motivation": "字段多的编辑页只能一列往下堆,长表单要滚很久;页面里已有 grid,form 却没用上,调用方只能自己拆成多个 form(校验被割裂)", + "acceptance": "form 新增 columns(1/2/3),>1 时字段走 grid 铺列、窄屏自动单列、按钮区始终整行;单向依赖(form 复用 grid);manifest props/steps 与 demo 同步;npm run check 全绿", + "notes": "自补:既有组件可用性细节", + "status": "open" } ] } diff --git a/.ua/index.json b/.ua/index.json index 8b2aa15..eeadbeb 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T07:24:00.739Z", + "generatedAt": "2026-10-26T07:26:53.819Z", "repoRoot": ".", "packages": [ { @@ -1772,6 +1772,12 @@ "d": "布局方向;缺省 vertical(horizontal 为标签左置)", "df": "vertical" }, + { + "n": "columns", + "t": "1 | 2 | 3", + "d": "字段列数:>1 时字段走栅格铺列(沿用 grid 的窄屏单列),按钮区始终整行;缺省 1", + "df": "1" + }, { "n": "submitText", "t": "string", @@ -1851,7 +1857,8 @@ "单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type=\"submit\",不会与点击回调重复触发", "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", - "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行" + "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", + "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" ] }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 7a20a6c..550e2d6 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -487,9 +487,9 @@ const content = h( section( 'sec-formlab', '表单校验', - 'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。', + 'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行。', form( - { submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) }, + { columns: 2, submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) }, [ formItem({ label: '用户名', required: true, value: username, rules: [{ min: 2, max: 12, message: '用户名需 2-12 个字符' }] }, textField({ placeholder: '2-12 个字符', value: username })), formItem({ label: '邮箱', required: true, value: email, hint: '用于登录与找回密码,不会公开', rules: [{ pattern: /^[\w.-]+@[\w-]+\.[\w.]+$/, message: '邮箱格式不正确' }] }, textField({ placeholder: 'you@example.com', value: email })), diff --git a/llms-full.txt b/llms-full.txt index 0ce6c63..540b068 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T07:24:00.739Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T07:26:53.819Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -922,6 +922,7 @@ const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: 参数: - layout(默认 vertical):'vertical' | 'horizontal' —— 布局方向;缺省 vertical(horizontal 为标签左置) +- columns(默认 1):1 | 2 | 3 —— 字段列数:>1 时字段走栅格铺列(沿用 grid 的窄屏单列),按钮区始终整行;缺省 1 - submitText:string —— 提交按钮文案;缺省「提交」 - resetText:string —— 重置按钮文案;缺省不渲染 - onSubmit(回调):() => void —— 全部字段校验通过后的提交回调 @@ -955,6 +956,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label: 10. rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖 11. formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交 12. 需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行 +13. 字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1) 现场演示:apps/docs/demo.html#sec-formlab diff --git a/packages/skin/ui/src/form.ts b/packages/skin/ui/src/form.ts index 0bbccce..fd24256 100644 --- a/packages/skin/ui/src/form.ts +++ b/packages/skin/ui/src/form.ts @@ -1,5 +1,6 @@ import { h } from '@ui-arch/frame'; import { button } from './button.js'; +import { grid } from './grid.js'; import type { FormItemHandle, FormItemProps, FormProps, FormRule } from './types.js'; /*** 执行规则的同步部分(required / min / max / pattern);null 通过,字符串为错误消息 ***/ @@ -124,11 +125,13 @@ export function form(props: FormProps, items: FormItemHandle[]): HTMLElement { props.onSubmit?.(); }; + const fields = items.map((i) => i.el); // 字段节点:单列直接铺,多列交给栅格 + const board = props.columns && props.columns > 1 ? grid({ columns: props.columns, gutter: 'lg' }, ...fields) : fields; // 多列布局复用 grid const el = h( 'form', { class: props.layout === 'horizontal' ? 'ua-form ua-form-h' : 'ua-form' }, props.errorSummary === false ? null : summary, // 汇总条:关掉时连节点都不插 - ...items.map((i) => i.el), + board, // 多列时是一层 grid,单列时是字段数组(h 自动扁平化) actions, ); el.addEventListener('submit', (e) => { diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 58d6202..5ad3a59 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -400,6 +400,7 @@ export interface FormItemHandle { /*** 表单容器入参 ***/ export interface FormProps { layout?: 'vertical' | 'horizontal'; // 布局方向;缺省 vertical + columns?: 1 | 2 | 3; // 字段列数:>1 时字段走栅格铺列(窄屏自动单列);缺省 1 submitText?: string; // 提交按钮文案;缺省「提交」 resetText?: string; // 重置按钮文案;缺省不渲染重置按钮 onSubmit?: () => void; // 全部字段校验通过后的提交回调 diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 1e7183b..362791f 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1643,6 +1643,12 @@ "d": "布局方向;缺省 vertical(horizontal 为标签左置)", "df": "vertical" }, + { + "n": "columns", + "t": "1 | 2 | 3", + "d": "字段列数:>1 时字段走栅格铺列(沿用 grid 的窄屏单列),按钮区始终整行;缺省 1", + "df": "1" + }, { "n": "submitText", "t": "string", @@ -1722,7 +1728,8 @@ "单行输入框内按 Enter 等同点击提交(textarea 内仍是换行);提交按钮为 type=\"submit\",不会与点击回调重复触发", "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", - "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行" + "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", + "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" ] }, {