iter(g169): 表单字段支持跨列

This commit is contained in:
HIsheR
2026-10-26 16:16:07 +08:00
parent adc94cabf5
commit bd98fc8e70
7 changed files with 26 additions and 5 deletions
+8 -2
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "ui-arch-registry-index/0.1", "$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-26T08:13:59.371Z", "generatedAt": "2026-10-26T08:15:48.744Z",
"repoRoot": ".", "repoRoot": ".",
"packages": [ "packages": [
{ {
@@ -1898,6 +1898,11 @@
"t": "string", "t": "string",
"d": "控件下方辅助说明(格式提示等);报错时让位给错误文案" "d": "控件下方辅助说明(格式提示等);报错时让位给错误文案"
}, },
{
"n": "(formItem)span",
"t": "number",
"d": "多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行"
},
{ {
"n": "onReset", "n": "onReset",
"t": "() => void", "t": "() => void",
@@ -1929,7 +1934,8 @@
"rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖",
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交",
"需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行",
"字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)",
"多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)"
] ]
}, },
{ {
+3 -1
View File
@@ -184,6 +184,7 @@ const modalPick = signal('a'); // 弹窗内下拉的取值,用于演示 Esc
// —— 表单校验状态集中声明 —— // —— 表单校验状态集中声明 ——
const inviteCode = signal(''); // 异步校验演示:邀请码 const inviteCode = signal(''); // 异步校验演示:邀请码
const note = signal(''); // 表单跨列字段的值
const username = signal(''); const username = signal('');
const email = signal(''); const email = signal('');
const formResult = signal(''); const formResult = signal('');
@@ -524,7 +525,7 @@ const content = h(
section( section(
'sec-formlab', 'sec-formlab',
'表单校验', '表单校验',
'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行。', 'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处,onError 拿到不通字段数。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行;字段传 span 可跨列(备注整宽)。',
form( form(
{ columns: 2, submitText: '注册', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) }, { 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: '校验未通过' }] }, { 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 }), 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() || '等待提交…'), h('span', { class: 'out' }, () => formResult() || '等待提交…'),
+3 -1
View File
@@ -1,6 +1,6 @@
# @ui-arch 组件库 Agent 文档(无图形版) # @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)。 > 组件 = 普通函数,传 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)rules:FormRule[] —— 校验规则:required / min / max / pattern / validator,按序执行取首个失败
- (formItem)validateOn(默认 blur):'input' | 'blur' | 'submit' —— 校验时机;缺省 blur(输入时自动清已报错误) - (formItem)validateOn(默认 blur):'input' | 'blur' | 'submit' —— 校验时机;缺省 blur(输入时自动清已报错误)
- (formItem)hint:string —— 控件下方辅助说明(格式提示等);报错时让位给错误文案 - (formItem)hint:string —— 控件下方辅助说明(格式提示等);报错时让位给错误文案
- (formItem)span:number —— 多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行
- onReset:() => void —— 重置按钮点击回调;组件不自行清空字段,由调用方重置信号;不传 resetText 不渲染按钮 - onReset:() => void —— 重置按钮点击回调;组件不自行清空字段,由调用方重置信号;不传 resetText 不渲染按钮
- onError(回调):(count: number) => void —— 校验不通过时的回调,参数为不通字段数(适合埋点或顶部提示) - onError(回调):(count: number) => void —— 校验不通过时的回调,参数为不通字段数(适合埋点或顶部提示)
- errorSummary:boolean —— 是否在表单顶部显示「有 N 项需要修改」汇总条;缺省 true,自带错误 UI 时传 false - 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 聚合提交 11. formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交
12. 需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行 12. 需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行
13. 字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1) 13. 字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)
14. 多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)
现场演示:apps/docs/demo.html#sec-formlab 现场演示:apps/docs/demo.html#sec-formlab
+1
View File
@@ -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 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 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)); 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; // 校验轮次:异步结论回来时若已有更新的一轮,丢弃旧结果(防乱序覆盖) let seq = 0; // 校验轮次:异步结论回来时若已有更新的一轮,丢弃旧结果(防乱序覆盖)
/*** 错误态落点:控件根 + 内部原生控件,保证 select / checkbox 这类包裹式控件也能标红 ***/ /*** 错误态落点:控件根 + 内部原生控件,保证 select / checkbox 这类包裹式控件也能标红 ***/
+1
View File
@@ -400,6 +400,7 @@ export interface FormItemProps {
rules?: FormRule[]; // 附加校验规则,按序执行 rules?: FormRule[]; // 附加校验规则,按序执行
validateOn?: 'input' | 'blur' | 'submit'; // 校验时机;缺省 blur validateOn?: 'input' | 'blur' | 'submit'; // 校验时机;缺省 blur
hint?: string; // 控件下方辅助说明(格式提示等);报错时让位给错误文案 hint?: string; // 控件下方辅助说明(格式提示等);报错时让位给错误文案
span?: number; // 多列表单里本字段占几列(如整宽的多行文本);单列布局下无效
} }
/*** 表单字段句柄:form 聚合校验用 ***/ /*** 表单字段句柄:form 聚合校验用 ***/
+7 -1
View File
@@ -1769,6 +1769,11 @@
"t": "string", "t": "string",
"d": "控件下方辅助说明(格式提示等);报错时让位给错误文案" "d": "控件下方辅助说明(格式提示等);报错时让位给错误文案"
}, },
{
"n": "(formItem)span",
"t": "number",
"d": "多列表单里本字段占几列(如整宽的多行备注);单列布局或窄屏下自动退回整行"
},
{ {
"n": "onReset", "n": "onReset",
"t": "() => void", "t": "() => void",
@@ -1800,7 +1805,8 @@
"rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖", "rules.validator 返回 Promise 即为异步规则(如「邀请码是否已被使用」远程校验):提交时逐项 await,校验期间提交按钮禁用,结论按轮次取舍避免乱序覆盖",
"formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交", "formItem 返回 { el, validate, validateAsync } 句柄:validate 只跑同步规则(供 blur/input 即时反馈),validateAsync 逐条 await 供 form 聚合提交",
"需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行", "需要给字段加格式说明就传 hint:平时显示在控件下方,校验失败时自动让位给错误文案,同一位置不堆两行",
"字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)" "字段多的编辑页传 columns: 2 或 3 走多列栅格:列间距与窄屏堆叠沿用 grid,按钮区始终整行;单列表单不要传(默认 1)",
"多列表单里想整宽就传 span:formItem({ span: 2 }, textarea(...)) 会占满两列,窄屏单列时自动忽略跨列(否则会撑出横向滚动)"
] ]
}, },
{ {
+3
View File
@@ -4240,6 +4240,9 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
.ua-grid { .ua-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.ua-grid > * {
grid-column: auto !important; /* 单列下忽略跨列请求,否则会凭空多出一列撑出横向滚动 */
}
} }
/* —— 悬浮操作按钮:fixed 停靠角落,hover 上浮 + 按压回缩 —— */ /* —— 悬浮操作按钮:fixed 停靠角落,hover 上浮 + 按压回缩 —— */