值:20:00
<script setup lang="ts">
import { ref } from 'vue'
import { Clock } from '@lucide/vue'
import { Stack, Text, TimeField } from '@hina-ui/vue'
const time = ref<string | null>('20:00')
</script>
<template>
<Stack gap="sm" align="start">
<TimeField v-model="time" clearable aria-label="开播时间" class="w-44">
<template #leading>
<Clock />
</template>
</TimeField>
<Text tone="muted" size="sm">值:{{ time ?? '空' }}</Text>
</Stack>
</template>
vue
用法
import { TimeField } from '@hina-ui/vue'
ts
时间输入框把时间拆成时、分两段,每段只接受数字,输入完一段后自动进入下一段,上下方向键逐段增减。v-model 绑定 HH:mm 格式的字符串,精度到秒时是 HH:mm:ss,与浏览器原生时间控件的格式相同。段的顺序、分隔符与占位文字随当前语言变化,12 小时制的语言会多出上午与下午一段。未声明的属性都会传给包裹各段的组元素,请用 aria-label 或者 aria-labelledby 命名。
值:空
<script setup lang="ts">
import { ref } from 'vue'
import { Stack, Text, TimeField } from '@hina-ui/vue'
const time = ref<string | null>(null)
</script>
<template>
<Stack gap="sm" align="start">
<TimeField v-model="time" aria-label="开播时间" class="w-40" />
<Text tone="muted" size="sm">值:{{ time ?? '空' }}</Text>
</Stack>
</template>
vue
示例
精度
granularity 决定精度:默认 minute 有时与分,second 再增加秒,hour 只有小时。hourCycle 指定 12 小时制或者 24 小时制,默认随语言决定。
<script setup lang="ts">
import { ref } from 'vue'
import { Stack, TimeField } from '@hina-ui/vue'
const hour = ref<string | null>('20:00')
const second = ref<string | null>('20:00:30')
const twelve = ref<string | null>('20:00')
</script>
<template>
<Stack class="w-56">
<TimeField v-model="hour" granularity="hour" aria-label="整点" />
<TimeField v-model="second" granularity="second" aria-label="到秒" />
<TimeField v-model="twelve" :hour-cycle="12" aria-label="12 小时制" />
</Stack>
</template>
vue
分钟步长
minuteStep 让分段按固定步长增减,键入的分钟也会吸附到最近的步长,适合按刻钟或者半小时安排的时段。
值:09:00
<script setup lang="ts">
import { ref } from 'vue'
import { Stack, Text, TimeField } from '@hina-ui/vue'
const time = ref<string | null>('09:00')
</script>
<template>
<Stack gap="sm" align="start">
<TimeField v-model="time" :minute-step="15" aria-label="预约时段" class="w-40" />
<Text tone="muted" size="sm">值:{{ time ?? '空' }}</Text>
</Stack>
</template>
vue
范围
min 与 max 限定可选范围,值超出范围时输入框显示为校验未通过,但值仍然会更新。placeholder 指定没有值时各段从哪个时间开始增减,默认是当前时间。
<script setup lang="ts">
import { ref } from 'vue'
import { TimeField } from '@hina-ui/vue'
const time = ref<string | null>('21:30')
</script>
<template>
<TimeField
v-model="time"
min="09:00"
max="18:00"
placeholder="09:00"
aria-label="营业时间"
class="w-40"
/>
</template>
vue
可清除
clearable 在末尾加一个清除按钮,清空后值为 null,焦点回到第一段。
<script setup lang="ts">
import { ref } from 'vue'
import { TimeField } from '@hina-ui/vue'
const time = ref<string | null>('20:00')
</script>
<template>
<TimeField v-model="time" clearable aria-label="开播时间" class="w-40" />
</template>
vue
尺寸
size 有 sm、md、lg 三档,与同档输入框等高。
<script setup lang="ts">
import { Stack, TimeField } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-40">
<TimeField size="sm" model-value="20:00" aria-label="小号" />
<TimeField size="md" model-value="20:00" aria-label="中号" />
<TimeField size="lg" model-value="20:00" aria-label="大号" />
</Stack>
</template>
vue
状态
invalid 给输入面加上警示色,disabled 禁用整组,readonly 只读,仍然可以聚焦与选中。variant="secondary" 是放在 surface 之内的扁平形态。
<script setup lang="ts">
import { Stack, TimeField } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-40">
<TimeField model-value="20:00" invalid aria-label="校验未通过" />
<TimeField model-value="20:00" disabled aria-label="已禁用" />
<TimeField model-value="20:00" readonly aria-label="只读" />
<TimeField model-value="20:00" variant="secondary" aria-label="扁平形态" />
</Stack>
</template>
vue
在表单中
放进 FormField 后,标签通过 aria-labelledby 关联到整个时间输入,错误信息由字段渲染;校验规则与提交交给 Form。值是 HH:mm 字符串,可以直接按字符串比较。
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Form, FormField, Text, TimeField } from '@hina-ui/vue'
const schema = v.object({
opensAt: v.pipe(
v.string('请填写开门时间'),
v.check(value => value >= '06:00', '开门不早于 06:00'),
),
})
const values = reactive({ opensAt: null as string | null })
const saved = ref('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = JSON.stringify(data)
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="opensAt" label="开门时间" required>
<TimeField v-model="values.opensAt" />
</FormField>
<Button type="submit" :loading="submitting" class="self-start">保存</Button>
<Text v-if="saved" tone="muted" size="sm">已保存:{{ saved }}</Text>
</Form>
</template>
vue
行为
- 点击任意一段即可输入;点击输入面的空白处时,焦点落到第一个空段。
- 每段只接受数字,输入完一段后自动进入下一段;左右方向键在段之间移动,上下方向键增减,退格清空当前段。
- 所有段都填满时才会更新值;清空任意一段后,值变为
null。 - 超出
min或者max的值仍然会更新,同时标为校验未通过。
无障碍
- 各段是
role="spinbutton",带本地化名称与当前值,分隔符对辅助技术隐藏。 - 通过
aria-label或者aria-labelledby为整组命名。 invalid与超出范围都会在组元素上设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | string | null | null | HH:mm 字符串,精度到秒时是 HH:mm:ss |
placeholder | string | 当前时间 | 空值时各段的起点, HH:mm 字符串 |
min | string | — | 可选范围的下限 |
max | string | — | 可选范围的上限 |
granularity | 'hour' | 'minute' | 'second' | 'minute' | 精度 |
hourCycle | 12 | 24 | 随语言 | 小时制 |
minuteStep | number | — | 分段的步长 |
clearable | boolean | false | 是否显示清除按钮 |
readonly | boolean | false | 是否只读 |
name | string | — | 表单字段名 |
variant | 'primary' | 'secondary' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
disabled | boolean | false | 是否禁用 |
invalid | boolean | false | 是否处于校验未通过状态 |
class | string | — | 追加至根元素的类名 |
插槽
| 插槽 | 说明 |
|---|---|
leading | 前置附属格的内容 |
trailing | 后置附属格的内容 |
事件
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | value: string | null | 值变化 |
clear | — | 点击清除 |