- UI组件
- 前端
- 设计系统
【免费下载链接】arco-design
A comprehensive React UI components library based on Arco Design
Arco Design(arco-design)的TimePicker是一个"在弹出面板上选择时间,以便捷完成时间输入"的组件,覆盖单值时间选择与RangePicker时间段选择两类场景。本文以官方英文文档TimePicker的 API 说明为主体,逐条解析其通用属性、单值与范围属性的语义、默认值与版本演进,并结合组件源码(components/TimePicker目录下的picker.tsx、time-picker.tsx、range-picker.tsx、time-column.tsx、util.ts)与测试用例,说明列生成、滚动吸附、确认流程、时区转换等底层实现机制,帮助你既会用、又懂其原理。
一、组件构成与基本用法
1.1 导出结构
从源码 index.tsx 看,组件通过一个wrapper高阶函数把内部面板(TimePickerPopup/RangePickerPopup)包裹进通用的Picker壳:
const TimePicker = wrapper<TimePickerProps>(<TimePickerPopup />, 'TimePicker'); const RangePicker = wrapper<RangePickerProps>( <RangePickerPopup />, 'TimePickerRangePicker', 'range' ); Object.assign(TimePicker, { RangePicker });因此对外只导出一个TimePicker,RangePicker作为静态属性挂载,即TimePicker.RangePicker。类型定义中TimePickerDecorator接口(见 interface.tsx)明确了这一结构:TimePickerProps = BaseTimePickerProps & PickerProps,RangePickerProps = BaseRangePickerProps & PickerProps——即单值与范围选择器共享同一套PickerProps通用属性,这正是文档将 API 分为 "Picker / TimePicker / TimePicker.RangePicker" 三张表的原因。
1.2 基础用法
来自示例 basic.md:
import { TimePicker } from '@arco-design/web-react'; const App = () => { return <TimePicker style={{ width: 194 }} />; }; export default App;默认格式为HH:mm:ss(24 小时制),面板包含时/分/秒三列,底部有"此刻"与"确定"两个按钮。这一点可由单元测试 index.test.tsx 印证:基础渲染断言了三列.arco-timepicker-list与两个底部按钮,且三列单元格数分别为 24、60、60。
二、API 全量参考(以官方文档为准)
2.1 Picker:TimePicker 与 RangePicker 的通用属性
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
allowClear | 是否显示清除按钮 | boolean | true | - |
disableConfirm | 禁用确认步骤,开启后直接点选时间无需点击确认按钮 | boolean | - | 2.12.0 |
disabled | 是否禁用 | boolean | - | - |
editable | 输入框是否可手动输入 | boolean | true | - |
error | 是否错误状态(已废弃,下个大版本移除,请用status='error'替代) | boolean | - | - |
hideDisabledOptions | 隐藏禁止选择的选项 | boolean | - | - |
popupVisible | 受控弹出框的显示/隐藏 | boolean | - | - |
scrollSticky | 时间列滚动时自动吸附并选中 | boolean | true | 2.23.0 |
unmountOnExit | 关闭后是否销毁弹层 DOM | boolean | - | - |
use12Hours | 12 小时制显示,默认格式h:mm:ss a | boolean | - | - |
utcOffset | 时区偏移;如需 UTC 时间则设为0 | number | - | - |
format | 时间展示格式(dayjs 占位符) | string | HH:mm:ss | - |
timezone | 时区名称;若设置了utcOffset,以utcOffset为准 | string | - | - |
position | 弹层位置 | 'top' \| 'tl' \| 'tr' \| 'bottom' \| 'bl' \| 'br' | bl | - |
size | 输入框尺寸 | 'mini' \| 'small' \| 'default' \| 'large' | - | - |
status | 状态 | 'error' \| 'warning' | - | 2.45.0 |
extra | 底部附加内容 | ReactNode | - | - |
prefix | 前缀 | ReactNode | - | 2.43.0 |
triggerElement | 自定义触发元素 | ReactNode | - | 2.38.0 |
className | 附加 CSS 类名 | string \| string[] | - | - |
icons | 图标配置 | { inputSuffix?: ReactNode } | - | - |
placeholder | 输入框提示文案 | string \| string[] | - | - |
step | 时/分/秒的选择间隔 | { hour?: number; minute?: number; second?: number } | - | - |
style | 附加样式 | CSSProperties | - | - |
triggerProps | 透传给Trigger组件的参数 | Partial<TriggerProps> | - | - |
disabledHours | 禁用的部分小时选项 | () => number[] | - | - |
disabledMinutes | 禁用的部分分钟选项 | (selectedHour) => number[] | - | - |
disabledSeconds | 禁用的部分秒数选项 | (selectedHour, selectedMinute) => number[] | - | - |
getPopupContainer | 弹层挂载的父节点 | (node: HTMLElement) => Element | - | - |
onClear | 点击清除按钮的回调 | () => void | - | - |
triggerProps最终透传给 Trigger 组件,可控制弹层的更多行为(如autoAlignPopupWidth,见后文时区示例)。
默认值在源码中的落点:picker.tsx 中定义了defaultProps,并通过useMergeProps与componentConfig?.TimePicker(即ConfigProvider的componentConfig)三层合并:
const defaultProps: InnerPickerProps = { allowClear: true, position: 'bl', format: 'HH:mm:ss', editable: true, order: true, scrollSticky: true, };这意味着默认值不仅写死在组件里,还可被ConfigProvider的全局componentConfig统一覆盖。另外,在 RTL(从右到左)布局下源码会把默认position从'bl'调整为'br',适配右起语言的弹层方位。
2.2 TimePicker 单值属性
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
showNowBtn | 是否显示"此刻"(当前时间)按钮 | boolean | true | 2.21.0 |
defaultValue | 默认时间 | CalendarValue | - | - |
value | 组件的值,受控模式 | CalendarValue | - | - |
onChange | 选择时间后的回调(值变化时触发) | (valueString: string, value: Dayjs) => void | - | - |
onSelect | 选择时间时的回调(面板内选择即触发) | (valueString: string, value: Dayjs) => void | - | - |
onSelect与onChange的区别在源码中非常清晰:面板内每点选一个时间单元都会先触发onSelect(见 time-picker.tsx 的onHandleSelect),而onChange只在点击"确定"(或disableConfirm时点选即确认)且值真正变化后由Picker层的onHandleChange触发,并用isDayjsChange做了变化检测,避免无效回调。
2.3 TimePicker.RangePicker 范围属性
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
order | 起止时间是否自动排序 | boolean | true | 2.21.0 |
defaultValue | 默认时间 | CalendarValue[] | - | - |
placeholder | 输入框提示文案 | string[] | - | - |
value | 组件的值,受控模式 | CalendarValue[] | - | - |
onChange | 值改变时的回调 | (valueString: string[], value: Dayjs[]) => void | - | - |
onSelect | 选择时间时的回调 | (valueString: string[], value: Dayjs[]) => void | - | - |
2.4 CalendarValue 类型
export type CalendarValue = Dayjs | Date | string | number;即defaultValue/value接受Dayjs、Date、字符串或时间戳四种形式。源码 picker.tsx 中getDefaultValue()会先经过公共工具getDayjsValue(见 _util/dayjs.ts)统一归一化为Dayjs:字符串会先按当前format解析,失败后依次尝试内置的多种回退格式,保证"18:24:23"、"18:24"等常见写法都能被识别。
三、典型场景与可复制示例
以下示例均取自官方 demo 目录(components/TimePicker/demo),可直接运行。
3.1 受控组件
value与onChange需配合使用(control.md):
import { useState } from 'react'; import { TimePicker } from '@arco-design/web-react'; function App() { const [value, setValue] = useState(); return ( <TimePicker style={{ width: 194 }} value={value} onChange={(valueString) => setValue(valueString)} /> ); }源码中受控/非受控的判定是'value' in props ? getDayjsValue(propsValue, ...) : value——只要显式传入value(哪怕是undefined),即视为受控。测试用例(index.test.tsx 中 "set to undefined and onChange correctly",对应 issue #358)验证了受控值从具体时间重置为undefined后输入框清空、再次选择仍能正确触发onChange。
3.2 默认值
defaultValue.md:
import { TimePicker } from '@arco-design/web-react'; const { RangePicker } = TimePicker; const App = () => { return ( <div> <TimePicker defaultValue="18:24:23" style={{ width: 194 }} /> <RangePicker defaultValue={['09:24:53', '18:44:33']} style={{ width: 252 }} /> </div> ); };3.3 定制格式 format
format决定输入框展示,同时决定面板渲染哪些列。format.md:
import { TimePicker } from '@arco-design/web-react'; import dayjs from 'dayjs'; const App = () => ( <TimePicker format="HH:mm" defaultValue={dayjs('09:24', 'HH:mm')} style={{ width: 130 }} /> );实现依据是 util.ts 中的getColumnsFromFormat:它扫描格式串中的H、h、m、s、a、A占位符,只渲染对应列;若出现a/A还自动推断为 12 小时制(此时可不显式传use12Hours)。测试用例 "format" 验证了format="HH:mm"时面板只有时、分两列。
3.4 定制步长 step
step.md:
const App = () => ( <TimePicker defaultValue="10:25:30" step={{ hour: 2, minute: 5, second: 10 }} style={{ width: 194 }} /> );源码time-picker.tsx的getShowList按步长生成选项:小时列为0, 2, 4, ..., 22,分钟列为0, 5, ..., 55,秒列为0, 10, ..., 50;12 小时制下小时列仅 12 项且首项改写为12。单测 "step" 断言三列单元格数恰为 12、12、6,与24/2、60/5、60/10完全一致。
3.5 12 小时制 use12Hours
use-12-hours.md:
import { TimePicker } from '@arco-design/web-react'; import dayjs from 'dayjs'; const App = () => ( <div> <TimePicker use12Hours format="hh:mm:ss A" defaultValue={dayjs('12:20:20 AM', 'hh:mm:ss A')} placeholder="请选择时间" style={{ width: 194 }} /> <TimePicker.RangePicker use12Hours format="hh:mm:ss A" defaultValue={[dayjs('12:20:20 AM', 'hh:mm:ss A'), dayjs('08:30:30 PM', 'hh:mm:ss A')]} style={{ width: 300 }} /> </div> );启用后面板会额外出现一列am/pm选择列(renderAmPm),其大小写跟随格式串中的A/a占位符。RangePicker 的单测(range.test.tsx)验证了 12 小时制下会多渲染第 4 列且仅 2 个单元格(am/pm)。
3.6 禁用与隐藏部分时间选项
disabledTime.md:
const App = () => ( <div> <TimePicker style={{ width: 194 }} disabledHours={() => [1, 2, 4, 14]} disabledMinutes={() => [1, 2, 3, 4, 14, 15, 16, 20, 50]} disabledSeconds={() => [1, 2, 3, 4, 5, 6, 7, 10, 14, 60]} /> </div> );注意三个回调的签名差异:disabledHours无参数;disabledMinutes(selectedHour)与disabledSeconds(selectedHour, selectedMinute)可依据上级列的当前选择做联动禁用。
源码还有两个值得了解的行为:
- 选中值联动纠正:在
onHandleSelect中,若用户切换小时后当前分钟/秒落入新小时的禁用集合,组件会自动改为该范围内第一个未禁用的值,而不是产生非法组合; - 默认值避开禁用项:
getDefaultStr在无初始值时,会选取"第一个未禁用的选项"作为各列默认展示。这修复了早期版本的已知缺陷——变更日志 index.zh-CN.md 记载 2.51.1 "修复选中默认值没有兼容disabledHoursdisabledMinutesdisabledSeconds的 bug",测试用例 "should get non-disabled value as default"(对应 issue #494)断言最终值为03:00:01(0、1、2 时与 0、1 分均被禁用的场景)。
配合hideDisabledOptions可进一步直接隐藏禁用项(渲染时先filter再建列)。单测 "disabledHours / disabledMinutes / disabledSeconds" 断言了三列中禁用样式单元格数分别为 4、9、9。
3.7 禁用确认 disableConfirm
disableConfirm.md:
const App = () => ( <div> <TimePicker disableConfirm onSelect={(v, vd) => console.log('onSelect', v, vd)} onChange={(v, vd) => console.log('onChange', v, vd)} style={{ width: 194 }} /> <TimePicker.RangePicker disableConfirm onSelect={onSelect} onChange={onChange} style={{ width: 252 }} /> </div> );开启后底部确认按钮不再渲染,点选时间列即视为确认(onHandleSelect末尾直接调用onConfirmValue)。源码中页脚渲染逻辑_hideFooter表明:disableConfirm且单值模式且showNowBtn=false时整个页脚都会被隐藏。单测 "disableConfirm" 验证了点选第 2 个小时后输入框直接变为01:00:00且onChange立即触发。
3.8 范围选择器 RangePicker
rangepicker.md:
const App = () => ( <TimePicker.RangePicker style={{ width: 252 }} onSelect={(valueString, value) => console.log('onSelect:', valueString, value)} onChange={(valueString, value) => console.log('onChange:', valueString, value)} /> );范围模式的交互细节在 range-picker.tsx 中实现:
- 面板复用单值
TimePicker,只展示"聚焦输入框"对应的那一个值,通过focusedInputIndex与changeFocusedInputIndex在两个输入框间切换焦点; - 任一输入框未填时"确定"按钮保持禁用(
confirmBtnDisabled={!isDayjs(currentShowValue)}),点击确定若发现有一项为空,会自动把焦点切换到另一侧输入框而非报错; - 最终
onConfirmValue在Picker层处理:order(默认true)为真时对两个值调用getSortedDayjsArray(dayjs.ts)按时间戳升序排序后再落值并触发onChange,保证回调参数始终是[start, end]。
3.9 时区与 UTC 时间
时区由timezone(IANA 时区名)或utcOffset(偏移量)控制,二者同设时utcOffset优先。官方 demo(timezone.md、utcOffset.md)特别强调:使用 UTC 或时区时间时,传值请用 timestamp 或 Date 对象,字符串无法表示唯一时间,会造成困扰。
import { TimePicker, Select } from '@arco-design/web-react'; const zoneList = ['America/Los_Angeles', 'Europe/London', 'Africa/Cairo', 'Asia/Shanghai']; function App() { const [timezone, setTimezone] = useState('Asia/Shanghai'); const [value, setValue] = useState(new Date('2022-02-22')); return ( <Space> <Select defaultValue={timezone} options={zoneList} onChange={(tz) => setTimezone(tz)} triggerProps={{ autoAlignPopupWidth: false, position: 'bl' }} /> <TimePicker timezone={timezone} defaultValue={value} onChange={(v, vd) => setValue(vd && vd.toDate())} /> </Space> ); }实现原理在 _util/dayjs.ts:内部用toTimezone/toLocal做时区偏移换算(含对夏令时/冬令时边界的微调修正),getDayjsValue在设置了utcOffset/timezone时会把外部传入的值先转换到目标时区;而回调onChange返回前统一经toLocal转回本地时间。时区/偏移通过 context.ts 的PickerContext从Picker壳下发给面板层,Picker层还会监听utcOffset/timezone变化(usePrevious对比前后值),动态把已存储的值重新换算,保证切换时区时显示值正确。该能力自 2.30.0 引入。
3.10 其他外观类属性
- status(status.md):
<TimePicker status="error" />/status="warning",2.45.0 起替代已废弃的error布尔属性; - prefix(prefix.md):
<TimePicker prefix={<IconInfoCircle />} />,2.43.0 引入; - extra(extra.md):
<TimePicker extra="Extra Footer" />,在列区与页脚按钮之间渲染附加节点(time-picker.tsx中.arco-timepicker-footer-extra-wrapper); - size(size.md):
'mini' | 'small' | 'default' | 'large'四种尺寸; - disabled(disabled.md):整组件禁用,
Trigger不再响应点击。
四、面板底层机制
4.1 列渲染与滚动吸附
单列时间选项由TimeColumn(time-column.tsx)渲染,scrollSticky(默认true,2.23.0 引入)的行为是:
- 监听
onWheel,经 100ms 防抖后按当前scrollTop计算吸附索引(向上滚用Math.ceil、向下滚用Math.floor),自动选中该索引处且未禁用的选项;scrollSticky={false}时完全不绑定onWheel(2.49.1 修复过此时控制台出现 warning 的 bug); - 选中值变化时通过
scrollTo(util.ts)以b-tween的quartInOut缓动做 150ms 平滑滚动定位;弹层每次打开则瞬间(duration 0)滚动到当前值。
4.2 确认流程与页脚按钮
time-picker.tsx的页脚渲染逻辑:
const _hideFooter = hideFooter || (disableConfirm && isRangePicker) || (!isRangePicker && disableConfirm && !showNowBtn);- 单值模式默认显示
locale.TimePicker.now("此刻"按钮,showNowBtn默认true,2.21.0 引入)与"确定"按钮; - "此刻"按钮通过
getNow(utcOffset, timezone)取当前时间(设置了时区时会取目标时区的"现在",见 dayjs.ts 的getNow),并触发onSelect; - "确定"按钮在
confirmBtnDisabled || !valueShow时禁用,即必须已有面板内选择才可确认; - 键盘回车(
onPressEnter)等价于点击"确定":单值直接确认,Range 模式下先检查输入框合法字符串、焦点切换,两个值齐全后才确认(picker.tsx)。
4.3 triggerElement:自定义触发器与纯面板模式
triggerElement(2.38.0 引入)替换默认的输入框触发器。源码中有一个特殊分支:triggerElement === null时直接渲染面板本身(renderPopup(true)),即"只用面板、不用触发器"的纯面板模式——变更日志(2.44.0)明确记载了这一特性。若传入自定义节点,则节点被包在Trigger内作为点击触发源,triggerProps继续透传Trigger的其他参数(如autoAlignPopupWidth)。
五、版本演进与使用注意事项
从 变更日志 可梳理出该组件的能力时间线:
| 版本 | 变更 |
|---|---|
| 2.12.0 | 新增disableConfirm,支持点选即确认 |
| 2.21.0 | TimePicker新增showNowBtn;RangePicker新增order自动排序 |
| 2.23.0 | 新增scrollSticky滚动吸附开关 |
| 2.30.0 | 支持utcOffset与timezone |
| 2.38.0 | 新增triggerElement自定义触发器 |
| 2.43.0 | 新增prefix |
| 2.44.0 | 支持triggerElement={null}纯面板模式 |
| 2.45.0 | 新增status(error属性同期标记废弃) |
| 2.51.1 | 修复默认值未兼容disabled*选项的 bug |
使用建议小结:
format是核心参数——它同时决定展示格式、面板列构成、12 小时制推断,写值格式(HH:mm或hh:mm:ss A)应与期望粒度一致;- 时区/UTC 场景请传
Date或时间戳,不要用字符串表达"某时区的时间";切换时区时组件会自动换算已有值; onSelect与onChange分工:前者是"面板操作反馈",后者是"最终值确认",表单提交逻辑应挂在onChange;disableConfirm适合"点选即生效"的快捷场景,此时onChange在每次点选后触发;RangePicker的order默认开启,回调中拿到的永远是排序后的[start, end],不需要自行排序。
本文所有 API 语义以官方文档components/TimePicker/README.en-US.md(及对应中文版 README.zh-CN.md)为准,实现细节可继续在 TimePicker 源码目录、公共 dayjs 工具 与 测试用例 中查证。
- UI组件
- 前端
- 设计系统
【免费下载链接】arco-design
A comprehensive React UI components library based on Arco Design
相关推荐
Ant Design时间选择面板:TimePicker与时间范围选择
Ant Design时间选择面板:TimePicker与时间范围选择 概述 时间选择是Web应用中常见的功能需求,Ant Design提供了强大的时间选择解决方
UI组件前端设计系统Semi Design TimePicker 时间选择器完整指南:API 配置、时间范围与源码实现解析
Semi Design TimePicker 时间选择器完整指南:API 配置、时间范围与源码实现解析 导读 本文是 Semi Design( @douyinf
前端UI组件设计系统ant-design-vue TimePicker 时间选择框完全指南:API 详解、范围选择与源码级实现剖析
ant design vue TimePicker 时间选择框完全指南:API 详解、范围选择与源码级实现剖析 TimePicker 是 ant design
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考