【听见课堂 HarmonyOS NEXT 实战系列 46】不接系统日历,也能做一周任务视图
【听见课堂 HarmonyOS NEXT 实战系列 46】不接系统日历,也能做一周任务视图
任务中心需要“一周视图”,并不意味着应用必须先申请系统日历权限。听见课堂的 P09 页面只围绕本地已确认任务生成周历:Service 统一计算日期键,ArkUI 只负责展示和组合筛选,系统日历里不会被静默写入任何事件。

一、先确认需求:查看任务,不等于写入系统日历
当前目标是让用户快速判断本周哪天有任务、哪天存在逾期项,并能点击某天缩小列表范围。数据已经保存在应用自己的 RelationalStore 中,因此无需把同一份任务复制到系统日历。
只有当产品要创建系统事件、读取用户既有日程或与其他日历应用协同时,才需要评估 Calendar Kit、权限说明、拒绝路径和数据同步冲突。
二、周历的数据源必须是正式任务快照
getTaskCenterSnapshot() 先过滤 confirmed=true 的任务,再解析截止时间、过期状态和日期键。候选任务不会因为带有“明天”之类文本就出现在周历中。
这使周历继续服从前文建立的人工确认边界:自动规则只能提出建议,正式任务视图只消费用户确认后的 canonical data。
三、为什么要先固定同一个 nowMillis
Service 在一次快照中只调用一次 Date.now(),再把相同的 nowMillis 传给截止时间解析、是否逾期、日期分组和周历构建。
如果每个步骤各自取时间,恰好跨过午夜时可能出现“列表认为今天、周历认为昨天”的短暂不一致。固定时间基准是一个成本很低但很重要的确定性设计。
四、周一作为一周起点如何计算
startOfWeek() 先把本地时间归零到 00:00,再根据 getDay() 计算偏移。JavaScript 中周日是 0,因此周日需要回退 6 天,其余日期使用 1 - weekday 回到周一。
const weekday: number = value.getDay();
const offset: number = weekday === 0 ? -6 : 1 - weekday;
return value.getTime() + offset * DAY_MILLIS;
这段规则明确选择“周一到周日”,与页面中文习惯保持一致。
五、七天不是七个硬编码按钮
buildWeekDays() 从周一起点循环 7 次,为每一天生成 TaskCalendarDay。模型包含 dateKey、星期标签、日期数字、是否今天、任务数和过期数。
页面只是 ForEach(calendarDays),不用维护七份重复状态,也不会把业务统计散落进 Builder。
六、dateKey 是列表与周历的连接键
任务在映射为 TaskCenterItem 时就得到形如 YYYY-MM-DD 的 dateKey。周历生成每一天时也调用同一个 TaskDateResolver.dateKey()。
两边用相同规则形成稳定连接,避免页面拿“周一”“今天”这类展示文案做数据匹配。
七、一天的任务数和过期数怎样聚合
每个日历格先筛选 item.dateKey === dateKey 的任务,再分别计算总数和 overdue=true 的数量。
const dayItems = items.filter((item: TaskCenterItem) => item.dateKey === dateKey);
const taskCount = dayItems.length;
const overdueCount = dayItems.filter((item: TaskCenterItem) => item.overdue).length;
统计来自同一批列表项,因此不会出现周历角标和任务列表各算一套口径。
八、今天标识不能只依赖颜色
P09 用边框和颜色突出今天,同时在格子内部显示“今天”,并提供完整的 accessibilityText:星期、日期、任务数、过期数和当前是否选中。
颜色只是视觉增强,文字与无障碍语义才是状态信息的可靠载体。

九、点击日期后如何组合筛选
selectTaskCalendarDay() 保存所选 dateKey,并把日期筛选切换为“今天”或“本周”。真正的列表过滤同时检查状态、日期分组和精确日期键。
因此“已过期 + 周三”会取两者交集,而不是后一次点击覆盖前一次条件。
十、为什么本周筛选还要包含今天
任务的 dateGroup 把当天标记为“今天”,本周其他日期标记为“本周”。所以页面在选择“本周”时显式兼容 dateGroup === '今天'。
这是展示分组与筛选集合不同的典型例子:今天既是独立快捷分组,也属于自然意义上的本周。
十一、空态要解释是“组合后为空”
某天可能有任务,但叠加“已完成”状态后没有结果。页面显示“所选日期暂无符合当前状态筛选的任务”,而不是笼统说“没有任务”。
准确空态能帮助用户理解数据没有丢失,只是当前筛选组合没有命中。
十二、手机和大屏共享同一份周历数据
手机端周历横向滚动,每格固定宽度;大屏端七格按可用空间等分。两种布局都消费同一个 calendarDays,选择逻辑和筛选逻辑没有分叉。
响应式差异留在 ArkUI 布局层,日期计算留在 Resolver,符合页面与业务规则分离的原则。
十三、未知截止时间为什么不进入具体日期格
无法解析的截止文本会得到 dueAtMillis=0 和空 dateKey,仍可出现在任务列表的“未指定”分组,但不会被硬塞进本周任意一天。
这比猜一个日期更诚实,也提醒后续产品补充人工修订入口。
十四、当前实现有哪些时间边界
周历以本地时区和固定 24 小时毫秒数推进。对当前中国本地课堂场景可用,但若未来支持存在夏令时切换的地区,应改为逐日操作本地 Date,避免跨 DST 时产生小时偏移。
当前周视图也只有本周,没有前后周翻页;这属于产品范围,不应描述为完整日历系统。
十五、什么时候才值得接入 Calendar Kit
当需求升级为“把确认任务写入系统日历”“读取冲突日程”“跨应用提醒”时,才进入 Calendar Kit 方案评估。届时必须补充权限申请时机、拒绝后继续使用本地周历、重复事件幂等键和撤销同步策略。
在此之前,本地周历更简单、更少权限,也更符合课堂隐私数据最小化原则。
十六、总结
一周任务视图的核心不是系统日历 API,而是统一时间基准、稳定 dateKey、明确的周起点和可组合筛选。听见课堂用本地 Resolver 与任务快照完成七天映射,让手机和大屏共享业务语义,同时保留未来接入 Calendar Kit 的清晰边界。
下一篇继续分析:完成与撤销完成为什么不能只是一个没有数据门禁的 Toggle。
更多推荐
所有评论(0)