diff --git a/.ua/index.json b/.ua/index.json index 29228fa..6e46be6 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T08:13:59.371Z", + "generatedAt": "2026-10-26T08:15:48.744Z", "repoRoot": ".", "packages": [ { @@ -1898,6 +1898,11 @@ "t": "string", "d": "控件下方辅助说明(格式提示等);报错时让位给错误文案" }, + { + "n": "(formItem)span", + "t": "number", + "d": "多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行" + }, { "n": "onReset", "t": "() => void", @@ -1929,7 +1934,8 @@ "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", - "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" + "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)", + "多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)" ] }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index b2311f6..7f2beac 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -184,6 +184,7 @@ const modalPick = signal('a'); // 弹窗内下拉的取值,用于演示 Esc // —— 表单校验状态集中声明 —— const inviteCode = signal(''); // 异步校验演示:邀请码 +const note = signal(''); // 表单跨列字段的值 const username = signal(''); const email = signal(''); const formResult = signal(''); @@ -524,7 +525,7 @@ const content = h( section( 'sec-formlab', '表单校验', - 'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行。', + 'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行;字段传 span 可跨列(备注整宽)。', form( { columns: 2, submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) }, [ @@ -535,6 +536,7 @@ const content = h( { label: '邀请码', required: true, value: inviteCode, rules: [{ validator: async (v) => { await new Promise((r) => setTimeout(r, 400)); return v === 'USED' ? '该邀请码已被使用' : true; }, message: '校验未通过' }] }, textField({ placeholder: '异步规则:输入 USED 会校验失败', value: inviteCode }), ), + formItem({ label: '备注', value: note, span: 2, hint: 'span: 2 —— 多列表单里整行铺开,单列时自动退回整行' }, textarea({ placeholder: '多行备注…', value: note, rows: 2, maxCount: 120 })), ], ), h('span', { class: 'out' }, () => formResult() || '等待提交…'), diff --git a/llms-full.txt b/llms-full.txt index c9134ae..3f41ada 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T08:13:59.371Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T08:15:48.744Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -951,6 +951,7 @@ const open = signal(false); drawer({ title: '详情', open, width: 360, onClose: - (formItem)rules:FormRule[] —— 校验规则:required / min / max / pattern / validator,按序执行取首个失败 - (formItem)validateOn(默认 blur):'input' | 'blur' | 'submit' —— 校验时机;缺省 blur(输入时自动清已报错误) - (formItem)hint:string —— 控件下方辅助说明(格式提示等);报错时让位给错误文案 +- (formItem)span:number —— 多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行 - onReset:() => void —— 重置按钮点击回调;组件不自行清空字段,由调用方重置信号;不传 resetText 不渲染按钮 - onError(回调):(count: number) => void —— 校验不通过时的回调,参数为不通字段数(适合埋点或顶部提示) - errorSummary:boolean —— 是否在表单顶部显示「有 N 项需要修改」汇总条;缺省 true,自带错误 UI 时传 false @@ -976,6 +977,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label: 11. formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交 12. 需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行 13. 字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1) +14. 多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动) 现场演示:apps/docs/demo.html#sec-formlab diff --git a/packages/skin/ui/src/form.ts b/packages/skin/ui/src/form.ts index fd24256..7401f20 100644 --- a/packages/skin/ui/src/form.ts +++ b/packages/skin/ui/src/form.ts @@ -39,6 +39,7 @@ export function formItem(props: FormItemProps, control: HTMLElement): FormItemHa const hint = props.hint ? h('div', { class: 'ua-form-hint' }, props.hint) : null; // 辅助说明:报错时隐藏,避免与错误挤在一起 const label = h('label', { class: 'ua-form-label' }, props.label, props.required ? h('i', { class: 'ua-form-req' }, '*') : null); const el = h('div', { class: 'ua-form-item' }, label, h('div', { class: 'ua-form-control' }, control, hint, error)); + if (props.span) el.style.gridColumn = `span ${props.span}`; // 多列布局里跨列;单列容器下由 CSS 还原为整行 let seq = 0; // 校验轮次:异步结论回来时若已有更新的一轮,丢弃旧结果(防乱序覆盖) /*** 错误态落点:控件根 + 内部原生控件,保证 select / checkbox 这类包裹式控件也能标红 ***/ diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 9d2ef72..d405d2a 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -400,6 +400,7 @@ export interface FormItemProps { rules?: FormRule[]; // 附加校验规则,按序执行 validateOn?: 'input' | 'blur' | 'submit'; // 校验时机;缺省 blur hint?: string; // 控件下方辅助说明(格式提示等);报错时让位给错误文案 + span?: number; // 多列表单里本字段占几列(如整宽的多行文本);单列布局下无效 } /*** 表单字段句柄:form 聚合校验用 ***/ diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 8daa84f..2d754e8 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1769,6 +1769,11 @@ "t": "string", "d": "控件下方辅助说明(格式提示等);报错时让位给错误文案" }, + { + "n": "(formItem)span", + "t": "number", + "d": "多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行" + }, { "n": "onReset", "t": "() => void", @@ -1800,7 +1805,8 @@ "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", - "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" + "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)", + "多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)" ] }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 5eee6fe..d7d24b9 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -4240,6 +4240,9 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); } .ua-grid { grid-template-columns: 1fr; } + .ua-grid > * { + grid-column: auto !important; /* 单列下忽略跨列请求,否则会凭空多出一列撑出横向滚动 */ + } } /* —— 悬浮操作按钮:fixed 停靠角落,hover 上浮 + 按压回缩 —— */