diff --git a/.ua/index.json b/.ua/index.json index 9d69e1f..ec2308d 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T08:04:47.351Z", + "generatedAt": "2026-10-26T08:09:26.855Z", "repoRoot": ".", "packages": [ { @@ -2104,6 +2104,18 @@ "n": "title", "t": "string", "d": "分组标题;缺省不渲染标题行" + }, + { + "n": "layout", + "t": "'horizontal' | 'vertical'", + "df": "'horizontal'", + "d": "字段名位置:horizontal 在值左侧,vertical 在值上方(窄栏 / 长文本更耐读,配 bordered 时去掉标签区的分隔线)" + }, + { + "n": "extra", + "t": "unknown", + "k": 1, + "d": "标题行右侧内容(如更新时间、刷新按钮);仅在传了 title 时渲染" } ], "example": "descriptions({ title: '订单信息', column: 2, bordered: true, items: [{ label: '订单号', value: 'RB-2048' }, { label: '金额', value: '¥1,280.00' }, { label: '备注', value: '加急', span: 2 }] })", @@ -2113,7 +2125,8 @@ "value 支持任意 DOM:放 tag、badge、button 都可以;要给值做响应式更新就传函数 () => 内容", "bordered: true 是表格形态(适合打印与密集信息),纯网格形态更轻,适合卡片内嵌", "窄屏自动降为单列,无需在调用方写媒体查询", - "纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value" + "纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value", + "窄栏或长文本场景把 layout 设成 'vertical':字段名落到值上方,bordered 形态会自动去掉标签右侧的分隔线与底纹;标题行要放操作就传 extra(数组可放多个节点)" ] }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 4fb6c38..6119d08 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -698,7 +698,7 @@ const content = h( section( 'sec-descriptions', '描述列表', - 'descriptions:label : value 网格,column 定列数(窄屏自动单列);bordered 切表格形态,span 让长字段占整行。', + 'descriptions:label : value 网格,column 定列数(窄屏自动单列);bordered 切表格形态,span 让长字段占整行;layout=vertical 把字段名挪到值上方,extra 给标题行加操作区。', descriptions({ title: '订单信息(纯网格 · 3 列)', column: 3, @@ -722,6 +722,18 @@ const content = h( { label: '收件地址', value: '浙江省杭州市余杭区文一西路 969 号', span: 2 }, ], }), + divider({ dashed: true }), + descriptions({ + title: '竖排 + 标题操作区(窄栏场景)', + column: 2, + layout: 'vertical', + extra: [h('span', {}, '更新于 15:20'), button({ label: '刷新', variant: 'ghost', onClick: () => toast.info('已刷新') })], + items: [ + { label: '合同编号', value: 'HT-2026-0913' }, + { label: '签署日期', value: '2026-09-13' }, + { label: '备注', value: '续签两年,价格按季度结算;此列用 span 占满整行以演示长文本换行。', span: 2 }, + ], + }), ), section( 'sec-segmented', diff --git a/llms-full.txt b/llms-full.txt index 616cc9e..8d25641 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T08:04:47.351Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T08:09:26.855Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -1074,6 +1074,8 @@ segmented({ options: [{ value: 'day', label: '日' }, { value: 'week', label: ' - bordered(默认 false):boolean —— 表格形态(带分隔线与字段名底纹);缺省 false 纯网格 - size(默认 md):'sm' | 'md' —— 尺寸档位;缺省 md - title:string —— 分组标题;缺省不渲染标题行 +- layout(默认 'horizontal'):'horizontal' | 'vertical' —— 字段名位置:horizontal 在值左侧,vertical 在值上方(窄栏 / 长文本更耐读,配 bordered 时去掉标签区的分隔线) +- extra(回调):unknown —— 标题行右侧内容(如更新时间、刷新按钮);仅在传了 title 时渲染 示例: @@ -1089,6 +1091,7 @@ descriptions({ title: '订单信息', column: 2, bordered: true, items: [{ label 4. bordered: true 是表格形态(适合打印与密集信息),纯网格形态更轻,适合卡片内嵌 5. 窄屏自动降为单列,无需在调用方写媒体查询 6. 纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value +7. 窄栏或长文本场景把 layout 设成 'vertical':字段名落到值上方,bordered 形态会自动去掉标签右侧的分隔线与底纹;标题行要放操作就传 extra(数组可放多个节点) 现场演示:apps/docs/demo.html#sec-descriptions diff --git a/packages/skin/ui/src/descriptions.ts b/packages/skin/ui/src/descriptions.ts index 1a1f083..ef213d6 100644 --- a/packages/skin/ui/src/descriptions.ts +++ b/packages/skin/ui/src/descriptions.ts @@ -10,12 +10,16 @@ function cell(item: DescriptionItem, column: number): HTMLElement { return cellEl; } -/*** 描述列表:label:value 两栏网格,column 定桌面列数,窄屏由样式表降到单列;bordered 走表格形态 ***/ +/*** 描述列表:label:value 两栏网格,column 定桌面列数,窄屏由样式表降到单列;bordered 走表格形态,layout=vertical 把字段名挪到值上方 ***/ export function descriptions(props: DescriptionsProps): HTMLElement { const column = props.column ?? 3; - const grid = h('div', { class: `ua-desc ua-desc-${props.size ?? 'md'}${props.bordered ? ' is-bordered' : ''}` }, + const vertical = props.layout === 'vertical' ? ' is-vertical' : ''; + const grid = h('div', { class: `ua-desc ua-desc-${props.size ?? 'md'}${props.bordered ? ' is-bordered' : ''}${vertical}` }, ...props.items.map((item) => cell(item, column))); grid.style.setProperty('--ua-desc-col', String(column)); // 列数交给 CSS 变量,媒体查询可在窄屏覆盖 if (!props.title) return grid; - return h('div', { class: 'ua-desc-wrap' }, h('div', { class: 'ua-desc-title' }, props.title), grid); + const head = props.extra // 标题行:给了操作区就左右分栏,否则保持原来的单标题结构 + ? h('div', { class: 'ua-desc-head' }, h('div', { class: 'ua-desc-title' }, props.title), h('div', { class: 'ua-desc-extra' }, props.extra)) + : h('div', { class: 'ua-desc-title' }, props.title); + return h('div', { class: 'ua-desc-wrap' }, head, grid); } diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 68c234a..4d94d48 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -445,6 +445,8 @@ export interface DescriptionsProps { bordered?: boolean; // 表格形态(带分隔线与底纹);缺省 false(纯网格) size?: 'sm' | 'md'; // 尺寸档位;缺省 md title?: string; // 分组标题;缺省不渲染 + layout?: 'horizontal' | 'vertical'; // 字段名位置:horizontal 在值左侧(缺省),vertical 在值上方(窄栏 / 长文本更耐读) + extra?: unknown; // 标题行右侧内容(如刷新按钮、更新时间);无 title 时忽略 } /*** 统计数值入参 ***/ diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 2fe0a10..d7de390 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1975,6 +1975,18 @@ "n": "title", "t": "string", "d": "分组标题;缺省不渲染标题行" + }, + { + "n": "layout", + "t": "'horizontal' | 'vertical'", + "df": "'horizontal'", + "d": "字段名位置:horizontal 在值左侧,vertical 在值上方(窄栏 / 长文本更耐读,配 bordered 时去掉标签区的分隔线)" + }, + { + "n": "extra", + "t": "unknown", + "k": 1, + "d": "标题行右侧内容(如更新时间、刷新按钮);仅在传了 title 时渲染" } ], "example": "descriptions({ title: '订单信息', column: 2, bordered: true, items: [{ label: '订单号', value: 'RB-2048' }, { label: '金额', value: '¥1,280.00' }, { label: '备注', value: '加急', span: 2 }] })", @@ -1984,7 +1996,8 @@ "value 支持任意 DOM:放 tag、badge、button 都可以;要给值做响应式更新就传函数 () => 内容", "bordered: true 是表格形态(适合打印与密集信息),纯网格形态更轻,适合卡片内嵌", "窄屏自动降为单列,无需在调用方写媒体查询", - "纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value" + "纯展示组件,无状态无回调:值变了由调用方重新渲染或传函数式 value", + "窄栏或长文本场景把 layout 设成 'vertical':字段名落到值上方,bordered 形态会自动去掉标签右侧的分隔线与底纹;标题行要放操作就传 extra(数组可放多个节点)" ] }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 97e168a..e525bc6 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -2247,6 +2247,29 @@ button.ua-step-dot:hover { font-size: var(--ua-font-size-md); font-weight: 600; } +.ua-desc-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--ua-space-3); + min-width: 0; +} +.ua-desc-extra { + display: flex; + align-items: center; + gap: var(--ua-space-1); + color: var(--ua-color-text-muted); + font-size: var(--ua-font-size-sm); +} +.ua-desc.is-vertical .ua-desc-item { + flex-direction: column; + gap: 2px; /* 竖排时字段名与值贴紧,避免看成两行内容 */ +} +.ua-desc.is-vertical.is-bordered .ua-desc-label { + border-right: 0; + background: transparent; + padding-bottom: 0; +} .ua-desc-item { display: flex; gap: var(--ua-space-2);