diff --git a/components/index.js b/components/index.js index 7c6cc7313..cfb3b4429 100644 --- a/components/index.js +++ b/components/index.js @@ -137,6 +137,8 @@ import { default as ConfigProvider } from './config-provider'; import { default as Empty } from './empty'; +import { default as Result } from './result'; + const components = [ Base, Affix, @@ -195,6 +197,7 @@ const components = [ Comment, ConfigProvider, Empty, + Result, ]; const install = function(Vue) { @@ -279,6 +282,7 @@ export { Comment, ConfigProvider, Empty, + Result, }; export default { diff --git a/components/result/__tests__/__snapshots__/demo.test.js.snap b/components/result/__tests__/__snapshots__/demo.test.js.snap new file mode 100644 index 000000000..01b14d639 --- /dev/null +++ b/components/result/__tests__/__snapshots__/demo.test.js.snap @@ -0,0 +1,289 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders ./components/result/demo/403.md correctly 1`] = ` +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
403
+
Sorry, you are not authorized to access this page.
+
+
+`; + +exports[`renders ./components/result/demo/404.md correctly 1`] = ` +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
404
+
Sorry, the page you visited does not exist.
+
+
+`; + +exports[`renders ./components/result/demo/500.md correctly 1`] = ` +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
500
+
Sorry, the server is wrong.
+
+
+`; + +exports[`renders ./components/result/demo/customIcon.md correctly 1`] = ` +
+
+
Great, we have done all the operations!
+
+
+`; + +exports[`renders ./components/result/demo/error.md correctly 1`] = ` +
+
+
Submission Failed
+
Please check and modify the following information before resubmitting.
+
+
+

The content you submitted has the following error:

+

Your account has been frozen Thaw immediately >

+

Your account is not yet eligible to apply Apply Unlock >

+
+
+
+
+`; + +exports[`renders ./components/result/demo/info.md correctly 1`] = ` +
+
+
Your operation has been executed
+
+
+`; + +exports[`renders ./components/result/demo/success.md correctly 1`] = ` +
+
+
Successfully Purchased Cloud Server ECS!
+
Order number: 2017182818828182881 Cloud server configuration takes 1-5 minutes, please wait.
+
+
+`; + +exports[`renders ./components/result/demo/warning.md correctly 1`] = ` +
+
+
There are some problems with your operation.
+
+
+`; diff --git a/components/result/__tests__/demo.test.js b/components/result/__tests__/demo.test.js new file mode 100644 index 000000000..bb3506ef1 --- /dev/null +++ b/components/result/__tests__/demo.test.js @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('result'); diff --git a/components/result/__tests__/index.test.js b/components/result/__tests__/index.test.js new file mode 100644 index 000000000..fff8d2530 --- /dev/null +++ b/components/result/__tests__/index.test.js @@ -0,0 +1,66 @@ +import { mount } from '@vue/test-utils'; +import Result from '../index'; +import Button from '../../button'; + +describe('Result', () => { + + it('🙂 successPercent should decide the progress status when it exists', () => { + const wrapper = mount({ + render () { + return ( + Go Console} + /> + ); + }, + }); + expect(wrapper.findAll('.anticon-check-circle')).toHaveLength(1); + }); + + it('🙂 different status, different class', () => { + const wrapper = mount(Result, { propsData: { status: 'warning' } }); + expect(wrapper.findAll('.ant-result-warning')).toHaveLength(1); + + wrapper.setProps({ + status: 'error', + }); + + expect(wrapper.findAll('.ant-result-error')).toHaveLength(1); + + wrapper.setProps({ + status: '500', + }); + + expect(wrapper.findAll('.ant-result-500')).toHaveLength(1); + }); + + it('🙂 When status = 404, the icon is an image', () => { + const wrapper = mount({ + render () { + return ; + }, + }); + expect(wrapper.findAll('.ant-result-404 .ant-result-image')).toHaveLength(1); + }); + + it('🙂 When extra is undefined, the extra dom is undefined', () => { + const wrapper = mount({ + render () { + return ; + }, + }); + expect(wrapper.findAll('.ant-result-extra')).toHaveLength(0); + }); + + it('🙂 result should support className', () => { + const wrapper = mount({ + render () { + return ; + }, + }); + expect(wrapper.findAll('.ant-result.my-result')).toHaveLength(1); + }); +}); diff --git a/components/result/demo/403.md b/components/result/demo/403.md new file mode 100644 index 000000000..88d86d9f1 --- /dev/null +++ b/components/result/demo/403.md @@ -0,0 +1,31 @@ + +#### 403 +你没有此页面的访问权限。 + + + +#### 403 +you are not authorized to access this page. + + +```tpl + + +``` diff --git a/components/result/demo/404.md b/components/result/demo/404.md new file mode 100644 index 000000000..0a5d1177d --- /dev/null +++ b/components/result/demo/404.md @@ -0,0 +1,31 @@ + +#### 404 +此页面未找到。 + + + +#### 404 +The page you visited does not exist. + + +```tpl + + +``` diff --git a/components/result/demo/500.md b/components/result/demo/500.md new file mode 100644 index 000000000..0658146b5 --- /dev/null +++ b/components/result/demo/500.md @@ -0,0 +1,31 @@ + +#### 500 +服务器发生了错误。 + + + +#### 500 +The server is wrong. + + +```tpl + + +``` diff --git a/components/result/demo/customIcon.md b/components/result/demo/customIcon.md new file mode 100644 index 000000000..dad2f3d5f --- /dev/null +++ b/components/result/demo/customIcon.md @@ -0,0 +1,30 @@ + +#### 自定义 icon +自定义 icon。 + + + +#### Custom icon +Custom icon. + + +```tpl + + +``` diff --git a/components/result/demo/error.md b/components/result/demo/error.md new file mode 100644 index 000000000..5d440f7bb --- /dev/null +++ b/components/result/demo/error.md @@ -0,0 +1,47 @@ + +#### Error +复杂的错误反馈。 + + + +#### Error +Complex error feedback. + + +```tpl + + + +``` diff --git a/components/result/demo/index.vue b/components/result/demo/index.vue new file mode 100644 index 000000000..fb87b832a --- /dev/null +++ b/components/result/demo/index.vue @@ -0,0 +1,71 @@ + diff --git a/components/result/demo/info.md b/components/result/demo/info.md new file mode 100644 index 000000000..56ac34874 --- /dev/null +++ b/components/result/demo/info.md @@ -0,0 +1,29 @@ + +#### Info +展示处理结果。 + + + +#### Info +Show processing results. + + +```tpl + + +``` diff --git a/components/result/demo/success.md b/components/result/demo/success.md new file mode 100644 index 000000000..f11d77abe --- /dev/null +++ b/components/result/demo/success.md @@ -0,0 +1,32 @@ + +#### Success +成功的结果。 + + + +#### Success +Show successful results. + + +```tpl + + +``` diff --git a/components/result/demo/warning.md b/components/result/demo/warning.md new file mode 100644 index 000000000..7ebac475b --- /dev/null +++ b/components/result/demo/warning.md @@ -0,0 +1,27 @@ + +#### Warning +警告类型的结果。 + + + +#### Warning +The result of the warning. + + +```tpl + + +``` diff --git a/components/result/index.en-US.md b/components/result/index.en-US.md new file mode 100644 index 000000000..e86df6f6e --- /dev/null +++ b/components/result/index.en-US.md @@ -0,0 +1,9 @@ +## API + +| Property | Description | Type | Default | +| -------- | ------------------------------------- | ----------------------------------------------------------------- | ------- | +| title | title string | string \| VNode \| v-slot:title | - | +| subTitle | subTitle string | string \| VNode \| v-slot:subTitle | - | +| status | result status,decide icons and colors | `'success' | 'error' | 'info' | 'warning'| '404' | '403' | '500'` | 'info' | +| icon | custom back icon | v-slot:icon | - | +| extra | operating area | v-slot:extra | - | diff --git a/components/result/index.jsx b/components/result/index.jsx new file mode 100644 index 000000000..b53f0072c --- /dev/null +++ b/components/result/index.jsx @@ -0,0 +1,94 @@ +import PropTypes from '../_util/vue-types'; +import { getOptionProps, getComponentFromProp } from '../_util/props-util'; +import { ConfigConsumerProps } from '../config-provider'; +import Icon from '../icon'; +import Base from '../base'; +import noFound from './noFound'; +import serverError from './serverError'; +import unauthorized from './unauthorized'; + +export const IconMap = { + success: 'check-circle', + error: 'close-circle', + info: 'exclamation-circle', + warning: 'warning', +}; + +export const ExceptionMap = { + '404': noFound, + '500': serverError, + '403': unauthorized, +}; + +// ExceptionImageMap keys +const ExceptionStatus = Object.keys(ExceptionMap); + +export const ResultProps = { + prefixCls: PropTypes.string, + icon: PropTypes.any, + status: PropTypes.oneOf(['success', 'error', 'info', 'warning', '404', '403', '500']).def('info'), + title: PropTypes.any, + subTitle: PropTypes.any, + extra: PropTypes.any, +}; + +const renderIcon = (h, prefixCls, { status, icon }) => { + if (ExceptionStatus.includes(status)) { + const SVGComponent = ExceptionMap[status]; + return ( +
+ +
+ ); + } + // prop `icon` require slot or VNode + const iconString = IconMap[status]; + const iconNode = icon || ; + return
{iconNode}
; +}; + +const renderExtra = (h, prefixCls, extra) => extra &&
{extra}
; + +const Result = { + name: 'AResult', + props: ResultProps, + inject: { + configProvider: { default: () => ConfigConsumerProps }, + }, + render(h) { + const { + prefixCls: customizePrefixCls, + status, + ...restProps + } = this; + const getPrefixCls = this.configProvider.getPrefixCls; + const prefixCls = getPrefixCls('result', customizePrefixCls); + + const title = getComponentFromProp(this, 'title'); + const subTitle = getComponentFromProp(this, 'subTitle'); + const icon = getComponentFromProp(this, 'icon'); + const extra = getComponentFromProp(this, 'extra'); + + return ( +
+ {renderIcon(h, prefixCls, { status, icon })} +
{title}
+ {subTitle &&
{subTitle}
} + {this.$slots.default &&
{this.$slots.default}
} + {renderExtra(h, prefixCls, extra)} +
+ ); + }, +}; + +/* add resource */ +Result.PRESENTED_IMAGE_403 = ExceptionMap[403]; +Result.PRESENTED_IMAGE_404 = ExceptionMap[404]; +Result.PRESENTED_IMAGE_500 = ExceptionMap[500]; + +/* istanbul ignore next */ +Result.install = function(Vue) { + Vue.use(Base); + Vue.component(Result.name, Result); +}; +export default Result; diff --git a/components/result/index.zh-CN.md b/components/result/index.zh-CN.md new file mode 100644 index 000000000..15d02eb28 --- /dev/null +++ b/components/result/index.zh-CN.md @@ -0,0 +1,9 @@ +## API + +| 参数 | 说明 | 类型 | 默认值 | +| -------- | ------------------------- | ----------------------------------------------------------------- | ------ | +| title | title 文字 | string \| VNode \| v-slot:title | - | +| subTitle | subTitle 文字 | string \| VNode \| v-slot:subTitle | - | +| status | 结果的状态,决定图标和颜色 | `'success' | 'error' | 'info' | 'warning'| '404' | '403' | '500'` | 'info' | +| icon | 自定义 icon | v-slot:icon | - | +| extra | 操作区 | v-slot:extra | - | diff --git a/components/result/noFound.jsx b/components/result/noFound.jsx new file mode 100644 index 000000000..9f352ed89 --- /dev/null +++ b/components/result/noFound.jsx @@ -0,0 +1,289 @@ +const NoFound = { + functional: true, + render() { + return( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +}; + +export default NoFound; diff --git a/components/result/serverError.jsx b/components/result/serverError.jsx new file mode 100644 index 000000000..986d03372 --- /dev/null +++ b/components/result/serverError.jsx @@ -0,0 +1,334 @@ +const ServerError = { + functional: true, + render() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +}; + +export default ServerError; diff --git a/components/result/style/index.js b/components/result/style/index.js new file mode 100644 index 000000000..3a3ab0de5 --- /dev/null +++ b/components/result/style/index.js @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/result/style/index.less b/components/result/style/index.less new file mode 100644 index 000000000..5ebf40dcf --- /dev/null +++ b/components/result/style/index.less @@ -0,0 +1,71 @@ +@import '../../style/themes/default'; +@import '../../style/mixins/index'; + +@result-prefix-cls: ~'@{ant-prefix}-result'; + +.@{result-prefix-cls} { + padding: 48px 32px; + // status color + &-success &-icon > .anticon { + color: @success-color; + } + + &-error &-icon > .anticon { + color: @error-color; + } + + &-info &-icon > .anticon { + color: @info-color; + } + + &-warning &-icon > .anticon { + color: @warning-color; + } + + // Exception Status image + &-image { + width: 250px; + height: 295px; + margin: auto; + } + + &-icon { + margin-bottom: 24px; + text-align: center; + + > .anticon { + font-size: 72px; + } + } + + &-title { + color: @heading-color; + font-size: 24px; + line-height: 1.8; + text-align: center; + } + + &-subtitle { + color: @text-color-secondary; + font-size: 14px; + line-height: 1.6; + text-align: center; + } + + &-extra { + margin-top: 32px; + text-align: center; + > * { + margin-right: 8px; + &:last-child { + margin-right: 0; + } + } + } + + &-content { + margin-top: 24px; + padding: 24px 40px; + background-color: @background-color-light; + } +} diff --git a/components/result/unauthorized.jsx b/components/result/unauthorized.jsx new file mode 100644 index 000000000..9fd2f4ac5 --- /dev/null +++ b/components/result/unauthorized.jsx @@ -0,0 +1,283 @@ +const Unauthorized = { + functional: true, + render() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + }, +}; + +export default Unauthorized; diff --git a/site/components.js b/site/components.js index 4a4df60ec..5fd855ddf 100644 --- a/site/components.js +++ b/site/components.js @@ -61,6 +61,7 @@ import { ConfigProvider, Empty, Base, + Result, } from 'ant-design-vue'; Vue.prototype.$message = message; @@ -130,6 +131,7 @@ Vue.use(Skeleton); Vue.use(Comment); Vue.use(ConfigProvider); Vue.use(Empty); +Vue.use(Result); /* v1.1.2 registration methods */ // Vue.component(Affix.name, Affix) // a-affix diff --git a/site/demo.js b/site/demo.js index 654774385..636ccf3bb 100644 --- a/site/demo.js +++ b/site/demo.js @@ -174,6 +174,12 @@ export default { type: 'Feedback', title: 'Spin', }, + result: { + category: 'Components', + subtitle: '结果', + type: 'Feedback', + title: 'Result', + }, switch: { category: 'Components', subtitle: '开关', diff --git a/site/demoRoutes.js b/site/demoRoutes.js index b23c6d993..5aa0e8445 100644 --- a/site/demoRoutes.js +++ b/site/demoRoutes.js @@ -455,4 +455,12 @@ export default [ path: 'empty-cn', component: () => import('../components/empty/demo/index.vue'), }, + { + path: 'result', + component: () => import('../components/result/demo/index.vue'), + }, + { + path: 'result-cn', + component: () => import('../components/result/demo/index.vue'), + }, ]; diff --git a/types/ant-design-vue.d.ts b/types/ant-design-vue.d.ts index 97f69d428..5b3deaf84 100644 --- a/types/ant-design-vue.d.ts +++ b/types/ant-design-vue.d.ts @@ -61,6 +61,7 @@ import { TimePicker } from './time-picker'; import { Timeline } from './timeline/timeline'; import { Tooltip } from './tootip/tooltip'; import { Upload } from './upload'; +import { Result } from './result'; /** * Install all ant-design-vue components into Vue. @@ -129,4 +130,5 @@ export { Upload, Drawer, Skeleton, + Result, }; diff --git a/types/result.d.ts b/types/result.d.ts new file mode 100644 index 000000000..bf6effa62 --- /dev/null +++ b/types/result.d.ts @@ -0,0 +1,38 @@ +import { AntdComponent } from './component'; + +export declare class Result extends AntdComponent { + /** + * result title + * @type string + */ + title: any; + + /** + * result sub title + * + * @type string + */ + subTitle: any; + + /** + * result status,decide icons and colors + * enum of 'success' | 'error' | 'info' | 'warning'| '404' | '403' | '500'` | 'info' + * + * @default 'info' + * @type string + */ + status: string; + + /** + * custom back icon + * @type any v-slot + */ + icon: any; + + /** + * operating area + * @type any v-slot + */ + extra: any; + +}