news 2026/9/13 19:18:39

WeKan 截止日期(Due Date)机制详解:颜色编码、倒计时与截止日变更追踪

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WeKan 截止日期(Due Date)机制详解:颜色编码、倒计时与截止日变更追踪

WeKan 截止日期(Due Date)机制详解:颜色编码、倒计时与截止日变更追踪

【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan

WeKan 为每张卡片提供了 received(接收)、start(开始)、due(截止)、end(完成)四类日期,围绕其中最具管理价值的“截止日期”,实现了一套由颜色编码、倒计时文案和变更次数追踪组成的可视化提醒体系。本文基于docs/Features/Date/Due-Date.md这一功能文档,结合 WeKan 仓库中的颜色判定逻辑、卡片模板与数据模型源码,完整讲解这套机制的设计意图、实现方式与验证手段。读完本文,你将能够准确理解 WeKan 卡片截止日期的展示规则从何而来,并在定制或排查 UI 问题时快速定位到对应源码。

一、卡片日期体系:四个日期各有其位

功能文档首先给出了一张简洁的日期语义表,它定义了 WeKan 卡片日期字段的四种用途:

  • received— bug 或任务被发现(接收)的时间;
  • start— 工作开始的时间;
  • due— 工作应当完成(截止)的时间;
  • end— 工作实际完成的时间。

这四个日期在数据层都落在卡片(或关联的板)文档上。从 卡片模型 可以看到:

  • getDue()/setDue(dueAt)读写dueAt字段;
  • 若当前卡片是“关联卡片”(linked card)或“关联板”(linked board),读写会透明地转发到真实卡片/板的文档上,保证跨看板引用的日期始终一致;
  • unsetDue()通过$unset移除dueAt字段,卡片上未设置截止日期时该字段为null

编辑入口方面,卡片详情中的 due 区块在 cardDetails.jade 中按$eq field "dueDate"渲染:已有截止日期时展示cardDueDate徽章组件,未设置且当前用户可修改卡片时展示“+”号添加按钮(且受currentBoard.allowsDueDate板级开关控制,并屏蔽 Worker 角色)。点击后打开editCardDueDate弹层,其初始化逻辑在 cardDate.js:

Template.editCardDueDatePopup.onCreated(function () { const card = Template.currentData(); setupDatePicker(this, { defaultTime: '1970-01-01 17:00:00', initialDate: card.getDue() ? card.getDue() : undefined, storeDate(date, currentCard) { return currentCard.setDue(date); }, deleteDate(currentCard) { return currentCard.unsetDue(); }, }); });

其中storeDate/deleteDate分别对接上文提到的setDue/unsetDue,完成了“弹层选择 → 模型写库”的完整链路。此外,cardDate.js 中的formatCardDateForDisplay还会根据用户个人设置把日期显示为 Jalali(波斯太阳历)或按用户偏好的公历格式渲染——这属于纯显示层转换,存储始终保持原生Date(公历)。

二、截止日期颜色编码:红 / 琥珀 / 灰的判定规则

功能文档的核心段落描述了截止日期徽章的颜色语义:

红色(red)= 已逾期(overdue);琥珀色(amber)= 48 小时之内到期;灰色(grey)= 距今超过 48 小时才到期。且列表/迷你卡(minicard)与打开的卡片详情上显示的颜色永远一致

文档同时提到卡片已有 end 日期的情形,源码把这一边界条件补充得更完整。颜色判定被抽成一个无 DOM 依赖的纯函数 dueDateClass,被卡片详情徽章与迷你卡徽章共用,这正是“两个视图颜色永远一致”这一承诺的实现基础:

