feat: image component

This commit is contained in:
chenjiahan 2020-07-05 08:33:36 +08:00
parent 12faaa2179
commit b79e59bc18
15 changed files with 1041 additions and 8 deletions

105
src-next/image/README.md Normal file
View File

@ -0,0 +1,105 @@
# Image
### Install
```js
import Vue from 'vue';
import { Image as VanImage } from 'vant';
Vue.use(VanImage);
```
## Usage
### Basic Usage
```html
<van-image width="100" height="100" src="https://img.yzcdn.cn/vant/cat.jpeg" />
```
### Fit Mode
```html
<van-image
width="10rem"
height="10rem"
fit="contain"
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
### Round
Show round image, it may not works at `fit=contain` and `fit=scale-down`
```html
<van-image
round
width="10rem"
height="10rem"
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
### Lazy Load
```html
<van-image
width="100"
height="100"
lazy-load
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
```js
import Vue from 'vue';
import { Lazyload } from 'vant';
Vue.use(Lazyload);
```
## API
### Props
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| src | Src | _string_ | - |
| fit | Fit mode | _string_ | `fill` |
| alt | Alt | _string_ | - |
| width | Width | _number \| string_ | - |
| height | Height | _number \| string_ | - |
| radius `v2.1.6` | Border Radius | _number \| string_ | `0` |
| round | Whether to be round | _boolean_ | `false` |
| lazy-load | Whether to enable lazy loadshould register [Lazyload](#/en-US/lazyload) component | _boolean_ | `false` |
| show-error `v2.0.9` | Whether to show error placeholder | _boolean_ | `true` |
| show-loading `v2.0.9` | Whether to show loading placeholder | _boolean_ | `true` |
| error-icon `v2.4.2` | Error icon | _string_ | `warning-o` |
| loading-icon `v2.4.2` | Loading icon | _string_ | `photo-o` |
### fit optional value
| name | desctription |
| --- | --- |
| contain | Keep aspect ratio, fully display the long side of the image |
| cover | Keep aspect ratio, fully display the short side of the image, cutting the long side |
| fill | Stretch and resize image to fill the content box |
| none | Not resize image |
| scale-down | Take the smaller of `none` or `contain` |
### Events
| Event | Description | Arguments |
| ----- | -------------------------------- | -------------- |
| click | Triggered when click image | _event: Event_ |
| load | Triggered when image loaded | - |
| error | Triggered when image load failed | - |
### Slots
| Name | Description |
| ---------------- | ---------------------------------- |
| default `v2.9.0` | Custom the content below the image |
| loading | Custom loading placeholder |
| error | Custom error placeholder |

View File

@ -0,0 +1,175 @@
# Image 图片
### 介绍
增强版的 img 标签,提供多种图片填充模式,支持图片懒加载、加载中提示、加载失败提示
### 引入
```js
import Vue from 'vue';
import { Image as VanImage } from 'vant';
Vue.use(VanImage);
```
## 代码演示
### 基础用法
基础用法与原生`img`标签一致,可以设置`src``width``height``alt`等原生属性
```html
<van-image width="100" height="100" src="https://img.yzcdn.cn/vant/cat.jpeg" />
```
### 填充模式
通过`fit`属性可以设置图片填充模式,可选值见下方表格
```html
<van-image
width="10rem"
height="10rem"
fit="contain"
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
### 圆形图片
通过`round`属性可以设置图片变圆,注意当图片宽高不相等且`fit``contain``scale-down`时,将无法填充一个完整的圆形。
```html
<van-image
round
width="10rem"
height="10rem"
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
### 图片懒加载
设置`lazy-load`属性来开启图片懒加载,需要搭配 [Lazyload](#/zh-CN/lazyload) 组件使用
```html
<van-image
width="100"
height="100"
lazy-load
src="https://img.yzcdn.cn/vant/cat.jpeg"
/>
```
```js
import Vue from 'vue';
import { Lazyload } from 'vant';
Vue.use(Lazyload);
```
### 加载中提示
`Image`组件提供了默认的加载中提示,支持通过`loading`插槽自定义内容
```html
<van-image src="https://img.yzcdn.cn/vant/cat.jpeg">
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
```
### 加载失败提示
`Image`组件提供了默认的加载失败提示,支持通过`error`插槽自定义内容
```html
<van-image src="https://img.yzcdn.cn/vant/cat.jpeg">
<template v-slot:error>加载失败</template>
</van-image>
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| src | 图片链接 | _string_ | - |
| fit | 图片填充模式 | _string_ | `fill` |
| alt | 替代文本 | _string_ | - |
| width | 宽度,默认单位为`px` | _number \| string_ | - |
| height | 高度,默认单位为`px` | _number \| string_ | - |
| radius `v2.1.6` | 圆角大小,默认单位为`px` | _number \| string_ | `0` |
| round | 是否显示为圆形 | _boolean_ | `false` |
| lazy-load | 是否开启图片懒加载,须配合 [Lazyload](#/zh-CN/lazyload) 组件使用 | _boolean_ | `false` |
| show-error `v2.0.9` | 是否展示图片加载失败提示 | _boolean_ | `true` |
| show-loading `v2.0.9` | 是否展示图片加载中提示 | _boolean_ | `true` |
| error-icon `v2.4.2` | 失败时提示的[图标名称](#/zh-CN/icon)或图片链接 | _string_ | `warning-o` |
| loading-icon `v2.4.2` | 加载时提示的[图标名称](#/zh-CN/icon)或图片链接 | _string_ | `photo-o` |
### 图片填充模式 
| 名称 | 含义 |
| ---------- | ------------------------------------------------------ |
| contain | 保持宽高缩放图片,使图片的长边能完全显示出来 |
| cover | 保持宽高缩放图片,使图片的短边能完全显示出来,裁剪长边 |
| fill | 拉伸图片,使图片填满元素 |
| none | 保持图片原有尺寸 |
| scale-down | 取`none``contain`中较小的一个 |
### Events
| 事件名 | 说明 | 回调参数 |
| ------ | ------------------ | -------------- |
| click | 点击图片时触发 | _event: Event_ |
| load | 图片加载完毕时触发 | - |
| error | 图片加载失败时触发 | - |
### Slots
| 名称 | 说明 |
| ---------------- | -------------------------- |
| default `v2.9.0` | 自定义图片下方的内容 |
| loading | 自定义加载中的提示内容 |
| error | 自定义加载失败时的提示内容 |
## 常见问题
### 如何引用本地图片?
在 .vue 文件中通过相对路径引用本地图片时,需要在图片的链接外包上一层 `require()`,将图片 URL 转换为 webpack 模块请求,并结合 [file-loader](https://github.com/webpack-contrib/file-loader) 或者 [url-loader](https://github.com/webpack-contrib/url-loader) 进行处理。
```html
<!-- 错误写法 -->
<van-image src="./image.png" />
<!-- 正确写法 -->
<van-image :src="require('./image.png')" />
```
> 对此更详细的解释可以参考 vue-loader 的[处理资源路径](https://vue-loader.vuejs.org/zh/guide/asset-url.html)章节。
### 使用 image 标签无法渲染?
使用 Image 组件时,可能会遇到将 \<image> 作为标签名时无法渲染的问题,比如下面的写法:
```html
<template>
<image src="xxx" />
</template>
<script>
import { Image } from 'vant';
export default {
components: {
Image,
},
};
<script>
```
这是因为 \<image> 标签是原生的 SVG 标签Vue 不允许将原生标签名注册为组件名,使用 \<van-image> 即可规避这个问题。

View File

@ -0,0 +1,117 @@
<template>
<demo-section>
<demo-block :title="t('basicUsage')">
<van-row>
<van-image width="100" height="100" :src="image" />
</van-row>
</demo-block>
<demo-block :title="t('fitMode')">
<van-row gutter="20">
<van-col v-for="fit in fits" span="8" :key="fit">
<van-image :fit="fit" width="100%" height="27vw" :src="image" />
<div class="text">{{ fit }}</div>
</van-col>
</van-row>
</demo-block>
<demo-block :title="t('round')">
<van-row gutter="20">
<van-col v-for="fit in fits" span="8" :key="fit">
<van-image round :fit="fit" width="100%" height="27vw" :src="image" />
<div class="text">{{ fit }}</div>
</van-col>
</van-row>
</demo-block>
<demo-block :title="t('loading')">
<van-row gutter="20">
<van-col span="8">
<van-image width="100%" height="27vw" />
<div class="text">{{ t('defaultTip') }}</div>
</van-col>
<van-col span="8">
<van-image width="100%" height="27vw">
<template #loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
<div class="text">{{ t('customTip') }}</div>
</van-col>
</van-row>
</demo-block>
<demo-block :title="t('error')">
<van-row gutter="20">
<van-col span="8">
<van-image width="100%" height="27vw" src="x" />
<div class="text">{{ t('defaultTip') }}</div>
</van-col>
<van-col span="8">
<van-image width="100%" height="27vw" src="x">
<template #error>{{ t('loadFail') }}</template>
</van-image>
<div class="text">{{ t('customTip') }}</div>
</van-col>
</van-row>
</demo-block>
</demo-section>
</template>
<script>
export default {
i18n: {
'zh-CN': {
fitMode: '填充模式',
round: '圆形图片',
loading: '加载中提示',
error: '加载失败提示',
defaultTip: '默认提示',
customTip: '自定义提示',
loadFail: '加载失败',
},
'en-US': {
fitMode: 'Fit Mode',
round: 'Round',
loading: 'Loading',
error: 'Error',
defaultTip: 'Default Tip',
customTip: 'Custom Tip',
loadFail: 'Load failed',
},
},
data() {
return {
image: 'https://img.yzcdn.cn/vant/cat.jpeg',
fits: ['contain', 'cover', 'fill', 'none', 'scale-down'],
};
},
};
</script>
<style lang="less">
@import '../../style/var';
.demo-image {
overflow-x: hidden;
background-color: @white;
.van-row {
padding: 0 @padding-md;
}
.van-col {
margin-bottom: 20px;
}
.text {
margin-top: 5px;
color: @gray-7;
font-size: 14px;
text-align: center;
}
}
</style>

184
src-next/image/index.js Normal file
View File

@ -0,0 +1,184 @@
import { createNamespace, isDef, addUnit } from '../utils';
import Icon from '../icon';
const [createComponent, bem] = createNamespace('image');
export default createComponent({
props: {
src: String,
fit: String,
alt: String,
round: Boolean,
width: [Number, String],
height: [Number, String],
radius: [Number, String],
lazyLoad: Boolean,
showError: {
type: Boolean,
default: true,
},
showLoading: {
type: Boolean,
default: true,
},
errorIcon: {
type: String,
default: 'warning-o',
},
loadingIcon: {
type: String,
default: 'photo-o',
},
},
data() {
return {
loading: true,
error: false,
};
},
watch: {
src() {
this.loading = true;
this.error = false;
},
},
computed: {
style() {
const style = {};
if (isDef(this.width)) {
style.width = addUnit(this.width);
}
if (isDef(this.height)) {
style.height = addUnit(this.height);
}
if (isDef(this.radius)) {
style.overflow = 'hidden';
style.borderRadius = addUnit(this.radius);
}
return style;
},
},
created() {
const { $Lazyload } = this;
if ($Lazyload) {
$Lazyload.$on('loaded', this.onLazyLoaded);
$Lazyload.$on('error', this.onLazyLoadError);
}
},
beforeDestroy() {
const { $Lazyload } = this;
if ($Lazyload) {
$Lazyload.$off('loaded', this.onLazyLoaded);
$Lazyload.$off('error', this.onLazyLoadError);
}
},
methods: {
onLoad(event) {
this.loading = false;
this.$emit('load', event);
},
onLazyLoaded({ el }) {
if (el === this.$refs.image && this.loading) {
this.onLoad();
}
},
onLazyLoadError({ el }) {
if (el === this.$refs.image && !this.error) {
this.onError();
}
},
onError(event) {
this.error = true;
this.loading = false;
this.$emit('error', event);
},
onClick(event) {
this.$emit('click', event);
},
genPlaceholder() {
if (this.loading && this.showLoading) {
return (
<div class={bem('loading')}>
{this.$slots.loading ? (
this.$slots.loading()
) : (
<Icon name={this.loadingIcon} class={bem('loading-icon')} />
)}
</div>
);
}
if (this.error && this.showError) {
return (
<div class={bem('error')}>
{this.$slots.error ? (
this.$slots.error()
) : (
<Icon name={this.errorIcon} class={bem('error-icon')} />
)}
</div>
);
}
},
genImage() {
const imgData = {
class: bem('img'),
attrs: {
alt: this.alt,
},
style: {
objectFit: this.fit,
},
};
if (this.error) {
return;
}
if (this.lazyLoad) {
return <img ref="image" vLazy={this.src} {...imgData} />;
}
return (
<img
src={this.src}
onLoad={this.onLoad}
onError={this.onError}
{...imgData}
/>
);
},
},
render() {
return (
<div
class={bem({ round: this.round })}
style={this.style}
onClick={this.onClick}
>
{this.genImage()}
{this.genPlaceholder()}
{this.$slots.default?.()}
</div>
);
},
});

45
src-next/image/index.less Normal file
View File

@ -0,0 +1,45 @@
@import '../style/var';
.van-image {
position: relative;
display: inline-block;
&--round {
overflow: hidden;
border-radius: 50%;
img {
border-radius: inherit;
}
}
&__img,
&__error,
&__loading {
display: block;
width: 100%;
height: 100%;
}
&__error,
&__loading {
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: @image-placeholder-text-color;
font-size: @image-placeholder-font-size;
background-color: @image-placeholder-background-color;
}
&__loading-icon {
font-size: @image-loading-icon-size;
}
&__error-icon {
font-size: @image-error-icon-size;
}
}

View File

@ -0,0 +1,129 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders demo correctly 1`] = `
<div>
<div>
<div class="van-row">
<div class="van-image" style="width: 100px; height: 100px;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
</div>
</div>
<div>
<div class="van-row">
<div class="van-col van-col--8" style="padding-right: 13.333333333333334px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: contain;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">contain</div>
</div>
<div class="van-col van-col--8" style="padding-left: 6.666666666666666px; padding-right: 6.666666666666668px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">cover</div>
</div>
<div class="van-col van-col--8" style="padding-left: 13.333333333333332px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: fill;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">fill</div>
</div>
<div class="van-col van-col--8" style="padding-right: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: none;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">none</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: scale-down;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">scale-down</div>
</div>
</div>
</div>
<div>
<div class="van-row">
<div class="van-col van-col--8" style="padding-right: 13.333333333333334px;">
<div class="van-image van-image--round" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: contain;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">contain</div>
</div>
<div class="van-col van-col--8" style="padding-left: 6.666666666666666px; padding-right: 6.666666666666668px;">
<div class="van-image van-image--round" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">cover</div>
</div>
<div class="van-col van-col--8" style="padding-left: 13.333333333333332px;">
<div class="van-image van-image--round" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: fill;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">fill</div>
</div>
<div class="van-col van-col--8" style="padding-right: 10px;">
<div class="van-image van-image--round" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: none;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">none</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px;">
<div class="van-image van-image--round" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: scale-down;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">scale-down</div>
</div>
</div>
</div>
<div>
<div class="van-row">
<div class="van-col van-col--8" style="padding-right: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">默认提示</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img class="van-image__img">
<div class="van-image__loading">
<div class="van-loading van-loading--spinner"><span class="van-loading__spinner van-loading__spinner--spinner" style="width: 20px; height: 20px;"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span></div>
</div>
</div>
<div class="text">自定义提示</div>
</div>
</div>
</div>
<div>
<div class="van-row">
<div class="van-col van-col--8" style="padding-right: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="x" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">默认提示</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img src="x" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
<div class="text">自定义提示</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,58 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`default slot 1`] = `
<div class="van-image"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>Custom Default
</div>
`;
exports[`error-icon prop 1`] = `
<div class="van-image">
<div class="van-image__error"><i class="van-icon van-icon-error van-image__error-icon">
<!----></i></div>
</div>
`;
exports[`lazy load 1`] = `
<div class="van-image"><img class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
`;
exports[`lazy-load error event 1`] = `
<div class="van-image">
<div class="van-image__error"><i class="van-icon van-icon-warning-o van-image__error-icon">
<!----></i></div>
</div>
`;
exports[`lazy-load load event 1`] = `<div class="van-image"><img class="van-image__img"></div>`;
exports[`load event 1`] = `<div class="van-image"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img"></div>`;
exports[`load event 2`] = `
<div class="van-image"><img src="" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
`;
exports[`loading-icon prop 1`] = `
<div class="van-image"><img class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-success van-image__loading-icon">
<!----></i></div>
</div>
`;
exports[`radius prop 1`] = `
<div class="van-image" style="overflow: hidden; border-radius: 3px;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o van-image__loading-icon">
<!----></i></div>
</div>
`;
exports[`show-error prop 1`] = `<div class="van-image"></div>`;
exports[`show-loading prop 1`] = `<div class="van-image"><img class="van-image__img"></div>`;

View File

@ -0,0 +1,4 @@
import Demo from '../demo';
import { snapshotDemo } from '../../../test/demo';
snapshotDemo(Demo);

View File

@ -0,0 +1,172 @@
import { mount } from '../../../test';
import VanImage from '..';
test('click event', () => {
const wrapper = mount(VanImage);
wrapper.trigger('click');
expect(wrapper.emitted('click')[0][0]).toBeTruthy();
wrapper.destroy();
});
test('load event', () => {
const wrapper = mount(VanImage, {
propsData: {
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
});
wrapper.find('img').trigger('load');
expect(wrapper.emitted('load')[0][0]).toBeTruthy();
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ src: '' });
expect(wrapper).toMatchSnapshot();
});
test('error event', () => {
const wrapper = mount(VanImage, {
propsData: {
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
});
wrapper.find('img').trigger('error');
expect(wrapper.emitted('error')[0][0]).toBeTruthy();
});
test('lazy load', () => {
const wrapper = mount(VanImage, {
propsData: {
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
lazyLoad: true,
},
});
expect(wrapper).toMatchSnapshot();
});
test('lazy-load load event', (done) => {
const wrapper = mount(VanImage, {
propsData: {
lazyLoad: true,
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
mocks: {
$Lazyload: {
$on(eventName, hanlder) {
if (eventName === 'loaded') {
setTimeout(() => {
hanlder({ el: null });
hanlder({ el: wrapper.find('img').element });
expect(wrapper.emitted('load').length).toEqual(1);
expect(wrapper).toMatchSnapshot();
wrapper.destroy();
});
}
},
$off() {
done();
},
},
},
});
});
test('lazy-load error event', (done) => {
const wrapper = mount(VanImage, {
propsData: {
lazyLoad: true,
},
mocks: {
$Lazyload: {
$on(eventName, hanlder) {
if (eventName === 'error') {
setTimeout(() => {
hanlder({ el: null });
hanlder({ el: wrapper.find('img').element });
expect(wrapper.emitted('error').length).toEqual(1);
expect(wrapper).toMatchSnapshot();
wrapper.destroy();
});
}
},
$off() {
done();
},
},
},
});
});
test('show-loading prop', () => {
const wrapper = mount(VanImage, {
propsData: {
showLoading: false,
},
});
expect(wrapper).toMatchSnapshot();
});
test('show-error prop', () => {
const wrapper = mount(VanImage, {
propsData: {
showError: false,
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
});
wrapper.find('img').trigger('error');
expect(wrapper).toMatchSnapshot();
});
test('error-icon prop', () => {
const wrapper = mount(VanImage, {
propsData: {
errorIcon: 'error',
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
});
wrapper.find('img').trigger('error');
expect(wrapper).toMatchSnapshot();
});
test('loading-icon prop', () => {
const wrapper = mount(VanImage, {
propsData: {
loadingIcon: 'success',
},
});
expect(wrapper).toMatchSnapshot();
});
test('radius prop', () => {
const wrapper = mount(VanImage, {
propsData: {
radius: 3,
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
});
expect(wrapper).toMatchSnapshot();
});
test('default slot', () => {
const wrapper = mount(VanImage, {
propsData: {
src: 'https://img.yzcdn.cn/vant/cat.jpeg',
},
scopedSlots: {
default: () => 'Custom Default',
},
});
expect(wrapper).toMatchSnapshot();
});

View File

@ -0,0 +1,8 @@
import { isNaN } from './number';
export function isDate(val: Date): val is Date {
return (
Object.prototype.toString.call(val) === '[object Date]' &&
!isNaN(val.getTime())
);
}

View File

@ -0,0 +1,5 @@
/* eslint-disable */
export function isEmail(value: string): boolean {
const reg = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
return reg.test(value);
}

View File

@ -0,0 +1,6 @@
export function isMobile(value: string): boolean {
value = value.replace(/[^-|\d]/g, '');
return (
/^((\+86)|(86))?(1)\d{10}$/.test(value) || /^0[0-9-]{10,13}$/.test(value)
);
}

View File

@ -0,0 +1,12 @@
export function isNumeric(val: string): boolean {
return /^\d+(\.\d+)?$/.test(val);
}
export function isNaN(val: number): val is typeof NaN {
if (Number.isNaN) {
return Number.isNaN(val);
}
// eslint-disable-next-line no-self-compare
return val !== val;
}

View File

@ -0,0 +1,13 @@
import { isServer } from '..';
export function isAndroid(): boolean {
/* istanbul ignore next */
return isServer ? false : /android/.test(navigator.userAgent.toLowerCase());
}
export function isIOS(): boolean {
/* istanbul ignore next */
return isServer
? false
: /ios|iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase());
}

View File

@ -90,10 +90,10 @@ module.exports = {
path: 'icon',
title: 'Icon 图标',
},
// {
// path: 'image',
// title: 'Image 图片',
// },
{
path: 'image',
title: 'Image 图片',
},
// {
// path: 'col',
// title: 'Layout 布局',
@ -437,10 +437,10 @@ module.exports = {
path: 'icon',
title: 'Icon',
},
// {
// path: 'image',
// title: 'Image',
// },
{
path: 'image',
title: 'Image',
},
// {
// path: 'col',
// title: 'Layout',