descriptions —— 描述列表
本頁內容尚未翻譯。
描述列表是「只读的表格」:展示一条记录的若干字段(标签 + 值),不需要排序、筛选、分页。它的难点很集中——条目占据多列时的换行计算:某个条目 span: 2,但本行只剩 1 列宽,怎么办?末行只有 2 个条目但共 4 列,要不要补位?buildDescriptionRows 就是在回答这些问题。
- 各端实现
LhDescriptions:详情页(订单详情、用户资料)、设置摘要、审批流信息展示。 - 只读的「标签-值」成组展示,且需要每行容纳多个字段。
- 不该用它:需要排序/筛选/行操作的表格(用
table);单字段展示(直接 label + 文本即可);需要编辑(用form)。
import { getDescriptionsClassNames, buildDescriptionRows,} from '@lh-ui/share/core/descriptions'import type { LhDescriptionsItem, LhDescriptionsState } from '@lh-ui/share/core/descriptions'buildDescriptionRows 的四条规则都是防御性的,每条对应一类真实输入:
- 非法 span 兜底为 1:
span <= 0、NaN、非数字(Number('宽')= NaN)一律按 1 处理。配置常来自用户传的 props,脏值不该让布局崩成 0 列。 - 超本行剩余宽度钳制:
used + span > column时钳到剩余宽度(至少 1 列)。若直接换行,当前行会留下一个空洞,视觉上像 bug。 - 单条 span 超整行钳到整行:
column: 3时传span: 5的条目占满整行,而不是溢出。 - 末行补位:最后一行不足
column时,filler字段告知渲染层要补几个占位单元格——带边框样式下不补位会缺一条边框。
返回值是「行的数组」,每行含 cells(条目 + 实际 span)和 filler(要补的占位数)。UI 层只需两层 v-for。
| 函数 | 签名 | 返回 | 作用 |
|---|---|---|---|
getDescriptionsClassNames |
(state: LhDescriptionsState) => string[] |
类名 | border 修饰 |
buildDescriptionRows |
(items: LhDescriptionsItem[], column: number) => Row[] |
行结构 | 换行 + 补位 |
type Row = { cells: { item: LhDescriptionsItem; span: number }[] filler: number}export interface LhDescriptionsItem { label: string value?: string | number // 也可用作用域插槽传入 span?: number // 占据列数,默认 1,超出 column 会被钳制}
export interface LhDescriptionsState { column: number // 每行展示的列数 border: boolean // 是否展示边框(表格布局)}descriptions 是展示性组件,没有 APG 角色要求。推荐的 DOM 结构是定义列表语义:
<dl> <dt>用户名</dt><dd>lhgb</dd> <dt>邮箱</dt><dd>i@lhgb.top</dd></dl>这与「标签-值」的数据模型天然对应,读屏软件能正确播报「用户名:lhgb」。若用 <table> 实现(带边框样式时常见),加 role="presentation" 或保留表头语义——不要用真正的 <th scope="row"> 又不给 summary,那会让读屏软件以为这是个可交互表格。
带边框时 filler 补位的占位单元格应加 aria-hidden="true",避免读屏软件播报空单元格。
import { buildDescriptionRows } from '@lh-ui/share/core/descriptions'
// column 3:第三个条目 span 2,但本行只剩 1 列宽 → 钳到 1buildDescriptionRows([ { label: '用户名', value: 'lhgb' }, { label: '邮箱', value: 'i@lhgb.top' }, { label: '签名', value: '写代码的', span: 2 },], 3)// [{ cells: [用户名(1), 邮箱(1), 签名(1)], filler: 0 }] ← 钳制后恰好满行
// 注意:实现是**钳制到剩余宽度**而非换行——span 放不进本行时不会顶到下一行buildDescriptionRows([ { label: '用户名', value: 'lhgb' }, { label: '签名', value: '写代码的', span: 2 }, { label: '邮箱', value: 'i@lhgb.top' },], 2)// [// { cells: [用户名(1), 签名(2→1 钳制)], filler: 0 }, ← 满行// { cells: [邮箱(1)], filler: 1 }, ← 末行补 1 个占位// ]
// 单条 span 超过整行列数:同样被钳到剩余宽度(此处即整行)buildDescriptionRows([{ label: 'A', span: 1 }, { label: 'B', span: 5 }], 2)// [{ cells: [A(1), B(5→1 钳制)], filler: 0 }] ← B 放进第一行剩余的 1 列
// 只有 B 一条、且 span 超过整行时才占满整行buildDescriptionRows([{ label: 'B', span: 5 }], 2)// [{ cells: [B(5→2 钳制)], filler: 0 }]
// 非法 span 兜底buildDescriptionRows([{ label: 'A', span: 0 }, { label: 'B', span: NaN }, { label: 'C', span: -1 }], 3)// [{ cells: [A(span 1), B(span 1), C(span 1)], filler: 0 }]<!-- Vue3 --><div :class="getDescriptionsClassNames({ column, border })"> <div v-for="(row, ri) in rows" :key="ri" class="lh-descriptions__row"> <template v-for="(cell, ci) in row.cells" :key="ci"> <div class="lh-descriptions__label" :style="{ gridColumn: `span ${cell.span}` }"> {{ cell.item.label }} </div> <div class="lh-descriptions__value" :style="{ gridColumn: `span ${cell.span}` }"> <slot :name="cell.item.label" :item="cell.item">{{ cell.item.value }}</slot> </div> </template> <div v-for="f in row.filler" :key="`filler-${f}`" class="lh-descriptions__filler" aria-hidden="true" /> </div></div>const rows = computed(() => buildDescriptionRows(props.items, props.column))// React:用 CSS grid 的 gridColumn: `span ${cell.span}`;// 注意 label 与 value 各占 span 列,所以「column 3」实际是 6 个网格单元// uniapp:<view> + flex 比例(span / column * 100%);// 带 border 时 flex 布局的边框需要用伪元素或 box-shadow 模拟表格线| 类名 | 语义 |
|---|---|
.lh-descriptions |
基础类 |
.lh-descriptions--border |
带边框(标签底色区分) |
.lh-descriptions__row |
一行 |
.lh-descriptions__label |
标签格 |
.lh-descriptions__value |
值格 |
.lh-descriptions__filler |
末行占位(aria-hidden) |
标签宽度的两种模式:border 下标签与值同列等宽;无边框时标签宽度由内容决定(--lh-descriptions-label-width 可覆盖)。标签文字 --lh-text-color-secondary,值 --lh-text-color-primary。
- column 与网格单元的关系:
span是「条目数」单位,但渲染时标签和值各占一份宽度。用 CSS grid 时若把 label/value 各当一个网格单元,column: 3实际需要 6 列网格——UI 层的列数定义要与 core 的column口径一致(一个条目 = 一个 span 单位)。 - 响应式 column:窄屏把
column调小(如 3 → 1),buildDescriptionRows重算即可。core 不监听屏幕宽度,UI 层用媒体查询或容器查询切换。 - 作用域插槽的 key:UI 层通常用
item.label作插槽名,标签含特殊字符(空格、中文)时框架兼容性 OK,但含/或重复标签会冲突——建议 UI 层提供slot字段别名(本类型暂未提供,各端可自行扩展)。 filler只在 border 模式下必需:无边框时补位单元格不可见,但保留它们不占额外渲染成本,统一渲染更简单。- 空 items:
buildDescriptionRows([], 3)返回[],UI 层渲染空容器——记得给空态(empty组件或提示文案)。 - 值的默认显示:
value为 undefined 时显示什么由 UI 层定(通常—),core 不插手文案。
@lh-ui/share/core/descriptions → buildDescriptionRows( )返回值
(填好参数后点运行)Copyright © 2024-2026 风默文学 | 宁ICP备2024006574号 |
苏公网安备32040002010537