From aab12a4906e5ff32da47ab575d1c9e37d6055b6f Mon Sep 17 00:00:00 2001
From: chenjiahan <chenjiahan@youzan.com>
Date: Mon, 6 Jul 2020 15:28:43 +0800
Subject: [PATCH] feat: Empty component

---
 src-next/cell/demo/index.vue                  |   8 +-
 src-next/empty/Network.js                     | 125 ++++++++++++++++++
 src-next/empty/README.md                      |  81 ++++++++++++
 src-next/empty/README.zh-CN.md                |  90 +++++++++++++
 src-next/empty/demo/index.vue                 |  88 ++++++++++++
 src-next/empty/index.js                       |  70 ++++++++++
 src-next/empty/index.less                     |  32 +++++
 .../test/__snapshots__/demo.spec.js.snap      |  55 ++++++++
 .../test/__snapshots__/index.spec.js.snap     |  83 ++++++++++++
 src-next/empty/test/demo.spec.js              |   4 +
 src-next/empty/test/index.spec.js             |  42 ++++++
 vant.config.js                                |  16 +--
 12 files changed, 679 insertions(+), 15 deletions(-)
 create mode 100644 src-next/empty/Network.js
 create mode 100644 src-next/empty/README.md
 create mode 100644 src-next/empty/README.zh-CN.md
 create mode 100644 src-next/empty/demo/index.vue
 create mode 100644 src-next/empty/index.js
 create mode 100644 src-next/empty/index.less
 create mode 100644 src-next/empty/test/__snapshots__/demo.spec.js.snap
 create mode 100644 src-next/empty/test/__snapshots__/index.spec.js.snap
 create mode 100644 src-next/empty/test/demo.spec.js
 create mode 100644 src-next/empty/test/index.spec.js

diff --git a/src-next/cell/demo/index.vue b/src-next/cell/demo/index.vue
index d8b0d46ac..f7b47398a 100644
--- a/src-next/cell/demo/index.vue
+++ b/src-next/cell/demo/index.vue
@@ -2,7 +2,7 @@
   <demo-section>
     <demo-block :title="t('basicUsage')">
       <van-cell-group>
-        <van-cell :title="t('cell')" :value="t('content')" @click="onClick" />
+        <van-cell :title="t('cell')" :value="t('content')" />
         <van-cell :title="t('cell')" :value="t('content')" :label="t('desc')" />
       </van-cell-group>
     </demo-block>
@@ -108,12 +108,6 @@ export default {
       verticalCenter: 'Vertical center',
     },
   },
-
-  methods: {
-    onClick() {
-      console.log('click');
-    },
-  },
 };
 </script>
 
