mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(TimePicker): add min-time and max-time prop (#11887)
* feat(TimePicker): Only default columns support minTime and maxTime * feat(TimePicker): adapted seconds, the specified format * feat(TimePicker): adapted seconds, the specified format * feat(TimePicker): adapted seconds, the specified format * fix(TimePicker): conflicts that must be resolved * fix(TimePicker): add props tag
This commit is contained in:
parent
41778cc9b3
commit
621c0fc2f1
@ -159,13 +159,39 @@ export default {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Limit Time Range
|
||||||
|
|
||||||
|
Using `min-time` and `max-time` props to limit the time range, Convention format `10:00:00`.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-time-picker
|
||||||
|
v-model="currentTime"
|
||||||
|
title="Choose Time"
|
||||||
|
:columns-type="['hour', 'minute', 'second']"
|
||||||
|
min-time="09:40:10"
|
||||||
|
max-time="20:20:50"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
````js
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const currentTime = ref(['12', '00', '00']);
|
||||||
|
return { currentTime };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Advanced Usage
|
### Advanced Usage
|
||||||
|
|
||||||
The third parameter of the `filter` function can get the currently selected time, which can be used to filter unwanted times more flexibly when using the uncontrolled mode.
|
The third parameter of the `filter` function can get the currently selected time, which can be used to filter unwanted times more flexibly when using the uncontrolled mode.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<van-time-picker title="Choose Time" :filter="filter" />
|
<van-time-picker title="Choose Time" :filter="filter" />
|
||||||
```
|
````
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export default {
|
export default {
|
||||||
@ -215,6 +241,8 @@ export default {
|
|||||||
| max-minute | Max minute | _number \| string_ | `59` |
|
| max-minute | Max minute | _number \| string_ | `59` |
|
||||||
| min-second | Min second | _number \| string_ | `0` |
|
| min-second | Min second | _number \| string_ | `0` |
|
||||||
| max-second | Max second | _number \| string_ | `59` |
|
| max-second | Max second | _number \| string_ | `59` |
|
||||||
|
| min-time `v4.4.2` | Min time, format reference `07:40:00`, `min-hour` `min-minute` `min-second` is invalid when used | _string_ | - |
|
||||||
|
| max-time `v4.4.2` | Max time, format reference `10:20:00`, `min-hour` `min-minute` `max-second` is invalid when used | _string_ | - |
|
||||||
| title | Toolbar title | _string_ | `''` |
|
| title | Toolbar title | _string_ | `''` |
|
||||||
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
|
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
|
||||||
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
|
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
|
||||||
|
@ -160,6 +160,31 @@ export default {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 限制时间范围
|
||||||
|
|
||||||
|
使用 `min-time` 和 `max-time` 来限制时间范围,约定格式 `10:00:00`。
|
||||||
|
|
||||||
|
```html
|
||||||
|
<van-time-picker
|
||||||
|
v-model="currentTime"
|
||||||
|
title="选择时间"
|
||||||
|
:columns-type="['hour', 'minute', 'second']"
|
||||||
|
min-time="09:40:10"
|
||||||
|
max-time="20:20:50"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const currentTime = ref(['12', '00', '00']);
|
||||||
|
return { currentTime };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
### 高级用法
|
### 高级用法
|
||||||
|
|
||||||
`filter` 函数的第三个参数能获取到当前选择的时间,这在使用非受控模式时,可以更灵活地过滤掉不需要的时间。
|
`filter` 函数的第三个参数能获取到当前选择的时间,这在使用非受控模式时,可以更灵活地过滤掉不需要的时间。
|
||||||
@ -216,6 +241,8 @@ export default {
|
|||||||
| max-minute | 可选的最大分钟 | _number \| string_ | `59` |
|
| max-minute | 可选的最大分钟 | _number \| string_ | `59` |
|
||||||
| min-second | 可选的最小秒数 | _number \| string_ | `0` |
|
| min-second | 可选的最小秒数 | _number \| string_ | `0` |
|
||||||
| max-second | 可选的最大秒数 | _number \| string_ | `59` |
|
| max-second | 可选的最大秒数 | _number \| string_ | `59` |
|
||||||
|
| min-time `v4.4.2` | 可选的最小时间,格式参考 `07:40:00`,使用时 `min-hour` `min-minute` `min-second` 无效 | _string_ | - |
|
||||||
|
| max-time `v4.4.2` | 可选的最大时间,格式参考 `10:20:00`,使用时 `max-hour` `max-minute` `max-second` 无效 | _string_ | - |
|
||||||
| title | 顶部栏标题 | _string_ | `''` |
|
| title | 顶部栏标题 | _string_ | `''` |
|
||||||
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
|
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
|
||||||
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
|
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import {
|
import {
|
||||||
ref,
|
|
||||||
watch,
|
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
type PropType,
|
ref,
|
||||||
|
watch,
|
||||||
type ExtractPropTypes,
|
type ExtractPropTypes,
|
||||||
|
type PropType,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {
|
import {
|
||||||
pick,
|
|
||||||
extend,
|
|
||||||
createNamespace,
|
|
||||||
makeNumericProp,
|
|
||||||
isSameValue,
|
|
||||||
} from '../utils';
|
|
||||||
import {
|
|
||||||
genOptions,
|
|
||||||
sharedProps,
|
|
||||||
pickerInheritKeys,
|
|
||||||
formatValueRange,
|
formatValueRange,
|
||||||
|
genOptions,
|
||||||
|
pickerInheritKeys,
|
||||||
|
sharedProps,
|
||||||
type TimeFilter,
|
type TimeFilter,
|
||||||
} from '../date-picker/utils';
|
} from '../date-picker/utils';
|
||||||
|
import {
|
||||||
|
createNamespace,
|
||||||
|
extend,
|
||||||
|
isSameValue,
|
||||||
|
makeNumericProp,
|
||||||
|
pick,
|
||||||
|
} from '../utils';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Picker } from '../picker';
|
import { Picker } from '../picker';
|
||||||
@ -30,6 +30,10 @@ const [name] = createNamespace('time-picker');
|
|||||||
|
|
||||||
export type TimePickerColumnType = 'hour' | 'minute' | 'second';
|
export type TimePickerColumnType = 'hour' | 'minute' | 'second';
|
||||||
|
|
||||||
|
const validateTime = (val: string) =>
|
||||||
|
/^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/.test(val);
|
||||||
|
const fullColumns: TimePickerColumnType[] = ['hour', 'minute', 'second'];
|
||||||
|
|
||||||
export const timePickerProps = extend({}, sharedProps, {
|
export const timePickerProps = extend({}, sharedProps, {
|
||||||
minHour: makeNumericProp(0),
|
minHour: makeNumericProp(0),
|
||||||
maxHour: makeNumericProp(23),
|
maxHour: makeNumericProp(23),
|
||||||
@ -37,6 +41,14 @@ export const timePickerProps = extend({}, sharedProps, {
|
|||||||
maxMinute: makeNumericProp(59),
|
maxMinute: makeNumericProp(59),
|
||||||
minSecond: makeNumericProp(0),
|
minSecond: makeNumericProp(0),
|
||||||
maxSecond: makeNumericProp(59),
|
maxSecond: makeNumericProp(59),
|
||||||
|
minTime: {
|
||||||
|
type: String,
|
||||||
|
validator: validateTime,
|
||||||
|
},
|
||||||
|
maxTime: {
|
||||||
|
type: String,
|
||||||
|
validator: validateTime,
|
||||||
|
},
|
||||||
columnsType: {
|
columnsType: {
|
||||||
type: Array as PropType<TimePickerColumnType[]>,
|
type: Array as PropType<TimePickerColumnType[]>,
|
||||||
default: () => ['hour', 'minute'],
|
default: () => ['hour', 'minute'],
|
||||||
@ -56,14 +68,48 @@ export default defineComponent({
|
|||||||
setup(props, { emit, slots }) {
|
setup(props, { emit, slots }) {
|
||||||
const currentValues = ref<string[]>(props.modelValue);
|
const currentValues = ref<string[]>(props.modelValue);
|
||||||
|
|
||||||
const columns = computed(() =>
|
const getValidTime = (time: string) => {
|
||||||
props.columnsType.map((type) => {
|
const timeLimitArr = time.split(':');
|
||||||
|
return fullColumns.map((col, i) =>
|
||||||
|
props.columnsType.includes(col) ? timeLimitArr[i] : '00'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = computed(() => {
|
||||||
|
let { minHour, maxHour, minMinute, maxMinute, minSecond, maxSecond } =
|
||||||
|
props;
|
||||||
|
|
||||||
|
if (props.minTime || props.maxTime) {
|
||||||
|
const fullTime: Record<TimePickerColumnType, string | number> = {
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
};
|
||||||
|
props.columnsType.forEach((col, i) => {
|
||||||
|
fullTime[col] = currentValues.value[i] ?? 0;
|
||||||
|
});
|
||||||
|
const { hour, minute } = fullTime;
|
||||||
|
if (props.minTime) {
|
||||||
|
const [minH, minM, minS] = getValidTime(props.minTime);
|
||||||
|
minHour = minH;
|
||||||
|
minMinute = +hour <= +minHour ? minM : '00';
|
||||||
|
minSecond = +hour <= +minHour && +minute <= +minMinute ? minS : '00';
|
||||||
|
}
|
||||||
|
if (props.maxTime) {
|
||||||
|
const [maxH, maxM, maxS] = getValidTime(props.maxTime);
|
||||||
|
maxHour = maxH;
|
||||||
|
maxMinute = +hour >= +maxHour ? maxM : '59';
|
||||||
|
maxSecond = +hour >= +maxHour && +minute >= +maxMinute ? maxS : '59';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.columnsType.map((type) => {
|
||||||
const { filter, formatter } = props;
|
const { filter, formatter } = props;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'hour':
|
case 'hour':
|
||||||
return genOptions(
|
return genOptions(
|
||||||
+props.minHour,
|
+minHour,
|
||||||
+props.maxHour,
|
+maxHour,
|
||||||
type,
|
type,
|
||||||
formatter,
|
formatter,
|
||||||
filter,
|
filter,
|
||||||
@ -71,8 +117,8 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
case 'minute':
|
case 'minute':
|
||||||
return genOptions(
|
return genOptions(
|
||||||
+props.minMinute,
|
+minMinute,
|
||||||
+props.maxMinute,
|
+maxMinute,
|
||||||
type,
|
type,
|
||||||
formatter,
|
formatter,
|
||||||
filter,
|
filter,
|
||||||
@ -80,8 +126,8 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
case 'second':
|
case 'second':
|
||||||
return genOptions(
|
return genOptions(
|
||||||
+props.minSecond,
|
+minSecond,
|
||||||
+props.maxSecond,
|
+maxSecond,
|
||||||
type,
|
type,
|
||||||
formatter,
|
formatter,
|
||||||
filter,
|
filter,
|
||||||
@ -95,8 +141,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
watch(currentValues, (newValues) => {
|
watch(currentValues, (newValues) => {
|
||||||
if (!isSameValue(newValues, props.modelValue)) {
|
if (!isSameValue(newValues, props.modelValue)) {
|
||||||
|
@ -14,6 +14,7 @@ const t = useTranslate({
|
|||||||
columnsType: '选项类型',
|
columnsType: '选项类型',
|
||||||
optionsFilter: '过滤选项',
|
optionsFilter: '过滤选项',
|
||||||
optionsFormatter: '格式化选项',
|
optionsFormatter: '格式化选项',
|
||||||
|
timeUniteRange: '时分时间范围',
|
||||||
},
|
},
|
||||||
'en-US': {
|
'en-US': {
|
||||||
hour: 'h',
|
hour: 'h',
|
||||||
@ -23,6 +24,7 @@ const t = useTranslate({
|
|||||||
columnsType: 'Columns Type',
|
columnsType: 'Columns Type',
|
||||||
optionsFilter: 'Options Filter',
|
optionsFilter: 'Options Filter',
|
||||||
optionsFormatter: 'Options Formatter',
|
optionsFormatter: 'Options Formatter',
|
||||||
|
timeUniteRange: 'Hour Minute Range',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ const secondTime = ref(['12', '00', '00']);
|
|||||||
const rangeTime = ref(['12', '35']);
|
const rangeTime = ref(['12', '35']);
|
||||||
const filterTime = ref(['12', ' 00']);
|
const filterTime = ref(['12', ' 00']);
|
||||||
const formatterTime = ref(['12', '00']);
|
const formatterTime = ref(['12', '00']);
|
||||||
|
const hourMinuteTime = ref(['12', '00', '00']);
|
||||||
|
|
||||||
const columnsType: TimePickerColumnType[] = ['hour', 'minute', 'second'];
|
const columnsType: TimePickerColumnType[] = ['hour', 'minute', 'second'];
|
||||||
|
|
||||||
@ -120,6 +123,16 @@ const formatter = (type: string, option: PickerOption) => {
|
|||||||
/>
|
/>
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block card :title="t('timeUniteRange')">
|
||||||
|
<van-time-picker
|
||||||
|
v-model="hourMinuteTime"
|
||||||
|
:title="t('chooseTime')"
|
||||||
|
:columns-type="['hour', 'minute', 'second']"
|
||||||
|
min-time="09:40:10"
|
||||||
|
max-time="20:20:50"
|
||||||
|
/>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
<demo-block card :title="t('advancedUsage')">
|
<demo-block card :title="t('advancedUsage')">
|
||||||
<van-time-picker :title="t('chooseTime')" :filter="timeFilter" />
|
<van-time-picker :title="t('chooseTime')" :filter="timeFilter" />
|
||||||
</demo-block>
|
</demo-block>
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -269,3 +269,51 @@ test('should emit confirm event correctly after setting smaller max-hour and max
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should time range when set props min-time', async () => {
|
||||||
|
const wrapper = mount(TimePicker, {
|
||||||
|
props: {
|
||||||
|
minTime: '09:40:10',
|
||||||
|
maxTime: '20:20:50',
|
||||||
|
modelValue: ['08', '30', '00'],
|
||||||
|
columnsType: ['hour', 'minute', 'second'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.find('.van-picker__confirm').trigger('click');
|
||||||
|
expect(wrapper.emitted('confirm')?.[0]).toEqual([
|
||||||
|
{
|
||||||
|
selectedOptions: [
|
||||||
|
{ text: '09', value: '09' },
|
||||||
|
{ text: '40', value: '40' },
|
||||||
|
{ text: '10', value: '10' },
|
||||||
|
],
|
||||||
|
selectedValues: ['09', '40', '10'],
|
||||||
|
selectedIndexes: [0, 0, 0],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should time range when set props max-time', async () => {
|
||||||
|
const wrapper = mount(TimePicker, {
|
||||||
|
props: {
|
||||||
|
minTime: '09:40:10',
|
||||||
|
maxTime: '20:20:50',
|
||||||
|
modelValue: ['23', '30', '55'],
|
||||||
|
columnsType: ['hour', 'minute', 'second'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.find('.van-picker__confirm').trigger('click');
|
||||||
|
expect(wrapper.emitted('confirm')?.[0]).toEqual([
|
||||||
|
{
|
||||||
|
selectedOptions: [
|
||||||
|
{ text: '20', value: '20' },
|
||||||
|
{ text: '20', value: '20' },
|
||||||
|
{ text: '50', value: '50' },
|
||||||
|
],
|
||||||
|
selectedValues: ['20', '20', '50'],
|
||||||
|
selectedIndexes: [11, 20, 50],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user