descriptions —— Lista de descripciones
La lista de descripciones es una «tabla de solo lectura»: muestra varios campos de un registro (etiqueta + valor), sin ordenación, ni filtrado, ni paginación. Su dificultad está muy concentrada: el cálculo del ajuste de línea cuando un elemento ocupa varias columnas. ¿Qué pasa si un elemento tiene span: 2 pero solo queda el ancho de 1 columna en la fila? ¿Y si la última fila solo tiene 2 elementos pero la fila tiene 4 columnas, hay que rellenar? buildDescriptionRows responde a estas preguntas.
Cuándo usarlo
Sección titulada «Cuándo usarlo»- Cuando cada plataforma implemente
LhDescriptions: páginas de detalle (detalle de pedido, perfil de usuario), resúmenes de configuración, presentación de información del flujo de aprobación. - Exhibición agrupada de «etiqueta-valor» en solo lectura, cuando cada fila debe alojar varios campos.
- No usarlo para: tablas que necesitan ordenación, filtrado o acciones por fila (usa
table); la exhibición de un único campo (basta con label + texto); cuando se necesita edición (usaform).
Importación
Sección titulada «Importación»import { getDescriptionsClassNames, buildDescriptionRows,} from '@lh-ui/share/core/descriptions'import type { LhDescriptionsItem, LhDescriptionsState } from '@lh-ui/share/core/descriptions'Intención de diseño
Sección titulada «Intención de diseño»Las cuatro reglas de buildDescriptionRows son todas defensivas, y cada una corresponde a un tipo de entrada real:
- El span ilegal se repliega a 1:
span <= 0, NaN, y los no numéricos (Number('宽')= NaN) se tratan siempre como 1. La configuración suele provenir de props que pasa el usuario, y un valor sucio no debería romper el layout hasta 0 columnas. - La limitación al ancho restante de la fila: cuando
used + span > column, se limita al ancho restante (mínimo 1 columna). Si se cambiara directamente de línea, la fila actual dejaría un hueco, con aspecto de bug. - Un span que supera toda la fila se limita a la fila completa: si con
column: 3se pasa un elemento conspan: 5, ocupa toda la fila, en lugar de desbordarse. - El relleno de la última fila: cuando la última fila no alcanza
column, el campofillerindica a la capa de renderizado cuántas celdas de marcador de posición hay que añadir: con el estilo de bordes, si no se rellena, faltaría un borde.
El valor devuelto es una «matriz de filas», cada una con cells (elementos + el span real) y filler (el número de marcadores a rellenar). La capa UI solo necesita dos niveles de v-for.
Tabla de funciones
Sección titulada «Tabla de funciones»| Función | Firma | Devuelve | Descripción |
|---|---|---|---|
getDescriptionsClassNames |
(state: LhDescriptionsState) => string[] |
nombres de clase | modificadores border |
buildDescriptionRows |
(items: LhDescriptionsItem[], column: number) => Row[] |
estructura de filas | ajuste de línea + relleno |
type Row = { cells: { item: LhDescriptionsItem; span: number }[] filler: number}Definiciones de tipos
Sección titulada «Definiciones de tipos»export interface LhDescriptionsItem { label: string value?: string | number // también se puede pasar con un slot de ámbito span?: number // número de columnas que ocupa; por defecto 1; si supera column se limita}
export interface LhDescriptionsState { column: number // número de columnas que se muestran por fila border: boolean // si se muestran los bordes (layout de tabla)}Accesibilidad
Sección titulada «Accesibilidad»descriptions es un componente de exhibición, sin requisitos de roles de APG. La estructura de DOM recomendada es la semántica de lista de definiciones:
<dl> <dt>Nombre de usuario</dt><dd>lhgb</dd> <dt>Correo</dt><dd>i@lhgb.top</dd></dl>Esto se corresponde de forma natural con el modelo de datos «etiqueta-valor», y el lector de pantalla puede anunciar correctamente «Nombre de usuario: lhgb». Si se implementa con <table> (habitual con el estilo de bordes), añade role="presentation" o conserva la semántica de la cabecera: no uses un verdadero <th scope="row"> sin un summary, o el lector de pantalla creerá que se trata de una tabla interactiva.
Cuando hay bordes, las celdas de relleno del filler deberían llevar aria-hidden="true", para evitar que el lector de pantalla anuncie celdas vacías.
Ejemplos de uso
Sección titulada «Ejemplos de uso»import { buildDescriptionRows } from '@lh-ui/share/core/descriptions'
// column 3: el tercer elemento tiene span 2, pero solo queda ancho de 1 columna → se limita a 1buildDescriptionRows([ { label: 'Nombre de usuario', value: 'lhgb' }, { label: 'Correo', value: 'i@lhgb.top' }, { label: 'Firma', value: 'Escribo código', span: 2 },], 3)// [{ cells: [Nombre de usuario(1), Correo(1), Firma(1)], filler: 0 }] ← tras la limitación, la fila queda justa
// Atención: la implementación **limita al ancho restante** en lugar de cambiar de línea:// cuando el span no cabe en la fila actual, no se empuja a la siguientebuildDescriptionRows([ { label: 'Nombre de usuario', value: 'lhgb' }, { label: 'Firma', value: 'Escribo código', span: 2 }, { label: 'Correo', value: 'i@lhgb.top' },], 2)// [// { cells: [Nombre de usuario(1), Firma(2→1 limitado)], filler: 0 }, ← la fila queda completa// { cells: [Correo(1)], filler: 1 }, ← la última fila se rellena con 1 marcador// ]
// Un elemento cuyo span supera el número de columnas de la fila: también se limita al ancho restante (aquí, toda la fila)buildDescriptionRows([{ label: 'A', span: 1 }, { label: 'B', span: 5 }], 2)// [{ cells: [A(1), B(5→1 limitado)], filler: 0 }] ← B entra en la 1 columna que resta de la primera fila
// Solo cuando hay un único elemento B y su span supera toda la fila, ocupa la fila completabuildDescriptionRows([{ label: 'B', span: 5 }], 2)// [{ cells: [B(5→2 limitado)], filler: 0 }]
// El span ilegal se repliegabuildDescriptionRows([{ 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: usa gridColumn: `span ${cell.span}` de CSS grid;// atención: label y value ocupan cada uno span columnas, de modo que un «column 3»// son en realidad 6 celdas de la cuadrícula// uniapp: <view> + proporción flex (span / column * 100%);// con border, las líneas de tabla de un layout flex se simulan con pseudoelementos o box-shadowEstilos y tokens
Sección titulada «Estilos y tokens»| Clase | Significado |
|---|---|
.lh-descriptions |
clase base |
.lh-descriptions--border |
con bordes (la etiqueta se distingue por el color de fondo) |
.lh-descriptions__row |
una fila |
.lh-descriptions__label |
casilla de la etiqueta |
.lh-descriptions__value |
casilla del valor |
.lh-descriptions__filler |
marcador de la última fila (aria-hidden) |
Los dos modos del ancho de la etiqueta: con border, etiqueta y valor comparten columna con el mismo ancho; sin bordes, el ancho de la etiqueta lo decide el contenido (se puede sobrescribir con --lh-descriptions-label-width). El texto de la etiqueta usa --lh-text-color-secondary y el valor, --lh-text-color-primary.
- La relación entre column y las celdas de la cuadrícula:
spanes una unidad de «número de elementos», pero al renderizar, la etiqueta y el valor ocupan cada uno una porción de ancho. Si con CSS grid se trata a label y value como celdas independientes, uncolumn: 3necesita en realidad una cuadrícula de 6 columnas: la definición del número de columnas de la capa UI debe coincidir con el criterio decolumndel core (un elemento = una unidad de span). - column responsivo: en pantallas estrechas reduce
column(por ejemplo, de 3 a 1) ybuildDescriptionRowsrecalcula. core no observa el ancho de la pantalla; la capa UI conmuta con media queries o container queries. - La key del slot de ámbito: la capa UI suele usar
item.labelcomo nombre del slot; si la etiqueta contiene caracteres especiales (espacios, caracteres chinos), la compatibilidad de los frameworks es aceptable, pero con/o con etiquetas duplicadas habrá conflictos: se recomienda que la capa UI ofrezca un alias de camposlot(este tipo aún no lo proporciona; cada plataforma puede extenderlo por su cuenta). fillersolo es obligatorio en el modo border: sin bordes, las celdas de relleno son invisibles, pero conservarlas no añade coste de renderizado y renderizarlas siempre resulta más simple.- items vacíos:
buildDescriptionRows([], 3)devuelve[]y la capa UI renderiza un contenedor vacío: recuerda ofrecer un estado vacío (el componenteemptyo un texto de aviso). - La exhibición por defecto del valor: lo que se muestra cuando
valuees undefined lo decide la capa UI (normalmente—); core no interviene en los textos.
@lh-ui/share/core/descriptions → buildDescriptionRows( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue