今天遇到了小程序日历范围组件返回值错误问题,使用TRAE国际版 Gemini-3.3-Pro模型,通过两次对话解决问题。
第一次:“请先了解下该微信小程序项目用的uview组件,然后再解决下u-calendar组件 mode为range可以选择日期范围时,点击日期时为正常选中,确认操作回显是 Invalid Date的问题”。
第二次:“问题一点都没有解决,请尝试查找下别的原因”。
真正的原因分析
在 u-calendar 的源码中,父组件 u-calendar.vue 会遍历生成每一天的配置项,并通过 months 数组传递给子组件 month.vue 进行渲染。
在生成配置时,有这样一段代码:
let config = {
// ...
date: new Date(date.replace(/\-/g, '/')), // 这是一个原生 Date 对象
// ...
}
const formatter = this.formatter || this.innerFormatter
return formatter(config)
官方这么做是为了让开发者通过 formatter 格式化函数可以拿到 config.date 这个原生 Date 对象进行加工(比如调用 .getFullYear() 等)。
但是,问题出在跨组件传值上:
在 UniApp 编译到微信小程序或部分 nvue 环境时,父组件向子组件传递的数据如果包含不可序列化的对象(如 Date 对象、函数等),框架在内部会将其“序列化”处理。这导致 Date 对象在到达 month.vue 时:
- 可能会变成一个空对象
{}。 - 或者是变成一个非标准的字符串格式。
当子组件 month.vue 接收到被破坏的 item.date 后,它执行了 dayjs(item.date).format("YYYY-MM-DD"):
- 如果
item.date是空对象{},由于 dayjs 内部解析机制,dayjs({})会直接生成一个非法的日期实例。 - 随后
.format()返回的必然是"Invalid Date"。
这直接导致了你选中的日期本质上被记录为了 "Invalid Date",从而在触发 confirm 拼接字符串(如 e[0] + '~' + e[e.length - 1])时,就回显出了 Invalid Date~Invalid Date。
修复方案
既然我们需要在 formatter 中给开发者保留原生的 Date 对象,但又绝对不能把 Date 对象直接传给子组件,最稳妥的解决办法就是在 formatter 处理完之后,把 config.date 强行恢复为普通的字符串。
我已经修改了uni_modules/uview-ui/components/u-calendar/u-calendar.vue#L306-L311的代码:
const formatter = this.formatter || this.innerFormatter
let res = formatter(config)
// 新增代码:在执行完 formatter 后,将 date 重新转回普通字符串,
// 彻底避免 Date 对象在跨组件传值时被 UniApp 序列化破坏而导致 Invalid Date!
if (res) {
res.date = date
}
return res
这样修改的双赢结果:
- 你的业务代码和自定义
formatter依然可以安全拿到Date对象。 uMonth子组件接收到的item.date会是一个纯粹且标准的"YYYY-MM-DD"字符串,dayjs解析永远不会报错。