代码演示
如何引入
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
基础使用
点击 TimePicker,然后可以在浮层中选择或者输入某一时间。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<TimePicker />
</template>无限滚动
版本V2.22.0开始,我们将 TimePicker 内的 ScrollItem 的默认模式从 wheel 变更为了 normal, 若想应用回无限滚动的效果,可参考以下示例。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<TimePicker :scrollItemProps="{ mode: 'wheel', cycled: true }" />
</template>受控组件
使用 v-model 作为受控组件,初始值用 defaultValue。传 null 是受控的空值,undefined 视为非受控;受控值从有值变回 undefined 时输入框清空并转为非受控。清除后 v-model 收到的单选值为 undefined,范围值为空数组。
代码
<script setup lang="ts">
import { ref } from 'vue';
import { TimePicker } from '@fibrous/semi-ui-vue';
const value = ref<Date | (Date | undefined)[] | null | undefined>(null);
const onChange = (time: unknown) => console.log(time);
</script>
<template>
<TimePicker v-model="value" @change="onChange" />
</template>不同的 Format 格式
TimePicker 浮层中的列会随着 format 变化,当略去 format 中的某部分时,浮层中对应的列也会消失。
NOTE: format 遵循 date-fns v4 的 format 格式。 https://date-fns.org/docs/format
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<TimePicker defaultValue="10:24" format="HH:mm" />
</template>设置面板头部,底部
禁用时间选择
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<TimePicker defaultValue="12:08:23" disabled />
</template>设置步长
可以使用 hourStep, minuteStep, secondStep 按步长展示可选的时分秒。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<TimePicker :minuteStep="15" :secondStep="10" />
</template>12 小时制
12 小时制的时间选择器,默认的 format 为 a h:mm:ss,传入的 format 格式必须在 dateFns 日期格式范围之内。
例如默认的 12 小时制格式串为:
a h:mm:ss,如果传入A h:mm:ss则会导致无法正确格式化。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<div>
<TimePicker use12Hours />
<br />
<br />
<TimePicker format="a h:mm" use12Hours />
</div>
</template>时间范围
传入 type="timeRange" 开启时间范围选择。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
</script>
<template>
<div>
<TimePicker :defaultValue="['10:23:15', '12:38:32']" type="timeRange" />
<br />
<br />
<TimePicker :defaultValue="['上午 08:11', '下午 11:21']" format="a h:mm" type="timeRange" use12Hours />
</div>
</template>Range 模式下分别禁用左右面板(disabledTime)
当 type="timeRange" 时,你可以通过 disabledTime(value, panelType) 对左右面板分别应用不同的禁用规则。
value:当前已选时间数组,未选的一侧为undefined(如[date, undefined]);没有值时为空数组,value、defaultValue或输入框只含一个时间时长度为 1panelType:'left' | 'right',分别代表开始/结束面板
下面示例实现:选择开始时间后,右侧结束时间面板会禁用早于开始时间的选项。
代码
<script setup lang="ts">
import { TimePicker } from '@fibrous/semi-ui-vue';
const range = (length: number) => Array.from({ length }, (_, i) => i);
// The right panel disables everything earlier than the start time picked on the left
const disabledTime = (value: (Date | undefined)[], panelType: 'left' | 'right') => {
const start = value[0];
if (panelType === 'right' && start) {
const h = start.getHours();
const m = start.getMinutes();
return {
disabledHours: () => range(h),
disabledMinutes: (hour: number) => (hour === h ? range(m) : []),
};
}
return {};
};
</script>
<template>
<TimePicker :disabledTime="disabledTime" type="timeRange" />
</template>自定义触发器
默认情况下我们使用 Input 组件作为 TimePicker 组件的触发器,通过 #trigger 作用域插槽你可以自定义这个触发器。作用域参数为 { value, inputValue, placeholder, disabled, componentProps, onChange },其中 value 未选的一侧为 undefined,onChange 用于更新输入框文本,效果同键入。
代码
<script setup lang="ts">
import { ref } from 'vue';
import * as dateFns from 'date-fns';
import { IconTimePicker } from '@fibrous/semi-icons-vue/lab';
import { Tag, TimePicker } from '@fibrous/semi-ui-vue';
const formatToken = 'HH:mm:ss';
const time = ref<Date | (Date | undefined)[] | undefined>(new Date(2024, 0, 1, 10, 30, 0));
</script>
<template>
<TimePicker v-model="time" :format="formatToken">
<template #trigger="{ placeholder }">
<Tag color="cyan" shape="circle" size="large" :style="{ padding: '12px', paddingRight: '16px', fontSize: '14px' }" type="light">
<template #prefixIcon>
<IconTimePicker />
</template>
{{ time instanceof Date ? dateFns.format(time, formatToken) : placeholder }}
</Tag>
</template>
</TimePicker>
</template>时区设置
Semi 所有关于时区的配置都收敛在 ConfigProvider 中,详细使用可以参考 ConfigProvider
Select Time Zone:
TimePicker:
代码
<script setup lang="ts">
import { ref } from 'vue';
import { ConfigProvider, Select, TimePicker } from '@fibrous/semi-ui-vue';
const timeZone = ref('GMT+08:00');
const defaultTimestamp = 1581599305265;
const gmtList: string[] = [];
for (let hourOffset = -11; hourOffset <= 14; hourOffset++) {
const prefix = hourOffset >= 0 ? '+' : '-';
gmtList.push(`GMT${prefix}${String(Math.abs(hourOffset)).padStart(2, '0')}:00`);
}
const onChange = (date: unknown, dateString: string | string[]) => console.log('DatePicker changed: ', date, dateString);
</script>
<template>
<ConfigProvider :timeZone="timeZone">
<div :style="{ width: '300px' }">
<h5 :style="{ margin: '10px' }">Select Time Zone:</h5>
<Select aria-label="时区" :modelValue="timeZone" placeholder="请选择时区" showClear :style="{ width: '300px' }" @select="timeZone = String($event)">
<Select.Option v-for="gmt in gmtList" :key="gmt" :value="gmt">{{ gmt }}</Select.Option>
</Select>
<br />
<br />
<h5 :style="{ margin: '10px' }">TimePicker:</h5>
<TimePicker :defaultValue="defaultTimestamp" @change="onChange" />
</div>
</ConfigProvider>
</template>API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 当前时间,配合 v-model 使用;type 为 timeRange 时为数组。传 null 是受控的空值,undefined 视为非受控。 | TimePickerValue | (TimePickerValue | undefined)[] | null | undefined |
| defaultValue | 非受控时的初始时间,形态同 modelValue。 | TimePickerValue | (TimePickerValue | undefined)[] | semiGlobalDefault("TimePicker", "defaultValue") |
| open | 面板是否打开,配合 v-model:open 使用;undefined 视为非受控。 | boolean | undefined |
| defaultOpen | 非受控时面板是否默认打开。 | boolean | semiDefault("TimePicker", "defaultOpen", false) |
| type | 选择类型,time 为单个时间,timeRange 为时间范围。 | TimePickerType | semiDefault("TimePicker", "type", strings.DEFAULT_TYPE) |
| format | 展示的时间格式,遵循 date-fns 的 format;use12Hours 时默认为 a h:mm:ss,否则为 HH:mm:ss。 | string | semiGlobalDefault("TimePicker", "format") |
| use12Hours | 使用 12 小时制。 | boolean | semiDefault("TimePicker", "use12Hours", false) |
| hourStep | 小时选项的间隔,必须是正整数。 | number | semiGlobalDefault("TimePicker", "hourStep") |
| minuteStep | 分钟选项的间隔,必须是正整数。 | number | semiGlobalDefault("TimePicker", "minuteStep") |
| secondStep | 秒选项的间隔,必须是正整数。 | number | semiGlobalDefault("TimePicker", "secondStep") |
| disabled | 禁用全部操作。 | boolean | semiDefault("TimePicker", "disabled", false) |
| disabledHours | 返回禁止选择的小时。 | Type(() => number[]) | undefined |
| disabledMinutes | 按所选小时返回禁止选择的分钟。 | Type((selectedHour: number) => number[]) | undefined |
| disabledSeconds | 按所选小时和分钟返回禁止选择的秒。 | Type((selectedHour: number, selectedMinute: number) => number[]) | undefined |
| disabledTime | 仅范围模式生效:按当前值与面板位置返回左右面板各自的禁用规则。value 中未选的一侧为 undefined;返回的 disabledHours、disabledMinutes、disabledSeconds 覆盖对应的同名属性,未返回的回退到同名属性。 | Type((value: (Date | undefined)[], panelType: PanelType) => DisabledTimeFns) | undefined |
| hideDisabledOptions | 隐藏禁止选择的选项。 | boolean | semiDefault("TimePicker", "hideDisabledOptions", false) |
| placeholder | 没有值时显示的提示,默认按 type 取语言包文案。 | string | semiGlobalDefault("TimePicker", "placeholder") |
| rangeSeparator | 时间范围的分隔符。 | string | TypesemiDefault("TimePicker", "rangeSeparator", strings.DEFAULT_RANGE_SEPARATOR) |
| size | 输入框的大小。 | Size | semiDefault("TimePicker", "size", "default") |
| borderless | 无边框模式。 | boolean | semiDefault("TimePicker", "borderless", false) |
| showClear | 是否展示清除按钮。 | boolean | semiDefault("TimePicker", "showClear", true) |
| inputReadOnly | 输入框只读,避免在移动设备上打开虚拟键盘。 | boolean | semiDefault("TimePicker", "inputReadOnly", false) |
| validateStatus | 校验状态。 | "default" | "error" | "warning" | "success" | semiGlobalDefault("TimePicker", "validateStatus") |
| insetLabelId | insetLabel 的 id,用于 aria-labelledby 关联。 | string | undefined |
| inputStyle | 输入框根元素的样式。 | StyleValue | semiGlobalDefault("TimePicker", "inputStyle") |
| autofocus | 挂载时让输入框获得焦点,同时打开面板。 | boolean | semiDefault("TimePicker", "autofocus", false) |
| focusOnOpen | 挂载后让输入框获得焦点并选中内容,同时打开面板。 | boolean | semiDefault("TimePicker", "focusOnOpen", false) |
| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法。 | boolean | semiGlobalDefault("TimePicker", "preventScroll") |
| motion | 是否展示弹出层动画。 | boolean | semiDefault("TimePicker", "motion", true) |
| autoAdjustOverflow | 浮层被遮挡时是否自动调整方向。 | boolean | semiDefault("TimePicker", "autoAdjustOverflow", true) |
| position | 浮层位置,type 为 timeRange 时默认为 bottom,为 time 时默认为 bottomLeft。 | Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver" | semiGlobalDefault("TimePicker", "position") |
| popupClassName | 弹出层的类名。 | string | semiGlobalDefault("TimePicker", "popupClassName") |
| popupStyle | 弹出层的样式。 | StyleValue | semiGlobalDefault("TimePicker", "popupStyle") |
| stopPropagation | 是否阻止弹出层上的点击事件冒泡。 | boolean | semiDefault("TimePicker", "stopPropagation", true) |
| timeZone | 时区,未设置时依次取 ConfigProvider 的 timeZone、全局默认值。 | string | number | undefined |
| scrollItemProps | 传给各列的属性,只支持 cycled、mode、motion、transform,含义同 ScrollList 的 ScrollItem。 | TimePickerScrollItemProps | semiGlobalDefault("TimePicker", "scrollItemProps") |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 时间变化时触发,供 v-model 回写;清除后单选为 undefined,范围为空数组,范围中未选的一侧为 undefined。 | [value: Date | (Date | undefined)[] | undefined] |
| change | 时间变化时触发,参数为 (Date 值, 格式化字符串),范围模式下两者都是数组。 | Type[value: Date | (Date | undefined)[] | undefined, str: string | string[]] |
| update:open | 面板打开或关闭时触发,供 v-model:open 回写。 | [open: boolean] |
| openChange | 面板打开或关闭时触发。 | [open: boolean] |
| focus | 输入框根元素获得焦点时触发;焦点在输入框与清除按钮之间移动不触发。 | [event: FocusEvent] |
| blur | 点击外部关闭面板时触发,参数为该次 mousedown 的原生事件;离开输入框不触发。 | [event: MouseEvent] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| trigger | 自定义触发器,替代默认的输入框,点击其中任意位置打开面板。参数含 value(未选的一侧为 undefined)、inputValue、placeholder、disabled、componentProps 与 onChange(更新输入框文本,效果同键入)。 | TimePickerTriggerSlotProps |
| panelHeader | 面板头部内容,参数 index 为面板序号(单选为 0,范围为 0 或 1)。范围模式下未提供时显示语言包的「开始时间」「结束时间」。 | TimePickerPanelSlotProps |
| panelFooter | 面板底部内容,参数 index 为面板序号。 | TimePickerPanelSlotProps |
| panel | 仅范围模式:同时作为第 index 个面板的头部和底部,有内容时优先于 panelHeader 与 panelFooter。 | TimePickerPanelSlotProps |
| prefix | 输入框的前缀内容。 | any |
| insetLabel | 输入框的内嵌标签。 | any |
| clearIcon | 自定义清除图标,showClear 为 true 时有效。 | any |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| focus | 使输入框获得焦点,同时打开面板;自定义触发器时无效。 | Type() => void |
| blur | 移除输入框的焦点;自定义触发器时无效。 | Type() => void |
未声明的属性中,class、style 落在根元素上,其余(id、title、aria-*、data-*、事件监听等)落在内部输入框上;使用 #trigger 自定义触发器时,全部落在根元素上,传 aria-label 或 aria-labelledby 时根元素的 role 为 group。focus、blur 事件声明为组件事件,@focus、@blur 只收到组件事件,不再收到冒泡上来的原生事件。
通过 ref 调用 focus()、blur(),使用自定义触发器时无效。
Accessibility
ARIA
- 输入框的 role 为
combobox,带aria-haspopup="dialog"、随面板开关变化的aria-expanded,aria-controls指向面板 - 时、分、秒与上午/下午列是 role 为
listbox的列表,名称分别为「小时」「分钟」「秒」「上午或下午」
键盘和焦点
- 输入框获得焦点时打开面板;面板打开后按
下箭头,焦点移入第一列 - 每列是一个 Tab 停靠点,
上箭头、下箭头、Home、End或输入字符选择该列的值 - 按
Esc关闭面板;焦点在面板内时回到输入框
文案规范
- 时间选择器至少包括时和分,如:11:30,它在本地化过程中,可以适应为12小时制或者24小时制
- 当选择12小时制,需要和AM/PM一起搭配使用
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $color-timePicker_range_picker_panel_split-border | var(--semi-color-border) | 时间选择器菜单分割线颜色 |
| $color-timePicker_range_panel-border | rgba(0, 0, 0, 0.1) | 时间选择器描边颜色 |
| $width-timePicker_range_panel-border | 1px | 时间选择器菜单分割线宽度 |
| $height-timePicker_panel_body | 252px | 时间选择器菜单高度 |
| $width-timePicker_panel_list_ampm | 72px | 时间选择器菜单中列宽度 - 上午下午 |
| $width-timePicker_panel_list_hour | 64px | 时间选择器菜单中列宽度 - 小时 |
| $width-timePicker_panel_list_minute | 64px | 时间选择器菜单中列宽度 - 分钟 |
| $width-timePicker_panel_list_second | 64px | 时间选择器菜单中列宽度 - 秒 |
| $width-timePicker_range_panel_scrolllist_body-border | 2px | 时间范围选择器双排菜单中间分割线宽度 |
| $spacing-timePicker_range_panel_scrolllist_header_body-padding | 0 | 时间范围选择器菜单header与内容内边距 |
| $radius-timePicker_range_panel | var(--semi-border-radius-medium) | 时间范围选择器菜单圆角 |
| $radius-timePicker_input | var(--semi-border-radius-small) | 时间范围选择器圆角 |
| $shadow-timePicker_range_panel | var(--semi-shadow-elevated) | 时间范围选择器菜单阴影 |