diff --git a/site/site.config.mjs b/site/site.config.mjs index c0eff9fd8..9f395e45a 100644 --- a/site/site.config.mjs +++ b/site/site.config.mjs @@ -464,6 +464,14 @@ const docs = [ component: () => import('tdesign-vue/descriptions/descriptions.md'), componentEn: () => import('tdesign-vue/descriptions/descriptions.en-US.md'), }, + { + title: 'Empty 空状态', + titleEn: 'Empty', + name: 'empty', + path: '/vue/components/empty', + component: () => import('tdesign-vue/empty/empty.md'), + componentEn: () => import('tdesign-vue/empty/empty.en-US.md'), + }, { title: 'Image 图片', titleEn: 'Image', diff --git a/site/test-coverage.js b/site/test-coverage.js index 94c4b7346..5ed2113f1 100644 --- a/site/test-coverage.js +++ b/site/test-coverage.js @@ -1,166 +1,185 @@ module.exports = { - '': '100%', - 'common/js/colorPicker': '63.51%', - 'common/js/datePicker': '67.47%', + '': '97.95%', + 'common/js': '87.23%', + 'common/js/colorPicker': '60.93%', + 'common/js/datePicker': '62.36%', 'common/js/globalConfig': '100%', 'common/js/globalConfig/locale': '100%', - 'common/js/inputNumber': '40.48%', - 'common/js/loading': '73.17%', + 'common/js/inputNumber': '34.52%', + 'common/js/loading': '65.85%', 'common/js/log': '72.72%', - 'common/js/table': '15.37%', - 'common/js/timePicker': '71.69%', - 'common/js/tree': '93.33%', - 'common/js/upload': '39.94%', - 'common/js/utils': '48.46%', - 'common/js/watermark': '6.97%', - affix: '89.47%', + 'common/js/slider': '24.07%', + 'common/js/statistic': '93.9%', + 'common/js/table': '19.27%', + 'common/js/tabs': '71.79%', + 'common/js/timePicker': '64.21%', + 'common/js/tree': '93.82%', + 'common/js/upload': '81.51%', + 'common/js/utils': '53.72%', + 'common/js/watermark': '100%', + affix: '87.44%', 'affix/style': '100%', - alert: '96.21%', + alert: '97.94%', 'alert/style': '100%', - anchor: '95.2%', + anchor: '94.04%', 'anchor/style': '100%', - autoComplete: '99.85%', + autoComplete: '97.62%', 'autoComplete/style': '100%', - avatar: '96.85%', + avatar: '95.88%', 'avatar/style': '100%', - badge: '100%', + backTop: '87.76%', + 'backTop/style': '100%', + badge: '98.05%', 'badge/style': '100%', - breadcrumb: '97.52%', + breadcrumb: '96.7%', 'breadcrumb/style': '100%', button: '100%', 'button/style': '100%', - calendar: '95.15%', + calendar: '86.17%', 'calendar/style': '100%', - card: '99.71%', + card: '96.07%', 'card/style': '100%', - cascader: '95.83%', - 'cascader/components': '84.49%', - 'cascader/core': '72.46%', + cascader: '91.18%', + 'cascader/components': '79.07%', + 'cascader/core': '65.84%', 'cascader/style': '100%', - checkbox: '92.16%', + checkbox: '93.93%', + 'checkbox/hooks': '44.44%', 'checkbox/style': '100%', - collapse: '94.73%', + collapse: '100%', 'collapse/style': '100%', - colorPicker: '97.08%', - 'colorPicker/panel': '64.9%', - 'colorPicker/panel/format': '93.59%', + colorPicker: '94.54%', + 'colorPicker/panel': '63.42%', + 'colorPicker/panel/format': '87.25%', 'colorPicker/style': '100%', 'colorPicker/utils': '100%', - comment: '95.5%', + comment: '83.14%', 'comment/style': '100%', commonComponents: '100%', - configProvider: '96.08%', - datePicker: '47.63%', - 'datePicker/base': '80.6%', - 'datePicker/hooks': '46.2%', - 'datePicker/panel': '64.04%', + configProvider: '95.5%', + datePicker: '49.66%', + 'datePicker/base': '77.09%', + 'datePicker/hooks': '86.17%', + 'datePicker/panel': '81.78%', 'datePicker/style': '100%', - dialog: '84.4%', + descriptions: '74.5%', + 'descriptions/const': '100%', + 'descriptions/style': '100%', + 'descriptions/utils': '54.38%', + dialog: '81.8%', 'dialog/style': '100%', - divider: '96.92%', + divider: '98.46%', 'divider/style': '100%', - drawer: '85.13%', + drawer: '83.92%', 'drawer/style': '100%', - dropdown: '90.84%', - 'dropdown/hooks': '96.05%', + dropdown: '86.53%', + 'dropdown/hooks': '78.94%', 'dropdown/style': '100%', - form: '92.6%', + empty: '96.41%', + 'empty/assets': '100%', + 'empty/style': '100%', + form: '87.04%', 'form/style': '100%', - grid: '84.13%', + grid: '82.25%', 'grid/style': '100%', - guide: '42.89%', + guide: '97.46%', 'guide/style': '100%', - 'guide/utils': '79.62%', - hooks: '57.56%', + 'guide/utils': '80.18%', + hooks: '55.24%', icon: '100%', - image: '98.82%', - imageViewer: '87.44%', - 'imageViewer/base': '95.37%', + image: '94.43%', + imageViewer: '83.62%', + 'imageViewer/base': '92.65%', 'imageViewer/style': '100%', 'image/style': '100%', - input: '99.48%', - inputAdornment: '100%', + input: '97.37%', + inputAdornment: '91.91%', 'inputAdornment/style': '100%', - inputNumber: '94.13%', + inputNumber: '92.53%', 'inputNumber/style': '100%', 'input/style': '100%', - layout: '96.31%', + layout: '95.9%', 'layout/style': '100%', link: '100%', 'link/style': '100%', - list: '98.4%', + list: '97.42%', + 'list/hooks': '81.17%', 'list/style': '100%', - loading: '84.17%', - 'loading/icon': '92.68%', + loading: '78.54%', + 'loading/icon': '95.12%', 'loading/style': '100%', - menu: '85.84%', + menu: '75.52%', 'menu/style': '100%', - message: '88.84%', + message: '87.29%', 'message/style': '100%', - notification: '96.83%', + notification: '96.04%', 'notification/style': '100%', - pagination: '99.12%', + pagination: '99.01%', 'pagination/style': '100%', - popconfirm: '96.67%', + popconfirm: '95.34%', 'popconfirm/style': '100%', - popup: '90.29%', + popup: '73.99%', 'popup/style': '100%', - progress: '95.96%', + progress: '94.2%', 'progress/style': '100%', - radio: '95.8%', + radio: '94.67%', 'radio/style': '100%', - rangeInput: '82.93%', + rangeInput: '88.29%', 'rangeInput/style': '100%', - rate: '90.03%', + rate: '87.41%', 'rate/style': '100%', - select: '62.1%', - selectInput: '95.33%', + select: '73.53%', + selectInput: '98.23%', 'selectInput/style': '100%', - 'select/hooks': '81.75%', + 'select/hooks': '79.33%', 'select/style': '100%', - skeleton: '94.14%', + skeleton: '93.75%', 'skeleton/style': '100%', - slider: '72.48%', + slider: '69.74%', 'slider/style': '100%', - space: '100%', + space: '97.07%', 'space/style': '100%', - steps: '98.44%', + statistic: '87.17%', + 'statistic/style': '100%', + steps: '96.92%', 'steps/style': '100%', - swiper: '76.68%', + stickyTool: '65.78%', + 'stickyTool/style': '100%', + swiper: '76.67%', 'swiper/style': '100%', - switch: '61.35%', + switch: '69.32%', 'switch/style': '100%', - table: '77.36%', - 'table/hooks': '58.16%', + table: '79.48%', + 'table/hooks': '49.97%', 'table/style': '100%', - tabs: '85.01%', + tabs: '92.85%', 'tabs/style': '100%', - tag: '97.78%', - tagInput: '90.53%', - 'tagInput/hooks': '52.13%', + tag: '85.9%', + tagInput: '96.06%', + 'tagInput/hooks': '55.91%', 'tagInput/style': '100%', 'tag/style': '100%', - textarea: '78.57%', + textarea: '86.18%', 'textarea/style': '100%', - timePicker: '77.38%', - 'timePicker/panel': '79.39%', + timePicker: '82.35%', + 'timePicker/panel': '72.46%', 'timePicker/style': '100%', - timeline: '51.2%', + timeline: '98.83%', 'timeline/style': '100%', - tooltip: '92.95%', + tooltip: '89.25%', 'tooltip/style': '100%', - transfer: '96.5%', - 'transfer/components': '92.06%', + transfer: '95.11%', + 'transfer/components': '75.32%', 'transfer/style': '100%', - tree: '96.46%', - treeSelect: '90.93%', + tree: '96.59%', + treeSelect: '96.91%', 'treeSelect/style': '100%', - 'tree/hooks': '80.88%', + 'tree/hooks': '77.79%', 'tree/style': '100%', - upload: '90.38%', - 'upload/hooks': '42.94%', + upload: '100%', + 'upload/hooks': '96.1%', 'upload/style': '100%', - 'upload/themes': '32.45%', - utils: '70.8%', - watermark: '40.19%', + 'upload/themes': '92.9%', + utils: '69.44%', + watermark: '49.01%', }; diff --git a/src/components.ts b/src/components.ts index 82de5d1b4..ca2ae4b83 100644 --- a/src/components.ts +++ b/src/components.ts @@ -50,6 +50,7 @@ export * from './calendar'; export * from './card'; export * from './comment'; export * from './descriptions'; +export * from './empty'; export * from './image'; export * from './image-viewer'; export * from './list'; diff --git a/src/config-provider/type.ts b/src/config-provider/type.ts index 58750f220..b7fa269c1 100644 --- a/src/config-provider/type.ts +++ b/src/config-provider/type.ts @@ -58,6 +58,10 @@ export interface GlobalConfigProvider { * 抽屉全局配置 */ drawer?: DrawerConfig; + /** + * 空状态全局配置 + */ + empty?: EmptyConfig; /** * 表单组件全局配置 */ @@ -919,6 +923,17 @@ export interface RateConfig { rateText?: string[]; } +export interface EmptyConfig { + /** + * 空状态组件各类型的图片配置 + */ + image?: { maintenance: TNode; success: TNode; fail: TNode; empty: TNode; networkError: TNode }; + /** + * 空状态组件各类型的标题文本配置 + */ + titleText?: { maintenance: string; success: string; fail: string; empty: string; networkError: string }; +} + export type AnimationType = 'ripple' | 'expand' | 'fade'; export type IconConfig = GlobalIconConfig; diff --git a/src/empty/__tests__/index.test.jsx b/src/empty/__tests__/index.test.jsx new file mode 100644 index 000000000..73e3e1017 --- /dev/null +++ b/src/empty/__tests__/index.test.jsx @@ -0,0 +1,43 @@ +import { mount } from '@vue/test-utils'; +import Empty from '@/src/empty/index.ts'; + +// every component needs four parts: props/events/slots/functions. +describe('Empty', () => { + // test props api + describe(':props', () => { + it('size', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.find('.t-empty.t-size-s')).not.toBeNull(); + }); + it('title', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.find('.t-empty__title').element.innerHTML).toBe('title'); + }); + it('description', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.find('.t-empty__description').element.innerHTML).toBe('description'); + }); + it('type', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + const successIconPath = 'M24 42C33.9411 42 42 33.9411 42 24C42 14.0589 33.9411 6 24 6C14.0589 6 6 14.0589 6 24C6 33.9411 14.0589 42 24 42ZM46 24C46 36.1503 36.1503 46 24 46C11.8497 46 2 36.1503 2 24C2 11.8497 11.8497 2 24 2C36.1503 2 46 11.8497 46 24ZM21 32.8284L12.1716 24L15 21.1716L21 27.1716L33 15.1716L35.8284 18L21 32.8284Z'; + + expect(wrapper.find('.t-empty__image').find('path').attributes('d')).toBe(successIconPath); + }); + }); +}); diff --git a/src/empty/_example-composition/base.vue b/src/empty/_example-composition/base.vue new file mode 100644 index 000000000..e1627e6a0 --- /dev/null +++ b/src/empty/_example-composition/base.vue @@ -0,0 +1,3 @@ + diff --git a/src/empty/_example-composition/descriptions.vue b/src/empty/_example-composition/descriptions.vue new file mode 100644 index 000000000..1f2d49712 --- /dev/null +++ b/src/empty/_example-composition/descriptions.vue @@ -0,0 +1,9 @@ + + diff --git a/src/empty/_example-composition/operation.vue b/src/empty/_example-composition/operation.vue new file mode 100644 index 000000000..0fcf328f9 --- /dev/null +++ b/src/empty/_example-composition/operation.vue @@ -0,0 +1,13 @@ + + diff --git a/src/empty/_example-composition/self-defined.vue b/src/empty/_example-composition/self-defined.vue new file mode 100644 index 000000000..5f9cf4720 --- /dev/null +++ b/src/empty/_example-composition/self-defined.vue @@ -0,0 +1,28 @@ + + diff --git a/src/empty/_example-composition/size.vue b/src/empty/_example-composition/size.vue new file mode 100644 index 000000000..d3ce46108 --- /dev/null +++ b/src/empty/_example-composition/size.vue @@ -0,0 +1,37 @@ + + diff --git a/src/empty/_example-composition/status.vue b/src/empty/_example-composition/status.vue new file mode 100644 index 000000000..afa596a7b --- /dev/null +++ b/src/empty/_example-composition/status.vue @@ -0,0 +1,19 @@ + diff --git a/src/empty/_example/base.vue b/src/empty/_example/base.vue new file mode 100644 index 000000000..e1627e6a0 --- /dev/null +++ b/src/empty/_example/base.vue @@ -0,0 +1,3 @@ + diff --git a/src/empty/_example/descriptions.vue b/src/empty/_example/descriptions.vue new file mode 100644 index 000000000..4d81ed1ab --- /dev/null +++ b/src/empty/_example/descriptions.vue @@ -0,0 +1,19 @@ + + diff --git a/src/empty/_example/operation.vue b/src/empty/_example/operation.vue new file mode 100644 index 000000000..49d40f493 --- /dev/null +++ b/src/empty/_example/operation.vue @@ -0,0 +1,20 @@ + + diff --git a/src/empty/_example/self-defined.vue b/src/empty/_example/self-defined.vue new file mode 100644 index 000000000..c61c17083 --- /dev/null +++ b/src/empty/_example/self-defined.vue @@ -0,0 +1,34 @@ + + diff --git a/src/empty/_example/size.vue b/src/empty/_example/size.vue new file mode 100644 index 000000000..a1ed74b39 --- /dev/null +++ b/src/empty/_example/size.vue @@ -0,0 +1,44 @@ + + diff --git a/src/empty/_example/status.vue b/src/empty/_example/status.vue new file mode 100644 index 000000000..6a873bdce --- /dev/null +++ b/src/empty/_example/status.vue @@ -0,0 +1,27 @@ + + diff --git a/src/empty/assets/EmptySvg.tsx b/src/empty/assets/EmptySvg.tsx new file mode 100644 index 000000000..9ee8e9ec3 --- /dev/null +++ b/src/empty/assets/EmptySvg.tsx @@ -0,0 +1,30 @@ +import { defineComponent } from '@vue/composition-api'; + +export default defineComponent({ + name: 'EmptySvg', + render() { + return ( + + + + + + + + + + + ); + }, +}); diff --git a/src/empty/assets/FailSvg.tsx b/src/empty/assets/FailSvg.tsx new file mode 100644 index 000000000..6e9ecd844 --- /dev/null +++ b/src/empty/assets/FailSvg.tsx @@ -0,0 +1,17 @@ +import { defineComponent } from '@vue/composition-api'; + +export default defineComponent({ + name: 'FailSvg', + render() { + return ( + + + + ); + }, +}); diff --git a/src/empty/assets/MaintenanceSvg.tsx b/src/empty/assets/MaintenanceSvg.tsx new file mode 100644 index 000000000..188c9031d --- /dev/null +++ b/src/empty/assets/MaintenanceSvg.tsx @@ -0,0 +1,21 @@ +import { defineComponent } from '@vue/composition-api'; + +export default defineComponent({ + name: 'MaintenanceSvg', + render() { + return ( + + + + + + + + + + ); + }, +}); diff --git a/src/empty/assets/NetworkErrorSvg.tsx b/src/empty/assets/NetworkErrorSvg.tsx new file mode 100644 index 000000000..341b53c0c --- /dev/null +++ b/src/empty/assets/NetworkErrorSvg.tsx @@ -0,0 +1,21 @@ +import { defineComponent } from '@vue/composition-api'; + +export default defineComponent({ + name: 'NetworkErrorSvg', + render() { + return ( + + + + + + + + + + ); + }, +}); diff --git a/src/empty/assets/SuccessSvg.tsx b/src/empty/assets/SuccessSvg.tsx new file mode 100644 index 000000000..53edc5031 --- /dev/null +++ b/src/empty/assets/SuccessSvg.tsx @@ -0,0 +1,17 @@ +import { defineComponent } from '@vue/composition-api'; + +export default defineComponent({ + name: 'SuccessSvg', + render() { + return ( + + + + ); + }, +}); diff --git a/src/empty/empty.en-US.md b/src/empty/empty.en-US.md new file mode 100644 index 000000000..f09773368 --- /dev/null +++ b/src/empty/empty.en-US.md @@ -0,0 +1,15 @@ +:: BASE_DOC :: + +## API + +### Empty Props + +name | type | default | description | required +-- | -- | -- | -- | -- +action | Slot / Function | - | action block。Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +description | String / Slot / Function | - | empty component description。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +image | String / Slot / Function | - | image url, or Image component props, or custom any node you need.。Typescript:`string \| ImageProps \| TNode `,[Image API Documents](./image?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/empty/type.ts) | N +imageStyle | Object | - | pass `Cascading Style Sheets` to image element。Typescript:`Styles`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +size | String | medium | size of Empty, default value is `medium`。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 | - | empty component title。Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +type | String | empty | Empty component type。options: empty/success/fail/network-error/maintenance | N diff --git a/src/empty/empty.md b/src/empty/empty.md new file mode 100644 index 000000000..98b843a09 --- /dev/null +++ b/src/empty/empty.md @@ -0,0 +1,15 @@ +:: BASE_DOC :: + +## API + +### Empty Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +action | Slot / Function | - | 操作按钮。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +description | String / Slot / Function | - | 描述文字。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +image | String / Slot / Function | - | 组件图片,可以完全自定义内容。值类型为字符串时,表示图片地址;值类型为对象时,则表示透传全部属性到图片组件。TS 类型:`string \| ImageProps \| TNode `,[Image API Documents](./image?tab=api)。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/empty/type.ts) | N +imageStyle | Object | - | 透传图片样式表。TS 类型:`Styles`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +size | String | medium | 空状态的尺寸,默认为 `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 +type | String | empty | 组件类型,如:空数据/成功/失败/网络错误/建设中。可选项:empty/success/fail/network-error/maintenance | N diff --git a/src/empty/empty.tsx b/src/empty/empty.tsx new file mode 100644 index 000000000..a6f5bcf59 --- /dev/null +++ b/src/empty/empty.tsx @@ -0,0 +1,123 @@ +import { + computed, defineComponent, h, toRefs, +} from '@vue/composition-api'; +import isPlainObject from 'lodash/isPlainObject'; +import isString from 'lodash/isString'; +import type { TNode } from '@src/common'; +import { useConfig, usePrefixClass } from '../config-provider/useConfig'; +import { useCommonClassName } from '../hooks/useConfig'; + +import props from './props'; +import Image from '../image'; +import { renderTNodeJSX } from '../utils/render-tnode'; +import MaintenanceSvg from './assets/MaintenanceSvg'; +import NetworkErrorSvg from './assets/NetworkErrorSvg'; +import EmptySvg from './assets/EmptySvg'; +import FailSvg from './assets/FailSvg'; +import SuccessSvg from './assets/SuccessSvg'; + +import type { TdEmptyProps } from './type'; + +export default defineComponent({ + name: 'TEmpty', + components: { TImage: Image }, + props, + setup(props: TdEmptyProps, { slots }) { + const { + size, image: propsImage, description: propsDescription, title: propsTitle, type, + } = toRefs(props); + const { globalConfig } = useConfig('empty'); + const componentName = usePrefixClass('empty'); + const showAction = computed(() => props.action || slots.action); + const { SIZE } = useCommonClassName(); + + const defaultMaps: { + [key in TdEmptyProps['type']]?: Pick; + } = { + maintenance: { + image: globalConfig.value.image.maintenance || (MaintenanceSvg as unknown as TNode), + title: globalConfig.value.titleText.maintenance, + }, + success: { + image: globalConfig.value.image.success || (SuccessSvg as unknown as TNode), + title: globalConfig.value.titleText.success, + }, + fail: { + image: globalConfig.value.image.fail || (FailSvg as unknown as TNode), + title: globalConfig.value.titleText.fail, + }, + 'network-error': { + image: globalConfig.value.image.networkError || (NetworkErrorSvg as unknown as TNode), + title: globalConfig.value.titleText.networkError, + }, + empty: { + image: globalConfig.value.image.empty || (EmptySvg as unknown as TNode), + title: globalConfig.value.titleText.empty, + }, + }; + + const emptyClasses = computed(() => [componentName.value, SIZE.value[size.value]]); + const titleClasses = [`${componentName.value}__title`]; + const imageClasses = [`${componentName.value}__image`]; + const descriptionClasses = [`${componentName.value}__description`]; + const actionClass = [`${componentName.value}__action`]; + + const typeImageProps = computed(() => defaultMaps[type.value] ?? null); + const showImage = computed(() => propsImage.value || slots.image || typeImageProps.value?.image); + const showTitle = computed(() => propsTitle.value || slots.title || typeImageProps.value?.title); + const showDescription = computed(() => propsDescription.value || slots.description); + + return { + emptyClasses, + imageClasses, + titleClasses, + descriptionClasses, + actionClass, + showImage, + showTitle, + showDescription, + showAction, + }; + }, + methods: { + renderTitle() { + if (!this.showTitle) { + return null; + } + return
{this.showTitle}
; + }, + renderDescription() { + if (!this.showDescription) { + return null; + } + return
{this.showDescription}
; + }, + getImageIns() { + const data = this.showImage; + let result = null; + if (isString(data)) { + result = ; + } else if (data && Reflect.has(data as TNode, 'render')) { + result = h(data as unknown); + } else if (isPlainObject(data)) { + result = ; + } + + return data ? result : null; + }, + }, + render() { + return ( +
+ {this.showImage ? ( +
+ {this.$slots.image ? renderTNodeJSX(this, 'image') : this.getImageIns()} +
+ ) : null} + {this.renderTitle()} + {this.renderDescription()} + {this.showAction ?
{renderTNodeJSX(this, 'action')}
: null} +
+ ); + }, +}); diff --git a/src/empty/index.ts b/src/empty/index.ts new file mode 100644 index 000000000..a43d0f522 --- /dev/null +++ b/src/empty/index.ts @@ -0,0 +1,10 @@ +import _Empty from './empty'; +import withInstall from '../utils/withInstall'; + +import './style'; + +export * from './type'; + +export const Empty = withInstall(_Empty); + +export default Empty; diff --git a/src/empty/props.ts b/src/empty/props.ts new file mode 100644 index 000000000..a4870f049 --- /dev/null +++ b/src/empty/props.ts @@ -0,0 +1,49 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdEmptyProps } from './type'; +import { PropType } from 'vue'; + +export default { + /** 操作按钮 */ + action: { + type: Function as PropType, + }, + /** 描述文字 */ + description: { + type: [String, Function] as PropType, + }, + /** 组件图片,可以完全自定义内容。值类型为字符串时,表示图片地址;值类型为对象时,则表示透传全部属性到图片组件,示例:`` */ + image: { + type: [String, Function] as PropType, + }, + /** 透传图片样式表 */ + imageStyle: { + type: Object as PropType, + }, + /** 空状态的尺寸,默认为 `medium` */ + size: { + type: String as PropType, + default: 'medium' as TdEmptyProps['size'], + validator(val: TdEmptyProps['size']): boolean { + if (!val) return true; + return ['small', 'medium', 'large'].includes(val); + }, + }, + /** 错误标题 */ + title: { + type: [String, Function] as PropType, + }, + /** 组件类型,如:空数据/成功/失败/网络错误/建设中 */ + type: { + type: String as PropType, + default: 'empty' as TdEmptyProps['type'], + validator(val: TdEmptyProps['type']): boolean { + if (!val) return true; + return ['empty', 'success', 'fail', 'network-error', 'maintenance'].includes(val); + }, + }, +}; diff --git a/src/empty/style/css.js b/src/empty/style/css.js new file mode 100644 index 000000000..6a9a4b132 --- /dev/null +++ b/src/empty/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/empty/style/index.js b/src/empty/style/index.js new file mode 100644 index 000000000..471d313eb --- /dev/null +++ b/src/empty/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/web/components/empty/_index.less'; diff --git a/src/empty/type.ts b/src/empty/type.ts new file mode 100644 index 000000000..1429d0382 --- /dev/null +++ b/src/empty/type.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { ImageProps } from '../image'; +import { TNode, SizeEnum, Styles } from '../common'; + +export interface TdEmptyProps { + /** + * 操作按钮 + */ + action?: TNode; + /** + * 描述文字 + */ + description?: string | TNode; + /** + * 组件图片,可以完全自定义内容。值类型为字符串时,表示图片地址;值类型为对象时,则表示透传全部属性到图片组件,示例:`` + */ + image?: string | ImageProps | TNode; + /** + * 透传图片样式表 + */ + imageStyle?: Styles; + /** + * 空状态的尺寸,默认为 `medium` + * @default medium + */ + size?: SizeEnum; + /** + * 错误标题 + */ + title?: string | TNode; + /** + * 组件类型,如:空数据/成功/失败/网络错误/建设中 + * @default empty + */ + type?: 'empty' | 'success' | 'fail' | 'network-error' | 'maintenance'; +} diff --git a/test/e2e/empty/empty.spec.js b/test/e2e/empty/empty.spec.js new file mode 100644 index 000000000..e69de29bb diff --git a/test/snap/__snapshots__/csr.test.js.snap b/test/snap/__snapshots__/csr.test.js.snap index 80dd2828a..293c82e20 100644 --- a/test/snap/__snapshots__/csr.test.js.snap +++ b/test/snap/__snapshots__/csr.test.js.snap @@ -36401,10 +36401,8 @@ exports[`csr snapshot test > csr test ./src/config-provider/_example/input.vue 1 width="1em" > @@ -39120,10 +39118,8 @@ exports[`csr snapshot test > csr test ./src/date-picker/_example/custom-icon.vue width="1em" > @@ -50184,6 +50180,762 @@ exports[`csr snapshot test > csr test ./src/dropdown/_example/theme.vue 1`] = ` `; +exports[`csr snapshot test > csr test ./src/empty/_example/base.vue 1`] = ` +
+
+ + + + + + + + + + +
+
+ 暂无数据 +
+
+`; + +exports[`csr snapshot test > csr test ./src/empty/_example/descriptions.vue 1`] = ` +
+
+ + + + + + + + + + +
+
+ Empty +
+
+ Description +
+
+`; + +exports[`csr snapshot test > csr test ./src/empty/_example/operation.vue 1`] = ` +
+
+ + + + + + + + + + +
+
+ 暂无数据 +
+
+ +
+
+`; + +exports[`csr snapshot test > csr test ./src/empty/_example/self-defined.vue 1`] = ` +
+
+
+
+ + + +
+
+ 暂无数据 +
+
+ 暂无数据 +
+
+
+
+
+
+
+
+
+ 暂无数据 +
+
+ 暂无数据 +
+
+
+
+`; + +exports[`csr snapshot test > csr test ./src/empty/_example/size.vue 1`] = ` +
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + + + + + + + + +
+
+ 暂无数据 +
+
+
+
+
+
+ + + + + + + + + +
+
+ 建设中 +
+
+
+
+
+
+ + + + + + + + + +
+
+ 网络错误 +
+
+
+
+
+
+ + + +
+
+ 成功 +
+
+
+
+
+
+ + + +
+
+ 失败 +
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test ./src/empty/_example/status.vue 1`] = ` +
+
+
+
+ + + + + + + + + + +
+
+ 暂无数据 +
+
+
+
+
+
+ + + + + + + + + +
+
+ 建设中 +
+
+
+
+
+
+ + + + + + + + + +
+
+ 网络错误 +
+
+
+
+
+
+ + + +
+
+ 成功 +
+
+
+
+
+
+ + + +
+
+ 失败 +
+
+
+
+`; + exports[`csr snapshot test > csr test ./src/form/_example/align.vue 1`] = `
csr test ./src/form/_example/login.vue 1`] = ` width="1em" > @@ -62952,10 +63702,8 @@ exports[`csr snapshot test > csr test ./src/input/_example/password.vue 1`] = ` width="1em" > @@ -63005,10 +63753,8 @@ exports[`csr snapshot test > csr test ./src/input/_example/password.vue 1`] = ` width="1em" > diff --git a/test/snap/__snapshots__/ssr.test.js.snap b/test/snap/__snapshots__/ssr.test.js.snap index 9c5b98680..367c33355 100644 --- a/test/snap/__snapshots__/ssr.test.js.snap +++ b/test/snap/__snapshots__/ssr.test.js.snap @@ -296,7 +296,7 @@ exports[`ssr snapshot test > renders ./src/config-provider/_example/dialog.vue c exports[`ssr snapshot test > renders ./src/config-provider/_example/global.vue correctly 1`] = `"

使用t-config-provider包裹业务功能的最外层组件,点击下方图标查看示例代码

英文语言包引入路径:import enConfig from 'tdesign-vue/es/locale/en_US';

中文语言包引入路径:import zhConfig from 'tdesign-vue/es/locale/zh_CN';

日文语言包引入路径:import jpConfig from 'tdesign-vue/es/locale/ja_JP';

韩文语言包引入路径:import koConfig from 'tdesign-vue/es/locale/ko_KR';

"`; -exports[`ssr snapshot test > renders ./src/config-provider/_example/input.vue correctly 1`] = `"
"`; +exports[`ssr snapshot test > renders ./src/config-provider/_example/input.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/config-provider/_example/others.vue correctly 1`] = `"


0 / 20
0 / 0
Empty Data





















Feature Tag
Feature Tag
Feature Tag
Feature Tag


Tree Empty Data


Department A
Department B



First Step
You need to click the blue button
Second Step
Fill your base information into the form
Error Step
Something Wrong! Custom Error Icon!
4
Last Step
You haven't finish this step.


\\"\\"
loading
"`; @@ -308,7 +308,7 @@ exports[`ssr snapshot test > renders ./src/config-provider/_example/table.vue co exports[`ssr snapshot test > renders ./src/date-picker/_example/base.vue correctly 1`] = `"
"`; -exports[`ssr snapshot test > renders ./src/date-picker/_example/custom-icon.vue correctly 1`] = `"
"`; +exports[`ssr snapshot test > renders ./src/date-picker/_example/custom-icon.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/date-picker/_example/date-presets-alt.vue correctly 1`] = `"
-
-
"`; @@ -416,6 +416,18 @@ exports[`ssr snapshot test > renders ./src/dropdown/_example/split.vue correctly exports[`ssr snapshot test > renders ./src/dropdown/_example/theme.vue correctly 1`] = `"
"`; +exports[`ssr snapshot test > renders ./src/empty/_example/base.vue correctly 1`] = `"
暂无数据
"`; + +exports[`ssr snapshot test > renders ./src/empty/_example/descriptions.vue correctly 1`] = `"
Empty
Description
"`; + +exports[`ssr snapshot test > renders ./src/empty/_example/operation.vue correctly 1`] = `"
暂无数据
"`; + +exports[`ssr snapshot test > renders ./src/empty/_example/self-defined.vue correctly 1`] = `"
暂无数据
暂无数据
暂无数据
暂无数据
"`; + +exports[`ssr snapshot test > renders ./src/empty/_example/size.vue correctly 1`] = `"
暂无数据
建设中
网络错误
成功
失败
"`; + +exports[`ssr snapshot test > renders ./src/empty/_example/status.vue correctly 1`] = `"
暂无数据
建设中
网络错误
成功
失败
"`; + exports[`ssr snapshot test > renders ./src/form/_example/align.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/form/_example/base.vue correctly 1`] = `"
这里可以展示一段说明文字
请用一句话介绍自己
"`; @@ -430,7 +442,7 @@ exports[`ssr snapshot test > renders ./src/form/_example/error-message.vue corre exports[`ssr snapshot test > renders ./src/form/_example/layout.vue correctly 1`] = `"
"`; -exports[`ssr snapshot test > renders ./src/form/_example/login.vue correctly 1`] = `"
"`; +exports[`ssr snapshot test > renders ./src/form/_example/login.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/form/_example/reset.vue correctly 1`] = `"
"`; @@ -546,7 +558,7 @@ exports[`ssr snapshot test > renders ./src/input/_example/group.vue correctly 1` exports[`ssr snapshot test > renders ./src/input/_example/max-length-count.vue correctly 1`] = `"
0/5
0/10
0/5
0/5
"`; -exports[`ssr snapshot test > renders ./src/input/_example/password.vue correctly 1`] = `"
"`; +exports[`ssr snapshot test > renders ./src/input/_example/password.vue correctly 1`] = `"
"`; exports[`ssr snapshot test > renders ./src/input/_example/size.vue correctly 1`] = `"
"`;