Merge branch 'dev' into next

This commit is contained in:
陈嘉涵
2020-02-05 16:40:04 +08:00
623 changed files with 18421 additions and 9855 deletions
+64 -55
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { ActionSheet } from 'vant';
@@ -13,13 +13,15 @@ Vue.use(ActionSheet);
### Basic Usage
Use `actions` prop to set options of action-sheet.
Use `actions` prop to set options of action-sheet.
```html
<van-action-sheet v-model="show" :actions="actions" @select="onSelect" />
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -31,38 +33,51 @@ export default {
]
};
},
methods: {
onSelect(item) {
this.show = false;
Toast(item.name);
}
}
}
};
```
### Status
### Show Cancel Button
```html
<van-action-sheet v-model="show" :actions="actions" />
<van-action-sheet
v-model="show"
:actions="actions"
cancel-text="Cancel"
@cancel="onCancel"
/>
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
show: false,
actions: [
{ name: 'Option', color: '#07c160' },
{ loading: true },
{ name: 'Disabled Option', disabled: true }
]
show: false
};
},
methods: {
onCancel() {
this.show = false;
Toast('cancel');
}
}
}
};
```
### ActionSheet with cancel button
### Show Description
```html
<van-action-sheet v-model="show" :actions="actions" description="Description" />
```
### Option Status
```html
<van-action-sheet
@@ -77,35 +92,29 @@ export default {
export default {
data() {
return {
show: false
show: false,
actions: [
{ name: 'Option', color: '#07c160' },
{ loading: true },
{ name: 'Disabled Option', disabled: true }
]
};
},
methods: {
onCancel() {
this.show = false;
Toast('cancel');
}
}
}
};
```
### Show Description
```html
<van-action-sheet
v-model="show"
:actions="actions"
description="Description"
/>
```
### ActionSheet with title
### Custom Panel
```html
<van-action-sheet v-model="show" title="Title">
<p>Content</p>
<div class="content">Content</div>
</van-action-sheet>
<style>
.content {
padding: 16px 16px 160px;
}
</style>
```
## API
@@ -114,20 +123,31 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| actions | Options | *Action[]* | `[]` |
| actions | Options | *Action[]* | `[]` |
| title | Title | *string* | - |
| cancel-text | Text of cancel button | *string* | - |
| description `v2.2.8` | Description above the options | *string* | - |
| overlay | Whether to show overlay | *boolean* | `true` |
| round `v2.0.9` | Whether to show round corner | *boolean* | `true` |
| close-icon `v2.2.13` | Close icon name | *string* | `cross` |
| duration `v2.0.3` | Transition duration, unit second | *number \| string* | `0.3` |
| round `v2.0.9` | Whether to show round corner | *boolean* | `true` |
| overlay | Whether to show overlay | *boolean* | `true` |
| lock-scroll | Whether to lock background scroll | *boolean* | `true` |
| lazy-render | Whether to lazy render util appeared | *boolean* | `true` |
| close-on-click-action | Whether to close when click action | *boolean* | `false` |
| close-on-click-overlay | Whether to close when click overlay | *boolean* | `true` |
| lazy-render | Whether to lazy render util appeared | *boolean* | `true` |
| lock-scroll | Whether to lock background scroll | *boolean* | `true` |
| duration `v2.0.3` | Transition duration, unit second | *number* | `0.3` |
| get-container | Return the mount node for action-sheet | *string \| () => Element* | - |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation | *boolean* | `true` |
| get-container | Return the mount node for ActionSheet | *string \| () => Element* | - |
### Data Structure of Action
| Key | Description | Type |
|------|------|------|
| name | Title | *string* |
| subname | Subtitle | *string* |
| color | Text color | *string* |
| className | className for the option | *any* |
| loading | Whether to be loading status | *boolean* |
| disabled | Whether to be disabled | *boolean* |
### Events
@@ -140,14 +160,3 @@ export default {
| opened | Triggered when opened ActionSheet | - |
| close | Triggered when close ActionSheet | - |
| closed | Triggered when closed ActionSheet | - |
### Data Structure of Action
| Key | Description | Type |
|------|------|------|
| name | Title | *string* |
| subname | Subtitle | *string* |
| color | Text color | *string* |
| className | className for the option | *any* |
| loading | Whether to be loading status | *boolean* |
| disabled | Whether to be disabled | *boolean* |
+63 -50
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { ActionSheet } from 'vant';
@@ -19,7 +19,9 @@ Vue.use(ActionSheet);
<van-action-sheet v-model="show" :actions="actions" @select="onSelect" />
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -31,11 +33,10 @@ export default {
]
};
},
methods: {
onSelect(item) {
// 默认情况下点击选项时不会自动关闭菜单
// 可以通过 close-on-click-action 属性开启自动关闭
// 默认情况下点击选项时不会自动收起
// 可以通过 close-on-click-action 属性开启自动收起
this.show = false;
Toast(item.name);
}
@@ -43,29 +44,6 @@ export default {
}
```
### 选项状态
选项可以设置为加载状态或禁用状态,也可以通过`color`设置选项颜色
```html
<van-action-sheet v-model="show" :actions="actions" />
```
```javascript
export default {
data() {
return {
show: false,
actions: [
{ name: '选项', color: '#07c160' },
{ loading: true },
{ name: '禁用选项', disabled: true }
]
};
}
}
```
### 展示取消按钮
设置`cancel-text`属性后,会在底部展示取消按钮,点击后关闭当前菜单
@@ -80,13 +58,14 @@ export default {
```
```js
import { Toast } from 'vant';
export default {
data() {
return {
show: false
};
},
methods: {
onCancel() {
this.show = false;
@@ -108,14 +87,48 @@ export default {
/>
```
### 展示标题栏
### 选项状态
通过设置`title`属性展示标题栏,同时可以使用插槽自定义菜单内容
可以将选项设置为加载状态或禁用状态,或者通过`color`设置选项颜色
```html
<van-action-sheet
v-model="show"
:actions="actions"
cancel-text="取消"
@cancel="onCancel"
/>
```
```js
export default {
data() {
return {
show: false,
actions: [
{ name: '选项', color: '#07c160' },
{ loading: true },
{ name: '禁用选项', disabled: true }
]
};
}
}
```
### 自定义面板
通过插槽可以自定义菜单的展示内容,同时可以使用`title`属性展示标题栏
```html
<van-action-sheet v-model="show" title="标题">
<p>内容</p>
<div class="content">内容</div>
</van-action-sheet>
<style>
.content {
padding: 16px 16px 160px;
}
</style>
```
## API
@@ -128,28 +141,16 @@ export default {
| title | 顶部标题 | *string* | - |
| cancel-text | 取消按钮文字 | *string* | - |
| description `v2.2.8` | 选项上方的描述信息 | *string* | - |
| overlay | 是否显示遮罩层 | *boolean* | `true` |
| close-icon `v2.2.13` | 关闭 [图标名称](#/zh-CN/icon) 或图片链接 | *string* | `cross` |
| duration `v2.0.3` | 动画时长,单位秒 | *number \| string* | `0.3` |
| round `v2.0.9` | 是否显示圆角 | *boolean* | `true` |
| close-icon `v2.2.13` | 关闭图标名称或图片链接 | *string* | `cross` |
| overlay | 是否显示遮罩层 | *boolean* | `true` |
| lock-scroll | 是否锁定背景滚动 | *boolean* | `true` |
| lazy-render | 是否在显示弹层时才渲染节点 | *boolean* | `true` |
| close-on-click-action | 是否在点击选项后关闭 | *boolean* | `false` |
| close-on-click-overlay | 是否在点击遮罩层后关闭 | *boolean* | `true` |
| lazy-render | 是否在显示弹层时才渲染节点 | *boolean* | `true` |
| lock-scroll | 是否锁定背景滚动 | *boolean* | `true` |
| duration `v2.0.3` | 动画时长,单位秒 | *number* | `0.3` |
| safe-area-inset-bottom | 是否开启 [底部安全区适配](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `true` |
| get-container | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | *string \| () => Element* | - |
| safe-area-inset-bottom | 是否开启底部安全区适配,[详细说明](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `true` |
### Events
| 事件名 | 说明 | 回调参数 |
|------|------|------|
| select | 选中选项时触发,禁用或加载状态下不会触发 | item: 选项对应的对象, index: 选择对应的索引 |
| cancel | 取消按钮点击时触发 | - |
| click-overlay | 点击遮罩层时触发 | - |
| open | 打开菜单时触发 | - |
| opened | 打开菜单且动画结束后触发 | - |
| close | 关闭菜单时触发 | - |
| closed | 关闭菜单且动画结束后触发 | - |
### Action 数据结构
@@ -164,6 +165,18 @@ export default {
| loading | 是否为加载状态 | *boolean* |
| disabled | 是否为禁用状态 | *boolean* |
### Events
| 事件名 | 说明 | 回调参数 |
|------|------|------|
| select | 选中选项时触发,禁用或加载状态下不会触发 | item: 选项对应的对象, index: 选择对应的索引 |
| cancel | 取消按钮点击时触发 | - |
| click-overlay | 点击遮罩层时触发 | - |
| open | 打开菜单时触发 | - |
| opened | 打开菜单且动画结束后触发 | - |
| close | 关闭菜单时触发 | - |
| closed | 关闭菜单且动画结束后触发 | - |
## 常见问题
### 引入时提示 dependencies not found
+71 -59
View File
@@ -1,42 +1,58 @@
<template>
<demo-section>
<demo-block :title="$t('basicUsage')">
<van-button type="primary" @click="show1 = true">{{ $t('buttonText') }}</van-button>
<van-action-sheet v-model="show1" :actions="simpleActions" @select="onSelect" />
</demo-block>
<demo-block :title="$t('status')">
<van-button type="primary" @click="show2 = true">{{ $t('buttonText') }}</van-button>
<van-action-sheet v-model="show2" close-on-click-action :actions="statusActions" />
</demo-block>
<demo-block :title="$t('showCancel')">
<van-button type="primary" @click="show3 = true">{{ $t('buttonText') }}</van-button>
<van-action-sheet
v-model="show3"
:actions="simpleActions"
close-on-click-action
:cancel-text="$t('cancel')"
@cancel="onCancel"
<van-cell is-link :title="$t('basicUsage')" @click="show.basic = true" />
<van-cell is-link :title="$t('showCancel')" @click="show.cancel = true" />
<van-cell
is-link
:title="$t('showDescription')"
@click="show.description = true"
/>
</demo-block>
<demo-block :title="$t('showDescription')">
<van-button type="primary" @click="show4 = true">{{ $t('buttonText') }}</van-button>
<van-action-sheet
v-model="show4"
:actions="simpleActions"
close-on-click-action
:description="$t('description')"
<demo-block :title="$t('optionStatus')">
<van-cell
is-link
:title="$t('optionStatus')"
@click="show.status = true"
/>
</demo-block>
<demo-block :title="$t('showTitle')">
<van-button type="primary" @click="show5 = true">{{ $t('buttonText') }}</van-button>
<van-action-sheet v-model="show5" :title="$t('title')">
<p>{{ $t('content') }}</p>
</van-action-sheet>
<demo-block :title="$t('customPanel')">
<van-cell is-link :title="$t('customPanel')" @click="show.title = true" />
</demo-block>
<van-action-sheet
v-model="show.basic"
:actions="simpleActions"
@select="onSelect"
/>
<van-action-sheet
v-model="show.status"
close-on-click-action
:actions="statusActions"
:cancel-text="$t('cancel')"
/>
<van-action-sheet
v-model="show.cancel"
:actions="simpleActions"
close-on-click-action
:cancel-text="$t('cancel')"
@cancel="onCancel"
/>
<van-action-sheet
v-model="show.description"
:actions="simpleActions"
close-on-click-action
:description="$t('description')"
/>
<van-action-sheet v-model="show.title" :title="$t('title')">
<div class="demo-action-sheet-content">{{ $t('content') }}</div>
</van-action-sheet>
</demo-section>
</template>
@@ -46,34 +62,36 @@ import { GREEN } from '../../utils/constant';
export default {
i18n: {
'zh-CN': {
buttonText: '弹出菜单',
status: '选项状态',
subname: '副文本',
disabledOption: '禁用选项',
showTitle: '展示标题栏',
showCancel: '展示取消按钮',
buttonText: '弹出菜单',
customPanel: '自定义面板',
description: '这是一段描述信息',
optionStatus: '选项状态',
disabledOption: '禁用选项',
showDescription: '展示描述信息',
description: '这是一段描述信息'
},
'en-US': {
buttonText: 'Show ActionSheet',
status: 'Status',
subname: 'Subname',
showCancel: 'Show Cancel Button',
buttonText: 'Show ActionSheet',
customPanel: 'Custom Panel',
description: 'Description',
optionStatus: 'Option Status',
disabledOption: 'Disabled Option',
showTitle: 'ActionSheet with title',
showCancel: 'ActionSheet with cancel button',
showDescription: 'ActionSheet with description',
description: 'Description'
}
showDescription: 'Show Description',
},
},
data() {
return {
show1: false,
show2: false,
show3: false,
show4: false,
show5: false
show: {
basic: false,
cancel: false,
title: false,
status: false,
description: false,
},
};
},
@@ -82,7 +100,7 @@ export default {
return [
{ name: this.$t('option') },
{ name: this.$t('option') },
{ name: this.$t('option'), subname: this.$t('subname') }
{ name: this.$t('option'), subname: this.$t('subname') },
];
},
@@ -90,21 +108,21 @@ export default {
return [
{ name: this.$t('option'), color: GREEN },
{ loading: true },
{ name: this.$t('disabledOption'), disabled: true }
{ name: this.$t('disabledOption'), disabled: true },
];
}
},
},
methods: {
onSelect(item) {
this.show1 = false;
this.show.basic = false;
this.$toast(item.name);
},
onCancel() {
this.$toast('cancel');
}
}
},
},
};
</script>
@@ -112,13 +130,7 @@ export default {
@import '../../style/var';
.demo-action-sheet {
background-color: @white;
.van-button {
margin-left: @padding-md;
}
p {
&-content {
padding: @padding-md @padding-md @padding-md * 10;
}
}
+14 -9
View File
@@ -1,7 +1,12 @@
// Utils
import { createNamespace } from '../utils';
import { emit, inherit } from '../utils/functional';
import { BORDER_TOP } from '../utils/constant';
// Mixins
import { popupMixinProps } from '../mixins/popup';
// Components
import Icon from '../icon';
import Popup from '../popup';
import Loading from '../loading';
@@ -25,7 +30,7 @@ export type ActionSheetProps = PopupMixinProps & {
round: boolean;
title?: string;
actions?: ActionSheetItem[];
duration: number;
duration: number | string;
closeIcon: string;
cancelText?: string;
description?: string;
@@ -97,7 +102,7 @@ function ActionSheet(
return [
<span class={bem('name')}>{item.name}</span>,
item.subname && <span class={bem('subname')}>{item.subname}</span>
item.subname && <span class={bem('subname')}>{item.subname}</span>,
];
}
@@ -155,31 +160,31 @@ ActionSheet.props = {
...popupMixinProps,
title: String,
actions: Array,
duration: Number,
duration: [Number, String],
cancelText: String,
description: String,
getContainer: [String, Function],
closeOnClickAction: Boolean,
round: {
type: Boolean,
default: true
default: true,
},
closeIcon: {
type: String,
default: 'cross'
default: 'cross',
},
safeAreaInsetBottom: {
type: Boolean,
default: true
default: true,
},
overlay: {
type: Boolean,
default: true
default: true,
},
closeOnClickOverlay: {
type: Boolean,
default: true
}
default: true,
},
};
export default createComponent<ActionSheetProps>(ActionSheet);
@@ -2,20 +2,36 @@
exports[`renders demo correctly 1`] = `
<div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">弹出菜单</span></button>
<!---->
<div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>基础用法</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>展示取消按钮</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>展示描述信息</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
</div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">弹出菜单</span></button>
<!---->
<div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>选项状态</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
</div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">弹出菜单</span></button>
<!---->
</div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">弹出菜单</span></button>
<!---->
</div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">弹出菜单</span></button>
<!---->
<div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>自定义面板</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
</div>
<!---->
<!---->
<!---->
<!---->
<!---->
</div>
`;
+28 -31
View File
@@ -18,15 +18,15 @@ test('callback events', () => {
propsData: {
value: true,
actions,
cancelText: 'Cancel'
cancelText: 'Cancel',
},
context: {
on: {
input: onInput,
cancel: onCancel,
select: onSelect
}
}
select: onSelect,
},
},
});
const options = wrapper.findAll('.van-action-sheet__item');
@@ -58,17 +58,17 @@ test('click overlay and close', async () => {
</div>
`,
components: {
ActionSheet
ActionSheet,
},
data() {
return {
getContainer: () => div
getContainer: () => div,
};
},
methods: {
onInput,
onClickOverlay
}
onClickOverlay,
},
});
await later();
@@ -82,12 +82,9 @@ test('disable lazy-render', () => {
const wrapper = mount(ActionSheet, {
propsData: {
lazyRender: false,
actions: [
{ name: 'Option' },
{ name: 'Option' }
],
cancelText: 'Cancel'
}
actions: [{ name: 'Option' }, { name: 'Option' }],
cancelText: 'Cancel',
},
});
expect(wrapper).toMatchSnapshot();
@@ -97,13 +94,13 @@ test('render title and default slot', () => {
const wrapper = mount(ActionSheet, {
propsData: {
value: true,
title: 'Title'
title: 'Title',
},
scopedSlots: {
default() {
return 'Default';
}
}
},
},
});
expect(wrapper).toMatchSnapshot();
@@ -113,8 +110,8 @@ test('get container', () => {
const wrapper = mount(ActionSheet, {
propsData: {
value: true,
getContainer: 'body'
}
getContainer: 'body',
},
});
expect(wrapper.vm.$el.parentNode).toEqual(document.body);
@@ -126,13 +123,13 @@ test('close-on-click-action prop', () => {
propsData: {
value: true,
actions: [{ name: 'Option' }],
closeOnClickAction: true
closeOnClickAction: true,
},
context: {
on: {
input: onInput
}
}
input: onInput,
},
},
});
const option = wrapper.find('.van-action-sheet__item');
@@ -146,8 +143,8 @@ test('round prop', () => {
propsData: {
value: true,
round: true,
actions: [{ name: 'Option' }]
}
actions: [{ name: 'Option' }],
},
});
expect(wrapper).toMatchSnapshot();
@@ -157,8 +154,8 @@ test('color option', () => {
const wrapper = mount(ActionSheet, {
propsData: {
value: true,
actions: [{ name: 'Option', color: 'red' }]
}
actions: [{ name: 'Option', color: 'red' }],
},
});
expect(wrapper).toMatchSnapshot();
@@ -169,8 +166,8 @@ test('description prop', () => {
propsData: {
value: true,
description: 'This is a description',
actions: [{ name: 'Option' }]
}
actions: [{ name: 'Option' }],
},
});
expect(wrapper).toMatchSnapshot();
@@ -181,8 +178,8 @@ test('close-icon prop', () => {
propsData: {
value: true,
title: 'Title',
closeIcon: 'cross'
}
closeIcon: 'cross',
},
});
expect(wrapper).toMatchSnapshot();
+10 -7
View File
@@ -1,5 +1,8 @@
// Utils
import { createNamespace } from '../utils';
import { isAndroid } from '../utils/validate/system';
// Components
import Cell from '../cell';
import Field from '../field';
@@ -11,16 +14,16 @@ export default createComponent({
value: String,
errorMessage: String,
focused: Boolean,
detailRows: Number,
detailRows: [Number, String],
searchResult: Array,
detailMaxlength: Number,
showSearchResult: Boolean
detailMaxlength: [Number, String],
showSearchResult: Boolean,
},
computed: {
shouldShowSearchResult() {
return this.focused && this.searchResult && this.showSearchResult;
}
},
},
methods: {
@@ -71,12 +74,12 @@ export default createComponent({
return <div domPropsInnerHTML={text} />;
}
}
},
}}
/>
));
}
}
},
},
render() {
@@ -100,5 +103,5 @@ export default createComponent({
{this.genSearchResult()}
</Cell>
);
}
},
});
+8 -7
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { AddressEdit } from 'vant';
@@ -28,7 +28,9 @@ Vue.use(AddressEdit);
/>
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -36,7 +38,6 @@ export default {
searchResult: []
}
},
methods: {
onSave() {
Toast('save');
@@ -74,13 +75,13 @@ export default {
| show-search-result | Whether to show address search result | *boolean* | `false` |
| save-button-text | Save button text | *string* | `Save` |
| delete-button-text | Delete button text | *string* | `Delete` |
| detail-rows | Detail input rows | *number* | `1` |
| detail-maxlength `v2.0.4` | Detail maxlength | *number* | `200` |
| detail-rows | Detail input rows | *number \| string* | `1` |
| detail-maxlength `v2.0.4` | Detail maxlength | *number \| string* | `200` |
| 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* | - |
| postal-validator `v2.1.2` | The method to validate postal | *(tel: string) => boolean* | - |
| validator | Custom validator | *(key, value) => string* | - |
| validator | Custom validator | *(key, val) => string* | - |
### Events
@@ -113,7 +114,7 @@ Use [ref](https://vuejs.org/v2/api/#ref) to get AddressEdit instance and call in
| key | Description | Type |
|------|------|------|
| id | Address Id | *string \| number* |
| id | Address Id | *number \| string* |
| name | Name | *string* |
| tel | Phone | *string* |
| province | Province | *string* |
+9 -8
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { AddressEdit } from 'vant';
@@ -28,7 +28,9 @@ Vue.use(AddressEdit);
/>
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -36,7 +38,6 @@ export default {
searchResult: []
}
},
methods: {
onSave() {
Toast('save');
@@ -74,13 +75,13 @@ export default {
| show-search-result | 是否显示搜索结果 | *boolean* | `false` |
| save-button-text | 保存按钮文字 | *string* | `保存` |
| delete-button-text | 删除按钮文字 | *string* | `删除` |
| detail-rows | 详细地址输入框行数 | *number* | `1` |
| detail-maxlength `v2.0.4` | 详细地址最大长度 | *number* | `200` |
| detail-rows | 详细地址输入框行数 | *number \| string* | `1` |
| detail-maxlength `v2.0.4` | 详细地址最大长度 | *number \| string* | `200` |
| is-saving | 是否显示保存按钮加载动画 | *boolean* | `false` |
| is-deleting | 是否显示删除按钮加载动画 | *boolean* | `false` |
| tel-validator | 手机号格式校验函数 | *string => boolean* | - |
| postal-validator `v2.1.2` | 邮政编码格式校验函数 | *string => boolean* | - |
| validator | 自定义校验函数 | *(key, value) => string* | - |
| validator | 自定义校验函数 | *(key, val) => string* | - |
### Events
@@ -103,7 +104,7 @@ export default {
### 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 AddressEdit 实例并调用实例方法
通过 ref 可以获取到 AddressEdit 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
@@ -115,7 +116,7 @@ export default {
| key | 说明 | 类型 |
|------|------|------|
| id | 每条地址的唯一标识 | *string \| number* |
| id | 每条地址的唯一标识 | *number \| string* |
| name | 收货人姓名 | *string* |
| tel | 收货人手机号 | *string* |
| province | 省份 | *string* |
+30 -22
View File
@@ -24,34 +24,42 @@ export default {
i18n: {
'zh-CN': {
areaColumnsPlaceholder: ['请选择', '请选择', '请选择'],
searchResult: [{
name: '黄龙万科中心',
address: '杭州市西湖区'
}, {
name: '黄龙万科中心G座'
}, {
name: '黄龙万科中心H座',
address: '杭州市西湖区'
}]
searchResult: [
{
name: '黄龙万科中心',
address: '杭州市西湖区',
},
{
name: '黄龙万科中心G座',
},
{
name: '黄龙万科中心H座',
address: '杭州市西湖区',
},
],
},
'en-US': {
areaColumnsPlaceholder: ['Choose', 'Choose', 'Choose'],
searchResult: [{
name: 'Name1',
address: 'Address'
}, {
name: 'Name2'
}, {
name: 'Name3',
address: 'Address'
}]
}
searchResult: [
{
name: 'Name1',
address: 'Address',
},
{
name: 'Name2',
},
{
name: 'Name3',
address: 'Address',
},
],
},
},
data() {
return {
areaList,
searchResult: []
searchResult: [],
};
},
@@ -66,8 +74,8 @@ export default {
onChangeDetail(val) {
this.searchResult = val ? this.$t('searchResult') : [];
}
}
},
},
};
</script>
+26 -23
View File
@@ -1,5 +1,8 @@
import { createNamespace, isObj } from '../utils';
// Utils
import { createNamespace, isObject } from '../utils';
import { isMobile } from '../utils/validate/mobile';
// Components
import Area from '../area';
import Field from '../field';
import Popup from '../popup';
@@ -21,7 +24,7 @@ const defaultData = {
areaCode: '',
postalCode: '',
addressDetail: '',
isDefault: false
isDefault: false,
};
function isPostal(value) {
@@ -43,36 +46,36 @@ export default createComponent({
deleteButtonText: String,
showArea: {
type: Boolean,
default: true
default: true,
},
showDetail: {
type: Boolean,
default: true
default: true,
},
detailRows: {
type: Number,
default: 1
type: [Number, String],
default: 1,
},
detailMaxlength: {
type: Number,
default: 200
type: [Number, String],
default: 200,
},
addressInfo: {
type: Object,
default: () => ({ ...defaultData })
default: () => ({ ...defaultData }),
},
telValidator: {
type: Function,
default: isMobile
default: isMobile,
},
postalValidator: {
type: Function,
default: isPostal
default: isPostal,
},
areaColumnsPlaceholder: {
type: Array,
default: () => []
}
default: () => [],
},
},
data() {
@@ -85,14 +88,14 @@ export default createComponent({
name: '',
areaCode: '',
postalCode: '',
addressDetail: ''
}
addressDetail: '',
},
};
},
computed: {
areaListLoaded() {
return isObj(this.areaList) && Object.keys(this.areaList).length;
return isObject(this.areaList) && Object.keys(this.areaList).length;
},
areaText() {
@@ -105,7 +108,7 @@ export default createComponent({
return arr.filter(text => text).join('/');
}
return '';
}
},
},
watch: {
@@ -113,18 +116,18 @@ export default createComponent({
handler(val) {
this.data = {
...defaultData,
...val
...val,
};
this.setAreaCode(val.areaCode);
},
deep: true,
immediate: true
immediate: true,
},
areaList() {
this.setAreaCode(this.data.areaCode);
}
},
},
methods: {
@@ -209,7 +212,7 @@ export default createComponent({
onDelete() {
Dialog.confirm({
title: t('confirmDelete')
title: t('confirmDelete'),
})
.then(() => {
this.$emit('delete', this.data);
@@ -243,7 +246,7 @@ export default createComponent({
setTimeout(() => {
this.detailFocused = false;
});
}
},
},
render() {
@@ -368,5 +371,5 @@ export default createComponent({
</Popup>
</div>
);
}
},
});
@@ -20,7 +20,7 @@ exports[`renders demo correctly 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -18,7 +18,7 @@ exports[`create a AddressEdit 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -57,7 +57,7 @@ exports[`create a AddressEdit with props 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -110,7 +110,7 @@ exports[`set-default 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -163,7 +163,7 @@ exports[`show area component 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -216,7 +216,7 @@ exports[`show area component 2`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
@@ -269,7 +269,7 @@ exports[`valid area placeholder confirm 1`] = `
<div role="button" tabindex="0" class="van-cell van-cell--clickable van-field">
<div class="van-cell__title van-field__label"><span>地区</span></div>
<div class="van-cell__value">
<div class="van-field__body"><input type="text" placeholder="选择省 / 市 / 区" readonly="readonly" class="van-field__control">
<div class="van-field__body"><input type="text" readonly="readonly" placeholder="选择省 / 市 / 区" class="van-field__control">
<div class="van-field__right-icon"><i class="van-icon van-icon-arrow">
<!----></i></div>
</div>
+20 -20
View File
@@ -11,7 +11,7 @@ const addressInfo = {
addressDetail: '详细地址',
areaCode: '110101',
postalCode: '10000',
isDefault: true
isDefault: true,
};
const createComponent = () => {
@@ -20,8 +20,8 @@ const createComponent = () => {
areaList,
addressInfo,
showPostal: true,
showSetDefault: true
}
showSetDefault: true,
},
});
const button = wrapper.find('.van-button');
@@ -33,7 +33,7 @@ const createComponent = () => {
field,
button,
wrapper,
errorInfo
errorInfo,
};
};
@@ -48,8 +48,8 @@ test('create a AddressEdit with props', () => {
addressInfo,
showPostal: true,
showSetDefault: true,
showSearchResult: true
}
showSearchResult: true,
},
});
expect(wrapper).toMatchSnapshot();
@@ -60,7 +60,7 @@ test('valid area placeholder confirm', async () => {
propsData: {
areaList,
areaColumnsPlaceholder: ['请选择', '请选择', '请选择'],
}
},
});
const { data } = wrapper.vm;
@@ -100,8 +100,8 @@ test('validator props', async () => {
areaList,
validator(key, value) {
return `${key}${value}`;
}
}
},
},
});
const { errorInfo, data } = wrapper.vm;
@@ -192,13 +192,13 @@ test('watch address info', () => {
test('set/get area code', async () => {
const wrapper = mount(AddressEdit, {
propsData: { areaList }
propsData: { areaList },
});
expect(wrapper.vm.getArea()).toEqual([
{ code: '110000', name: '北京市' },
{ code: '110100', name: '北京市' },
{ code: '110101', name: '东城区' }
{ code: '110101', name: '东城区' },
]);
wrapper.vm.setAreaCode('110102');
@@ -208,7 +208,7 @@ test('set/get area code', async () => {
expect(wrapper.vm.getArea()).toEqual([
{ code: '110000', name: '北京市' },
{ code: '110100', name: '北京市' },
{ code: '110102', name: '西城区' }
{ code: '110102', name: '西城区' },
]);
wrapper.vm.$refs = [];
@@ -221,9 +221,9 @@ test('watch area code', async () => {
propsData: {
areaList: {},
addressInfo: {
areaCode: '110101'
}
}
areaCode: '110101',
},
},
});
expect(wrapper.vm.data.city).toEqual('');
@@ -240,9 +240,9 @@ test('show search result', async () => {
searchResult: [
{ name: 'name1', address: 'address1' },
{ name: 'name2' },
{ address: 'address2' }
]
}
{ address: 'address2' },
],
},
});
const field = wrapper.findAll('.van-field__control').at(3);
@@ -266,8 +266,8 @@ test('delete address', async () => {
const wrapper = mount(AddressEdit, {
attachToDocument: true,
propsData: {
showDelete: true
}
showDelete: true,
},
});
const deleteButton = wrapper.findAll('.van-button').at(1);
+7 -4
View File
@@ -1,9 +1,12 @@
// Utils
import { createNamespace } from '../utils';
import { emit, inherit } from '../utils/functional';
// Components
import Tag from '../tag';
import Icon from '../icon';
import Cell from '../cell';
import Radio from '../radio';
import Tag from '../tag';
// Types
import { CreateElement, RenderContext } from 'vue/types';
@@ -77,7 +80,7 @@ function AddressItem(
{`${data.name} ${data.tel}`}
{genTag()}
</div>,
<div class={bem('address')}>{data.address}</div>
<div class={bem('address')}>{data.address}</div>,
];
if (switchable && !disabled) {
@@ -99,7 +102,7 @@ function AddressItem(
clickable={switchable && !disabled}
scopedSlots={{
default: genContent,
'right-icon': genRightIcon
'right-icon': genRightIcon,
}}
onClick={onClick}
{...inherit(ctx)}
@@ -111,7 +114,7 @@ AddressItem.props = {
data: Object,
disabled: Boolean,
switchable: Boolean,
defaultTagText: String
defaultTagText: String,
};
export default createComponent<AddressItemProps, AddressItemEvents>(
+6 -5
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { AddressList } from 'vant';
@@ -25,7 +25,9 @@ Vue.use(AddressList);
/>
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -54,7 +56,6 @@ export default {
]
}
},
methods: {
onAdd() {
Toast('Add');
@@ -95,9 +96,9 @@ export default {
| Key | Description | Type |
|------|------|------|
| id | Id | *string \| number* |
| id | Id | *number \| string* |
| name | Name | *string* |
| tel | Phone | *string \| number* |
| tel | Phone | *number \| string* |
| address | Address | *string* |
| isDefault | Is default address | *boolean* |
+6 -6
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { AddressList } from 'vant';
@@ -25,7 +25,9 @@ Vue.use(AddressList);
/>
```
```javascript
```js
import { Toast } from 'vant';
export default {
data() {
return {
@@ -54,12 +56,10 @@ export default {
]
}
},
methods: {
onAdd() {
Toast('新增地址');
},
onEdit(item, index) {
Toast('编辑地址:' + index);
}
@@ -96,9 +96,9 @@ export default {
| 键名 | 说明 | 类型 |
|------|------|------|
| id | 每条地址的唯一标识 | *string \| number* |
| id | 每条地址的唯一标识 | *number \| string* |
| name | 收货人姓名 | *string* |
| tel | 收货人手机号 | *string \| number* |
| tel | 收货人手机号 | *number \| string* |
| address | 收货地址 | *string* |
| isDefault | 是否为默认地址 | *boolean* |
+16 -16
View File
@@ -24,27 +24,27 @@ export default {
name: '张三',
tel: '13000000000',
address: '浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室',
isDefault: true
isDefault: true,
},
{
id: '2',
name: '李四',
tel: '1310000000',
address: '浙江省杭州市拱墅区莫干山路 50 号'
}
address: '浙江省杭州市拱墅区莫干山路 50 号',
},
],
disabledList: [
{
id: '3',
name: '王五',
tel: '1320000000',
address: '浙江省杭州市滨江区江南大道 15 号'
}
address: '浙江省杭州市滨江区江南大道 15 号',
},
],
add: '新增地址',
edit: '编辑地址',
disabledText: '以下地址超出配送范围',
defaultTagText: '默认'
defaultTagText: '默认',
},
'en-US': {
list: [
@@ -53,33 +53,33 @@ export default {
name: 'John Snow',
tel: '13000000000',
address: 'Somewhere',
isDefault: true
isDefault: true,
},
{
id: '2',
name: 'Ned Stark',
tel: '1310000000',
address: 'Somewhere'
}
address: 'Somewhere',
},
],
disabledList: [
{
id: '3',
name: 'Tywin',
tel: '1320000000',
address: 'Somewhere'
}
address: 'Somewhere',
},
],
add: 'Add',
edit: 'Edit',
disabledText: 'The following address is out of range',
defaultTagText: 'Default'
}
defaultTagText: 'Default',
},
},
data() {
return {
chosenAddressId: '1'
chosenAddressId: '1',
};
},
@@ -90,8 +90,8 @@ export default {
onEdit(item, index) {
this.$toast(`${this.$t('edit')}:${index}`);
}
}
},
},
};
</script>
+8 -3
View File
@@ -1,5 +1,8 @@
// Utils
import { createNamespace } from '../utils';
import { emit, inherit } from '../utils/functional';
// Components
import Button from '../button';
import RadioGroup from '../radio-group';
import AddressItem, { AddressItemData } from './Item';
@@ -66,7 +69,9 @@ function AddressList(
<div class={bem()} {...inherit(ctx)}>
{slots.top?.()}
<RadioGroup value={props.value}>{List}</RadioGroup>
{props.disabledText && <div class={bem('disabled-text')}>{props.disabledText}</div>}
{props.disabledText && (
<div class={bem('disabled-text')}>{props.disabledText}</div>
)}
{DisabledList}
{slots.default?.()}
<div class={bem('bottom')}>
@@ -94,8 +99,8 @@ AddressList.props = {
defaultTagText: String,
switchable: {
type: Boolean,
default: true
}
default: true,
},
};
export default createComponent<AddressListProps>(AddressList);
+13 -13
View File
@@ -6,22 +6,22 @@ const list = [
id: '1',
name: '张三',
tel: '13000000000',
address: '浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室'
address: '浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室',
},
{
id: '2',
name: '李四',
tel: '1310000000',
address: '浙江省杭州市拱墅区莫干山路 50 号'
}
address: '浙江省杭州市拱墅区莫干山路 50 号',
},
];
test('unswitchable', () => {
const wrapper = mount(AddressList, {
propsData: {
list,
switchable: false
}
switchable: false,
},
});
expect(wrapper).toMatchSnapshot();
@@ -31,13 +31,13 @@ test('select event', () => {
const onSelect = jest.fn();
const wrapper = mount(AddressList, {
propsData: {
list
list,
},
context: {
on: {
select: onSelect
}
}
select: onSelect,
},
},
});
wrapper.find('.van-radio__icon').trigger('click');
@@ -49,13 +49,13 @@ test('click-item event', () => {
const onClickItem = jest.fn();
const wrapper = mount(AddressList, {
propsData: {
list
list,
},
context: {
on: {
'click-item': onClickItem
}
}
'click-item': onClickItem,
},
},
});
wrapper.find('.van-address-item').trigger('click');
+10 -10
View File
@@ -6,7 +6,7 @@ The Picker component is usually used with [Popup](#/en-US/popup) Component.
### Install
``` javascript
```js
import Vue from 'vue';
import { Area } from 'vant';
@@ -60,16 +60,16 @@ Set `columns-num` with 2, you'll have a 2 level picker.
|------|------|------|------|
| value | the `code` of selected area | *string* | - |
| title | Toolbar title | *string* | - |
| area-list | Area data | *object* | - |
| columns-num | level of picker | *string \| number* | `3` |
| columns-placeholder `v2.2.5` | placeholder of columns | *string[]* | `[]` |
| item-height | Option height | *number* | `44` |
| loading | Whether to show loading prompt | *boolean* | `false` |
| visible-item-count | Count of visible columns | *number* | `5` |
| confirm-button-text | Text of confirm button | *string* | `Confirm` |
| cancel-button-text | Text of cancel button | *string* | `Cancel` |
| area-list | Area list data | *object* | - |
| columns-placeholder `v2.2.5` | Placeholder of columns | *string[]* | `[]` |
| loading | Whether to show loading prompt | *boolean* | `false` |
| item-height | Option height | *number \| string* | `44` |
| columns-num | Level of picker | *number \| string* | `3` |
| visible-item-count | Count of visible columns | *number \| string* | `5` |
| swipe-duration `v2.2.13` | Duration of the momentum animationunit `ms` | *number \| string* | `1000` |
| is-oversea-code `v2.1.4` | The method to validate oversea code | *() => boolean* | - |
| swipe-duration `v2.2.13` | Duration of the momentum animationunit `ms` | *number* | `1000` |
### Events
@@ -94,7 +94,7 @@ Each property is a simple key-value object, key is a 6-bit code of the area of w
Example of `AreaList`
```javascript
```js
{
province_list: {
110000: 'Beijing',
@@ -123,7 +123,7 @@ All code of China: [Area.json](https://github.com/youzan/vant/blob/dev/src/area/
An array contains selected area objects.
`code` - code of selected area, `name` - name of selected area
```javascript
```js
[{
code: '330000',
name: 'Zhejiang Province'
+12 -12
View File
@@ -2,11 +2,11 @@
### 介绍
省市区选择组件通常与 [弹出层](#/zh-CN/popup) 组件配合使用
省市区三级联动选择,通常与 [弹出层](#/zh-CN/popup) 组件配合使用
### 引入
```javascript
```js
import Vue from 'vue';
import { Area } from 'vant';
@@ -59,16 +59,16 @@ Vue.use(Area);
|------|------|------|------|
| value | 当前选中的省市区`code` | *string* | - |
| title | 顶部栏标题 | *string* | - |
| area-list | 省市区数据,格式见下方 | *object* | - |
| columns-num | 显示列数,3-省市区,2-省市,1-省 | *string \| number* | `3` |
| columns-placeholder `v2.2.5` | 列占位提示文字 | *string[]* | `[]` |
| loading | 是否显示加载状态 | *boolean* | `false` |
| item-height | 选项高度 | *number* | `44` |
| visible-item-count | 可见的选项个数 | *number* | `5` |
| confirm-button-text | 确认按钮文字 | *string* | `确认` |
| cancel-button-text | 取消按钮文字 | *string* | `取消` |
| area-list | 省市区数据,格式见下方 | *object* | - |
| columns-placeholder `v2.2.5` | 列占位提示文字 | *string[]* | `[]` |
| loading | 是否显示加载状态 | *boolean* | `false` |
| item-height | 选项高度 | *number \| string* | `44` |
| columns-num | 显示列数,3-省市区,2-省市,1-省 | *number \| string* | `3` |
| visible-item-count | 可见的选项个数 | *number \| string* | `5` |
| swipe-duration `v2.2.13` | 快速滑动时惯性滚动的时长,单位`ms` | *number \| string* | `1000` |
| is-oversea-code `v2.1.4` | 根据`code`校验海外地址,海外地址会划分至单独的分类 | *() => boolean* | - |
| swipe-duration `v2.2.13` | 快速滑动时惯性滚动的时长,单位`ms` | *number* | `1000` |
### Events
@@ -80,7 +80,7 @@ Vue.use(Area);
### 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 Area 实例并调用实例方法
通过 ref 可以获取到 Area 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
@@ -94,7 +94,7 @@ Vue.use(Area);
`AreaList`具体格式如下:
```javascript
```js
{
province_list: {
110000: '北京市',
@@ -129,7 +129,7 @@ Vue.use(Area);
`code` 代表被选中的地区编码, `name` 代表被选中的地区名称
```javascript
```js
[
{
code: '110000',
+4 -4
View File
@@ -2,7 +2,7 @@ export default {
province_list: {
110000: 'Beijing',
330000: 'Zhejiang',
810000: 'Hong Kong'
810000: 'Hong Kong',
},
city_list: {
110100: 'Beijing City',
@@ -13,7 +13,7 @@ export default {
331100: 'Lishui',
810100: 'Hong Kong Island',
810200: 'Kowloon',
810300: 'New Territories'
810300: 'New Territories',
},
county_list: {
110101: 'Dongcheng',
@@ -66,6 +66,6 @@ export default {
810305: 'Yuen Long',
810306: 'Tuen Mun',
810307: 'Tsuen Wan',
810309: 'Lantau Island'
}
810309: 'Lantau Island',
},
};
+4 -4
View File
@@ -34,7 +34,7 @@ export default {
710000: '台湾省',
810000: '香港特别行政区',
820000: '澳门特别行政区',
900000: '海外'
900000: '海外',
},
city_list: {
110100: '北京市',
@@ -648,7 +648,7 @@ export default {
987600: '瓦利斯和富图纳',
988200: '萨摩亚',
988700: '也门',
989400: '赞比亚'
989400: '赞比亚',
},
county_list: {
110101: '东城区',
@@ -4035,6 +4035,6 @@ export default {
810308: '葵青区',
810309: '离岛区',
820101: '澳门半岛',
820201: '离岛'
}
820201: '离岛',
},
};
+4 -4
View File
@@ -1,13 +1,13 @@
export default {
province_list: {
110000: '北京市',
120000: '天津市'
120000: '天津市',
},
city_list: {
110100: '北京市',
110200: '县',
120100: '天津市',
120200: '县'
120200: '县',
},
county_list: {
110101: '东城区',
@@ -16,6 +16,6 @@ export default {
110229: '延庆县',
120101: '和平区',
120102: '河东区',
120225: '蓟县'
}
120225: '蓟县',
},
};
+4 -7
View File
@@ -5,10 +5,7 @@
</demo-block>
<demo-block :title="$t('title2')">
<van-area
:area-list="$t('areaList')"
:value="value"
/>
<van-area :area-list="$t('areaList')" :value="value" />
</demo-block>
<demo-block :title="$t('title3')">
@@ -48,13 +45,13 @@ export default {
title4: 'Columns Placeholder',
columnsPlaceholder: ['Choose', 'Choose', 'Choose'],
areaList: AreaListEn,
}
},
},
data() {
return {
value: '330302'
value: '330302',
};
}
},
};
</script>
+25 -25
View File
@@ -16,26 +16,26 @@ export default createComponent({
value: String,
areaList: {
type: Object,
default: () => ({})
default: () => ({}),
},
columnsNum: {
type: [Number, String],
default: 3
default: 3,
},
isOverseaCode: {
type: Function,
default: isOverseaCode
default: isOverseaCode,
},
columnsPlaceholder: {
type: Array,
default: () => []
}
default: () => [],
},
},
data() {
return {
code: this.value,
columns: [{ values: [] }, { values: [] }, { values: [] }]
columns: [{ values: [] }, { values: [] }, { values: [] }],
};
},
@@ -60,27 +60,27 @@ export default createComponent({
return {
province: this.columnsPlaceholder[0] || '',
city: this.columnsPlaceholder[1] || '',
county: this.columnsPlaceholder[2] || ''
county: this.columnsPlaceholder[2] || '',
};
}
},
},
watch: {
value() {
this.code = this.value;
value(val) {
this.code = val;
this.setValues();
},
areaList: {
deep: true,
handler: 'setValues'
handler: 'setValues',
},
columnsNum() {
this.$nextTick(() => {
this.setValues();
});
}
},
},
mounted() {
@@ -98,7 +98,7 @@ export default createComponent({
const list = this[type];
result = Object.keys(list).map(listCode => ({
code: listCode,
name: list[listCode]
name: list[listCode],
}));
if (code) {
@@ -112,16 +112,16 @@ export default createComponent({
if (this.placeholderMap[type] && result.length) {
// set columns placeholder
const codeFill =
type === 'province'
? ''
: type === 'city'
? PLACEHOLDER_CODE.slice(2, 4)
: PLACEHOLDER_CODE.slice(4, 6);
let codeFill = '';
if (type === 'city') {
codeFill = PLACEHOLDER_CODE.slice(2, 4);
} else if (type === 'county') {
codeFill = PLACEHOLDER_CODE.slice(4, 6);
}
result.unshift({
code: `${code}${codeFill}`,
name: this.placeholderMap[type]
name: this.placeholderMap[type],
});
}
@@ -218,7 +218,7 @@ export default createComponent({
picker.setIndexes([
this.getIndex('province', code),
this.getIndex('city', code),
this.getIndex('county', code)
this.getIndex('county', code),
]);
},
@@ -236,7 +236,7 @@ export default createComponent({
country: '',
province: '',
city: '',
county: ''
county: '',
};
if (!values.length) {
@@ -266,14 +266,14 @@ export default createComponent({
reset(code) {
this.code = code || '';
this.setValues();
}
},
},
render() {
const on = {
...this.$listeners,
change: this.onChange,
confirm: this.onConfirm
confirm: this.onConfirm,
};
return (
@@ -293,5 +293,5 @@ export default createComponent({
{...{ on }}
/>
);
}
},
});
+26 -20
View File
@@ -5,13 +5,13 @@ import { mount, later, triggerDrag } from '../../../test';
const firstOption = [
{ code: '110000', name: '北京市' },
{ code: '110100', name: '北京市' },
{ code: '110101', name: '东城区' }
{ code: '110101', name: '东城区' },
];
const secondOption = [
{ code: '120000', name: '天津市' },
{ code: '120100', name: '天津市' },
{ code: '120101', name: '和平区' }
{ code: '120101', name: '和平区' },
];
test('confirm & cancel event', async () => {
@@ -19,12 +19,12 @@ test('confirm & cancel event', async () => {
const onCancel = jest.fn();
const wrapper = mount(Area, {
propsData: {
areaList
areaList,
},
listeners: {
confirm: onConfirm,
cancel: onCancel
}
cancel: onCancel,
},
});
await later();
@@ -39,8 +39,8 @@ test('confirm & cancel event', async () => {
test('watch areaList & code', async () => {
const wrapper = mount(Area, {
propsData: {
areaList
}
areaList,
},
});
expect(wrapper).toMatchSnapshot();
@@ -50,7 +50,7 @@ test('watch areaList & code', async () => {
expect(wrapper).toMatchSnapshot();
wrapper.setProps({
value: ''
value: '',
});
expect(wrapper).toMatchSnapshot();
});
@@ -59,22 +59,28 @@ test('change option', () => {
const onChange = jest.fn();
const wrapper = mount(Area, {
propsData: {
areaList
areaList,
},
listeners: {
change: onChange
}
change: onChange,
},
});
const columns = wrapper.findAll('.van-picker-column');
expect(wrapper).toMatchSnapshot();
triggerDrag(columns.at(0), 0, -100);
columns.at(0).find('ul').trigger('transitionend');
columns
.at(0)
.find('ul')
.trigger('transitionend');
expect(wrapper).toMatchSnapshot();
triggerDrag(columns.at(2), 0, -100);
columns.at(2).find('ul').trigger('transitionend');
columns
.at(2)
.find('ul')
.trigger('transitionend');
expect(wrapper).toMatchSnapshot();
expect(onChange.mock.calls[0][1]).toEqual(secondOption);
@@ -83,11 +89,11 @@ test('change option', () => {
test('getValues method', () => {
const wrapper = mount(Area, {
propsData: {
areaList
areaList,
},
created() {
expect(this.getValues()).toEqual([]);
}
},
});
expect(wrapper.vm.getValues()).toEqual(firstOption);
@@ -97,8 +103,8 @@ test('reset method', async () => {
const wrapper = mount(Area, {
propsData: {
areaList,
value: '120225'
}
value: '120225',
},
});
await later();
@@ -111,12 +117,12 @@ test('columns-num prop', async () => {
const wrapper = mount(Area, {
propsData: {
areaList,
columnsNum: 3
}
columnsNum: 3,
},
});
wrapper.setProps({
columnsNum: 2
columnsNum: 2,
});
await later();
+7 -6
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Button } from 'vant';
@@ -74,6 +74,12 @@ Vue.use(Button);
<van-button type="primary" size="mini">Mini</van-button>
```
### Block Element
```html
<van-button type="primary" block>Block Element</van-button>
```
### Route
```html
@@ -89,11 +95,6 @@ Vue.use(Button);
<van-button color="linear-gradient(to right, #4bb0ff, #6149f6)">Gradient</van-button>
```
### Block Element
```html
<van-button type="primary" block>Block Element</van-button>
```
## API
### Props
+11 -12
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Button } from 'vant';
@@ -90,6 +90,14 @@ Vue.use(Button);
<van-button type="primary" size="mini">迷你按钮</van-button>
```
### 块级元素
按钮在默认情况下为行内块级元素,通过`block`属性可以将按钮的元素类型设置为块级元素
```html
<van-button type="primary" block>块级元素</van-button>
```
### 页面导航
可以通过`url`属性进行 URL 跳转,或通过`to`属性进行路由跳转
@@ -109,15 +117,6 @@ Vue.use(Button);
<van-button color="linear-gradient(to right, #4bb0ff, #6149f6)">渐变色按钮</van-button>
```
### 块级元素
通过`block`属性可以将按钮的元素类型设置为块级元素
```html
<van-button type="primary" block>块级元素</van-button>
```
## API
### Props
@@ -128,7 +127,7 @@ Vue.use(Button);
| size | 尺寸,可选值为 `large` `small` `mini` | *string* | `normal` |
| text | 按钮文字 | *string* | - |
| color `v2.1.8` | 按钮颜色,支持传入`linear-gradient`渐变色 | *string* | - |
| icon | 左侧图标名称或图片链接,可选值见 [Icon 组件](#/zh-CN/icon) | *string* | - |
| icon | 左侧 [图标名称](#/zh-CN/icon) 或图片链接 | *string* | - |
| tag | HTML 标签 | *string* | `button` |
| native-type | 原生 button 标签 type 属性 | *string* | - |
| block | 是否为块级元素 | *boolean* | `false` |
@@ -139,7 +138,7 @@ Vue.use(Button);
| hairline | 是否使用 0.5px 边框 | *boolean* | `false` |
| loading | 是否显示为加载状态 | *boolean* | `false` |
| loading-text | 加载状态提示文字 | *string* | - |
| loading-type | 加载图标类型,可选值为`spinner` | *string* | `circular` |
| loading-type | [加载图标类型](#/zh-CN/loading),可选值为`spinner` | *string* | `circular` |
| loading-size | 加载图标大小 | *string* | `20px` |
| url | 点击后跳转的链接地址 | *string* | - |
| to | 点击后跳转的目标路由对象,同 vue-router 的 [to 属性](https://router.vuejs.org/zh/api/#to) | *string \| object* | - |
+17 -13
View File
@@ -54,22 +54,26 @@
<van-button type="primary" size="mini">{{ $t('mini') }}</van-button>
</demo-block>
<demo-block :title="$t('blockElement')">
<van-button type="primary" block>{{ $t('blockElement') }}</van-button>
</demo-block>
<demo-block :title="$t('router')">
<van-button :text="$t('urlRoute')" type="primary" url="/vant/mobile.html" />
<van-button
:text="$t('urlRoute')"
type="primary"
url="/vant/mobile.html"
/>
<van-button :text="$t('vueRoute')" type="primary" to="index" />
</demo-block>
<demo-block :title="$t('customColor')">
<van-button color="#7232dd" :text="$t('pure')" />
<van-button plain color="#7232dd" :text="$t('pure')" />
<van-button color="linear-gradient(to right, #4bb0ff, #6149f6)" :text="$t('gradient')" />
</demo-block>
<demo-block :title="$t('blockElement')">
<div class="demo-button-row">
<van-button type="primary">{{ $t('normal') }}</van-button>
</div>
<van-button type="primary" block>{{ $t('blockElement') }}</van-button>
<van-button
color="linear-gradient(to right, #4bb0ff, #6149f6)"
:text="$t('gradient')"
/>
</demo-block>
</demo-section>
</template>
@@ -104,7 +108,7 @@ export default {
customColor: '自定义颜色',
pure: '单色按钮',
gradient: '渐变色按钮',
blockElement: '块级元素'
blockElement: '块级元素',
},
'en-US': {
type: 'Type',
@@ -133,9 +137,9 @@ export default {
customColor: 'Custom Color',
pure: 'Pure',
gradient: 'Gradient',
blockElement: 'Block Element'
}
}
blockElement: 'Block Element',
},
},
};
</script>
+1 -1
View File
@@ -32,7 +32,7 @@
}
&:active::before {
opacity: .1;
opacity: 0.1;
}
&--loading,
+12 -9
View File
@@ -1,7 +1,10 @@
// Utils
import { createNamespace } from '../utils';
import { emit, inherit } from '../utils/functional';
import { BORDER_SURROUND, WHITE } from '../utils/constant';
import { routeProps, RouteProps, functionalRoute } from '../utils/router';
// Components
import Icon from '../icon';
import Loading, { LoadingType } from '../loading';
@@ -54,7 +57,7 @@ function Button(
disabled,
loading,
hairline,
loadingText
loadingText,
} = props;
const style: Record<string, string | number> = {};
@@ -97,10 +100,10 @@ function Button(
hairline,
block: props.block,
round: props.round,
square: props.square
}
square: props.square,
},
]),
{ [BORDER_SURROUND]: hairline }
{ [BORDER_SURROUND]: hairline },
];
function Content() {
@@ -165,20 +168,20 @@ Button.props = {
loadingType: String,
tag: {
type: String,
default: 'button'
default: 'button',
},
type: {
type: String,
default: 'default'
default: 'default',
},
size: {
type: String,
default: 'normal'
default: 'normal',
},
loadingSize: {
type: String,
default: '20px'
}
default: '20px',
},
};
export default createComponent<ButtonProps, ButtonEvents>(Button);
@@ -21,10 +21,8 @@ exports[`renders demo correctly 1`] = `
<!----></i><span class="van-button__text">按钮</span></button> <button class="van-button van-button--primary van-button--normal van-button--plain"><i class="van-icon van-button__icon"><img src="https://img.yzcdn.cn/vant/logo.png" class="van-icon__image">
<!----></i><span class="van-button__text">按钮</span></button></div>
<div><button class="van-button van-button--primary van-button--large"><span class="van-button__text">大号按钮</span></button> <button class="van-button van-button--primary van-button--normal"><span class="van-button__text">普通按钮</span></button> <button class="van-button van-button--primary van-button--small"><span class="van-button__text">小型按钮</span></button> <button class="van-button van-button--primary van-button--mini"><span class="van-button__text">迷你按钮</span></button></div>
<div><button class="van-button van-button--primary van-button--normal van-button--block"><span class="van-button__text">块级元素</span></button></div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">URL 跳转</span></button> <button class="van-button van-button--primary van-button--normal"><span class="van-button__text">路由跳转</span></button></div>
<div><button class="van-button van-button--default van-button--normal" style="color: rgb(255, 255, 255); background: rgb(114, 50, 221); border-color: #7232dd;"><span class="van-button__text">单色按钮</span></button> <button class="van-button van-button--default van-button--normal van-button--plain" style="color: rgb(114, 50, 221); border-color: #7232dd;"><span class="van-button__text">单色按钮</span></button> <button class="van-button van-button--default van-button--normal" style="color: rgb(255, 255, 255); border: 0px;"><span class="van-button__text">渐变色按钮</span></button></div>
<div>
<div class="demo-button-row"><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">普通按钮</span></button></div> <button class="van-button van-button--primary van-button--normal van-button--block"><span class="van-button__text">块级元素</span></button>
</div>
</div>
`;
+18 -18
View File
@@ -5,8 +5,8 @@ test('loading size', () => {
const wrapper = mount(Button, {
propsData: {
loading: true,
loadingSize: '10px'
}
loadingSize: '10px',
},
});
expect(wrapper).toMatchSnapshot();
});
@@ -16,9 +16,9 @@ test('click event', () => {
const wrapper = mount(Button, {
context: {
on: {
click: onClick
}
}
click: onClick,
},
},
});
wrapper.trigger('click');
@@ -29,13 +29,13 @@ test('not trigger click event when disabled', () => {
const onClick = jest.fn();
const wrapper = mount(Button, {
propsData: {
disabled: true
disabled: true,
},
context: {
on: {
click: onClick
}
}
click: onClick,
},
},
});
wrapper.trigger('click');
@@ -46,13 +46,13 @@ test('not trigger click event when loading', () => {
const onClick = jest.fn();
const wrapper = mount(Button, {
propsData: {
loading: true
loading: true,
},
context: {
on: {
click: onClick
}
}
click: onClick,
},
},
});
wrapper.trigger('click');
@@ -64,9 +64,9 @@ test('touchstart event', () => {
const wrapper = mount(Button, {
context: {
on: {
touchstart: onTouchstart
}
}
touchstart: onTouchstart,
},
},
});
wrapper.trigger('touchstart');
@@ -76,8 +76,8 @@ test('touchstart event', () => {
test('hide border when color is gradient', () => {
const wrapper = mount(Button, {
propsData: {
color: 'linear-gradient(#000, #fff)'
}
color: 'linear-gradient(#000, #fff)',
},
});
expect(wrapper.element.style.border).toEqual('0px');
+18 -6
View File
@@ -6,7 +6,7 @@ Calendar component for selecting dates or date ranges
### Install
``` javascript
```js
import Vue from 'vue';
import { Calendar } from 'vant';
@@ -21,7 +21,6 @@ The `confirm` event will be triggered after the date selection is completed
```html
<van-cell title="Select Single Date" :value="date" @click="show = true" />
<van-calendar v-model="show" @confirm="onConfirm" />
```
@@ -33,7 +32,6 @@ export default {
show: false
};
},
methods: {
formatDate(date) {
return `${date.getMonth() + 1}/${date.getDate()}`;
@@ -52,7 +50,6 @@ You can select a date range after setting `type` to` range`. In range mode, the
```html
<van-cell title="Select Date Range" :value="date" @click="show = true" />
<van-calendar v-model="show" type="range" @confirm="onConfirm" />
```
@@ -64,7 +61,6 @@ export default {
show: false
};
},
methods: {
formatDate(date) {
return `${date.getMonth() + 1}/${date.getDate()}`;
@@ -184,6 +180,18 @@ Use `position` to custom popup positioncan be set to `top`、`left`、`right`
/>
```
### Max Range
When selecting a date range, you can use the `max-range` prop to specify the maximum number of selectable days
```html
<van-calendar
type="range"
:max-range="3"
:style="{ height: '500px' }"
/>
```
### Tiled display
Set `poppable` to `false`, the calendar will be displayed directly on the page instead of appearing as a popup
@@ -210,17 +218,21 @@ Set `poppable` to `false`, the calendar will be displayed directly on the page i
| min-date | Min date | *Date* | Today |
| max-date | Max date | *Date* | Six months after the today |
| default-date | Default selected date | *Date \| Date[]* | Today |
| row-height | Row height | *number* | `64` |
| row-height | Row height | *number \| string* | `64` |
| formatter | Day formatter | *(day: Day) => Day* | - |
| position | Popup position, can be set to `top` `right` `left` | *string* | `bottom` |
| poppable | Whether to show the calendar inside a popup | *boolean* | `true` |
| round | Whether to show round corner | *boolean* | `true` |
| show-mark | Whether to show background month mark | *boolean* | `true` |
| show-confirm | Whether to show confirm button | *boolean* | `true` |
| close-on-popstate `v2.4.4` | Whether to close when popstate | *boolean* | `false` |
| close-on-click-overlay | Whether to close when click overlay | *boolean* | `true` |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation | *boolean* | `true` |
| confirm-text | Confirm button text | *string* | `Confirm` |
| confirm-disabled-text | Confirm button text when disabled | *string* | `Confirm` |
| max-range `v2.4.3` | Number of selectable days | *number \| string* | - |
| range-prompt `v2.4.3` | Error message when exceeded max range | *string* | `Choose no more than xx days` |
| get-container `v2.4.4` | Return the mount node for Calendar | _string \| () => Element_ | - |
### Data Structure of Day
+20 -8
View File
@@ -6,7 +6,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Calendar } from 'vant';
@@ -21,7 +21,6 @@ Vue.use(Calendar);
```html
<van-cell title="选择单个日期" :value="date" @click="show = true" />
<van-calendar v-model="show" @confirm="onConfirm" />
```
@@ -33,7 +32,6 @@ export default {
show: false
};
},
methods: {
formatDate(date) {
return `${date.getMonth() + 1}/${date.getDate()}`;
@@ -52,7 +50,6 @@ export default {
```html
<van-cell title="选择日期区间" :value="date" @click="show = true" />
<van-calendar v-model="show" type="range" @confirm="onConfirm" />
```
@@ -64,7 +61,6 @@ export default {
show: false
};
},
methods: {
formatDate(date) {
return `${date.getMonth() + 1}/${date.getDate()}`;
@@ -184,6 +180,18 @@ export default {
/>
```
### 日期区间最大范围
选择日期区间时,可以通过`max-range`属性来指定最多可选天数,选择的范围超过最多可选天数时,会弹出相应的提示文案
```html
<van-calendar
type="range"
:max-range="3"
:style="{ height: '500px' }"
/>
```
### 平铺展示
`poppable`设置为`false`,日历会直接展示在页面内,而不是以弹层的形式出现
@@ -210,17 +218,21 @@ export default {
| min-date | 最小日期 | *Date* | 当前日期 |
| max-date | 最大日期 | *Date* | 当前日期的六个月后 |
| default-date | 默认选中的日期 | *Date \| Date[]* | 今天 |
| row-height | 日期行高 | *number* | `64` |
| row-height | 日期行高 | *number \| string* | `64` |
| formatter | 日期格式化函数 | *(day: Day) => Day* | - |
| position | 弹出位置,可选值为 `top` `right` `left` | *string* | `bottom` |
| poppable | 是否以弹层的形式展示日历 | *boolean* | `true` |
| round | 是否显示圆角弹窗 | *boolean* | `true` |
| show-mark | 是否显示月份背景水印 | *boolean* | `true` |
| show-confirm | 是否展示确认按钮 | *boolean* | `true` |
| close-on-popstate `v2.4.4` | 是否在页面回退时自动关闭 | *boolean* | `false` |
| close-on-click-overlay | 是否在点击遮罩层后关闭 | *boolean* | `true` |
| safe-area-inset-bottom | 是否开启底部安全区适配[详细说明](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `true` |
| safe-area-inset-bottom | 是否开启 [底部安全区适配](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `true` |
| confirm-text | 确认按钮的文字 | *string* | `确定` |
| confirm-disabled-text | 确认按钮处于禁用状态时的文字 | *string* | `确定` |
| max-range `v2.4.3` | 日期区间最多可选天数,默认无限制 | *number \| string* | - |
| range-prompt `v2.4.3` | 选择超过区间范围时的提示文案 | *string* | `选择天数不能超过 xx 天` |
| get-container `v2.4.4` | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | *string \| () => Element* | - |
### Day 数据结构
@@ -251,7 +263,7 @@ export default {
### 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 Calendar 实例并调用实例方法
通过 ref 可以获取到 Calendar 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
+3 -3
View File
@@ -6,7 +6,7 @@ const [createComponent] = createNamespace('calendar-header');
export default createComponent({
props: {
title: String,
monthTitle: String
monthTitle: String,
},
methods: {
@@ -29,7 +29,7 @@ export default createComponent({
))}
</div>
);
}
},
},
render() {
@@ -40,5 +40,5 @@ export default createComponent({
{this.genWeekDays()}
</div>
);
}
},
});
+8 -8
View File
@@ -13,14 +13,14 @@ export default createComponent({
maxDate: Date,
showMark: Boolean,
showTitle: Boolean,
rowHeight: Number,
rowHeight: [Number, String],
formatter: Function,
currentDate: [Date, Array]
currentDate: [Date, Array],
},
data() {
return {
visible: false
visible: false,
};
},
@@ -43,7 +43,7 @@ export default createComponent({
Math.ceil((this.totalDay + this.offset) / 7) * this.rowHeight;
return {
paddingBottom: `${padding}px`
paddingBottom: `${padding}px`,
};
}
},
@@ -61,7 +61,7 @@ export default createComponent({
date,
type,
text: day,
bottomInfo: this.getBottomInfo(type)
bottomInfo: this.getBottomInfo(type),
};
if (this.formatter) {
@@ -72,7 +72,7 @@ export default createComponent({
}
return days;
}
},
},
mounted() {
@@ -219,7 +219,7 @@ export default createComponent({
{BottomInfo}
</div>
);
}
},
},
render() {
@@ -229,5 +229,5 @@ export default createComponent({
{this.genDays()}
</div>
);
}
},
});
+23 -7
View File
@@ -67,6 +67,13 @@
:value="formatFullDate(date.customPosition)"
@click="show('single', 'customPosition')"
/>
<van-cell
is-link
:title="$t('maxRange')"
:value="formatRange(date.maxRange)"
@click="show('range', 'maxRange')"
/>
</demo-block>
<demo-block :title="$t('tiledDisplay')">
@@ -85,9 +92,10 @@
:type="type"
:color="color"
:round="round"
:position="position"
:min-date="minDate"
:max-date="maxDate"
:position="position"
:max-range="maxRange"
:formatter="formatter"
:show-confirm="showConfirm"
:confirm-text="confirmText"
@@ -109,6 +117,7 @@ export default {
laborDay: '劳动节',
youthDay: '五四青年节',
calendar: '日历',
maxRange: '日期区间最大范围',
selectSingle: '选择单个日期',
selectRange: '选择日期区间',
quickSelect: '快捷选择',
@@ -120,7 +129,7 @@ export default {
customPosition: '自定义弹出位置',
customCalendar: '自定义日历',
confirmDisabledText: '请选择结束时间',
tiledDisplay: '平铺展示'
tiledDisplay: '平铺展示',
},
'en-US': {
in: 'In',
@@ -129,6 +138,7 @@ export default {
laborDay: 'Labor day',
youthDay: 'Youth Day',
calendar: 'Calendar',
maxRange: 'Max Range',
selectSingle: 'Select Single Date',
selectRange: 'Select Date Range',
quickSelect: 'Quick Select',
@@ -140,13 +150,14 @@ export default {
customPosition: 'Custom Position',
customCalendar: 'Custom Calendar',
confirmDisabledText: 'Select End Time',
tiledDisplay: 'Tiled display'
}
tiledDisplay: 'Tiled display',
},
},
data() {
return {
date: {
maxRange: [],
selectSingle: null,
selectRange: [],
quickSelect1: null,
@@ -155,13 +166,14 @@ export default {
customConfirm: [],
customRange: null,
customDayText: [],
customPosition: null
customPosition: null,
},
type: 'single',
round: true,
color: undefined,
minDate: undefined,
maxDate: undefined,
maxRange: undefined,
position: undefined,
formatter: undefined,
showConfirm: false,
@@ -179,6 +191,7 @@ export default {
this.color = undefined;
this.minDate = undefined;
this.maxDate = undefined;
this.maxRange = undefined;
this.position = undefined;
this.formatter = undefined;
this.showConfirm = true;
@@ -217,6 +230,9 @@ export default {
this.round = false;
this.position = 'right';
break;
case 'maxRange':
this.maxRange = 3;
break;
}
},
@@ -265,7 +281,7 @@ export default {
onConfirm(date) {
this.showCalendar = false;
this.date[this.id] = date;
}
}
},
},
};
</script>
+72 -44
View File
@@ -1,3 +1,4 @@
// Utils
import { isDate } from '../utils/validate/date';
import { getScrollTop } from '../utils/dom/scroll';
import {
@@ -7,11 +8,14 @@ import {
compareDay,
compareMonth,
createComponent,
ROW_HEIGHT
calcDateNum,
ROW_HEIGHT,
} from './utils';
// Components
import Popup from '../popup';
import Button from '../button';
import Toast from '../toast';
import Month from './components/Month';
import Header from './components/Header';
@@ -21,17 +25,20 @@ export default createComponent({
color: String,
value: Boolean,
formatter: Function,
defaultDate: [Date, Array],
confirmText: String,
rangePrompt: String,
defaultDate: [Date, Array],
getContainer: [String, Function],
closeOnPopstate: Boolean,
confirmDisabledText: String,
type: {
type: String,
default: 'single'
default: 'single',
},
minDate: {
type: Date,
validator: isDate,
default: () => new Date()
default: () => new Date(),
},
maxDate: {
type: Date,
@@ -39,50 +46,58 @@ export default createComponent({
default() {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
}
},
},
position: {
type: String,
default: 'bottom'
default: 'bottom',
},
rowHeight: {
type: Number,
default: ROW_HEIGHT
type: [Number, String],
default: ROW_HEIGHT,
},
round: {
type: Boolean,
default: true
default: true,
},
poppable: {
type: Boolean,
default: true
default: true,
},
showMark: {
type: Boolean,
default: true
default: true,
},
showConfirm: {
type: Boolean,
default: true
default: true,
},
safeAreaInsetBottom: {
type: Boolean,
default: true
default: true,
},
closeOnClickOverlay: {
type: Boolean,
default: true
}
default: true,
},
maxRange: {
type: [Number, String],
default: null,
},
},
data() {
return {
monthTitle: '',
currentDate: this.getInitialDate()
currentDate: this.getInitialDate(),
};
},
computed: {
range() {
return this.type === 'range';
},
months() {
const months = [];
const cursor = new Date(this.minDate);
@@ -98,21 +113,16 @@ export default createComponent({
},
buttonDisabled() {
if (this.type === 'single') {
return !this.currentDate;
}
/* istanbul ignore else */
if (this.type === 'range') {
if (this.range) {
return !this.currentDate[0] || !this.currentDate[1];
}
}
return !this.currentDate;
},
},
watch: {
type() {
this.reset();
},
type: 'reset',
value(val) {
if (val) {
@@ -123,7 +133,7 @@ export default createComponent({
defaultDate(val) {
this.currentDate = val;
}
},
},
mounted() {
@@ -148,8 +158,8 @@ export default createComponent({
// scroll to current month
scrollIntoView() {
this.$nextTick(() => {
const { type, currentDate } = this;
const targetDate = type === 'range' ? currentDate[0] : currentDate;
const { currentDate } = this;
const targetDate = this.range ? currentDate[0] : currentDate;
/* istanbul ignore if */
if (!targetDate) {
@@ -170,15 +180,12 @@ export default createComponent({
getInitialDate() {
const { type, defaultDate, minDate } = this;
if (type === 'single') {
return defaultDate || minDate;
}
/* istanbul ignore else */
if (type === 'range') {
const [startDay, endDay] = defaultDate || [];
return [startDay || minDate, endDay || getNextDay(minDate)];
}
return defaultDate || minDate;
},
// calculate the position of the elements
@@ -219,11 +226,7 @@ export default createComponent({
onClickDay(item) {
const { date } = item;
if (this.type === 'single') {
this.select(date, true);
}
if (this.type === 'range') {
if (this.range) {
const [startDay, endDay] = this.currentDate;
if (startDay && !endDay) {
@@ -237,6 +240,8 @@ export default createComponent({
} else {
this.select([date, null]);
}
} else {
this.select(date, true);
}
},
@@ -248,13 +253,34 @@ export default createComponent({
this.currentDate = date;
this.$emit('select', this.currentDate);
if (complete && this.range) {
const valid = this.checkRange();
if (!valid) {
return;
}
}
if (complete && !this.showConfirm) {
this.onConfirm();
}
},
checkRange() {
const { maxRange, currentDate, rangePrompt } = this;
if (maxRange && calcDateNum(currentDate) > maxRange) {
Toast(rangePrompt || t('rangePrompt', maxRange));
return false;
}
return true;
},
onConfirm() {
this.$emit('confirm', this.currentDate);
if (this.checkRange()) {
this.$emit('confirm', this.currentDate);
}
},
genMonth(date, index) {
@@ -309,7 +335,7 @@ export default createComponent({
return (
<div
class={bem('footer', {
'safe-area-inset-bottom': this.safeAreaInsetBottom
'safe-area-inset-bottom': this.safeAreaInsetBottom,
})}
>
{this.genFooterContent()}
@@ -324,7 +350,7 @@ export default createComponent({
title={this.title}
monthTitle={this.monthTitle}
scopedSlots={{
title: () => this.slots('title')
title: () => this.slots('title'),
}}
/>
<div ref="body" class={bem('body')} onScroll={this.onScroll}>
@@ -333,7 +359,7 @@ export default createComponent({
{this.genFooter()}
</div>
);
}
},
},
render() {
@@ -346,6 +372,8 @@ export default createComponent({
value={this.value}
round={this.round}
position={this.position}
getContainer={this.getContainer}
closeOnPopstate={this.closeOnPopstate}
closeOnClickOverlay={this.closeOnClickOverlay}
onInput={this.togglePopup}
>
@@ -355,5 +383,5 @@ export default createComponent({
}
return this.genCalendar();
}
},
});
@@ -43,6 +43,10 @@ exports[`renders demo correctly 1`] = `
<div class="van-cell__title"><span>自定义弹出位置</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
<div role="button" tabindex="0" class="van-cell van-cell--clickable">
<div class="van-cell__title"><span>日期区间最大范围</span></div><i class="van-icon van-icon-arrow van-cell__right-icon">
<!----></i>
</div>
</div>
<div>
<div class="van-calendar" style="height: 500px;">
+66 -43
View File
@@ -23,8 +23,8 @@ test('select event when type is single', async () => {
propsData: {
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -43,8 +43,8 @@ test('select event when type is range', async () => {
type: 'range',
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -68,8 +68,8 @@ test('should not trigger select event when click disabled day', async () => {
propsData: {
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -87,8 +87,8 @@ test('confirm event when type is single', async () => {
propsData: {
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -110,8 +110,8 @@ test('confirm event when type is range', async () => {
type: 'range',
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -131,8 +131,8 @@ test('confirm event when type is range', async () => {
test('default single date', async () => {
const wrapper = mount(Calendar, {
propsData: {
poppable: false
}
poppable: false,
},
});
await later();
@@ -145,8 +145,8 @@ test('default range date', async () => {
const wrapper = mount(Calendar, {
propsData: {
type: 'range',
poppable: false
}
poppable: false,
},
});
await later();
@@ -163,8 +163,8 @@ test('reset method', async () => {
minDate,
maxDate,
type: 'range',
poppable: false
}
poppable: false,
},
});
await later();
@@ -188,8 +188,8 @@ test('set show-confirm to false', async () => {
maxDate,
type: 'range',
poppable: false,
showConfirm: false
}
showConfirm: false,
},
});
await later();
@@ -209,8 +209,8 @@ test('row-height prop', async () => {
minDate,
maxDate,
poppable: false,
rowHeight: 50
}
rowHeight: 50,
},
});
await later();
@@ -243,8 +243,8 @@ test('formatter prop', async () => {
}
return day;
}
}
},
},
});
await later();
@@ -257,12 +257,12 @@ test('title & footer slot', async () => {
propsData: {
minDate,
maxDate,
poppable: false
poppable: false,
},
scopedSlots: {
title: () => 'Custom Title',
footer: () => 'Custom Footer'
}
footer: () => 'Custom Footer',
},
});
await later();
@@ -275,8 +275,8 @@ test('should reset when type changed', async () => {
propsData: {
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -297,8 +297,8 @@ test('default-date prop in single type', async () => {
minDate,
maxDate,
defaultDate: getNextDay(minDate),
poppable: false
}
poppable: false,
},
});
await later();
@@ -317,8 +317,8 @@ test('default-date prop in range type', async () => {
type: 'range',
minDate,
maxDate,
poppable: false
}
poppable: false,
},
});
await later();
@@ -340,13 +340,13 @@ test('popup wrapper', async () => {
const wrapper = mount(Calendar, {
propsData: {
minDate,
maxDate
maxDate,
},
listeners: {
input(value) {
wrapper.setProps({ value });
}
}
},
},
});
await later();
@@ -367,8 +367,8 @@ test('set show-mark prop to false', async () => {
minDate,
maxDate,
showMark: false,
poppable: false
}
poppable: false,
},
});
await later();
@@ -382,8 +382,8 @@ test('color prop when type is single', async () => {
minDate,
maxDate,
color: 'blue',
poppable: false
}
poppable: false,
},
});
await later();
@@ -399,8 +399,8 @@ test('color prop when type is range', async () => {
minDate,
maxDate,
color: 'blue',
poppable: false
}
poppable: false,
},
});
await later();
@@ -414,12 +414,14 @@ test('should scroll to current month when show', async done => {
type: 'range',
minDate: new Date(2010, 0, 10),
maxDate: new Date(2010, 11, 10),
defaultDate: [new Date(2010, 3, 1), new Date(2010, 5, 1)]
}
defaultDate: [new Date(2010, 3, 1), new Date(2010, 5, 1)],
},
});
Element.prototype.scrollIntoView = function() {
expect(this.parentNode).toEqual(wrapper.findAll('.van-calendar__month').at(3).element);
expect(this.parentNode).toEqual(
wrapper.findAll('.van-calendar__month').at(3).element
);
done();
};
@@ -427,3 +429,24 @@ test('should scroll to current month when show', async done => {
await later();
});
test('max-range prop', async () => {
const wrapper = mount(Calendar, {
propsData: {
type: 'range',
minDate,
maxDate,
maxRange: 1,
poppable: false,
},
});
await later();
const days = wrapper.findAll('.van-calendar__day');
days.at(15).trigger('click');
days.at(18).trigger('click');
wrapper.find('.van-calendar__confirm').trigger('click');
expect(wrapper.emitted('confirm')).toBeFalsy();
});
+6 -1
View File
@@ -1,4 +1,4 @@
import { compareDay, compareMonth, getNextDay } from '../utils';
import { compareDay, compareMonth, getNextDay, calcDateNum } from '../utils';
const date1 = new Date(2010, 0, 1);
const date2 = new Date(2010, 0, 2);
@@ -24,3 +24,8 @@ test('getNextDay', () => {
expect(getNextDay(date1).getDate()).toEqual(2);
expect(getNextDay(date2).getDate()).toEqual(3);
});
test('calcDateNum', () => {
expect(calcDateNum([date1, date2])).toEqual(2);
expect(calcDateNum([date1, date3])).toEqual(32);
});
+6
View File
@@ -42,3 +42,9 @@ export function getNextDay(date: Date) {
return date;
}
export function calcDateNum(date: [Date, Date]) {
const day1 = date[0].getTime();
const day2 = date[1].getTime();
return (day2 - day1) / (1000 * 60 * 60 * 24) + 1;
}
+7 -7
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Card } from 'vant';
@@ -19,7 +19,7 @@ Vue.use(Card);
price="2.00"
title="Title"
desc="Description"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
/>
```
@@ -33,7 +33,7 @@ Vue.use(Card);
title="Title"
desc="Description"
origin-price="10.00"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
/>
```
@@ -47,7 +47,7 @@ Use slot to custom content.
title="Title"
desc="Description"
price="2.00"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
>
<div slot="tags">
<van-tag plain type="danger">Tag</van-tag>
@@ -70,9 +70,9 @@ Use slot to custom content.
| title | Title | *string* | - |
| desc | Description | *string* | - |
| tag | Tag | *string* | - |
| num | number | *string \| number* | - |
| price | Price | *string \| number* | - |
| origin-price | Origin price | *string \| number* | - |
| num | number | *number \| string* | - |
| price | Price | *number \| string* | - |
| origin-price | Origin price | *number \| string* | - |
| centered | Whether content vertical centered | *boolean* | `false` |
| currency | Currency symbol | *string* | `¥` |
| thumb-link | Thumb link URL | *string* | - |
+7 -7
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Card } from 'vant';
@@ -19,7 +19,7 @@ Vue.use(Card);
price="2.00"
desc="描述信息"
title="商品标题"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
/>
```
@@ -34,7 +34,7 @@ Vue.use(Card);
price="2.00"
desc="描述信息"
title="商品标题"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
origin-price="10.00"
/>
```
@@ -49,7 +49,7 @@ Vue.use(Card);
price="2.00"
desc="描述信息"
title="商品标题"
thumb="https://img.yzcdn.cn/vant/t-thirt.jpg"
thumb="https://img.yzcdn.cn/vant/ipad.jpeg"
>
<div slot="tags">
<van-tag plain type="danger">标签</van-tag>
@@ -72,9 +72,9 @@ Vue.use(Card);
| title | 标题 | *string* | - |
| desc | 描述 | *string* | - |
| tag | 图片角标 | *string* | - |
| num | 商品数量 | *string \| number* | - |
| price | 商品价格 | *string \| number* | - |
| origin-price | 商品划线原价 | *string \| number* | - |
| num | 商品数量 | *number \| string* | - |
| price | 商品价格 | *number \| string* | - |
| origin-price | 商品划线原价 | *number \| string* | - |
| centered | 内容是否垂直居中 | *boolean* | `false` |
| currency | 货币符号 | *string* | `¥` |
| thumb-link | 点击左侧图片后跳转的链接地址 | *string* | - |
+9 -22
View File
@@ -32,17 +32,10 @@
>
<template #tags>
<div>
<van-tag
plain
type="danger"
style="margin-right: 5px;"
>
<van-tag plain type="danger" style="margin-right: 5px;">
标签
</van-tag>
<van-tag
plain
type="danger"
>
<van-tag plain type="danger">
标签
</van-tag>
</div>
@@ -50,16 +43,10 @@
<template #footer>
<div>
<van-button
round
size="mini"
>
<van-button round size="mini">
{{ $t('button') }}
</van-button>
<van-button
round
size="mini"
>
<van-button round size="mini">
{{ $t('button') }}
</van-button>
</div>
@@ -75,19 +62,19 @@ export default {
'zh-CN': {
title: '商品名称',
discountInfo: '营销信息',
customContent: '自定义内容'
customContent: '自定义内容',
},
'en-US': {
discountInfo: 'Discount Info',
customContent: 'Custom Content'
}
customContent: 'Custom Content',
},
},
data() {
return {
imageURL: 'https://img.yzcdn.cn/vant/t-thirt.jpg'
imageURL: 'https://img.yzcdn.cn/vant/ipad.jpeg',
};
}
},
};
</script>
+6 -3
View File
@@ -1,5 +1,8 @@
// Utils
import { createNamespace, isDef } from '../utils';
import { emit, inherit } from '../utils/functional';
// Components
import Tag from '../tag';
import Image from '../image';
@@ -85,7 +88,7 @@ function Card(
src={thumb}
width="100%"
height="100%"
fit="contain"
fit="cover"
lazy-load={props.lazyLoad}
/>
)}
@@ -206,8 +209,8 @@ Card.props = {
originPrice: [Number, String],
currency: {
type: String,
default: '¥'
}
default: '¥',
},
};
export default createComponent<CardProps, CardEvents>(Card);
@@ -5,7 +5,7 @@ exports[`renders demo correctly 1`] = `
<div>
<div class="van-card">
<div class="van-card__header"><a class="van-card__thumb">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/t-thirt.jpg" class="van-image__img" style="object-fit: contain;">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/ipad.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>
@@ -28,7 +28,7 @@ exports[`renders demo correctly 1`] = `
<div>
<div class="van-card">
<div class="van-card__header"><a class="van-card__thumb">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/t-thirt.jpg" class="van-image__img" style="object-fit: contain;">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/ipad.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>
@@ -53,7 +53,7 @@ exports[`renders demo correctly 1`] = `
<div>
<div class="van-card">
<div class="van-card__header"><a class="van-card__thumb">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/t-thirt.jpg" class="van-image__img" style="object-fit: contain;">
<div class="van-image" style="width: 100%; height: 100%;"><img src="https://img.yzcdn.cn/vant/ipad.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>
+21 -21
View File
@@ -6,16 +6,16 @@ test('click event', () => {
const wrapper = mount(Card, {
context: {
on: {
click: onClick
}
}
click: onClick,
},
},
});
wrapper.trigger('click');
expect(onClick).toHaveBeenCalledWith(
expect.objectContaining({
isTrusted: expect.any(Boolean)
isTrusted: expect.any(Boolean),
})
);
});
@@ -24,20 +24,20 @@ test('click-thumb event', () => {
const onClickThumb = jest.fn();
const wrapper = mount(Card, {
propsData: {
thumb: 'xx'
thumb: 'xx',
},
context: {
on: {
'click-thumb': onClickThumb
}
}
'click-thumb': onClickThumb,
},
},
});
wrapper.find('.van-card__thumb').trigger('click');
expect(onClickThumb).toHaveBeenCalledWith(
expect.objectContaining({
isTrusted: expect.any(Boolean)
isTrusted: expect.any(Boolean),
})
);
});
@@ -46,8 +46,8 @@ test('render price & num slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
num: () => 'Custom Num',
price: () => 'Custom Price'
}
price: () => 'Custom Price',
},
});
expect(wrapper).toMatchSnapshot();
@@ -56,8 +56,8 @@ test('render price & num slot', () => {
test('render origin-price slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
'origin-price': () => 'Custom Origin Price'
}
'origin-price': () => 'Custom Origin Price',
},
});
expect(wrapper).toMatchSnapshot();
@@ -66,8 +66,8 @@ test('render origin-price slot', () => {
test('render bottom slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
bottom: () => 'Custom Bottom'
}
bottom: () => 'Custom Bottom',
},
});
expect(wrapper).toMatchSnapshot();
@@ -77,8 +77,8 @@ test('render thumb & tag slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
tag: () => 'Custom Tag',
thumb: () => 'Custom Thumb'
}
thumb: () => 'Custom Thumb',
},
});
expect(wrapper).toMatchSnapshot();
@@ -88,8 +88,8 @@ test('render title & desc slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
title: () => 'Custom Title',
desc: () => 'Custom desc'
}
desc: () => 'Custom desc',
},
});
expect(wrapper).toMatchSnapshot();
@@ -99,8 +99,8 @@ test('render price & price-top slot', () => {
const wrapper = mount(Card, {
scopedSlots: {
price: () => 'Custom Price',
'price-top': () => 'Custom Price-top'
}
'price-top': () => 'Custom Price-top',
},
});
expect(wrapper).toMatchSnapshot();
+10 -4
View File
@@ -1,3 +1,4 @@
// Utils
import { createNamespace } from '../utils';
import { inherit } from '../utils/functional';
import { BORDER_TOP_BOTTOM } from '../utils/constant';
@@ -24,7 +25,10 @@ function CellGroup(
ctx: RenderContext<CellGroupProps>
) {
const Group = (
<div class={[bem(), { [BORDER_TOP_BOTTOM]: props.border }]} {...inherit(ctx, true)}>
<div
class={[bem(), { [BORDER_TOP_BOTTOM]: props.border }]}
{...inherit(ctx, true)}
>
{slots.default?.()}
</div>
);
@@ -32,7 +36,9 @@ function CellGroup(
if (props.title || slots.title) {
return (
<div>
<div class={bem('title')}>{slots.title ? slots.title() : props.title}</div>
<div class={bem('title')}>
{slots.title ? slots.title() : props.title}
</div>
{Group}
</div>
);
@@ -45,8 +51,8 @@ CellGroup.props = {
title: String,
border: {
type: Boolean,
default: true
}
default: true,
},
};
export default createComponent<CellGroupProps>(CellGroup);
+6 -5
View File
@@ -2,11 +2,12 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Cell, CellGroup } from 'vant';
Vue.use(Cell).use(CellGroup);
Vue.use(Cell);
Vue.use(CellGroup);
```
## Usage
@@ -114,8 +115,8 @@ Vue.use(Cell).use(CellGroup);
| Attribute | Description | Type | Default |
|------|------|------|------|
| icon | Left Icon | *string* | - |
| title | Title | *string \| number* | - |
| value | Right text | *string \| number* | - |
| title | Title | *number \| string* | - |
| value | Right text | *number \| string* | - |
| label | Description below the title | *string* | - |
| size | Sizecan be set to `large` | *string* | - |
| border | Whether to show inner border | *boolean* | `true` |
@@ -126,7 +127,7 @@ Vue.use(Cell).use(CellGroup);
| clickable | Whether to show click feedback when clicked | *boolean* | `false` |
| is-link | Whether to show link icon | *boolean* | `false` |
| required | Whether to show required mark | *boolean* | `false` |
| arrow-direction | Can be set to `left` `up` `down` | *string* | - |
| arrow-direction | Can be set to `left` `up` `down` | *string* | `right` |
| title-style | Title style | *any* | - |
| title-class | Title className | *any* | - |
| value-class | Value className | *any* | - |
+7 -6
View File
@@ -2,11 +2,12 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Cell, CellGroup } from 'vant';
Vue.use(Cell).use(CellGroup);
Vue.use(Cell);
Vue.use(CellGroup);
```
## 代码演示
@@ -124,9 +125,9 @@ Vue.use(Cell).use(CellGroup);
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| icon | 左侧图标名称或图片链接,可选值见 [Icon 组件](#/zh-CN/icon) | *string* | - |
| title | 左侧标题 | *string \| number* | - |
| value | 右侧内容 | *string \| number* | - |
| icon | 左侧 [图标名称](#/zh-CN/icon) 或图片链接 | *string* | - |
| title | 左侧标题 | *number \| string* | - |
| value | 右侧内容 | *number \| string* | - |
| label | 标题下方的描述信息 | *string* | - |
| size | 单元格大小,可选值为 `large` | *string* | - |
| url | 点击后跳转的链接地址 | *string* | - |
@@ -137,7 +138,7 @@ Vue.use(Cell).use(CellGroup);
| is-link | 是否展示右侧箭头并开启点击反馈 | *boolean* | `false` |
| required | 是否显示表单必填星号 | *boolean* | `false` |
| center | 是否使内容垂直居中 | *boolean* | `false` |
| arrow-direction | 箭头方向,可选值为 `left` `up` `down` | *string* | - |
| arrow-direction | 箭头方向,可选值为 `left` `up` `down` | *string* | `right` |
| title-style | 左侧标题额外样式 | *any* | - |
| title-class | 左侧标题额外类名 | *any* | - |
| value-class | 右侧内容额外类名 | *any* | - |
+27 -8
View File
@@ -3,13 +3,22 @@
<demo-block :title="$t('basicUsage')">
<van-cell-group>
<van-cell :title="$t('cell')" :value="$t('content')" />
<van-cell :title="$t('cell')" :value="$t('content')" :label="$t('desc')" />
<van-cell
:title="$t('cell')"
:value="$t('content')"
:label="$t('desc')"
/>
</van-cell-group>
</demo-block>
<demo-block :title="$t('largeSize')">
<van-cell :title="$t('cell')" :value="$t('content')" size="large" />
<van-cell :title="$t('cell')" :value="$t('content')" size="large" :label="$t('desc')" />
<van-cell
:title="$t('cell')"
:value="$t('content')"
size="large"
:label="$t('desc')"
/>
</demo-block>
<demo-block :title="$t('showIcon')">
@@ -23,7 +32,12 @@
<demo-block :title="$t('showArrow')">
<van-cell :title="$t('cell')" is-link />
<van-cell :title="$t('cell')" is-link :value="$t('content')" />
<van-cell :title="$t('cell')" is-link arrow-direction="down" :value="$t('content')" />
<van-cell
:title="$t('cell')"
is-link
arrow-direction="down"
:value="$t('content')"
/>
</demo-block>
<demo-block :title="$t('router')">
@@ -56,7 +70,12 @@
</demo-block>
<demo-block :title="$t('verticalCenter')">
<van-cell center :title="$t('cell')" :value="$t('content')" :label="$t('desc')" />
<van-cell
center
:title="$t('cell')"
:value="$t('content')"
:label="$t('desc')"
/>
</demo-block>
</demo-section>
</template>
@@ -76,7 +95,7 @@ export default {
urlRoute: 'URL 跳转',
vueRoute: '路由跳转',
useSlots: '使用插槽',
verticalCenter: '垂直居中'
verticalCenter: '垂直居中',
},
'en-US': {
cell: 'Cell title',
@@ -90,9 +109,9 @@ export default {
urlRoute: 'URL',
vueRoute: 'Vue Router',
useSlots: 'Use Slots',
verticalCenter: 'Vertical center'
}
}
verticalCenter: 'Vertical center',
},
},
};
</script>
+1 -1
View File
@@ -1,5 +1,5 @@
@import '../style/var';
@import "../style/mixins/hairline";
@import '../style/mixins/hairline';
.van-cell {
position: relative;
+6 -3
View File
@@ -1,7 +1,10 @@
// Utils
import { createNamespace, isDef } from '../utils';
import { cellProps, SharedCellProps } from './shared';
import { emit, inherit } from '../utils/functional';
import { routeProps, RouteProps, functionalRoute } from '../utils/router';
import { cellProps, SharedCellProps } from './shared';
// Components
import Icon from '../icon';
// Types
@@ -109,7 +112,7 @@ function Cell(
clickable,
center: props.center,
required: props.required,
borderless: !props.border
borderless: !props.border,
};
if (size) {
@@ -135,7 +138,7 @@ function Cell(
Cell.props = {
...cellProps,
...routeProps
...routeProps,
};
export default createComponent<CellProps, CellEvents, CellSlots>(Cell);
+3 -3
View File
@@ -14,7 +14,7 @@ export type SharedCellProps = {
value?: string | number;
label?: string | number;
arrowDirection?: 'up' | 'down' | 'left' | 'right';
}
};
export const cellProps = {
icon: String,
@@ -33,6 +33,6 @@ export const cellProps = {
arrowDirection: String,
border: {
type: Boolean,
default: true
}
default: true,
},
};
+12 -12
View File
@@ -7,9 +7,9 @@ test('click event', () => {
const wrapper = mount(Cell, {
context: {
on: {
click
}
}
click,
},
},
});
wrapper.trigger('click');
@@ -20,8 +20,8 @@ test('arrow direction', () => {
const wrapper = mount(Cell, {
propsData: {
isLink: true,
arrowDirection: 'down'
}
arrowDirection: 'down',
},
});
expect(wrapper).toMatchSnapshot();
@@ -38,8 +38,8 @@ test('render slot', () => {
</cell>
`,
components: {
Cell
}
Cell,
},
});
expect(wrapper).toMatchSnapshot();
@@ -50,9 +50,9 @@ test('title-style prop', () => {
propsData: {
title: 'title',
titleStyle: {
color: 'red'
}
}
color: 'red',
},
},
});
expect(wrapper).toMatchSnapshot();
@@ -61,8 +61,8 @@ test('title-style prop', () => {
test('CellGroup title slot', () => {
const wrapper = mount(CellGroup, {
scopedSlots: {
title: () => 'CustomTitle'
}
title: () => 'CustomTitle',
},
});
expect(wrapper).toMatchSnapshot();
+6 -6
View File
@@ -7,20 +7,20 @@ export default createComponent({
mixins: [ParentMixin('vanCheckbox')],
props: {
max: Number,
max: [Number, String],
disabled: Boolean,
iconSize: [Number, String],
checkedColor: String,
value: {
type: Array,
default: () => []
}
default: () => [],
},
},
watch: {
value(val) {
this.$emit('change', val);
}
},
},
methods: {
@@ -38,10 +38,10 @@ export default createComponent({
const names = children.map(item => item.name);
this.$emit('input', names);
}
},
},
render() {
return <div class={bem()}>{this.slots()}</div>;
}
},
});
+8 -7
View File
@@ -2,11 +2,12 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Checkbox, CheckboxGroup } from 'vant';
Vue.use(Checkbox).use(CheckboxGroup);
Vue.use(Checkbox);
Vue.use(CheckboxGroup);
```
## Usage
@@ -17,7 +18,7 @@ Vue.use(Checkbox).use(CheckboxGroup);
<van-checkbox v-model="checked">Checkbox</van-checkbox>
```
```javascript
```js
export default {
data() {
return {
@@ -96,7 +97,7 @@ When Checkboxes are inside a CheckboxGroup, the checked checkboxes's name is an
</van-checkbox-group>
```
```javascript
```js
export default {
data() {
return {
@@ -192,7 +193,7 @@ export default {
| disabled | Disable checkbox | *boolean* | `false` |
| label-disabled | Whether to disable label click | *boolean* | `false` |
| label-position | Can be set to `left` | *string* | `right` |
| icon-size | Icon size | *string \| number* | `20px` |
| icon-size | Icon size | *number \| string* | `20px` |
| checked-color | Checked color | *string* | `#1989fa` | - |
| bind-group `v2.2.4` | Whether to bind with CheckboxGroup | *boolean* | `true` |
@@ -201,9 +202,9 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| v-model | Names of all checked checkboxes | *any[]* | - |
| max | Maximum amount of checked options | *number* | `0`(Unlimited) |
| max | Maximum amount of checked options | *number \| string* | `0`(Unlimited) |
| disabled | Disable all checkboxes | *boolean* | `false` |
| icon-size `v2.2.3` | Icon size of all checkboxes | *string \| number* | `20px` |
| icon-size `v2.2.3` | Icon size of all checkboxes | *number \| string* | `20px` |
| checked-color `v2.2.3` | Checked color of all checkboxes | *string* | `#1989fa` | - |
### Checkbox Events
+10 -9
View File
@@ -2,11 +2,12 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Checkbox, CheckboxGroup } from 'vant';
Vue.use(Checkbox).use(CheckboxGroup);
Vue.use(Checkbox);
Vue.use(CheckboxGroup);
```
## 代码演示
@@ -19,7 +20,7 @@ Vue.use(Checkbox).use(CheckboxGroup);
<van-checkbox v-model="checked">复选框</van-checkbox>
```
```javascript
```js
export default {
data() {
return {
@@ -106,7 +107,7 @@ export default {
</van-checkbox-group>
```
```javascript
```js
export default {
data() {
return {
@@ -208,7 +209,7 @@ export default {
| disabled | 是否禁用复选框 | *boolean* | `false` |
| label-disabled | 是否禁用复选框文本点击 | *boolean* | `false` |
| label-position | 文本位置,可选值为 `left` | *string* | `right` |
| icon-size | 图标大小,默认单位为`px` | *string \| number* | `20px` |
| icon-size | 图标大小,默认单位为`px` | *number \| string* | `20px` |
| checked-color | 选中状态颜色 | *string* | `#1989fa` |
| bind-group `v2.2.4` | 是否与复选框组绑定 | *boolean* | `true` |
@@ -218,8 +219,8 @@ export default {
|------|------|------|------|
| v-model | 所有选中项的标识符 | *any[]* | - |
| disabled | 是否禁用所有复选框 | *boolean* | `false` |
| max | 最大可选数,0 为无限制 | *number* | `0` |
| icon-size `v2.2.3` | 所有复选框的图标大小,默认单位为`px` | *string \| number* | `20px` |
| max | 最大可选数,0 为无限制 | *number \| string* | `0` |
| icon-size `v2.2.3` | 所有复选框的图标大小,默认单位为`px` | *number \| string* | `20px` |
| checked-color `v2.2.3` | 所有复选框的选中状态颜色 | *string* | `#1989fa` |
### Checkbox Events
@@ -244,7 +245,7 @@ export default {
### CheckboxGroup 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 CheckboxGroup 实例并调用实例方法
通过 ref 可以获取到 CheckboxGroup 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
@@ -252,7 +253,7 @@ export default {
### Checkbox 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 Checkbox 实例并调用实例方法
通过 ref 可以获取到 Checkbox 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
+15 -15
View File
@@ -41,7 +41,7 @@
<van-checkbox v-model="checkbox3">
{{ $t('customIcon') }}
<template #icon="{ checked }">
<img :src="checked ? activeIcon : inactiveIcon">
<img :src="checked ? activeIcon : inactiveIcon" />
</template>
</van-checkbox>
</demo-block>
@@ -70,8 +70,12 @@
</van-checkbox-group>
<div class="demo-checkbox-buttons">
<van-button type="primary" @click="checkAll">{{ $t('checkAll') }}</van-button>
<van-button type="info" @click="toggleAll">{{ $t('inverse') }}</van-button>
<van-button type="primary" @click="checkAll">
{{ $t('checkAll') }}
</van-button>
<van-button type="info" @click="toggleAll">
{{ $t('inverse') }}
</van-button>
</div>
</demo-block>
@@ -110,7 +114,7 @@ export default {
toggleAll: '全选与反选',
checkAll: '全选',
inverse: '反选',
disabledLabelClick: '禁用文本点击'
disabledLabelClick: '禁用文本点击',
},
'en-US': {
checkbox: 'Checkbox',
@@ -124,8 +128,8 @@ export default {
toggleAll: 'Toggle All',
checkAll: 'Check All',
inverse: 'Inverse',
disabledLabelClick: 'Disable the click event of label'
}
disabledLabelClick: 'Disable the click event of label',
},
},
data() {
@@ -136,17 +140,13 @@ export default {
checkboxShape: true,
checkboxLabel: true,
checboxIcon: true,
list: [
'a',
'b',
'c'
],
list: ['a', 'b', 'c'],
result: ['a', 'b'],
result2: [],
result3: [],
checkAllResult: [],
activeIcon: 'https://img.yzcdn.cn/vant/user-active.png',
inactiveIcon: 'https://img.yzcdn.cn/vant/user-inactive.png'
inactiveIcon: 'https://img.yzcdn.cn/vant/user-inactive.png',
};
},
@@ -161,13 +161,13 @@ export default {
toggleAll() {
this.$refs.group.toggleAll();
}
}
},
},
};
</script>
<style lang="less">
@import "../../style/var";
@import '../../style/var';
.demo-checkbox {
background: @white;
+16 -11
View File
@@ -4,16 +4,21 @@ import { CheckboxMixin } from '../mixins/checkbox';
const [createComponent, bem] = createNamespace('checkbox');
export default createComponent({
mixins: [CheckboxMixin({
bem,
role: 'checkbox',
parent: 'vanCheckbox'
})],
mixins: [
CheckboxMixin({
bem,
role: 'checkbox',
parent: 'vanCheckbox',
}),
],
computed: {
checked: {
get() {
return this.parent ? this.parent.value.indexOf(this.name) !== -1 : this.value;
if (this.parent) {
return this.parent.value.indexOf(this.name) !== -1;
}
return this.value;
},
set(val) {
@@ -22,14 +27,14 @@ export default createComponent({
} else {
this.$emit('input', val);
}
}
}
},
},
},
watch: {
value(val) {
this.$emit('change', val);
}
},
},
methods: {
@@ -67,6 +72,6 @@ export default createComponent({
parent.$emit('input', value);
}
}
}
}
},
},
});
+1 -1
View File
@@ -28,7 +28,7 @@
width: 1.25em;
height: 1.25em;
color: transparent;
font-size: .8em;
font-size: 0.8em;
line-height: inherit;
text-align: center;
border: 1px solid @checkbox-border-color;
@@ -108,7 +108,11 @@ exports[`renders demo correctly 1`] = `
<!----></i></div><span class="van-checkbox__label">复选框 c</span>
</div>
</div>
<div class="demo-checkbox-buttons"><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">全选</span></button> <button class="van-button van-button--info van-button--normal"><span class="van-button__text">反选</span></button></div>
<div class="demo-checkbox-buttons"><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
全选
</span></button> <button class="van-button van-button--info van-button--normal"><span class="van-button__text">
反选
</span></button></div>
</div>
<div>
<div class="van-checkbox-group">
+19 -24
View File
@@ -1,11 +1,6 @@
import Vue from 'vue';
import Checkbox from '..';
import CheckboxGroup from '../../checkbox-group';
import { mount, later } from '../../../test';
Vue.use(Checkbox);
Vue.use(CheckboxGroup);
test('switch checkbox', async () => {
const wrapper = mount(Checkbox);
@@ -25,8 +20,8 @@ test('switch checkbox', async () => {
test('disabled', () => {
const wrapper = mount(Checkbox, {
propsData: {
disabled: true
}
disabled: true,
},
});
wrapper.find('.van-checkbox__icon').trigger('click');
@@ -36,11 +31,11 @@ test('disabled', () => {
test('label disabled', () => {
const wrapper = mount(Checkbox, {
scopedSlots: {
default: () => 'Label'
default: () => 'Label',
},
propsData: {
labelDisabled: true
}
labelDisabled: true,
},
});
wrapper.find('.van-checkbox__label').trigger('click');
@@ -59,9 +54,9 @@ test('checkbox group', async () => {
`,
data() {
return {
result: []
result: [],
};
}
},
});
const icons = wrapper.findAll('.van-checkbox__icon');
@@ -82,8 +77,8 @@ test('click event', () => {
const onClick = jest.fn();
const wrapper = mount(Checkbox, {
listeners: {
click: onClick
}
click: onClick,
},
});
wrapper.trigger('click');
@@ -97,11 +92,11 @@ test('click event', () => {
test('label-position prop', () => {
const wrapper = mount(Checkbox, {
scopedSlots: {
default: () => 'Label'
default: () => 'Label',
},
propsData: {
labelPosition: 'left'
}
labelPosition: 'left',
},
});
expect(wrapper).toMatchSnapshot();
@@ -114,7 +109,7 @@ test('icon-size prop', () => {
<van-checkbox>label</van-checkbox>
<van-checkbox icon-size="5rem">label</van-checkbox>
</van-checkbox-group>
`
`,
});
expect(wrapper).toMatchSnapshot();
@@ -127,7 +122,7 @@ test('checked-color prop', () => {
<van-checkbox name="a" :value="true">label</van-checkbox>
<van-checkbox name="b" :value="true" checked-color="white">label</van-checkbox>
</van-checkbox-group>
`
`,
});
expect(wrapper).toMatchSnapshot();
@@ -145,9 +140,9 @@ test('bind-group prop', async () => {
return {
value: false,
result: [],
list: ['a', 'b', 'c']
list: ['a', 'b', 'c'],
};
}
},
});
const icons = wrapper.findAll('.van-checkbox__icon');
@@ -168,14 +163,14 @@ test('toggleAll method', async () => {
`,
data() {
return {
result: ['a']
result: ['a'],
};
},
methods: {
toggleAll(checked) {
this.$refs.group.toggleAll(checked);
}
}
},
},
});
wrapper.vm.toggleAll();
+8 -8
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Circle } from 'vant';
@@ -22,7 +22,7 @@ Vue.use(Circle);
/>
```
``` javascript
```js
export default {
data() {
return {
@@ -70,7 +70,7 @@ export default {
/>
```
``` javascript
```js
export default {
data() {
return {
@@ -113,14 +113,14 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| v-model | Current rate | *number* | - |
| rate | Target rate | *number* | `100` |
| size | Circle size | *string \| number* | `100px` |
| rate | Target rate | *number \| string* | `100` |
| size | Circle size | *number \| string* | `100px` |
| color `v2.1.4` | Progress color, passing object to render gradient | *string \| object* | `#1989fa` |
| layer-color | Layer color | *string* | `#fff` |
| layer-color | Layer color | *string* | `white` |
| fill | Fill color | *string* | `none` |
| speed | Animate speedrate/s| *number* | `0` |
| speed | Animate speedrate/s| *number \| string* | `0` |
| text | Text | *string* | - |
| stroke-width | Stroke width | *number* | `40` |
| stroke-width | Stroke width | *number \| string* | `40` |
| stroke-linecap `v2.2.15` | Stroke linecapcan be set to `sqaure` `butt` | *string* | `round` |
| clockwise | Whether to be clockwise | *boolean* | `true` |
+8 -8
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Circle } from 'vant';
@@ -24,7 +24,7 @@ Vue.use(Circle);
/>
```
``` javascript
```js
export default {
data() {
return {
@@ -78,7 +78,7 @@ export default {
/>
```
``` javascript
```js
export default {
data() {
return {
@@ -125,14 +125,14 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| v-model | 当前进度 | *number* | - |
| rate | 目标进度 | *number* | `100` |
| size | 圆环直径,默认单位为 `px` | *string \| number* | `100px` |
| rate | 目标进度 | *number \| string* | `100` |
| size | 圆环直径,默认单位为 `px` | *number \| string* | `100px` |
| color `v2.1.4` | 进度条颜色,传入对象格式可以定义渐变色 | *string \| object* | `#1989fa` |
| layer-color | 轨道颜色 | *string* | `#fff` |
| layer-color | 轨道颜色 | *string* | `white` |
| fill | 填充颜色 | *string* | `none` |
| speed | 动画速度(单位为 rate/s| *number* | `0` |
| speed | 动画速度(单位为 rate/s| *number \| string* | `0` |
| text | 文字 | *string* | - |
| stroke-width | 进度条宽度 | *number* | `40` |
| stroke-width | 进度条宽度 | *number \| string* | `40` |
| stroke-linecap `v2.2.15` | 进度条端点的形状,可选值为`sqaure` `butt` | *string* | `round` |
| clockwise | 是否顺时针增加 | *boolean* | `true` |
+9 -14
View File
@@ -58,12 +58,7 @@
</demo-block>
<div style="margin-top: 15px;">
<van-button
:text="$t('add')"
type="primary"
size="small"
@click="add"
/>
<van-button :text="$t('add')" type="primary" size="small" @click="add" />
<van-button
:text="$t('decrease')"
type="danger"
@@ -85,7 +80,7 @@ export default {
customStyle: '样式定制',
customColor: '颜色定制',
customWidth: '宽度定制',
counterClockwise: '逆时针'
counterClockwise: '逆时针',
},
'en-US': {
gradient: 'Gradient',
@@ -93,8 +88,8 @@ export default {
customStyle: 'Custom Style',
customColor: 'Custom Color',
customWidth: 'Custom Width',
counterClockwise: 'Counter Clockwise'
}
counterClockwise: 'Counter Clockwise',
},
},
data() {
@@ -106,8 +101,8 @@ export default {
currentRate4: 70,
gradientColor: {
'0%': '#3fecff',
'100%': '#6149f6'
}
'100%': '#6149f6',
},
};
},
@@ -118,13 +113,13 @@ export default {
reduce() {
this.rate = format(this.rate - 20);
}
}
},
},
};
</script>
<style lang="less">
@import "../../style/var";
@import '../../style/var';
.demo-circle {
.van-circle {
+28 -28
View File
@@ -1,4 +1,4 @@
import { createNamespace, isObj, addUnit } from '../utils';
import { createNamespace, isObject, addUnit } from '../utils';
import { raf, cancelRaf } from '../utils/dom/raf';
import { BLUE, WHITE } from '../utils/constant';
@@ -24,40 +24,40 @@ export default createComponent({
strokeLinecap: String,
value: {
type: Number,
default: 0
default: 0,
},
speed: {
type: Number,
default: 0
type: [Number, String],
default: 0,
},
size: {
type: [String, Number],
default: 100
type: [Number, String],
default: 100,
},
fill: {
type: String,
default: 'none'
default: 'none',
},
rate: {
type: Number,
default: 100
type: [Number, String],
default: 100,
},
layerColor: {
type: String,
default: WHITE
default: WHITE,
},
color: {
type: [String, Object],
default: BLUE
default: BLUE,
},
strokeWidth: {
type: Number,
default: 40
type: [Number, String],
default: 40,
},
clockwise: {
type: Boolean,
default: true
}
default: true,
},
},
beforeCreate() {
@@ -69,7 +69,7 @@ export default createComponent({
const size = addUnit(this.size);
return {
width: size,
height: size
height: size,
};
},
@@ -78,7 +78,7 @@ export default createComponent({
},
viewBoxSize() {
return 1000 + this.strokeWidth;
return +this.strokeWidth + 1000;
},
layerStyle() {
@@ -86,9 +86,9 @@ export default createComponent({
return {
stroke: `${this.color}`,
strokeWidth: `${this.strokeWidth + 1}px`,
strokeWidth: `${+this.strokeWidth + 1}px`,
strokeLinecap: this.strokeLinecap,
strokeDasharray: `${offset}px ${PERIMETER}px`
strokeDasharray: `${offset}px ${PERIMETER}px`,
};
},
@@ -96,12 +96,12 @@ export default createComponent({
return {
fill: `${this.fill}`,
stroke: `${this.layerColor}`,
strokeWidth: `${this.strokeWidth}px`
strokeWidth: `${this.strokeWidth}px`,
};
},
gradient() {
return isObj(this.color);
return isObject(this.color);
},
LinearGradient() {
@@ -122,15 +122,15 @@ export default createComponent({
</linearGradient>
</defs>
);
}
},
},
watch: {
rate: {
handler() {
handler(rate) {
this.startTime = Date.now();
this.startRate = this.value;
this.endRate = format(this.rate);
this.endRate = format(rate);
this.increase = this.endRate > this.startRate;
this.duration = Math.abs(
((this.startRate - this.endRate) * 1000) / this.speed
@@ -143,8 +143,8 @@ export default createComponent({
this.$emit('input', this.endRate);
}
},
immediate: true
}
immediate: true,
},
},
methods: {
@@ -158,7 +158,7 @@ export default createComponent({
if (this.increase ? rate < this.endRate : rate > this.endRate) {
this.rafId = raf(this.animate);
}
}
},
},
render() {
@@ -178,5 +178,5 @@ export default createComponent({
(this.text && <div class={bem('text')}>{this.text}</div>)}
</div>
);
}
},
});
+10 -10
View File
@@ -6,15 +6,15 @@ test('speed is 0', async () => {
const wrapper = mount(Circle, {
propsData: {
rate: 50,
value: 0
value: 0,
},
listeners: {
input(value) {
Vue.nextTick(() => {
wrapper.setProps({ value });
});
}
}
},
},
});
await later();
@@ -26,11 +26,11 @@ test('animate', async () => {
mount(Circle, {
propsData: {
rate: 50,
speed: 100
speed: 100,
},
listeners: {
input: onInput
}
input: onInput,
},
});
await later(50);
@@ -41,8 +41,8 @@ test('animate', async () => {
test('size prop', () => {
const wrapper = mount(Circle, {
propsData: {
size: 100
}
size: 100,
},
});
expect(wrapper).toMatchSnapshot();
@@ -51,8 +51,8 @@ test('size prop', () => {
test('stroke-linecap prop', () => {
const wrapper = mount(Circle, {
propsData: {
strokeLinecap: 'square'
}
strokeLinecap: 'square',
},
});
expect(wrapper).toMatchSnapshot();
+7 -6
View File
@@ -6,11 +6,12 @@ Quickly and easily create layouts with `van-row` and `van-col`
### Install
``` javascript
```js
import Vue from 'vue';
import { Row, Col } from 'vant';
import { Col, Row } from 'vant';
Vue.use(Row).use(Col);
Vue.use(Col);
Vue.use(Row);
```
## Usage
@@ -94,7 +95,7 @@ Setting `type` to `flex` to enable flex layout
| Attribute | Description | Type | Default |
|------|------|------|------|
| type | Layout type, can be set to `flex` | *string* | - |
| gutter | Grid spacingpx | *string \| number* | - |
| gutter | Grid spacingpx | *number \| string* | - |
| tag | Custom element tag | *string* | `div` |
| justify | Flex main axiscan be set to end/center/space-around/space-between | *string* | `start` |
| align | Flex cross axis, be set to center/bottom | *string* | `top` |
@@ -103,8 +104,8 @@ Setting `type` to `flex` to enable flex layout
| Attribute | Description | Type | Default |
|------|------|------|------|
| span | number of column the grid spans | *string \| number* | - |
| offset | number of spacing on the left side of the grid | *string \| number* | - |
| span | number of column the grid spans | *number \| string* | - |
| offset | number of spacing on the left side of the grid | *number \| string* | - |
| tag | Custom element tag | *string* | `div` |
### Row Events
+7 -6
View File
@@ -6,11 +6,12 @@ Layout 提供了`van-row`和`van-col`两个组件来进行行列布局
### 引入
``` javascript
```js
import Vue from 'vue';
import { Row, Col } from 'vant';
import { Col, Row } from 'vant';
Vue.use(Row).use(Col);
Vue.use(Col);
Vue.use(Row);
```
## 代码演示
@@ -97,7 +98,7 @@ Layout 组件提供了`24列栅格`,通过在`Col`上添加`span`属性设置
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| type | 布局方式,可选值为`flex` | *string* | - |
| gutter | 列元素之间的间距(单位为px) | *string \| number* | - |
| gutter | 列元素之间的间距(单位为px) | *number \| string* | - |
| tag | 自定义元素标签 | *string* | `div` |
| justify | Flex 主轴对齐方式,可选值为 `end` `center` <br> `space-around` `space-between` | *string* | `start` |
| align | Flex 交叉轴对齐方式,可选值为 `center` `bottom` | *string* | `top` |
@@ -106,8 +107,8 @@ Layout 组件提供了`24列栅格`,通过在`Col`上添加`span`属性设置
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| span | 列元素宽度 | *string \| number* | - |
| offset | 列元素偏移距离 | *string \| number* | - |
| span | 列元素宽度 | *number \| string* | - |
| offset | 列元素偏移距离 | *number \| string* | - |
| tag | 自定义元素标签 | *string* | `div` |
### Row Events
+11 -32
View File
@@ -9,19 +9,13 @@
<van-row>
<van-col span="4">span: 4</van-col>
<van-col
span="10"
offset="4"
>
<van-col span="10" offset="4">
offset: 4, span: 10
</van-col>
</van-row>
<van-row>
<van-col
offset="12"
span="12"
>
<van-col offset="12" span="12">
offset: 12, span: 12
</van-col>
</van-row>
@@ -35,47 +29,32 @@
</van-row>
</demo-block>
<demo-block
v-if="!isWeapp"
:title="$t('title3')"
>
<demo-block v-if="!isWeapp" :title="$t('title3')">
<van-row type="flex">
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
</van-row>
<van-row
type="flex"
justify="center"
>
<van-row type="flex" justify="center">
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
</van-row>
<van-row
type="flex"
justify="end"
>
<van-row type="flex" justify="end">
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
</van-row>
<van-row
type="flex"
justify="space-between"
>
<van-row type="flex" justify="space-between">
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
</van-row>
<van-row
type="flex"
justify="space-around"
>
<van-row type="flex" justify="space-around">
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
<van-col span="6">span: 6</van-col>
@@ -89,13 +68,13 @@ export default {
i18n: {
'zh-CN': {
title2: '在列元素之间增加间距',
title3: 'Flex 布局'
title3: 'Flex 布局',
},
'en-US': {
title2: 'Column Spacing',
title3: 'Flex Layout'
}
}
title3: 'Flex Layout',
},
},
};
</script>
+5 -5
View File
@@ -8,8 +8,8 @@ export default createComponent({
offset: [Number, String],
tag: {
type: String,
default: 'div'
}
default: 'div',
},
},
computed: {
@@ -20,13 +20,13 @@ export default createComponent({
style() {
const padding = `${this.gutter / 2}px`;
return this.gutter ? { paddingLeft: padding, paddingRight: padding } : {};
}
},
},
methods: {
onClick(event) {
this.$emit('click', event);
}
},
},
render() {
@@ -40,5 +40,5 @@ export default createComponent({
{this.slots()}
</this.tag>
);
}
},
});
+8 -2
View File
@@ -8,7 +8,13 @@
.generate-col(24);
.generate-col(@n, @i: 1) when (@i =< @n) {
.van-col--@{i} { width: @i * 100% / 24; }
.van-col--offset-@{i} { margin-left: @i * 100% / 24; }
.van-col--@{i} {
width: @i * 100% / 24;
}
.van-col--offset-@{i} {
margin-left: @i * 100% / 24;
}
.generate-col(@n, (@i + 1));
}
+16 -10
View File
@@ -1,9 +1,14 @@
// Utils
import { createNamespace, isDef } from '../utils';
import { BORDER_TOP } from '../utils/constant';
import { raf, doubleRaf } from '../utils/dom/raf';
// Mixins
import { ChildrenMixin } from '../mixins/relation';
// Components
import Cell from '../cell';
import { cellProps } from '../cell/shared';
import { ChildrenMixin } from '../mixins/relation';
const [createComponent, bem] = createNamespace('collapse-item');
@@ -18,14 +23,14 @@ export default createComponent({
disabled: Boolean,
isLink: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {
show: null,
inited: null
inited: null,
};
},
@@ -53,7 +58,7 @@ export default createComponent({
return accordion
? value === this.currentName
: value.some(name => name === this.currentName);
}
},
},
created() {
@@ -96,7 +101,7 @@ export default createComponent({
this.onTransitionEnd();
}
});
}
},
},
methods: {
@@ -106,9 +111,10 @@ export default createComponent({
}
const { parent, currentName } = this;
const name = parent.accordion && currentName === parent.value ? '' : currentName;
const close = parent.accordion && currentName === parent.value;
const name = close ? '' : currentName;
this.parent.switch(name, !this.expanded);
parent.switch(name, !this.expanded);
},
onTransitionEnd() {
@@ -162,7 +168,7 @@ export default createComponent({
</div>
);
}
}
},
},
render() {
@@ -172,5 +178,5 @@ export default createComponent({
{this.genContent()}
</div>
);
}
},
});
+11 -14
View File
@@ -2,11 +2,12 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { Collapse, CollapseItem } from 'vant';
Vue.use(Collapse).use(CollapseItem);
Vue.use(Collapse);
Vue.use(CollapseItem);
```
## Usage
@@ -23,7 +24,7 @@ Use `v-model` to control the name of active panels
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -45,7 +46,7 @@ In accordion mode, only one panel can be expanded at the same time.
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -63,17 +64,13 @@ export default {
<div slot="title">Title1 <van-icon name="question-o" /></div>
Content
</van-collapse-item>
<van-collapse-item
title="Title2"
name="2"
icon="shop-o"
>
<van-collapse-item title="Title2" name="2" icon="shop-o">
Content
</van-collapse-item>
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -89,7 +86,7 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| v-model | Names of current active panels | accordion mode *string \| number*<br>non-accordion mode*(string \| number)[]* | - |
| v-model | Names of current active panels | accordion mode *number \| string*<br>non-accordion mode*(number \| string)[]* | - |
| accordion | Whether to be accordion mode | *boolean* | `false` |
| border | Whether to show outer border | *boolean* | `true` |
@@ -103,11 +100,11 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| name | Name | *string \| number* | `index` |
| name | Name | *number \| string* | `index` |
| icon | Left Icon | *string* | - |
| size | Title sizecan be set to `large` | *string* | - |
| title | Title | *string \| number* | - |
| value | Right text | *string \| number* | - |
| title | Title | *number \| string* | - |
| value | Right text | *number \| string* | - |
| label | Description below the title | *string* | - |
| border | Whether to show inner border | *boolean* | `true` |
| disabled | Whether to disabled collapse | *boolean* | `false` |
+13 -17
View File
@@ -2,11 +2,12 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { Collapse, CollapseItem } from 'vant';
Vue.use(Collapse).use(CollapseItem);
Vue.use(Collapse);
Vue.use(CollapseItem);
```
## 代码演示
@@ -23,7 +24,7 @@ Vue.use(Collapse).use(CollapseItem);
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -45,7 +46,7 @@ export default {
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -63,18 +64,13 @@ export default {
<div slot="title">标题1 <van-icon name="question-o" /></div>
内容
</van-collapse-item>
<van-collapse-item
title="标题2"
name="2"
icon="shop-o"
>
<van-collapse-item title="标题2" name="2" icon="shop-o">
内容
</van-collapse-item>
</van-collapse>
```
``` javascript
```js
export default {
data() {
return {
@@ -90,7 +86,7 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| v-model | 当前展开面板的 name | 手风琴模式:*string \| number*<br>非手风琴模式:*(string \| number)[]* | - |
| v-model | 当前展开面板的 name | 手风琴模式:*number \| string*<br>非手风琴模式:*(number \| string)[]* | - |
| accordion | 是否开启手风琴模式 | *boolean* | `false` |
| border | 是否显示外边框 | *boolean* | `true` |
@@ -104,12 +100,12 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| name | 唯一标识符,默认为索引值 | *string \| number* | `index` |
| icon | 标题栏左侧图标名称或图片链接,可选值见 [Icon 组件](#/zh-CN/icon) | *string* | - |
| name | 唯一标识符,默认为索引值 | *number \| string* | `index` |
| icon | 标题栏左侧 [图标名称](#/zh-CN/icon) 或图片链接 | *string* | - |
| size | 标题栏大小,可选值为 `large` | *string* | - |
| title | 标题栏左侧内容 | *string \| number* | - |
| value | 标题栏右侧内容 | *string \| number* | - |
| label | 标题栏描述信息 | *string \| number* | - |
| title | 标题栏左侧内容 | *number \| string* | - |
| value | 标题栏右侧内容 | *number \| string* | - |
| label | 标题栏描述信息 | *number \| string* | - |
| border | 是否显示内边框 | *boolean* | `true` |
| is-link | 是否展示标题栏右侧箭头并开启点击反馈 | *boolean* | `true` |
| disabled | 是否禁用面板 | *boolean* | `false` |
+22 -18
View File
@@ -2,25 +2,29 @@
<demo-section>
<demo-block :title="$t('basicUsage')">
<van-collapse v-model="active1">
<van-collapse-item :title="$t('title') + 1">{{ $t('text') }}</van-collapse-item>
<van-collapse-item :title="$t('title') + 2">{{ $t('text') }}</van-collapse-item>
<van-collapse-item
:title="$t('title') + 3"
disabled
>
<van-collapse-item :title="$t('title') + 1">
{{ $t('text') }}
</van-collapse-item>
<van-collapse-item :title="$t('title') + 2">
{{ $t('text') }}
</van-collapse-item>
<van-collapse-item :title="$t('title') + 3" disabled>
{{ $t('text') }}
</van-collapse-item>
</van-collapse>
</demo-block>
<demo-block :title="$t('accordion')">
<van-collapse
v-model="active2"
accordion
>
<van-collapse-item :title="$t('title') + 1">{{ $t('text') }}</van-collapse-item>
<van-collapse-item :title="$t('title') + 2">{{ $t('text') }}</van-collapse-item>
<van-collapse-item :title="$t('title') + 3">{{ $t('text') }}</van-collapse-item>
<van-collapse v-model="active2" accordion>
<van-collapse-item :title="$t('title') + 1">
{{ $t('text') }}
</van-collapse-item>
<van-collapse-item :title="$t('title') + 2">
{{ $t('text') }}
</van-collapse-item>
<van-collapse-item :title="$t('title') + 3">
{{ $t('text') }}
</van-collapse-item>
</van-collapse>
</demo-block>
@@ -50,22 +54,22 @@ export default {
'zh-CN': {
accordion: '手风琴',
titleSlot: '自定义标题内容',
text: '代码是写出来给人看的,附带能在机器上运行'
text: '代码是写出来给人看的,附带能在机器上运行',
},
'en-US': {
accordion: 'Accordion',
titleSlot: 'Custom title',
text: 'Content'
}
text: 'Content',
},
},
data() {
return {
active1: [0],
active2: 0,
active3: []
active3: [],
};
}
},
};
</script>
+4 -4
View File
@@ -12,8 +12,8 @@ export default createComponent({
value: [String, Number, Array],
border: {
type: Boolean,
default: true
}
default: true,
},
},
methods: {
@@ -25,7 +25,7 @@ export default createComponent({
}
this.$emit('change', name);
this.$emit('input', name);
}
},
},
render() {
@@ -34,5 +34,5 @@ export default createComponent({
{this.slots()}
</div>
);
}
},
});
@@ -10,7 +10,9 @@ exports[`renders demo correctly 1`] = `
<!----></i>
</div>
<div class="van-collapse-item__wrapper">
<div class="van-collapse-item__content">代码是写出来给人看的,附带能在机器上运行</div>
<div class="van-collapse-item__content">
代码是写出来给人看的,附带能在机器上运行
</div>
</div>
</div>
<div class="van-collapse-item van-hairline--top">
@@ -35,7 +37,9 @@ exports[`renders demo correctly 1`] = `
<!----></i>
</div>
<div class="van-collapse-item__wrapper">
<div class="van-collapse-item__content">代码是写出来给人看的,附带能在机器上运行</div>
<div class="van-collapse-item__content">
代码是写出来给人看的,附带能在机器上运行
</div>
</div>
</div>
<div class="van-collapse-item van-hairline--top">
+15 -19
View File
@@ -1,11 +1,7 @@
import Vue from 'vue';
import Collapse from '..';
import CollapseItem from '../../collapse-item';
import { later, mount } from '../../../test';
Vue.use(Collapse);
Vue.use(CollapseItem);
const component = {
template: `
<van-collapse v-model="active" :accordion="accordion" :border="border">
@@ -18,14 +14,14 @@ const component = {
accordion: Boolean,
border: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {
active: this.accordion ? '' : []
active: this.accordion ? '' : [],
};
}
},
};
test('basic mode', async () => {
@@ -49,8 +45,8 @@ test('basic mode', async () => {
test('accordion', async () => {
const wrapper = mount(component, {
propsData: {
accordion: true
}
accordion: true,
},
});
const titles = wrapper.findAll('.van-collapse-item__title');
@@ -82,9 +78,9 @@ test('render collapse-item slot', () => {
`,
data() {
return {
active: []
active: [],
};
}
},
});
expect(wrapper).toMatchSnapshot();
@@ -93,8 +89,8 @@ test('render collapse-item slot', () => {
test('disable border', () => {
const wrapper = mount(component, {
propsData: {
border: false
}
border: false,
},
});
expect(wrapper).toMatchSnapshot();
@@ -110,14 +106,14 @@ test('lazy render collapse content', async () => {
`,
components: {
Collapse,
CollapseItem
CollapseItem,
},
data() {
return {
content: '',
active: []
active: [],
};
}
},
});
const titles = wrapper.findAll('.van-collapse-item__title');
@@ -140,9 +136,9 @@ test('warn when value type is incorrect', () => {
`,
data() {
return {
active: 0
active: 0,
};
}
},
});
expect(error).toHaveBeenCalledTimes(1);
+7 -8
View File
@@ -2,14 +2,13 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { ContactCard, ContactList, ContactEdit } from 'vant';
Vue
.use(ContactCard)
.use(ContactList)
.use(ContactEdit);
Vue.use(ContactCard);
Vue.use(ContactList);
Vue.use(ContactEdit);
```
## Usage
@@ -47,7 +46,7 @@ Vue
</van-popup>
```
``` javascript
```js
export default {
data() {
return {
@@ -152,7 +151,7 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| v-model | Id of chosen contact | *string \| number* | - |
| 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* | - |
@@ -189,7 +188,7 @@ export default {
| key | Description | Type |
|------|------|------|
| id | ID | *string \| number* |
| id | ID | *number \| string* |
| name | Name | *string* |
| tel | Phone | *string* |
| isDefault | Is default contact | *boolean* |
+8 -9
View File
@@ -6,14 +6,13 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { ContactCard, ContactList, ContactEdit } from 'vant';
Vue
.use(ContactCard)
.use(ContactList)
.use(ContactEdit);
Vue.use(ContactCard);
Vue.use(ContactList);
Vue.use(ContactEdit);
```
## 代码演示
@@ -51,7 +50,7 @@ Vue
</van-popup>
```
``` javascript
```js
export default {
data() {
return {
@@ -156,7 +155,7 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| v-model | 当前选中联系人的 id | *string \| number* | - |
| v-model | 当前选中联系人的 id | *number \| string* | - |
| list | 联系人列表 | *Contact[]* | `[]` |
| add-text | 新建按钮文案 | *string* | `新建联系人` |
| default-tag-text `v2.3.0` | 默认联系人标签文案 | *string* | - |
@@ -192,7 +191,7 @@ export default {
| 键名 | 说明 | 类型 |
|------|------|------|
| id | 每位联系人的唯一标识 | *string \| number* |
| id | 每位联系人的唯一标识 | *number \| string* |
| name | 联系人姓名 | *string* |
| tel | 联系人手机号 | *string \| number* |
| tel | 联系人手机号 | *number \| string* |
| isDefault | 是否为默认联系人 | *boolean* |
+10 -18
View File
@@ -8,11 +8,7 @@
@click="showList = true"
/>
<van-popup
v-model="showList"
position="bottom"
:lazy-render="false"
>
<van-popup v-model="showList" position="bottom" :lazy-render="false">
<van-contact-list
v-model="chosenContactId"
:list="list"
@@ -23,11 +19,7 @@
/>
</van-popup>
<van-popup
v-model="showEdit"
position="bottom"
:lazy-render="false"
>
<van-popup v-model="showEdit" position="bottom" :lazy-render="false">
<van-contact-edit
show-set-default
:set-default-label="$t('defaultLabel')"
@@ -56,13 +48,13 @@ export default {
'zh-CN': {
name: '张三',
defaultLabel: '设为默认联系人',
defaultTagText: '默认'
defaultTagText: '默认',
},
'en-US': {
name: 'John Snow',
defaultLabel: 'Set as the default contact',
defaultTagText: 'default'
}
defaultTagText: 'default',
},
},
data() {
@@ -72,7 +64,7 @@ export default {
showList: false,
showEdit: false,
isEdit: false,
list: []
list: [],
};
},
@@ -82,7 +74,7 @@ export default {
name: this.$t('name'),
tel: '13000000000',
id: 0,
isDefault: 1
isDefault: 1,
};
},
@@ -93,7 +85,7 @@ export default {
currentContact() {
const id = this.chosenContactId;
return id !== null ? this.list.filter(item => item.id === id)[0] : {};
}
},
},
created() {
@@ -135,8 +127,8 @@ export default {
if (this.chosenContactId === info.id) {
this.chosenContactId = null;
}
}
}
},
},
};
</script>
+7 -4
View File
@@ -1,5 +1,8 @@
// Utils
import { createNamespace } from '../utils';
import { emit, inherit } from '../utils/functional';
// Components
import Cell from '../cell';
// Types
@@ -37,7 +40,7 @@ function ContactCard(
return [
<div>{`${t('name')}${props.name}`}</div>,
<div>{`${t('tel')}${props.tel}`}</div>
<div>{`${t('tel')}${props.tel}`}</div>,
];
}
@@ -63,12 +66,12 @@ ContactCard.props = {
addText: String,
editable: {
type: Boolean,
default: true
default: true,
},
type: {
type: String,
default: 'add'
}
default: 'add',
},
};
export default createComponent<ContactCardProps>(ContactCard);
+20 -20
View File
@@ -5,7 +5,7 @@ import { mount, later } from '../../../test';
const contactInfo = {
name: 'test',
tel: '123123213'
tel: '123123213',
};
describe('ContactCard', () => {
@@ -14,9 +14,9 @@ describe('ContactCard', () => {
const wrapper = mount(ContactCard, {
context: {
on: {
click
}
}
click,
},
},
});
wrapper.trigger('click');
@@ -27,13 +27,13 @@ describe('ContactCard', () => {
const click = jest.fn();
const wrapper = mount(ContactCard, {
propsData: {
editable: false
editable: false,
},
context: {
on: {
click
}
}
click,
},
},
});
wrapper.trigger('click');
@@ -45,8 +45,8 @@ describe('ContactList', () => {
test('render', () => {
const wrapper = mount(ContactList, {
propsData: {
list: [contactInfo]
}
list: [contactInfo],
},
});
expect(wrapper).toMatchSnapshot();
});
@@ -55,13 +55,13 @@ describe('ContactList', () => {
const onSelect = jest.fn();
const wrapper = mount(ContactList, {
propsData: {
list: [contactInfo]
list: [contactInfo],
},
context: {
on: {
select: onSelect
}
}
select: onSelect,
},
},
});
wrapper.find('.van-radio__icon').trigger('click');
@@ -74,8 +74,8 @@ describe('ContactEdit', () => {
const createComponent = () => {
const wrapper = mount(ContactEdit, {
propsData: {
contactInfo
}
contactInfo,
},
});
const button = wrapper.find('.van-button');
@@ -86,7 +86,7 @@ describe('ContactEdit', () => {
data,
field,
button,
errorInfo
errorInfo,
};
};
@@ -114,7 +114,7 @@ describe('ContactEdit', () => {
expect(errorInfo.tel).toBeFalsy();
expect(wrapper.emitted('save')[0][0]).toEqual({
name: 'test',
tel: '13000000000'
tel: '13000000000',
});
});
@@ -127,8 +127,8 @@ describe('ContactEdit', () => {
test('delete contact', async () => {
const wrapper = mount(ContactEdit, {
propsData: {
isEdit: true
}
isEdit: true,
},
});
const deleteButton = wrapper.findAll('.van-button').at(1);
+18 -15
View File
@@ -1,16 +1,19 @@
// Utils
import { createNamespace } from '../utils';
import Button from '../button';
import { isMobile } from '../utils/validate/mobile';
// Components
import Cell from '../cell';
import Field from '../field';
import Button from '../button';
import Dialog from '../dialog';
import Switch from '../switch';
import Cell from '../cell';
import { isMobile } from '../utils/validate/mobile';
const [createComponent, bem, t] = createNamespace('contact-edit');
const defaultContact = {
tel: '',
name: ''
name: '',
};
export default createComponent({
@@ -22,24 +25,24 @@ export default createComponent({
setDefaultLabel: String,
contactInfo: {
type: Object,
default: () => ({ ...defaultContact })
default: () => ({ ...defaultContact }),
},
telValidator: {
type: Function,
default: isMobile
}
default: isMobile,
},
},
data() {
return {
data: {
...defaultContact,
...this.contactInfo
...this.contactInfo,
},
errorInfo: {
name: '',
tel: ''
}
tel: '',
},
};
},
@@ -47,9 +50,9 @@ export default createComponent({
contactInfo(val) {
this.data = {
...defaultContact,
...val
...val,
};
}
},
},
methods: {
@@ -83,11 +86,11 @@ export default createComponent({
onDelete() {
Dialog.confirm({
message: t('confirmDelete')
message: t('confirmDelete'),
}).then(() => {
this.$emit('delete', this.data);
});
}
},
},
render() {
@@ -152,5 +155,5 @@ export default createComponent({
</div>
</div>
);
}
},
});
+7 -4
View File
@@ -1,16 +1,19 @@
// Utils
import { createNamespace } from '../utils';
import { RED } from '../utils/constant';
import { emit, inherit } from '../utils/functional';
// Components
import Tag from '../tag';
import Icon from '../icon';
import Cell from '../cell';
import Tag from '../tag';
import Button from '../button';
import Radio from '../radio';
import Button from '../button';
import RadioGroup from '../radio-group';
// Types
import { CreateElement, RenderContext, VNode } from 'vue/types';
import { DefaultSlots } from '../utils/types';
import { CreateElement, RenderContext, VNode } from 'vue/types';
export type ContactListItem = {
id: string | number;
@@ -122,7 +125,7 @@ ContactList.props = {
value: null as any,
list: Array,
addText: String,
defaultTagText: String
defaultTagText: String,
};
export default createComponent(ContactList);
+8 -12
View File
@@ -2,7 +2,7 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { CountDown } from 'vant';
@@ -30,20 +30,13 @@ export default {
### Custom Format
```html
<van-count-down
:time="time"
format="DD Day, HH:mm:ss"
/>
<van-count-down :time="time" format="DD Day, HH:mm:ss" />
```
### Millisecond
```html
<van-count-down
millisecond
:time="time"
format="HH:mm:ss:SS"
/>
<van-count-down millisecond :time="time" format="HH:mm:ss:SS" />
```
### Custom Style
@@ -89,6 +82,8 @@ export default {
```
```js
import { Toast } from 'vant';
export default {
methods: {
start() {
@@ -101,7 +96,7 @@ export default {
this.$refs.countDown.reset();
},
finish() {
this.$toast('Finished');
Toast('Finished');
}
}
}
@@ -113,7 +108,7 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| time | Total time | *number* | - |
| time | Total time | *number \| string* | `0` |
| format | Time format | *string* | `HH:mm:ss` |
| auto-start | Whether to auto start count down | *boolean* | `true` |
| millisecond | Whether to enable millisecond render | *boolean* | `false` |
@@ -135,6 +130,7 @@ export default {
| Event | Description | Arguments |
|------|------|------|
| finish | Triggered when count down finished | - |
| change `v2.4.4` | Triggered when count down changed | timeData |
### Slots
+9 -13
View File
@@ -2,7 +2,7 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { CountDown } from 'vant';
@@ -34,10 +34,7 @@ export default {
通过`format`属性设置倒计时文本的内容
```html
<van-count-down
:time="time"
format="DD 天 HH 时 mm 分 ss 秒"
/>
<van-count-down :time="time" format="DD 天 HH 时 mm 分 ss 秒" />
```
### 毫秒级渲染
@@ -45,11 +42,7 @@ export default {
倒计时默认每秒渲染一次,设置`millisecond`属性可以开启毫秒级渲染
```html
<van-count-down
millisecond
:time="time"
format="HH:mm:ss:SS"
/>
<van-count-down millisecond :time="time" format="HH:mm:ss:SS" />
```
### 自定义样式
@@ -99,6 +92,8 @@ export default {
```
```js
import { Toast } from 'vant';
export default {
methods: {
start() {
@@ -111,7 +106,7 @@ export default {
this.$refs.countDown.reset();
},
finish() {
this.$toast('倒计时结束');
Toast('倒计时结束');
}
}
}
@@ -123,7 +118,7 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| time | 倒计时时长,单位毫秒 | *number* | - |
| time | 倒计时时长,单位毫秒 | *number \| string* | `0` |
| format | 时间格式 | *string* | `HH:mm:ss` |
| auto-start | 是否自动开始倒计时 | *boolean* | `true` |
| millisecond | 是否开启毫秒级渲染 | *boolean* | `false` |
@@ -145,6 +140,7 @@ export default {
| 事件名 | 说明 | 回调参数 |
|------|------|------|
| finish | 倒计时结束时触发 | - |
| change `v2.4.4` | 倒计时变化时触发 | timeData |
### Slots
@@ -164,7 +160,7 @@ export default {
### 方法
通过 [ref](https://cn.vuejs.org/v2/api/#ref) 可以获取到 CountDown 实例并调用实例方法
通过 ref 可以获取到 CountDown 实例并调用实例方法,详见 [组件实例方法](#/zh-CN/quickstart#zu-jian-shi-li-fang-fa)
| 方法名 | 说明 | 参数 | 返回值 |
|------|------|------|------|
+6 -6
View File
@@ -60,7 +60,7 @@ export default {
reset: '重置',
pause: '暂停',
start: '开始',
finished: '倒计时结束'
finished: '倒计时结束',
},
'en-US': {
millisecond: 'Millisecond',
@@ -71,13 +71,13 @@ export default {
reset: 'Reset',
pause: 'Pause',
start: 'Start',
finished: 'Finished'
}
finished: 'Finished',
},
},
data() {
return {
time: 30 * 60 * 60 * 1000
time: 30 * 60 * 60 * 1000,
};
},
@@ -92,8 +92,8 @@ export default {
reset() {
this.$refs.countDown.reset();
}
}
},
},
};
</script>
+13 -12
View File
@@ -8,22 +8,22 @@ export default createComponent({
props: {
millisecond: Boolean,
time: {
type: Number,
default: 0
type: [Number, String],
default: 0,
},
format: {
type: String,
default: 'HH:mm:ss'
default: 'HH:mm:ss',
},
autoStart: {
type: Boolean,
default: true
}
default: true,
},
},
data() {
return {
remain: 0
remain: 0,
};
},
@@ -34,14 +34,14 @@ export default createComponent({
formattedTime() {
return parseFormat(this.format, this.timeData);
}
},
},
watch: {
time: {
immediate: true,
handler: 'reset'
}
handler: 'reset',
},
},
activated() {
@@ -84,7 +84,7 @@ export default createComponent({
// @exposed-api
reset() {
this.pause();
this.remain = this.time;
this.remain = +this.time;
if (this.autoStart) {
this.start();
@@ -141,12 +141,13 @@ export default createComponent({
setRemain(remain) {
this.remain = remain;
this.$emit('change', this.timeData);
if (remain === 0) {
this.pause();
this.$emit('finish');
}
}
},
},
render() {
@@ -155,5 +156,5 @@ export default createComponent({
{this.slots('default', this.timeData) || this.formattedTime}
</div>
);
}
},
});
+45 -30
View File
@@ -1,14 +1,11 @@
import Vue from 'vue';
import CountDown from '..';
import { mount, later } from '../../../test';
Vue.use(CountDown);
test('macro task finish event', async () => {
const wrapper = mount(CountDown, {
propsData: {
time: 1
}
time: 1,
},
});
expect(wrapper.emitted('finish')).toBeFalsy();
@@ -20,8 +17,8 @@ test('micro task finish event', async () => {
const wrapper = mount(CountDown, {
propsData: {
time: 1,
millisecond: true
}
millisecond: true,
},
});
expect(wrapper.emitted('finish')).toBeFalsy();
@@ -33,8 +30,8 @@ test('macro task re-render', async () => {
const wrapper = mount(CountDown, {
propsData: {
time: 1000,
format: 'SSS'
}
format: 'SSS',
},
});
const prevSnapShot = wrapper.html();
@@ -49,8 +46,8 @@ test('micro task re-render', async () => {
propsData: {
time: 100,
format: 'SSS',
millisecond: true
}
millisecond: true,
},
});
const prevSnapShot = wrapper.html();
@@ -65,8 +62,8 @@ test('disable auto-start prop', async () => {
propsData: {
time: 100,
format: 'SSS',
autoStart: false
}
autoStart: false,
},
});
await later(50);
@@ -79,8 +76,8 @@ test('start method', async () => {
time: 100,
format: 'SSS',
autoStart: false,
millisecond: true
}
millisecond: true,
},
});
const prevSnapShot = wrapper.html();
@@ -100,8 +97,8 @@ test('pause method', async () => {
propsData: {
time: 100,
format: 'SSS',
millisecond: true
}
millisecond: true,
},
});
const prevSnapShot = wrapper.html();
@@ -117,8 +114,8 @@ test('reset method', async () => {
propsData: {
time: 100,
format: 'SSS',
millisecond: true
}
millisecond: true,
},
});
const prevSnapShot = wrapper.html();
@@ -134,8 +131,8 @@ test('complete format prop', () => {
propsData: {
time: 30 * 60 * 60 * 1000 - 1,
autoStart: false,
format: 'DD-HH-mm-ss-SSS'
}
format: 'DD-HH-mm-ss-SSS',
},
});
expect(wrapper).toMatchSnapshot();
@@ -146,8 +143,8 @@ test('milliseconds format SS', () => {
propsData: {
time: 1500,
autoStart: false,
format: 'ss-SS'
}
format: 'ss-SS',
},
});
expect(wrapper).toMatchSnapshot();
@@ -158,8 +155,8 @@ test('milliseconds format S', () => {
propsData: {
time: 1500,
autoStart: false,
format: 'ss-S'
}
format: 'ss-S',
},
});
expect(wrapper).toMatchSnapshot();
@@ -170,8 +167,8 @@ test('incomplate format prop', () => {
propsData: {
time: 30 * 60 * 60 * 1000 - 1,
autoStart: false,
format: 'HH-mm-ss-SSS'
}
format: 'HH-mm-ss-SSS',
},
});
expect(wrapper).toMatchSnapshot();
@@ -193,14 +190,14 @@ test('pause when deactivated', async () => {
`,
data() {
return {
render: true
render: true,
};
},
methods: {
getCountDown() {
return this.$refs.countDown;
}
}
},
},
});
const countDown = wrapper.vm.getCountDown();
@@ -216,3 +213,21 @@ test('pause when deactivated', async () => {
wrapper.setData({ render: true });
expect(countDown.counting).toBeFalsy();
});
test('change event', async () => {
const wrapper = mount(CountDown, {
propsData: {
time: 1,
},
});
expect(wrapper.emitted('change')).toBeFalsy();
await later(50);
expect(wrapper.emitted('change')[0][0]).toEqual({
days: 0,
hours: 0,
milliseconds: 0,
minutes: 0,
seconds: 0,
});
});
+1 -1
View File
@@ -25,7 +25,7 @@ export function parseTimeData(time: number): TimeData {
hours,
minutes,
seconds,
milliseconds
milliseconds,
};
}
+14 -11
View File
@@ -1,5 +1,8 @@
// Utils
import { createNamespace } from '../utils';
import { inherit } from '../utils/functional';
// Components
import Cell from '../cell';
// Types
@@ -13,14 +16,14 @@ export type CouponCellProps = {
coupons: Coupon[];
currency: string;
editable: boolean;
chosenCoupon: number;
chosenCoupon: number | string;
};
const [createComponent, bem, t] = createNamespace('coupon-cell');
function formatValue(props: CouponCellProps) {
const { coupons, chosenCoupon, currency } = props;
const coupon = coupons[chosenCoupon];
const coupon = coupons[+chosenCoupon];
if (coupon) {
const value = coupon.value || coupon.denominations || 0;
@@ -36,7 +39,7 @@ function CouponCell(
slots: DefaultSlots,
ctx: RenderContext<CouponCellProps>
) {
const valueClass = props.coupons[props.chosenCoupon]
const valueClass = props.coupons[+props.chosenCoupon]
? 'van-coupon-cell--selected'
: '';
const value = formatValue(props);
@@ -55,31 +58,31 @@ function CouponCell(
}
CouponCell.model = {
prop: 'chosenCoupon'
prop: 'chosenCoupon',
};
CouponCell.props = {
title: String,
coupons: {
type: Array,
default: () => []
default: () => [],
},
currency: {
type: String,
default: '¥'
default: '¥',
},
border: {
type: Boolean,
default: true
default: true,
},
editable: {
type: Boolean,
default: true
default: true,
},
chosenCoupon: {
type: Number,
default: -1
}
type: [Number, String],
default: -1,
},
};
export default createComponent<CouponCellProps>(CouponCell);
+5 -6
View File
@@ -2,11 +2,12 @@
### Install
``` javascript
```js
import Vue from 'vue';
import { CouponCell, CouponList } from 'vant';
Vue.use(CouponCell).use(CouponList);
Vue.use(CouponCell);
Vue.use(CouponList);
```
## Usage
@@ -20,7 +21,6 @@ Vue.use(CouponCell).use(CouponList);
:chosen-coupon="chosenCoupon"
@click="showList = true"
/>
<!-- Coupon List -->
<van-popup
v-model="showList"
@@ -38,7 +38,7 @@ Vue.use(CouponCell).use(CouponList);
</van-popup>
```
```javascript
```js
const coupon = {
available: 1,
originCondition: 0,
@@ -59,7 +59,6 @@ export default {
disabledCoupons: [coupon]
}
},
methods: {
onChange(index) {
this.showList = false;
@@ -79,7 +78,7 @@ export default {
| Attribute | Description | Type | Default |
|------|------|------|------|
| title | Cell title | *string* | `Coupon` |
| chosen-coupon | Index of chosen coupon | *number* | `-1` |
| chosen-coupon | Index of chosen coupon | *number \| string* | `-1` |
| coupons | Coupon list | *Coupon[]* | `[]` |
| editable | Cell editable | *boolean* | `true` |
| border | Whether to show innner border | *boolean* | `true` |
+5 -6
View File
@@ -2,11 +2,12 @@
### 引入
``` javascript
```js
import Vue from 'vue';
import { CouponCell, CouponList } from 'vant';
Vue.use(CouponCell).use(CouponList);
Vue.use(CouponCell);
Vue.use(CouponList);
```
## 代码演示
@@ -20,7 +21,6 @@ Vue.use(CouponCell).use(CouponList);
:chosen-coupon="chosenCoupon"
@click="showList = true"
/>
<!-- 优惠券列表 -->
<van-popup
v-model="showList"
@@ -38,7 +38,7 @@ Vue.use(CouponCell).use(CouponList);
</van-popup>
```
```javascript
```js
const coupon = {
available: 1,
condition: '无使用门槛\n最多优惠12元',
@@ -59,7 +59,6 @@ export default {
disabledCoupons: [coupon]
}
},
methods: {
onChange(index) {
this.showList = false;
@@ -79,7 +78,7 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|------|
| title | 单元格标题 | *string* | `优惠券` |
| chosen-coupon | 当前选中优惠券的索引 | *number* | `-1` |
| chosen-coupon | 当前选中优惠券的索引 | *number \| string* | `-1` |
| coupons | 可用优惠券列表 | *Coupon[]* | `[]` |
| editable | 能否切换优惠券 | *boolean* | `true` |
| border | 是否显示内边框 | *boolean* | `true` |

Some files were not shown because too many files have changed in this diff Show More