import type { PropType, VNode } from 'vue' import { defineComponent } from 'vue' import { getPrefixCls } from '../_utils/getPrefixCls' import { getComponentInnerData } from '../_utils/vnode' import Descriptions from './descriptions' interface DescriptionsData { label: string | VNode | (() => string | VNode) content: string | VNode | (() => string | VNode) span?: number } export default defineComponent({ name: 'DescriptionsGroup', props: { data: { type: Array as PropType, default: () => [], }, header: { type: [String, Object, Function] as PropType VNode)>, }, size: { type: String as PropType<'small' | 'medium' | 'large'>, default: 'medium', }, tableLayout: { type: String as PropType<'fixed' | 'auto'>, default: 'fixed', }, layout: { type: String as PropType<'row' | 'row-vertical' | 'column' | 'column-vertical'>, default: 'row', }, columns: { type: Number, default: 3, }, border: { type: Boolean, default: true, }, colon: { type: Boolean, default: false, }, labelClass: { type: [String, Array, Object], }, labelStyle: { type: Object, default: () => ({}), }, contentClass: { type: [String, Array, Object], }, contentStyle: { type: Object, default: () => ({}), }, class: { type: [String, Array, Object], default: '', }, style: { type: Object, default: () => ({}), }, }, emits: [], setup(props, { slots }) { const prefixCls = getPrefixCls('descriptions') const renderLabel = (item: DescriptionsData) => { if (typeof item.label === 'function') return item.label() return item.label } const renderContent = (item: DescriptionsData) => { if (typeof item.content === 'function') return item.content() return item.content } const getRows = () => { const dataList: DescriptionsData[] = [] // 1️⃣ 构建基础数据列表 if (props.data && props.data.length) { props.data.forEach((item) => { dataList.push({ label: item.label, content: item.content, span: item.span ?? 1, // 默认 span = 1 }) }) } else if (slots.default) { const childrenData = getComponentInnerData(slots.default(), Descriptions) childrenData.forEach(({ item, slots }) => { dataList.push({ label: slots.label ? slots.label() : item.label, content: slots.content ? slots.content() : slots.default ? slots.default() : item.content, span: item.span ?? 1, }) }) } // 2️⃣ 如果是列布局,直接一行返回 if (props.layout === 'column' || props.layout === 'column-vertical') { return [dataList] } // 3️⃣ Row 布局按列分行 const res: DescriptionsData[][] = [] let temp: DescriptionsData[] = [] let currentSpan = 0 const addRow = () => { if (temp.length) { // 填充剩余列到最后一个 item const restSpan = props.columns - currentSpan if (restSpan > 0) { const lastItem = temp[temp.length - 1] lastItem.span = (lastItem.span ?? 1) + restSpan } res.push(temp) } } dataList.forEach((item) => { const span = Math.min(item.span ?? 1, props.columns) if (currentSpan + span > props.columns) { addRow() temp = [] currentSpan = 0 } temp.push({ ...item, span }) currentSpan += span }) // 最后一行 addRow() return res } const label = (node: DescriptionsData) => { const labelClass = [`${prefixCls}-label`] let colspan = 1 // 水平布局下 if (props.layout === 'row') { colspan = 1 } else if (props.layout === 'row-vertical') { colspan = node.span ?? 1 } return ( {renderLabel(node)} {props.colon ? ':' : null} ) } const content = (node: DescriptionsData) => { const contentClass = [`${prefixCls}-content`] let colspan = 1 if (props.layout === 'row') { const span = node.span ?? 1 colspan = span * 2 - 1 } else if (props.layout === 'row-vertical') { colspan = node.span ?? 1 } return ( {renderContent(node)} ) } const row = (data: DescriptionsData[]) => { return ( { data.map(node => ( <> {label(node)} {content(node)} )) } ) } const rowVertical = (data: DescriptionsData[]) => { return ( <> {data.map(node => label(node))} {data.map(node => content(node))} ) } const column = (data: DescriptionsData[]) => { return ( <> {data.map(node => ( {label(node)} {content(node)} ))} ) } const columnVertical = (data: DescriptionsData[]) => { return ( <> {data.map(node => ( <> {label(node)} {content(node)} ))} ) } const renderHeader = () => { const header = slots.header ? slots.header() : typeof props.header === 'function' ? props.header() : props.header return
{header}
} const renderBody = () => { return ( {getRows().map((item) => { if (props.layout === 'row') { return row(item) } else if (props.layout === 'row-vertical') { return rowVertical(item) } else if (props.layout === 'column') { return column(item) } else if (props.layout === 'column-vertical') { return columnVertical(item) } // 默认 fallback return row(item) })}
) } return () => (
{renderHeader()} {renderBody()}
) }, })