镜像自地址
https://github.com/lhly/cloud-drive-renamer.git
已同步 2026-10-21 06:01:51 +08:00
- 在平台适配器中统一暴露 getCurrentDirectoryKey() 方法,用于确定当前目录范围 - 新增 LastRenameOperation 数据模型和核心操作函数,支持单条撤销记录的创建、合并和范围校验 - 在 file-selector-panel 中集成撤销逻辑,支持执行后保存记录、retry 成功后合并记录以及撤销操作 - 在 config-panel 底部操作区新增撤销按钮,根据当前目录范围动态显示/隐藏 - 添加多语言支持,包含撤销相关文案 - 添加单元测试和集成测试,确保撤销功能的正确性和稳定性
5.8 KiB
5.8 KiB
Last Batch Undo Design(仅撤销最近一次批量重命名)
Date: 2026-03-18
Goal
在现有批量重命名面板中增加“撤销上一次批量重命名”能力:只回退当前平台、当前目录下最近一次点击“批量重命名”按钮触发的那次批量操作,并允许用户关闭面板查看真实结果后再返回撤销。
Scope
- 在
file-selector-panel中保存 1 条可撤销记录,而不是历史队列。 - 该记录绑定当前平台 + 当前目录。
- 关闭面板后保留记录;只要用户仍在原目录即可执行撤销。
retry不创建新历史,但 retry 成功项要并入这唯一的一条记录。- 在
config-panel中新增单一撤销按钮,不做 split-button / 下拉历史。 - 撤销部分失败时,弹窗列出失败文件并提示用户手动处理。
Non-goals
- 不保留最近 10 次历史。
- 不支持选择任意历史回退。
- 不做跨刷新、跨页面、跨平台持久化撤销。
- 不新增独立弹窗组件;第一版沿用现有
alert/confirm风格提示。
Decisions
- 可撤销记录只有 1 条,新的批量重命名会覆盖旧记录。
- 生命周期采用“当前平台 + 当前目录 + 当前页面会话”的交集:
- 关闭面板不清空;
- 离开当前目录、刷新页面、页面卸载、跨平台访问时失效。
- “离开目录即销毁”第一版采用懒校验:在面板打开、点击撤销、点击批量重命名、点击 retry 时检查当前目录 scope;若 scope 不匹配,则先清空记录。
retry成功项并入当前记录,保证 undo 对应“这次批量操作最终成功改名的全部文件”。- 部分撤销失败后,保留失败项为新的可撤销记录,方便用户在未离开目录前再次点击 undo;同时弹窗提示手动处理建议。
Architecture
1) 目录 scope 抽象
为 PlatformAdapter 增加统一能力:
getCurrentDirectoryKey(): string;
实现方式:
- Quark:复用现有
getCurrentFolderId()。 - Aliyun:复用现有
getParentIdFromUrl()。 - Baidu:复用现有
getCurrentPath()。
file-selector-panel 只依赖 adapter.platform + adapter.getCurrentDirectoryKey() 判断当前撤销记录是否仍在有效 scope 内。
2) 单条撤销记录模型
新增一个轻量数据模型,仅保存 undo 必需信息:
interface UndoRenameItem {
fileId: string;
original: string;
renamed: string;
index: number;
}
interface LastRenameOperation {
platform: PlatformName;
directoryKey: string;
createdAt: number;
updatedAt: number;
items: UndoRenameItem[];
}
只存成功改名项;不存完整 FileItem,避免状态冗余。
3) 记录写入与合并
主执行(点击“批量重命名”)
handleExecute()执行完成后,如果存在成功项:- 用当前
platform + directoryKey + results.success创建一条新的LastRenameOperation; - 覆盖旧记录。
- 用当前
Retry
handleRetryFailed()执行完成后,如果存在成功项:- 先校验当前 scope 是否仍与现有记录一致;
- 一致则把 retry 成功项 merge 进现有记录;
- 若记录已失效,则不再创建新记录(因为 retry 只是补全原操作,不应在丢失上下文后凭空产生新 undo 历史)。
4) 撤销执行
新增 handleUndoLastRename():
- 校验当前 scope;不匹配则清空记录并退出。
- 从当前记录生成反向任务:
fileId不变,newName = original。 - 复用
BatchExecutor执行这些 tasks。 - 成功项通过现有
applyExecutionResults()+syncAfterRename()回写页面与面板状态。 - 若全部成功:清空记录。
- 若部分失败:将记录收窄为失败项,并弹窗提示用户手动处理。
5) UI 变更
config-panel 新增:
canUndo: booleanundoBusy: boolean@undo事件
页脚按钮区新增:
撤销上一次批量重命名
按钮行为:
- 仅当当前 scope 有有效记录时可点击;
- 正在执行批量重命名或撤销时禁用;
- 文案建议加辅助说明:仅针对当前目录最近一次批量重命名。
6) 错误处理
- scope 不匹配:静默清空记录,并隐藏/禁用撤销按钮。
- 撤销零任务:若记录为空或已无可回退项,直接清空记录并返回。
- 部分失败:使用 i18n 头部文案 + 文件列表 + 手动处理提示拼接成
alert()文本。 - 全部失败:记录保留,便于用户在确认真实页面状态后再次尝试。
Data Flow
A. 主执行
点击 Execute
-> handleExecute()
-> BatchExecutor.execute()
-> applyExecutionResults()
-> create LastRenameOperation from results.success
B. Retry
点击 Retry
-> handleRetryFailed()
-> BatchExecutor.execute(tasks=failed only)
-> applyExecutionResults()
-> merge retry success into LastRenameOperation
C. Undo
点击 Undo
-> validate current scope
-> build reverse tasks from LastRenameOperation.items
-> BatchExecutor.execute(tasks=undo)
-> applyExecutionResults()
-> clear record OR keep only failed items
Testing Strategy (TDD)
- 单元测试新建纯函数/帮助模块:
- 从
BatchResults.success创建记录; - retry 成功项 merge;
- scope 校验;
- 部分 undo 失败后保留失败项。
- 从
- 适配器测试覆盖
getCurrentDirectoryKey()。 - 现有依赖
PlatformAdapter的 mock 全部补上getCurrentDirectoryKey()以通过 typecheck。 - 回归验证:
- 普通批量重命名流程不受影响;
- retry 后 undo 能覆盖首次成功项 + retry 成功项;
- 离开目录后 undo 按钮消失/失效。
Rollout
- 第一版优先采用懒校验而不是复杂的实时路由监听。
- 若后续观察到“切目录后按钮残留到下一次交互前”影响体验,再补目录变化监听器。