diff --git a/site/site.config.mjs b/site/site.config.mjs index ffac24e478..9a897c5172 100644 --- a/site/site.config.mjs +++ b/site/site.config.mjs @@ -465,6 +465,14 @@ export const docs = [ component: () => import('tdesign-vue-next/descriptions/descriptions.md'), componentEn: () => import('tdesign-vue-next/descriptions/descriptions.en-US.md'), }, + { + title: 'Empty 空状态', + titleEn: 'Empty', + name: 'empty', + path: '/vue-next/components/empty', + component: () => import('tdesign-vue-next/empty/empty.md'), + componentEn: () => import('tdesign-vue-next/empty/empty.en-US.md'), + }, { title: 'Image 图片', titleEn: 'Image', diff --git a/site/test-coverage.js b/site/test-coverage.js index 80c0c29c6f..a21a7696e8 100644 --- a/site/test-coverage.js +++ b/site/test-coverage.js @@ -18,10 +18,10 @@ module.exports = { lines: '84.61%', }, autoComplete: { - statements: '98.86%', + statements: '98.88%', branches: '91.07%', functions: '96.15%', - lines: '98.86%', + lines: '98.88%', }, avatar: { statements: '98.23%', @@ -42,10 +42,10 @@ module.exports = { lines: '100%', }, breadcrumb: { - statements: '93.35%', - branches: '82.85%', + statements: '93.63%', + branches: '81.08%', functions: '76.92%', - lines: '93.35%', + lines: '93.63%', }, button: { statements: '100%', @@ -66,16 +66,16 @@ module.exports = { lines: '100%', }, cascader: { - statements: '90.49%', - branches: '76.81%', + statements: '90.28%', + branches: '75.71%', functions: '60.97%', - lines: '90.49%', + lines: '90.28%', }, checkbox: { - statements: '96.76%', - branches: '81.52%', + statements: '96.86%', + branches: '81.44%', functions: '95%', - lines: '96.76%', + lines: '96.86%', }, collapse: { statements: '99.33%', @@ -84,10 +84,10 @@ module.exports = { lines: '99.33%', }, colorPicker: { - statements: '93.83%', + statements: '93.98%', branches: '85.18%', functions: '73.07%', - lines: '93.83%', + lines: '93.98%', }, comment: { statements: '98.44%', @@ -102,28 +102,28 @@ module.exports = { lines: '97.95%', }, configProvider: { - statements: '80.71%', + statements: '79.57%', branches: '89.47%', functions: '50%', - lines: '80.71%', + lines: '79.57%', }, datePicker: { - statements: '62.29%', - branches: '67.74%', - functions: '48.38%', - lines: '62.29%', + statements: '58.9%', + branches: '69.69%', + functions: '47.05%', + lines: '58.9%', }, descriptions: { statements: '100%', - branches: '91.66%', - functions: '94.11%', + branches: '90.32%', + functions: '94.44%', lines: '100%', }, dialog: { - statements: '73.34%', + statements: '73.6%', branches: '77.31%', functions: '63.04%', - lines: '73.34%', + lines: '73.6%', }, divider: { statements: '96.8%', @@ -132,22 +132,28 @@ module.exports = { lines: '96.8%', }, drawer: { - statements: '83.26%', - branches: '70.14%', - functions: '69.56%', - lines: '83.26%', + statements: '91.48%', + branches: '79.76%', + functions: '86.36%', + lines: '91.48%', }, dropdown: { - statements: '53.96%', + statements: '55.1%', branches: '64.28%', functions: '53.33%', - lines: '53.96%', + lines: '55.1%', + }, + empty: { + statements: '95.15%', + branches: '58.06%', + functions: '85.71%', + lines: '95.15%', }, form: { - statements: '96.34%', - branches: '86.34%', - functions: '90.9%', - lines: '96.34%', + statements: '95.61%', + branches: '85.88%', + functions: '88.23%', + lines: '95.61%', }, grid: { statements: '89.8%', @@ -162,10 +168,10 @@ module.exports = { lines: '97.29%', }, hooks: { - statements: '59.53%', - branches: '86.53%', - functions: '57.5%', - lines: '59.53%', + statements: '58.32%', + branches: '88.15%', + functions: '58.75%', + lines: '58.32%', }, icon: { statements: '100%', @@ -174,34 +180,34 @@ module.exports = { lines: '100%', }, image: { - statements: '98.25%', + statements: '98.24%', branches: '82.85%', functions: '94.73%', - lines: '98.25%', + lines: '98.24%', }, imageViewer: { - statements: '72.56%', - branches: '82.81%', + statements: '72.99%', + branches: '83.07%', functions: '59.18%', - lines: '72.56%', + lines: '72.99%', }, input: { - statements: '97.03%', + statements: '97.07%', branches: '83.76%', functions: '97.67%', - lines: '97.03%', + lines: '97.07%', }, inputAdornment: { - statements: '89.24%', + statements: '89.47%', branches: '64.28%', functions: '66.66%', - lines: '89.24%', + lines: '89.47%', }, inputNumber: { - statements: '93.59%', + statements: '93.72%', branches: '79.38%', functions: '91.3%', - lines: '93.59%', + lines: '93.72%', }, layout: { statements: '98.88%', @@ -216,16 +222,16 @@ module.exports = { lines: '100%', }, list: { - statements: '96.82%', - branches: '84.09%', + statements: '96.87%', + branches: '82.22%', functions: '100%', - lines: '96.82%', + lines: '96.87%', }, loading: { - statements: '71.24%', + statements: '67.29%', branches: '96.87%', - functions: '43.75%', - lines: '71.24%', + functions: '36.84%', + lines: '67.29%', }, menu: { statements: '63.67%', @@ -236,20 +242,20 @@ module.exports = { message: { statements: '65.04%', branches: '66.66%', - functions: '42.42%', + functions: '41.17%', lines: '65.04%', }, notification: { - statements: '89.86%', - branches: '86.9%', - functions: '82.5%', - lines: '89.86%', + statements: '95.33%', + branches: '88.65%', + functions: '85.36%', + lines: '95.33%', }, pagination: { - statements: '86.65%', - branches: '83.17%', + statements: '86.14%', + branches: '82.24%', functions: '48.64%', - lines: '86.65%', + lines: '86.14%', }, popconfirm: { statements: '96.61%', @@ -258,64 +264,64 @@ module.exports = { lines: '96.61%', }, popup: { - statements: '85.33%', - branches: '85.71%', + statements: '85.24%', + branches: '85.16%', functions: '81.63%', - lines: '85.33%', + lines: '85.24%', }, progress: { - statements: '96.72%', - branches: '80.39%', + statements: '96.73%', + branches: '77.35%', functions: '91.66%', - lines: '96.72%', + lines: '96.73%', }, radio: { - statements: '92.68%', + statements: '92.39%', branches: '92.13%', functions: '90.9%', - lines: '92.68%', + lines: '92.39%', }, rangeInput: { - statements: '86.93%', - branches: '57.14%', + statements: '87.16%', + branches: '55.17%', functions: '29.03%', - lines: '86.93%', + lines: '87.16%', }, rate: { - statements: '90.67%', + statements: '90.62%', branches: '96.87%', functions: '66.66%', - lines: '90.67%', + lines: '90.62%', }, select: { - statements: '82.34%', - branches: '67.8%', - functions: '60.78%', - lines: '82.34%', + statements: '85.39%', + branches: '70.77%', + functions: '66.66%', + lines: '85.39%', }, selectInput: { - statements: '94.67%', - branches: '75.63%', - functions: '76.66%', - lines: '94.67%', + statements: '94.81%', + branches: '75.2%', + functions: '77.41%', + lines: '94.81%', }, skeleton: { - statements: '97.87%', - branches: '86.04%', - functions: '90.9%', - lines: '97.87%', + statements: '97.44%', + branches: '86.36%', + functions: '90%', + lines: '97.44%', }, slider: { - statements: '72.62%', - branches: '75.28%', + statements: '73.35%', + branches: '76.13%', functions: '38.23%', - lines: '72.62%', + lines: '73.35%', }, space: { - statements: '97.85%', + statements: '97.88%', branches: '77.27%', functions: '100%', - lines: '97.85%', + lines: '97.88%', }, statistic: { statements: '84.21%', @@ -324,10 +330,10 @@ module.exports = { lines: '84.21%', }, steps: { - statements: '98.23%', - branches: '80.48%', + statements: '97.96%', + branches: '79.51%', functions: '87.5%', - lines: '98.23%', + lines: '97.96%', }, stickyTool: { statements: '49.14%', @@ -336,10 +342,10 @@ module.exports = { lines: '49.14%', }, swiper: { - statements: '83.07%', - branches: '69.23%', + statements: '82.23%', + branches: '70.4%', functions: '67.74%', - lines: '83.07%', + lines: '82.23%', }, switch: { statements: '97.95%', @@ -348,46 +354,46 @@ module.exports = { lines: '97.95%', }, table: { - statements: '65.06%', - branches: '61.31%', + statements: '65.19%', + branches: '62.42%', functions: '43.08%', - lines: '65.06%', + lines: '65.19%', }, tabs: { - statements: '96.15%', - branches: '79.36%', - functions: '90.47%', - lines: '96.15%', + statements: '95.01%', + branches: '79.52%', + functions: '88.37%', + lines: '95.01%', }, tag: { - statements: '83.9%', - branches: '86.27%', - functions: '68.18%', - lines: '83.9%', + statements: '81.68%', + branches: '79.62%', + functions: '66.66%', + lines: '81.68%', }, tagInput: { - statements: '96.6%', - branches: '87.01%', + statements: '96.76%', + branches: '87.34%', functions: '82.35%', - lines: '96.6%', + lines: '96.76%', }, textarea: { - statements: '88.75%', - branches: '70.17%', + statements: '89.22%', + branches: '71.66%', functions: '70%', - lines: '88.75%', + lines: '89.22%', }, timePicker: { - statements: '65.36%', - branches: '61.11%', - functions: '40%', - lines: '65.36%', + statements: '66.83%', + branches: '59.09%', + functions: '42.85%', + lines: '66.83%', }, timeline: { - statements: '98.1%', + statements: '98.08%', branches: '83.87%', functions: '92.85%', - lines: '98.1%', + lines: '98.08%', }, tooltip: { statements: '84.03%', @@ -396,39 +402,39 @@ module.exports = { lines: '84.03%', }, transfer: { - statements: '94.56%', - branches: '82.67%', + statements: '94.32%', + branches: '81.67%', functions: '80%', - lines: '94.56%', + lines: '94.32%', }, tree: { - statements: '93.83%', + statements: '93.87%', branches: '85.34%', functions: '83.07%', - lines: '93.83%', + lines: '93.87%', }, treeSelect: { - statements: '83.01%', - branches: '73.52%', - functions: '61.29%', - lines: '83.01%', + statements: '83.99%', + branches: '73.07%', + functions: '63.33%', + lines: '83.99%', }, upload: { statements: '100%', - branches: '91.11%', + branches: '91.66%', functions: '100%', lines: '100%', }, utils: { - statements: '71.7%', - branches: '76.28%', - functions: '64.04%', - lines: '71.7%', + statements: '73.09%', + branches: '77.84%', + functions: '62.63%', + lines: '73.09%', }, watermark: { - statements: '92.5%', - branches: '67.74%', - functions: '68.75%', - lines: '92.5%', + statements: '92.25%', + branches: '65.62%', + functions: '64.7%', + lines: '92.25%', }, }; diff --git a/src/components.ts b/src/components.ts index 49456fdd89..a9eee6fc3f 100644 --- a/src/components.ts +++ b/src/components.ts @@ -50,6 +50,7 @@ export * from './badge'; export * from './calendar'; export * from './card'; export * from './comment'; +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 98c46c0fde..8f229454c3 100644 --- a/src/config-provider/type.ts +++ b/src/config-provider/type.ts @@ -58,6 +58,11 @@ export interface GlobalConfigProvider { * 抽屉全局配置 */ drawer?: DrawerConfig; + /** + * 空状态全局配置 + */ + empty?: EmptyConfig; + /** /** * 表单组件全局配置 */ @@ -919,6 +924,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 0000000000..e5a27fbe19 --- /dev/null +++ b/src/empty/__tests__/index.test.jsx @@ -0,0 +1,28 @@ +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(() => ); + expect(wrapper.find('.t-empty.t-size-s')).not.toBeNull(); + }); + it('title', () => { + const wrapper = mount(() => ); + expect(wrapper.find('.t-empty__title').element.innerHTML).toBe('title'); + }); + it('description', () => { + const wrapper = mount(() => ); + expect(wrapper.find('.t-empty__description').element.innerHTML).toBe('description'); + }); + it('type', () => { + const wrapper = mount(() => ); + 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-ts/base.vue b/src/empty/_example-ts/base.vue new file mode 100644 index 0000000000..576806d4fd --- /dev/null +++ b/src/empty/_example-ts/base.vue @@ -0,0 +1,5 @@ + diff --git a/src/empty/_example-ts/descriptions.vue b/src/empty/_example-ts/descriptions.vue new file mode 100644 index 0000000000..e46f42ad55 --- /dev/null +++ b/src/empty/_example-ts/descriptions.vue @@ -0,0 +1,8 @@ + + diff --git a/src/empty/_example-ts/operation.vue b/src/empty/_example-ts/operation.vue new file mode 100644 index 0000000000..451014e9c4 --- /dev/null +++ b/src/empty/_example-ts/operation.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/empty/_example-ts/self-defined.vue b/src/empty/_example-ts/self-defined.vue new file mode 100644 index 0000000000..838b0439a5 --- /dev/null +++ b/src/empty/_example-ts/self-defined.vue @@ -0,0 +1,28 @@ + + diff --git a/src/empty/_example-ts/size.vue b/src/empty/_example-ts/size.vue new file mode 100644 index 0000000000..1b53bb7878 --- /dev/null +++ b/src/empty/_example-ts/size.vue @@ -0,0 +1,38 @@ + + diff --git a/src/empty/_example-ts/status.vue b/src/empty/_example-ts/status.vue new file mode 100644 index 0000000000..afa596a7b6 --- /dev/null +++ b/src/empty/_example-ts/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 0000000000..576806d4fd --- /dev/null +++ b/src/empty/_example/base.vue @@ -0,0 +1,5 @@ + diff --git a/src/empty/_example/descriptions.vue b/src/empty/_example/descriptions.vue new file mode 100644 index 0000000000..0f9bbefeb3 --- /dev/null +++ b/src/empty/_example/descriptions.vue @@ -0,0 +1,8 @@ + + diff --git a/src/empty/_example/operation.vue b/src/empty/_example/operation.vue new file mode 100644 index 0000000000..359ea03318 --- /dev/null +++ b/src/empty/_example/operation.vue @@ -0,0 +1,13 @@ + + diff --git a/src/empty/_example/self-defined.vue b/src/empty/_example/self-defined.vue new file mode 100644 index 0000000000..c2c9da75a6 --- /dev/null +++ b/src/empty/_example/self-defined.vue @@ -0,0 +1,28 @@ + + diff --git a/src/empty/_example/size.vue b/src/empty/_example/size.vue new file mode 100644 index 0000000000..6cdd5feb9e --- /dev/null +++ b/src/empty/_example/size.vue @@ -0,0 +1,36 @@ + + diff --git a/src/empty/_example/status.vue b/src/empty/_example/status.vue new file mode 100644 index 0000000000..18ad83890b --- /dev/null +++ b/src/empty/_example/status.vue @@ -0,0 +1,20 @@ + + diff --git a/src/empty/assets/EmptySvg.tsx b/src/empty/assets/EmptySvg.tsx new file mode 100644 index 0000000000..6b9b36e964 --- /dev/null +++ b/src/empty/assets/EmptySvg.tsx @@ -0,0 +1,30 @@ +import { defineComponent } from 'vue'; + +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 0000000000..9fff3798a8 --- /dev/null +++ b/src/empty/assets/FailSvg.tsx @@ -0,0 +1,17 @@ +import { defineComponent } from 'vue'; + +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 0000000000..d08880c8f4 --- /dev/null +++ b/src/empty/assets/MaintenanceSvg.tsx @@ -0,0 +1,21 @@ +import { defineComponent } from 'vue'; + +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 0000000000..6276e52b42 --- /dev/null +++ b/src/empty/assets/NetworkErrorSvg.tsx @@ -0,0 +1,21 @@ +import { defineComponent } from 'vue'; + +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 0000000000..0385cf2dbe --- /dev/null +++ b/src/empty/assets/SuccessSvg.tsx @@ -0,0 +1,17 @@ +import { defineComponent } from 'vue'; + +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 0000000000..eb87db3a98 --- /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-next/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-next/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-next/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-vue-next/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-next/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-next/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-next/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 0000000000..7de78f9bff --- /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-next/blob/develop/src/common.ts) | N +description | String / Slot / Function | - | 描述文字。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue-next/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-next/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-vue-next/tree/develop/src/empty/type.ts) | N +imageStyle | Object | - | 透传图片样式表。TS 类型:`Styles`。[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N +size | String | medium | 空状态的尺寸,默认为 `medium`。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/src/common.ts) | N +title | String / Slot / Function | - | 错误标题。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue-next/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 0000000000..f660f9d325 --- /dev/null +++ b/src/empty/empty.tsx @@ -0,0 +1,104 @@ +import { computed, defineComponent, h, toRefs } from 'vue'; +import isString from 'lodash/isString'; +import isPlainObject from 'lodash/isPlainObject'; +import { useCommonClassName, useConfig, usePrefixClass } from '../hooks/useConfig'; +import { useTNodeJSX } from '../hooks/tnode'; +import props from './props'; +import type { TdEmptyProps } from './type'; +import Image from '../image'; +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'; + +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 classPrefix = usePrefixClass('empty'); + const showAction = computed(() => props.action || slots.action); + const { SIZE } = useCommonClassName(); + const renderTNodeJSX = useTNodeJSX(); + + const defaultMaps: { + [key in TdEmptyProps['type']]?: Pick; + } = { + maintenance: { + image: globalConfig.value.image.maintenance || MaintenanceSvg, + title: globalConfig.value.titleText.maintenance, + }, + success: { + image: globalConfig.value.image.success || SuccessSvg, + title: globalConfig.value.titleText.success, + }, + fail: { + image: globalConfig.value.image.fail || FailSvg, + title: globalConfig.value.titleText.fail, + }, + 'network-error': { + image: globalConfig.value.image.networkError || NetworkErrorSvg, + title: globalConfig.value.titleText.networkError, + }, + empty: { + image: globalConfig.value.image.empty || EmptySvg, + title: globalConfig.value.titleText.empty, + }, + }; + + const emptyClasses = computed(() => [classPrefix.value, SIZE.value[size.value]]); + const titleClasses = [`${classPrefix.value}__title`]; + const imageClasses = [`${classPrefix.value}__image`]; + const descriptionClasses = [`${classPrefix.value}__description`]; + const actionClass = [`${classPrefix.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?.()); + + const renderTitle = () => { + if (!showTitle.value) { + return null; + } + return
{showTitle.value}
; + }; + const renderDescription = () => { + if (!showDescription.value) { + return null; + } + return
{showDescription.value}
; + }; + const getImageIns = () => { + const data = showImage.value; + let result = null; + if (isString(data)) { + result = ; + } else if (data && Reflect.has(data, 'render')) { + result = h(data as unknown); + } else if (isPlainObject(data)) { + result = ; + } + + return data ? result : null; + }; + + return () => { + return ( +
+ {showImage.value ? ( +
+ {slots?.image ? renderTNodeJSX('image') : getImageIns()} +
+ ) : null} + {renderTitle()} + {renderDescription()} + {showAction.value ?
{renderTNodeJSX('action')}
: null} +
+ ); + }; + }, +}); diff --git a/src/empty/index.ts b/src/empty/index.ts new file mode 100644 index 0000000000..ae63e6df95 --- /dev/null +++ b/src/empty/index.ts @@ -0,0 +1,11 @@ +import _Empty from './empty'; +import withInstall from '../utils/withInstall'; +import type { TdEmptyProps } from './type'; + +import './style'; + +export * from './type'; +export type EmptyProps = TdEmptyProps; + +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 0000000000..a4870f049e --- /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 0000000000..6a9a4b1328 --- /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 0000000000..471d313eb5 --- /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 0000000000..1429d0382c --- /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 0000000000..e69de29bb2 diff --git a/test/unit/snap/__snapshots__/csr.test.js.snap b/test/unit/snap/__snapshots__/csr.test.js.snap index 98955119cb..8688bc020b 100644 --- a/test/unit/snap/__snapshots__/csr.test.js.snap +++ b/test/unit/snap/__snapshots__/csr.test.js.snap @@ -61186,6 +61186,865 @@ 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`] = `
ssr test ./src/dropdown/_example/split.vue 1`] = `" exports[`ssr snapshot test > ssr test ./src/dropdown/_example/theme.vue 1`] = `"
"`; +exports[`ssr snapshot test > ssr test ./src/empty/_example/base.vue 1`] = `"
暂无数据
"`; + +exports[`ssr snapshot test > ssr test ./src/empty/_example/descriptions.vue 1`] = `"
空状态 Empty
暂无数据 Description
"`; + +exports[`ssr snapshot test > ssr test ./src/empty/_example/operation.vue 1`] = `"
暂无数据
"`; + +exports[`ssr snapshot test > ssr test ./src/empty/_example/self-defined.vue 1`] = `"
暂无数据
暂无数据
暂无数据
暂无数据
"`; + +exports[`ssr snapshot test > ssr test ./src/empty/_example/size.vue 1`] = `"
暂无数据
建设中
网络错误
成功
失败
"`; + +exports[`ssr snapshot test > ssr test ./src/empty/_example/status.vue 1`] = `"
暂无数据
建设中
网络错误
成功
失败
"`; + exports[`ssr snapshot test > ssr test ./src/form/_example/align.vue 1`] = `"
"`; exports[`ssr snapshot test > ssr test ./src/form/_example/base.vue 1`] = `"
"`;