From 5080ac1e8dc0ba27225c197e7add2ff3d1c42c75 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 10 Jul 2025 22:56:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E5=B0=81=E8=A3=85=20h?= =?UTF-8?q?ook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useProgressCommand.ts | 233 +++++++++++++++++++++++++++++++ src/pages/TemplateManagePage.tsx | 68 ++++----- src/services/tauri.ts | 199 ++++++++++++++++++++++---- 3 files changed, 431 insertions(+), 69 deletions(-) create mode 100644 src/hooks/useProgressCommand.ts diff --git a/src/hooks/useProgressCommand.ts b/src/hooks/useProgressCommand.ts new file mode 100644 index 0000000..2af5e26 --- /dev/null +++ b/src/hooks/useProgressCommand.ts @@ -0,0 +1,233 @@ +import { useState, useCallback, useRef } from 'react' +import { executeCommandWithProgress, ProgressListener } from '../services/tauri' + +export interface ProgressState { + step: string + progress: number // -1 for indeterminate, 0-100 for percentage + message: string + details?: any + timestamp: number +} + +export interface UseProgressCommandOptions { + onProgress?: (progress: ProgressState) => void + onSuccess?: (result: any) => void + onError?: (error: Error) => void + autoReset?: boolean // Auto reset state after completion +} + +export interface UseProgressCommandReturn { + // State + isExecuting: boolean + progress: ProgressState | null + result: any + error: Error | null + logs: string[] + + // Actions + execute: (command: string, params: any, eventName: string) => Promise + reset: () => void + addLog: (message: string) => void + clearLogs: () => void +} + +/** + * React Hook for executing Tauri commands with progress monitoring + * + * @param options - Configuration options + * @returns Hook state and actions + */ +export function useProgressCommand(options: UseProgressCommandOptions = {}): UseProgressCommandReturn { + const [isExecuting, setIsExecuting] = useState(false) + const [progress, setProgress] = useState(null) + const [result, setResult] = useState(null) + const [error, setError] = useState(null) + const [logs, setLogs] = useState([]) + + const listenerRef = useRef(null) + + const addLog = useCallback((message: string) => { + const timestamp = new Date().toLocaleTimeString() + setLogs(prev => [...prev, `[${timestamp}] ${message}`]) + }, []) + + const clearLogs = useCallback(() => { + setLogs([]) + }, []) + + const reset = useCallback(() => { + setIsExecuting(false) + setProgress(null) + setResult(null) + setError(null) + if (options.autoReset !== false) { + setLogs([]) + } + }, [options.autoReset]) + + const execute = useCallback(async ( + command: string, + params: any, + eventName: string + ): Promise => { + try { + setIsExecuting(true) + setError(null) + setResult(null) + + if (options.autoReset !== false) { + setProgress(null) + setLogs([]) + } + + addLog(`开始执行命令: ${command}`) + + const onProgress = (progressData: ProgressState) => { + setProgress(progressData) + addLog(progressData.message) + options.onProgress?.(progressData) + } + + const commandResult = await executeCommandWithProgress( + command, + params, + eventName, + onProgress + ) + + setResult(commandResult) + addLog('命令执行成功') + options.onSuccess?.(commandResult) + + return commandResult + } catch (err) { + const error = err instanceof Error ? err : new Error(String(err)) + setError(error) + addLog(`命令执行失败: ${error.message}`) + options.onError?.(error) + throw error + } finally { + setIsExecuting(false) + } + }, [options, addLog]) + + return { + // State + isExecuting, + progress, + result, + error, + logs, + + // Actions + execute, + reset, + addLog, + clearLogs, + } +} + +/** + * Specialized hook for template operations + */ +export function useTemplateProgress(options: UseProgressCommandOptions = {}) { + const hook = useProgressCommand(options) + + const batchImport = useCallback(async (sourceFolder: string) => { + return hook.execute( + 'batch_import_templates_with_progress', + { request: { source_folder: sourceFolder } }, + 'template-import-progress' + ) + }, [hook]) + + return { + ...hook, + batchImport, + } +} + +/** + * Specialized hook for AI video operations + */ +export function useVideoProgress(options: UseProgressCommandOptions = {}) { + const hook = useProgressCommand(options) + + const generateVideo = useCallback(async ( + imagePath: string, + prompt: string, + outputPath: string + ) => { + return hook.execute( + 'generate_video_with_progress', + { imagePath, prompt, outputPath }, + 'video-generation-progress' + ) + }, [hook]) + + return { + ...hook, + generateVideo, + } +} + +/** + * Specialized hook for data processing operations + */ +export function useDataProcessingProgress(options: UseProgressCommandOptions = {}) { + const hook = useProgressCommand(options) + + const processData = useCallback(async ( + inputFile: string, + outputFile: string, + processingType: string + ) => { + return hook.execute( + 'process_data_with_progress', + { request: { input_file: inputFile, output_file: outputFile, processing_type: processingType } }, + 'data-processing-progress' + ) + }, [hook]) + + const batchConvertFiles = useCallback(async ( + filePaths: string[], + outputFormat: string + ) => { + return hook.execute( + 'batch_convert_files_with_progress', + { filePaths, outputFormat }, + 'file-conversion-progress' + ) + }, [hook]) + + return { + ...hook, + processData, + batchConvertFiles, + } +} + +/** + * Specialized hook for machine learning operations + */ +export function useMLProgress(options: UseProgressCommandOptions = {}) { + const hook = useProgressCommand(options) + + const trainModel = useCallback(async ( + datasetPath: string, + modelType: string, + epochs: number, + learningRate: number + ) => { + return hook.execute( + 'train_model_with_progress', + { request: { dataset_path: datasetPath, model_type: modelType, epochs, learning_rate: learningRate } }, + 'model-training-progress' + ) + }, [hook]) + + return { + ...hook, + trainModel, + } +} diff --git a/src/pages/TemplateManagePage.tsx b/src/pages/TemplateManagePage.tsx index 575b7f6..d6ea56b 100644 --- a/src/pages/TemplateManagePage.tsx +++ b/src/pages/TemplateManagePage.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react' import { Upload, FolderOpen, Trash2, Eye, Download, Search, Filter, Grid, List } from 'lucide-react' import { invoke } from '@tauri-apps/api/core' import { TemplateService } from '../services/tauri' +import { useTemplateProgress } from '../hooks/useProgressCommand' interface TemplateInfo { id: string @@ -19,6 +20,9 @@ interface TemplateInfo { tags: string[] } +// Import the progress interface from the hook +import type { ProgressState } from '../hooks/useProgressCommand' + interface ImportResult { status: boolean msg: string @@ -31,26 +35,30 @@ interface ImportResult { }> } -interface ImportProgress { - step: string - progress: number // -1 for indeterminate, 0-100 for percentage - message: string - details?: any // Additional details from Python - timestamp: number -} - const TemplateManagePage: React.FC = () => { const [templates, setTemplates] = useState([]) const [loading, setLoading] = useState(false) - const [importing, setImporting] = useState(false) const [searchTerm, setSearchTerm] = useState('') const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') const [selectedTemplate, setSelectedTemplate] = useState(null) - const [importResult, setImportResult] = useState(null) - const [importProgress, setImportProgress] = useState(null) - const [importLogs, setImportLogs] = useState([]) const [showImportModal, setShowImportModal] = useState(false) + // Use the progress hook for template operations + const { + isExecuting: importing, + progress: importProgress, + result: importResult, + logs: importLogs, + batchImport, + reset: resetImport + } = useTemplateProgress({ + onSuccess: async (result) => { + if (result.status && result.imported_count > 0) { + await loadTemplates() // Reload templates after successful import + } + } + }) + // Load templates on component mount useEffect(() => { loadTemplates() @@ -80,39 +88,11 @@ const TemplateManagePage: React.FC = () => { const folderResult = await invoke('select_folder') if (!folderResult) return - // Reset states - setImporting(true) - setImportResult(null) - setImportProgress(null) - setImportLogs([]) + // Show import modal and start import setShowImportModal(true) - - // Progress callback - const onProgress = (progress: ImportProgress) => { - setImportProgress(progress) - setImportLogs(prev => [...prev, `[${new Date().toLocaleTimeString()}] ${progress.message}`]) - } - - // Use the new progress-enabled import - const result = await TemplateService.batchImportTemplatesWithProgress(folderResult, onProgress) - setImportResult(result) - - if (result.status && result.imported_count > 0) { - // Reload templates - await loadTemplates() - } + await batchImport(folderResult) } catch (error) { console.error('Import failed:', error) - setImportResult({ - status: false, - msg: error instanceof Error ? error.message : 'Unknown error', - imported_count: 0, - failed_count: 0, - imported_templates: [], - failed_templates: [] - }) - } finally { - setImporting(false) } } @@ -233,7 +213,7 @@ const TemplateManagePage: React.FC = () => { {importResult.failed_count} 个模板导入失败 (点击查看详情)
- {importResult.failed_templates.map((failed, index) => ( + {importResult.failed_templates.map((failed: any, index: number) => (
{failed.name}: {failed.error}
@@ -456,7 +436,7 @@ const TemplateManagePage: React.FC = () => { {importResult.failed_count} 个模板导入失败 (点击查看详情)
- {importResult.failed_templates.map((failed, index) => ( + {importResult.failed_templates.map((failed: any, index: number) => (
{failed.name}: {failed.error}
diff --git a/src/services/tauri.ts b/src/services/tauri.ts index 11fe522..86bed60 100644 --- a/src/services/tauri.ts +++ b/src/services/tauri.ts @@ -5,6 +5,94 @@ import { invoke } from '@tauri-apps/api/core' +// Generic progress listener utility +export class ProgressListener { + private unlisten: (() => void) | null = null + + /** + * Execute a command with progress monitoring + * @param command - Tauri command name + * @param params - Command parameters + * @param eventName - Progress event name to listen to + * @param onProgress - Progress callback function + * @returns Promise with command result + */ + static async executeWithProgress( + command: string, + params: any, + eventName: string, + onProgress?: (progress: any) => void + ): Promise { + const listener = new ProgressListener() + return listener.execute(command, params, eventName, onProgress) + } + + /** + * Execute a command with progress monitoring (instance method) + */ + async execute( + command: string, + params: any, + eventName: string, + onProgress?: (progress: any) => void + ): Promise { + try { + // Set up progress listener if callback provided + if (onProgress) { + await this.startListening(eventName, onProgress) + } + + try { + const result = await invoke(command, params) + return JSON.parse(result as string) + } finally { + // Clean up listener + this.stopListening() + } + } catch (error) { + console.error(`Failed to execute command ${command} with progress:`, error) + throw error + } + } + + /** + * Start listening to progress events + */ + async startListening(eventName: string, onProgress: (progress: any) => void): Promise { + const { listen } = await import('@tauri-apps/api/event') + this.unlisten = await listen(eventName, (event) => { + onProgress(event.payload) + }) + } + + /** + * Stop listening to progress events + */ + stopListening(): void { + if (this.unlisten) { + this.unlisten() + this.unlisten = null + } + } + + /** + * Cleanup method (can be called manually if needed) + */ + cleanup(): void { + this.stopListening() + } +} + +// Utility function for simple one-off progress commands +export async function executeCommandWithProgress( + command: string, + params: any, + eventName: string, + onProgress?: (progress: any) => void +): Promise { + return ProgressListener.executeWithProgress(command, params, eventName, onProgress) +} + // Types for video processing export interface VideoProcessRequest { input_path: string @@ -467,6 +555,85 @@ export class AIVideoService { } } +// AI Video generation operations with progress +export class AIVideoProgressService { + /** + * Generate video with progress monitoring + */ + static async generateVideoWithProgress( + imagePath: string, + prompt: string, + outputPath: string, + onProgress?: (progress: any) => void + ): Promise { + return executeCommandWithProgress( + 'generate_video_with_progress', + { imagePath, prompt, outputPath }, + 'video-generation-progress', + onProgress + ) + } +} + +// Data processing operations +export class DataProcessingService { + /** + * Process data with progress monitoring + */ + static async processDataWithProgress( + inputFile: string, + outputFile: string, + processingType: string, + onProgress?: (progress: any) => void + ): Promise { + const request = { input_file: inputFile, output_file: outputFile, processing_type: processingType } + return executeCommandWithProgress( + 'process_data_with_progress', + { request }, + 'data-processing-progress', + onProgress + ) + } + + /** + * Batch convert files with progress monitoring + */ + static async batchConvertFilesWithProgress( + filePaths: string[], + outputFormat: string, + onProgress?: (progress: any) => void + ): Promise { + return executeCommandWithProgress( + 'batch_convert_files_with_progress', + { filePaths, outputFormat }, + 'file-conversion-progress', + onProgress + ) + } +} + +// Machine Learning operations +export class MLService { + /** + * Train model with progress monitoring + */ + static async trainModelWithProgress( + datasetPath: string, + modelType: string, + epochs: number, + learningRate: number, + onProgress?: (progress: any) => void + ): Promise { + const request = { dataset_path: datasetPath, model_type: modelType, epochs, learning_rate: learningRate } + return executeCommandWithProgress( + 'train_model_with_progress', + { request }, + 'model-training-progress', + onProgress + ) + } +} + // Template management operations export class TemplateService { /** @@ -490,31 +657,13 @@ export class TemplateService { sourceFolder: string, onProgress?: (progress: any) => void ): Promise { - try { - const request = { source_folder: sourceFolder } - - // Set up progress listener if callback provided - let unlisten: (() => void) | null = null - if (onProgress) { - const { listen } = await import('@tauri-apps/api/event') - unlisten = await listen('template-import-progress', (event) => { - onProgress(event.payload) - }) - } - - try { - const result = await invoke('batch_import_templates_with_progress', { request }) - return JSON.parse(result as string) - } finally { - // Clean up listener - if (unlisten) { - unlisten() - } - } - } catch (error) { - console.error('Failed to batch import templates with progress:', error) - throw error - } + const request = { source_folder: sourceFolder } + return executeCommandWithProgress( + 'batch_import_templates_with_progress', + { request }, + 'template-import-progress', + onProgress + ) } /**