diff --git a/script/generate-usage/config.js b/script/generate-usage/config.js index 0cabd38c4..4e22f95d3 100644 --- a/script/generate-usage/config.js +++ b/script/generate-usage/config.js @@ -664,4 +664,17 @@ module.exports = { `, }, }, + descriptions: { + panelStr: `const panelList = [{label: 'descriptions', value: 'descriptions'}];`, + render: { + descriptions: ` + + TDesign + 139****0609 + China Tencent Headquarters + Shenzhen Penguin Island D1 4A Mail Center + + `, + }, + }, }; diff --git a/site/site.config.mjs b/site/site.config.mjs index 8a5a95ce1..c0eff9fd8 100644 --- a/site/site.config.mjs +++ b/site/site.config.mjs @@ -456,6 +456,14 @@ const docs = [ component: () => import('tdesign-vue/comment/comment.md'), componentEn: () => import('tdesign-vue/comment/comment.en-US.md'), }, + { + title: 'Descriptions 描述', + titleEn: 'Descriptions', + name: 'descriptions', + path: '/vue/components/descriptions', + component: () => import('tdesign-vue/descriptions/descriptions.md'), + componentEn: () => import('tdesign-vue/descriptions/descriptions.en-US.md'), + }, { title: 'Image 图片', titleEn: 'Image', diff --git a/src/_common b/src/_common index 8dc08b647..25b3b27eb 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 8dc08b647ccae126ef1740f9b51fad857bf25e80 +Subproject commit 25b3b27eb060bd09a06d5ec275b1c82705dc06ab diff --git a/src/components.ts b/src/components.ts index 1ad64ac30..82de5d1b4 100644 --- a/src/components.ts +++ b/src/components.ts @@ -49,6 +49,7 @@ export * from './badge'; export * from './calendar'; export * from './card'; export * from './comment'; +export * from './descriptions'; export * from './image'; export * from './image-viewer'; export * from './list'; diff --git a/src/descriptions/_example/base.vue b/src/descriptions/_example/base.vue new file mode 100644 index 000000000..da9ab37ef --- /dev/null +++ b/src/descriptions/_example/base.vue @@ -0,0 +1,10 @@ + diff --git a/src/descriptions/_example/bordered.vue b/src/descriptions/_example/bordered.vue new file mode 100644 index 000000000..e6acc9452 --- /dev/null +++ b/src/descriptions/_example/bordered.vue @@ -0,0 +1,8 @@ + diff --git a/src/descriptions/_example/colon.vue b/src/descriptions/_example/colon.vue new file mode 100644 index 000000000..2c2cd3b19 --- /dev/null +++ b/src/descriptions/_example/colon.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/descriptions/_example/column.vue b/src/descriptions/_example/column.vue new file mode 100644 index 000000000..77095a760 --- /dev/null +++ b/src/descriptions/_example/column.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/descriptions/_example/custom-style.vue b/src/descriptions/_example/custom-style.vue new file mode 100644 index 000000000..9ba507abf --- /dev/null +++ b/src/descriptions/_example/custom-style.vue @@ -0,0 +1,14 @@ + diff --git a/src/descriptions/_example/items.vue b/src/descriptions/_example/items.vue new file mode 100644 index 000000000..8f0fa2dbb --- /dev/null +++ b/src/descriptions/_example/items.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/descriptions/_example/layout.vue b/src/descriptions/_example/layout.vue new file mode 100644 index 000000000..f89feb011 --- /dev/null +++ b/src/descriptions/_example/layout.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/descriptions/_example/size.vue b/src/descriptions/_example/size.vue new file mode 100644 index 000000000..756105f17 --- /dev/null +++ b/src/descriptions/_example/size.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/descriptions/_usage/index.vue b/src/descriptions/_usage/index.vue new file mode 100644 index 000000000..6214af8ed --- /dev/null +++ b/src/descriptions/_usage/index.vue @@ -0,0 +1,32 @@ + + + + diff --git a/src/descriptions/_usage/props.json b/src/descriptions/_usage/props.json new file mode 100644 index 000000000..7399bab76 --- /dev/null +++ b/src/descriptions/_usage/props.json @@ -0,0 +1,63 @@ +[ + { + "name": "bordered", + "type": "Boolean", + "defaultValue": false, + "options": [] + }, + { + "name": "colon", + "type": "Boolean", + "defaultValue": false, + "options": [] + }, + { + "name": "size", + "type": "enum", + "defaultValue": "medium", + "options": [ + { + "label": "large", + "value": "large" + }, + { + "label": "medium", + "value": "medium" + }, + { + "label": "small", + "value": "small" + } + ] + }, + { + "name": "layout", + "type": "enum", + "defaultValue": "horizontal", + "options": [ + { + "label": "horizontal", + "value": "horizontal" + }, + { + "label": "vertical", + "value": "vertical" + } + ] + }, + { + "name": "itemLayout", + "type": "enum", + "defaultValue": "horizontal", + "options": [ + { + "label": "horizontal", + "value": "horizontal" + }, + { + "label": "vertical", + "value": "vertical" + } + ] + } +] \ No newline at end of file diff --git a/src/descriptions/const/index.ts b/src/descriptions/const/index.ts new file mode 100644 index 000000000..9a6fec8c3 --- /dev/null +++ b/src/descriptions/const/index.ts @@ -0,0 +1,6 @@ +import type { InjectionKey } from '@vue/composition-api'; +import { TdDescriptionsProps } from '../type'; + +const descriptionsKey: InjectionKey = Symbol('TDescriptions'); + +export default descriptionsKey; diff --git a/src/descriptions/description-item-props.ts b/src/descriptions/description-item-props.ts new file mode 100644 index 000000000..0ecbc8088 --- /dev/null +++ b/src/descriptions/description-item-props.ts @@ -0,0 +1,28 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdDescriptionItemProps } from '../descriptions/type'; +import { PropType } from 'vue'; + +export default { + /** 描述项内容 */ + content: { + type: [String, Function] as PropType, + }, + /** 描述项内容,同 `content` */ + default: { + type: [String, Function] as PropType, + }, + /** 描述项标签 */ + label: { + type: [String, Function] as PropType, + }, + /** 占用的宽度数量 */ + span: { + type: Number, + default: 1, + }, +}; diff --git a/src/descriptions/descriptions-body.tsx b/src/descriptions/descriptions-body.tsx new file mode 100644 index 000000000..573da88ee --- /dev/null +++ b/src/descriptions/descriptions-body.tsx @@ -0,0 +1,121 @@ +import { defineComponent, inject, PropType } from '@vue/composition-api'; + +import { LayoutEnum } from '../common'; +import { usePrefixClass, useCommonClassName } from '../hooks/useConfig'; + +import descriptionsKey from './const'; +import { ItemsType, TdDescriptionItem } from './interface'; +import { renderVNodeTNode, itemTypeIsProps } from './utils'; +import { TdDescriptionsProps } from './type'; + +export default defineComponent({ + name: 'TDescriptionsBody', + props: { + rows: Array as PropType, + itemType: String as PropType, + }, + setup() { + const descriptionsProps = inject(descriptionsKey); + const COMPONENT_NAME = usePrefixClass('descriptions'); + const { SIZE } = useCommonClassName(); + + return { + descriptionsProps, + COMPONENT_NAME, + SIZE, + }; + }, + render() { + const props = this.$props; + + const label = (node: TdDescriptionItem, layout: LayoutEnum = LayoutEnum.HORIZONTAL) => { + const labelClass = [`${this.COMPONENT_NAME}__label`]; + + let label = null; + let span = null; + if (itemTypeIsProps(props.itemType, node)) { + label = node.label; + span = node.span; + } else { + label = renderVNodeTNode(node, 'label'); + const propsData: Record = node.componentOptions.propsData || {}; + span = propsData.span; + } + const labelSpan = layout === LayoutEnum.HORIZONTAL ? 1 : span; + + return ( + + {label} + {this.descriptionsProps.colon && ':'} + + ); + }; + + const content = (node: TdDescriptionItem, layout: LayoutEnum = LayoutEnum.HORIZONTAL) => { + const contentClass = [`${this.COMPONENT_NAME}__content`]; + + let content = null; + let span = null; + if (itemTypeIsProps(props.itemType, node)) { + content = node.content; + span = node.span; + } else { + content = renderVNodeTNode(node, 'content', 'default'); + const propsData: Record = node.componentOptions.propsData || {}; + span = propsData.span; + } + const contentSpan = span > 1 && layout === LayoutEnum.HORIZONTAL ? span * 2 - 1 : span; + + return ( + + {content} + + ); + }; + + // 总共有四种布局 + // Layout horizontal vertical + // itemLayout horizontal vertical + + const hh = (row: TdDescriptionItem[]) => {row.map((node) => [label(node), content(node)])}; + + const hv = (row: TdDescriptionItem[]) => [ + {row.map((node) => label(node, LayoutEnum.VERTICAL))}, + {row.map((node) => content(node, LayoutEnum.VERTICAL))}, + ]; + + const vh = (row: TdDescriptionItem[]) => row.map((node) => ( + + {label(node)} + {content(node)} + + )); + + const vv = (row: TdDescriptionItem[]) => row.map((node) => [{label(node)}, {content(node)}]); + + const renderRow = (row: TdDescriptionItem[]) => { + if (this.descriptionsProps.layout === LayoutEnum.HORIZONTAL) { + if (this.descriptionsProps.itemLayout === LayoutEnum.HORIZONTAL) { + return hh(row); + } + return hv(row); + } + if (this.descriptionsProps.itemLayout === LayoutEnum.HORIZONTAL) { + return vh(row); + } + return vv(row); + }; + + const tableClass = [ + `${this.COMPONENT_NAME}__body`, + this.SIZE[this.descriptionsProps.size], + { [`${this.COMPONENT_NAME}__body--border`]: this.descriptionsProps.bordered }, + ]; + + return ( + + {props.rows.map((row) => renderRow(row))} +
+ ); + }, +}); diff --git a/src/descriptions/descriptions-item.tsx b/src/descriptions/descriptions-item.tsx new file mode 100644 index 000000000..b8ed8ca80 --- /dev/null +++ b/src/descriptions/descriptions-item.tsx @@ -0,0 +1,7 @@ +import { defineComponent } from '@vue/composition-api'; +import props from './description-item-props'; + +export default defineComponent({ + name: 'TDescriptionsItem', + props, +}); diff --git a/src/descriptions/descriptions.en-US.md b/src/descriptions/descriptions.en-US.md new file mode 100644 index 000000000..04bebaab0 --- /dev/null +++ b/src/descriptions/descriptions.en-US.md @@ -0,0 +1,27 @@ +:: BASE_DOC :: + +## API + +### Descriptions Props + +name | type | default | description | required +-- | -- | -- | -- | -- +bordered | Boolean | false | set description list with grey border | N +colon | Boolean | - | set label with ":" on the right | N +column | Number | 2 | count of DescriptionItem in one row | N +contentStyle | Object | - | style of description cotent。Typescript:`Styles`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +itemLayout | String | horizontal | layout direction of description item。options: horizontal/vertical | N +items | Array | - | list of descriptions items。Typescript:`Array` | N +labelStyle | Object | - | style of description item。Typescript:`Styles`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +layout | String | horizontal | layout direction。options: horizontal/vertical | N +size | String | medium | a descriptions has three size。options: small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +title | String / Slot / Function | - | title of descriptions。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N + +### DescriptionItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +content | String / Slot / Function | - | content of description item。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +default | String / Slot / Function | - | content of description item。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +label | String / Slot / Function | - | label of description item。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +span | Number | 1 | width count | N diff --git a/src/descriptions/descriptions.md b/src/descriptions/descriptions.md new file mode 100644 index 000000000..1f5df50bd --- /dev/null +++ b/src/descriptions/descriptions.md @@ -0,0 +1,27 @@ +:: BASE_DOC :: + +## API + +### Descriptions Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +bordered | Boolean | false | 是否带边框 | N +colon | Boolean | - | 字段名右侧是否携带冒号“:” | N +column | Number | 2 | 一行 `DescriptionItem` 的数量 | N +contentStyle | Object | - | 自定义描述项内容的样式。TS 类型:`Styles`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +itemLayout | String | horizontal | 描述项的排列方向。可选项:horizontal/vertical | N +items | Array | - | 描述项的列表。TS 类型:`Array` | N +labelStyle | Object | - | 自定义描述项标签的样式。TS 类型:`Styles`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +layout | String | horizontal | 排列方向。可选项:horizontal/vertical | N +size | String | medium | 组件尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +title | String / Slot / Function | - | 描述列表的标题。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N + +### DescriptionItem Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +content | String / Slot / Function | - | 描述项内容。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +default | String / Slot / Function | - | 描述项内容,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +label | String / Slot / Function | - | 描述项标签。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +span | Number | 1 | 占用的宽度数量 | N diff --git a/src/descriptions/descriptions.tsx b/src/descriptions/descriptions.tsx new file mode 100644 index 000000000..4aa38de99 --- /dev/null +++ b/src/descriptions/descriptions.tsx @@ -0,0 +1,141 @@ +import isNil from 'lodash/isNil'; +import isArray from 'lodash/isArray'; +import { defineComponent, provide, ref } from '@vue/composition-api'; + +import { LayoutEnum } from '../common'; +import { useTNodeJSX } from '../hooks/tnode'; +import { useChildComponentSlots } from '../hooks'; +import { usePrefixClass } from '../hooks/useConfig'; + +import props from './props'; +import descriptionsKey from './const'; +import { TdDescriptionsProps } from './type'; +import DescriptionsBody from './descriptions-body'; +import { renderCustomNode, itemTypeIsProps } from './utils'; +import { ItemsType, TdDescriptionItem } from './interface'; + +/** + * 实现思路 + * 1. 基于 table tbody tr td 来实现布局 + * 2. 通过 span 计算总共有几行以及每一行的 item 个数,特别注意最后一行,要填充满 + * 3. 整体布局:左右布局(column 和 span 生效)/上下布局(column 和 span 失效,一行一个 item) + * 4. item 布局:左右布局/上下布局 + */ + +/** + * TDescriptions:承载 header(title) 和 body(table, tbody) + * TDescriptionsRow:承载每一行(tr) + * TDescriptionsItem:获取 item 数据(span, label, content) + */ + +export default defineComponent({ + name: 'TDescriptions', + props, + setup(props: TdDescriptionsProps) { + const COMPONENT_NAME = usePrefixClass('descriptions'); + const getChildByName = useChildComponentSlots(); + const itemsType = ref(ItemsType.props); + const renderTNodeJSX = useTNodeJSX(); + const title = renderTNodeJSX('title'); + + // 计算渲染的行内容 + const getRows = () => { + /** + * 1. 两种方式获取要渲染的 items + * a. props 传 items + * b. slots t-descriptions-item + * a 优先级更高 + */ + const { column, layout } = props; + + let items: TdDescriptionItem[] = []; + + if (isArray(props.items)) { + /** + * 2.1 a 方式获取 items + * ! 这里要支持 label: string /
/ () =>
+ * ! 暂时没有这样一个全局的方法,所以先在组件内部写一个临时方法,无论之后是有了更好的处理方式要删除掉,还是其它组件也需要时再放到公共方法里面,都是可行的 + */ + items = props.items.map((item) => ({ + label: renderCustomNode(item.label), + content: renderCustomNode(item.content), + span: item.span || 1, + })); + itemsType.value = ItemsType.props; + } else { + const slots = getChildByName('TDescriptionsItem'); + if (slots.length !== 0) { + // 2.2 b 方式 获取 TDescriptionsItem + items = slots; + itemsType.value = ItemsType.slots; + } + } + + // 2. 判断布局,如果整体布局为 LayoutEnum.VERTICAL,那么直接返回即可。 + if (layout === LayoutEnum.VERTICAL) { + return [items]; + } + + // 3. 布局为 LayoutEnum.HORIZONTAL 时,需要计算每一行的 item 个数 + let temp: TdDescriptionItem[] = []; + let reset = column; + + // 4. 记录结果 + const res: TdDescriptionItem[][] = []; + items.forEach((item, index) => { + let span = 1; + if (itemTypeIsProps(itemsType.value, item)) { + span = isNil(item.span) ? span : item.span; + } else { + const propsData: Record = item.componentOptions.propsData || {}; + span = isNil(propsData?.span) ? span : propsData.span; + propsData.span = span; + Object.assign(item.componentOptions, { ...item.componentOptions, propsData }); + } + + if (reset >= span) { + // 当前行还剩余空间 + temp.push(item); + reset -= span; + } else { + // 当前行放不下了,放下一行 + res.push(temp); + temp = [item]; + reset = column - span; + } + + if (index === items.length - 1) { + // 最后一个 + if (itemTypeIsProps(itemsType.value, item)) { + Object.assign(item, { span: item.span + reset }); + } else { + const propsData: Record = item.componentOptions.propsData || {}; + propsData.span += reset; + Object.assign(item.componentOptions, { ...item.componentOptions, propsData }); + } + res.push(temp); + } + }); + return res; + }; + + provide(descriptionsKey, props); + + return { + title, + getRows, + itemsType, + COMPONENT_NAME, + }; + }, + render() { + const renderHeader = () => (this.title ?
{this.title}
: ''); + + return ( +
+ {renderHeader()} + +
+ ); + }, +}); diff --git a/src/descriptions/index.ts b/src/descriptions/index.ts new file mode 100644 index 000000000..ea82645f7 --- /dev/null +++ b/src/descriptions/index.ts @@ -0,0 +1,14 @@ +import _Descriptions from './descriptions'; +import _DescriptionsItem from './descriptions-item'; +import withInstall from '../utils/withInstall'; +import { TdDescriptionsProps, TdDescriptionItemProps } from './type'; + +import './style'; + +export * from './type'; +export type DescriptionsProps = TdDescriptionsProps; +export type DescriptionsItemProps = TdDescriptionItemProps; + +export const Descriptions = withInstall(_Descriptions); +export const DescriptionsItem = withInstall(_DescriptionsItem); +export default Descriptions; diff --git a/src/descriptions/interface.ts b/src/descriptions/interface.ts new file mode 100644 index 000000000..2a04d32ac --- /dev/null +++ b/src/descriptions/interface.ts @@ -0,0 +1,9 @@ +import type { VNode } from 'vue'; +import { TdDescriptionItemProps } from './type'; + +export enum ItemsType { + props = 'props', + slots = 'slots', +} + +export type TdDescriptionItem = TdDescriptionItemProps | VNode; diff --git a/src/descriptions/props.ts b/src/descriptions/props.ts new file mode 100644 index 000000000..23af8624e --- /dev/null +++ b/src/descriptions/props.ts @@ -0,0 +1,63 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdDescriptionsProps } from './type'; +import { PropType } from 'vue'; + +export default { + /** 是否带边框 */ + bordered: Boolean, + /** 字段名右侧是否携带冒号“:” */ + colon: Boolean, + /** 一行 `DescriptionItem` 的数量 */ + column: { + type: Number, + default: 2, + }, + /** 自定义描述项内容的样式 */ + contentStyle: { + type: Object as PropType, + }, + /** 描述项的排列方向 */ + itemLayout: { + type: String as PropType, + default: 'horizontal' as TdDescriptionsProps['itemLayout'], + validator(val: TdDescriptionsProps['itemLayout']): boolean { + if (!val) return true; + return ['horizontal', 'vertical'].includes(val); + }, + }, + /** 描述项的列表 */ + items: { + type: Array as PropType, + }, + /** 自定义描述项标签的样式 */ + labelStyle: { + type: Object as PropType, + }, + /** 排列方向 */ + layout: { + type: String as PropType, + default: 'horizontal' as TdDescriptionsProps['layout'], + validator(val: TdDescriptionsProps['layout']): boolean { + if (!val) return true; + return ['horizontal', 'vertical'].includes(val); + }, + }, + /** 组件尺寸 */ + size: { + type: String as PropType, + default: 'medium' as TdDescriptionsProps['size'], + validator(val: TdDescriptionsProps['size']): boolean { + if (!val) return true; + return ['small', 'medium', 'large'].includes(val); + }, + }, + /** 描述列表的标题 */ + title: { + type: [String, Function] as PropType, + }, +}; diff --git a/src/descriptions/style/css.js b/src/descriptions/style/css.js new file mode 100644 index 000000000..6a9a4b132 --- /dev/null +++ b/src/descriptions/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/descriptions/style/index.js b/src/descriptions/style/index.js new file mode 100644 index 000000000..f590f64d4 --- /dev/null +++ b/src/descriptions/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/web/components/descriptions/_index.less'; diff --git a/src/descriptions/type.ts b/src/descriptions/type.ts new file mode 100644 index 000000000..9190783dc --- /dev/null +++ b/src/descriptions/type.ts @@ -0,0 +1,75 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TNode, SizeEnum, Styles } from '../common'; + +export interface TdDescriptionsProps { + /** + * 是否带边框 + * @default false + */ + bordered?: boolean; + /** + * 字段名右侧是否携带冒号“:” + */ + colon?: boolean; + /** + * 一行 `DescriptionItem` 的数量 + * @default 2 + */ + column?: number; + /** + * 自定义描述项内容的样式 + */ + contentStyle?: Styles; + /** + * 描述项的排列方向 + * @default horizontal + */ + itemLayout?: 'horizontal' | 'vertical'; + /** + * 描述项的列表 + */ + items?: Array; + /** + * 自定义描述项标签的样式 + */ + labelStyle?: Styles; + /** + * 排列方向 + * @default horizontal + */ + layout?: 'horizontal' | 'vertical'; + /** + * 组件尺寸 + * @default medium + */ + size?: SizeEnum; + /** + * 描述列表的标题 + */ + title?: string | TNode; +} + +export interface TdDescriptionItemProps { + /** + * 描述项内容 + */ + content?: string | TNode; + /** + * 描述项内容,同 `content` + */ + default?: string | TNode; + /** + * 描述项标签 + */ + label?: string | TNode; + /** + * 占用的宽度数量 + * @default 1 + */ + span?: number; +} diff --git a/src/descriptions/utils/index.tsx b/src/descriptions/utils/index.tsx new file mode 100644 index 000000000..0d3aafe9e --- /dev/null +++ b/src/descriptions/utils/index.tsx @@ -0,0 +1,57 @@ +import { h, ComponentInternalInstance } from '@vue/composition-api'; +import { VNode } from 'vue'; +import isFunction from 'lodash/isFunction'; +import isString from 'lodash/isString'; + +import { ItemsType, TdDescriptionItem } from '../interface'; +import { TdDescriptionItemProps } from '../type'; + +/** + * ! 处理 node string /
/ () =>
/ Component + * [ + * { key: 'string /
/ () =>
/ Component' } + * ] + * @param node + * @param params + * @returns + */ +export function renderCustomNode(node: string | ((...args: any[]) => any) | ComponentInternalInstance, params = {}) { + if (isString(node)) { + return node; + } + if (isFunction(node)) { + return node(h, params); + } + if (isFunction(node.render)) { + return ; + } + + return node; +} + +/** + * ! 处理 VNode 中的 slot prop,同时存在时,props 优先级更高 + * @param node VNode + * @param name1 props 名称,slot 名称应与其一致 + * @param name2 slot 别名 + * @returns + */ +export function renderVNodeTNode(node: VNode | ComponentInternalInstance, name1: string, name2?: string) { + const prop = node.componentOptions.propsData?.[name1]; + if (prop) return prop; + + const slot = node.data.scopedSlots?.[name1]?.() || node.data.scopedSlots?.[name2]?.(); + if (slot) return slot; + + return node.componentOptions.children; +} + +/** + * 判断 item 当前类型 + * @param itemsType + * @param item + * @returns + */ +export function itemTypeIsProps(itemsType: ItemsType, item: TdDescriptionItem): item is TdDescriptionItemProps { + return itemsType === ItemsType.props; +} diff --git a/test/snap/__snapshots__/csr.test.js.snap b/test/snap/__snapshots__/csr.test.js.snap index 123b19639..7013f2f3f 100644 --- a/test/snap/__snapshots__/csr.test.js.snap +++ b/test/snap/__snapshots__/csr.test.js.snap @@ -43537,6 +43537,751 @@ exports[`csr snapshot test > csr test ./src/date-picker/_example/year.vue 1`] =
`; +exports[`csr snapshot test > csr test ./src/descriptions/_example/base.vue 1`] = ` +
+
+ Shipping address +
+ + + + + + + +
+ + + +
+ + + +
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/bordered.vue 1`] = ` +
+
+ Shipping address +
+ + + + + + + +
+ + + + + +
+ +
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/colon.vue 1`] = ` +
+
+ +
+
+
+
+ Shipping address +
+ + + + + + + +
+ + + + + +
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/column.vue 1`] = ` +
+
+
+ + + +
+
+
+
+ + + + + + + +
+ + + +
+ + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/custom-style.vue 1`] = ` +
+
+ Shipping address +
+ + + + + + + +
+ + + + + +
+ +
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/items.vue 1`] = ` +
+
+ Shipping address +
+ + + + + + + + + +
+ Name + + TDesign + +
+
+ Shipping address +
+ + + + + + + +
+ + + +
+ + + +
+
+
+ 139****0609 +
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/layout.vue 1`] = ` +
+
+
+ + layout: + +
+ + +
+
+
+
+
+ + itemLayout: + +
+ + +
+
+
+
+
+
+ Shipping address +
+ + + + + + + +
+ + + + + +
+ +
+
+
+
+`; + +exports[`csr snapshot test > csr test ./src/descriptions/_example/size.vue 1`] = ` +
+
+
+ + + +
+
+
+
+ + + + + + + +
+ + + + + +
+ +
+
+
+
+`; + exports[`csr snapshot test > csr test ./src/dialog/_example/async.vue 1`] = `
renders ./src/date-picker/_example/week.vue correct exports[`ssr snapshot test > renders ./src/date-picker/_example/year.vue correctly 1`] = `"
-
"`; +exports[`ssr snapshot test > renders ./src/descriptions/_example/base.vue correctly 1`] = `"
Shipping address
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/bordered.vue correctly 1`] = `"
Shipping address
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/colon.vue correctly 1`] = `"
Shipping address
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/column.vue correctly 1`] = `"
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/custom-style.vue correctly 1`] = `"
Shipping address
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/items.vue correctly 1`] = `"
Shipping address
NameTDesign
Shipping address
139****0609
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/layout.vue correctly 1`] = `"
layout:
itemLayout:
Shipping address
"`; + +exports[`ssr snapshot test > renders ./src/descriptions/_example/size.vue correctly 1`] = `"
"`; + exports[`ssr snapshot test > renders ./src/dialog/_example/async.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/dialog/_example/attach.vue correctly 1`] = `"
"`;