diff --git a/docs/examples-docs/address-edit.md b/docs/examples-docs/address-edit.md
new file mode 100644
index 000000000..9e0ebf529
--- /dev/null
+++ b/docs/examples-docs/address-edit.md
@@ -0,0 +1,145 @@
+
+
+## AddressEdit 地址编辑
+
+### 使用指南
+``` javascript
+import { AddressEdit } from 'vant';
+
+Vue.component(AddressEdit.name, AddressEdit);
+```
+
+### 代码演示
+
+#### 基础用法
+
+:::demo 基础用法
+```html
+
+```
+
+```javascript
+export default {
+ data() {
+ return {
+ areaList,
+ searchResult: []
+ }
+ },
+
+ methods: {
+ onSave() {
+ Toast('save');
+ },
+ onDelete() {
+ Toast('delete');
+ },
+ onChangeDetail(val) {
+ if (val) {
+ this.searchResult = [{
+ name: '黄龙万科中心',
+ address: '杭州市西湖区'
+ }];
+ } else {
+ this.searchResult = [];
+ }
+ }
+ }
+}
+```
+:::
+
+### API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| areaList | 地区列表 | `Object` | - | - |
+| addressInfo | 收货人信息 | `Object` | `{}` | - |
+| searchResult | 详细地址搜索结果 | `Array` | `[]` | - |
+| addressText | "地址"文案前缀 | `String` | `收货` | - |
+| showPostal | 是否显示邮政编码 | `Boolean` | `false` | - |
+| showSetDefault | 是否显示默认地址栏 | `Boolean` | `false` | - |
+| showSearchResult | 是否显示搜索结果 | `Boolean` | `false` | - |
+| isSaving | 是否显示保存按钮加载动画 | `Boolean` | `false` | - |
+| isDeleting | 是否显示删除按钮加载动画 | `Boolean` | `false` | - |
+
+### Event
+
+| 事件名 | 说明 | 参数 |
+|-----------|-----------|-----------|
+| save | 点击保存按钮时触发 | content:表单内容 |
+| delete | 点击删除按钮时触发 | content:表单内容 |
+| change-detail | 修改详细地址时触发 | value: 详细地址内容 |
+
+### 数据格式
+
+#### addressInfo 数据格式
+| key | 说明 | 类型 |
+|-----------|-----------|-----------|
+| id | 每条地址的唯一标识 | `String | Number` |
+| name | 收货人姓名 | `String` |
+| tel | 收货人手机号 | `String` |
+| province | 省份 | `String` |
+| city | 城市 | `String` |
+| county | 区县 | `String` |
+| address_detail | 详细地址 | `String` |
+| area_code | 地区编码,通过省市区选择获取 | `String` |
+| postal_code | 邮政编码 | `String` |
+| is_default | 是否为默认地址 | `String` |
+
+#### searchResult 数据格式
+| key | 说明 | 类型 |
+|-----------|-----------|-----------|
+| name | 地名 | `String` |
+| address | 详细地址 | `String` |
+
+#### 省市县列表数据格式
+请参考 [Area](/zanui/vue/component/area) 组件。
diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js
index 619899742..1302676f1 100644
--- a/docs/src/doc.config.js
+++ b/docs/src/doc.config.js
@@ -214,6 +214,10 @@ module.exports = {
{
"groupName": "业务组件",
"list": [
+ {
+ "path": "/address-edit",
+ "title": "AddressEdit 地址编辑"
+ },
{
"path": "/address-list",
"title": "AddressList 地址列表"
diff --git a/packages/address-edit/Detail.vue b/packages/address-edit/Detail.vue
new file mode 100644
index 000000000..1371f05b5
--- /dev/null
+++ b/packages/address-edit/Detail.vue
@@ -0,0 +1,108 @@
+
+
+
+
+ 完成
+
+
+
+
+
+
+
+
+
{{ express.name }}
+
{{ express.address }}
+
+
+
+
+
+
+
diff --git a/packages/address-edit/index.vue b/packages/address-edit/index.vue
new file mode 100644
index 000000000..489ef7dc2
--- /dev/null
+++ b/packages/address-edit/index.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+ {{ currentInfo.province || '选择省' }}
+ {{ currentInfo.city || '选择市' }}
+ {{ currentInfo.county || '选择区' }}
+
+
+
+
+
+
+
+ 保存
+ 删除{{ addressText }}地址
+
+
+
+
+
+
+
+
diff --git a/packages/field/index.vue b/packages/field/index.vue
index ba281a720..cd0efd5cc 100644
--- a/packages/field/index.vue
+++ b/packages/field/index.vue
@@ -15,20 +15,22 @@
{
+ let wrapper;
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a AddressEdit', () => {
+ wrapper = mount(AddressEdit);
+ expect(wrapper.hasClass('van-address-edit')).to.be.true;
+ expect(wrapper.find('.van-field__control')[0].element.value).to.equal('');
+ expect(wrapper.find('.van-field__control')[1].element.value).to.equal('');
+ expect(wrapper.find('.van-field__control')[2].element.value).to.equal('');
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[0].text()).to.equal('选择省');
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[1].text()).to.equal('选择市');
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[2].text()).to.equal('选择区');
+ });
+
+ it('create a AddressEdit with props', () => {
+ const addressInfo = {
+ user_name: '测试',
+ tel: '123123213',
+ province: '浙江省',
+ city: '杭州市',
+ county: '西湖区',
+ address_detail: '详细地址',
+ postal_code: '10000',
+ is_default: true
+ };
+
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ areaList,
+ addressInfo: addressInfo,
+ showPostal: true,
+ showSetDefault: true,
+ showSearchResult: true,
+ searchResult: []
+ }
+ });
+
+ expect(wrapper.find('.van-field__control')[0].element.value).to.equal(addressInfo.user_name);
+ expect(wrapper.find('.van-field__control')[1].element.value).to.equal(addressInfo.tel);
+ expect(wrapper.find('.van-field__control')[2].element.value).to.equal(addressInfo.address_detail);
+ expect(wrapper.find('.van-field__control')[3].element.value).to.equal(addressInfo.postal_code);
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[0].text()).to.equal(addressInfo.province);
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[1].text()).to.equal(addressInfo.city);
+ expect(wrapper.find('.van-address-edit__area .van-cell__value span')[2].text()).to.equal(addressInfo.county);
+ expect(wrapper.find('.van-switch-cell').length).to.equal(1);
+ });
+
+ it('save AddressInfo', () => {
+ const addressInfo = {
+ user_name: '',
+ tel: '123123213',
+ province: '浙江省',
+ city: '杭州市',
+ county: '西湖区',
+ address_detail: '详细地址',
+ postal_code: '10000',
+ is_default: true
+ };
+
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ areaList,
+ addressInfo: addressInfo,
+ showPostal: true,
+ showSetDefault: true,
+ showSearchResult: true,
+ searchResult: []
+ }
+ });
+
+ const saveButton = wrapper.find('.van-button')[0];
+
+ // name empty
+ wrapper.vm.addressInfo.user_name = '';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['user_name']).to.be.true;
+ wrapper.find('.van-field__control')[0].trigger('focus');
+ expect(wrapper.vm.errorInfo['user_name']).to.be.false;
+
+ // name too long
+ wrapper.vm.addressInfo.user_name = '111111111111111111111111111';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['user_name']).to.be.true;
+ wrapper.find('.van-field__control')[0].trigger('focus');
+ expect(wrapper.vm.errorInfo['user_name']).to.be.false;
+
+ // tel empty
+ wrapper.vm.addressInfo.user_name = '123';
+ wrapper.vm.addressInfo.tel = '';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['tel']).to.be.true;
+ wrapper.find('.van-field__control')[1].trigger('focus');
+ expect(wrapper.vm.errorInfo['tel']).to.be.false;
+
+ // area_code empty
+ wrapper.vm.addressInfo.tel = '13000000000';
+ wrapper.vm.addressInfo.area_code = '';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['area_code']).to.be.true;
+
+ // area_code invalid
+ wrapper.vm.addressInfo.tel = '13000000000';
+ wrapper.vm.addressInfo.area_code = '-1';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['area_code']).to.be.true;
+
+ // address_detail empty
+ wrapper.vm.addressInfo.area_code = '100000';
+ wrapper.vm.addressInfo.address_detail = '';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['address_detail']).to.be.true;
+ wrapper.find('.van-field__control')[2].trigger('focus');
+ expect(wrapper.vm.errorInfo['address_detail']).to.be.false;
+
+ // address_detail too long
+ let longAddress = '1';
+ for (let i = 0; i < 300; i++) {
+ longAddress += '1';
+ }
+ wrapper.vm.addressInfo.address_detail = longAddress;
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['address_detail']).to.be.true;
+ wrapper.find('.van-field__control')[2].trigger('focus');
+ expect(wrapper.vm.errorInfo['address_detail']).to.be.false;
+
+ // postal_code invalid
+ wrapper.vm.addressInfo.address_detail = '123';
+ wrapper.vm.addressInfo.postal_code = '123';
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['postal_code']).to.be.true;
+ wrapper.find('.van-field__control')[3].trigger('focus');
+ expect(wrapper.vm.errorInfo['postal_code']).to.be.false;
+
+ // valid result
+ wrapper.vm.addressInfo.postal_code = '123456';
+ saveButton.trigger('click');
+
+ // not show postal_code
+ wrapper.vm.addressInfo.postal_code = '156';
+ wrapper.vm.showPostal = false;
+ saveButton.trigger('click');
+ expect(wrapper.vm.errorInfo['postal_code']).to.be.false;
+ });
+
+ it('show search result', done => {
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ addressInfo: {},
+ showSearchResult: true,
+ searchResult: [{
+ name: '黄龙万科中心',
+ address: '杭州市西湖区'
+ }, {
+ name: '黄龙万科中心H座'
+ }, {
+ address: '杭州市西湖区'
+ }]
+ }
+ });
+
+ wrapper.find('.van-field__control')[2].trigger('focus');
+ wrapper.vm.$nextTick(() => {
+ const items = wrapper.find('.van-address-edit-detail__suggest-item');
+ expect(items.length).to.equal(3);
+
+ items[0].trigger('click');
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.find('.van-field__control')[2].element.value).to.equal('杭州市西湖区 黄龙万科中心');
+
+ items[1].trigger('click');
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.find('.van-field__control')[2].element.value).to.equal('黄龙万科中心H座');
+ items[2].trigger('click');
+
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.find('.van-field__control')[2].element.value).to.equal('杭州市西湖区');
+
+ wrapper.find('.van-field__control')[2].trigger('blur');
+ setTimeout(() => {
+ done();
+ }, 300);
+ });
+ });
+ });
+ });
+ });
+
+ it('select area', () => {
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ areaList,
+ addressInfo: {}
+ }
+ });
+
+ wrapper.vm.onAreaConfirm([]);
+ wrapper.vm.onAreaConfirm([{ code: -1 }]);
+ wrapper.vm.onAreaConfirm([{ code: 1 }, { code: -1 }]);
+ wrapper.vm.onAreaConfirm([{ code: 1 }, { code: 1 }, { code: -1 }]);
+ expect(wrapper.vm.addressInfo['area_code']).to.equal(undefined);
+
+ wrapper.vm.onAreaConfirm([{ name: '浙江省' }, { name: '杭州市' }, { name: '西湖区', code: '123456' }]);
+ expect(wrapper.vm.addressInfo['province']).to.equal('浙江省');
+ expect(wrapper.vm.addressInfo['city']).to.equal('杭州市');
+ expect(wrapper.vm.addressInfo['county']).to.equal('西湖区');
+ expect(wrapper.vm.addressInfo['area_code']).to.equal('123456');
+ });
+
+ it('delete address', done => {
+ wrapper = mount(AddressEdit, {
+ attachToDocument: true,
+ propsData: {
+ areaList,
+ isDeleting: true,
+ addressInfo: {
+ id: '123'
+ }
+ }
+ });
+
+ const deleteButton = wrapper.find('.van-button')[1];
+ deleteButton.trigger('click');
+ wrapper.vm.onDeleteAddress();
+
+ setTimeout(() => {
+ expect(document.querySelectorAll('.van-dialog').length).to.equal(0);
+ wrapper.vm.isDeleting = false;
+ wrapper.vm.$nextTick(() => {
+ deleteButton.trigger('click');
+ setTimeout(() => {
+ expect(document.querySelectorAll('.van-dialog').length).to.equal(1);
+
+ wrapper.vm.$on('delete', () => {
+ done();
+ });
+ document.querySelector('.van-dialog__confirm').click();
+ }, 300);
+ });
+ }, 300);
+ });
+
+ it('on change detail', done => {
+ wrapper = mount(AddressEdit);
+
+ wrapper.vm.$on('change-detail', (val) => {
+ expect(val).to.equal('123');
+ done();
+ });
+
+ const field = wrapper.find('.van-field__control')[2];
+ field.element.value = '123';
+ field.trigger('input');
+ });
+
+ it('clear address detail in ios', done => {
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ addressInfo: {
+ address_detail: '123'
+ }
+ }
+ });
+
+ wrapper.vm.isAndriod = false;
+ wrapper.find('.van-field__control')[2].trigger('focus');
+
+ wrapper.vm.$nextTick(() => {
+ wrapper.find('.van-field__icon')[0].trigger('touchstart');
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.vm.addressInfo.address_detail).to.equal('');
+ done();
+ });
+ });
+ });
+
+ it('finish edit address detail in andriod', done => {
+ wrapper = mount(AddressDetail, {
+ propsData: {
+ value: '123'
+ }
+ });
+
+ wrapper.vm.$on('input', val => {
+ wrapper.vm.value = val;
+ });
+
+ wrapper.setData({
+ isAndroid: true
+ });
+ wrapper.find('.van-field__control')[0].trigger('focus');
+
+ wrapper.vm.$nextTick(() => {
+ wrapper.find('.van-field__icon')[0].trigger('touchstart');
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.vm.value).to.equal('123');
+ done();
+ });
+ });
+ });
+
+ it('watch address info', done => {
+ const addressInfo = {
+ user_name: '123'
+ };
+
+ wrapper = mount(AddressEdit, {
+ propsData: {
+ addressInfo: {}
+ }
+ });
+
+ wrapper.setProps({ addressInfo });
+ wrapper.vm.$nextTick(() => {
+ expect(wrapper.vm.currentInfo.user_name).to.equal('123');
+ done();
+ });
+ });
+});
diff --git a/test/unit/specs/dialog.spec.js b/test/unit/specs/dialog.spec.js
index 81d2a212e..ec1bf4a56 100644
--- a/test/unit/specs/dialog.spec.js
+++ b/test/unit/specs/dialog.spec.js
@@ -14,10 +14,9 @@ describe('Dialog', () => {
done();
});
- expect(document.querySelector('.van-dialog')).to.exist;
- expect(document.querySelector('.van-dialog__cancel').style.display).to.equal('none');
-
setTimeout(() => {
+ expect(document.querySelector('.van-dialog')).to.exist;
+ expect(document.querySelector('.van-dialog__cancel').style.display).to.equal('none');
document.querySelector('.van-dialog__confirm').click();
}, 500);
});
diff --git a/test/unit/specs/field.spec.js b/test/unit/specs/field.spec.js
index 5da380573..b0c92c0ee 100644
--- a/test/unit/specs/field.spec.js
+++ b/test/unit/specs/field.spec.js
@@ -136,12 +136,15 @@ describe('Field', () => {
it('blur event', (done) => {
const blur = sinon.spy();
+ const focus = sinon.spy();
const clickIcon = sinon.spy();
wrapper = mount(FieldWithIcon, {});
wrapper.vm.$on('blur', blur);
+ wrapper.vm.$on('focus', focus);
wrapper.find('.van-field__icon')[0].trigger('click');
+ wrapper.find('.van-field__control')[0].trigger('focus');
wrapper.find('.van-field__control')[0].trigger('blur');
expect(blur.calledOnce).to.be.true;