TraceStudio-dev/docs/web1.1_filemanager/FEATURE_FILE_OVERWRITE_DIALOG.md
2026-01-07 21:31:22 +08:00

294 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 文件覆盖保护功能
## 🎯 功能概述
为保存工作流功能添加了文件覆盖保护机制。当保存的工作流文件已存在时,系统会弹出友好的对话框,让用户选择如何处理。
**更新版本**: 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