From f69a24de949bb45e49650c756c6cec6761dd272c Mon Sep 17 00:00:00 2001 From: chenshoulin Date: Mon, 19 Oct 2026 17:20:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E8=AF=A6=E6=83=85=E6=8A=BD?= =?UTF-8?q?=E5=B1=89=E6=94=AF=E6=8C=81=E7=8A=B6=E6=80=81=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E3=80=81=E5=BD=92=E6=A1=A3=E6=81=A2=E5=A4=8D=E4=B8=8E=E6=9C=AA?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增三列状态切换,今日视图也能把任务移到进行中 - 归档任务显示归档提示与「恢复」入口 - 关闭抽屉前检查未保存修改,二次确认后才丢弃草稿 --- src/components/TaskDetailDrawer.spec.ts | 80 +++++++++++++- src/components/TaskDetailDrawer.vue | 140 +++++++++++++++++++++--- 2 files changed, 203 insertions(+), 17 deletions(-) diff --git a/src/components/TaskDetailDrawer.spec.ts b/src/components/TaskDetailDrawer.spec.ts index 32cb1de..a7c449e 100644 --- a/src/components/TaskDetailDrawer.spec.ts +++ b/src/components/TaskDetailDrawer.spec.ts @@ -46,9 +46,9 @@ describe('TaskDetailDrawer 回填', () => { expect((wrapper.find('input[aria-label="标题"]').element as HTMLInputElement).value).toBe( '原标题', ) - expect( - (wrapper.find('textarea[aria-label="描述"]').element as HTMLTextAreaElement).value, - ).toBe('原描述') + expect((wrapper.find('textarea[aria-label="描述"]').element as HTMLTextAreaElement).value).toBe( + '原描述', + ) expect(wrapper.text()).toContain('#设计') expect((wrapper.find('input[type="date"]').element as HTMLInputElement).value).toBe( '2026-12-01', @@ -125,12 +125,31 @@ describe('TaskDetailDrawer 保存', () => { }) describe('TaskDetailDrawer 取消', () => { - it('取消不产生脏数据', async () => { + it('没有修改时取消直接关闭', async () => { + const { wrapper } = setup() + + await buttonByText(wrapper, '取消')?.trigger('click') + + expect(wrapper.text()).not.toContain('有未保存的修改') + expect(wrapper.emitted('close')).toHaveLength(1) + }) + + it('有未保存修改时先确认,放弃后才关闭且不产生脏数据', async () => { const { store, wrapper, taskId } = setup() await wrapper.find('input[aria-label="标题"]').setValue('改了一半') await buttonByText(wrapper, '取消')?.trigger('click') + // 还没有关闭,先给出确认 + expect(wrapper.emitted('close')).toBeUndefined() + expect(wrapper.text()).toContain('有未保存的修改') + + await buttonByText(wrapper, '继续编辑')?.trigger('click') + expect(wrapper.text()).not.toContain('有未保存的修改') + + await buttonByText(wrapper, '取消')?.trigger('click') + await buttonByText(wrapper, '放弃修改')?.trigger('click') + expect(store.tasks.find((item) => item.id === taskId)?.title).toBe('原标题') expect(wrapper.emitted('close')).toHaveLength(1) }) @@ -140,6 +159,7 @@ describe('TaskDetailDrawer 取消', () => { await wrapper.find('input[aria-label="标题"]').setValue('临时草稿') await buttonByText(wrapper, '取消')?.trigger('click') + await buttonByText(wrapper, '放弃修改')?.trigger('click') // 模拟真实的「关闭再打开」:BoardView 会把 editingId 置空再设回, // 因此 prop 会经历 null -> task 的变化并触发 watcher @@ -153,6 +173,58 @@ describe('TaskDetailDrawer 取消', () => { }) }) +describe('TaskDetailDrawer 状态', () => { + it('可以把任务从待办改为进行中并保存', async () => { + const { store, wrapper, taskId } = setup() + + await buttonByText(wrapper, '进行中')?.trigger('click') + await buttonByText(wrapper, '保存')?.trigger('click') + + expect(store.tasks.find((item) => item.id === taskId)?.status).toBe('doing') + }) + + it('切到已完成会写入 completedAt', async () => { + const { store, wrapper, taskId } = setup() + + await buttonByText(wrapper, '已完成')?.trigger('click') + await buttonByText(wrapper, '保存')?.trigger('click') + + const task = store.tasks.find((item) => item.id === taskId) + expect(task?.status).toBe('done') + expect(task?.completedAt).not.toBeNull() + }) + + it('修改后显示未保存标记,保存后消失', async () => { + const { store, wrapper, taskId } = setup() + + expect(wrapper.text()).not.toContain('未保存') + + await wrapper.find('input[aria-label="标题"]').setValue('改过的标题') + expect(wrapper.text()).toContain('未保存') + + await buttonByText(wrapper, '保存')?.trigger('click') + + // 保存后 store 里的任务已更新,抽屉被关闭,草稿不再算脏 + expect(store.tasks.find((item) => item.id === taskId)?.title).toBe('改过的标题') + }) +}) + +describe('TaskDetailDrawer 归档', () => { + it('归档任务显示恢复入口,点击后取消归档', async () => { + const { store, toast, wrapper, taskId } = setup() + + store.archiveTasks([taskId]) + await wrapper.setProps({ task: store.tasks.find((item) => item.id === taskId) ?? null }) + + expect(wrapper.text()).toContain('已归档') + + await buttonByText(wrapper, '恢复')?.trigger('click') + + expect(store.tasks.find((item) => item.id === taskId)?.archivedAt).toBeNull() + expect(toast.toasts[toast.toasts.length - 1]?.message).toContain('已恢复') + }) +}) + describe('TaskDetailDrawer 删除与撤销', () => { it('删除后任务消失并弹出可撤销提示', async () => { const { store, toast, wrapper } = setup() diff --git a/src/components/TaskDetailDrawer.vue b/src/components/TaskDetailDrawer.vue index 634f228..5f15e5f 100644 --- a/src/components/TaskDetailDrawer.vue +++ b/src/components/TaskDetailDrawer.vue @@ -1,9 +1,9 @@