feat(descriptions): 新增 FaDescriptions 组件及示例

This commit is contained in:
Hooray
2026-06-10 18:27:21 +08:00
parent 6ebdea493c
commit c0cd2ffc9f
19 changed files with 627 additions and 0 deletions
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -93,6 +93,14 @@ const routes: RouteRecordRaw = {
title: '右键菜单',
},
},
{
path: 'descriptions',
name: 'componentExampleDescriptions',
component: () => import('@/views/component_example/descriptions.vue'),
meta: {
title: '描述列表',
},
},
{
path: 'divider',
name: 'componentExampleDivider',
+2
View File
@@ -29,6 +29,7 @@ declare module 'vue' {
FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -86,6 +87,7 @@ declare global {
const FaCheckboxGroup: typeof import('@fantastic-admin/components')['FaCheckboxGroup']
const FaCollapsible: typeof import('@fantastic-admin/components')['FaCollapsible']
const FaContextMenu: typeof import('@fantastic-admin/components')['FaContextMenu']
const FaDescriptions: typeof import('@fantastic-admin/components')['FaDescriptions']
const FaDivider: typeof import('@fantastic-admin/components')['FaDivider']
const FaDrawer: typeof import('@fantastic-admin/components')['FaDrawer']
const FaDropdown: typeof import('@fantastic-admin/components')['FaDropdown']
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { descriptions } from '@fantastic-admin/components/examples'
</script>
<template>
<div>
<FaPageHeader title="描述列表" description="FaDescriptions" />
<FaPageMain
v-for="example in descriptions"
:key="example.title"
:code="example.componentRaw"
:title="example.title"
>
<component :is="example.component" />
</FaPageMain>
</div>
</template>
+1
View File
@@ -16,6 +16,7 @@ const BASIC_COMPONENT_NAMES = [
'FaCheckboxGroup',
'FaCollapsible',
'FaContextMenu',
'FaDescriptions',
'FaDivider',
'FaDrawer',
'FaDropdown',
@@ -0,0 +1,55 @@
# FaDescriptions 描述列表
纯展示信息组件,用于详情页、资料卡片、审核信息等只读数据场景。支持横向与纵向布局、边框、跨列、具名插槽和单项扩展字段。
## 使用场景
- 用户、订单、商品等详情信息展示
- 表单提交后的只读确认信息
- 弹窗中的轻量信息摘要
- 卡片内的结构化字段展示
## Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `items` | `DescriptionItem[]` | `[]` | 描述项配置 |
| `column` | `number` | `3` | 每行展示的描述项数量 |
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | 布局方向 |
| `border` | `boolean` | `false` | 是否显示边框和单元格分割线 |
| `labelWidth` | `string \| number` | - | label 区域宽度,仅横向布局生效 |
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | 单元格密度 |
| `emptyText` | `string` | `'-'` | value 为空时的占位文本 |
| `class` | `HTMLAttributes['class']` | - | 根节点 class |
| `labelClass` | `HTMLAttributes['class']` | - | 全局 label 单元格 class |
| `valueClass` | `HTMLAttributes['class']` | - | 全局 value 单元格 class |
## DescriptionItem
| 属性 | 类型 | 说明 |
|------|------|------|
| `key` | `string` | 插槽标识。使用具名插槽时需要提供,组件不会自动转换 |
| `label` | `string` | label 文本 |
| `value` | `string \| number \| boolean \| null \| undefined` | value 文本 |
| `span` | `number` | 占用几个描述项列,默认 `1` |
| `class` | `HTMLAttributes['class']` | 当前项 label 和 value 的公共 class |
| `labelClass` | `HTMLAttributes['class']` | 当前项 label 单元格 class |
| `valueClass` | `HTMLAttributes['class']` | 当前项 value 单元格 class |
`DescriptionItem` 支持扩展字段,扩展字段会原样保留在插槽参数的 `item` 中。
## Slots
| 名称 | 说明 | 作用域参数 |
|------|------|------------|
| `label-{key}` | 自定义指定项 label | `item`、`index`、`label`、`value` |
| `value-{key}` | 自定义指定项 value | `item`、`index`、`label`、`value` |
## 注意事项
1. `key` 只用于生成具名插槽,不用于读取数据。
2. `key` 不会自动转换,建议使用字母、数字、短横线和下划线。
3. `value` 为 `null`、`undefined` 或空字符串时显示 `emptyText`;`0` 和 `false` 会正常显示。
4. `span` 表示占用几个描述项列,超出 `column` 时会按 `column` 处理。
5. 最后一行不足 `column` 时会自动补齐空单元格,保证边框网格完整。
6. 组件不内置标题、空状态和点击事件,可与 `FaCard`、`FaEmpty` 等组件组合使用。
@@ -0,0 +1,16 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaDescriptions from '../index.vue'
const items = [
{ label: 'Username', value: 'kooriokami' },
{ label: 'Telephone', value: '18100000000' },
{ label: 'Place', value: 'Suzhou' },
{ label: 'Remarks', value: 'School' },
{ label: 'Address', value: 'No.1188, Wuzhong Avenue', span: 2 },
]
</script>
<template>
<FaDescriptions :items="items" label-width="120px" />
</template>
@@ -0,0 +1,16 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaDescriptions from '../index.vue'
const items = [
{ label: 'Username', value: 'kooriokami' },
{ label: 'Telephone', value: '18100000000' },
{ label: 'Place', value: 'Suzhou' },
{ label: 'Remarks', value: 'School' },
{ label: 'Address', value: 'No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province', span: 2 },
]
</script>
<template>
<FaDescriptions :items="items" border label-width="120px" />
</template>
@@ -0,0 +1,65 @@
<script setup lang="ts">
import type { DescriptionItem } from '../index.vue'
// 组件实际使用时无需手动导入,框架会自动导入
import FaIcon from '../../icon/index.vue'
import FaTag from '../../tag/index.vue'
import FaDescriptions from '../index.vue'
type ProfileItem = DescriptionItem<{
icon: string
tagVariant?: 'default' | 'secondary' | 'destructive' | 'outline'
}>
const items: ProfileItem[] = [
{ key: 'username', label: 'Username', value: 'kooriokami', icon: 'i-lucide:user' },
{ key: 'telephone', label: 'Telephone', value: '18100000000', icon: 'i-lucide:smartphone' },
{ key: 'place', label: 'Place', value: 'Suzhou', icon: 'i-lucide:map-pin' },
{ key: 'remarks', label: 'Remarks', value: 'School', icon: 'i-lucide:notebook-text', tagVariant: 'outline' },
{ key: 'address', label: 'Address', value: 'No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province', icon: 'i-lucide:building-2', span: 2 },
]
</script>
<template>
<FaDescriptions :items="items" border label-width="200px">
<template #label-username="{ item, label }">
<div class="inline-flex gap-2 items-center">
<FaIcon :name="item.icon" class="size-4" />
<span>{{ label }}</span>
</div>
</template>
<template #label-telephone="{ item, label }">
<div class="inline-flex gap-2 items-center">
<FaIcon :name="item.icon" class="size-4" />
<span>{{ label }}</span>
</div>
</template>
<template #label-place="{ item, label }">
<div class="inline-flex gap-2 items-center">
<FaIcon :name="item.icon" class="size-4" />
<span>{{ label }}</span>
</div>
</template>
<template #label-remarks="{ item, label }">
<div class="inline-flex gap-2 items-center">
<FaIcon :name="item.icon" class="size-4" />
<span>{{ label }}</span>
</div>
</template>
<template #label-address="{ item, label }">
<div class="inline-flex gap-2 items-center">
<FaIcon :name="item.icon" class="size-4" />
<span>{{ label }}</span>
</div>
</template>
<template #value-remarks="{ item, value }">
<FaTag :variant="item.tagVariant">
{{ value }}
</FaTag>
</template>
</FaDescriptions>
</template>
@@ -0,0 +1,20 @@
<script setup lang="ts">
// 组件实际使用时无需手动导入,框架会自动导入
import FaDescriptions from '../index.vue'
const items = [
{ label: '早餐', value: '苹果' },
{ label: '早午餐', value: '苹果' },
{ label: '午餐', value: '苹果' },
{ label: '晚餐', value: '苹果' },
{ label: '正餐', value: '苹果' },
{ label: '夜宵', value: '苹果' },
{ label: '早餐', value: '苹果' },
{ label: '早午餐', value: '苹果', span: 2 },
{ label: '晚餐', value: '苹果', span: 3 },
]
</script>
<template>
<FaDescriptions :items="items" :column="6" direction="vertical" border />
</template>
@@ -0,0 +1,31 @@
import Basic from './_basic.vue'
import BasicRaw from './_basic.vue?raw'
import Border from './_border.vue'
import BorderRaw from './_border.vue?raw'
import Slot from './_slot.vue'
import SlotRaw from './_slot.vue?raw'
import Vertical from './_vertical.vue'
import VerticalRaw from './_vertical.vue?raw'
export default [
{
title: '基础',
component: Basic,
componentRaw: BasicRaw,
},
{
title: '边框',
component: Border,
componentRaw: BorderRaw,
},
{
title: '纵向布局',
component: Vertical,
componentRaw: VerticalRaw,
},
{
title: '自定义插槽',
component: Slot,
componentRaw: SlotRaw,
},
]
@@ -0,0 +1,381 @@
<script lang="ts">
import type { CSSProperties, HTMLAttributes } from 'vue'
</script>
<script setup lang="ts" generic="TItem extends DescriptionItem = DescriptionItem">
import { computed } from 'vue'
import { cn } from '#utils'
export type DescriptionValue = string | number | boolean | null | undefined
export type DescriptionDirection = 'horizontal' | 'vertical'
export type DescriptionSize = 'sm' | 'default' | 'lg'
export interface DescriptionItemBase {
key?: string
label?: string
value?: DescriptionValue
span?: number
class?: HTMLAttributes['class']
labelClass?: HTMLAttributes['class']
valueClass?: HTMLAttributes['class']
}
export type DescriptionItem<
TExtra extends Record<string, any> = Record<string, any>,
> = DescriptionItemBase & Omit<TExtra, keyof DescriptionItemBase>
export interface DescriptionsProps<
TItem extends DescriptionItem = DescriptionItem,
> {
items?: TItem[]
column?: number
direction?: DescriptionDirection
border?: boolean
labelWidth?: string | number
size?: DescriptionSize
emptyText?: string
class?: HTMLAttributes['class']
labelClass?: HTMLAttributes['class']
valueClass?: HTMLAttributes['class']
}
export interface DescriptionSlotProps<
TItem extends DescriptionItem = DescriptionItem,
> {
item: TItem
index: number
label: TItem['label']
value: TItem['value']
}
type LabelSlotName = `label-${string}`
type ValueSlotName = `value-${string}`
type DescriptionsSlots<TItem extends DescriptionItem = DescriptionItem> = {
[key: LabelSlotName]: ((props: DescriptionSlotProps<TItem>) => any) | undefined
} & {
[key: ValueSlotName]: ((props: DescriptionSlotProps<TItem>) => any) | undefined
}
interface ResolvedDescriptionItem<TItem extends DescriptionItem = DescriptionItem> {
item: TItem
index: number
span: number
}
interface DescriptionRow<TItem extends DescriptionItem = DescriptionItem> {
items: ResolvedDescriptionItem<TItem>[]
span: number
rest: number
}
defineOptions({
name: 'BuiltInDescriptions',
})
const props = withDefaults(defineProps<DescriptionsProps<TItem>>(), {
column: 3,
direction: 'horizontal',
border: false,
size: 'default',
emptyText: '-',
})
defineSlots<DescriptionsSlots<TItem>>()
const cellSizeClasses: Record<DescriptionSize, string> = {
sm: 'px-3 py-2 text-xs',
default: 'px-4 py-3 text-sm',
lg: 'px-5 py-4 text-base',
}
const normalizedColumn = computed(() => normalizeColumn(props.column))
const rows = computed(() => createRows(props.items ?? [], normalizedColumn.value))
const rootClass = computed(() => cn(
'w-full overflow-hidden',
props.border && 'rounded-lg border',
props.class,
))
const tableClass = computed(() => cn(
'w-full table-fixed border-collapse',
!props.border && 'border-separate border-spacing-0',
))
const labelColStyle = computed<CSSProperties | undefined>(() => {
if (props.direction !== 'horizontal') {
return undefined
}
const width = resolveSize(props.labelWidth)
return width ? { width } : undefined
})
function normalizeColumn(column?: number) {
const resolved = Number(column)
return Number.isFinite(resolved) && resolved > 0 ? Math.floor(resolved) : 1
}
function normalizeSpan(span: number | undefined, column: number) {
const resolved = Number(span)
if (!Number.isFinite(resolved) || resolved <= 0) {
return 1
}
return Math.min(Math.floor(resolved), column)
}
function createRows(items: TItem[], column: number): DescriptionRow<TItem>[] {
const rows: DescriptionRow<TItem>[] = []
let currentItems: ResolvedDescriptionItem<TItem>[] = []
let currentSpan = 0
function pushCurrentRow() {
if (!currentItems.length) {
return
}
rows.push({
items: currentItems,
span: currentSpan,
rest: Math.max(column - currentSpan, 0),
})
currentItems = []
currentSpan = 0
}
items.forEach((item, index) => {
const span = normalizeSpan(item.span, column)
if (currentItems.length && currentSpan + span > column) {
pushCurrentRow()
}
currentItems.push({
item,
index,
span,
})
currentSpan += span
if (currentSpan >= column) {
pushCurrentRow()
}
})
pushCurrentRow()
return rows
}
function resolveSize(size?: string | number) {
if (size == null || size === '') {
return undefined
}
return typeof size === 'number' ? `${size}px` : size
}
function isEmptyValue(value: DescriptionValue) {
return value === null || value === undefined || value === ''
}
function resolveValue(value: DescriptionValue) {
return isEmptyValue(value) ? props.emptyText : value
}
function getLabelSlotName(item: TItem): LabelSlotName | undefined {
return item.key ? `label-${item.key}` : undefined
}
function getValueSlotName(item: TItem): ValueSlotName | undefined {
return item.key ? `value-${item.key}` : undefined
}
function getRowClass(isLastRow: boolean) {
return cn(props.border && !isLastRow && 'border-b')
}
function getBaseCellClass(isLastCell: boolean) {
return cn(
'align-middle break-words',
cellSizeClasses[props.size],
props.border && !isLastCell && 'border-r',
)
}
function getLabelCellClass(entry: ResolvedDescriptionItem<TItem>, isLastCell: boolean) {
return cn(
getBaseCellClass(isLastCell),
'text-left font-medium text-muted-foreground',
props.border && 'bg-muted/50',
entry.item.class,
props.labelClass,
entry.item.labelClass,
)
}
function getValueCellClass(entry: ResolvedDescriptionItem<TItem>, isLastCell: boolean) {
return cn(
getBaseCellClass(isLastCell),
'text-foreground',
entry.item.class,
props.valueClass,
entry.item.valueClass,
)
}
function getPlaceholderCellClass(isLastCell: boolean) {
return cn(
getBaseCellClass(isLastCell),
props.border && 'bg-background',
)
}
function isLastContentCell(row: DescriptionRow<TItem>, index: number) {
return index === row.items.length - 1 && row.rest === 0
}
</script>
<template>
<div v-if="rows.length" :class="rootClass" data-slot="descriptions">
<table :class="tableClass" data-slot="descriptions-table">
<colgroup v-if="direction === 'horizontal'">
<template v-for="columnIndex in normalizedColumn" :key="columnIndex">
<col :style="labelColStyle">
<col>
</template>
</colgroup>
<colgroup v-else>
<col v-for="columnIndex in normalizedColumn" :key="columnIndex">
</colgroup>
<tbody>
<template v-if="direction === 'vertical'">
<template v-for="(row, rowIndex) in rows" :key="rowIndex">
<tr :class="getRowClass(false)" data-slot="descriptions-label-row">
<th
v-for="(entry, entryIndex) in row.items"
:key="`label-${entry.index}`"
:colspan="entry.span"
:class="getLabelCellClass(entry, isLastContentCell(row, entryIndex))"
scope="col"
data-slot="descriptions-label"
>
<slot
v-if="getLabelSlotName(entry.item)"
:name="getLabelSlotName(entry.item)"
:item="entry.item"
:index="entry.index"
:label="entry.item.label"
:value="entry.item.value"
>
{{ entry.item.label }}
</slot>
<template v-else>
{{ entry.item.label }}
</template>
</th>
<td
v-if="row.rest"
:colspan="row.rest"
:class="getPlaceholderCellClass(true)"
aria-hidden="true"
data-slot="descriptions-placeholder"
/>
</tr>
<tr :class="getRowClass(rowIndex === rows.length - 1)" data-slot="descriptions-value-row">
<td
v-for="(entry, entryIndex) in row.items"
:key="`value-${entry.index}`"
:colspan="entry.span"
:class="getValueCellClass(entry, isLastContentCell(row, entryIndex))"
data-slot="descriptions-value"
>
<slot
v-if="getValueSlotName(entry.item)"
:name="getValueSlotName(entry.item)"
:item="entry.item"
:index="entry.index"
:label="entry.item.label"
:value="entry.item.value"
>
{{ resolveValue(entry.item.value) }}
</slot>
<template v-else>
{{ resolveValue(entry.item.value) }}
</template>
</td>
<td
v-if="row.rest"
:colspan="row.rest"
:class="getPlaceholderCellClass(true)"
aria-hidden="true"
data-slot="descriptions-placeholder"
/>
</tr>
</template>
</template>
<template v-else>
<tr
v-for="(row, rowIndex) in rows"
:key="rowIndex"
:class="getRowClass(rowIndex === rows.length - 1)"
data-slot="descriptions-row"
>
<template v-for="(entry, entryIndex) in row.items" :key="entry.index">
<th
:class="getLabelCellClass(entry, false)"
scope="row"
data-slot="descriptions-label"
>
<slot
v-if="getLabelSlotName(entry.item)"
:name="getLabelSlotName(entry.item)"
:item="entry.item"
:index="entry.index"
:label="entry.item.label"
:value="entry.item.value"
>
{{ entry.item.label }}
</slot>
<template v-else>
{{ entry.item.label }}
</template>
</th>
<td
:colspan="entry.span * 2 - 1"
:class="getValueCellClass(entry, isLastContentCell(row, entryIndex))"
data-slot="descriptions-value"
>
<slot
v-if="getValueSlotName(entry.item)"
:name="getValueSlotName(entry.item)"
:item="entry.item"
:index="entry.index"
:label="entry.item.label"
:value="entry.item.value"
>
{{ resolveValue(entry.item.value) }}
</slot>
<template v-else>
{{ resolveValue(entry.item.value) }}
</template>
</td>
</template>
<td
v-if="row.rest"
:colspan="row.rest * 2"
:class="getPlaceholderCellClass(true)"
aria-hidden="true"
data-slot="descriptions-placeholder"
/>
</tr>
</template>
</tbody>
</table>
</div>
</template>
+1
View File
@@ -8,6 +8,7 @@ export { default as checkboxGroup } from './basic/checkbox-group/_examples'
export { default as checkbox } from './basic/checkbox/_examples'
export { default as collapsible } from './basic/collapsible/_examples'
export { default as contextMenu } from './basic/context-menu/_examples'
export { default as descriptions } from './basic/descriptions/_examples'
export { default as divider } from './basic/divider/_examples'
export { default as drawer } from './basic/drawer/_examples'
export { default as dropdown } from './basic/dropdown/_examples'
+2
View File
@@ -9,6 +9,8 @@ export { default as FaCheckboxGroup } from './basic/checkbox-group/index.vue'
export { default as FaCheckbox } from './basic/checkbox/index.vue'
export { default as FaCollapsible } from './basic/collapsible/index.vue'
export { default as FaContextMenu } from './basic/context-menu/index.vue'
export { default as FaDescriptions } from './basic/descriptions/index.vue'
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './basic/descriptions/index.vue'
export { default as FaDivider } from './basic/divider/index.vue'
export type { DrawerEmits, DrawerProps } from './basic/drawer/index'
export { useDrawer as useFaDrawer } from './basic/drawer/index'