mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
refactor: TimePicker component
This commit is contained in:
parent
15e7461a93
commit
42d1dd836c
@ -1,15 +1,19 @@
|
||||
import { extend } from '../utils';
|
||||
import { pickerSharedProps } from '../picker/Picker';
|
||||
import type { PropType } from 'vue';
|
||||
import type { PickerInstance } from '../picker';
|
||||
import type { PickerInstance, PickerOption } from '../picker';
|
||||
import type { DatetimePickerColumnType } from './types';
|
||||
|
||||
export const sharedProps = extend({}, pickerSharedProps, {
|
||||
filter: Function as PropType<(type: string, values: string[]) => string[]>,
|
||||
filter: Function as PropType<
|
||||
(columnType: string, options: PickerOption[]) => PickerOption[]
|
||||
>,
|
||||
columnsOrder: Array as PropType<DatetimePickerColumnType[]>,
|
||||
formatter: {
|
||||
type: Function as PropType<(type: string, value: string) => string>,
|
||||
default: (type: string, value: string) => value,
|
||||
type: Function as PropType<
|
||||
(type: string, option: PickerOption) => PickerOption
|
||||
>,
|
||||
default: (type: string, option: PickerOption) => option,
|
||||
},
|
||||
});
|
||||
|
||||
|
178
packages/vant/src/time-picker/README.md
Normal file
178
packages/vant/src/time-picker/README.md
Normal file
@ -0,0 +1,178 @@
|
||||
# TimePicker
|
||||
|
||||
### Intro
|
||||
|
||||
Used to select time, usually used with the [Popup](#/en-US/popup) component.
|
||||
|
||||
### Install
|
||||
|
||||
Register component globally via `app.use`, refer to [Component Registration](#/en-US/advanced-usage#zu-jian-zhu-ce) for more registration ways.
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import { TimePicker } from 'vant';
|
||||
|
||||
const app = createApp();
|
||||
app.use(TimePicker);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```html
|
||||
<van-time-picker v-model="currentTime" title="Choose Time" />
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
return { currentTime };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Time Range
|
||||
|
||||
```html
|
||||
<van-time-picker
|
||||
v-model="currentTime"
|
||||
title="Choose Time"
|
||||
:min-hour="10"
|
||||
:max-hour="20"
|
||||
:min-minute="30"
|
||||
:max-minute="40"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:35');
|
||||
return { currentTime };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Options Formatter
|
||||
|
||||
Using `formatter` prop to format option text.
|
||||
|
||||
```html
|
||||
<van-time-picker
|
||||
v-model="currentTime"
|
||||
title="Choose Time"
|
||||
:formatter="formatter"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
const formatter = (type, option) => {
|
||||
if (type === 'hour') {
|
||||
option.text += 'h';
|
||||
}
|
||||
if (type === 'minute') {
|
||||
option.text += 'm';
|
||||
}
|
||||
return option;
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Options Filter
|
||||
|
||||
Using `filter` prop to filter options.
|
||||
|
||||
```html
|
||||
<van-time-picker v-model="currentTime" title="Choose Time" :filter="filter" />
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
|
||||
const filter = (type, options) => {
|
||||
if (type === 'minute') {
|
||||
return options.filter((option) => Number(option) % 10 === 0);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| v-model | Current time | _string_ | - |
|
||||
| title | Toolbar title | _string_ | `''` |
|
||||
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
|
||||
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
|
||||
| show-toolbar | Whether to show toolbar | _boolean_ | `true` |
|
||||
| loading | Whether to show loading prompt | _boolean_ | `false` |
|
||||
| readonly | Whether to be readonly | _boolean_ | `false` |
|
||||
| filter | Option filter | _(type: string, options: PickerOption[]) => PickerOption[]_ | - |
|
||||
| formatter | Option text formatter | _(type: string, option: PickerOption) => PickerOption_ | - |
|
||||
| columns-order | Array for ordering columns, where item can be set to<br> `hour`、`minute` | _string[]_ | - |
|
||||
| option-height | Option height, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `44` |
|
||||
| visible-option-num | Count of visible columns | _number \| string_ | `6` |
|
||||
| swipe-duration | Duration of the momentum animation,unit `ms` | _number \| string_ | `1000` |
|
||||
| min-hour | Min hour for `time` type | _number \| string_ | `0` |
|
||||
| max-hour | Max hour for `time` type | _number \| string_ | `23` |
|
||||
| min-minute | Max minute for `time` type | _number \| string_ | `0` |
|
||||
| max-minute | Max minute for `time` type | _number \| string_ | `59` |
|
||||
|
||||
### Events
|
||||
|
||||
| Event | Description | Arguments |
|
||||
| --- | --- | --- |
|
||||
| confirm | Emitted when the confirm button is clicked | _{ selectedValues, selectedOptions }_ |
|
||||
| cancel | Emitted when the cancel button is clicked | _{ selectedValues, selectedOptions }_ |
|
||||
| change | Emitted when current option is changed | _{ selectedValues, selectedOptions, columnIndex }_ |
|
||||
|
||||
### Slots
|
||||
|
||||
| Name | Description | SlotProps |
|
||||
| -------------- | ---------------------------- | ---------------------- |
|
||||
| default | Custom toolbar content | - |
|
||||
| title | Custom title | - |
|
||||
| confirm | Custom confirm button text | - |
|
||||
| cancel | Custom cancel button text | - |
|
||||
| option | Custom option content | _option: PickerOption_ |
|
||||
| columns-top | Custom content above columns | - |
|
||||
| columns-bottom | Custom content below columns | - |
|
||||
|
||||
### Types
|
||||
|
||||
The component exports the following type definitions:
|
||||
|
||||
```ts
|
||||
import type { TimePickerProps } from 'vant';
|
||||
```
|
183
packages/vant/src/time-picker/README.zh-CN.md
Normal file
183
packages/vant/src/time-picker/README.zh-CN.md
Normal file
@ -0,0 +1,183 @@
|
||||
# TimePicker 时间选择
|
||||
|
||||
### 介绍
|
||||
|
||||
时间选择器,通常与[弹出层](#/zh-CN/popup)组件配合使用。
|
||||
|
||||
### 引入
|
||||
|
||||
通过以下方式来全局注册组件,更多注册方式请参考[组件注册](#/zh-CN/advanced-usage#zu-jian-zhu-ce)。
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import { TimePicker } from 'vant';
|
||||
|
||||
const app = createApp();
|
||||
app.use(TimePicker);
|
||||
```
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 基础用法
|
||||
|
||||
```html
|
||||
<van-time-picker v-model="currentTime" title="选择时间" />
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
return { currentTime };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 时间范围
|
||||
|
||||
```html
|
||||
<van-time-picker
|
||||
v-model="currentTime"
|
||||
title="选择时间"
|
||||
:min-hour="10"
|
||||
:max-hour="20"
|
||||
:min-minute="30"
|
||||
:max-minute="40"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:35');
|
||||
return { currentTime };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 格式化选项
|
||||
|
||||
通过传入 `formatter` 函数,可以对选项的文字进行格式化。
|
||||
|
||||
```html
|
||||
<van-time-picker
|
||||
v-model="currentTime"
|
||||
title="选择时间"
|
||||
:formatter="formatter"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
const formatter = (type, option) => {
|
||||
if (type === 'hour') {
|
||||
option.text += '时';
|
||||
}
|
||||
if (type === 'minute') {
|
||||
option.text += '分';
|
||||
}
|
||||
return option;
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 过滤选项
|
||||
|
||||
通过传入 `filter` 函数,可以对选项数组进行过滤,剔除不需要的时间,实现自定义时间间隔。
|
||||
|
||||
```html
|
||||
<van-time-picker v-model="currentTime" title="选择时间" :filter="filter" />
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const currentTime = ref('12:00');
|
||||
const filter = (type, options) => {
|
||||
if (type === 'minute') {
|
||||
return options.filter((option) => Number(option) % 10 === 0);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
return {
|
||||
filter,
|
||||
currentTime,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| v-model | 当前选中的时间 | _string_ | - |
|
||||
| title | 顶部栏标题 | _string_ | `''` |
|
||||
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
|
||||
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
|
||||
| show-toolbar | 是否显示顶部栏 | _boolean_ | `true` |
|
||||
| loading | 是否显示加载状态 | _boolean_ | `false` |
|
||||
| readonly | 是否为只读状态,只读状态下无法切换选项 | _boolean_ | `false` |
|
||||
| filter | 选项过滤函数 | _(type: string, options: PickerOption[]) => PickerOption[]_ | - |
|
||||
| formatter | 选项格式化函数 | _(type: string, option: PickerOption) => PickerOption_ | - |
|
||||
| columns-order | 自定义列排序数组, 子项可选值为<br> `hour`、`minute` | _string[]_ | - |
|
||||
| option-height | 选项高度,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `44` |
|
||||
| visible-option-num | 可见的选项个数 | _number \| string_ | `6` |
|
||||
| swipe-duration | 快速滑动时惯性滚动的时长,单位 `ms` | _number \| string_ | `1000` |
|
||||
| min-hour | 可选的最小小时 | _number \| string_ | `0` |
|
||||
| max-hour | 可选的最大小时 | _number \| string_ | `23` |
|
||||
| min-minute | 可选的最小分钟 | _number \| string_ | `0` |
|
||||
| max-minute | 可选的最大分钟 | _number \| string_ | `59` |
|
||||
|
||||
### Events
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| confirm | 点击完成按钮时触发 | _{ selectedValues, selectedOptions }_ |
|
||||
| cancel | 点击取消按钮时触发 | _{ selectedValues, selectedOptions }_ |
|
||||
| change | 选项改变时触发 | _{ selectedValues, selectedOptions, columnIndex }_ |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 说明 | 参数 |
|
||||
| -------------- | ---------------------- | ---------------------- |
|
||||
| default | 自定义整个顶部栏的内容 | - |
|
||||
| title | 自定义标题内容 | - |
|
||||
| confirm | 自定义确认按钮内容 | - |
|
||||
| cancel | 自定义取消按钮内容 | - |
|
||||
| option | 自定义选项内容 | _option: PickerOption_ |
|
||||
| columns-top | 自定义选项上方内容 | - |
|
||||
| columns-bottom | 自定义选项下方内容 | - |
|
||||
|
||||
### 类型定义
|
||||
|
||||
组件导出以下类型定义:
|
||||
|
||||
```ts
|
||||
import type { TimePickerProps } from 'vant';
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 在桌面端无法操作组件?
|
||||
|
||||
参见[桌面端适配](#/zh-CN/advanced-usage#zhuo-mian-duan-gua-pei)。
|
129
packages/vant/src/time-picker/TimePicker.tsx
Normal file
129
packages/vant/src/time-picker/TimePicker.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
computed,
|
||||
defineComponent,
|
||||
type ExtractPropTypes,
|
||||
} from 'vue';
|
||||
|
||||
// Utils
|
||||
import {
|
||||
pick,
|
||||
clamp,
|
||||
extend,
|
||||
padZero,
|
||||
createNamespace,
|
||||
makeNumericProp,
|
||||
} from '../utils';
|
||||
import {
|
||||
times,
|
||||
sharedProps,
|
||||
pickerInheritKeys,
|
||||
} from '../datetime-picker/utils';
|
||||
|
||||
// Components
|
||||
import { Picker, PickerOption } from '../picker';
|
||||
|
||||
const [name] = createNamespace('time-picker');
|
||||
|
||||
const timePickerProps = extend({}, sharedProps, {
|
||||
minHour: makeNumericProp(0),
|
||||
maxHour: makeNumericProp(23),
|
||||
minMinute: makeNumericProp(0),
|
||||
maxMinute: makeNumericProp(59),
|
||||
modelValue: String,
|
||||
});
|
||||
|
||||
export type TimePickerProps = ExtractPropTypes<typeof timePickerProps>;
|
||||
|
||||
export default defineComponent({
|
||||
name,
|
||||
|
||||
props: timePickerProps,
|
||||
|
||||
emits: ['confirm', 'cancel', 'change', 'update:modelValue'],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const formatValues = (value?: string) => {
|
||||
const { minHour, maxHour, maxMinute, minMinute } = props;
|
||||
|
||||
if (value) {
|
||||
const [hour, minute] = value.split(':');
|
||||
return [
|
||||
padZero(clamp(+hour, +minHour, +maxHour)),
|
||||
padZero(clamp(+minute, +minMinute, +maxMinute)),
|
||||
];
|
||||
}
|
||||
|
||||
return [padZero(minHour), padZero(minMinute)];
|
||||
};
|
||||
|
||||
const currentValues = ref<string[]>(formatValues(props.modelValue));
|
||||
|
||||
const ranges = computed(() => [
|
||||
{
|
||||
type: 'hour',
|
||||
range: [+props.minHour, +props.maxHour],
|
||||
},
|
||||
{
|
||||
type: 'minute',
|
||||
range: [+props.minMinute, +props.maxMinute],
|
||||
},
|
||||
]);
|
||||
|
||||
const columns = computed(() =>
|
||||
ranges.value.map(({ type, range }) => {
|
||||
const options = times(
|
||||
range[1] - range[0] + 1,
|
||||
(index): PickerOption => {
|
||||
const value = padZero(range[0] + index);
|
||||
return props.formatter(type, {
|
||||
text: value,
|
||||
value,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (props.filter) {
|
||||
return props.filter(type, options);
|
||||
}
|
||||
|
||||
return options;
|
||||
})
|
||||
);
|
||||
|
||||
watch(
|
||||
currentValues,
|
||||
(values) => {
|
||||
const newValue = values.join(':');
|
||||
if (newValue !== props.modelValue) {
|
||||
emit('update:modelValue', newValue);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
currentValues.value = formatValues(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
const onChange = (...args: unknown[]) => emit('change', ...args);
|
||||
const onCancel = (...args: unknown[]) => emit('cancel', ...args);
|
||||
const onConfirm = (...args: unknown[]) => emit('confirm', ...args);
|
||||
|
||||
return () => (
|
||||
<Picker
|
||||
v-model={currentValues.value}
|
||||
v-slots={slots}
|
||||
columns={columns.value}
|
||||
onChange={onChange}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
{...pick(props, pickerInheritKeys)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
80
packages/vant/src/time-picker/demo/index.vue
Normal file
80
packages/vant/src/time-picker/demo/index.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import VanTimePicker from '..';
|
||||
import { ref } from 'vue';
|
||||
import { useTranslate } from '../../../docs/site/use-translate';
|
||||
import type { PickerOption } from '../../picker';
|
||||
|
||||
const t = useTranslate({
|
||||
'zh-CN': {
|
||||
hour: '时',
|
||||
minute: '分',
|
||||
timeRange: '时间范围',
|
||||
chooseTime: '选择时间',
|
||||
optionsFilter: '过滤选项',
|
||||
optionsFormatter: '格式化选项',
|
||||
},
|
||||
'en-US': {
|
||||
hour: 'h',
|
||||
minute: 'm',
|
||||
timeRange: 'Time Range',
|
||||
chooseTime: 'Choose Time',
|
||||
optionsFilter: 'Options Filter',
|
||||
optionsFormatter: 'Options Formatter',
|
||||
},
|
||||
});
|
||||
|
||||
const baseTime = ref('12:00');
|
||||
const rangeTime = ref('12:35');
|
||||
const filterTime = ref('12:00');
|
||||
const formatterTime = ref('12:00');
|
||||
|
||||
const filter = (type: string, options: PickerOption[]) => {
|
||||
if (type === 'minute') {
|
||||
return options.filter((option) => Number(option.value) % 10 === 0);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
const formatter = (type: string, option: PickerOption) => {
|
||||
if (type === 'hour') {
|
||||
option.text += t('hour');
|
||||
}
|
||||
if (type === 'minute') {
|
||||
option.text += t('minute');
|
||||
}
|
||||
return option;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<demo-block card :title="t('basicUsage')">
|
||||
<van-time-picker v-model="baseTime" :title="t('chooseTime')" />
|
||||
</demo-block>
|
||||
|
||||
<demo-block card :title="t('timeRange')">
|
||||
<van-time-picker
|
||||
v-model="rangeTime"
|
||||
:title="t('chooseTime')"
|
||||
:min-hour="10"
|
||||
:max-hour="20"
|
||||
:min-minute="30"
|
||||
:max-minute="40"
|
||||
/>
|
||||
</demo-block>
|
||||
|
||||
<demo-block card :title="t('optionsFormatter')">
|
||||
<van-time-picker
|
||||
v-model="formatterTime"
|
||||
:title="t('chooseTime')"
|
||||
:formatter="formatter"
|
||||
/>
|
||||
</demo-block>
|
||||
|
||||
<demo-block card :title="t('optionsFilter')">
|
||||
<van-time-picker
|
||||
v-model="filterTime"
|
||||
:title="t('chooseTime')"
|
||||
:filter="filter"
|
||||
/>
|
||||
</demo-block>
|
||||
</template>
|
12
packages/vant/src/time-picker/index.ts
Normal file
12
packages/vant/src/time-picker/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { withInstall } from '../utils';
|
||||
import _TimePicker, { TimePickerProps } from './TimePicker';
|
||||
|
||||
export const TimePicker = withInstall(_TimePicker);
|
||||
export default TimePicker;
|
||||
export type { TimePickerProps };
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
VanTimePicker: typeof TimePicker;
|
||||
}
|
||||
}
|
@ -170,7 +170,7 @@ export default {
|
||||
},
|
||||
{
|
||||
path: 'datetime-picker',
|
||||
title: 'DatetimePicker 时间选择',
|
||||
title: 'DatetimePicker 日期选择',
|
||||
},
|
||||
{
|
||||
path: 'field',
|
||||
@ -216,6 +216,10 @@ export default {
|
||||
path: 'switch',
|
||||
title: 'Switch 开关',
|
||||
},
|
||||
{
|
||||
path: 'time-picker',
|
||||
title: 'timePicker 时间选择',
|
||||
},
|
||||
{
|
||||
path: 'uploader',
|
||||
title: 'Uploader 文件上传',
|
||||
@ -622,6 +626,10 @@ export default {
|
||||
path: 'switch',
|
||||
title: 'Switch',
|
||||
},
|
||||
{
|
||||
path: 'time-picker',
|
||||
title: 'timePicker',
|
||||
},
|
||||
{
|
||||
path: 'uploader',
|
||||
title: 'Uploader',
|
||||
|
Loading…
x
Reference in New Issue
Block a user