uview2 u-calendar组件返回值为Invalid Date解决方法

50 阅读2分钟

今天遇到了小程序日历范围组件返回值错误问题,使用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 时:

  1. 可能会变成一个空对象 {}
  2. 或者是变成一个非标准的字符串格式。

当子组件 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

image.png 这样修改的双赢结果:

  1. 你的业务代码和自定义 formatter 依然可以安全拿到 Date 对象。
  2. uMonth 子组件接收到的 item.date 会是一个纯粹且标准的 "YYYY-MM-DD" 字符串,dayjs 解析永远不会报错。