TimeRangeField
概述
TimeRangeField 是一个分段式时间范围输入组件,提供独立的开始和结束时间编辑分段(小时、分钟,以及可选的秒和上午/下午分段),同时保留两侧原生表单提交值。当用户需要快速、精确地输入开始和结束时间时使用——排期、预订、班次或时间窗口筛选。单个时间使用 STimeField;日期时间范围则使用带时间粒度的 SDateRangeField 或 SDateRangePicker。
用法
特性
- ⏱ 双组分段编辑 — 独立的开始/结束小时、分钟及可选秒和上午/下午分段,每段均为可编辑的
role="spinbutton" - 🎚 受控 / 非受控 — 对整个
{ start, end }范围支持v-model/defaultValue,底层由useControllableState驱动 - 🕛 12/24 小时制 —
hourCycle按语言环境在AM/PM与 24 小时制之间切换,两侧均可编辑上午/下午分段 - ⌨️ 完整键盘支持 — 数字键入自动进位、
ArrowUp/ArrowDown增减、ArrowLeft/ArrowRight跨组导航且在 RTL 下方向反转 - ⛓ 范围校验 — 自动强制
start ≤ end,配合minValue/maxValue/isTimeUnavailable在根节点标记data-invalid - 📝 两个原生表单值 — 视觉隐藏的
input[type="time"]以startName/endName提交两侧值 - ⚙️ 粒度与步长 —
granularity(hour/minute/second)与step控制渲染的分段与每次按键的增量 - 🧩 可组合插槽 —
leading/trailing在两组周围放置内容,separator自定义中间分隔符 - 🚫 禁用 / 只读 — 两种状态均阻止所有分段编辑并渲染对应的
data-*/aria-*属性
组件家族
STimeRangeField— 样式包装层,将 props 转发给 Aria compact 并注入dateRangeFieldVariants(与SDateRangeField共享)类TimeRangeFieldCompact(Aria)—TimeRangeFieldRoot+ 两组各段一个TimeRangeFieldInput的数据驱动组合,含leading/separator/trailing插槽;无样式用法从@vean/aria/time-range-field导入TimeRangeFieldRoot/TimeRangeFieldInput(Aria)— 状态所有者(分段值、范围校验、两个隐藏输入、跨组焦点管理)与绑定共享useDateField组合式的单个可编辑分段
演示
基础
自定义样式
API
Aria 层
4TimeRangeField
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 应用于根元素的额外类名。 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<TimeRangeFieldUi> | — | 该组件的按槽类重写。 |
inputProps | TimeRangeFieldInputProps | — | 转发给输入元素的属性。 |
separator | string | — | 分隔符。 |
defaultValue | TimeRange | — | 默认值。 |
modelValue | TimeRange | — | 当前模型值。 |
startName | string | — | 名称开头。 |
endName | string | — | 结束名称。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
dir | Direction | — | 组件的阅读方向。 |
id | string | — | 同上。 |
placeholder | Date | — | 占位符。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
readonly | boolean | — | 该组件是否为只读。 |
step | DateStep | — | 步骤。 |
locale | string | — | 语言环境。 |
defaultPlaceholder | Date | — | 默认占位符。 |
maxValue | Date | — | 最大值。 |
minValue | Date | — | 最小值。 |
granularity | TimeGranularity | — | 翻译粒度。 |
hourCycle | HourCycle | — | 小时周期。 |
hideTimeZone | boolean | — | 是否隐藏时区。 |
isTimeUnavailable | TimeMatcher | — | 时间是否不可用。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | TimeRange | 当模型值发生变化时触发。 |
update:placeholder | Date | 当占位符值发生变化时触发。 |
update:startValue | Date | null | 当起始值发生变化时触发。 |
update:endValue | Date | null | 当结束值发生变化时触发。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
leading | — | 为首行位置提供自定义内容。 |
trailing | — | 尾部槽位的自定义内容。 |
separator | — | 分隔符槽位的自定义内容。 |
TimeRangeFieldCompact
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
inputProps | TimeRangeFieldInputProps | — | 转发给输入元素的属性。 |
separator | string | — | 分隔符。 |
defaultValue | TimeRange | — | 默认值。 |
modelValue | TimeRange | — | 当前模型值。 |
startName | string | — | 名称开头。 |
endName | string | — | 结束名称。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
dir | Direction | — | 组件的阅读方向。 |
id | string | — | 同上。 |
placeholder | Date | — | 占位符。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
readonly | boolean | — | 该组件是否为只读。 |
step | DateStep | — | 步骤。 |
locale | string | — | 语言环境。 |
defaultPlaceholder | Date | — | 默认占位符。 |
maxValue | Date | — | 最大值。 |
minValue | Date | — | 最小值。 |
granularity | TimeGranularity | — | 翻译粒度。 |
hourCycle | HourCycle | — | 小时周期。 |
hideTimeZone | boolean | — | 是否隐藏时区。 |
isTimeUnavailable | TimeMatcher | — | 时间是否不可用。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | TimeRange | 当模型值发生变化时触发。 |
update:placeholder | Date | 当占位符值发生变化时触发。 |
update:startValue | Date | null | 当起始值发生变化时触发。 |
update:endValue | Date | null | 当结束值发生变化时触发。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
leading | — | 为首行位置提供自定义内容。 |
trailing | — | 尾部槽位的自定义内容。 |
separator | — | 分隔符槽位的自定义内容。 |
TimeRangeFieldInput
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | 'start' | 'end' | — | 类型。 |
part | SegmentPart | — | 部分。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
TimeRangeFieldRoot
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
defaultValue | TimeRange | — | 默认值。 |
modelValue | TimeRange | — | 当前模型值。 |
startName | string | — | 名称开头。 |
endName | string | — | 结束名称。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
dir | Direction | — | 组件的阅读方向。 |
id | string | — | 同上。 |
placeholder | Date | — | 占位符。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
readonly | boolean | — | 该组件是否为只读。 |
step | DateStep | — | 步骤。 |
locale | string | — | 语言环境。 |
defaultPlaceholder | Date | — | 默认占位符。 |
maxValue | Date | — | 最大值。 |
minValue | Date | — | 最小值。 |
granularity | TimeGranularity | — | 翻译粒度。 |
hourCycle | HourCycle | — | 小时周期。 |
hideTimeZone | boolean | — | 是否隐藏时区。 |
isTimeUnavailable | TimeMatcher | — | 时间是否不可用。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | TimeRange | 当模型值发生变化时触发。 |
update:placeholder | Date | 当占位符值发生变化时触发。 |
update:startValue | Date | null | 当起始值发生变化时触发。 |
update:endValue | Date | null | 当结束值发生变化时触发。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
default | TimeRangeFieldSlotProps | 默认插槽的自定义内容。 |
注意事项
架构与对标差异
TimeRangeFieldRoot 通过 useControllableState 持有 TimeRange,以两个 shallowRef 维护 startSegmentValues/endSegmentValues,并通过 isInvalid 完成范围校验(end 早于 start、minValue/maxValue、isTimeUnavailable)。每个 TimeRangeFieldInput 绑定 date-field 家族共享的 useDateField 组合式;TimeRangeFieldCompact 遍历 createContent 生成的分段,在两个 data-time-range-field-part 分组内各渲染一个输入,分组之间以 separator 插槽分隔。跨组焦点移动由 root 的 moveFocus 处理——依据 dir 映射物理方向键,RTL 下 ArrowLeft/ArrowRight 互换,分组边界按物理方向(delta)而非按键语义跨越。对标库多为「两个独立文本框」(或纯选择面板);双分段组 + 跨边界键盘导航与 Aria/styled 分离是差异点。
| 能力 | VeanUI | Ant Design | Element Plus | Mantine | Naive UI | shadcn |
|---|---|---|---|---|---|---|
| Aria/styled 分离 | ✅ | — | — | — | — | — |
| 双组分段可编辑组 | ✅ | — | — | — | — | — |
| 跨组焦点移动 | ✅ | — | — | — | — | — |
| RTL 方向反转 | ✅ | — | — | — | — | — |
| 受控 / 非受控 | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| 键盘增减 / 键入 | ✅ | — | — | — | — | — |
| 范围校验(start ≤ end) | ✅ | ✅ | ✅ | ✅ | ✅ | — |
isTimeUnavailable | ✅ | — | — | — | — | — |
| 粒度(秒) | ✅ | — | — | — | — | — |
| 双原生表单值 | ✅ | ✅ | ✅ | ✅ | — | — |
| 分隔符 prop / 插槽 | ✅ | — | — | — | — | — |
| 禁用 / 只读 | ✅ | ✅ | ✅ | ✅ | ✅ | — |
运行时注意事项
- 值是
TimeRange——由TimeValue(时间部分有意义的原生Date)组成的{ start, end }对象,而非string。请与其他日期家族组件搭配使用。 defaultValue仅在挂载时读取——外部控制请使用v-model。- 某一侧为空时,其分段显示占位时间(由
defaultValue/defaultPlaceholder或当前时间派生)——提交的表单值在用户键入前仍为空。 granularity默认'minute';传入'second'可在两侧渲染秒分段。hourCycle(12/24)控制上午/下午分段与可访问的小时范围;不传则跟随语言环境。- 范围校验会标记根节点
data-invalid但不会钳制值——请在父级清空或纠正。 - 两个隐藏输入共用
required;如需按侧提交命名请传startName/endName(未传时 start 侧回退使用name)。
常见问题
如何用键盘编辑范围?
聚焦任一组后直接键入数字——分段填满后自动进位。ArrowUp/ArrowDown 增减当前分段,ArrowLeft/ArrowRight 在分段间移动——包括跨越开始/结束边界——RTL 下方向反转。12 小时制下 a/p 切换 AM/PM。
如何在两侧显示秒分段?
传入 granularity: 'second' 即可在开始和结束组渲染小时、分钟与秒分段。
如何自定义开始与结束之间的分隔符?
通过 separator: '→' prop 使用简单文本分隔符,或使用 separator 插槽渲染任意内容(例如图标)。
如何强制 end 晚于 start?
当 end 早于 start 时组件会自动标记根节点 data-invalid。可配合 minValue/maxValue/isTimeUnavailable 施加任意约束——校验不会钳制值,请在父级纠正。
STimeRangeField 与 SDateRangeField 有何区别?
STimeRangeField 是纯时间范围输入——绝不渲染日期分段。SDateRangeField 渲染完整日期(可通过粒度带时间)并接受 DateValue 对。