diff --git a/components/index.js b/components/index.js
index 1374a81f9..79d18c6c5 100644
--- a/components/index.js
+++ b/components/index.js
@@ -122,6 +122,8 @@ import { default as version } from './version'
import { default as Drawer } from './drawer'
+import { default as Skeleton } from './skeleton'
+
const components = [
Affix,
Anchor,
@@ -174,6 +176,7 @@ const components = [
Tooltip,
Upload,
Drawer,
+ Skeleton,
]
const install = function (Vue) {
@@ -251,6 +254,7 @@ export {
Tooltip,
Upload,
Drawer,
+ Skeleton,
}
export default {
diff --git a/components/skeleton/Avatar.jsx b/components/skeleton/Avatar.jsx
new file mode 100644
index 000000000..c6ae2d518
--- /dev/null
+++ b/components/skeleton/Avatar.jsx
@@ -0,0 +1,37 @@
+import classNames from 'classnames'
+import PropTypes from '../_util/vue-types'
+import { initDefaultProps } from '../_util/props-util'
+
+const skeletonAvatarProps = {
+ prefixCls: PropTypes.string,
+ size: PropTypes.oneOf(['large', 'small', 'default']),
+ shape: PropTypes.oneOf(['circle', 'square']),
+}
+
+export const SkeletonAvatarProps = PropTypes.shape(skeletonAvatarProps).loose
+
+const Avatar = {
+ props: initDefaultProps(skeletonAvatarProps, {
+ prefixCls: 'ant-skeleton-avatar',
+ size: 'large',
+ }),
+ render () {
+ const { prefixCls, size, shape } = this.$props
+
+ const sizeCls = classNames({
+ [`${prefixCls}-lg`]: size === 'large',
+ [`${prefixCls}-sm`]: size === 'small',
+ })
+
+ const shapeCls = classNames({
+ [`${prefixCls}-circle`]: shape === 'circle',
+ [`${prefixCls}-square`]: shape === 'square',
+ })
+
+ return (
+
+ )
+ },
+}
+
+export default Avatar
diff --git a/components/skeleton/Paragraph.jsx b/components/skeleton/Paragraph.jsx
new file mode 100644
index 000000000..cd3032a86
--- /dev/null
+++ b/components/skeleton/Paragraph.jsx
@@ -0,0 +1,53 @@
+import PropTypes from '../_util/vue-types'
+import { initDefaultProps } from '../_util/props-util'
+
+const widthUnit = PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.string,
+])
+
+const skeletonParagraphProps = {
+ prefixCls: PropTypes.string,
+ width: PropTypes.oneOfType([
+ widthUnit,
+ PropTypes.arrayOf(widthUnit),
+ ]),
+ rows: PropTypes.number,
+}
+
+export const SkeletonParagraphProps = PropTypes.shape(skeletonParagraphProps)
+
+const Paragraph = {
+ props: initDefaultProps(skeletonParagraphProps, {
+ prefixCls: 'ant-skeleton-paragraph',
+ }),
+ methods: {
+ getWidth (index) {
+ const { width, rows = 2 } = this
+ if (Array.isArray(width)) {
+ return width[index]
+ }
+ // last paragraph
+ if (rows - 1 === index) {
+ return width
+ }
+ return undefined
+ },
+ },
+ render () {
+ const { prefixCls, rows } = this.$props
+ const rowList = [...Array(rows)].map((_, index) => {
+ const width = this.getWidth(index)
+ return
+ })
+ return (
+
+ )
+ },
+}
+
+export default Paragraph
diff --git a/components/skeleton/Title.jsx b/components/skeleton/Title.jsx
new file mode 100644
index 000000000..5c0520a3e
--- /dev/null
+++ b/components/skeleton/Title.jsx
@@ -0,0 +1,30 @@
+import PropTypes from '../_util/vue-types'
+import { initDefaultProps } from '../_util/props-util'
+
+const skeletonTitleProps = {
+ prefixCls: PropTypes.string,
+ width: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.string,
+ ]),
+}
+
+export const SkeletonTitleProps = PropTypes.shape(skeletonTitleProps)
+
+const Title = {
+ props: initDefaultProps(skeletonTitleProps, {
+ prefixCls: 'ant-skeleton-title',
+ }),
+ render () {
+ const { prefixCls, width } = this.$props
+ const zWidth = typeof width === 'number' ? `${width}px` : width
+ return (
+
+ )
+ },
+}
+
+export default Title
diff --git a/components/skeleton/__tests__/__snapshots__/demo.test.js.snap b/components/skeleton/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..92240169d
--- /dev/null
+++ b/components/skeleton/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,104 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/skeleton/demo/active.md correctly 1`] = `
+
+`;
+
+exports[`renders ./components/skeleton/demo/basic.md correctly 1`] = `
+
+`;
+
+exports[`renders ./components/skeleton/demo/children.md correctly 1`] = `
+
+
+
Ant Design Vue, a design language
+
We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
+
Show Skeleton
+
+`;
+
+exports[`renders ./components/skeleton/demo/complex.md correctly 1`] = `
+
+`;
+
+exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
+
+`;
diff --git a/components/skeleton/__tests__/__snapshots__/index.test.js.snap b/components/skeleton/__tests__/__snapshots__/index.test.js.snap
new file mode 100644
index 000000000..df16afe2e
--- /dev/null
+++ b/components/skeleton/__tests__/__snapshots__/index.test.js.snap
@@ -0,0 +1,120 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Skeleton avatar shape 1`] = `
+
+`;
+
+exports[`Skeleton avatar shape 2`] = `
+
+`;
+
+exports[`Skeleton avatar size 1`] = `
+
+`;
+
+exports[`Skeleton avatar size 2`] = `
+
+`;
+
+exports[`Skeleton avatar size 3`] = `
+
+`;
+
+exports[`Skeleton paragraph rows 1`] = `
+
+`;
+
+exports[`Skeleton paragraph width 1`] = `
+
+`;
+
+exports[`Skeleton paragraph width 2`] = `
+
+`;
+
+exports[`Skeleton title width 1`] = `
+
+`;
diff --git a/components/skeleton/__tests__/demo.test.js b/components/skeleton/__tests__/demo.test.js
new file mode 100644
index 000000000..6456f6f69
--- /dev/null
+++ b/components/skeleton/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest'
+
+demoTest('skeleton')
diff --git a/components/skeleton/__tests__/index.test.js b/components/skeleton/__tests__/index.test.js
new file mode 100644
index 000000000..bc10c398b
--- /dev/null
+++ b/components/skeleton/__tests__/index.test.js
@@ -0,0 +1,82 @@
+import { mount } from '@vue/test-utils'
+import { asyncExpect } from '@/tests/utils'
+import Skeleton from '..'
+
+describe('Skeleton', () => {
+ const genSkeleton = props => {
+ const skeletonProps = {
+ propsData: {
+ loading: true,
+ ...props,
+ },
+ slots: {
+ default: 'Bamboo',
+ },
+ sync: false,
+ }
+ return mount(Skeleton, skeletonProps)
+ }
+
+ describe('avatar', () => {
+ it('size', async () => {
+ const wrapperSmall = genSkeleton({ avatar: { size: 'small' }})
+ await asyncExpect(() => {
+ expect(wrapperSmall.html()).toMatchSnapshot()
+ })
+
+ const wrapperDefault = genSkeleton({ avatar: { size: 'default' }})
+
+ await asyncExpect(() => {
+ expect(wrapperDefault.html()).toMatchSnapshot()
+ })
+
+ const wrapperLarge = genSkeleton({ avatar: { size: 'large' }})
+
+ await asyncExpect(() => {
+ expect(wrapperLarge.html()).toMatchSnapshot()
+ })
+ })
+
+ it('shape', async () => {
+ const wrapperCircle = genSkeleton({ avatar: { shape: 'circle' }})
+ await asyncExpect(() => {
+ expect(wrapperCircle.html()).toMatchSnapshot()
+ })
+
+ const wrapperSquare = genSkeleton({ avatar: { shape: 'square' }})
+ await asyncExpect(() => {
+ expect(wrapperSquare.html()).toMatchSnapshot()
+ })
+ })
+ })
+
+ describe('title', () => {
+ it('width', async () => {
+ const wrapper = genSkeleton({ title: { width: '93%' }})
+ await asyncExpect(() => {
+ expect(wrapper.html()).toMatchSnapshot()
+ })
+ })
+ })
+
+ describe('paragraph', () => {
+ it('rows', async () => {
+ const wrapper = genSkeleton({ paragraph: { rows: 5 }})
+ await asyncExpect(() => {
+ expect(wrapper.html()).toMatchSnapshot()
+ })
+ })
+
+ it('width', async () => {
+ const wrapperPure = genSkeleton({ paragraph: { width: '93%' }})
+ await asyncExpect(() => {
+ expect(wrapperPure.html()).toMatchSnapshot()
+ })
+
+ const wrapperList = genSkeleton({ paragraph: { width: ['28%', '93%'] }})
+ await asyncExpect(() => {
+ expect(wrapperList.html()).toMatchSnapshot()
+ })
+ })
+ })
+})
diff --git a/components/skeleton/demo/active.md b/components/skeleton/demo/active.md
new file mode 100644
index 000000000..6dce2df1f
--- /dev/null
+++ b/components/skeleton/demo/active.md
@@ -0,0 +1,16 @@
+
+#### 动画效果
+显示动画效果。
+
+
+
+#### Active Animation
+Display active animation.
+
+
+```html
+
+
+
+```
+
diff --git a/components/skeleton/demo/basic.md b/components/skeleton/demo/basic.md
new file mode 100644
index 000000000..3fdbf37ad
--- /dev/null
+++ b/components/skeleton/demo/basic.md
@@ -0,0 +1,15 @@
+
+#### 基本
+最简单的占位效果。
+
+
+
+#### Basic
+Simplest Skeleton usage.
+
+
+```html
+
+
+
+```
diff --git a/components/skeleton/demo/children.md b/components/skeleton/demo/children.md
new file mode 100644
index 000000000..0da5b60ef
--- /dev/null
+++ b/components/skeleton/demo/children.md
@@ -0,0 +1,51 @@
+
+#### 包含子组件
+加载占位图包含子组件。
+
+
+
+#### Contains sub component
+Skeleton contains sub component.
+
+
+```html
+
+
+
+
+
Ant Design Vue, a design language
+
We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
+
+
+
+ Show Skeleton
+
+
+
+
+
+```
+
diff --git a/components/skeleton/demo/complex.md b/components/skeleton/demo/complex.md
new file mode 100644
index 000000000..39c86ec85
--- /dev/null
+++ b/components/skeleton/demo/complex.md
@@ -0,0 +1,16 @@
+
+#### 复杂的组合
+更复杂的组合。
+
+
+
+#### Complex combination
+Complex combination with avatar and multiple paragraphs.
+
+
+```html
+
+
+
+```
+
diff --git a/components/skeleton/demo/index.vue b/components/skeleton/demo/index.vue
new file mode 100644
index 000000000..718372dc6
--- /dev/null
+++ b/components/skeleton/demo/index.vue
@@ -0,0 +1,62 @@
+
diff --git a/components/skeleton/demo/list.md b/components/skeleton/demo/list.md
new file mode 100644
index 000000000..e48f621b5
--- /dev/null
+++ b/components/skeleton/demo/list.md
@@ -0,0 +1,79 @@
+
+#### 列表
+在列表组件中使用加载占位符。
+
+
+
+#### List
+Use skeleton in list component.
+
+
+```html
+
+
+
+
+
+
+
+
+
+ {{text}}
+
+
+
+
+
+ {{item.title}}
+
+
+ {{item.content}}
+
+
+
+
+
+
+
+```
+
+
diff --git a/components/skeleton/index.en-US.md b/components/skeleton/index.en-US.md
new file mode 100644
index 000000000..c63d6da24
--- /dev/null
+++ b/components/skeleton/index.en-US.md
@@ -0,0 +1,31 @@
+## API
+
+### Skeleton
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| active | Show animation effect | boolean | false |
+| avatar | Show avatar placeholder | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
+| loading | Display the skeleton when `true` | boolean | - |
+| paragraph | Show paragraph placeholder | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
+| title | Show title placeholder | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
+
+### SkeletonAvatarProps
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| size | Set the size of avatar | Enum{ 'large', 'small', 'default' } | - |
+| shape | Set the shape of avatar | Enum{ 'circle', 'square' } | - |
+
+### SkeletonTitleProps
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| width | Set the width of title | number \| string | - |
+
+### SkeletonParagraphProps
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| rows | Set the row count of paragraph | number | - |
+| width | Set the width of paragraph. When width is an Array, it can set the width of each row. Otherwise only set the last row width | number \| string \| Array | - |
diff --git a/components/skeleton/index.jsx b/components/skeleton/index.jsx
new file mode 100644
index 000000000..8330a4244
--- /dev/null
+++ b/components/skeleton/index.jsx
@@ -0,0 +1,171 @@
+import classNames from 'classnames'
+import PropTypes from '../_util/vue-types'
+import { initDefaultProps, hasProp } from '../_util/props-util'
+import Avatar, { SkeletonAvatarProps } from './Avatar'
+import Title, { SkeletonTitleProps } from './Title'
+import Paragraph, { SkeletonParagraphProps } from './Paragraph'
+
+export const SkeletonProps = {
+ active: PropTypes.bool,
+ loading: PropTypes.bool,
+ prefixCls: PropTypes.string,
+ children: PropTypes.any,
+ avatar: PropTypes.oneOfType([
+ PropTypes.string,
+ SkeletonAvatarProps,
+ PropTypes.bool,
+ ]),
+ title: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.string,
+ SkeletonTitleProps,
+ ]),
+ paragraph: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.string,
+ SkeletonParagraphProps,
+ ]),
+}
+
+function getComponentProps (prop) {
+ if (prop && typeof prop === 'object') {
+ return prop
+ }
+ return {}
+}
+
+function getAvatarBasicProps (hasTitle, hasParagraph) {
+ if (hasTitle && !hasParagraph) {
+ return { shape: 'square' }
+ }
+
+ return { shape: 'circle' }
+}
+
+function getTitleBasicProps (hasAvatar, hasParagraph) {
+ if (!hasAvatar && hasParagraph) {
+ return { width: '38%' }
+ }
+
+ if (hasAvatar && hasParagraph) {
+ return { width: '50%' }
+ }
+
+ return {}
+}
+
+function getParagraphBasicProps (hasAvatar, hasTitle) {
+ const basicProps = {}
+
+ // Width
+ if (!hasAvatar || !hasTitle) {
+ basicProps.width = '61%'
+ }
+
+ // Rows
+ if (!hasAvatar && hasTitle) {
+ basicProps.rows = 3
+ } else {
+ basicProps.rows = 2
+ }
+
+ return basicProps
+}
+
+const Skeleton = {
+ name: 'ASkeleton',
+ props: initDefaultProps(SkeletonProps, {
+ prefixCls: 'ant-skeleton',
+ avatar: false,
+ title: true,
+ paragraph: true,
+ }),
+ render () {
+ const {
+ loading, prefixCls,
+ avatar, title, paragraph, active,
+ } = this.$props
+ if (loading || !hasProp(this, 'loading')) {
+ const hasAvatar = !!avatar || avatar === ''
+ const hasTitle = !!title
+ const hasParagraph = !!paragraph
+
+ // Avatar
+ let avatarNode
+ if (hasAvatar) {
+ const avatarProps = {
+ props: {
+ ...getAvatarBasicProps(hasTitle, hasParagraph),
+ ...getComponentProps(avatar),
+ },
+ }
+
+ avatarNode = (
+
+ )
+ }
+
+ let contentNode
+ if (hasTitle || hasParagraph) {
+ // Title
+ let $title
+ if (hasTitle) {
+ const titleProps = {
+ props: {
+ ...getTitleBasicProps(hasAvatar, hasParagraph),
+ ...getComponentProps(title),
+ },
+ }
+
+ $title = (
+
+ )
+ }
+
+ // Paragraph
+ let paragraphNode
+ if (hasParagraph) {
+ const paragraphProps = {
+ props: {
+ ...getParagraphBasicProps(hasAvatar, hasTitle),
+ ...getComponentProps(paragraph),
+ },
+ }
+
+ paragraphNode = (
+
+ )
+ }
+
+ contentNode = (
+
+ {$title}
+ {paragraphNode}
+
+ )
+ }
+
+ const cls = classNames(
+ prefixCls, {
+ [`${prefixCls}-with-avatar`]: hasAvatar,
+ [`${prefixCls}-active`]: active,
+ },
+ )
+
+ return (
+
+ {avatarNode}
+ {contentNode}
+
+ )
+ }
+ return this.$slots.default && this.$slots.default[0]
+ },
+}
+/* istanbul ignore next */
+Skeleton.install = function (Vue) {
+ Vue.component(Skeleton.name, Skeleton)
+}
+export default Skeleton
diff --git a/components/skeleton/index.zh-CN.md b/components/skeleton/index.zh-CN.md
new file mode 100644
index 000000000..57bf22230
--- /dev/null
+++ b/components/skeleton/index.zh-CN.md
@@ -0,0 +1,31 @@
+## API
+
+### Skeleton
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| active | 是否展示动画效果 | boolean | false |
+| avatar | 是否显示头像占位图 | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
+| loading | 为 `true` 时,显示占位图。反之则直接展示子组件 | boolean | - |
+| paragraph | 是否显示段落占位图 | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
+| title | 是否显示标题占位图 | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
+
+### SkeletonAvatarProps
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| size | 设置头像占位图的大小 | Enum{ 'large', 'small', 'default' } | - |
+| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | - |
+
+### SkeletonTitleProps
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| width | 设置标题占位图的宽度 | number \| string | - |
+
+### SkeletonParagraphProps
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| rows | 设置段落占位图的行数 | number | - |
+| width | 设置段落占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度 | number \| string \| Array | - |
diff --git a/components/skeleton/style/index.js b/components/skeleton/style/index.js
new file mode 100644
index 000000000..3a3ab0de5
--- /dev/null
+++ b/components/skeleton/style/index.js
@@ -0,0 +1,2 @@
+import '../../style/index.less';
+import './index.less';
diff --git a/components/skeleton/style/index.less b/components/skeleton/style/index.less
new file mode 100644
index 000000000..1d5054c45
--- /dev/null
+++ b/components/skeleton/style/index.less
@@ -0,0 +1,123 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+@skeleton-prefix-cls: ~"@{ant-prefix}-skeleton";
+@skeleton-avatar-prefix-cls: ~"@{skeleton-prefix-cls}-avatar";
+@skeleton-title-prefix-cls: ~"@{skeleton-prefix-cls}-title";
+@skeleton-paragraph-prefix-cls: ~"@{skeleton-prefix-cls}-paragraph";
+
+@skeleton-to-color: shade(@skeleton-color, 5%);
+
+.@{skeleton-prefix-cls} {
+ display: table;
+ width: 100%;
+
+ &-header {
+ display: table-cell;
+ vertical-align: top;
+ padding-right: 16px;
+
+ // Avatar
+ .@{skeleton-avatar-prefix-cls} {
+ display: inline-block;
+ vertical-align: top;
+ background: @skeleton-color;
+
+ .avatar-size(@avatar-size-base);
+
+ &-lg {
+ .avatar-size(@avatar-size-lg);
+ }
+
+ &-sm {
+ .avatar-size(@avatar-size-sm);
+ }
+ }
+ }
+
+ &-content {
+ display: table-cell;
+ vertical-align: top;
+ width: 100%;
+
+ // Title
+ .@{skeleton-title-prefix-cls} {
+ margin-top: 16px;
+ height: 16px;
+ width: 100%;
+ background: @skeleton-color;
+
+ + .@{skeleton-paragraph-prefix-cls} {
+ margin-top: 24px;
+ }
+ }
+
+ // paragraph
+ .@{skeleton-paragraph-prefix-cls} {
+ > li {
+ height: 16px;
+ background: @skeleton-color;
+ list-style: none;
+ width: 100%;
+
+ &:last-child:not(:first-child):not(:nth-child(2)) {
+ width: 61%;
+ }
+
+ + li {
+ margin-top: 16px;
+ }
+ }
+ }
+ }
+
+ &-with-avatar &-content {
+ // Title
+ .@{skeleton-title-prefix-cls} {
+ margin-top: 12px;
+
+ + .@{skeleton-paragraph-prefix-cls} {
+ margin-top: 28px;
+ }
+ }
+ }
+
+ // With active animation
+ &.@{skeleton-prefix-cls}-active {
+ & .@{skeleton-prefix-cls}-content {
+ .@{skeleton-title-prefix-cls},
+ .@{skeleton-paragraph-prefix-cls} > li {
+ .skeleton-color();
+ }
+ }
+
+ .@{skeleton-avatar-prefix-cls} {
+ .skeleton-color();
+ }
+ }
+}
+
+.avatar-size(@size) {
+ width: @size;
+ height: @size;
+ line-height: @size;
+
+ &.@{skeleton-avatar-prefix-cls}-circle {
+ border-radius: 50%;
+ }
+}
+
+.skeleton-color() {
+ background: linear-gradient(90deg, @skeleton-color 25%, @skeleton-to-color 37%, @skeleton-color 63%);
+ animation: ~"@{skeleton-prefix-cls}-loading" 1.4s ease infinite;
+ background-size: 400% 100%;
+}
+
+@keyframes ~"@{skeleton-prefix-cls}-loading" {
+ 0% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0 50%;
+ }
+}
diff --git a/components/style.js b/components/style.js
index 9d68364d9..4b4bbda16 100644
--- a/components/style.js
+++ b/components/style.js
@@ -50,3 +50,4 @@ import './list/style'
import './carousel/style'
import './tree-select/style'
import './drawer/style'
+import './skeleton/style'
diff --git a/components/style/themes/default.less b/components/style/themes/default.less
index 7de75b370..a5a6fc9e8 100644
--- a/components/style/themes/default.less
+++ b/components/style/themes/default.less
@@ -361,6 +361,11 @@
@tag-default-color: @text-color;
@tag-font-size: @font-size-sm;
+// Skeleton
+// ---
+@skeleton-color: #f2f2f2;
+
+
// TimePicker
// ---
@time-picker-panel-column-width: 56px;
diff --git a/site/components.js b/site/components.js
index 3537ef9c6..54b59717e 100644
--- a/site/components.js
+++ b/site/components.js
@@ -55,6 +55,7 @@ import {
Upload,
// version,
Drawer,
+ Skeleton,
} from 'ant-design-vue'
Vue.prototype.$message = message
@@ -117,6 +118,7 @@ Vue.use(TimePicker)
Vue.use(Timeline)
Vue.use(Tooltip)
Vue.use(Upload)
+Vue.use(Skeleton)
/* v1.1.2 registration methods */
// Vue.component(Affix.name, Affix) // a-affix