diff --git a/src/contact-card/README.md b/src/contact-card/README.md
index 1b2b91dff..cc84a4253 100644
--- a/src/contact-card/README.md
+++ b/src/contact-card/README.md
@@ -4,120 +4,57 @@
```js
import Vue from 'vue';
-import { ContactCard, ContactList, ContactEdit } from 'vant';
+import { ContactCard } from 'vant';
Vue.use(ContactCard);
-Vue.use(ContactList);
-Vue.use(ContactEdit);
```
## Usage
-### Basic Usage
+### Add Contact
```html
-
-
-
-
-
-
-
-
-
-
-
-
+
```
```js
+import { Toast } from 'vant';
+
+export default {
+ methods: {
+ onAdd() {
+ Toast('add');
+ },
+ },
+};
+```
+
+### Edit Contact
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
export default {
data() {
return {
- chosenContactId: null,
- editingContact: {},
- showList: false,
- showEdit: false,
- isEdit: false,
- list: [
- {
- name: 'John Snow',
- tel: '13000000000',
- id: 0,
- },
- ],
+ currentContact: {
+ name: 'John Snow',
+ tel: '13000000000',
+ },
};
},
-
- computed: {
- cardType() {
- return this.chosenContactId !== null ? 'edit' : 'add';
- },
-
- currentContact() {
- const id = this.chosenContactId;
- return id !== null ? this.list.filter((item) => item.id === id)[0] : {};
- },
- },
-
methods: {
- // add contact
- onAdd() {
- this.editingContact = { id: this.list.length };
- this.isEdit = false;
- this.showEdit = true;
- },
-
- // edit contact
- onEdit(item) {
- this.isEdit = true;
- this.showEdit = true;
- this.editingContact = item;
- },
-
- // select contact
- onSelect() {
- this.showList = false;
- },
-
- // save contact
- onSave(info) {
- this.showEdit = false;
- this.showList = false;
-
- if (this.isEdit) {
- this.list = this.list.map((item) =>
- item.id === info.id ? info : item
- );
- } else {
- this.list.push(info);
- }
- this.chosenContactId = info.id;
- },
-
- // delete contact
- onDelete(info) {
- this.showEdit = false;
- this.list = this.list.filter((item) => item.id !== info.id);
- if (this.chosenContactId === info.id) {
- this.chosenContactId = null;
- }
+ onEdit() {
+ Toast('edit');
},
},
};
@@ -136,62 +73,17 @@ export default {
## API
-### ContactCard Props
+### Props
-| Attribute | Description | Type | Default |
-| --------- | -------------------------- | -------- | ------------------ |
-| type | Can be set to `add` `edit` | _string_ | `add` |
-| name | Name | _string_ | - |
-| tel | Phone | _string_ | - |
-| add-text | Add card text | _string_ | `Add contact info` |
+| Attribute | Description | Type | Default |
+| --------- | -------------------- | -------- | ------------------ |
+| type | Can be set to `edit` | _string_ | `add` |
+| name | Name | _string_ | - |
+| tel | Phone | _string_ | - |
+| add-text | Add card text | _string_ | `Add contact info` |
-### ContactCard Events
+### Events
| Event | Description | Arguments |
| ----- | ---------------------- | -------------- |
| click | Triggered when clicked | _event: Event_ |
-
-### ContactList Props
-
-| Attribute | Description | Type | Default |
-| --- | --- | --- | --- |
-| v-model | Id of chosen contact | _number \| string_ | - |
-| list | Contact list | _Contact[]_ | `[]` |
-| add-text | Add button text | _string_ | `Add new contact` |
-| default-tag-text `v2.3.0` | Default tag text | _string_ | - |
-
-### ContactList Events
-
-| Event | Description | Arguments |
-| ------ | -------------------------------- | --------------------------- |
-| add | Triggered when click add button | - |
-| edit | Triggered when click edit button | item: contact object,index |
-| select | Triggered when select contact | item: contact object |
-
-### ContactEdit Props
-
-| Attribute | Description | Type | Default |
-| --- | --- | --- | --- |
-| contact-info | Contact Info | _object_ | `[]` |
-| is-edit | Whether is editing | _boolean_ | `false` |
-| is-saving | Whether to show save button loading status | _boolean_ | `false` |
-| is-deleting | Whether to show delete button loading status | _boolean_ | `false` |
-| tel-validator | The method to validate tel | _(tel: string) => boolean_ | - |
-| show-set-default `v2.3.0` | Whether to show default contact switch | _boolean_ | `false` |
-| set-default-label `v2.3.0` | default contact switch label | _string_ | - |
-
-### ContactEdit Events
-
-| Event | Description | Arguments |
-| ------ | ---------------------------------- | --------------------- |
-| save | Triggered when click save button | content:contact info |
-| delete | Triggered when click delete button | content:contact info |
-
-### Data Structure of Contact
-
-| key | Description | Type |
-| --------- | ------------------ | ------------------ |
-| id | ID | _number \| string_ |
-| name | Name | _string_ |
-| tel | Phone | _string_ |
-| isDefault | Is default contact | _boolean_ |
diff --git a/src/contact-card/README.zh-CN.md b/src/contact-card/README.zh-CN.md
index ed9e441af..6b3f994b9 100644
--- a/src/contact-card/README.zh-CN.md
+++ b/src/contact-card/README.zh-CN.md
@@ -1,127 +1,64 @@
-# Contact 联系人
+# ContactCard 联系人卡片
### 介绍
-通过 Contact 组件可以实现联系人的展示、选择、编辑等功能。
+以卡片的形式展示联系人信息。
### 引入
```js
import Vue from 'vue';
-import { ContactCard, ContactList, ContactEdit } from 'vant';
+import { ContactCard } from 'vant';
Vue.use(ContactCard);
-Vue.use(ContactList);
-Vue.use(ContactEdit);
```
## 代码演示
-### 基础用法
+### 添加联系人
```html
-
-
-
-
-
-
-
-
-
-
-
-
+
```
```js
+import { Toast } from 'vant';
+
+export default {
+ methods: {
+ onAdd() {
+ Toast('新增');
+ },
+ },
+};
+```
+
+### 编辑联系人
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
export default {
data() {
return {
- chosenContactId: null,
- editingContact: {},
- showList: false,
- showEdit: false,
- isEdit: false,
- list: [
- {
- name: '张三',
- tel: '13000000000',
- id: 0,
- },
- ],
+ currentContact: {
+ name: '张三',
+ tel: '13000000000',
+ },
};
},
-
- computed: {
- cardType() {
- return this.chosenContactId !== null ? 'edit' : 'add';
- },
-
- currentContact() {
- const id = this.chosenContactId;
- return id !== null ? this.list.filter((item) => item.id === id)[0] : {};
- },
- },
-
methods: {
- // 添加联系人
- onAdd() {
- this.editingContact = { id: this.list.length };
- this.isEdit = false;
- this.showEdit = true;
- },
-
- // 编辑联系人
- onEdit(item) {
- this.isEdit = true;
- this.showEdit = true;
- this.editingContact = item;
- },
-
- // 选中联系人
- onSelect() {
- this.showList = false;
- },
-
- // 保存联系人
- onSave(info) {
- this.showEdit = false;
- this.showList = false;
-
- if (this.isEdit) {
- this.list = this.list.map((item) =>
- item.id === info.id ? info : item
- );
- } else {
- this.list.push(info);
- }
- this.chosenContactId = info.id;
- },
-
- // 删除联系人
- onDelete(info) {
- this.showEdit = false;
- this.list = this.list.filter((item) => item.id !== info.id);
- if (this.chosenContactId === info.id) {
- this.chosenContactId = null;
- }
+ onEdit() {
+ Toast('编辑');
},
},
};
@@ -135,62 +72,17 @@ export default {
## API
-### ContactCard Props
+### Props
-| 参数 | 说明 | 类型 | 默认值 |
-| -------- | --------------------------- | -------- | -------------------- |
-| type | 类型,可选值为 `add` `edit` | _string_ | `add` |
-| name | 联系人姓名 | _string_ | - |
-| tel | 联系人手机号 | _string_ | - |
-| add-text | 添加时的文案提示 | _string_ | `添加订单联系人信息` |
+| 参数 | 说明 | 类型 | 默认值 |
+| -------- | ------------------------- | -------- | ------------ |
+| type | 卡片类型,可选值为 `edit` | _string_ | `add` |
+| name | 联系人姓名 | _string_ | - |
+| tel | 联系人手机号 | _string_ | - |
+| add-text | 添加时的文案提示 | _string_ | `添加联系人` |
-### ContactCard Events
+### Events
| 事件名 | 说明 | 回调参数 |
| ------ | ---------- | -------------- |
| click | 点击时触发 | _event: Event_ |
-
-### ContactList Props
-
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| v-model | 当前选中联系人的 id | _number \| string_ | - |
-| list | 联系人列表 | _Contact[]_ | `[]` |
-| add-text | 新建按钮文案 | _string_ | `新建联系人` |
-| default-tag-text `v2.3.0` | 默认联系人标签文案 | _string_ | - |
-
-### ContactList Events
-
-| 事件名 | 说明 | 回调参数 |
-| ------ | ---------------------- | --------------------------------- |
-| add | 点击新增按钮时触发 | - |
-| edit | 点击编辑按钮时触发 | item: 当前联系人对象,index: 索引 |
-| select | 切换选中的联系人时触发 | item: 当前联系人对象,index: 索引 |
-
-### ContactEdit Props
-
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| contact-info | 联系人信息 | _object_ | `[]` |
-| is-edit | 是否为编辑联系人 | _boolean_ | `false` |
-| is-saving | 是否显示保存按钮加载动画 | _boolean_ | `false` |
-| is-deleting | 是否显示删除按钮加载动画 | _boolean_ | `false` |
-| tel-validator | 手机号格式校验函数 | _(tel: string) => boolean_ | - |
-| show-set-default `v2.3.0` | 是否显示默认联系人栏 | _boolean_ | `false` |
-| set-default-label `v2.3.0` | 默认联系人栏文案 | _string_ | - |
-
-### ContactEdit Events
-
-| 事件名 | 说明 | 回调参数 |
-| ------ | ------------------ | ----------------- |
-| save | 点击保存按钮时触发 | content:表单内容 |
-| delete | 点击删除按钮时触发 | content:表单内容 |
-
-### Contact 数据结构
-
-| 键名 | 说明 | 类型 |
-| --------- | -------------------- | ------------------ |
-| id | 每位联系人的唯一标识 | _number \| string_ |
-| name | 联系人姓名 | _string_ |
-| tel | 联系人手机号 | _number \| string_ |
-| isDefault | 是否为默认联系人 | _boolean_ |
diff --git a/src/contact-card/demo/index.vue b/src/contact-card/demo/index.vue
index c1968e315..b128eda89 100644
--- a/src/contact-card/demo/index.vue
+++ b/src/contact-card/demo/index.vue
@@ -1,41 +1,23 @@
-
+
+
+
+
+
-
-
-
-
-
-
-
-
@@ -43,104 +25,43 @@
-
-
diff --git a/src/contact-card/test/__snapshots__/demo.spec.js.snap b/src/contact-card/test/__snapshots__/demo.spec.js.snap
index ac19a8cda..080c4b76f 100644
--- a/src/contact-card/test/__snapshots__/demo.spec.js.snap
+++ b/src/contact-card/test/__snapshots__/demo.spec.js.snap
@@ -8,49 +8,15 @@ exports[`renders demo correctly 1`] = `
diff --git a/src/contact-card/test/index.spec.js b/src/contact-card/test/index.spec.js
index 7fb160c7a..b21628e7f 100644
--- a/src/contact-card/test/index.spec.js
+++ b/src/contact-card/test/index.spec.js
@@ -1,143 +1,33 @@
import ContactCard from '..';
-import ContactList from '../../contact-list';
-import ContactEdit from '../../contact-edit';
-import { mount, later } from '../../../test';
+import { mount } from '../../../test';
-const contactInfo = {
- name: 'test',
- tel: '123123213',
-};
-
-describe('ContactCard', () => {
- test('click event', () => {
- const click = jest.fn();
- const wrapper = mount(ContactCard, {
- context: {
- on: {
- click,
- },
+test('should emit click event after clicking the ContactCard', () => {
+ const click = jest.fn();
+ const wrapper = mount(ContactCard, {
+ context: {
+ on: {
+ click,
},
- });
-
- wrapper.trigger('click');
- expect(click).toHaveBeenCalledTimes(1);
+ },
});
- test('not editable', () => {
- const click = jest.fn();
- const wrapper = mount(ContactCard, {
- propsData: {
- editable: false,
- },
- context: {
- on: {
- click,
- },
- },
- });
-
- wrapper.trigger('click');
- expect(click).toHaveBeenCalledTimes(0);
- });
+ wrapper.trigger('click');
+ expect(click).toHaveBeenCalledTimes(1);
});
-describe('ContactList', () => {
- test('render', () => {
- const wrapper = mount(ContactList, {
- propsData: {
- list: [contactInfo],
+test('should not emit click event after clicking the uneditable ContactCard', () => {
+ const click = jest.fn();
+ const wrapper = mount(ContactCard, {
+ propsData: {
+ editable: false,
+ },
+ context: {
+ on: {
+ click,
},
- });
- expect(wrapper).toMatchSnapshot();
+ },
});
- test('select event', () => {
- const onSelect = jest.fn();
- const wrapper = mount(ContactList, {
- propsData: {
- list: [contactInfo],
- },
- context: {
- on: {
- select: onSelect,
- },
- },
- });
-
- wrapper.find('.van-radio__icon').trigger('click');
-
- expect(onSelect).toHaveBeenCalled();
- });
-});
-
-describe('ContactEdit', () => {
- const createComponent = () => {
- const wrapper = mount(ContactEdit, {
- propsData: {
- contactInfo,
- },
- });
-
- const button = wrapper.find('.van-button');
- const field = wrapper.findAll('.van-field__control');
- const { errorInfo, data } = wrapper.vm;
- return {
- wrapper,
- data,
- field,
- button,
- errorInfo,
- };
- };
-
- test('valid name', () => {
- const { data, field, button, errorInfo } = createComponent();
-
- // name empty
- data.name = '';
- button.trigger('click');
- expect(errorInfo.name).toBeTruthy();
- field.at(0).trigger('focus');
- expect(errorInfo.name).toBeFalsy();
- });
-
- test('valid tel', () => {
- const { data, field, button, errorInfo, wrapper } = createComponent();
- data.tel = '';
- button.trigger('click');
- expect(errorInfo.tel).toBeTruthy();
- field.at(1).trigger('focus');
- expect(errorInfo.tel).toBeFalsy();
-
- data.tel = '13000000000';
- button.trigger('click');
- expect(errorInfo.tel).toBeFalsy();
- expect(wrapper.emitted('save')[0][0]).toEqual({
- name: 'test',
- tel: '13000000000',
- });
- });
-
- test('watch contact info', () => {
- const wrapper = mount(ContactEdit);
- wrapper.setProps({ contactInfo: { name: '123' } });
- expect(wrapper.vm.data.name).toEqual('123');
- });
-
- test('delete contact', async () => {
- const wrapper = mount(ContactEdit, {
- propsData: {
- isEdit: true,
- },
- });
-
- const deleteButton = wrapper.findAll('.van-button').at(1);
- deleteButton.trigger('click');
-
- await later();
- document.querySelector('.van-dialog__confirm').click();
-
- await later();
- expect(wrapper.emitted('delete')).toBeTruthy();
- });
+ wrapper.trigger('click');
+ expect(click).toHaveBeenCalledTimes(0);
});
diff --git a/src/contact-edit/README.md b/src/contact-edit/README.md
new file mode 100644
index 000000000..6a323dbaf
--- /dev/null
+++ b/src/contact-edit/README.md
@@ -0,0 +1,73 @@
+# ContactEdit
+
+### Install
+
+```js
+import Vue from 'vue';
+import { ContactEdit } from 'vant';
+
+Vue.use(ContactEdit);
+```
+
+## Usage
+
+### Basic Usage
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
+export default {
+ data() {
+ return {
+ editingContact: {},
+ };
+ },
+ methods: {
+ onSave(contactInfo) {
+ Toast('Save');
+ },
+ onDelete(contactInfo) {
+ Toast('Delete');
+ },
+ },
+};
+```
+
+## API
+
+### Props
+
+| Attribute | Description | Type | Default |
+| --- | --- | --- | --- |
+| contact-info | Contact Info | _Contact_ | `[]` |
+| is-edit | Whether is editing | _boolean_ | `false` |
+| is-saving | Whether to show save button loading status | _boolean_ | `false` |
+| is-deleting | Whether to show delete button loading status | _boolean_ | `false` |
+| tel-validator | The method to validate tel | _(tel: string) => boolean_ | - |
+| show-set-default `v2.3.0` | Whether to show default contact switch | _boolean_ | `false` |
+| set-default-label `v2.3.0` | default contact switch label | _string_ | - |
+
+### Events
+
+| Event | Description | Arguments |
+| ------ | ---------------------------------- | --------------------- |
+| save | Triggered when click save button | content:contact info |
+| delete | Triggered when click delete button | content:contact info |
+
+### Data Structure of Contact
+
+| key | Description | Type |
+| ---- | ----------- | -------- |
+| name | Name | _string_ |
+| tel | Phone | _string_ |
diff --git a/src/contact-edit/README.zh-CN.md b/src/contact-edit/README.zh-CN.md
new file mode 100644
index 000000000..1e24b2fca
--- /dev/null
+++ b/src/contact-edit/README.zh-CN.md
@@ -0,0 +1,77 @@
+# ContactEdit 联系人编辑
+
+### 介绍
+
+编辑并保存联系人信息。
+
+### 引入
+
+```js
+import Vue from 'vue';
+import { ContactEdit } from 'vant';
+
+Vue.use(ContactEdit);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
+export default {
+ data() {
+ return {
+ editingContact: {},
+ };
+ },
+ methods: {
+ onSave(contactInfo) {
+ Toast('保存');
+ },
+ onDelete(contactInfo) {
+ Toast('删除');
+ },
+ },
+};
+```
+
+## API
+
+### Props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| contact-info | 联系人信息 | _Contact_ | `{}` |
+| is-edit | 是否为编辑联系人 | _boolean_ | `false` |
+| is-saving | 是否显示保存按钮加载动画 | _boolean_ | `false` |
+| is-deleting | 是否显示删除按钮加载动画 | _boolean_ | `false` |
+| tel-validator | 手机号格式校验函数 | _(tel: string) => boolean_ | - |
+| show-set-default `v2.3.0` | 是否显示默认联系人栏 | _boolean_ | `false` |
+| set-default-label `v2.3.0` | 默认联系人栏文案 | _string_ | - |
+
+### Events
+
+| 事件名 | 说明 | 回调参数 |
+| ------ | ------------------ | ----------------- |
+| save | 点击保存按钮时触发 | content:表单内容 |
+| delete | 点击删除按钮时触发 | content:表单内容 |
+
+### Contact 数据结构
+
+| 键名 | 说明 | 类型 |
+| ---- | ------------ | ------------------ |
+| name | 联系人姓名 | _string_ |
+| tel | 联系人手机号 | _number \| string_ |
diff --git a/src/contact-edit/demo/index.vue b/src/contact-edit/demo/index.vue
new file mode 100644
index 000000000..6c947cfe5
--- /dev/null
+++ b/src/contact-edit/demo/index.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/contact-edit/test/__snapshots__/demo.spec.js.snap b/src/contact-edit/test/__snapshots__/demo.spec.js.snap
new file mode 100644
index 000000000..c185ae53b
--- /dev/null
+++ b/src/contact-edit/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/src/contact-edit/test/demo.spec.js b/src/contact-edit/test/demo.spec.js
new file mode 100644
index 000000000..5c70922b5
--- /dev/null
+++ b/src/contact-edit/test/demo.spec.js
@@ -0,0 +1,4 @@
+import Demo from '../demo';
+import { snapshotDemo } from '../../../test/demo';
+
+snapshotDemo(Demo);
diff --git a/src/contact-edit/test/index.spec.js b/src/contact-edit/test/index.spec.js
new file mode 100644
index 000000000..150ba2723
--- /dev/null
+++ b/src/contact-edit/test/index.spec.js
@@ -0,0 +1,77 @@
+import ContactEdit from '..';
+import { mount, later } from '../../../test';
+
+const contactInfo = {
+ name: 'test',
+ tel: '123123213',
+};
+
+const createComponent = () => {
+ const wrapper = mount(ContactEdit, {
+ propsData: {
+ contactInfo,
+ },
+ });
+
+ const button = wrapper.find('.van-button');
+ const field = wrapper.findAll('.van-field__control');
+ const { errorInfo, data } = wrapper.vm;
+ return {
+ wrapper,
+ data,
+ field,
+ button,
+ errorInfo,
+ };
+};
+
+test('should validate contact name before submit form', () => {
+ const { data, field, button, errorInfo } = createComponent();
+
+ // name empty
+ data.name = '';
+ button.trigger('click');
+ expect(errorInfo.name).toBeTruthy();
+ field.at(0).trigger('focus');
+ expect(errorInfo.name).toBeFalsy();
+});
+
+test('should validate contact tel before submit form', () => {
+ const { data, field, button, errorInfo, wrapper } = createComponent();
+ data.tel = '';
+ button.trigger('click');
+ expect(errorInfo.tel).toBeTruthy();
+ field.at(1).trigger('focus');
+ expect(errorInfo.tel).toBeFalsy();
+
+ data.tel = '13000000000';
+ button.trigger('click');
+ expect(errorInfo.tel).toBeFalsy();
+ expect(wrapper.emitted('save')[0][0]).toEqual({
+ name: 'test',
+ tel: '13000000000',
+ });
+});
+
+test('should watch contact info', () => {
+ const wrapper = mount(ContactEdit);
+ wrapper.setProps({ contactInfo: { name: '123' } });
+ expect(wrapper.vm.data.name).toEqual('123');
+});
+
+test('should allow deleting contact', async () => {
+ const wrapper = mount(ContactEdit, {
+ propsData: {
+ isEdit: true,
+ },
+ });
+
+ const deleteButton = wrapper.findAll('.van-button').at(1);
+ deleteButton.trigger('click');
+
+ await later();
+ document.querySelector('.van-dialog__confirm').click();
+
+ await later();
+ expect(wrapper.emitted('delete')).toBeTruthy();
+});
diff --git a/src/contact-list/README.md b/src/contact-list/README.md
new file mode 100644
index 000000000..b61defc8a
--- /dev/null
+++ b/src/contact-list/README.md
@@ -0,0 +1,89 @@
+# ContactList
+
+### Install
+
+```js
+import Vue from 'vue';
+import { ContactList } from 'vant';
+
+Vue.use(ContactList);
+```
+
+## Usage
+
+### Basic Usage
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
+export default {
+ data() {
+ return {
+ chosenContactId: '1',
+ list: [
+ {
+ id: '1',
+ name: 'John Snow',
+ tel: '13000000000',
+ isDefault: true,
+ },
+ {
+ id: '2',
+ name: 'Ned Stark',
+ tel: '1310000000',
+ },
+ ],
+ };
+ },
+ methods: {
+ onAdd() {
+ Toast('Add');
+ },
+ onEdit(contact) {
+ Toast('Edit' + contact.id);
+ },
+ onSelect(contact) {
+ Toast('Select' + contact.id);
+ },
+ },
+};
+```
+
+## API
+
+### Props
+
+| Attribute | Description | Type | Default |
+| --- | --- | --- | --- |
+| v-model | Id of chosen contact | _number \| string_ | - |
+| list | Contact list | _Contact[]_ | `[]` |
+| add-text | Add button text | _string_ | `Add new contact` |
+| default-tag-text `v2.3.0` | Default tag text | _string_ | - |
+
+### Events
+
+| Event | Description | Arguments |
+| --- | --- | --- |
+| add | Triggered when click add button | - |
+| edit | Triggered when click edit button | _contact: Contact,index: number_ |
+| select | Triggered when select contact | _contact: Contact, index: number_ |
+
+### Data Structure of Contact
+
+| key | Description | Type |
+| --------- | ------------------ | ------------------ |
+| id | ID | _number \| string_ |
+| name | Name | _string_ |
+| tel | Phone | _string_ |
+| isDefault | Is default contact | _boolean_ |
diff --git a/src/contact-list/README.zh-CN.md b/src/contact-list/README.zh-CN.md
new file mode 100644
index 000000000..9be5cf312
--- /dev/null
+++ b/src/contact-list/README.zh-CN.md
@@ -0,0 +1,93 @@
+# ContactList 联系人列表
+
+### 介绍
+
+展示联系人列表。
+
+### 引入
+
+```js
+import Vue from 'vue';
+import { ContactList } from 'vant';
+
+Vue.use(ContactList);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+
+```
+
+```js
+import { Toast } from 'vant';
+
+export default {
+ data() {
+ return {
+ chosenContactId: '1',
+ list: [
+ {
+ id: '1',
+ name: '张三',
+ tel: '13000000000',
+ isDefault: true,
+ },
+ {
+ id: '2',
+ name: '李四',
+ tel: '1310000000',
+ },
+ ],
+ };
+ },
+ methods: {
+ onAdd() {
+ Toast('新增');
+ },
+ onEdit(contact) {
+ Toast('编辑' + contact.id);
+ },
+ onSelect(contact) {
+ Toast('选择' + contact.id);
+ },
+ },
+};
+```
+
+## API
+
+### Props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| v-model | 当前选中联系人的 id | _number \| string_ | - |
+| list | 联系人列表 | _Contact[]_ | `[]` |
+| add-text | 新建按钮文案 | _string_ | `新建联系人` |
+| default-tag-text `v2.3.0` | 默认联系人标签文案 | _string_ | - |
+
+### Events
+
+| 事件名 | 说明 | 回调参数 |
+| ------ | ---------------------- | --------------------------------- |
+| add | 点击新增按钮时触发 | - |
+| edit | 点击编辑按钮时触发 | _contact: Contact,index: number_ |
+| select | 切换选中的联系人时触发 | _contact: Contact,index: number_ |
+
+### Contact 数据结构
+
+| 键名 | 说明 | 类型 |
+| --------- | -------------------- | ------------------ |
+| id | 每位联系人的唯一标识 | _number \| string_ |
+| name | 联系人姓名 | _string_ |
+| tel | 联系人手机号 | _number \| string_ |
+| isDefault | 是否为默认联系人 | _boolean_ |
diff --git a/src/contact-list/demo/index.vue b/src/contact-list/demo/index.vue
new file mode 100644
index 000000000..a106b1f02
--- /dev/null
+++ b/src/contact-list/demo/index.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/contact-list/test/__snapshots__/demo.spec.js.snap b/src/contact-list/test/__snapshots__/demo.spec.js.snap
new file mode 100644
index 000000000..e0365b24f
--- /dev/null
+++ b/src/contact-list/test/__snapshots__/demo.spec.js.snap
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders demo correctly 1`] = `
+
+`;
diff --git a/src/contact-card/test/__snapshots__/index.spec.js.snap b/src/contact-list/test/__snapshots__/index.spec.js.snap
similarity index 95%
rename from src/contact-card/test/__snapshots__/index.spec.js.snap
rename to src/contact-list/test/__snapshots__/index.spec.js.snap
index 5c4e54037..8946ba610 100644
--- a/src/contact-card/test/__snapshots__/index.spec.js.snap
+++ b/src/contact-list/test/__snapshots__/index.spec.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`ContactList render 1`] = `
+exports[`should render ContactList correctly 1`] = `