news 2026/10/15 15:36:43

TDesign 小程序 Calendar 日历组件实战指南:从配置到源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TDesign 小程序 Calendar 日历组件实战指南:从配置到源码解析
  • 前端
  • UI组件
  • 小程序
  • 设计系统
  • 移动开发

【免费下载链接】tdesign-miniprogram

Wechat MiniProgram and Uniapp UI components lib for TDesign

项目地址:https://gitcode.com/gh_mirrors/tde/tdesign-miniprogram
点击查看免费下载

导读

Calendar是 TDesign 小程序组件库(tdesign-miniprogram)中用于"按照日历形式展示数据或日期的容器"的组件,支持单选、多选、区间选择三种模式,并提供翻页切换、自定义格式化、国际化等能力。本文将基于官方文档(packages/components/calendar/README.en-US.md),结合组件源码与测试用例,完整讲解组件的引入方式、全部 Props / Events / Slots / CSS Variables 的配置含义,并给出可直接运行的代码示例,帮助你在微信小程序中快速落地日期选择功能。

说明:Calendar 组件自0.22.0版本上线,渲染框架支持情况为 WebView。以下所有版本号标记(如1.11.2、0.34.0)均取自官方文档,表示该能力引入的版本。

一、组件引入:全局与局部两种方式

Calendar 组件与其他 TDesign 组件一样,可通过全局或局部方式注册:

  • 全局引入:在 miniprogram 根目录下的app.json中配置usingComponents;
  • 局部引入:在需要使用日历的页面或组件的index.json中配置。
"usingComponents": { "t-calendar": "tdesign-miniprogram/calendar/calendar" }

组件内部依赖t-popup、t-button、t-icon三个子组件,在 calendar.json 中已声明(styleIsolation: apply-shared)。引入后即可在 WXML 中直接使用<t-calendar>。

从源码结构看,Calendar 组件由以下文件组成,便于后续深度阅读:

  • 组件逻辑:calendar.ts
  • 属性声明:props.ts
  • 类型定义:type.ts
  • 模板:calendar.wxml、template.wxml、calendar-header.wxml
  • 样式:calendar.less
  • 底层算法:common/shared/calendar/index.ts

二、Calendar Props:完整配置参数解析

下表完整列出组件所有属性(与官方文档一致),并在其后补充源码层面的实现说明。

| name | type | default | description | required | | -- | -- | -- | -- | -- | | style | Object | - | 样式 | N | | custom-style | Object | - | 样式,一般用于开启虚拟化组件节点场景 | N | | allow-same-day | Boolean | false |1.11.2。是否允许区间选择日历的起止时间相同,仅当type='range'时有效 | N | | auto-close | Boolean | true |0.34.0。自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible | N | | confirm-btn | String / Object | '' | 确认按钮。值为 null 则不显示确认按钮;值为字符串则自定义按钮文本;值为 Object 则透传 Button 组件属性 | N | | first-day-of-week | Number | 0 | 第一天从星期几开始,默认 0 = 周日 | N | | format | Function | - | 用于格式化日期的函数 | N | | locale-text | Object | - | 国际化文案 | N | | max-date | Number | - | 最大可选的日期,不传则默认半年后 | N | | min-date | Number | - | 最小可选的日期,不传则默认今天 | N | | readonly | Boolean | - |1.9.7。是否只读,只读状态下不能选择日期 | N | | switch-mode | String | none |1.8.2。切换模式:none 平铺所有月份;month 按月切换;year-month 既按年也按月切换 | N | | title | String | - | 标题,不传默认为"请选择日期" | N | | type | String | single | 选择类型:single 单选;multiple 多选;range 区间选择 | N | | use-popup | Boolean | true |0.32.0。是否使用弹出层包裹日历 | N | | using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N | | value | Number / Array | - | 当前选择的日期,不传则选用 minDate 或今天,优先级 minDate > today;type 为 multiple 或 range 时传数组 | N | | default-value | Number / Array | undefined | 同上,非受控属性 | N | | visible | Boolean | false | 是否显示日历;usePopup 为 true 时有效 | N |

2.1 选择类型 type:single / multiple / range