diff --git a/src-next/empty/Network.js b/src-next/empty/Network.js
new file mode 100644
index 000000000..5fd851910
--- /dev/null
+++ b/src-next/empty/Network.js
@@ -0,0 +1,125 @@
+export default {
+  render() {
+    const genStop = (color, offset, opacity) => (
+      <stop stop-color={color} offset={`${offset}%`} stop-opacity={opacity} />
+    );
+
+    return (
+      <svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
+        <defs>
+          <linearGradient id="c" x1="64.022%" y1="100%" x2="64.022%" y2="0%">
+            {genStop('#FFF', 0, 0.5)}
+            {genStop('#F2F3F5', 100)}
+          </linearGradient>
+          <linearGradient id="d" x1="64.022%" y1="96.956%" x2="64.022%" y2="0%">
+            {genStop('#F2F3F5', 0, 0.3)}
+            {genStop('#F2F3F5', 100)}
+          </linearGradient>
+          <linearGradient id="h" x1="50%" y1="0%" x2="50%" y2="84.459%">
+            {genStop('#EBEDF0', 0)}
+            {genStop('#DCDEE0', 100, 0)}
+          </linearGradient>
+          <linearGradient id="i" x1="100%" y1="0%" x2="100%" y2="100%">
+            {genStop('#EAEDF0', 0)}
+            {genStop('#DCDEE0', 100)}
+          </linearGradient>
+          <linearGradient id="k" x1="100%" y1="100%" x2="100%" y2="0%">
+            {genStop('#EAEDF0', 0)}
+            {genStop('#DCDEE0', 100)}
+          </linearGradient>
+          <linearGradient id="m" x1="0%" y1="43.982%" x2="100%" y2="54.703%">
+            {genStop('#EAEDF0', 0)}
+            {genStop('#DCDEE0', 100)}
+          </linearGradient>
+          <linearGradient
+            id="n"
+            x1="94.535%"
+            y1="43.837%"
+            x2="5.465%"
+            y2="54.948%"
+          >
+            {genStop('#EAEDF0', 0)}
+            {genStop('#DCDEE0', 100)}
+          </linearGradient>
+          <radialGradient
+            id="g"
+            cx="50%"
+            cy="0%"
+            fx="50%"
+            fy="0%"
+            r="100%"
+            gradientTransform="matrix(0 1 -.54835 0 .5 -.5)"
+          >
+            {genStop('#EBEDF0', 0)}
+            {genStop('#FFF', 100, 0)}
+          </radialGradient>
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <g opacity=".8">
+            <path
+              d="M0 124V46h20v20h14v58H0z"
+              fill="url(#c)"
+              transform="matrix(-1 0 0 1 36 7)"
+            />
+            <path
+              d="M40.5 5a8.504 8.504 0 018.13 6.009l.12-.005L49 11a8 8 0 11-1 15.938V27H34v-.174a6.5 6.5 0 11-1.985-12.808A8.5 8.5 0 0140.5 5z"
+              fill="url(#d)"
+              transform="translate(2 7)"
+            />
+            <path
+              d="M96.016 0a4.108 4.108 0 013.934 2.868l.179-.004c2.138 0 3.871 1.71 3.871 3.818 0 2.109-1.733 3.818-3.871 3.818-.164 0-.325-.01-.484-.03v.03h-6.774v-.083a3.196 3.196 0 01-.726.083C90.408 10.5 89 9.111 89 7.398c0-1.636 1.284-2.976 2.911-3.094a3.555 3.555 0 01-.008-.247c0-2.24 1.842-4.057 4.113-4.057z"
+              fill="url(#d)"
+              transform="translate(2 7)"
+            />
+            <path
+              d="M121 8h22.231v14H152v77.37h-31V8z"
+              fill="url(#c)"
+              transform="translate(2 7)"
+            />
+          </g>
+          <path fill="url(#g)" d="M0 139h160v21H0z" />
+          <path
+            d="M37 18a7 7 0 013 13.326v26.742c0 1.23-.997 2.227-2.227 2.227h-1.546A2.227 2.227 0 0134 58.068V31.326A7 7 0 0137 18z"
+            fill="url(#h)"
+            fill-rule="nonzero"
+            transform="translate(43 36)"
+          />
+          <g opacity=".6" stroke-linecap="round" stroke-width="7">
+            <path
+              d="M20.875 11.136a18.868 18.868 0 00-5.284 13.121c0 5.094 2.012 9.718 5.284 13.12"
+              stroke="url(#i)"
+              transform="translate(43 36)"
+            />
+            <path
+              d="M9.849 0C3.756 6.225 0 14.747 0 24.146c0 9.398 3.756 17.92 9.849 24.145"
+              stroke="url(#i)"
+              transform="translate(43 36)"
+            />
+            <path
+              d="M57.625 11.136a18.868 18.868 0 00-5.284 13.121c0 5.094 2.012 9.718 5.284 13.12"
+              stroke="url(#k)"
+              transform="rotate(-180 76.483 42.257)"
+            />
+            <path
+              d="M73.216 0c-6.093 6.225-9.849 14.747-9.849 24.146 0 9.398 3.756 17.92 9.849 24.145"
+              stroke="url(#k)"
+              transform="rotate(-180 89.791 42.146)"
+            />
+          </g>
+          <g transform="translate(31 105)" fill-rule="nonzero">
+            <rect fill="url(#m)" width="98" height="34" rx="2" />
+            <rect fill="#FFF" x="9" y="8" width="80" height="18" rx="1.114" />
+            <rect
+              fill="url(#n)"
+              x="15"
+              y="12"
+              width="18"
+              height="6"
+              rx="1.114"
+            />
+          </g>
+        </g>
+      </svg>
+    );
+  },
+};
diff --git a/src-next/empty/README.md b/src-next/empty/README.md
new file mode 100644
index 000000000..8b13ced49
--- /dev/null
+++ b/src-next/empty/README.md
@@ -0,0 +1,81 @@
+# Empty
+
+### Install
+
+```js
+import Vue from 'vue';
+import { Empty } from 'vant';
+
+Vue.use(Empty);
+```
+
+## Usage
+
+### Basic Usage
+
+```html
+<van-empty description="Description" />
+```
+
+### Image Type
+
+Use the image prop to display different placeholder images
+
+```html
+<!-- Error -->
+<van-empty image="error" description="Description" />
+<!-- Network -->
+<van-empty image="network" description="Description" />
+<!-- Search -->
+<van-empty image="search" description="Description" />
+```
+
+### Custom Image
+
+```html
+<van-empty
+  class="custom-image"
+  image="https://img.yzcdn.cn/vant/leaf.jpg"
+  description="Description"
+/>
+
+<style>
+  .custom-image img {
+    border-radius: 100%;
+  }
+</style>
+```
+
+### Bottom Content
+
+```html
+<van-empty description="Description">
+  <van-button round type="danger" class="bottom-button">
+    Button
+  </van-button>
+</van-empty>
+
+<style>
+  .bottom-button {
+    width: 160px;
+    height: 40px;
+  }
+</style>
+```
+
+## API
+
+### Props
+
+| Attribute | Description | Type | Default |
+| --- | --- | --- | --- |
+| image | Image type,can be set to `error` `network` `search` or image URL | _string_ | `default` |
+| description | Desciption | _string_ | - |
+
+### Slots
+
+| Name        | Description           |
+| ----------- | --------------------- |
+| default     | Custom bottom content |
+| image       | Custom image          |
+| description | Custom description    |
diff --git a/src-next/empty/README.zh-CN.md b/src-next/empty/README.zh-CN.md
new file mode 100644
index 000000000..1b8af0093
--- /dev/null
+++ b/src-next/empty/README.zh-CN.md
@@ -0,0 +1,90 @@
+# Empty 空状态
+
+### 介绍
+
+空状态时的占位提示,2.6 版本开始支持此组件
+
+### 引入
+
+```js
+import Vue from 'vue';
+import { Empty } from 'vant';
+
+Vue.use(Empty);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+<van-empty description="描述文字" />
+```
+
+### 图片类型
+
+Empty 组件内置了多种占位图片类型,可以在不同业务场景下使用
+
+```html
+<!-- 通用错误 -->
+<van-empty image="error" description="描述文字" />
+<!-- 网络错误 -->
+<van-empty image="network" description="描述文字" />
+<!-- 搜索提示 -->
+<van-empty image="search" description="描述文字" />
+```
+
+### 自定义图片
+
+需要自定义图片时,可以在 image 属性中传入任意图片 URL
+
+```html
+<van-empty
+  class="custom-image"
+  image="https://img.yzcdn.cn/vant/custom-empty-image.png"
+  description="描述文字"
+/>
+
+<style>
+  .custom-image .van-empty__image {
+    width: 90px;
+    height: 90px;
+  }
+</style>
+```
+
+### 底部内容
+
+通过默认插槽可以在 Empty 组件的下方插入内容
+
+```html
+<van-empty description="描述文字">
+  <van-button round type="danger" class="bottom-button">
+    按钮
+  </van-button>
+</van-empty>
+
+<style>
+  .bottom-button {
+    width: 160px;
+    height: 40px;
+  }
+</style>
+```
+
+## API
+
+### Props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| image | 图片类型,可选值为 `error` `network` `search`,支持传入图片 URL | _string_ | `default` |
+| description | 图片下方的描述文字 | _string_ | - |
+
+### Slots
+
+| 名称        | 说明           |
+| ----------- | -------------- |
+| default     | 自定义底部内容 |
+| image       | 自定义图标     |
+| description | 自定义描述文字 |
diff --git a/src-next/empty/demo/index.vue b/src-next/empty/demo/index.vue
new file mode 100644
index 000000000..3c8d4d49a
--- /dev/null
+++ b/src-next/empty/demo/index.vue
@@ -0,0 +1,88 @@
+<template>
+  <demo-section>
+    <demo-block :title="t('basicUsage')">
+      <van-empty :description="t('description')" />
+    </demo-block>
+
+    <demo-block :title="t('imageType')">
+      <van-tabs v-model="active">
+        <van-tab name="error" :title="t('error')">
+          <van-empty image="error" :description="t('description')" />
+        </van-tab>
+        <van-tab name="network" :title="t('network')">
+          <van-empty image="network" :description="t('description')" />
+        </van-tab>
+        <van-tab name="search" :title="t('search')">
+          <van-empty image="search" :description="t('description')" />
+        </van-tab>
+      </van-tabs>
+    </demo-block>
+
+    <demo-block :title="t('customImage')">
+      <van-empty
+        class="custom-image"
+        image="https://img.yzcdn.cn/vant/custom-empty-image.png"
+        :description="t('description')"
+      />
+    </demo-block>
+
+    <demo-block :title="t('bottomContent')">
+      <van-empty :description="t('description')">
+        <van-button round type="danger" class="bottom-button">
+          {{ t('button') }}
+        </van-button>
+      </van-empty>
+    </demo-block>
+  </demo-section>
+</template>
+
+<script>
+export default {
+  i18n: {
+    'zh-CN': {
+      error: '通用错误',
+      search: '搜索提示',
+      network: '网络错误',
+      imageType: '图片类型',
+      description: '描述文字',
+      customImage: '自定义图片',
+      bottomContent: '底部内容',
+    },
+    'en-US': {
+      error: 'Error',
+      search: 'Search',
+      network: 'Network',
+      imageType: 'Image Type',
+      description: 'Description',
+      customImage: 'Custom Image',
+      bottomContent: 'Bottom Content',
+    },
+  },
+
+  data() {
+    return {
+      active: 'error',
+    };
+  },
+};
+</script>
+
+<style lang="less">
+@import '../../style/var';
+
+.demo-empty {
+  background: @white;
+
+  .custom-image {
+    .van-empty__image {
+      width: 90px;
+      height: 90px;
+    }
+  }
+
+  .bottom-button {
+    width: 160px;
+    height: 40px;
+  }
+}
+</style>
diff --git a/src-next/empty/index.js b/src-next/empty/index.js
new file mode 100644
index 000000000..a59d4de40
--- /dev/null
+++ b/src-next/empty/index.js
@@ -0,0 +1,70 @@
+import { createNamespace } from '../utils';
+import Network from './Network';
+
+const [createComponent, bem] = createNamespace('empty');
+
+const PRESETS = ['error', 'search', 'default'];
+
+export default createComponent({
+  props: {
+    description: String,
+    image: {
+      type: String,
+      default: 'default',
+    },
+  },
+
+  methods: {
+    genImageContent() {
+      const slots = this.$slots.image?.();
+
+      if (slots) {
+        return slots;
+      }
+
+      if (this.image === 'network') {
+        return <Network />;
+      }
+
+      let { image } = this;
+
+      if (PRESETS.indexOf(image) !== -1) {
+        image = `https://img.yzcdn.cn/vant/empty-image-${image}.png`;
+      }
+
+      return <img src={image} />;
+    },
+
+    genImage() {
+      return <div class={bem('image')}>{this.genImageContent()}</div>;
+    },
+
+    genDescription() {
+      const description = this.$slots.description
+        ? this.slot.description()
+        : this.description;
+
+      if (description) {
+        return <p class={bem('description')}>{description}</p>;
+      }
+    },
+
+    genBottom() {
+      const slot = this.$slots.default?.();
+
+      if (slot) {
+        return <div class={bem('bottom')}>{slot}</div>;
+      }
+    },
+  },
+
+  render() {
+    return (
+      <div class={bem()}>
+        {this.genImage()}
+        {this.genDescription()}
+        {this.genBottom()}
+      </div>
+    );
+  },
+});
diff --git a/src-next/empty/index.less b/src-next/empty/index.less
new file mode 100644
index 000000000..2a28d2b86
--- /dev/null
+++ b/src-next/empty/index.less
@@ -0,0 +1,32 @@
+@import '../style/var';
+
+.van-empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  padding: @empty-padding;
+
+  &__image {
+    width: @empty-image-size;
+    height: @empty-image-size;
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  &__description {
+    margin-top: @empty-description-margin-top;
+    padding: @empty-description-padding;
+    color: @empty-description-color;
+    font-size: @empty-description-font-size;
+    line-height: @empty-description-line-height;
+  }
+
+  &__bottom {
+    margin-top: @empty-bottom-margin-top;
+  }
+}
diff --git a/src-next/empty/test/__snapshots__/demo.spec.js.snap b/src-next/empty/test/__snapshots__/demo.spec.js.snap
new file mode 100644
index 000000000..a2ae408b9
--- /dev/null
+++ b/src-next/empty/test/__snapshots__/demo.spec.js.snap
@@ -0,0 +1,55 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders demo correctly 1`] = `
+<div>
+  <div>
+    <div class="van-empty">
+      <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/empty-image-default.png"></div>
+      <p class="van-empty__description">描述文字</p>
+    </div>
+  </div>
+  <div>
+    <div class="van-tabs van-tabs--line">
+      <div class="van-tabs__wrap van-hairline--top-bottom">
+        <div role="tablist" class="van-tabs__nav van-tabs__nav--line">
+          <div role="tab" aria-selected="true" class="van-tab van-tab--active"><span class="van-tab__text van-tab__text--ellipsis">通用错误</span></div>
+          <div role="tab" class="van-tab"><span class="van-tab__text van-tab__text--ellipsis">网络错误</span></div>
+          <div role="tab" class="van-tab"><span class="van-tab__text van-tab__text--ellipsis">搜索提示</span></div>
+          <div class="van-tabs__line" style="width: 0px; transform: translateX(0px) translateX(-50%);"></div>
+        </div>
+      </div>
+      <div class="van-tabs__content">
+        <div role="tabpanel" class="van-tab__pane" style="">
+          <div class="van-empty">
+            <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/empty-image-error.png"></div>
+            <p class="van-empty__description">描述文字</p>
+          </div>
+        </div>
+        <div role="tabpanel" class="van-tab__pane" style="display: none;">
+          <!---->
+        </div>
+        <div role="tabpanel" class="van-tab__pane" style="display: none;">
+          <!---->
+        </div>
+      </div>
+    </div>
+  </div>
+  <div>
+    <div class="custom-image van-empty">
+      <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/custom-empty-image.png"></div>
+      <p class="van-empty__description">描述文字</p>
+    </div>
+  </div>
+  <div>
+    <div class="van-empty">
+      <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/empty-image-default.png"></div>
+      <p class="van-empty__description">描述文字</p>
+      <div class="van-empty__bottom"><button class="bottom-button van-button van-button--danger van-button--normal van-button--round">
+          <div class="van-button__content"><span class="van-button__text">
+        按钮
+      </span></div>
+        </button></div>
+    </div>
+  </div>
+</div>
+`;
diff --git a/src-next/empty/test/__snapshots__/index.spec.js.snap b/src-next/empty/test/__snapshots__/index.spec.js.snap
new file mode 100644
index 000000000..4484b79f0
--- /dev/null
+++ b/src-next/empty/test/__snapshots__/index.spec.js.snap
@@ -0,0 +1,83 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`bottom slot 1`] = `
+<div class="van-empty">
+  <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/empty-image-default.png"></div>
+  <div class="van-empty__bottom">Custom bottom</div>
+</div>
+`;
+
+exports[`description slot 1`] = `
+<div class="van-empty">
+  <div class="van-empty__image"><img src="https://img.yzcdn.cn/vant/empty-image-default.png"></div>
+  <p class="van-empty__description">Custom description</p>
+</div>
+`;
+
+exports[`image slot 1`] = `
+<div class="van-empty">
+  <div class="van-empty__image">Custom Image</div>
+</div>
+`;
+
+exports[`render svg when image is network 1`] = `
+<div class="van-empty">
+  <div class="van-empty__image"><svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
+      <defs>
+        <linearGradient id="c" x1="64.022%" y1="100%" x2="64.022%" y2="0%">
+          <stop stop-color="#FFF" offset="0%" stop-opacity="0.5"></stop>
+          <stop stop-color="#F2F3F5" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient id="d" x1="64.022%" y1="96.956%" x2="64.022%" y2="0%">
+          <stop stop-color="#F2F3F5" offset="0%" stop-opacity="0.3"></stop>
+          <stop stop-color="#F2F3F5" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient id="h" x1="50%" y1="0%" x2="50%" y2="84.459%">
+          <stop stop-color="#EBEDF0" offset="0%"></stop>
+          <stop stop-color="#DCDEE0" offset="100%" stop-opacity="0"></stop>
+        </linearGradient>
+        <linearGradient id="i" x1="100%" y1="0%" x2="100%" y2="100%">
+          <stop stop-color="#EAEDF0" offset="0%"></stop>
+          <stop stop-color="#DCDEE0" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient id="k" x1="100%" y1="100%" x2="100%" y2="0%">
+          <stop stop-color="#EAEDF0" offset="0%"></stop>
+          <stop stop-color="#DCDEE0" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient id="m" x1="0%" y1="43.982%" x2="100%" y2="54.703%">
+          <stop stop-color="#EAEDF0" offset="0%"></stop>
+          <stop stop-color="#DCDEE0" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient id="n" x1="94.535%" y1="43.837%" x2="5.465%" y2="54.948%">
+          <stop stop-color="#EAEDF0" offset="0%"></stop>
+          <stop stop-color="#DCDEE0" offset="100%"></stop>
+        </linearGradient>
+        <radialGradient id="g" cx="50%" cy="0%" fx="50%" fy="0%" r="100%" gradientTransform="matrix(0 1 -.54835 0 .5 -.5)">
+          <stop stop-color="#EBEDF0" offset="0%"></stop>
+          <stop stop-color="#FFF" offset="100%" stop-opacity="0"></stop>
+        </radialGradient>
+      </defs>
+      <g fill="none" fill-rule="evenodd">
+        <g opacity=".8">
+          <path d="M0 124V46h20v20h14v58H0z" fill="url(#c)" transform="matrix(-1 0 0 1 36 7)"></path>
+          <path d="M40.5 5a8.504 8.504 0 018.13 6.009l.12-.005L49 11a8 8 0 11-1 15.938V27H34v-.174a6.5 6.5 0 11-1.985-12.808A8.5 8.5 0 0140.5 5z" fill="url(#d)" transform="translate(2 7)"></path>
+          <path d="M96.016 0a4.108 4.108 0 013.934 2.868l.179-.004c2.138 0 3.871 1.71 3.871 3.818 0 2.109-1.733 3.818-3.871 3.818-.164 0-.325-.01-.484-.03v.03h-6.774v-.083a3.196 3.196 0 01-.726.083C90.408 10.5 89 9.111 89 7.398c0-1.636 1.284-2.976 2.911-3.094a3.555 3.555 0 01-.008-.247c0-2.24 1.842-4.057 4.113-4.057z" fill="url(#d)" transform="translate(2 7)"></path>
+          <path d="M121 8h22.231v14H152v77.37h-31V8z" fill="url(#c)" transform="translate(2 7)"></path>
+        </g>
+        <path fill="url(#g)" d="M0 139h160v21H0z"></path>
+        <path d="M37 18a7 7 0 013 13.326v26.742c0 1.23-.997 2.227-2.227 2.227h-1.546A2.227 2.227 0 0134 58.068V31.326A7 7 0 0137 18z" fill="url(#h)" fill-rule="nonzero" transform="translate(43 36)"></path>
+        <g opacity=".6" stroke-linecap="round" stroke-width="7">
+          <path d="M20.875 11.136a18.868 18.868 0 00-5.284 13.121c0 5.094 2.012 9.718 5.284 13.12" stroke="url(#i)" transform="translate(43 36)"></path>
+          <path d="M9.849 0C3.756 6.225 0 14.747 0 24.146c0 9.398 3.756 17.92 9.849 24.145" stroke="url(#i)" transform="translate(43 36)"></path>
+          <path d="M57.625 11.136a18.868 18.868 0 00-5.284 13.121c0 5.094 2.012 9.718 5.284 13.12" stroke="url(#k)" transform="rotate(-180 76.483 42.257)"></path>
+          <path d="M73.216 0c-6.093 6.225-9.849 14.747-9.849 24.146 0 9.398 3.756 17.92 9.849 24.145" stroke="url(#k)" transform="rotate(-180 89.791 42.146)"></path>
+        </g>
+        <g transform="translate(31 105)" fill-rule="nonzero">
+          <rect fill="url(#m)" width="98" height="34" rx="2"></rect>
+          <rect fill="#FFF" x="9" y="8" width="80" height="18" rx="1.114"></rect>
+          <rect fill="url(#n)" x="15" y="12" width="18" height="6" rx="1.114"></rect>
+        </g>
+      </g>
+    </svg></div>
+</div>
+`;
diff --git a/src-next/empty/test/demo.spec.js b/src-next/empty/test/demo.spec.js
new file mode 100644
index 000000000..5c70922b5
--- /dev/null
+++ b/src-next/empty/test/demo.spec.js
@@ -0,0 +1,4 @@
+import Demo from '../demo';
+import { snapshotDemo } from '../../../test/demo';
+
+snapshotDemo(Demo);
diff --git a/src-next/empty/test/index.spec.js b/src-next/empty/test/index.spec.js
new file mode 100644
index 000000000..54d3d4438
--- /dev/null
+++ b/src-next/empty/test/index.spec.js
@@ -0,0 +1,42 @@
+import Empty from '..';
+import { mount } from '../../../test';
+
+test('image slot', () => {
+  const wrapper = mount(Empty, {
+    scopedSlots: {
+      image: () => 'Custom Image',
+    },
+  });
+
+  expect(wrapper).toMatchSnapshot();
+});
+
+test('description slot', () => {
+  const wrapper = mount(Empty, {
+    scopedSlots: {
+      description: () => 'Custom description',
+    },
+  });
+
+  expect(wrapper).toMatchSnapshot();
+});
+
+test('bottom slot', () => {
+  const wrapper = mount(Empty, {
+    scopedSlots: {
+      default: () => 'Custom bottom',
+    },
+  });
+
+  expect(wrapper).toMatchSnapshot();
+});
+
+test('render svg when image is network', () => {
+  const wrapper = mount(Empty, {
+    propsData: {
+      image: 'network',
+    },
+  });
+
+  expect(wrapper).toMatchSnapshot();
+});
diff --git a/vant.config.js b/vant.config.js
index 38c0110ec..26f7e0aaa 100644
--- a/vant.config.js
+++ b/vant.config.js
@@ -237,10 +237,10 @@ module.exports = {
               //   path: 'divider',
               //   title: 'Divider 分割线',
               // },
-              // {
-              //   path: 'empty',
-              //   title: 'Empty 空状态',
-              // },
+              {
+                path: 'empty',
+                title: 'Empty 空状态',
+              },
               // {
               //   path: 'image-preview',
               //   title: 'ImagePreview 图片预览',
@@ -571,10 +571,10 @@ module.exports = {
               //   path: 'divider',
               //   title: 'Divider',
               // },
-              // {
-              //   path: 'empty',
-              //   title: 'Empty',
-              // },
+              {
+                path: 'empty',
+                title: 'Empty',
+              },
               // {
               //   path: 'image-preview',
               //   title: 'ImagePreview',