Ant Design DatePicker 日期选择器深度指南:从 API 全解到源码实现剖析
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
本文以 Ant Design 官方 DatePicker 组件文档为主线,系统讲解日期/周/月/季度/年选择器与 RangePicker 五种形态的完整 API、国际化配置、格式化规则与主题 Token,并结合 components/date-picker 目录下的源码(generatePicker工厂、rc-picker封装、useComponents定制机制)剖析其底层实现,帮助你在实际业务中快速落地日期选择场景并理解每个参数背后的真实行为。
何时使用与组件形态
当用户需要输入一个日期时,可以点击标准输入框,弹出日期面板进行选择。日期类组件包括以下形式:
DatePicker(默认picker="date")DatePicker[picker="month"]DatePicker[picker="week"]DatePicker[picker="year"]DatePicker[picker="quarter"](4.1.0 新增)RangePicker- 自 5.14.0 起,单值
DatePicker还支持multiple多选形态
从源码结构看,components/date-picker/generatePicker/index.tsx 中的工厂函数generatePicker接收一个日期库生成配置(当前仓库注入的是 dayjs 的dayjsGenerateConfig),一次性产出DatePicker、WeekPicker、MonthPicker、YearPicker、QuarterPicker、TimePicker以及RangePicker,并以静态属性的形式挂载到DatePicker上:
// components/date-picker/generatePicker/index.tsx(节选) const { DatePicker, WeekPicker, MonthPicker, YearPicker, TimePicker, QuarterPicker } = generateSinglePicker(generateConfig); const RangePicker = generateRangePicker(generateConfig); MergedDatePicker.WeekPicker = WeekPicker; MergedDatePicker.MonthPicker = MonthPicker; MergedDatePicker.YearPicker = YearPicker; MergedDatePicker.RangePicker = RangePicker; MergedDatePicker.TimePicker = TimePicker; MergedDatePicker.QuarterPicker = QuarterPicker;因此在实际使用中,<DatePicker picker="week" />与<DatePicker.WeekPicker />是等价的两套写法;源码中 generateSinglePicker.tsx 会在开发环境下对DatePicker.WeekPicker这类旧式用法给出deprecated警告,推荐直接使用picker属性。
快速上手代码示例
以下示例均取自仓库中的官方 demo,可直接复制到项目中运行(依赖antd与dayjs)。
基本:五种 picker 形态
来自 demo/basic.tsx:
import React from 'react'; import type { DatePickerProps } from 'antd'; import { DatePicker, Space } from 'antd'; const onChange: DatePickerProps['onChange'] = (date, dateString) => { console.log(date, dateString); }; const App: React.FC = () => ( <Space direction="vertical"> <DatePicker onChange={onChange} /> <DatePicker onChange={onChange} picker="week" /> <DatePicker onChange={onChange} picker="month" /> <DatePicker onChange={onChange} picker="quarter" /> <DatePicker onChange={onChange} picker="year" /> </Space> ); export default App;onChange的第一个参数是dayjs对象(multiple时为Dayjs[]),第二个参数是格式化后的字符串(多选时为string[]),这一点由 generatePicker/interface.ts 中的PickerPropsWithMultiple类型定义得到印证:onChange?: (date: ValueType, dateString: string | string[]) => void。
范围选择器 RangePicker
来自 demo/range-picker.tsx:
import React from 'react'; import { DatePicker, Space } from 'antd'; const { RangePicker } = DatePicker; const App: React.FC = () => ( <Space direction="vertical" size={12}> <RangePicker /> <RangePicker showTime /> <RangePicker picker="week" /> <RangePicker picker="month" /> <RangePicker picker="quarter" /> <RangePicker picker="year" id={{ start: 'startInput', end: 'endInput', }} onFocus={(_, info) => { console.log('Focus:', info.range); // 'start' | 'end' }} onBlur={(_, info) => { console.log('Blur:', info.range); }} /> </Space> ); export default App;注意 5.14.0 起新增的id参数支持{ start, end }对象,onFocus/onBlur回调的第二个参数会带上{ range: 'start' | 'end' },便于区分焦点落在哪个输入框。
多选(multiple)
来自 demo/multiple.tsx,5.14.0 起支持:
import React from 'react'; import type { DatePickerProps } from 'antd'; import { DatePicker, Flex } from 'antd'; import dayjs from 'dayjs'; import type { Dayjs } from 'dayjs'; const onChange: DatePickerProps<Dayjs[]>['onChange'] = (date, dateString) => { console.log(date, dateString); }; const defaultValue = [dayjs('2000-01-01'), dayjs('2000-01-03'), dayjs('2000-01-05')]; const App: React.FC = () => ( <Flex vertical gap="small"> <DatePicker multiple onChange={onChange} maxTagCount="responsive" defaultValue={defaultValue} size="small" /> <DatePicker multiple onChange={onChange} maxTagCount="responsive" defaultValue={defaultValue} /> </Flex> ); export default App;多选模式下value/defaultValue类型为Dayjs[],且不支持showTime;配合maxTagCount="responsive"可以自适应展示已选标签。多选、范围模式下order参数(默认true)控制是否自动排序,needConfirm在multiple时默认false(失焦即代表选择)。
国际化配置
默认配置为 en-US。如果项目需要其他语言,推荐在入口处使用 ConfigProvider 全局设置国际化组件:
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale // 确保还导入相关的 dayjs 文件,否则所有文本的区域设置都不会更改(例如范围选择器月份) import locale from 'antd/locale/zh_CN'; import dayjs from 'dayjs'; import 'dayjs/locale/zh-cn'; dayjs.locale('zh-cn'); <ConfigProvider locale={locale}> <DatePicker defaultValue={dayjs('2015-01-01', 'YYYY-MM-DD')} /> </ConfigProvider>;重要提示:在搭配 Next.js 的 App Router 使用时,注意在引入 dayjs 的 locale 文件时加上'use client'。这是由于 Ant Design 的组件都是客户端组件,在 RSC 中引入 dayjs 的 locale 文件将不会在客户端生效。
如有特殊需求(仅修改单一组件的语言),请使用locale参数,其默认配置结构可参考仓库中的 locale/example.json:
{ "lang": { "locale": "en_US", "placeholder": "Select date", "rangePlaceholder": ["Start date", "End date"], "today": "Today", "ok": "OK", "clear": "Clear", "dateFormat": "M/D/YYYY", "monthBeforeYear": true, "shortWeekDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] }, "timePickerLocale": { "placeholder": "Select time" }, "dateFormat": "YYYY-MM-DD", "dateTimeFormat": "YYYY-MM-DD HH:mm:ss", "weekFormat": "YYYY-wo", "monthFormat": "YYYY-MM" }从源码看,locale 的合并逻辑在 generateSinglePicker.tsx:const locale = { ...contextLocale, ...props.locale },即组件级locale会覆盖 ConfigProvider 注入的contextLocale(默认兜底为 locale/en_US.ts 中的DatePicker段)。这也解释了为什么“仅修改单一组件语言”时可以直接传locale参数。
placeholder 的解析则委托给 util.ts 中的getPlaceholder:它会按picker类型依次检查yearPlaceholder、quarterPlaceholder、monthPlaceholder、weekPlaceholder等字段,最终回落到通用的placeholder。RangePicker 有对应的getRangePlaceholder处理rangePlaceholder/rangeYearPlaceholder等字段。
共同的 API
以下 API 为DatePicker、RangePicker共享:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| allowClear | 自定义清除按钮 | boolean | { clearIcon?: ReactNode } | true | 5.8.0: 支持对象类型 |
| autoFocus | 自动获取焦点 | boolean | false | |
| className | 选择器 className | string | - | |
| dateRender | 自定义日期单元格的内容,5.4.0 起用cellRender代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| cellRender | 自定义单元格的内容 | (current: dayjs, info: { originNode: React.ReactElement, today: DateType, range?: 'start' | 'end', type: PanelMode, locale?: Locale, subType?: 'hour' | 'minute' | 'second' | 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| components | 自定义面板 | Record<Panel | 'input', React.ComponentType> | - | 5.14.0 |
| disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs, info: { from?: dayjs }) => boolean | - | info: 5.14.0 |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 dayjs#format 文档 | formatType | 由 rc-picker 内置格式决定 | |
| order | 多选、范围时是否自动排序 | boolean | true | 5.14.0 |
| preserveInvalidOnBlur | 失去焦点是否要清空输入框内无效内容 | boolean | false | 5.14.0 |
| popupClassName | 额外的弹出日历 className | string | - | 4.23.0 |
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | |
| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | |
| locale | 国际化配置 | object | 见 locale/example.json | |
| minDate | 最小日期,同样会限制面板的切换范围 | dayjs | - | 5.14.0 |
| maxDate | 最大日期,同样会限制面板的切换范围 | dayjs | - | 5.14.0 |
| mode | 日期面板的状态 | time|date|month|year|decade | - | |
| needConfirm | 是否需要确认按钮,为false时失去焦点即代表选择。当设置multiple时默认为false | boolean | - | 5.14.0 |
| nextIcon | 自定义下一个图标 | ReactNode | - | 4.17.0 |
| open | 控制弹层是否展开 | boolean | - | |
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
| picker | 设置选择器类型 | date|week|month|quarter|year | date | quarter: 4.1.0 |
| placeholder | 输入框提示文字 | string | [string, string] | - | |
| placement | 选择框弹出的位置 | bottomLeftbottomRighttopLefttopRight | bottomLeft | |
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
| presets | 预设时间范围快捷选择,自 5.8.0 起 value 支持函数返回值 | { label: React.ReactNode, value: Dayjs | (() => Dayjs) }[] | - | |
| size | 输入框大小,large高度为 40px,small为 24px,默认是 32px | large|middle|small | - | |
| status | 设置校验状态 | 'error' | 'warning' | - | 4.19.0 |
| style | 自定义输入框样式 | CSSProperties | {} | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| superNextIcon | 自定义>>切换图标 | ReactNode | - | 4.17.0 |
| superPrevIcon | 自定义<<切换图标 | ReactNode | - | 4.17.0 |
| variant | 形态变体 | outlined|borderless|filled | outlined | 5.13.0 |
| onOpenChange | 弹出日历和关闭日历的回调 | function(open) | - | |
| onPanelChange | 日历面板切换的回调 | function(value, mode) | - |
几个参数在源码中值得注意:
- allowClear 对象类型:util.ts 的
useIcons中,allowClear === true时使用默认清除图标,传入对象时则展开{ clearIcon }等字段进行覆盖,这与 Select 的清除逻辑共用useSelectIcons。 - placement 的底层映射:
transPlacement2DropdownAlign(util.ts)会把placement转换为 rc-picker 所需的dropdownAlign,例如bottomLeft对应锚点['tl', 'bl']加[0, 4]偏移;而纯展示面板(_InternalPanelDoNotUseOrYouWillBeFired)在 index.tsx 的postPureProps中会强制adjustX/adjustY为false,保证静态渲染时弹出层位置不漂移。 - variant 形态:5.13.0 引入
variant取代旧bordered,源码中通过useVariant生成ant-picker-{variant}类名,并对bordered给出弃用警告。 - zIndex:弹层 z-index 取自 generateSinglePicker.tsx 的
useZIndex('DatePicker', props.popupStyle?.zIndex),即全局主题zIndexPopup可被popupStyle.zIndex局部覆盖。 - 受控禁用:
disabled与ConfigProvider的DisabledContext取并集(customDisabled ?? disabled),因此外层 ConfigProvider 设置disabled可批量禁用日期组件。
共同的方法
| 名称 | 描述 | 版本 |
|---|---|---|
| blur() | 移除焦点 | |
| focus() | 获取焦点 |
这两个命令式方法通过forwardRef+useImperativeHandle透传到底层RCPicker的PickerRef(见 generateSinglePicker.tsx),因此ref.current.focus()直接作用在输入框上。
DatePicker 专有 API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| defaultPickerValue | 默认面板日期,每次面板打开时会被重置到该日期 | dayjs | - | 5.14.0 |
| defaultValue | 默认日期,如果开始时间或结束时间为null或者undefined,日期范围将是一个开区间 | dayjs | - | |
| disabledTime | 不可选择的时间 | function(date) | - | |
| format | 展示的日期格式 | formatType | YYYY-MM-DD | |
| multiple | 是否为多选,不支持showTime | boolean | false | 5.14.0 |
| pickerValue | 面板日期,可以用于受控切换面板所在日期。配合onPanelChange使用。 | dayjs | - | 5.14.0 |
| renderExtraFooter | 在面板中添加额外的页脚 | (mode) => React.ReactNode | - | |
| showNow | 显示当前日期时间的快捷选择 | boolean | - | |
| showTime | 增加时间选择功能 | Object | boolean | 参考 TimePicker API | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒 | dayjs | dayjs() | |
| showWeek | DatePicker 下展示当前周 | boolean | false | 5.14.0 |
| value | 日期 | dayjs | - | |
| onChange | 时间发生变化的回调 | function(date: dayjs, dateString: string) | - | |
| onOk | 点击确定按钮的回调 | function() | - | |
| onPanelChange | 日期面板变化时的回调 | function(value, mode) | - |
注:
showTime传对象时,其取值(如format、use24Hours等)参考 TimePicker 组件的 API 文档。
各 picker 类型的默认格式
不同picker类型拥有各自的默认format,其余参数(defaultValue、value、onChange、renderExtraFooter、multiple)与上文 DatePicker 一致:
| 类型 | 默认 format | 说明 |
|---|---|---|
DatePicker[picker="year"] | YYYY | 4.1.0 起可选 |
DatePicker[picker="quarter"] | YYYY-\QQ | 4.1.0 新增 |
DatePicker[picker="month"] | YYYY-MM | |
DatePicker[picker="week"] | YYYY-wo |
这些默认值与 locale/example.json 中顶层的dateFormat: "YYYY-MM-DD"、weekFormat: "YYYY-wo"、monthFormat: "YYYY-MM"字段一一对应,locale 对象中的这些字段正是面板展示格式的来源之一。
RangePicker 专有 API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| allowEmpty | 允许起始项部分为空 | [boolean, boolean] | [false, false] | |
| cellRender | 自定义单元格的内容 | (current: dayjs, info: { originNode: React.ReactElement, today: DateType, range?: 'start' | 'end', type: PanelMode, locale?: Locale, subType?: 'hour' | 'minute' | 'second' | 'meridiem' }) => React.ReactNode | - | 5.4.0 |
| dateRender | 自定义日期单元格的内容,5.4.0 起用cellRender代替 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |
| defaultPickerValue | 默认面板日期,每次面板打开时会被重置到该日期 | dayjs[] | - | 5.14.0 |
| defaultValue | 默认日期 | dayjs[] | - | |
| disabled | 禁用起始项 | [boolean, boolean] | - | |
| disabledTime | 不可选择的时间 | function(date: dayjs, partial:start|end, info: { from?: dayjs }) | - | info.from: 5.17.0 |
| format | 展示的日期格式 | formatType | YYYY-MM-DD HH:mm:ss | |
| id | 设置输入框id属性 | { start?: string, end?: string } | - | 5.14.0 |
| pickerValue | 面板日期,可以用于受控切换面板所在日期。配合onPanelChange使用。 | dayjs[] | - | 5.14.0 |
| presets | 预设时间范围快捷选择,自 5.8.0 起 value 支持函数返回值 | { label: React.ReactNode, value: (Dayjs | (() => Dayjs))[] }[] | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| separator | 设置分隔符 | React.ReactNode | <SwapRightOutlined /> | |
| showTime | 增加时间选择功能 | Object | boolean | 参考 TimePicker API | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒 | dayjs[] | [dayjs(), dayjs()] | |
| value | 日期 | dayjs[] | - | |
| onCalendarChange | 待选日期发生变化的回调。info参数自 4.4.0 添加 | function(dates: [dayjs, dayjs], dateStrings: [string, string], info: { range:start|end}) | - | |
| onChange | 日期范围发生变化的回调 | function(dates: [dayjs, dayjs], dateStrings: [string, string]) | - | |
| onFocus | 聚焦时回调 | function(event, { range: 'start' | 'end' }) | - | range: 5.14.0 |
| onBlur | 失焦时回调 | function(event, { range: 'start' | 'end' }) | - | range: 5.14.0 |
onCalendarChange与onChange的区别在源码中也有对应:generateSinglePicker.tsx 将onCalendarChange作为“待选”事件的钩子(点击面板日期时触发,范围选择中第一个日期点击即触发),而onChange仅在最终确认完整范围后触发。旧版 TimePicker 的onSelect也被映射为onCalendarChange的兼容回调,并附带弃用警告。
formatType 类型定义
format参数的完整类型定义如下(注意:type字段为 5.14.0 新增,用于“格式对齐”mask 场景):
import type { Dayjs } from 'dayjs'; type Generic = string; type GenericFn = (value: Dayjs) => string; export type FormatType = | Generic | GenericFn | Array<Generic | GenericFn> | { format: string; type?: 'mask'; };- 字符串:如
'YYYY-MM-DD',占位符语法遵循 dayjs 的 format 文档; - 函数:接收
Dayjs返回展示字符串,可完全自定义格式化逻辑; - 数组:支持多格式匹配输入,展示以第一个为准,适合兼容历史脏数据;
- 对象
{ format, type: 'mask' }:仅用于输入框展示对齐,不改变实际解析行为。
定制面板(components 与纯展示面板)
5.14.0 起可通过components属性替换面板中的任意部分(面板、头部、输入框等)。源码实现非常简洁,generatePicker/useComponents.ts:
import PickerButton from '../PickerButton'; export default function useComponents(components?: Components) { return useMemo( () => ({ button: PickerButton, ...components, }), [components], ); }可以看到 antd 默认将面板按钮替换为统一风格的 PickerButton.tsx(保证 Today/OK 按钮与 antd 按钮设计语言一致),用户传入的components会在其后展开,可覆盖button以外的任意面板节点。
另外,DatePicker还暴露了DatePicker._InternalPanelDoNotUseOrYouWillBeFired静态属性(见 components/date-picker/index.tsx),它是通过通用工具genPurePanel生成的非受控纯展示面板,不挂载弹层、不响应交互,适用于设计稿静态渲染或截图场景;从postPureProps的实现看,它同时禁用了浮层自动偏移校正(adjustX/adjustY均为false)。
主题变量(Design Token)
DatePicker 支持组件级 Design Token 定制,可通过 ConfigProvider 的theme.components.DatePicker配置项覆盖组件默认值,例如调整输入框高度、圆角、面板底色等。组件 Token 的解析与合并发生在 components/date-picker/style/index.ts 中,该文件同时消费了全局 motion token(motionDurationMid、motionDurationSlow)来驱动输入框边框/背景过渡与面板展开动画。官方文档站中的 ComponentTokenTable 会列出全部 Token 及其默认值;在代码中使用时,建议先在浏览器 DevTools 中观察ant-picker前缀的 CSS 变量(组件已接入 CSS 变量体系,见 generateSinglePicker.tsx 的useCSSVarCls),再按需覆盖。
通用属性(rootClassName、style、classNames、styles等)可参考仓库docs/react目录下的 common-props 文档。
源码级实现要点
理解 DatePicker 的实现有助于排查复杂问题,关键链路如下:
- 入口装配:components/date-picker/index.tsx 将
rc-picker的 dayjs 生成配置传入generatePicker,得到合并后的DatePicker,并挂上generatePicker(供自定义日期库复用)、两个 PurePanel 静态属性。 - 属性注入:generatePicker/interface.ts 中
InjectDefaultProps在 rc-picker 原始 props 之上扩展了 antd 特有的size、placement、variant、status、popupClassName等字段,并保留locale?: PickerLocale(含lang+timePickerLocale两段结构)。 - rc-picker 封装:generateSinglePicker.tsx 是核心——每个 Picker 都是
forwardRef组件,内部渲染RCPicker并注入:showToday: true(always 显示“今天”快捷键);- 后缀图标按
picker === 'time'区分ClockCircleOutlined与CalendarOutlined,Form 的hasFeedback会自动追加反馈图标; dropdownAlign由placement+direction(rtl 支持)换算而来;- 类名合并了尺寸(
ant-picker-large/middle/small)、形态(variant)、状态(status+ Form 校验态)、CSS 变量 hashId 与 Space.Compact 的紧凑样式; - 开发环境的弃用警告集中在
devUseWarning:包括dropdownClassName→popupClassName、bordered→variant、onSelect→onCalendarChange、以及DatePicker.WeekPicker等旧式组件写法。
- RangePicker由 generatePicker/generateRangePicker.tsx 独立生成,与单值 Picker 共享
InjectDefaultProps类型约束。 - 上下文隔离:组件外层包裹
ContextIsolator space,避免内部 Space 等布局组件意外继承外部 Space 上下文。
FAQ
当我指定了 DatePicker/RangePicker 的 mode 属性后,点击后无法选择年份/月份?
mode属性用于受控面板所处层级(time/date/month/year/decade)。指定mode后面板会被锁定在该层级,若不同步更新它(通常配合pickerValue+onPanelChange受控使用),点击年份/月份单元格后无法自动下钻。官方 FAQ 中给出了标准做法:用onPanelChange回调中的mode回写mode状态。
为何日期选择年份后返回的是日期面板而不是月份面板?
当用户选择完年份后,系统会直接切换至日期面板,而非显式提供月份选择。这样设计在于用户只需进行一次点击即可完成年份修改,无需再次点击进入月份选择界面,从而减少操作负担,同时也避免需要额外感知月份的记忆负担。
如何在 DatePicker 中使用自定义日期库(如 Moment.js)?
generatePicker是泛型工厂:generatePicker<DateType>(generateConfig)。仓库中DatePicker.generatePicker静态属性(index.tsx)即导出该工厂,可以传入基于其他日期库的GenerateConfig生成自定义 Picker,官方文档站另有《使用自定义日期库》专题说明具体配置方式。
为什么时间类组件的国际化 locale 设置不生效?
多数情况是只设置了 antd 的ConfigProvider locale却没有同步导入并调用dayjs/locale/xx与dayjs.locale('xx')——antd locale 只影响组件自身文案(Today/OK 等),而面板中“几月”“星期”等文本由 dayjs 自身语言包决定,两者必须同时设置。另外在 Next.js App Router 下,dayjs locale 的引入需放在客户端组件(加'use client')。
如何修改周的起始日?
请确保使用了正确的语言包,或者修改 dayjs 的locale配置:
import dayjs from 'dayjs'; import 'dayjs/locale/zh-cn'; import updateLocale from 'dayjs/plugin/updateLocale'; dayjs.extend(updateLocale); dayjs.updateLocale('zh-cn', { weekStart: 0, });为何使用panelRender时,原来面板无法切换?
当你通过panelRender动态改变层级结构时,会使得原本的 Panel 被当做新的节点删除并创建,这会导致其原本的状态被重置,保持结构稳定即可。该问题的典型表现是面板停留在初始月/年、点击无法响应切换。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考