镜像自地址
https://github.com/lhly/cloud-drive-renamer.git
已同步 2026-10-20 21:57:47 +08:00
更新 package.json、manifest.json 和版本常量文件中的版本号至 1.0.4。 新增冲突检测与对话框优化的详细设计规格文档和实现计划文档,为后续功能开发做准备。
144 行
6.4 KiB
Markdown
144 行
6.4 KiB
Markdown
# 批量重命名冲突误报修复与冲突弹窗优化设计
|
||
|
||
## 背景
|
||
当前批量重命名在执行前会先进行冲突检测,并在检测到冲突时通过浏览器原生 `prompt()` 让用户选择处理方式。现有实现存在两个问题:
|
||
|
||
1. **误报冲突**:未发生改名的文件仍然参与外部重名检测,阿里云盘适配器只按文件名匹配,不排除自身,导致“与自己重名”被误判为冲突。
|
||
2. **交互能力不足**:原生 `prompt()` 无法展示结构化冲突详情,也无法承载滚动列表、说明文本、危险操作强调等 UI。
|
||
|
||
## 目标
|
||
- 修复“未改名文件被误判冲突”的问题。
|
||
- 保持真实冲突检测不变:
|
||
- 批量内部生成相同目标名仍应识别。
|
||
- 当前目录已存在其他同名文件仍应识别。
|
||
- 将原生冲突弹窗升级为扩展内自定义对话框。
|
||
- 在对话框中显示可滚动的冲突明细,至少包含:原文件名、目标文件名、冲突类型。
|
||
- 删除误导性的“强制覆盖”选项,改为用户可安全理解的“取消本次执行”。
|
||
|
||
## 非目标
|
||
- 不改动批量执行器的核心重命名流程。
|
||
- 不新增“手动编辑每个冲突项”的复杂交互。
|
||
- 不重构所有 confirm/alert,仅替换冲突处理相关弹窗。
|
||
|
||
## 方案对比
|
||
|
||
### 方案 A:仅在适配器层排除自身
|
||
- 做法:扩展 `checkNameConflict` 接口,额外传入当前文件 ID,在适配器层排除自己。
|
||
- 优点:语义最准确,冲突判断在数据源层完成。
|
||
- 缺点:需要修改平台接口和多个适配器,改动面更大;对“未改名文件直接应跳过检测”这一业务规则表达不够直接。
|
||
|
||
### 方案 B:执行前仅对“实际会改名”的任务做冲突检测(推荐)
|
||
- 做法:在 `handleExecute()` 中先根据 `newName !== file.name` 过滤出真正需要改名的文件,再对这些任务执行冲突检测和后续执行计划构建。
|
||
- 优点:
|
||
- 直接匹配产品语义:“没改名就不应参与改名冲突检测”。
|
||
- 改动范围小,平台接口无需变动。
|
||
- 与现有 `skipUnchanged` 行为一致,避免检测和执行语义不一致。
|
||
- 缺点:如果未来需要对“未改名文件”做别的预检查,需要单独扩展逻辑。
|
||
|
||
### 方案 C:保留现有检测范围,但在冲突结果合并后剔除 unchanged 自冲突
|
||
- 做法:检测后再根据 `newName === oldName` 把相关冲突结果清理掉。
|
||
- 优点:改动集中。
|
||
- 缺点:是事后修补,逻辑绕,不如方案 B 清晰。
|
||
|
||
**采用方案:B。**
|
||
|
||
## 详细设计
|
||
|
||
### 1. 冲突检测修复
|
||
在 `FileSelectorPanel.handleExecute()` 中新增“候选任务”概念:
|
||
|
||
- 从 `selectedFiles` 和 `newNames` 派生出 `renameCandidates`。
|
||
- 只有 `newName !== file.name` 的候选项才参与:
|
||
- 冲突检测
|
||
- 冲突解决
|
||
- 执行计划
|
||
- 当候选项为空时,继续沿用现有 `no_rename_needed` 提示。
|
||
|
||
这样可以保证:
|
||
- 预览、冲突检测、执行三者都只关注实际要改名的文件。
|
||
- 不再出现“预览里没显示,但执行阶段又报冲突”的割裂体验。
|
||
|
||
### 2. 冲突详情数据模型
|
||
在 `conflict-detector.ts` 中补充冲突详情结构,面向 UI 传递:
|
||
|
||
- 文件 ID
|
||
- 原文件名
|
||
- 目标文件名
|
||
- 冲突类型:
|
||
- `DUPLICATE_IN_BATCH`
|
||
- `NAME_EXISTS`
|
||
- 相关冲突文件名列表(若适用)
|
||
|
||
新增一个将 `Map<string, ConflictResult>` + 文件列表 + 新名称列表整理为 UI 明细数组的辅助函数,供对话框渲染使用。
|
||
|
||
### 3. 冲突对话框 UI
|
||
新增一个 Lit 组件,例如 `conflict-resolution-dialog`,风格对齐现有 `rename-dialog` / `progress-dialog`:
|
||
|
||
#### 布局
|
||
- 标题:检测到 N 个文件名冲突
|
||
- 说明:提示用户先查看冲突详情,再选择处理方式
|
||
- 可滚动详情区域:
|
||
- 每项显示
|
||
- 原文件名
|
||
- 箭头/目标文件名
|
||
- 冲突类型标签
|
||
- 若为批量内部冲突,可显示“同批次重复目标名”及相关文件名
|
||
- 底部操作区:
|
||
- 自动添加编号(主按钮,推荐)
|
||
- 跳过冲突文件
|
||
- 取消本次执行
|
||
|
||
#### 交互
|
||
- 点击遮罩或取消本次执行:返回 `null`
|
||
- 点击“自动添加编号”或“跳过冲突文件”:返回对应 `ConflictResolution`
|
||
- 移除 `ConflictResolution.OVERWRITE` 及相关误导文案,避免让用户误以为扩展具备删除/覆盖网盘同名文件的能力
|
||
|
||
### 4. 集成方式
|
||
在 `file-selector-panel.ts` 中以状态驱动方式集成:
|
||
|
||
- 增加对话框开关与冲突详情状态。
|
||
- `handleExecute()` 检测到冲突时:
|
||
- 先保存待执行上下文
|
||
- 打开冲突对话框
|
||
- 等用户选择后再继续执行
|
||
- 为减少侵入,可将“等待用户选择”封装成 `Promise<ConflictResolution | null>`,由对话框按钮事件 resolve。
|
||
|
||
### 5. 国际化
|
||
为三套语言文案新增/补充分组 key:
|
||
- 对话框标题、说明、副标题
|
||
- 按钮文案
|
||
- 冲突类型标签
|
||
- 明细字段标签:原文件名、目标文件名、冲突文件
|
||
- 取消执行提示文案
|
||
|
||
## 测试策略
|
||
|
||
### 单元测试
|
||
1. `FileSelectorPanel.handleExecute()` 相关行为:
|
||
- 当部分文件未变化时,仅变化文件参与冲突检测。
|
||
- 未变化文件不会触发误报冲突。
|
||
2. `conflict-detector`:
|
||
- 批量内部重复名仍可正确生成冲突详情。
|
||
- 冲突详情整理函数输出正确。
|
||
3. 冲突对话框组件:
|
||
- 渲染冲突条目
|
||
- 点击不同按钮返回对应 resolution
|
||
|
||
### 回归验证
|
||
- 真实替换场景:仅 7 个变化文件,无重复时不弹冲突框。
|
||
- 存在真实重名时:弹窗能列出冲突文件,并能按选择继续处理。
|
||
|
||
## 风险与缓解
|
||
- **风险:** 改成异步自定义对话框后,执行流程可能被打断。
|
||
- **缓解:** 用 `Promise` 封装选择流程,保持 `handleExecute()` 线性。
|
||
- **风险:** 新增文案导致多语言缺失。
|
||
- **缓解:** 同步更新 `zh_CN` / `zh_TW` / `en`。
|
||
- **风险:** 冲突数量统计与 UI 列表不一致。
|
||
- **缓解:** 统一由冲突详情数组派生数量,而不是重复计算。
|
||
|
||
## 成功标准
|
||
- 用户给出的样例不再出现“检测到 2 个冲突”的误报。
|
||
- 真正冲突时会显示自定义对话框,而不是浏览器原生 prompt。
|
||
- 对话框中能滚动查看冲突文件列表。
|
||
- 自动加序号 / 跳过 两种处理策略继续可用,且可以安全取消本次执行。
|