feat(board): 展示任务时间,并修正已完成任务的逾期基准
CI / verify (push) Successful in 1m43s
CI / 部署到服务器 (push) Successful in 26s

时间展示(ADR-008):
- 详情抽屉新增只读「时间」区块:创建 / 更新常驻,完成时间缺失时标注「未完成」,归档任务追加归档时间;时间不进入草稿与 dirty 判定
- 已完成卡片在元信息行右侧显示「完成于 M月D日」,卡片根元素 title 汇总创建 / 完成 / 归档时间(精确到分钟)
- 回收站条目补创建时间,删掉内联 formatTime,统一走 @/utils/date
- date.ts 新增 formatDateTime(YYYY-MM-DD HH:mm)与 formatCompactDate(同年 M月D日 / 跨年带年份),空值与非法时间戳返回 null

逾期基准修复:
- 新增 formatDueDateForTask:未完成任务仍以「今天」推算;已完成任务改以「完成日」推算,19 号截止、19 号当天完成的任务不会在 20 号显示「逾期 1 天」,完成时确实超期则定格为「逾期 N 天完成」,不再随日期增长
- 新增 localDateOf:completedAt 存 UTC 时间戳,按本地时区取日期,避免跨日偏差
- TaskCard 改用 dueLabel computed 取代模板内直接调用 formatDueDate;颜色仍由 isOverdue 决定,已完成卡片保持中性色

测试:date / TaskCard 补时间格式化与逾期基准用例(当天完成、提前完成、完成时超期定格、缺 completedAt、跨年),全量 728 单测通过;vue-tsc 与 eslint 干净。
This commit is contained in:
chenshoulin
2026-10-20 09:23:29 +08:00
parent a269694834
commit b39267fba7
10 changed files with 392 additions and 11 deletions
+52 -1
View File
@@ -81,11 +81,32 @@ describe('截止日期', () => {
it('已完成任务不再标记逾期', () => {
const yesterday = dayjs().subtract(2, 'day').format('YYYY-MM-DD')
const wrapper = mountCard({ dueDate: yesterday, status: 'done' })
const completedAt = dayjs().subtract(1, 'day').hour(10).minute(0).toISOString()
const wrapper = mountCard({ dueDate: yesterday, status: 'done', completedAt })
const badge = wrapper.findAll('span').find((node) => node.text().includes('逾期'))
expect(badge?.text()).toBe('逾期 1 天完成')
expect(badge?.classes().join(' ')).not.toContain('rose')
})
it('当天完成的任务第二天不再显示逾期天数', () => {
// 复现:19 号截止、19 号完成,20 号查看时曾显示「逾期 1 天」
const dueDate = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
const completedAt = dayjs().subtract(1, 'day').hour(10).minute(0).toISOString()
const wrapper = mountCard({ dueDate, status: 'done', completedAt })
expect(wrapper.text()).not.toContain('逾期')
expect(wrapper.text()).toContain('原定')
expect(wrapper.text()).toMatch(/原定 \d+月\d+日/)
})
it('完成时已超期的任务显示「逾期 N 天完成」,不再随日期增长', () => {
const dueDate = dayjs().subtract(3, 'day').format('YYYY-MM-DD')
const completedAt = dayjs().subtract(2, 'day').hour(9).minute(0).toISOString()
const wrapper = mountCard({ dueDate, status: 'done', completedAt })
expect(wrapper.text()).toContain('逾期 1 天完成')
})
it('今天到期显示「今天」', () => {
expect(mountCard({ dueDate: dayjs().format('YYYY-MM-DD') }).text()).toContain('今天')
})
@@ -105,6 +126,36 @@ describe('已完成样式', () => {
})
})
describe('时间信息', () => {
it('已完成卡片显示完成日期', () => {
const completedAt = new Date(2026, 9, 18, 10, 20).toISOString()
const wrapper = mountCard({ status: 'done', completedAt })
expect(wrapper.text()).toContain('完成于')
expect(wrapper.text()).toContain('10月18日')
})
it('未完成卡片不显示完成时间', () => {
expect(mountCard({ completedAt: null }).text()).not.toContain('完成于')
})
it('卡片 title 暴露完整创建与完成时间', () => {
const createdAt = new Date(2026, 9, 18, 10, 8).toISOString()
const completedAt = new Date(2026, 9, 18, 10, 20).toISOString()
const title = mountCard({ status: 'done', createdAt, completedAt })
.find('article')
.attributes('title')
expect(title).toContain('创建于 2026-10-18 10:08')
expect(title).toContain('完成于 2026-10-18 10:20')
})
it('归档任务在 title 中补充归档时间', () => {
const archivedAt = new Date(2026, 9, 19, 8, 0).toISOString()
const title = mountCard({ archivedAt }).find('article').attributes('title')
expect(title).toContain('归档于 2026-10-19 08:00')
})
})
describe('标签展示', () => {
it('最多显示 3 个标签', () => {
const wrapper = mountCard({ tags: ['a', 'b', 'c', 'd', 'e'] })