From ede4bfe88f9314a9559dd9860035346a2afc6d18 Mon Sep 17 00:00:00 2001 From: chenshoulin Date: Mon, 19 Oct 2026 17:20:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(data):=20=E8=A6=86=E7=9B=96=E5=BC=8F?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E4=B8=8E=E5=BF=AB=E7=85=A7=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E5=89=8D=E8=87=AA=E5=8A=A8=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - importJson / restoreSnapshot 覆盖前先写一份当前数据快照,误操作可回退 - 导入校验增加 id 唯一性检查,宽容读取路径保留首条 - 快照索引对同一时间点的重复写入去重 - 落盘失败时翻转 persistent,恢复后自动撤除告警 - 备份面板提示导入前数据已自动备份 --- src/components/BackupPanel.spec.ts | 1 + src/components/BackupPanel.vue | 14 ++++- src/data/schema.spec.ts | 23 +++++++ src/data/schema.ts | 23 +++++-- src/data/snapshot.spec.ts | 11 ++++ src/data/snapshot.ts | 13 +++- src/stores/tasks.spec.ts | 99 +++++++++++++++++++++++++++++- src/stores/tasks.ts | 44 ++++++++++++- 8 files changed, 214 insertions(+), 14 deletions(-) diff --git a/src/components/BackupPanel.spec.ts b/src/components/BackupPanel.spec.ts index 77203bd..85b730a 100644 --- a/src/components/BackupPanel.spec.ts +++ b/src/components/BackupPanel.spec.ts @@ -118,6 +118,7 @@ describe('BackupPanel 导入', () => { expect(store.tasks.map((task) => task.title).sort()).toEqual(['新任务一', '新任务二']) expect(wrapper.text()).toContain('已从 backup.json 导入 2 个任务') + expect(wrapper.text()).toContain('导入前的数据已自动备份') }) it('取消确认后数据保持不变', async () => { diff --git a/src/components/BackupPanel.vue b/src/components/BackupPanel.vue index 7b7f493..f68c0d1 100644 --- a/src/components/BackupPanel.vue +++ b/src/components/BackupPanel.vue @@ -59,9 +59,13 @@ function confirmImport(): void { const target = pending.value if (!target) return + // 记录导入前的数量,用于提示「旧数据已自动备份」是否真的发生了 + const beforeCount = store.tasks.length + try { const count = store.importJson(target.text) - message.value = `已从 ${target.fileName} 导入 ${count} 个任务` + const backupNote = beforeCount > 0 ? ',导入前的数据已自动备份' : '' + message.value = `已从 ${target.fileName} 导入 ${count} 个任务${backupNote}` error.value = null } catch (cause) { error.value = cause instanceof Error ? cause.message : '导入失败' @@ -71,9 +75,12 @@ function confirmImport(): void { } function restore(key: string): void { + const beforeCount = store.tasks.length try { const count = store.restoreSnapshot(key) - message.value = count > 0 ? `已从备份恢复 ${count} 个任务` : '该备份是空的,未做改动' + const backupNote = beforeCount > 0 ? ',恢复前的数据已自动备份' : '' + message.value = + count > 0 ? `已从备份恢复 ${count} 个任务${backupNote}` : '该备份是空的,未做改动' error.value = null } catch (cause) { error.value = cause instanceof Error ? cause.message : '恢复失败' @@ -137,7 +144,8 @@ function formatTime(iso: string): string { >

将从「{{ pending.fileName }}」导入 {{ pending.count }} 个任务, - 并覆盖当前全部 {{ store.tasks.length }} 个任务。此操作不可撤销。 + 并覆盖当前全部 {{ store.tasks.length }} 个任务。 覆盖前会自动把当前数据 + 存一份到下方「自动备份」,选错文件也能从那里恢复。