From 6cbc0cfc0f2b2202be9ad7d3f7061ed553f141cc Mon Sep 17 00:00:00 2001 From: chenshoulin Date: Thu, 29 Oct 2026 16:09:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=BB=E5=8A=A1=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=88=9B=E5=BB=BA=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 元信息行右侧新增「创建于 M月D日」,与「完成于」互斥:已完成卡片仍只显示 完成日期,避免两个时间挤占同一行把标签推到第二行 - 精确到分钟的时刻保留在 span 的 title 与卡片 title 中 - 「完成于」补 whitespace-nowrap,窄列下不再拆行 - 补充单元测试:待办/进行中显示创建日期、已完成不叠加、createdAt 非法时不渲染 --- src/components/TaskCard.spec.ts | 34 +++++++++++++++++++++++++++++++++ src/components/TaskCard.vue | 19 +++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/components/TaskCard.spec.ts b/src/components/TaskCard.spec.ts index f7a9da6..0f13f7d 100644 --- a/src/components/TaskCard.spec.ts +++ b/src/components/TaskCard.spec.ts @@ -173,6 +173,40 @@ describe('时间信息', () => { expect(mountCard({ completedAt: null }).text()).not.toContain('完成于') }) + it('未完成卡片显示创建日期', () => { + const createdAt = new Date(2026, 9, 8, 9, 30).toISOString() + const wrapper = mountCard({ status: 'todo', createdAt }) + + const badge = wrapper.find('[data-role="created-at"]') + expect(badge.exists()).toBe(true) + expect(badge.text()).toBe('创建于 10月8日') + // 精确时刻放在 title,悬停即可看到 + expect(badge.attributes('title')).toBe('创建于 2026-10-08 09:30') + }) + + it('进行中的卡片同样显示创建日期', () => { + const createdAt = new Date(2026, 9, 20, 14, 0).toISOString() + const badge = mountCard({ status: 'doing', createdAt }).find('[data-role="created-at"]') + + expect(badge.exists()).toBe(true) + expect(badge.text()).toBe('创建于 10月20日') + }) + + it('已完成卡片只显示完成日期,不叠加创建日期', () => { + const createdAt = new Date(2026, 9, 8, 9, 30).toISOString() + const completedAt = new Date(2026, 9, 18, 10, 20).toISOString() + const wrapper = mountCard({ status: 'done', createdAt, completedAt }) + + expect(wrapper.text()).toContain('完成于') + expect(wrapper.find('[data-role="created-at"]').exists()).toBe(false) + }) + + it('创建时间无效时不渲染创建日期', () => { + expect(mountCard({ createdAt: 'not-a-date' }).find('[data-role="created-at"]').exists()).toBe( + false, + ) + }) + it('卡片 title 暴露完整创建与完成时间', () => { const createdAt = new Date(2026, 9, 18, 10, 8).toISOString() const completedAt = new Date(2026, 9, 18, 10, 20).toISOString() diff --git a/src/components/TaskCard.vue b/src/components/TaskCard.vue index cb70089..fd31016 100644 --- a/src/components/TaskCard.vue +++ b/src/components/TaskCard.vue @@ -89,6 +89,14 @@ const hiddenTagCount = computed(() => Math.max(0, props.task.tags.length - MAX_V const completedAtFull = computed(() => formatDateTime(props.task.completedAt)) const completedAtShort = computed(() => formatCompactDate(props.task.completedAt)) +/** + * 创建时间:未完成卡片在元信息行右侧补一行短日期,用来判断这条待办躺了多久。 + * 与「完成于」互斥 —— 已完成卡片右侧已被完成日期占用,再叠一个会把标签挤到第二行, + * 而且回顾已完成任务时更关心「什么时候做完的」。精确到分钟的时刻仍在 `title` 里。 + */ +const createdAtShort = computed(() => formatCompactDate(props.task.createdAt)) +const showCreatedAt = computed(() => !isDone.value && createdAtShort.value !== null) + /** 悬停卡片即可看到完整时间,不必先打开详情抽屉 */ const timeTitle = computed(() => { const lines = [`创建于 ${formatDateTime(props.task.createdAt) ?? '—'}`] @@ -269,10 +277,19 @@ const priorityTextClass = computed(() => { v-if="completedAtShort" data-role="completed-at" :title="`完成于 ${completedAtFull}`" - class="ml-auto text-emerald-700 dark:text-emerald-400" + class="ml-auto whitespace-nowrap text-emerald-700 dark:text-emerald-400" > 完成于 {{ completedAtShort }} + + + 创建于 {{ createdAtShort }} +