diff --git a/docs/progress_system_usage.md b/docs/progress_system_usage.md
new file mode 100644
index 0000000..62e7054
--- /dev/null
+++ b/docs/progress_system_usage.md
@@ -0,0 +1,349 @@
+# 🚀 通用进度系统使用指南
+
+一个完全重构的通用进度监听系统,从特定的模板导入功能抽离出来,现在可以用于任何需要进度监控的操作。
+
+## 🎯 系统架构
+
+```
+Frontend (React)
+├── useProgressCommand Hook # React Hook for progress management
+├── ProgressListener Class # Generic progress listener utility
+└── Service Classes # Specialized service classes
+
+Backend (Rust)
+├── execute_python_action_with_progress # Generic Python executor
+├── PythonCommandBuilder # Command construction helper
+└── Progress Callbacks # Flexible callback system
+
+Python
+└── JSON-RPC Protocol # Standardized progress reporting
+```
+
+## 📚 使用方式
+
+### 1. 使用React Hook(推荐)
+
+```tsx
+import { useTemplateProgress } from '../hooks/useProgressCommand'
+
+function MyComponent() {
+ const {
+ isExecuting,
+ progress,
+ result,
+ error,
+ logs,
+ batchImport
+ } = useTemplateProgress({
+ onSuccess: (result) => {
+ console.log('Import completed:', result)
+ },
+ onError: (error) => {
+ console.error('Import failed:', error)
+ }
+ })
+
+ const handleImport = async () => {
+ try {
+ await batchImport('/path/to/templates')
+ } catch (error) {
+ // Error is already handled by the hook
+ }
+ }
+
+ return (
+
+ {isExecuting && (
+
+
Progress: {progress?.progress}%
+
Message: {progress?.message}
+
+ )}
+
+
+
+ {/* Real-time logs */}
+
+ {logs.map((log, index) => (
+
{log}
+ ))}
+
+
+ )
+}
+```
+
+### 2. 使用通用Hook
+
+```tsx
+import { useProgressCommand } from '../hooks/useProgressCommand'
+
+function DataProcessingComponent() {
+ const { execute, isExecuting, progress, logs } = useProgressCommand()
+
+ const processData = async () => {
+ await execute(
+ 'process_data_with_progress',
+ {
+ request: {
+ input_file: '/input.csv',
+ output_file: '/output.json',
+ processing_type: 'transform'
+ }
+ },
+ 'data-processing-progress'
+ )
+ }
+
+ return (
+
+
+
+ {progress && (
+
+
Step: {progress.step}
+
Progress: {progress.progress}%
+
Message: {progress.message}
+
+ )}
+
+ )
+}
+```
+
+### 3. 使用Service类
+
+```tsx
+import { AIVideoProgressService, DataProcessingService } from '../services/tauri'
+
+// AI Video Generation
+const generateVideo = async () => {
+ const onProgress = (progress) => {
+ console.log(`${progress.step}: ${progress.message}`)
+ updateProgressBar(progress.progress)
+ }
+
+ try {
+ const result = await AIVideoProgressService.generateVideoWithProgress(
+ '/path/to/image.jpg',
+ 'A beautiful sunset',
+ '/path/to/output.mp4',
+ onProgress
+ )
+ console.log('Video generated:', result)
+ } catch (error) {
+ console.error('Generation failed:', error)
+ }
+}
+
+// Data Processing
+const processFiles = async () => {
+ const result = await DataProcessingService.batchConvertFilesWithProgress(
+ ['/file1.txt', '/file2.txt'],
+ 'pdf',
+ (progress) => console.log(progress.message)
+ )
+}
+```
+
+### 4. 使用底层ProgressListener
+
+```tsx
+import { ProgressListener } from '../services/tauri'
+
+const customOperation = async () => {
+ const listener = new ProgressListener()
+
+ try {
+ const result = await listener.execute(
+ 'my_custom_command',
+ { param1: 'value1' },
+ 'my-progress-event',
+ (progress) => {
+ // Custom progress handling
+ console.log(progress)
+ }
+ )
+ } finally {
+ listener.cleanup()
+ }
+}
+```
+
+## 🔧 添加新的进度命令
+
+### 1. Rust端(后端)
+
+```rust
+#[tauri::command]
+pub async fn my_new_command_with_progress(
+ app: AppHandle,
+ my_param: String
+) -> Result {
+ let params = &[("--my_param", my_param.as_str())];
+
+ execute_python_action_with_progress(
+ app,
+ "python_core.my_module",
+ "my_action",
+ params,
+ "my-command-progress",
+ None,
+ ).await
+}
+```
+
+### 2. Python端
+
+```python
+from python_core.utils.jsonrpc import create_response_handler, create_progress_reporter
+
+def my_action():
+ rpc = create_response_handler("my_action")
+ progress = create_progress_reporter()
+
+ try:
+ progress.step("start", "开始处理...")
+ # ... processing ...
+
+ progress.report("process", 50.0, "处理中...", {"detail": "info"})
+ # ... more processing ...
+
+ progress.complete("处理完成!")
+
+ result = {"status": True, "data": "result"}
+ rpc.success(result)
+
+ except Exception as e:
+ progress.error(f"处理失败: {str(e)}")
+ rpc.error(-32603, "处理失败", str(e))
+```
+
+### 3. 前端Service
+
+```tsx
+export class MyService {
+ static async myOperationWithProgress(
+ param: string,
+ onProgress?: (progress: any) => void
+ ): Promise {
+ return executeCommandWithProgress(
+ 'my_new_command_with_progress',
+ { myParam: param },
+ 'my-command-progress',
+ onProgress
+ )
+ }
+}
+```
+
+### 4. 专用Hook
+
+```tsx
+export function useMyOperationProgress(options = {}) {
+ const hook = useProgressCommand(options)
+
+ const executeOperation = useCallback(async (param: string) => {
+ return hook.execute(
+ 'my_new_command_with_progress',
+ { myParam: param },
+ 'my-command-progress'
+ )
+ }, [hook])
+
+ return {
+ ...hook,
+ executeOperation,
+ }
+}
+```
+
+## 🎨 UI组件示例
+
+### 进度模态框
+
+```tsx
+function ProgressModal({ isOpen, onClose, progress, logs, isExecuting }) {
+ return (
+
+
+
操作进度
+
+ {/* Progress Bar */}
+ {progress && (
+
+
+ {progress.step}
+ {progress.progress >= 0 ? `${progress.progress}%` : '处理中...'}
+
+
+
{progress.message}
+
+ )}
+
+ {/* Logs */}
+
+ {logs.map((log, index) => (
+
+ {log}
+
+ ))}
+
+
+ {/* Actions */}
+
+ {!isExecuting && (
+
+ )}
+
+
+
+ )
+}
+```
+
+## 🔄 迁移指南
+
+### 从旧的模板导入方式迁移
+
+**旧方式**:
+```tsx
+const [importing, setImporting] = useState(false)
+const [progress, setProgress] = useState(null)
+const [logs, setLogs] = useState([])
+
+const handleImport = async () => {
+ setImporting(true)
+ // ... manual progress handling
+}
+```
+
+**新方式**:
+```tsx
+const { isExecuting, progress, logs, batchImport } = useTemplateProgress()
+
+const handleImport = async () => {
+ await batchImport(folderPath)
+}
+```
+
+## 🎯 最佳实践
+
+1. **优先使用Hook**:React Hook提供最佳的状态管理和生命周期处理
+2. **合理的进度粒度**:不要过于频繁地报告进度,影响性能
+3. **错误处理**:始终处理可能的错误情况
+4. **用户反馈**:提供清晰的进度信息和状态反馈
+5. **资源清理**:确保在组件卸载时清理监听器
+
+这个通用进度系统现在可以轻松扩展到任何需要进度监控的操作!🚀
diff --git a/src-tauri/src/commands/template.rs b/src-tauri/src/commands/template.rs
index edc91c6..12493fc 100644
--- a/src-tauri/src/commands/template.rs
+++ b/src-tauri/src/commands/template.rs
@@ -102,6 +102,20 @@ pub async fn delete_template(app: tauri::AppHandle, template_id: String) -> Resu
execute_python_command(app, &args, None).await
}
+#[tauri::command]
+pub async fn get_template_detail(app: tauri::AppHandle, template_id: String) -> Result {
+ let args = vec![
+ "-m".to_string(),
+ "python_core.services.template_manager".to_string(),
+ "--action".to_string(),
+ "get_template_detail".to_string(),
+ "--template_id".to_string(),
+ template_id,
+ ];
+
+ execute_python_command(app, &args, None).await
+}
+
// Example: AI Video generation with progress
#[allow(dead_code)]
#[tauri::command]
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index ac9ec10..56a3e8c 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -41,6 +41,7 @@ pub fn run() {
commands::batch_import_templates_with_progress,
commands::get_templates,
commands::get_template,
+ commands::get_template_detail,
commands::delete_template
])
.run(tauri::generate_context!())
diff --git a/src/pages/TemplateManagePage.tsx b/src/pages/TemplateManagePage.tsx
index d6ea56b..f7c348f 100644
--- a/src/pages/TemplateManagePage.tsx
+++ b/src/pages/TemplateManagePage.tsx
@@ -20,6 +20,39 @@ interface TemplateInfo {
tags: string[]
}
+// 轨道和片段的数据结构
+interface TrackSegment {
+ id: string
+ type: 'video' | 'audio' | 'image' | 'text' | 'effect'
+ name: string
+ start_time: number
+ end_time: number
+ duration: number
+ resource_path?: string
+ properties?: any
+ effects?: any[]
+}
+
+interface Track {
+ id: string
+ name: string
+ type: 'video' | 'audio' | 'subtitle'
+ index: number
+ segments: TrackSegment[]
+ properties?: any
+}
+
+interface TemplateDetail {
+ id: string
+ name: string
+ description: string
+ canvas_config: any
+ tracks: Track[]
+ duration: number
+ fps: number
+ sample_rate?: number
+}
+
// Import the progress interface from the hook
import type { ProgressState } from '../hooks/useProgressCommand'
@@ -41,6 +74,8 @@ const TemplateManagePage: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('')
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
const [selectedTemplate, setSelectedTemplate] = useState(null)
+ const [templateDetail, setTemplateDetail] = useState(null)
+ const [loadingDetail, setLoadingDetail] = useState(false)
const [showImportModal, setShowImportModal] = useState(false)
// Use the progress hook for template operations
@@ -82,6 +117,24 @@ const TemplateManagePage: React.FC = () => {
}
}
+ // 加载模板详情(包含轨道和片段信息)
+ const loadTemplateDetail = async (template: TemplateInfo) => {
+ try {
+ setLoadingDetail(true)
+ setSelectedTemplate(template)
+
+ // 调用后端API获取模板详情
+ const detail = await TemplateService.getTemplateDetail(template.id)
+ setTemplateDetail(detail)
+ } catch (error) {
+ console.error('Failed to load template detail:', error)
+ // 如果加载详情失败,至少显示基本信息
+ setTemplateDetail(null)
+ } finally {
+ setLoadingDetail(false)
+ }
+ }
+
const handleBatchImport = async () => {
try {
// Select folder using Tauri dialog
@@ -127,6 +180,13 @@ const TemplateManagePage: React.FC = () => {
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`
}
+ // Helper function to format time (for segments, in seconds)
+ const formatTime = (seconds: number): string => {
+ const minutes = Math.floor(seconds / 60)
+ const secs = (seconds % 60).toFixed(2)
+ return `${minutes}:${secs.padStart(5, '0')}`
+ }
+
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('zh-CN')
}
@@ -300,7 +360,7 @@ const TemplateManagePage: React.FC = () => {
{/* Actions */}
+ {/* 轨道和片段信息 */}
+
+
+
+ {loadingDetail ? (
+
+ ) : templateDetail ? (
+
+ {/* 画布信息 */}
+
+
画布配置
+
+
尺寸: {templateDetail.canvas_config?.width || 0} × {templateDetail.canvas_config?.height || 0}
+
帧率: {templateDetail.fps || 30} FPS
+
时长: {formatDuration(templateDetail.duration)}
+ {templateDetail.sample_rate && (
+
采样率: {templateDetail.sample_rate} Hz
+ )}
+
+
+
+ {/* 轨道列表 */}
+
+ {templateDetail.tracks.map((track, trackIndex) => (
+
+
+
+ 轨道 {track.index + 1}: {track.name}
+
+
+ {track.type === 'video' ? '视频' : track.type === 'audio' ? '音频' : '字幕'}
+
+
+
+ {/* 片段列表 */}
+
+ {track.segments.length > 0 ? (
+ track.segments.map((segment, segmentIndex) => (
+
+
+ {segment.name}
+
+ {segment.type === 'video' ? '视频' :
+ segment.type === 'audio' ? '音频' :
+ segment.type === 'image' ? '图片' :
+ segment.type === 'text' ? '文本' : '特效'}
+
+
+
+
开始: {formatTime(segment.start_time)}
+
结束: {formatTime(segment.end_time)}
+
时长: {formatTime(segment.duration)}
+
+ {segment.resource_path && (
+
+ 资源: {segment.resource_path}
+
+ )}
+
+ ))
+ ) : (
+
该轨道暂无片段
+ )}
+
+
+ ))}
+
+
+ ) : (
+
+
无法加载模板详情
+
请检查模板文件是否完整
+
+ )}
+
+