diff --git a/docs/markdown/v2-progress-tracking.md b/docs/markdown/v2-progress-tracking.md
index 277183a23..72816069f 100644
--- a/docs/markdown/v2-progress-tracking.md
+++ b/docs/markdown/v2-progress-tracking.md
@@ -2,7 +2,7 @@
## 主要变动
-- 增加三个新组件
+- 增加四个新组件
- 增加数十个 API
- 全新的卡片风格文档,更直观
- 所有组件支持通过`less`变量自定义样式
@@ -12,6 +12,7 @@
在 2.0 版本中,我们按照社区反馈新增以下组件:
+- `Image`图片组件
- `Skeleton`骨架屏组件
- `IndexBar`、`IndexAnchor`索引栏组件
- `DropdownMenu`、`DropdownItem`下拉菜单组件
diff --git a/docs/src/demo-entry.js b/docs/src/demo-entry.js
index c0a78f54a..d3c4412e9 100644
--- a/docs/src/demo-entry.js
+++ b/docs/src/demo-entry.js
@@ -22,6 +22,7 @@ export default {
'field': () => wrapper(import('../../packages/field/demo'), 'field'),
'goods-action': () => wrapper(import('../../packages/goods-action/demo'), 'goods-action'),
'icon': () => wrapper(import('../../packages/icon/demo'), 'icon'),
+ 'image': () => wrapper(import('../../packages/image/demo'), 'image'),
'image-preview': () => wrapper(import('../../packages/image-preview/demo'), 'image-preview'),
'index-bar': () => wrapper(import('../../packages/index-bar/demo'), 'index-bar'),
'lazyload': () => wrapper(import('../../packages/lazyload/demo'), 'lazyload'),
diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js
index ef5cf8e29..580a26ed7 100644
--- a/docs/src/doc.config.js
+++ b/docs/src/doc.config.js
@@ -89,6 +89,10 @@ module.exports = {
path: '/icon',
title: 'Icon 图标'
},
+ {
+ path: '/image',
+ title: 'Image 图片'
+ },
{
path: '/col',
title: 'Layout 布局'
@@ -408,6 +412,10 @@ module.exports = {
path: '/icon',
title: 'Icon'
},
+ {
+ path: '/image',
+ title: 'Image'
+ },
{
path: '/col',
title: 'Layout'
diff --git a/docs/src/docs-entry.js b/docs/src/docs-entry.js
index 0651306f9..ec65fd252 100644
--- a/docs/src/docs-entry.js
+++ b/docs/src/docs-entry.js
@@ -51,6 +51,8 @@ export default {
'goods-action.zh-CN': () => import('../../packages/goods-action/zh-CN.md'),
'icon.en-US': () => import('../../packages/icon/en-US.md'),
'icon.zh-CN': () => import('../../packages/icon/zh-CN.md'),
+ 'image.en-US': () => import('../../packages/image/en-US.md'),
+ 'image.zh-CN': () => import('../../packages/image/zh-CN.md'),
'image-preview.en-US': () => import('../../packages/image-preview/en-US.md'),
'image-preview.zh-CN': () => import('../../packages/image-preview/zh-CN.md'),
'index-bar.en-US': () => import('../../packages/index-bar/en-US.md'),
diff --git a/packages/image/demo/index.vue b/packages/image/demo/index.vue
new file mode 100644
index 000000000..5b8b0dd76
--- /dev/null
+++ b/packages/image/demo/index.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ fit }}
+
+
+
+
+
+
+
+
+
diff --git a/packages/image/en-US.md b/packages/image/en-US.md
new file mode 100644
index 000000000..5dd864a86
--- /dev/null
+++ b/packages/image/en-US.md
@@ -0,0 +1,80 @@
+# Image
+
+### Install
+
+``` javascript
+import { Image } from 'vant';
+
+Vue.use(Image);
+```
+
+## Usage
+
+### Basic Usage
+
+```html
+
+```
+
+### Fit Mode
+
+```html
+
+```
+
+### Lazy Load
+
+```html
+
+```
+
+```js
+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 | `String | Number` | - | - |
+| height | Height | `String | Number` | - | - |
+| lazy-load | Whether to enable lazy load,should register [Lazyload](#/en-US/lazyload) component | `Boolean` | `false` | - |
+
+### 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 | - |
diff --git a/packages/image/index.js b/packages/image/index.js
new file mode 100644
index 000000000..fd062a171
--- /dev/null
+++ b/packages/image/index.js
@@ -0,0 +1,81 @@
+import { use, isDef, suffixPx } from '../utils';
+
+const [sfc, bem] = use('image');
+
+export default sfc({
+ props: {
+ src: String,
+ fit: String,
+ alt: String,
+ lazyLoad: Boolean,
+ width: [String, Number],
+ height: [String, Number]
+ },
+
+ data() {
+ return {
+ loading: true,
+ error: false
+ };
+ },
+
+ computed: {
+ style() {
+ const style = {};
+
+ if (isDef(this.width)) {
+ style.width = suffixPx(this.width);
+ }
+
+ if (isDef(this.height)) {
+ style.height = suffixPx(this.height);
+ }
+
+ return style;
+ }
+ },
+
+ methods: {
+ onLoad(event) {
+ this.loading = false;
+ this.$emit('load', event);
+ },
+
+ onError(event) {
+ this.error = true;
+ this.$emit('error', event);
+ },
+
+ onClick(event) {
+ this.$emit('click', event);
+ }
+ },
+
+ render(h) {
+ const imgData = {
+ class: bem('img'),
+ attrs: {
+ alt: this.alt
+ },
+ style: {
+ objectFit: this.fit
+ },
+ on: {
+ load: this.onLoad,
+ error: this.onError
+ }
+ };
+
+ const Image = this.lazyLoad ? (
+
+ ) : (
+
+ );
+
+ return (
+
+ {Image}
+
+ );
+ }
+});
diff --git a/packages/image/index.less b/packages/image/index.less
new file mode 100644
index 000000000..5f7da3686
--- /dev/null
+++ b/packages/image/index.less
@@ -0,0 +1,11 @@
+@import '../style/var';
+
+.van-image {
+ display: inline-block;
+
+ &__img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+}
diff --git a/packages/image/test/__snapshots__/demo.spec.js.snap b/packages/image/test/__snapshots__/demo.spec.js.snap
new file mode 100644
index 000000000..6011ba2db
--- /dev/null
+++ b/packages/image/test/__snapshots__/demo.spec.js.snap
@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders demo correctly 1`] = `
+
+`;
diff --git a/packages/image/test/__snapshots__/index.spec.js.snap b/packages/image/test/__snapshots__/index.spec.js.snap
new file mode 100644
index 000000000..f66af2b11
--- /dev/null
+++ b/packages/image/test/__snapshots__/index.spec.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`lazy load 1`] = ``;
diff --git a/packages/image/test/demo.spec.js b/packages/image/test/demo.spec.js
new file mode 100644
index 000000000..d647cfabc
--- /dev/null
+++ b/packages/image/test/demo.spec.js
@@ -0,0 +1,4 @@
+import Demo from '../demo';
+import demoTest from '../../../test/demo-test';
+
+demoTest(Demo);
diff --git a/packages/image/test/index.spec.js b/packages/image/test/index.spec.js
new file mode 100644
index 000000000..0e5e936e9
--- /dev/null
+++ b/packages/image/test/index.spec.js
@@ -0,0 +1,45 @@
+
+import { mount } from '../../../test/utils';
+import Image from '..';
+
+test('click event', () => {
+ const wrapper = mount(Image);
+
+ wrapper.trigger('click');
+ expect(wrapper.emitted('click')[0][0]).toBeTruthy();
+});
+
+test('load event', () => {
+ const wrapper = mount(Image, {
+ propsData: {
+ src: 'https://img.yzcdn.cn/vant/cat.jpeg'
+ }
+ });
+
+ wrapper.find('img').trigger('load');
+
+ expect(wrapper.emitted('load')[0][0]).toBeTruthy();
+});
+
+test('error event', () => {
+ const wrapper = mount(Image, {
+ 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(Image, {
+ propsData: {
+ src: 'https://img.yzcdn.cn/vant/cat.jpeg',
+ lazyLoad: true
+ }
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
diff --git a/packages/image/zh-CN.md b/packages/image/zh-CN.md
new file mode 100644
index 000000000..91d4aea77
--- /dev/null
+++ b/packages/image/zh-CN.md
@@ -0,0 +1,80 @@
+# Image 图片
+
+### 引入
+
+``` javascript
+import { Image } from 'vant';
+
+Vue.use(Image);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+
+```
+
+### 缩放模式
+
+```html
+
+```
+
+### 图片懒加载
+
+```html
+
+```
+
+```js
+import { Lazyload } from 'vant';
+
+Vue.use(Lazyload);
+```
+
+## API
+
+### Props
+
+| 参数 | 说明 | 类型 | 默认值 | 版本 |
+|------|------|------|------|------|
+| src | 图片链接 | `String` | - | - |
+| fit | 图片裁剪、缩放的模式 | `String` | `fill` | - |
+| alt | 替代文本 | `String` | - | - |
+| width | 宽度,默认单位为 px | `String | Number` | - | - |
+| height | 高度,默认单位为 px | `String | Number` | - | - |
+| lazy-load | 是否开启图片懒加载,须配合 [Lazyload](#/zh-CN/lazyload) 组件使用 | `Boolean` | `false` | - |
+
+### fit 可选值
+
+| 名称 | 含义 |
+|------|------|
+| contain | 保持宽高缩放图片,使图片的长边能完全显示出来 |
+| cover | 保持宽高缩放图片,使图片的短边能完全显示出来,裁剪长边 |
+| fill | 拉伸图片,使图片填满元素 |
+| none | 保持图片原有尺寸 |
+| scale-down | 取`none`或`contain`中较小的一个 |
+
+### Events
+
+| 事件名 | 说明 | 回调参数 |
+|------|------|------|
+| click | 点击图片时触发 | event: Event |
+| load | 图片加载完毕时触发 | - |
+| error | 图片加载失败时触发 | - |
diff --git a/packages/index.less b/packages/index.less
index 975bba9c2..b445ed6a4 100644
--- a/packages/index.less
+++ b/packages/index.less
@@ -8,6 +8,7 @@
/* common components */
@import './col/index';
@import './row/index';
+@import './image/index';
@import './circle/index';
@import './collapse-item/index';
@import './list/index';
diff --git a/packages/index.ts b/packages/index.ts
index 149047efe..fc805b72b 100644
--- a/packages/index.ts
+++ b/packages/index.ts
@@ -30,6 +30,7 @@ import GoodsAction from './goods-action';
import GoodsActionButton from './goods-action-button';
import GoodsActionIcon from './goods-action-icon';
import Icon from './icon';
+import Image from './image';
import ImagePreview from './image-preview';
import IndexAnchor from './index-anchor';
import IndexBar from './index-bar';
@@ -115,6 +116,7 @@ const components = [
GoodsActionButton,
GoodsActionIcon,
Icon,
+ Image,
ImagePreview,
IndexAnchor,
IndexBar,
@@ -205,6 +207,7 @@ export {
GoodsActionButton,
GoodsActionIcon,
Icon,
+ Image,
ImagePreview,
IndexAnchor,
IndexBar,