247 lines
6.5 KiB
Markdown
247 lines
6.5 KiB
Markdown
# 模板导入进度日志复制功能
|
||
|
||
## 功能概述
|
||
|
||
为模板导入进度弹窗中的导入日志添加了复制功能,用户可以方便地复制日志内容用于调试、分享或存档。
|
||
|
||
## 功能特性
|
||
|
||
### 🎯 主要功能
|
||
|
||
1. **一键复制详细日志** - 包含完整的导入信息和日志内容
|
||
2. **多种复制格式** - 支持详细格式和简单格式
|
||
3. **智能下拉菜单** - 提供更多复制选项
|
||
4. **复制状态反馈** - 显示复制成功状态
|
||
5. **错误处理** - 支持备用复制方法
|
||
|
||
### 📋 复制格式
|
||
|
||
#### 详细格式(默认)
|
||
```
|
||
=== 模板导入日志 ===
|
||
导出时间: 2024-01-15 14:30:25
|
||
当前进度: 导入模板 - 正在处理模板文件... (75.5%)
|
||
导入结果: 成功 - 导入完成 (成功导入 8 个模板)
|
||
=== 详细日志 ===
|
||
|
||
[2024-01-15 14:30:20] 开始扫描模板目录...
|
||
[2024-01-15 14:30:21] 发现 10 个模板文件
|
||
[2024-01-15 14:30:22] 开始导入模板: template1
|
||
...
|
||
```
|
||
|
||
#### 简单格式
|
||
```
|
||
[2024-01-15 14:30:20] 开始扫描模板目录...
|
||
[2024-01-15 14:30:21] 发现 10 个模板文件
|
||
[2024-01-15 14:30:22] 开始导入模板: template1
|
||
...
|
||
```
|
||
|
||
## 用户界面
|
||
|
||
### 复制按钮组
|
||
- **主复制按钮**: 一键复制详细格式日志
|
||
- **下拉菜单按钮**: 展开更多复制选项
|
||
|
||
### 下拉菜单选项
|
||
- **复制详细日志**: 包含时间戳、进度信息、结果信息和完整日志
|
||
- **仅复制日志内容**: 只复制原始日志内容,不包含额外信息
|
||
|
||
### 状态反馈
|
||
- **复制中**: 显示"复制日志"文本和复制图标
|
||
- **复制成功**: 显示"已复制"文本和勾选图标(2秒后自动恢复)
|
||
|
||
## 技术实现
|
||
|
||
### 核心功能
|
||
|
||
#### 1. 复制函数
|
||
```typescript
|
||
const copyLogsToClipboard = async (detailed: boolean = true) => {
|
||
try {
|
||
let logsText: string
|
||
|
||
if (detailed) {
|
||
// 构建详细格式
|
||
const timestamp = new Date().toLocaleString()
|
||
const progressInfo = progress ? `当前进度: ${progress.step}...` : ''
|
||
const resultInfo = result ? `导入结果: ${result.status ? '成功' : '失败'}...` : ''
|
||
|
||
const header = [
|
||
'=== 模板导入日志 ===',
|
||
`导出时间: ${timestamp}`,
|
||
progressInfo,
|
||
resultInfo,
|
||
'=== 详细日志 ===',
|
||
''
|
||
].filter(Boolean).join('\n')
|
||
|
||
logsText = header + logs.join('\n')
|
||
} else {
|
||
// 简单格式
|
||
logsText = logs.join('\n')
|
||
}
|
||
|
||
await navigator.clipboard.writeText(logsText)
|
||
setIsCopied(true)
|
||
setTimeout(() => setIsCopied(false), 2000)
|
||
} catch (error) {
|
||
// 备用复制方法
|
||
fallbackCopy(logs.join('\n'))
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 备用复制方法
|
||
```typescript
|
||
const fallbackCopy = (text: string) => {
|
||
const textArea = document.createElement('textarea')
|
||
textArea.value = text
|
||
document.body.appendChild(textArea)
|
||
textArea.select()
|
||
document.execCommand('copy')
|
||
document.body.removeChild(textArea)
|
||
}
|
||
```
|
||
|
||
#### 3. 下拉菜单控制
|
||
```typescript
|
||
const [showCopyOptions, setShowCopyOptions] = useState(false)
|
||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||
|
||
// 点击外部关闭下拉菜单
|
||
useEffect(() => {
|
||
const handleClickOutside = (event: MouseEvent) => {
|
||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||
setShowCopyOptions(false)
|
||
}
|
||
}
|
||
|
||
if (showCopyOptions) {
|
||
document.addEventListener('mousedown', handleClickOutside)
|
||
}
|
||
|
||
return () => {
|
||
document.removeEventListener('mousedown', handleClickOutside)
|
||
}
|
||
}, [showCopyOptions])
|
||
```
|
||
|
||
### 组件结构
|
||
|
||
```tsx
|
||
<div className="flex items-center justify-between mb-2">
|
||
<h4 className="font-medium text-gray-900">导入日志</h4>
|
||
{logs.length > 0 && (
|
||
<div className="relative">
|
||
<div className="flex items-center gap-1">
|
||
{/* 主复制按钮 */}
|
||
<button onClick={() => copyLogsToClipboard(true)}>
|
||
{isCopied ? '已复制' : '复制日志'}
|
||
</button>
|
||
|
||
{/* 下拉菜单 */}
|
||
<div className="relative" ref={dropdownRef}>
|
||
<button onClick={() => setShowCopyOptions(!showCopyOptions)}>
|
||
<ChevronDown />
|
||
</button>
|
||
|
||
{showCopyOptions && (
|
||
<div className="dropdown-menu">
|
||
<button onClick={() => copyLogsToClipboard(true)}>
|
||
复制详细日志
|
||
</button>
|
||
<button onClick={() => copyLogsToClipboard(false)}>
|
||
仅复制日志内容
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
```
|
||
|
||
## 使用场景
|
||
|
||
### 1. 调试问题
|
||
当模板导入失败时,用户可以复制详细日志发送给技术支持:
|
||
- 包含完整的错误信息
|
||
- 包含导入时间和环境信息
|
||
- 便于问题定位和解决
|
||
|
||
### 2. 分享成功经验
|
||
用户可以复制成功的导入日志分享给其他用户:
|
||
- 展示导入过程
|
||
- 分享最佳实践
|
||
- 帮助其他用户学习
|
||
|
||
### 3. 存档记录
|
||
用户可以保存导入日志作为操作记录:
|
||
- 项目文档
|
||
- 操作历史
|
||
- 审计追踪
|
||
|
||
## 兼容性
|
||
|
||
### 浏览器支持
|
||
- **现代浏览器**: 使用 `navigator.clipboard.writeText()` API
|
||
- **旧版浏览器**: 自动降级到 `document.execCommand('copy')` 方法
|
||
|
||
### 错误处理
|
||
- **剪贴板权限被拒绝**: 自动使用备用方法
|
||
- **API 不可用**: 降级到传统复制方法
|
||
- **复制失败**: 在控制台记录错误信息
|
||
|
||
## 测试覆盖
|
||
|
||
### 单元测试
|
||
- ✅ 复制按钮显示/隐藏逻辑
|
||
- ✅ 详细格式复制功能
|
||
- ✅ 简单格式复制功能
|
||
- ✅ 下拉菜单交互
|
||
- ✅ 复制状态反馈
|
||
- ✅ 错误处理机制
|
||
- ✅ 点击外部关闭菜单
|
||
|
||
### 集成测试
|
||
- ✅ 与模板导入流程集成
|
||
- ✅ 实时日志更新
|
||
- ✅ 进度状态同步
|
||
|
||
## 性能考虑
|
||
|
||
### 内存使用
|
||
- 日志内容在内存中临时存储
|
||
- 复制完成后立即释放
|
||
- 不会造成内存泄漏
|
||
|
||
### 用户体验
|
||
- 复制操作响应迅速(< 100ms)
|
||
- 状态反馈及时(2秒自动恢复)
|
||
- 下拉菜单流畅交互
|
||
|
||
## 未来改进
|
||
|
||
### 短期计划
|
||
- [ ] 添加复制格式自定义选项
|
||
- [ ] 支持复制特定时间段的日志
|
||
- [ ] 添加日志过滤功能
|
||
|
||
### 长期计划
|
||
- [ ] 支持导出为文件
|
||
- [ ] 添加日志搜索功能
|
||
- [ ] 集成云端存储
|
||
|
||
## 相关文件
|
||
|
||
- `src/components/template/ImportProgressModal.tsx` - 主要组件
|
||
- `src/components/template/ImportProgressModal.test.tsx` - 单元测试
|
||
- `src/hooks/useProgressCommand.ts` - 进度管理 Hook
|
||
|
||
---
|
||
|
||
通过这个复制功能,用户可以更方便地管理和分享模板导入日志,提升了整体的用户体验和问题解决效率。
|