尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Ant Design Calendar 的 headerRender 实战:自定义日历头部内容

发布时间:2026/9/7 19:40:39

资讯中心
01
ARTICLE

Ant Design Calendar 的 headerRender 实战:自定义日历头部内容

Ant Design Calendar 的 headerRender 实战:自定义日历头部内容
Ant Design Calendar 的 headerRender 实战自定义日历头部内容【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design在 antd 的 Calendar 组件中默认头部提供“年份选择 月份选择 月/年模式切换”这一固定形态当产品需要展示品牌标题、自定义跳转范围、或把头部控件换成自己的一套交互例如示例中的 Radio 切换 双 Select 跳转时就需要用到headerRender属性来整体接管头部渲染。本篇以 customize-header 示例 为主线结合 generateCalendar.tsx 的源码实现与 单元测试讲清楚headerRender的入参结构、回调约定、与默认头部的差异以及面板切换事件的触发链路。读完后你可以直接复制示例代码实现自定义头部并理解其背后onChange/onTypeChange的内部调用机制。headerRender 的定位与 APIheaderRender是 Calendar 的可选属性官方 API 表见 index.zh-CN.md中对其描述为属性说明类型默认值headerRender自定义头部内容function(object: { value: Dayjs, type: year | month, onChange: f(), onTypeChange: f() })-在源码中它被定义为泛型函数类型generateCalendar.tsxexport type CalendarMode year | month; export type HeaderRenderDateType (config: { value: DateType; type: CalendarMode; onChange: (date: DateType) void; onTypeChange: (type: CalendarMode) void; }) React.ReactNode;四个入参的含义value当前面板所显示的日期泛型DateType默认 dayjs 实现下为Dayjs见 index.tsx用于读取当前年/月来构造选项列表type当前日历模式month显示月历面板year显示年历面板onChange(date)把面板切换到新日期内部会标记选择来源为customize见下文“事件链路”onTypeChange(type)在月/年两种面板模式之间切换。组件在渲染时的分支逻辑非常直接generateCalendar.tsx只要传了headerRender就完全不再渲染内置的CalendarHeader而是把你的返回值原样放入根节点顶部没传才走默认头部。也就是说headerRender是“全量替换”而非“局部覆盖”头部的一切样式与交互都由你负责。完整示例Radio 切换 年份/月份双 Selectcustomize-header.tsx 展示了如何用一个 Radio.Group 控制模式、两个 Select 分别跳转年份与月份并用theme.useToken()的 token 给外层容器加上与组件风格一致的边框圆角。完整代码如下import React from react; import dayjs from dayjs; import dayjs/locale/zh-cn; import { Calendar, Flex, Radio, Select, theme, Typography } from antd; import type { CalendarProps } from antd; import type { Dayjs } from dayjs; import dayLocaleData from dayjs/plugin/localeData; dayjs.extend(dayLocaleData); const App: React.FC () { const { token } theme.useToken(); const onPanelChange (value: Dayjs, mode: CalendarPropsDayjs[mode]) { console.log(value.format(YYYY-MM-DD), mode); }; const wrapperStyle: React.CSSProperties { width: 300, border: ${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}, borderRadius: token.borderRadiusLG, }; return ( div style{wrapperStyle} Calendar fullscreen{false} headerRender{({ value, type, onChange, onTypeChange }) { const year value.year(); const month value.month(); // 以当前年份为中心构造 ±10 年共 20 个选项 const yearOptions Array.from({ length: 20 }, (_, i) { const label year - 10 i; return { label, value: label }; }); // 通过 dayjs localeData 插件获取当前语言的月份简称 const monthOptions value .localeData() .monthsShort() .map((label, index) ({ label, value: index, })); return ( div style{{ padding: 8 }} Typography.Title level{4}Custom header/Typography.Title Flex gap{8} Radio.Group sizesmall onChange{(e) onTypeChange(e.target.value)} value{type} Radio.Button valuemonthMonth/Radio.Button Radio.Button valueyearYear/Radio.Button /Radio.Group Select sizesmall popupMatchSelectWidth{false} value{year} options{yearOptions} onChange{(newYear) { const now value.clone().year(newYear); onChange(now); }} / Select sizesmall popupMatchSelectWidth{false} value{month} options{monthOptions} onChange{(newMonth) { const now value.clone().month(newMonth); onChange(now); }} / /Flex /div ); }} onPanelChange{onPanelChange} / /div ); }; export default App;示例中的几个关键细节fullscreen{false}迷你模式下头部空间有限所以 Select、Radio.Group 都使用sizesmall外层再手动收窄为 300px 宽并用token.colorBorderSecondary、token.borderRadiusLG绘制边框让自定义头部与迷你日历的视觉语言保持一致value.clone().year(newYear)/value.clone().month(newMonth)dayjs 对象是不可变风格的修改年或月时必须先clone()再设置然后把新日期交给onChange。若直接value.year(...)会污染原引用并导致面板不刷新dayjs.extend(dayLocaleData)value.localeData().monthsShort()通过 dayjs 的localeData插件拿到“随当前 locale 变化”的月份简称如已import dayjs/locale/zh-cn时为“1月…12月”这样下拉项文案天然国际化而不用硬编码月份数组。这是自定义头部里值得复用的小技巧onPanelChange面板切换无论是选格子还是通过头部onChange跳转时回传新日期与当前模式可用于触发按月拉取数据等副作用。与默认头部的实现差异不传headerRender时组件渲染的是内置 CalendarHeader理解它的行为有助于设计自己的头部年份下拉范围默认以当前年份为中心取 20 年即YEAR_SELECT_OFFSET 10、YEAR_SELECT_TOTAL 20Header.tsx。示例中手写的“±10 年”与内置行为完全一致但自定义后可以随意扩窄validRange的月份钳制内置YearSelect在设置了validRange时会检查所选年份是否越出范围的起/止月并把月份强制钳回范围内Header.tsxMonthSelect也会按validRange裁剪可选月份区间Header.tsx。自定义头部不会获得这些钳制逻辑——如果你在业务里同时使用了validRange和headerRender需要在自己的选项构造与onChange前自行做范围校验否则可能把面板切到不可用区间模式切换控件内置ModeSwitch使用 antd 的 Radio.Group取值固定为month/year并通过 locale 渲染文案Header.tsx示例中的 Radio 方案与之等价只是样式自由Form 集成细节内置头部会注入FormItemInputContext把isFormItemInput置为false避免头部 Select 被 FormItem 的前缀逻辑影响Header.tsx。自定义头部内若嵌入了 Select在 FormItem 场景下需要自己注意这一上下文缺失带来的影响。事件链路onChange / onTypeChange 内部做了什么从源码可以确认两个回调的完整行为generateCalendar.tsxconst triggerChange (date: DateType) { setMergedValue(date); if (!isSameDate(date, mergedValue, generateConfig)) { // 月面板切月 / 年面板切年 时触发 onPanelChange if ( (panelMode date !isSameMonth(date, mergedValue, generateConfig)) || (panelMode month !isSameYear(date, mergedValue, generateConfig)) ) { triggerPanelChange(date, mergedMode); } onChange?.(date); } }; const triggerModeChange (newMode: CalendarMode) { setMergedMode(newMode); triggerPanelChange(mergedValue, newMode); }; const onInternalSelect (date: DateType, source: SelectInfo[source]) { triggerChange(date); onSelect?.(date, { source }); };headerRender里的onChange实际走onInternalSelect(date, customize)generateCalendar.tsx先更新受控值再在“跨月/跨年”时触发onPanelChange最后触发onChange同时会调用onSelect并且selectInfo.source为customize——SelectInfo.source的枚举值包括year | month | date | customizegenerateCalendar.tsx你可以根据这个来源区分“用户点格子”和“用户走自定义头部跳转”两种选择onTypeChange走triggerModeChange更新 mode 后立刻以当前日期触发一次onPanelChange所以示例里的console.log(value.format(YYYY-MM-DD), mode)在切换月/年模式时也会打印面板模式与日历模式存在映射日历mode为year时内部RCPickerPanel实际以panelMode month年历渲染month则对应panelMode date月历见 generateCalendar.tsx。测试对 headerRender 行为的印证仓库单测 index.test.tsx 中headerRender should work correctly用例覆盖了与示例完全对应的三条路径年份选择器在headerRender返回的 Select 中点击某选项断言业务回调onYearChange被调用月份选择器同样通过value.localeData().monthsShort()构造选项点击后断言onMonthChange被调用模式切换headerRender返回 Radio.Group点击后断言onTypeChange被调用。这印证了前文的结论headerRender只负责“渲染什么”而值变更、模式切换、面板回调全部由组件内部统一收口你的头部 UI 只需要把用户意图翻译为onChange/onTypeChange两次调用即可不必也无法绕开组件的受控状态。实践要点小结headerRender是全量替换传了它内置头部年份/月份 Select、模式切换按钮、validRange 钳制都不会再渲染头部布局、间距、响应式全部由你控制头部内操作日期请遵循“value.clone()→ 修改 →onChange(新日期)”的模式避免直接改写传入的value月份文案建议通过 dayjslocaleData获取保持多语言一致需要validRange约束时自定义头部要自行补齐范围裁剪因为内置钳制逻辑只存在于默认CalendarHeader用onSelect的source customize区分头部触发与面板点选配合onPanelChange即可实现“按月加载数据”这类需求。【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。