Перейти к содержимому

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 — оборонительные, каждое отвечает классу реальных входных данных:

  1. Некорректный span откатывается к 1: span <= 0, NaN, нечисловое значение (Number('ширина') = NaN) обрабатываются как 1. Конфигурация часто приходит из props, которые передаёт пользователь, и грязное значение не должно обрушить раскладку в 0 столбцов.
  2. Ограничение по оставшейся ширине строки: при used + span > column span ограничивается остатком (не менее 1 столбца). Если сделать перенос, в текущей строке останется дыра, что визуально похоже на баг.
  3. Одиночный span, превышающий целую строку, ограничивается до целой строки: при column: 3 элемент со span: 5 занимает всю строку, а не переполняется.
  4. Дополнение последней строки: если в последней строке меньше 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 // можно также передать через scoped-слот
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 столбец → ограничивается до 1
buildDescriptionRows([
{ 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 оставшийся столбец первой строки
// Целую строку элемент занимает, только если он один и его 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-слой переключает через media query или container query.
  • Ключ scoped-слота: UI-слой обычно использует item.label в качестве имени слота; если метка содержит спецсимволы (пробелы, китайские иероглифы), совместимость каркаса нормальная, но наличие / или повторяющихся меток приведёт к конфликтам — рекомендуется, чтобы UI-слой предоставлял поле-псевдоним slot (в этом типе оно пока не предусмотрено, каждая платформа может расширить сама).
  • filler обязателен только в режиме border: без рамок плейсхолдерные ячейки невидимы, но их присутствие не стоит дополнительного рендера, а единообразная отрисовка проще.
  • Пустые items: buildDescriptionRows([], 3) возвращает [], UI-слой отрисует пустой контейнер — не забудьте про пустое состояние (компонент empty или поясняющий текст).
  • Отображение значения по умолчанию: что показывать, когда value равен undefined, решает UI-слой (обычно —); core в тексты не вмешивается.
@lh-ui/share/core/descriptions → buildDescriptionRows( )
返回值
(填好参数后点运行)

Copyright © 2024-2026 FengMo WenXue