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 > columnspan ограничивается остатком (не менее 1 столбца). Если сделать перенос, в текущей строке останется дыра, что визуально похоже на баг. - Одиночный 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 // можно также передать через 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 столбец → ограничивается до 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 оставшийся столбец первой строки
// Целую строку элемент занимает, только если он один и его span превышает строкуbuildDescriptionRows([{ label: 'B', span: 5 }], 2)// [{ cells: [B(5→2 ограничение)], filler: 0 }]
// Откат некорректных spanbuildDescriptionRows([{ 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-слое должно совпадать с принятым в corecolumn(один элемент = одна единица 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