mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat: add Empty component (#6010)
This commit is contained in:
parent
14299a2011
commit
525f7ecb88
80
src/empty/README.md
Normal file
80
src/empty/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
# 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;
|
||||
}
|
||||
</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 |
|
89
src/empty/README.zh-CN.md
Normal file
89
src/empty/README.zh-CN.md
Normal file
@ -0,0 +1,89 @@
|
||||
# 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;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|------|------|------|------|
|
||||
| image | 图片类型,可选值为 `error` `network` `search`,支持传入图片 URL | *string* | `default` |
|
||||
| description | 图片下方的描述文字 | *string* | - |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 说明 |
|
||||
|------|------|
|
||||
| default | 自定义底部内容 |
|
||||
| image | 自定义图标 |
|
||||
| description | 自定义描述文字 |
|
87
src/empty/demo/index.vue
Normal file
87
src/empty/demo/index.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<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;
|
||||
}
|
||||
}
|
||||
</style>
|
58
src/empty/index.js
Normal file
58
src/empty/index.js
Normal file
@ -0,0 +1,58 @@
|
||||
import { createNamespace } from '../utils';
|
||||
|
||||
const [createComponent, bem] = createNamespace('empty');
|
||||
|
||||
const PRESETS = ['error', 'search', 'default', 'network'];
|
||||
|
||||
export default createComponent({
|
||||
props: {
|
||||
description: String,
|
||||
image: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
url() {
|
||||
if (PRESETS.indexOf(this.image) !== -1) {
|
||||
return `https://img.yzcdn.cn/vant/empty-image-${this.image}.png`;
|
||||
}
|
||||
|
||||
return this.image;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
genImage() {
|
||||
const image = this.slots('image') || <img src={this.url} />;
|
||||
return <div class={bem('image')}>{image}</div>;
|
||||
},
|
||||
|
||||
genDescription() {
|
||||
const description = this.slots('description') || this.description;
|
||||
|
||||
if (description) {
|
||||
return <p class={bem('description')}>{description}</p>;
|
||||
}
|
||||
},
|
||||
|
||||
genBottom() {
|
||||
const slot = this.slots();
|
||||
|
||||
if (slot) {
|
||||
return <div class={bem('bottom')}>{slot}</div>;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class={bem()}>
|
||||
{this.genImage()}
|
||||
{this.genDescription()}
|
||||
{this.genBottom()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
32
src/empty/index.less
Normal file
32
src/empty/index.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
53
src/empty/test/__snapshots__/demo.spec.js.snap
Normal file
53
src/empty/test/__snapshots__/demo.spec.js.snap
Normal file
@ -0,0 +1,53 @@
|
||||
// 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 van-ellipsis"><span class="van-tab__text">通用错误<!----></span></div>
|
||||
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">网络错误<!----></span></div>
|
||||
<div role="tab" class="van-tab van-ellipsis"><span class="van-tab__text">搜索提示<!----></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"><span class="van-button__text">
|
||||
按钮
|
||||
</span></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
14
src/empty/test/__snapshots__/index.spec.js.snap
Normal file
14
src/empty/test/__snapshots__/index.spec.js.snap
Normal file
@ -0,0 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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>
|
||||
`;
|
4
src/empty/test/demo.spec.js
Normal file
4
src/empty/test/demo.spec.js
Normal file
@ -0,0 +1,4 @@
|
||||
import Demo from '../demo';
|
||||
import { snapshotDemo } from '../../../test/demo';
|
||||
|
||||
snapshotDemo(Demo);
|
22
src/empty/test/index.spec.js
Normal file
22
src/empty/test/index.spec.js
Normal file
@ -0,0 +1,22 @@
|
||||
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();
|
||||
});
|
@ -333,6 +333,16 @@
|
||||
@dropdown-menu-content-max-height: 80%;
|
||||
@dropdown-item-z-index: 10;
|
||||
|
||||
// Empty
|
||||
@empty-padding: @padding-xl 0;
|
||||
@empty-image-size: 160px;
|
||||
@empty-description-margin-top: @padding-md;
|
||||
@empty-description-padding: 0 60px;
|
||||
@empty-description-color: @gray-6;
|
||||
@empty-description-font-size: 14px;
|
||||
@empty-description-line-height: 20px;
|
||||
@empty-bottom-margin-top: 24px;
|
||||
|
||||
// Field
|
||||
@field-label-width: 90px;
|
||||
@field-input-text-color: @text-color;
|
||||
|
1
types/index.d.ts
vendored
1
types/index.d.ts
vendored
@ -45,6 +45,7 @@ export class CouponCell extends VanComponent {}
|
||||
export class CouponList extends VanComponent {}
|
||||
export class Divider extends VanComponent {}
|
||||
export class DropdownMenu extends VanComponent {}
|
||||
export class Empty extends VanComponent {}
|
||||
export class Grid extends VanComponent {}
|
||||
export class GridItem extends VanComponent {}
|
||||
export class GoodsAction extends VanComponent {}
|
||||
|
@ -232,6 +232,10 @@ module.exports = {
|
||||
path: 'divider',
|
||||
title: 'Divider 分割线',
|
||||
},
|
||||
{
|
||||
path: 'empty',
|
||||
title: 'Empty 空状态',
|
||||
},
|
||||
{
|
||||
path: 'image-preview',
|
||||
title: 'ImagePreview 图片预览',
|
||||
@ -575,6 +579,10 @@ module.exports = {
|
||||
path: 'divider',
|
||||
title: 'Divider',
|
||||
},
|
||||
{
|
||||
path: 'empty',
|
||||
title: 'Empty',
|
||||
},
|
||||
{
|
||||
path: 'image-preview',
|
||||
title: 'ImagePreview',
|
||||
|
Loading…
x
Reference in New Issue
Block a user