TraceStudio-dev/docs/web1.1_filemanager/FEATURE_FILE_OVERWRITE_DIALOG.md

294 lines
7.7 KiB
Markdown
Raw Normal View History

# 文件覆盖保护功能
## 🎯 功能概述
为保存工作流功能添加了文件覆盖保护机制。当保存的工作流文件已存在时,系统会弹出友好的对话框,让用户选择如何处理。
**更新版本**: v0.2.2.1
**更新日期**: 2026-01-07
**涉及文件**: `web/src/stores/useStore.ts`
---
## ✨ 功能特性
### 保存工作流时的智能检测
当用户点击保存工作流按钮时:
1. **检查文件是否存在** - 调用 `GET /api/files/info` 检查目标路径
2. **文件不存在** - 直接保存
3. **文件已存在** - 弹出选择对话框
### 对话框选项
```
⚠️ 文件已存在
工作流 数据分析.utrace 已存在。
请选择操作:
┌─────────────────────────┐
│ 🔄 覆盖现有文件 │ → 直接覆盖,不改变文件名
├─────────────────────────┤
│ ✏️ 重命名后保存 │ → 提示输入新名称
├─────────────────────────┤
│ ❌ 取消 │ → 取消保存操作
└─────────────────────────┘
```
---
## 🎨 UI 设计
### 对话框样式
- **背景遮罩**: 半透明黑色 `rgba(0, 0, 0, 0.7)`
- **模态框**: 深色主题 `#1e293b`,圆角 12px
- **按钮**: 不同颜色区分操作
- 覆盖: 红色 `#ef4444`
- 重命名: 绿色 `#22c55e`
- 取消: 灰色 `#94a3b8`
- **交互**: 悬停时按钮上移,带阴影
### 键盘支持
- **ESC 键**: 取消操作,关闭对话框
---
## 📖 使用流程
### 场景 1: 覆盖现有文件
```
步骤 1: 编辑工作流,标题为"数据分析"
步骤 2: 点击 💾 保存按钮
步骤 3: 检测到 数据分析.utrace 已存在
步骤 4: 弹出对话框,用户选择"🔄 覆盖现有文件"
步骤 5: 文件被覆盖,显示 "✅ 工作流已保存到: users/guest/workflows/数据分析.utrace"
```
### 场景 2: 重命名后保存
```
步骤 1: 编辑工作流,标题为"数据分析"
步骤 2: 点击 💾 保存按钮
步骤 3: 检测到 数据分析.utrace 已存在
步骤 4: 弹出对话框,用户选择"✏️ 重命名后保存"
步骤 5: 弹出输入框: "请输入新的工作流名称: 数据分析"
步骤 6: 用户输入 "数据分析_v2"
步骤 7: 工作流标题自动更新为 "数据分析_v2"
步骤 8: 保存为 数据分析_v2.utrace
```
### 场景 3: 取消保存
```
步骤 1: 点击 💾 保存按钮
步骤 2: 检测到文件已存在
步骤 3: 弹出对话框,用户选择"❌ 取消"或按 ESC 键
步骤 4: 对话框关闭,不执行任何保存操作
```
---
## 🔧 技术实现
### 文件存在检查
```typescript
// 1. 构建文件完整路径
const filename = `${workflowTitle.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g, '_')}.utrace`
const fullPath = `users/${currentUser}/workflows/${filename}`
// 2. 调用 API 检查文件是否存在
const checkResponse = await fetch(
`http://127.0.0.1:8000/api/files/info?path=${encodeURIComponent(fullPath)}`
)
const fileExists = checkResponse.ok
```
### 动态对话框创建
使用原生 DOM API 创建模态对话框,避免依赖 React 组件:
```typescript
const choice = await new Promise<'overwrite' | 'rename' | 'cancel'>((resolve) => {
// 创建遮罩层
const dialog = document.createElement('div')
dialog.style.cssText = '...'
// 创建模态框
const modal = document.createElement('div')
modal.innerHTML = `
<button id="btn-overwrite">🔄 覆盖现有文件</button>
<button id="btn-rename">✏️ 重命名后保存</button>
<button id="btn-cancel">❌ 取消</button>
`
// 绑定事件
modal.querySelector('#btn-overwrite').addEventListener('click', () => {
document.body.removeChild(dialog)
resolve('overwrite')
})
// ESC 键支持
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') resolve('cancel')
})
})
```
### 处理用户选择
```typescript
if (choice === 'cancel') {
return // 直接返回,不保存
}
if (choice === 'rename') {
const newName = prompt('请输入新的工作流名称:', currentTitle)
if (newName) {
filename = `${newName.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g, '_')}.utrace`
setWorkflowTitle(newName) // 更新标题
}
}
// choice === 'overwrite' 时,继续使用原文件名保存
```
---
## 🚀 优势
### 用户体验
- ✅ 防止意外覆盖重要工作流
- ✅ 友好的视觉提示和清晰的选项
- ✅ 支持快速重命名,无需重新编辑标题
- ✅ 键盘快捷键支持ESC 取消)
### 技术优势
- ✅ 使用 `GET /api/files/info` 检查,轻量高效
- ✅ Promise 封装,代码逻辑清晰
- ✅ 纯 JavaScript 实现,无额外依赖
- ✅ 自动清理 DOM避免内存泄漏
### 兼容性
- ✅ 向后兼容,不影响现有功能
- ✅ 在后端 API 不可用时优雅降级(直接保存)
---
## 📊 对比其他方案
| 方案 | 优点 | 缺点 |
|------|------|------|
| **直接覆盖** | 实现简单 | ❌ 可能丢失数据 |
| **自动重命名** | 避免覆盖 | ❌ 文件名混乱(如 file_1, file_2 |
| **询问用户** ✅ | 用户可控 | 需要额外交互 |
---
## 🛠️ 扩展建议
### 短期优化
- [ ] 显示文件最后修改时间,帮助用户决策
- [ ] 添加"查看现有文件"选项,预览内容
- [ ] 保存历史记录(自动备份)
### 长期规划
- [ ] 版本管理系统Git 式管理)
- [ ] 文件差异对比Diff 显示)
- [ ] 自动保存功能(定时保存草稿)
---
## 🐛 已知限制
### 1. 依赖后端 API
- **问题**: 需要 `GET /api/files/info` 接口支持
- **降级方案**: API 不可用时跳过检查,直接保存
### 2. 文件名冲突
- **问题**: 如果重命名后的文件名仍然存在,会再次弹出对话框
- **解决**: 可以在重命名时检查并提示
### 3. 并发保存
- **问题**: 多个用户同时保存同名文件可能冲突
- **解决**: 后期可添加文件锁机制
---
## 📝 测试用例
### 测试 1: 新文件保存
```
前置条件: 工作流标题为 "新工作流",该文件不存在
操作: 点击保存按钮
预期结果:
- 不显示对话框
- 直接保存成功
- 显示提示 "✅ 工作流已保存到: users/guest/workflows/新工作流.utrace"
```
### 测试 2: 覆盖现有文件
```
前置条件: 工作流标题为 "测试工作流",该文件已存在
操作:
1. 点击保存按钮
2. 选择 "🔄 覆盖现有文件"
预期结果:
- 显示对话框
- 文件被覆盖
- 显示保存成功提示
```
### 测试 3: 重命名保存
```
前置条件: 工作流标题为 "测试工作流",该文件已存在
操作:
1. 点击保存按钮
2. 选择 "✏️ 重命名后保存"
3. 输入 "测试工作流_备份"
预期结果:
- 弹出输入框
- 工作流标题更新为 "测试工作流_备份"
- 保存为 测试工作流_备份.utrace
```
### 测试 4: 取消保存
```
前置条件: 工作流标题为 "测试工作流",该文件已存在
操作:
1. 点击保存按钮
2. 选择 "❌ 取消" 或按 ESC 键
预期结果:
- 对话框关闭
- 不执行保存操作
- 不显示任何提示
```
### 测试 5: API 不可用
```
前置条件: 后端服务未启动
操作: 点击保存按钮
预期结果:
- 跳过文件存在检查
- 直接尝试保存
- 显示错误提示 "❌ 保存失败,请检查后端服务是否正常运行"
```
---
## 🔄 版本历史
| 版本 | 日期 | 更新内容 |
|------|------|---------|
| v0.2.2.1 | 2026-01-07 | 新增文件覆盖保护对话框 |
---
## 📧 反馈
如有问题或建议,请通过以下方式反馈:
- 提交 Issue: GitHub Issues
- 功能建议: GitHub Discussions