mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(PickerGroup): add next-step-text prop (#11487)
This commit is contained in:
parent
082a596568
commit
f5f4ae6997
@ -1,4 +1,9 @@
|
||||
import { defineComponent, type InjectionKey, type ExtractPropTypes } from 'vue';
|
||||
import {
|
||||
ref,
|
||||
defineComponent,
|
||||
type InjectionKey,
|
||||
type ExtractPropTypes,
|
||||
} from 'vue';
|
||||
|
||||
// Utils
|
||||
import { extend, makeArrayProp, createNamespace } from '../utils';
|
||||
@ -20,6 +25,7 @@ export const PICKER_GROUP_KEY: InjectionKey<PickerGroupProvide> = Symbol(name);
|
||||
export const pickerGroupProps = extend(
|
||||
{
|
||||
tabs: makeArrayProp<string>(),
|
||||
nextStepText: String,
|
||||
},
|
||||
pickerToolbarProps
|
||||
);
|
||||
@ -34,26 +40,47 @@ export default defineComponent({
|
||||
emits: ['confirm', 'cancel'],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const activeTab = ref(0);
|
||||
const { children, linkChildren } = useChildren(PICKER_GROUP_KEY);
|
||||
|
||||
linkChildren();
|
||||
|
||||
const showNextButton = () =>
|
||||
activeTab.value < props.tabs.length - 1 && props.nextStepText;
|
||||
|
||||
const onConfirm = () => {
|
||||
emit(
|
||||
'confirm',
|
||||
children.map((item) => item.confirm())
|
||||
);
|
||||
if (showNextButton()) {
|
||||
activeTab.value++;
|
||||
} else {
|
||||
emit(
|
||||
'confirm',
|
||||
children.map((item) => item.confirm())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => emit('cancel');
|
||||
|
||||
return () => {
|
||||
const childNodes = slots.default?.();
|
||||
const confirmButtonText = showNextButton()
|
||||
? props.nextStepText
|
||||
: props.confirmButtonText;
|
||||
|
||||
return (
|
||||
<div class={bem()}>
|
||||
<Toolbar {...props} onConfirm={onConfirm} onCancel={onCancel} />
|
||||
<Tabs shrink class={bem('tabs')} animated>
|
||||
<Toolbar
|
||||
{...props}
|
||||
confirmButtonText={confirmButtonText}
|
||||
onConfirm={onConfirm}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
<Tabs
|
||||
v-model:active={activeTab.value}
|
||||
class={bem('tabs')}
|
||||
shrink
|
||||
animated
|
||||
>
|
||||
{props.tabs.map((title, index) => (
|
||||
<Tab title={title} titleClass={bem('tab-title')}>
|
||||
{childNodes?.[index]}
|
||||
|
@ -75,6 +75,54 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
### Next Step Button
|
||||
|
||||
In some scenarios, in order to ensure that users can select all Pickers in turn, you can set the `next-step-text` prop of PickerGroup. After setting the `next-step-text` prop, if the user has not switched to the last tab, the button in the upper right corner will become "Next Step", and automatically switch to the next Picker after clicking. When the user switches to the last tab, the button in the upper right corner changes to "Confirm".
|
||||
|
||||
```html
|
||||
<van-picker-group
|
||||
title="Title"
|
||||
:tabs="['Date', 'Time']"
|
||||
next-step-text="Next Step"
|
||||
@confirm="onConfirm"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<van-date-picker
|
||||
v-model="currentDate"
|
||||
:min-date="minDate"
|
||||
:max-date="maxDate"
|
||||
/>
|
||||
<van-time-picker v-model="currentTime" />
|
||||
</van-picker-group>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentDate = ref(['2022', '06', '01']);
|
||||
const currentTime = ref(['12', '00']);
|
||||
const onConfirm = () => {
|
||||
showToast(
|
||||
`${currentDate.value.join('/')} ${currentTime.value.join(':')}`
|
||||
);
|
||||
};
|
||||
const onCancel = () => {
|
||||
showToast('cancel');
|
||||
};
|
||||
|
||||
return {
|
||||
minDate: new Date(2020, 0, 1),
|
||||
maxDate: new Date(2025, 5, 1),
|
||||
currentDate,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Select Date Range
|
||||
|
||||
Place two `DatePicker` components in the default slot of `PickerGroup` to select the time range.
|
||||
@ -165,12 +213,13 @@ export default {
|
||||
|
||||
### Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| ------------------- | ---------------------- | ---------- | --------- |
|
||||
| tabs | Titles of tabs | _string[]_ | `[]` |
|
||||
| title | Toolbar title | _string_ | `''` |
|
||||
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
|
||||
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
|
||||
| Attribute | Description | Type | Default |
|
||||
| ------------------- | ------------------------ | ---------- | --------- |
|
||||
| tabs | Titles of tabs | _string[]_ | `[]` |
|
||||
| title | Toolbar title | _string_ | `''` |
|
||||
| next-step-text | Text of next step button | _string_ | `''` |
|
||||
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
|
||||
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
|
||||
|
||||
### Slots
|
||||
|
||||
|
@ -75,6 +75,54 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
### 下一步按钮
|
||||
|
||||
部分场景下,为了保证用户能够依次选择所有的 Picker,你可以设置 PickerGroup 的 `next-step-text` 属性。在设置 `next-step-text` 属性后,如果用户未切换到最后一个标签页,那么右上角的按钮会变成「下一步」,点击后自动切换到下一个 Picker。当用户切换到最后一个标签页时,右上角的按钮会变为「确认」。
|
||||
|
||||
```html
|
||||
<van-picker-group
|
||||
title="预约日期"
|
||||
:tabs="['选择日期', '选择时间']"
|
||||
next-step-text="下一步"
|
||||
@confirm="onConfirm"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<van-date-picker
|
||||
v-model="currentDate"
|
||||
:min-date="minDate"
|
||||
:max-date="maxDate"
|
||||
/>
|
||||
<van-time-picker v-model="currentTime" />
|
||||
</van-picker-group>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentDate = ref(['2022', '06', '01']);
|
||||
const currentTime = ref(['12', '00']);
|
||||
const onConfirm = () => {
|
||||
showToast(
|
||||
`${currentDate.value.join('/')} ${currentTime.value.join(':')}`
|
||||
);
|
||||
};
|
||||
const onCancel = () => {
|
||||
showToast('cancel');
|
||||
};
|
||||
|
||||
return {
|
||||
minDate: new Date(2020, 0, 1),
|
||||
maxDate: new Date(2025, 5, 1),
|
||||
currentDate,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 选择日期范围
|
||||
|
||||
在 `PickerGroup` 的默认插槽中放置两个 `DatePicker` 组件,可以实现选择日期范围的交互效果。
|
||||
@ -169,8 +217,9 @@ export default {
|
||||
| ------------------- | ---------------- | ---------- | ------ |
|
||||
| tabs | 设置标签页的标题 | _string[]_ | `[]` |
|
||||
| title | 顶部栏标题 | _string_ | `''` |
|
||||
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
|
||||
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
|
||||
| next-step-text | 下一步按钮的文字 | _string_ | `''` |
|
||||
| confirm-button-text | 确认按钮的文字 | _string_ | `确认` |
|
||||
| cancel-button-text | 取消按钮的文字 | _string_ | `取消` |
|
||||
|
||||
### Slots
|
||||
|
||||
|
52
packages/vant/src/picker-group/demo/NextStepButton.vue
Normal file
52
packages/vant/src/picker-group/demo/NextStepButton.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useTranslate } from '../../../docs/site';
|
||||
import VanPickerGroup from '..';
|
||||
import VanTimePicker from '../../time-picker';
|
||||
import VanDatePicker from '../../date-picker';
|
||||
import { showToast } from '../../toast';
|
||||
|
||||
const t = useTranslate({
|
||||
'zh-CN': {
|
||||
date: '选择日期',
|
||||
time: '选择时间',
|
||||
title: '预约日期',
|
||||
nextStep: '下一步',
|
||||
},
|
||||
'en-US': {
|
||||
date: 'Date',
|
||||
time: 'Time',
|
||||
title: 'Title',
|
||||
nextStep: 'Next Step',
|
||||
},
|
||||
});
|
||||
|
||||
const currentTime = ref(['12', '00']);
|
||||
const currentDate = ref(['2022', '06', '01']);
|
||||
const minDate = new Date(2020, 0, 1);
|
||||
const maxDate = new Date(2025, 5, 1);
|
||||
|
||||
const onConfirm = () => {
|
||||
showToast(`${currentDate.value.join('/')} ${currentTime.value.join(':')}`);
|
||||
};
|
||||
const onCancel = () => {
|
||||
showToast('cancel');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-picker-group
|
||||
:title="t('title')"
|
||||
:tabs="[t('date'), t('time')]"
|
||||
:next-step-text="t('nextStep')"
|
||||
@confirm="onConfirm"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<van-date-picker
|
||||
v-model="currentDate"
|
||||
:min-date="minDate"
|
||||
:max-date="maxDate"
|
||||
/>
|
||||
<van-time-picker v-model="currentTime" />
|
||||
</van-picker-group>
|
||||
</template>
|
@ -2,6 +2,7 @@
|
||||
import SelectDateTime from './SelectDateTime.vue';
|
||||
import SelectTimeRange from './SelectTimeRange.vue';
|
||||
import SelectDateRange from './SelectDateRange.vue';
|
||||
import NextStepButton from './NextStepButton.vue';
|
||||
import { useTranslate } from '../../../docs/site';
|
||||
|
||||
const t = useTranslate({
|
||||
@ -9,11 +10,13 @@ const t = useTranslate({
|
||||
selectDateTime: '选择日期时间',
|
||||
selectDateRange: '选择日期范围',
|
||||
selectTimeRange: '选择时间范围',
|
||||
nextStepButton: '下一步按钮',
|
||||
},
|
||||
'en-US': {
|
||||
selectDateTime: 'Select Date Time',
|
||||
selectDateRange: 'Select Date Range',
|
||||
selectTimeRange: 'Select Time Range',
|
||||
nextStepButton: 'Next Step Button',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -23,6 +26,10 @@ const t = useTranslate({
|
||||
<select-date-time />
|
||||
</demo-block>
|
||||
|
||||
<demo-block card :title="t('nextStepButton')">
|
||||
<next-step-button />
|
||||
</demo-block>
|
||||
|
||||
<demo-block card :title="t('selectDateRange')">
|
||||
<select-date-range />
|
||||
</demo-block>
|
||||
|
File diff suppressed because it is too large
Load Diff
54
packages/vant/src/picker-group/test/index.spec.tsx
Normal file
54
packages/vant/src/picker-group/test/index.spec.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { ref } from 'vue';
|
||||
import { mount } from '../../../test';
|
||||
import { Picker, PickerConfirmEventParams } from '../../picker';
|
||||
import { PickerGroup } from '..';
|
||||
|
||||
test('should emit confirm event after clicking the confirm button', async () => {
|
||||
const value1 = ref(['1']);
|
||||
const value2 = ref(['2']);
|
||||
|
||||
const wrapper = mount({
|
||||
setup(props, { emit }) {
|
||||
const onConfirm = (params: PickerConfirmEventParams) => {
|
||||
emit('confirm', params);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<PickerGroup
|
||||
title="Title"
|
||||
tabs={['Tab1', 'Tab2']}
|
||||
nextStepText="Next Step"
|
||||
onConfirm={onConfirm}
|
||||
>
|
||||
<Picker
|
||||
v-model={value1.value}
|
||||
columns={[{ text: '1', value: '1' }]}
|
||||
/>
|
||||
<Picker
|
||||
v-model={value2.value}
|
||||
columns={[{ text: '2', value: '2' }]}
|
||||
/>
|
||||
</PickerGroup>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.find('.van-picker__confirm').trigger('click');
|
||||
expect(wrapper.emitted('confirm')).toBeFalsy();
|
||||
|
||||
await wrapper.find('.van-picker__confirm').trigger('click');
|
||||
expect(wrapper.emitted('confirm')![0]).toEqual([
|
||||
[
|
||||
{
|
||||
selectedIndexes: [0],
|
||||
selectedOptions: [{ text: '1', value: '1' }],
|
||||
selectedValues: ['1'],
|
||||
},
|
||||
{
|
||||
selectedIndexes: [0],
|
||||
selectedOptions: [{ text: '2', value: '2' }],
|
||||
selectedValues: ['2'],
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user