康复计划里常有“每年这个时候复查一次”。用户滚到一个日子,看起来只是挑了个日期,但落到每一年,它可能是三个不同的结果。选择 2 月 29 日就是最典型的例子:2028 年有这一天,2027 和 2029 年没有。

分析:组件只交出月日,跨年的事得自己算

DatePickerComponent 从 API 26.0.0 起提供,属于 Stage 模型组件。把 displayMode 设为 DisplayMode.DATE、dateOptions.mode 设为 DateMode.MONTH_AND_DAY,界面就只显示两列滚轮:月和日。

SDK 声明里写得很清楚:这个模式下,月份从 12 月滚到 1 月时年份不增加,从 1 月滚到 12 月时年份也不减少,年份保持在当前设定值。换句话说,用户选中的是一个“月日”,组件并没有替业务回答“明年这一天是哪天”。

这就带来第一个决策点:2 月 29 日在平年不存在,应用不能悄悄改成 2 月 28 日或 3 月 1 日。提前一天还是顺延一天,会改变复查间隔,也会影响医生排班和用户预期,必须来自门诊的明确规则,而不是代码里的顺手兜底。

第二个决策点更容易被忽略:年度复查只是提醒锚点,不是真实预约。号源、停诊、院区安排每年都会变,一次选择不应该被理解成永久占位。页面能做的,是把“每年哪一天提醒”记清楚,把“那一年具体约到哪天”留给预约服务逐年确认。

年度复查的三层数据(示意图)

· 年度复查的三层数据——选中的月日、门诊规则、逐年投影各管一段。

功能描述:锚点、门诊规则、年度投影分开存

康复随行的年度功能复查页把数据拆成三层。第一层是锚点,只记月和日;第二层是门诊规则,说明平年怎么落;第三层是年度投影,把锚点按规则展开成未来三年。

页面进来先看门诊规则是否同步。没同步时,三次预览一律显示“待门诊确认”,保存按钮置灰,说明文字写“同步门诊规则后,才知道平年落在哪一天”。这里没有拿 2 月 28 日先顶上,也没有把 3 月 1 日当成默认值。

同步之后,2 月 29 日的锚点展开成:2027 年 2 月 28 日、2028 年 2 月 29 日、2029 年 2 月 28 日。平年那两年标注“平年 · 二月末”,闰年保持原样。用户看到的是三个具体日期,不是一句“按规则处理”。

保存是单向的。函数里先查两件事:2 月 29 日的锚点是否已有门诊规则、当前锚点是否已经保存过。两个条件都通过才写入,按钮文案同时变成“已保存”并禁用。这个禁用只是视觉门,函数内会再查一遍,别的入口直接调用也写不进去。

改日期会让已保存状态立即失效。用户把锚点从 2 月 29 日换到 2 月 28 日,页面马上提示“换成 2 月 28 日了,这个日子每年都有”,保存按钮重新可点,预览也不再需要平年规则。旧锚点的保存状态没有被带到新日期上。

组件结果里的月份是零基的,0 表示 1 月。业务侧如果一处加 1、一处不加,就会出现“选了 2 月、存成 3 月”的错位。页面统一用 anchorText() 转换,其他函数只拿转换后的文本,避免两边各写一套。

案例:从“还没同步”到保存两次

进入页面时,门诊规则是“还没同步”,日期滚轮停在 2 月 29 日,三次复查全部写着“待门诊确认”,保存按钮是灰的。此时的反馈是“先同步门诊规则,才能确认平年怎么安排”。

点一下“同步门诊规则”,状态变成“已同步”,三次复查立刻展开成 2027 年 2 月 28 日、2028 年 2 月 29 日、2029 年 2 月 28 日,说明改成“平年没有 2 月 29 日,门诊规则把这一天落到二月末”。保存按钮这时才亮起来。

点“保存复查日期”,按钮变成“已保存”并禁用,反馈写“已保存:每年 2 月 29 日提醒你复查”。这一步只写入了提醒锚点,页面底部那句“这里设的是提醒日期,不会直接占用号源”始终在。

接着把日期从 29 日改到 28 日。三次复查变成清一色的 2 月 28 日,说明换成“这个日子每年都有,不需要平年规则”,已保存状态被清掉,按钮回到“保存复查日期”可点状态。这一步是故意的:换了锚点,旧保存就不该继续生效。

再保存一次,反馈变成“已保存:每年 2 月 28 日提醒你复查”,按钮重新变灰。整个过程里,组件只负责把月日滚出来,哪一年是哪天、平年怎么落、保存过没有,全部由页面判断。

边界都落在状态上:规则没同步时保存不可点;保存之后按钮禁用;改日期才恢复可点。如果用户在规则缺失时通过别的入口调用保存,函数里的第一道检查仍然会拒绝,不会写出一条“2 月 29 日、平年未知”的提醒。

运行截图

门诊规则未同步时三次复查全部待确认

· 门诊规则未同步时三次复查全部待确认——保存按钮保持灰色。

同步门诊规则后展开三年日期

· 同步门诊规则后展开三年日期——2027、2029 落到二月末,2028 保持 2 月 29 日。

保存 2 月 29 日锚点后按钮锁定

· 保存 2 月 29 日锚点后按钮锁定——“已保存”不可再点。

改到 2 月 28 日后旧保存失效

· 改到 2 月 28 日后旧保存失效——说明改成“不需要平年规则”。

2 月 28 日再次保存成功

· 2 月 28 日再次保存成功——每年都有这一天,平年规则退出计算。

代码片段:滚轮只给月日,投影自己算

组件配置只负责月日输入和稳定结果回调:

DatePickerComponent({
  options: {
    displayMode: DisplayMode.DATE,
    dateOptions: {
      mode: DateMode.MONTH_AND_DAY,
      selected: new Date(2028, this.selectedMonth, this.selectedDay),
      enableHapticFeedback: false,
      onScrollStop: (result: DatePickerComponentResult) => {
        this.onDateSettled(result);
      }
    }
  }
})

逐年投影不猜,按门诊规则分开处理:

private resolveForYear(year: number): string {
  if (!this.isLeapDay()) {
    return this.anchorText();
  }
  if (!this.clinicSynced) {
    return '待门诊确认';
  }
  return this.isLeapYear(year)
    ? '2月29日'
    : '2月28日(平年 · 二月末)';
}

保存先查规则和重复,再落下状态:

private saveRule(): void {
  if (this.needsClinicRule()) {
    this.feedback = '门诊规则还没同步,2 月 29 日先不保存。';
    return;
  }
  if (this.savedAnchor === this.anchorKey()) {
    this.feedback = '这个日子已经保存过,不用重复保存。';
    return;
  }
  this.savedAnchor = this.anchorKey();
  this.feedback = `已保存:每年${this.anchorText()}提醒你复查。`;
}

三段代码的分工同样清楚:组件交出月日,resolveForYear() 决定平年落点,saveRule() 管住写入时机。任何一段都不需要知道另外两段之外的东西。

总结

MONTH_AND_DAY 解决的是“怎么让用户选出 2 月 29 日”,不是“每年 2 月 29 日怎么办”。组件明确保持年份不变,跨年解析就必然落在业务层。

这个页面把月日锚点、门诊规则和年度投影拆开:规则没同步时不显示具体日期,保存前再查一次,改日期让旧保存失效。页面已构建并安装到模拟器,五种状态都实际点过;真实门诊规则服务、时区与节假日处理、号源确认和持久化还没接,界面状态不等于预约成功。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