export function dueDateClass(dueDate, now, endDate) { const due = new Date(dueDate); const nowVal = new Date(now); if (endDate) { const end = new Date(endDate); if (end.getTime() < due.getTime()) { return 'completed-early'; // 提前完成 } return 'completed'; // 按期或逾期完成 } const diffMs = due.getTime() - nowVal.getTime(); const hoursDiff = diffMs / (1000 * 60 * 60); if (hoursDiff < 0) { return 'overdue'; // 红色:已逾期 } else if (hoursDiff <= 48) { return 'due-soon'; // 琥珀色:48 小时内到期 } return 'not-due'; // 灰色:48 小时以后到期 }

完整的规则集可以整理为下表(CSS 类名即徽章样式选择器):

条件返回类名视觉语义
卡片已有 end 日期,且 end 早于 duecompleted-early提前完成
卡片已有 end 日期,且 end 不早于 duecompleted已完成
due 在当前时间之前overdue红色,已逾期
due 在 48 小时之内(含恰好 48 小时)due-soon琥珀色,临近到期
due 在 48 小时之后not-due灰色,尚不紧急

两处值得注意的实现细节:

  1. 48 小时边界是闭区间hoursDiff <= 48意味着恰好 48 小时后的到期日仍算“琥珀色”。这一边界曾被专门做回归测试(见下文测试小节,对应 issue #6000:“超过 48 小时约 1 分钟的将来日期必须显示为灰色”)。
  2. now是作为参数传入的。徽章模板通过 dateNowTicker 订阅一个周期更新的“当前时间”变量(cardDateOnCreatedsubscribeDateNowTicker()),因此一张长期停留的卡片徽章会在跨过 48 小时线时由灰变琥珀、在跨过到期时刻后变红,无需刷新页面。

调用侧见 cardDate.js 与 cardDate.js:cardDueDate(卡片详情)和minicardDueDate(迷你卡)两个模板的classes()helper 均返回`due-date ${dueDateClass(theDate, nowVal, endAt)}`——同一个函数、同一份输入,输出必然一致,从结构上杜绝了两个视图颜色漂移的可能。

三、倒计时文案:“N 天后到期 / N 天前逾期”

在颜色之外,徽章还会在日期后追加一段相对倒计时文案,例如 “Jun 15 (3 days left)” 或 “Jun 15 (Due today)”。这段文案由 dueCountdown 生成:

export function dueCountdown(dueDate, now) { const due = new Date(dueDate); const nowVal = new Date(now); const startOfDay = date => new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime(); const dayMs = 24 * 60 * 60 * 1000; const diffDays = Math.round((startOfDay(due) - startOfDay(nowVal)) / dayMs); if (diffDays === 0) { return { key: 'due-today', days: 0 }; } else if (diffDays > 0) { return { key: 'due-days-left', days: diffDays }; } return { key: 'due-days-overdue', days: -diffDays }; }

关键设计点:

  • 按“日历日”计算而非 24 小时差值:先把两个日期都归零到当天 00:00 再相减。因此“今天稍后到期”的卡片显示 “Due today”,而不是 “0 days left”。
  • 返回 i18n 键而不是文案本身days恒为非负数,方向信息由keydue-today/due-days-left/due-days-overdue)表达。调用点再用TAPi18n.__(key, { count: days })查翻译,见 cardDate.js 中的dueCountdownText。这样dueDateColor.js保持零国际化依赖、易于单测。
  • 与颜色共用同一模块:源码注释明确说明倒计时与徽章颜色“always agree”(总是相互一致),即dueCountdowndueDateClass放在同一文件中共同维护。

当卡片已有 end 日期时,showDate/showTitlehelper 会省略倒计时后缀、只展示 end 日期本身(cardDate.js),避免“已完成却显示 N 天前逾期”的误导性文案。

四、截止日期变更次数:来自活动历史的问责视图

功能文档的最后一节描述了“due date changed N times”(截止日期已修改 N 次)计数:它显示在卡片详情的截止日期旁,数据来源于卡片的活动历史(activity history),目的是让“截止日被改过多少次”可见,便于团队对自己承诺的日期保持问责。

实现上由三部分组成:

  1. 每次变更都会记入活动日志。卡片模型 的注释指出:每一次截止日期的设置/修改都会由服务端before.update钩子写入一条activityType'a-dueAt'的活动记录。
  2. 只读计数方法。模型提供getDueDateChangeCount()
getDueDateChangeCount() { const cardId = this.isLinkedCard() ? this.linkedId : this.getRealId(); const activities = ReactiveCache.getActivities({ cardId, activityType: 'a-dueAt', }); return Array.isArray(activities) ? activities.length : 0; }

它从ReactiveCache中按cardId+activityType: 'a-dueAt'取出活动列表并计数;对关联卡片会先解析出真实卡片 ID,保证跨板引用场景下计数正确。 3.模板层渲染。全局注册的 dueDateChangeCount helper 在卡片对象不可用时返回 0,从而让模板自然隐藏该行;cardDetails.jade 中:

if dueDateChangeCount .card-details-due-date-changes(title="{{_ 'due-date-changes'}}") | {{_ 'due-date-changed-times' dueDateChangeCount}}

仅在计数非 0 时显示,文案键due-date-changed-times已随多语言文件(如 en 源文件 及各语种翻译)全量覆盖。

这条链路“update 钩子 → 活动记录 → ReactiveCache 查询 → 模板 helper → jade 渲染”全部是响应式的:任何人再次修改截止日期,活动集合变化会自动驱动详情面板中的计数更新,无需任何手动刷新逻辑。

五、一致性保障:单元测试与回归

文档强调“minicard 与卡片详情颜色必须一致”“恰好 48 小时算琥珀、超过即灰色”等精确行为,这些约束在仓库中有对应的自动化验证:

  • dueDateColor.tests.js:针对dueDateClass的纯函数单测,覆盖“过去日期 → overdue”“48 小时内 → due-soon”“恰好 48 小时 → due-soon”“48 小时 + 1 分钟 → not-due(#6000 回归)”等边界;
  • dueDateCountdown.test.cjs:针对倒计时文案逻辑的独立测试;
  • 组件层测试(如 cardDate.js 中注释提到的 #6615 数据上下文问题)验证了详情视图传入{ card, canModifyCard }而迷你卡直接继承 Card 对象时,两套调用形态都不会在响应式重跑中互相串扰。

这些测试的存在意味着:如果你在二次开发中调整了 48 小时阈值或颜色语义,client/lib/tests/dueDateColor.tests.js会立即暴露与文档承诺不符的行为。

六、小结

WeKan 的截止日期功能以三个文档描述的特性为主线——四日期语义(received/start/due/end)红/琥珀/灰三档颜色编码截止日变更次数追踪——并在源码中落实为一套“纯函数判定 + 共享模板 + 活动日志计数”的清晰结构:

  • 颜色规则集中在 client/lib/dueDateColor.js,被详情与迷你卡两视图共用,48 小时阈值以小时差闭区间判定;
  • 倒计时按日历日计算并以 i18n 键输出,保证多语言下“Due today / N days left / N days overdue”语义准确;
  • 变更次数依赖服务端对a-dueAt活动的记录,通过 models/cards.js 的只读计数方法暴露给 cardDetails.jade 渲染。

理解这条从文档语义到dueDateClassdueCountdowngetDueDateChangeCount的实现映射,是掌握 WeKan 卡片日期体系、并在定制部署中正确扩展日期展示行为的关键。

【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan

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

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

SSM校园门户网站源码解析:从部署到安全加固的完整实践

简介&#xff1a;本资源是一套基于SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架开发的校园门户网站完整Web应用源码&#xff0c;面向Java初学者与高校课程设计、毕业设计实践者&#xff0c;解决校园信息平台从零搭建与后台管理功能实现的学习需求。压缩包为ZIP格式&…

作者头像 李华
网站建设 2026/9/13 19:11:09

分布式光伏集群电压协调控制方法与Matlab实现

简介&#xff1a;本资源面向电气工程、智能电网方向的本科生与硕士生&#xff0c;聚焦含分布式光伏的配电网集群划分与电压协调控制问题&#xff0c;提供一套完整可复现的Matlab仿真解决方案。包内包含核心算法代码&#xff08;基于K-means聚类与改进一致性协议&#xff09;、多…

作者头像 李华