diff --git a/components/_util/responsiveObserve.js b/components/_util/responsiveObserve.js
new file mode 100644
index 000000000..c5d2f8796
--- /dev/null
+++ b/components/_util/responsiveObserve.js
@@ -0,0 +1,96 @@
+// matchMedia polyfill for
+// https://github.com/WickyNilliams/enquire.js/issues/82
+let enquire;
+
+// TODO: Will be removed in antd 4.0 because we will no longer support ie9
+if (typeof window !== 'undefined') {
+ const matchMediaPolyfill = (mediaQuery) => {
+ return {
+ media: mediaQuery,
+ matches: false,
+ addListener() {},
+ removeListener() {},
+ };
+ };
+ // ref: https://github.com/ant-design/ant-design/issues/18774
+ if (!window.matchMedia) window.matchMedia = matchMediaPolyfill;
+ // eslint-disable-next-line global-require
+ enquire = require('enquire.js');
+}
+
+export const responsiveArray = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
+
+export const responsiveMap = {
+ xs: '(max-width: 575px)',
+ sm: '(min-width: 576px)',
+ md: '(min-width: 768px)',
+ lg: '(min-width: 992px)',
+ xl: '(min-width: 1200px)',
+ xxl: '(min-width: 1600px)',
+};
+
+let subscribers = [];
+let subUid = -1;
+let screens = {};
+
+const responsiveObserve = {
+ dispatch(pointMap) {
+ screens = pointMap;
+ if (subscribers.length < 1) {
+ return false;
+ }
+
+ subscribers.forEach(item => {
+ item.func(screens);
+ });
+
+ return true;
+ },
+ subscribe(func) {
+ if (subscribers.length === 0) {
+ this.register();
+ }
+ const token = (++subUid).toString();
+ subscribers.push({
+ token,
+ func,
+ });
+ func(screens);
+ return token;
+ },
+ unsubscribe(token) {
+ subscribers = subscribers.filter(item => item.token !== token);
+ if (subscribers.length === 0) {
+ this.unregister();
+ }
+ },
+ unregister() {
+ Object.keys(responsiveMap).map((screen) =>
+ enquire.unregister(responsiveMap[screen]),
+ );
+ },
+ register() {
+ Object.keys(responsiveMap).map((screen) =>
+ enquire.register(responsiveMap[screen], {
+ match: () => {
+ const pointMap = {
+ ...screens,
+ [screen]: true,
+ };
+ this.dispatch(pointMap);
+ },
+ unmatch: () => {
+ const pointMap = {
+ ...screens,
+ [screen]: false,
+ };
+ this.dispatch(pointMap);
+ },
+ // Keep a empty destory to avoid triggering unmatch when unregister
+ destroy() {},
+ }),
+ );
+ },
+};
+
+export default responsiveObserve;
diff --git a/components/descriptions/Col.jsx b/components/descriptions/Col.jsx
new file mode 100644
index 000000000..f0c456adf
--- /dev/null
+++ b/components/descriptions/Col.jsx
@@ -0,0 +1,74 @@
+import PropTypes from '../_util/vue-types';
+import { getOptionProps, getSlots, getComponentFromProp } from '../_util/props-util';
+
+const ColProps = {
+ child: PropTypes.any,
+ bordered: PropTypes.bool,
+ colon: PropTypes.bool,
+ type: PropTypes.oneOf(['label', 'content']),
+ layout: PropTypes.oneOf(['horizontal', 'vertical']),
+};
+
+const Col = {
+ props: ColProps,
+ render() {
+ const { child, bordered, colon, type, layout } = this.$props;
+ const { prefixCls, span = 1 } = getOptionProps(child);
+
+ const label = getComponentFromProp(child, 'label');
+ const slots = getSlots(child);
+ const labelProps = {
+ attrs: {},
+ class: [`${prefixCls}-item-label`, {
+ [`${prefixCls}-item-colon`]: colon,
+ [`${prefixCls}-item-no-label`]: !label,
+ }],
+ key: 'label',
+ };
+ if (layout === 'vertical') {
+ labelProps.attrs.colSpan = span * 2 - 1;
+ }
+
+ if (bordered) {
+ if (type === 'label') {
+ return
{label} | ;
+ }
+ return (
+
+ {slots.default}
+ |
+ );
+ }
+ if (layout === 'vertical') {
+ if (type === 'content') {
+ return (
+
+
+ {slots.default}
+
+ |
+ );
+ }
+ return (
+
+
+ {label}
+
+ |
+ );
+ }
+ return (
+
+ {label}
+
+ {slots.default}
+
+ |
+ );
+ },
+};
+
+export default Col;
diff --git a/components/descriptions/__tests__/__snapshots__/demo.test.js.snap b/components/descriptions/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..5234d8a8a
--- /dev/null
+++ b/components/descriptions/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,287 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/descriptions/demo/basic.md correctly 1`] = `
+
+
User Info
+
+
+
+
+ UserNameZhou Maomao |
+ Telephone1810000000 |
+ LiveHangzhou, Zhejiang |
+
+
+ Remarkempty |
+ Address
+ No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
+ |
+
+
+
+
+
+`;
+
+exports[`renders ./components/descriptions/demo/border.md correctly 1`] = `
+
+
User Info
+
+
+
+
+ Product |
+ Cloud Database |
+ Billing Mode |
+ Prepaid |
+ Automatic Renewal |
+ YES |
+
+
+ Order time |
+ 2018-04-24 18:00:00 |
+ Usage Time |
+
+ 2019-04-24 18:00:00
+ |
+
+
+ Status |
+ Running |
+
+
+ Negotiated Amount |
+ $80.00 |
+ Discount |
+ $20.00 |
+ Official Receipts |
+ $60.00 |
+
+
+ Config Info |
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
|
+
+
+
+
+
+`;
+
+exports[`renders ./components/descriptions/demo/responsive.md correctly 1`] = `
+
+
+
Responsive Descriptions
+
+
+
+
+ Product |
+ Cloud Database |
+ Billing |
+ Prepaid |
+ time |
+ 18:00:00 |
+
+
+ Amount |
+ $80.00 |
+ Discount |
+ $20.00 |
+ Official |
+ $60.00 |
+
+
+ Config Info |
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
+ |
+
+
+
+
+
+
+`;
+
+exports[`renders ./components/descriptions/demo/size.md correctly 1`] = `
+
+
+
+
Custom Size
+
+
+
+
+ Product |
+ Cloud Database |
+ Billing |
+ Prepaid |
+ time |
+ 18:00:00 |
+
+
+ Amount |
+ $80.00 |
+ Discount |
+ $20.00 |
+ Official |
+ $60.00 |
+
+
+ Config Info |
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
|
+
+
+
+
+
+
+
Custom Size
+
+
+
+
+ ProductCloud Database |
+ BillingPrepaid |
+ time18:00:00 |
+
+
+ Amount$80.00 |
+ Discount$20.00 |
+ Official$60.00 |
+
+
+
+
+
+
+`;
+
+exports[`renders ./components/descriptions/demo/vertical.md correctly 1`] = `
+
+
User Info
+
+
+
+
+ UserName |
+ Telephone |
+ Live |
+
+
+ Zhou Maomao |
+ 1810000000 |
+ Hangzhou, Zhejiang |
+
+
+ Address |
+ Remark |
+
+
+
+ No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
+ |
+ empty |
+
+
+
+
+
+`;
+
+exports[`renders ./components/descriptions/demo/vertical-border.md correctly 1`] = `
+
+
User Info
+
+
+
+
+ Product |
+ Billing Mode |
+ Automatic Renewal |
+
+
+ Cloud Database |
+ Prepaid |
+ YES |
+
+
+ Order time |
+ Usage Time |
+
+
+ 2018-04-24 18:00:00 |
+
+ 2019-04-24 18:00:00
+ |
+
+
+ Status |
+
+
+ Running |
+
+
+ Negotiated Amount |
+ Discount |
+ Official Receipts |
+
+
+ $80.00 |
+ $20.00 |
+ $60.00 |
+
+
+ Config Info |
+
+
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
|
+
+
+
+
+
+`;
diff --git a/components/descriptions/__tests__/demo.test.js b/components/descriptions/__tests__/demo.test.js
new file mode 100644
index 000000000..7d16a4ab6
--- /dev/null
+++ b/components/descriptions/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest';
+
+demoTest('descriptions');
\ No newline at end of file
diff --git a/components/descriptions/demo/basic.md b/components/descriptions/demo/basic.md
new file mode 100644
index 000000000..9a2c8682b
--- /dev/null
+++ b/components/descriptions/demo/basic.md
@@ -0,0 +1,23 @@
+
+#### 基本
+简单的展示。
+
+
+
+#### Basic
+Simplest Usage.
+
+
+```tpl
+
+
+ Zhou Maomao
+ 1810000000
+ Hangzhou, Zhejiang
+ empty
+
+ No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
+
+
+
+```
diff --git a/components/descriptions/demo/border.md b/components/descriptions/demo/border.md
new file mode 100644
index 000000000..509fb344e
--- /dev/null
+++ b/components/descriptions/demo/border.md
@@ -0,0 +1,41 @@
+
+#### 带边框的
+
+
+
+#### border
+Descriptions with border and background color.
+
+
+```tpl
+
+
+ Cloud Database
+ Prepaid
+ YES
+ 2018-04-24 18:00:00
+
+ 2019-04-24 18:00:00
+
+
+
+
+ $80.00
+ $20.00
+ $60.00
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
+
+
+
+```
diff --git a/components/descriptions/demo/index.vue b/components/descriptions/demo/index.vue
new file mode 100644
index 000000000..a99deb91c
--- /dev/null
+++ b/components/descriptions/demo/index.vue
@@ -0,0 +1,53 @@
+
diff --git a/components/descriptions/demo/responsive.md b/components/descriptions/demo/responsive.md
new file mode 100644
index 000000000..9cff357fb
--- /dev/null
+++ b/components/descriptions/demo/responsive.md
@@ -0,0 +1,41 @@
+
+#### 响应式
+通过响应式的配置可以实现在小屏幕设备上的完美呈现。
+
+
+
+#### responsive
+通过响应式的配置可以实现在小屏幕设备上的完美呈现。
+
+
+```tpl
+
+
+
+ Cloud Database
+ Prepaid
+ 18:00:00
+ $80.00
+ $20.00
+ $60.00
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
+
+
+
+
+```
diff --git a/components/descriptions/demo/size.md b/components/descriptions/demo/size.md
new file mode 100644
index 000000000..05375dccd
--- /dev/null
+++ b/components/descriptions/demo/size.md
@@ -0,0 +1,69 @@
+
+#### 自定义尺寸
+自定义尺寸,适应在各种容器中展示。
+
+
+
+#### Custom size
+Custom sizes to fit in a variety of containers.
+
+
+```tpl
+
+
+
+ default
+ middle
+ small
+
+
+
+
+ Cloud Database
+ Prepaid
+ 18:00:00
+ $80.00
+ $20.00
+ $60.00
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
+
+
+
+
+
+ Cloud Database
+ Prepaid
+ 18:00:00
+ $80.00
+ $20.00
+ $60.00
+
+
+
+
+```
diff --git a/components/descriptions/demo/vertical-border.md b/components/descriptions/demo/vertical-border.md
new file mode 100644
index 000000000..f49ce391f
--- /dev/null
+++ b/components/descriptions/demo/vertical-border.md
@@ -0,0 +1,42 @@
+
+#### 垂直带边框的
+垂直带边框和背景颜色的列表。
+
+
+
+#### Vertical border
+Descriptions with border and background color.
+
+
+```tpl
+
+
+ Cloud Database
+ Prepaid
+ YES
+ 2018-04-24 18:00:00
+
+ 2019-04-24 18:00:00
+
+
+
+
+ $80.00
+ $20.00
+ $60.00
+
+ Data disk type: MongoDB
+
+ Database version: 3.4
+
+ Package: dds.mongo.mid
+
+ Storage space: 10 GB
+
+ Replication_factor:3
+
+ Region: East China 1
+
+
+
+```
diff --git a/components/descriptions/demo/vertical.md b/components/descriptions/demo/vertical.md
new file mode 100644
index 000000000..1f5d89caa
--- /dev/null
+++ b/components/descriptions/demo/vertical.md
@@ -0,0 +1,23 @@
+
+#### 垂直
+垂直的列表。
+
+
+
+#### Vertical
+Simplest Usage.
+
+
+```tpl
+
+
+ Zhou Maomao
+ 1810000000
+ Hangzhou, Zhejiang
+
+ No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China
+
+ empty
+
+
+```
diff --git a/components/descriptions/index.en-US.md b/components/descriptions/index.en-US.md
new file mode 100644
index 000000000..7e745e809
--- /dev/null
+++ b/components/descriptions/index.en-US.md
@@ -0,0 +1,21 @@
+## API
+
+### Descriptions props
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| title | The title of the description list, placed at the top | string \| VNode \| v-slot:title | - |
+| bordered | whether to display the border | boolean | false |
+| column | the number of `DescriptionItems` in a row,could be a number or a object like `{ xs: 8, sm: 16, md: 24}`,(Only set `bordered={true}` to take effect) | number | 3 |
+| size | set the size of the list. Can be set to `middle`,`small`, or not filled | `default | middle | small` | `default` |
+| layout | Define description layout | `horizontal | vertical` | `horizontal` |
+| colon | change default props `colon` value of `Descriptions.Item` | boolean | true |
+
+### Item props
+
+| Property | Description | Type | Default |
+| -------- | ------------------------------ | ------------------------------- | ------- |
+| label | description of the content | string \| VNode \| v-slot:label | - |
+| span | The number of columns included | number | 1 |
+
+> The number of span Descriptions.Item. span={2} takes up the width of two DescriptionsItems.
diff --git a/components/descriptions/index.jsx b/components/descriptions/index.jsx
new file mode 100644
index 000000000..39742d3b7
--- /dev/null
+++ b/components/descriptions/index.jsx
@@ -0,0 +1,264 @@
+import warning from '../_util/warning';
+import ResponsiveObserve, { responsiveArray } from '../_util/responsiveObserve';
+import { ConfigConsumerProps } from '../config-provider';
+import Col from './Col';
+import PropTypes from '../_util/vue-types';
+import {
+ initDefaultProps,
+ isValidElement,
+ getOptionProps,
+ getComponentFromProp,
+} from '../_util/props-util';
+import BaseMixin from '../_util/BaseMixin';
+import Base from '../base';
+import { cloneElement } from '../_util/vnode';
+
+export const DescriptionsItemProps = {
+ prefixCls: PropTypes.string,
+ label: PropTypes.any,
+ span: PropTypes.number,
+};
+
+function toArray(value) {
+ let ret = value;
+ if (value === undefined) {
+ ret = [];
+ } else if (!Array.isArray(value)) {
+ ret = [value];
+ }
+ return ret;
+}
+
+export const DescriptionsItem = {
+ name: 'ADescriptionsItem',
+ props: initDefaultProps(DescriptionsItemProps, { span: 1 }),
+};
+
+export const DescriptionsProps = {
+ prefixCls: PropTypes.string,
+ bordered: PropTypes.bool,
+ size: PropTypes.oneOf(['default', 'middle', 'small']).def('default'),
+ title: PropTypes.any,
+ column: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
+ layout: PropTypes.oneOf(['horizontal', 'vertical']),
+ colon: PropTypes.bool,
+};
+
+/**
+ * Convert children into `column` groups.
+ * @param children: DescriptionsItem
+ * @param column: number
+ */
+const generateChildrenRows = (children, column) => {
+ const rows = [];
+ let columns = null;
+ let leftSpans;
+
+ const itemNodes = toArray(children);
+ itemNodes.forEach((node, index) => {
+ const itemProps = getOptionProps(node);
+ let itemNode = node;
+
+ if (!columns) {
+ leftSpans = column;
+ columns = [];
+ rows.push(columns);
+ }
+
+ // Always set last span to align the end of Descriptions
+ const lastItem = index === itemNodes.length - 1;
+ let lastSpanSame = true;
+ if (lastItem) {
+ lastSpanSame = !itemProps.span || itemProps.span === leftSpans;
+ itemNode = cloneElement(itemNode, {
+ props: {
+ span: leftSpans,
+ },
+ });
+ }
+
+ // Calculate left fill span
+ const { span = 1 } = itemProps;
+ columns.push(itemNode);
+ leftSpans -= span;
+
+ if (leftSpans <= 0) {
+ columns = null;
+
+ warning(
+ leftSpans === 0 && lastSpanSame,
+ 'Descriptions: Sum of column `span` in a line exceeds `column` of Descriptions.',
+ );
+ }
+ });
+
+ return rows;
+};
+
+const defaultColumnMap = {
+ xxl: 3,
+ xl: 3,
+ lg: 3,
+ md: 3,
+ sm: 2,
+ xs: 1,
+};
+
+const Descriptions = {
+ name: 'ADescriptions',
+ Item: DescriptionsItem,
+ mixins: [BaseMixin],
+ inject: {
+ configProvider: { default: () => ConfigConsumerProps },
+ },
+ props: initDefaultProps(DescriptionsProps, {
+ column: defaultColumnMap,
+ }),
+ data() {
+ return {
+ screens: {},
+ token: undefined,
+ };
+ },
+ methods: {
+ getColumn() {
+ const { column } = this.$props;
+ if (typeof column === 'object') {
+ for (let i = 0; i < responsiveArray.length; i++) {
+ const breakpoint = responsiveArray[i];
+ if (this.screens[breakpoint] && column[breakpoint] !== undefined) {
+ return column[breakpoint] || defaultColumnMap[breakpoint];
+ }
+ }
+ }
+ // If the configuration is not an object, it is a number, return number
+ if (typeof column === 'number') {
+ return column;
+ }
+ // If it is an object, but no response is found, this happens only in the test.
+ // Maybe there are some strange environments
+ return 3;
+ },
+ renderRow(children, index, { prefixCls }, bordered, layout, colon) {
+ const renderCol = (colItem, type, idx) => {
+ return (
+
+ );
+ };
+
+ const cloneChildren = [];
+ const cloneContentChildren = [];
+ toArray(children).forEach((childrenItem, idx) => {
+ cloneChildren.push(renderCol(childrenItem, 'label', idx));
+ if (layout === 'vertical') {
+ cloneContentChildren.push(renderCol(childrenItem, 'content', idx));
+ } else if (bordered) {
+ cloneChildren.push(renderCol(childrenItem, 'content', idx));
+ }
+ });
+
+ if (layout === 'vertical') {
+ return [
+
+ {cloneChildren}
+
,
+
+ {cloneContentChildren}
+
,
+ ];
+ }
+
+ return (
+
+ {cloneChildren}
+
+ );
+ },
+ },
+ mounted() {
+ const { column } = this.$props;
+ this.token = ResponsiveObserve.subscribe(screens => {
+ if (typeof column !== 'object') {
+ return;
+ }
+ this.setState({
+ screens,
+ });
+ });
+ },
+ beforeDestory() {
+ ResponsiveObserve.unsubscribe(this.token);
+ },
+ render() {
+ const {
+ prefixCls: customizePrefixCls,
+ size,
+ bordered = false,
+ layout = 'horizontal',
+ colon = true,
+ } = this.$props;
+ const title = getComponentFromProp(this, 'title') || null;
+ const getPrefixCls = this.configProvider.getPrefixCls;
+ const prefixCls = getPrefixCls('descriptions', customizePrefixCls);
+
+ const column = this.getColumn();
+ const children = this.$slots.default;
+ const cloneChildren = toArray(children)
+ .map(child => {
+ if (isValidElement(child)) {
+ return cloneElement(child, {
+ props: {
+ prefixCls,
+ },
+ });
+ }
+ return null;
+ })
+ .filter(node => node);
+
+ const childrenArray = generateChildrenRows(cloneChildren, column);
+ return (
+
+ {title &&
{title}
}
+
+
+
+ {childrenArray.map((child, index) =>
+ this.renderRow(
+ child,
+ index,
+ {
+ prefixCls,
+ },
+ bordered,
+ layout,
+ colon,
+ ),
+ )}
+
+
+
+
+ );
+ },
+};
+
+Descriptions.install = function(Vue) {
+ Vue.use(Base);
+ Vue.component(Descriptions.name, Descriptions);
+ Vue.component(Descriptions.Item.name, Descriptions.Item);
+};
+
+export default Descriptions;
diff --git a/components/descriptions/index.zh-CN.md b/components/descriptions/index.zh-CN.md
new file mode 100644
index 000000000..85bd87ead
--- /dev/null
+++ b/components/descriptions/index.zh-CN.md
@@ -0,0 +1,21 @@
+## API
+
+### Descriptions props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| title | 描述列表的标题,显示在最顶部 | string \| VNode \| v-slot:title | - |
+| bordered | 是否展示边框 | boolean | false |
+| column | 一行的 `DescriptionItems` 数量,可以写成像素值或支持响应式的对象写法 `{ xs: 8, sm: 16, md: 24}` | number | 3 |
+| size | 设置列表的大小。可以设置为 `middle` 、`small`, 或不填(只有设置 `bordered={true}` 生效) | `default | middle | small` | `default` |
+| layout | 描述布局 | `horizontal | vertical` | `horizontal` |
+| colon | 配置 `Descriptions.Item` 的 `colon` 的默认值 | boolean | true |
+
+### Item props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| ----- | ------------ | ------------------------------- | ------ |
+| label | 内容的描述 | string \| VNode \| v-slot:label | - |
+| span | 包含列的数量 | number | 1 |
+
+> span 是 Descriptions.Item 的数量。 span={2} 会占用两个 DescriptionsItem 的宽度。
diff --git a/components/descriptions/style/index.js b/components/descriptions/style/index.js
new file mode 100644
index 000000000..3a3ab0de5
--- /dev/null
+++ b/components/descriptions/style/index.js
@@ -0,0 +1,2 @@
+import '../../style/index.less';
+import './index.less';
diff --git a/components/descriptions/style/index.less b/components/descriptions/style/index.less
new file mode 100644
index 000000000..dae1baeb7
--- /dev/null
+++ b/components/descriptions/style/index.less
@@ -0,0 +1,145 @@
+@import '../../style/themes/default';
+@import '../../style/mixins/index';
+
+@descriptions-prefix-cls: ~'@{ant-prefix}-descriptions';
+
+@descriptions-default-padding: 16px 24px;
+@descriptions-middle-padding: 12px 24px;
+@descriptions-small-padding: 8px 16px;
+
+.@{descriptions-prefix-cls} {
+ &-title {
+ margin-bottom: 20px;
+ color: @heading-color;
+ font-weight: bold;
+ font-size: @font-size-lg;
+ line-height: @line-height-base;
+ }
+
+ &-view {
+ width: 100%;
+ overflow: hidden;
+ border-radius: @border-radius-base;
+ table {
+ width: 100%;
+ table-layout: fixed;
+ }
+ }
+
+ &-row {
+ > th,
+ > td {
+ padding-bottom: 16px;
+ }
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+
+ &-item-label {
+ color: @heading-color;
+ font-weight: normal;
+ font-size: @font-size-base;
+ line-height: @line-height-base;
+ white-space: nowrap;
+
+ &::after {
+ position: relative;
+ top: -0.5px;
+ margin: 0 8px 0 2px;
+ content: ' ';
+ }
+ }
+
+ &-item-colon {
+ &::after {
+ content: ':';
+ }
+ }
+
+ &-item-no-label {
+ &::after {
+ margin: 0;
+ content: '';
+ }
+ }
+
+ &-item-content {
+ display: table-cell;
+ color: @text-color;
+ font-size: @font-size-base;
+ line-height: @line-height-base;
+ }
+
+ &-item {
+ padding-bottom: 0;
+ > span {
+ display: inline-block;
+ }
+ }
+
+ &-middle {
+ .@{descriptions-prefix-cls}-row {
+ > th,
+ > td {
+ padding-bottom: 12px;
+ }
+ }
+ }
+
+ &-small {
+ .@{descriptions-prefix-cls}-row {
+ > th,
+ > td {
+ padding-bottom: 8px;
+ }
+ }
+ }
+
+ &-bordered {
+ .@{descriptions-prefix-cls}-view {
+ border: 1px solid @border-color-split;
+ > table {
+ table-layout: auto;
+ }
+ }
+
+ .@{descriptions-prefix-cls}-item-label,
+ .@{descriptions-prefix-cls}-item-content {
+ padding: @descriptions-default-padding;
+ border-right: 1px solid @border-color-split;
+
+ &:last-child {
+ border-right: none;
+ }
+ }
+
+ .@{descriptions-prefix-cls}-item-label {
+ background-color: @descriptions-bg;
+ &::after {
+ display: none;
+ }
+ }
+
+ .@{descriptions-prefix-cls}-row {
+ border-bottom: 1px solid @border-color-split;
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+
+ &.@{descriptions-prefix-cls}-middle {
+ .@{descriptions-prefix-cls}-item-label,
+ .@{descriptions-prefix-cls}-item-content {
+ padding: @descriptions-middle-padding;
+ }
+ }
+
+ &.@{descriptions-prefix-cls}-small {
+ .@{descriptions-prefix-cls}-item-label,
+ .@{descriptions-prefix-cls}-item-content {
+ padding: @descriptions-small-padding;
+ }
+ }
+ }
+}
diff --git a/components/index.js b/components/index.js
index cfb3b4429..ce0b3e234 100644
--- a/components/index.js
+++ b/components/index.js
@@ -139,6 +139,8 @@ import { default as Empty } from './empty';
import { default as Result } from './result';
+import { default as Descriptions } from './descriptions';
+
const components = [
Base,
Affix,
@@ -198,6 +200,7 @@ const components = [
ConfigProvider,
Empty,
Result,
+ Descriptions,
];
const install = function(Vue) {
@@ -283,6 +286,7 @@ export {
ConfigProvider,
Empty,
Result,
+ Descriptions,
};
export default {
diff --git a/components/style.js b/components/style.js
index f3903fd01..2dd79c5ff 100644
--- a/components/style.js
+++ b/components/style.js
@@ -56,3 +56,4 @@ import './config-provider/style';
import './empty/style';
import './statistic/style';
import './result/style';
+import './descriptions/style';
diff --git a/components/style/themes/default.less b/components/style/themes/default.less
index 07d809d16..5100de991 100644
--- a/components/style/themes/default.less
+++ b/components/style/themes/default.less
@@ -174,6 +174,9 @@
@checkbox-check-color: #fff;
@checkbox-border-width: @border-width-base;
+// Descriptions
+@descriptions-bg: #fafafa;
+
// Empty
@empty-font-size: @font-size-base;
diff --git a/site/components.js b/site/components.js
index 5fd855ddf..e0ca62f30 100644
--- a/site/components.js
+++ b/site/components.js
@@ -62,6 +62,7 @@ import {
Empty,
Base,
Result,
+ Descriptions,
} from 'ant-design-vue';
Vue.prototype.$message = message;
@@ -132,6 +133,7 @@ Vue.use(Comment);
Vue.use(ConfigProvider);
Vue.use(Empty);
Vue.use(Result);
+Vue.use(Descriptions);
/* v1.1.2 registration methods */
// Vue.component(Affix.name, Affix) // a-affix
diff --git a/site/demo.js b/site/demo.js
index 636ccf3bb..69a3029fd 100644
--- a/site/demo.js
+++ b/site/demo.js
@@ -364,4 +364,10 @@ export default {
type: 'Data Display',
title: 'Statistic',
},
+ descriptions: {
+ category: 'Components',
+ subtitle: '描述列表',
+ type: 'Data Display',
+ title: 'Descriptions'
+ }
};
diff --git a/site/demoRoutes.js b/site/demoRoutes.js
index 5aa0e8445..74e3b496d 100644
--- a/site/demoRoutes.js
+++ b/site/demoRoutes.js
@@ -463,4 +463,12 @@ export default [
path: 'result-cn',
component: () => import('../components/result/demo/index.vue'),
},
+ {
+ path: 'descriptions',
+ component: () => import('../components/descriptions/demo/index.vue'),
+ },
+ {
+ path: 'descriptions-cn',
+ component: () => import('../components/descriptions/demo/index.vue'),
+ },
];
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index b39917af9..8329ddeef 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -63,6 +63,7 @@ Array [
"Comment",
"ConfigProvider",
"Empty",
+ "Descriptions",
"default",
]
`;
diff --git a/types/ant-design-vue.d.ts b/types/ant-design-vue.d.ts
index 5b3deaf84..927bb40ea 100644
--- a/types/ant-design-vue.d.ts
+++ b/types/ant-design-vue.d.ts
@@ -62,6 +62,7 @@ import { Timeline } from './timeline/timeline';
import { Tooltip } from './tootip/tooltip';
import { Upload } from './upload';
import { Result } from './result';
+import { Descriptions } from './descriptions/descriptions'
/**
* Install all ant-design-vue components into Vue.
@@ -131,4 +132,5 @@ export {
Drawer,
Skeleton,
Result,
+ Descriptions,
};
diff --git a/types/descriptions/descriptions-item.d.ts b/types/descriptions/descriptions-item.d.ts
new file mode 100644
index 000000000..55d0eec28
--- /dev/null
+++ b/types/descriptions/descriptions-item.d.ts
@@ -0,0 +1,20 @@
+// Project: https://github.com/vueComponent/ant-design-vue
+// Definitions by: akki-jat
+// Definitions: https://github.com/vueComponent/ant-design-vue/types
+
+import { AntdComponent } from '../component';
+
+export declare class DescriptionsItem extends AntdComponent {
+ /**
+ * the label of descriptions item
+ * @type any
+ */
+ label: any;
+
+ /**
+ * can be set to small large or omitted
+ * @default 1
+ * @type number
+ */
+ span: number;
+}
diff --git a/types/descriptions/descriptions.d.ts b/types/descriptions/descriptions.d.ts
new file mode 100644
index 000000000..0c98966e1
--- /dev/null
+++ b/types/descriptions/descriptions.d.ts
@@ -0,0 +1,57 @@
+// Project: https://github.com/vueComponent/ant-design-vue
+// Definitions by: akki-jat
+// Definitions: https://github.com/vueComponent/ant-design-vue/types
+
+import { AntdComponent } from '../component';
+import { DescriptionsItem } from './descriptions-item';
+
+export declare class Descriptions extends AntdComponent {
+ static Item: typeof DescriptionsItem;
+
+ /**
+ * descriptions size type
+ * @default 'default'
+ * @type string
+ */
+ size: 'default' | 'middle' | 'small';
+
+ /**
+ * custom prefixCls
+ * @type string
+ */
+ prefixCls: string;
+
+ /**
+ * whether descriptions have border
+ * @default false
+ * @type boolean
+ */
+ bordered: boolean;
+
+ /**
+ * custom title
+ * @type any
+ */
+ title: any;
+
+ /**
+ * the number of descriptionsitem in one line
+ * @default 3
+ * @type number | object
+ */
+ column: number | object;
+
+ /**
+ * descriptions layout
+ * @default 'horizontal'
+ * @type string
+ */
+ layout: 'horizontal' | 'vertical';
+
+ /**
+ * whether have colon in descriptionsitem
+ * @default true
+ * @type boolean
+ */
+ colon: boolean;
+}
\ No newline at end of file