type控制日历的核心行为,对应 type.ts 中TdCalendarProps.type的类型'single' | 'multiple' | 'range',默认single。三种模式的差异在底层算法类 common/shared/calendar/index.ts 的select与calcType中体现:

  • single:点击即选中,value为单个时间戳;
  • multiple:点击切换选中/取消,value为时间戳数组(源码中用isSameDate判断是否已选中,已选中则从数组移除,否则追加);
  • range:第一次点击作为起点,第二次点击作为终点,value为[start, end]数组;若点击日期早于起点则重新以该日期为起点。当起止日期相同且allowSameDay为 false 时,区间会被重置为仅含起点。

区间模式下的日期状态由calcType计算得出:起点为start、终点为end、中间日期为centre,起止相同且allowSameDay时为start-end。

2.2 翻页切换 switch-mode:none / month / year-month

switch-mode用于控制月份/年份的切换方式(1.8.2引入):

  • none(默认):平铺展示minDate到maxDate之间的所有月份,滚动查看;
  • month:一次展示一个月,头部提供前后月份切换按钮;
  • year-month:在month基础上增加前后年份切换按钮。

从 calendar-header.wxml 可以看到,year-month模式会渲染chevron-left-double/chevron-right-double(按年切换)与chevron-left/chevron-right(按月切换)图标,且切换按钮的禁用状态由 calendar.ts 中updateActionButton依据minDate/maxDate计算。切换行为定义在handleSwitchModeChange,其底层月份/年份偏移逻辑见 utils.ts(getPrevMonth/getNextMonth/getPrevYear/getNextYear)。

2.3 日期范围 min-date / max-date 与默认值规则

  • minDate:最小可选日期,不传默认今天(底层 common/shared/date.ts 的getDate());
  • maxDate:最大可选日期,不传默认半年后(getDate(6),即当前日期 + 6 个月);
  • value不传时,初始值优先取minDate,否则取今天 0 点的时间戳;range模式会将终点初始化为起点次日(见 calendar.tsinitialValue)。

超出范围、早于minDate或晚于maxDate的日期会被标记为disabled(不可点击)。

2.4 确认按钮 confirm-btn 的三种形态

confirm-btn支持字符串 / 对象 /null三种取值(默认''):

  • 值为null:不显示确认按钮,此时type='single'或区间已选满(rawValue.length === 2)时,选择完即自动关闭并触发change事件;
  • 值为字符串:自定义按钮文本,如confirm-btn="好的";
  • 值为对象:透传 Button 组件属性,例如{ content: 'ok', theme: 'danger' }。

测试用例test/index.test.js 中的:custom button用例验证了confirmBtn: { content: 'ok' }时按钮文本为ok;:without-button用例验证了confirm-btn="{{null}}"时点击日期立即返回结果。在组件内部,confirmBtn的 observer 会将字符串转为{ content }对象,将'slot'特殊值交给插槽渲染(见 calendar.ts 的confirmBtnobserver)。

2.5 格式化函数 format:为日期附加文字

format是形如(day: TDate) => TDate的函数(CalendarFormatType,定义见 type.ts),其中:

type TDateType = 'selected' | 'disabled' | 'start' | 'start-end' | 'centre' | 'end' | ''; interface TDate { date: Date; day: number; type: TDateType; className?: string; prefix?: string; suffix?: string; }

它会在每个日期格子渲染前被调用,返回值可修改prefix(格子上方文字)、suffix(格子下方文字)与className。模板 template.wxml 中,prefix与suffix分别渲染在日期上下方,suffix的状态色(选中/起点/终点为反色,禁用为弱色)在 calendar.less 中定义。

官方示例 custom-text/index.js 展示了两种典型用法:

singleFormat(day) { day.suffix = '¥60'; return day; }, format(day) { const { date } = day; const year = date.getFullYear(); const month = date.getMonth() + 1; const curDate = date.getDate(); day.suffix = '¥60'; if (year === 2022) { if (month === 2) { const map = { 1: '初一', 2: '初二', 3: '初三', 14: '情人节', 15: '元宵节' }; if (curDate in map) { day.prefix = map[curDate]; // 格子上方文字 day.suffix = '¥100'; // 格子下方文字 day.className = 'is-holiday'; // 附加样式类 } } } return day; },

2.6 国际化 locale-text

localeText的 TS 类型为CalendarLocaleText:

interface CalendarLocaleText { title?: string; // 标题文案 weekdays?: string[]; // 星期表头(长度为 7 的数组) monthTitle?: string; // 月份标题模板,支持 {year} / {month} 占位符 months?: string[]; // 12 个月的名称数组 confirm?: string; // 确认按钮文案 }

组件内置中文语言包(locale/zh_CN.ts):title: '请选择日期'、weekdays: ['日','一',...]、monthTitle: '{year} 年 {month}'、months: ['1 月', ...]、confirm: '确认'。月份标题中的{year}/{month}占位符由 calendar.wxs 的getMonthTitle完成替换(月份自动补零)。

官方国际化示例 local-text/index.js 配置了英文文案:

localeText: { title: 'Select Date', weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], monthTitle: '{month} {year}', confirm: 'Confirm', months: ['January', 'February', /* ... */ 'December'], },

组件通过 using-config.ts 的usingConfigBehavior 订阅全局语言包;当通过config-provider修改全局locale时,组件会自动刷新文案。localeText与全局配置的合并逻辑由 config-provider/use-config.ts 处理,localeText的优先级更高。

2.7 弹层与可见性:use-popup / visible / auto-close

  • usePopup(默认true):为 true 时日历由底部弹层t-popup(placement="bottom")包裹,模板见 calendar.wxml;为 false 时日历直接平铺在页面中,官方示例 without-popup/index.wxml 即展示内嵌用法;
  • visible:控制弹层显隐,仅在usePopup为 true 时有效,默认 false;
  • autoClose(默认true):点击关闭按钮、确认按钮、遮罩层时自动关闭弹层。组件 calendar.ts 的close(trigger)方法会在autoClose为 true 时主动将visible置为 false,并触发close事件;
  • usingCustomNavbar:页面使用自定义导航栏时置为 true,弹层会避开自定义导航栏区域。

三、Calendar Events:事件体系与触发时机

| name | params | description | | -- | -- | -- | | change |(value: timestamp)|0.28.0。不显示 confirm-btn 时,完成选择时触发(暂不支持 type = multiple) | | close |(trigger: CalendarTrigger)|0.34.0。关闭时触发,trigger取值见下方类型定义 | | confirm |(value: timestamp)| 点击确认按钮时触发 | | panel-change |(detail: { year, month })|1.8.4。切换月或年时触发(switch-mode 不为 none 时有效) | | scroll |({scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY})|1.4.6。滚动时触发 | | select |(value: timestamp)|0.28.0。点击日期时触发 |

close事件的CalendarTrigger类型为:

type CalendarTrigger = 'close-btn' | 'confirm-btn' | 'overlay' | 'auto-close';

对应四种关闭来源:点击关闭按钮、点击确认按钮、点击遮罩层、自动关闭。

从源码看事件触发链路:

  • select:handleSelect中点击有效日期后触发,若readonly或日期为disabled则直接返回(见 calendar.tshandleSelect);
  • confirm:onTplButtonTap中读取当前值后触发,同时关闭弹层(close('confirm-btn'));
  • change:在不显示确认按钮(confirmBtn == null)且单选或区间已选满时,选择完成即触发并关闭弹层;注意文档说明change暂不支持 type = multiple;
  • panel-change:handleSwitchModeChange在切换月份/年份时触发,回调参数month为 1 起始(源码中month + 1);
  • scroll:由scroll-view的bindscroll直接转发事件详情。

value是受控属性(controlledProps将value绑定到confirm与change事件),同时提供非受控的defaultValue属性,二者用法与文档中value的默认值规则一致。

测试用例test/index.test.js 验证了关键行为:点击 disabled 日期不改变 value、点击普通日期不立即触发 confirm(需点击确认按钮)、关闭按钮触发close等。

四、Calendar Slots:自定义插槽

| name | Description | | -- | -- | | confirm-btn | 确认按钮 | | title | 标题 |

  • title插槽:替换日历顶部标题,配合title属性使用;不传插槽时展示title属性或语言包文案;
  • confirm-btn插槽:完全自定义确认按钮区域。使用插槽前需将confirm-btn设置为字符串'slot',组件内部 calendar.ts 的confirmBtnobserver 会将'slot'值识别为插槽渲染模式;插槽内容仅在usePopup为 true 时渲染(见 template.wxml)。

五、CSS Variables:主题定制

组件提供以下 CSS 变量用于自定义样式(默认值取自 TDesign 设计令牌):

| Name | Default Value | Description | | -- | -- | -- | | --td-calendar-active-color | @brand-color | 选中态背景色 | | --td-calendar-bg-color | @bg-color-container | 日历背景色 | | --td-calendar-days-color | @text-color-secondary | 星期表头文字色 | | --td-calendar-item-centre-color | @brand-color-light | 区间中间日期背景色 | | --td-calendar-item-disabled-color | @text-color-disabled | 禁用日期文字色 | | --td-calendar-item-suffix-color | @text-color-placeholder | 日期下方文字色 | | --td-calendar-radius | @radius-extraLarge | 弹层圆角 | | --td-calendar-selected-border-radius | @radius-default | 选中格子圆角 | | --td-calendar-selected-color | @text-color-anti | 选中态文字色 | | --td-calendar-switch-mode-icon-color | @text-color-secondary | 翻页图标颜色 | | --td-calendar-switch-mode-icon-disabled-color | @text-color-disabled | 翻页图标禁用色 | | --td-calendar-title-color | @text-color-primary | 标题文字色 | | --td-calendar-title-font | @font-title-large | 标题字体 |

这些变量在 calendar.less 中逐一声明为 Less 变量并使用,例如:

@calendar-active-color: var(--td-calendar-active-color, @brand-color);

因此在业务页面中,可通过page或组件外层作用域直接覆盖变量,例如:

page { --td-calendar-active-color: #00a870; --td-calendar-radius: 24rpx; }

六、完整可运行示例:区间选择日历

综合以上知识点,一个完整的区间选择日历示例(对应官方示例 range/index.wxml 与 range/index.js)如下:

WXML:

<t-calendar visible="{{visible}}" value="{{value}}" minDate="{{minDate}}" maxDate="{{maxDate}}" type="range" bind:confirm="handleConfirm" /> <view class="wrapper" bind:tap="handleCalendar"> <view class="wrapper__left">{{_this.formatTimestamp(value[0])}}</view> <t-icon class="wrapper__center" name="swap-right" size="40rpx" /> <view class="wrapper__right">{{_this.formatTimestamp(value[1])}}</view> </view>

JS:

Component({ data: { visible: false, value: [new Date(2024, 11, 5).getTime(), new Date(2024, 11, 10).getTime()], minDate: new Date(2024, 10, 1).getTime(), maxDate: new Date(2024, 12, 1).getTime(), }, methods: { handleCalendar() { this.setData({ visible: true }); }, handleConfirm(e) { this.setData({ value: e.detail.value }); }, }, });

示例中时间戳格式化工具函数见 _example/calendar.wxs 的formatTimestamp,演示了如何在 WXML 层将时间戳转换为YYYY-MM-DD字符串(多选/区间场景以、拼接)。

七、底层原理:月份渲染与日期状态计算

Calendar 的日期算法被抽离为独立的纯类TCalendar(common/shared/calendar/index.ts),组件实例在created生命周期中初始化this.base = new TCalendar(this.properties),后续所有选择、月份计算均委托给该类:

  • getMonths():从minDate到maxDate逐月生成数据,每项包含year、month、months(该月每日的TDate数组,含type状态)与weekdayOfFirstDay(首日星期几,用于 WXML 中补齐前导空格);
  • calcType():根据当前type与已选值判定每日状态(selected/disabled/start/centre/end/start-end),并调用format函数加工prefix/suffix/className;
  • select():处理三种类型的选择逻辑(单选覆盖、多选增删、区间两点选法);
  • getDays(weekdays):根据firstDayOfWeek重排星期表头顺序。

月份切换的边界控制由 calendar.ts 的updateActionButton完成:将目标月份与minDate/maxDate所在月份做时间戳比较,越界时置灰对应翻页按钮。switchMode为none时,全部月份一次性渲染进scroll-view并利用scrollIntoView定位到当前值所在月份;为month/year-month时仅渲染currentMonth单月面板。

快照测试位于test/snapshots,覆盖基础、多选、区间、翻页、国际化、自定义文案、自定义范围、不使用弹层等 8 个示例场景(见test/demo.test.js 的 mapper 列表),可作为理解组件渲染结果的参考。

八、常见使用要点小结

  • 最小可用:<t-calendar visible="{{visible}}" bind:confirm="onConfirm" />即可得到一个默认单选、弹层展示、自动关闭的日历;
  • 隐藏确认按钮:confirm-btn="{{null}}",单选或区间选满后自动完成并触发change;
  • 嵌入页面:use-popup="{{false}}"将日历平铺在页面,配合bind:select使用;
  • 只读:readonly(1.9.7起)禁止点击选择;
  • 区间同日:type="range"且需要允许起止同日时开启allow-same-day(1.11.2起);
  • 翻页联动:switch-mode="year-month"并监听panel-change获取当前年月的回调;
  • 自定义文字:通过format为每个日期追加prefix/suffix/className;
  • 多语言:全局走config-provider切换locale,局部覆盖用locale-text。

如需进一步了解组件在全局配置、弹层、按钮、图标等基础设施上的实现,可继续阅读 config-provider、popup、button、icon 等目录下的对应文档与源码。

  • 前端
  • UI组件
  • 小程序
  • 设计系统
  • 移动开发

【免费下载链接】tdesign-miniprogram

Wechat MiniProgram and Uniapp UI components lib for TDesign

项目地址:https://gitcode.com/gh_mirrors/tde/tdesign-miniprogram
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 15:36:25

Arduino BLE FTMS 实战:让老健身车接入 Zwift 等训练 App

简介&#xff1a;这份资源面向嵌入式开发与室内骑行训练爱好者&#xff0c;提供一套基于Arduino与ESP32的BLE FTMS室内自行车健身机实现方案&#xff0c;用两块ESP32分别模拟功率计与智能骑行台&#xff0c;解决商用骑行台价格高昂、协议封闭的问题。压缩包共30个文件&#xff…

作者头像 李华
网站建设 2026/10/15 15:36:12

Loop Engineering:从“提示 AI”到“设计自运转系统”

1. 引言 当我们谈论 AI 辅助开发时&#xff0c;最常见的画面是&#xff1a;开发者打开对话框&#xff0c;输入一段提示词&#xff0c;等待模型生成代码&#xff0c;复制粘贴&#xff0c;再继续下一个问题。这种“提示-响应”模式虽然高效&#xff0c;却把人类牢牢钉在循环的中心…

作者头像 李华
网站建设 2026/10/15 15:35:38

Kotlin变量声明深度解析:var、val、const val与不可变性

1. 从 var 和 val 说起&#xff1a;Kotlin 变量声明的底层逻辑1.1 为什么 Kotlin 要把变量声明拆成两个关键字刚从 Java 转到 Kotlin 的人&#xff0c;第一个卡壳的地方往往不是协程&#xff0c;也不是扩展函数&#xff0c;而是var和val这两个关键字。Java 里一个int a 1;就完…

作者头像 李华
网站建设 2026/10/15 15:35:37

Python程序员必备的Linux命令实战指南:排查日志、管理进程与稳定部署

凌晨一点半&#xff0c;线上服务日志突然刷屏&#xff0c;同事在群里连环你&#xff0c;说接口超时率肉眼可见地在涨。你一边打开浏览器控制台&#xff0c;一边手忙脚乱地在服务器上乱敲命令——如果你有过这种经历&#xff0c;你应该能理解我为什么总跟团队里的小朋友说&#…

作者头像 李华
网站建设 2026/10/15 15:35:10

STM32寄存器级开发实战:从时钟树到外设的完整教程

简介&#xff1a;这份STM32教程PPT资源面向嵌入式初学者与进阶开发者&#xff0c;围绕STM32微控制器的架构原理与实战应用展开&#xff0c;帮助读者从寄存器级操作逐步过渡到外设驱动开发。内容覆盖初识STM32、RCC时钟配置&#xff08;HSE/HSI&#xff09;、新建寄存器工程、SR…

作者头像 李华