import { useState, useCallback, useRef } from 'react'; import { Alert } from 'react-native'; import { runTemplate, getTemplateGeneration, pollTemplateGeneration } from '@/lib/api/template-runs'; import { RunTemplateData, TemplateGeneration, RunProgress, GenerationStatus } from '@/lib/types/template-run'; type UseTemplateRunState = 'idle' | 'submitting' | 'running' | 'completed' | 'error'; interface UseTemplateRunOptions { onSuccess?: (result: TemplateGeneration) => void; onError?: (error: Error) => void; onProgress?: (progress: RunProgress) => void; } export function useTemplateRun(options: UseTemplateRunOptions = {}) { const [state, setState] = useState('idle'); const [progress, setProgress] = useState({ status: 'pending', progress: 0, message: '准备开始...', }); const [result, setResult] = useState(null); const [error, setError] = useState(null); const pollTimeoutRef = useRef(null); const isMountedRef = useRef(true); // 清理轮询 const clearPolling = useCallback(() => { if (pollTimeoutRef.current) { clearTimeout(pollTimeoutRef.current); pollTimeoutRef.current = null; } }, []); // 更新进度状态 const updateProgress = useCallback((generation: TemplateGeneration) => { if (!isMountedRef.current) return; let progressPercent = 0; let message = '处理中...'; switch (generation.status) { case 'pending': progressPercent = 10; message = '任务已提交,等待开始...'; break; case 'running': progressPercent = 50; message = '正在生成内容,请稍候...'; break; case 'completed': progressPercent = 100; message = '生成完成!'; break; case 'failed': progressPercent = 0; message = '生成失败,请重试'; break; } const newProgress: RunProgress = { status: generation.status, progress: progressPercent, message, result: generation, }; setProgress(newProgress); options.onProgress?.(newProgress); }, [options.onProgress]); // 处理轮询完成 const handlePollComplete = useCallback((generation: TemplateGeneration) => { if (!isMountedRef.current) return; clearPolling(); setResult(generation); setState('completed'); updateProgress(generation); options.onSuccess?.(generation); }, [clearPolling, updateProgress, options.onSuccess]); // 处理轮询错误 const handlePollError = useCallback((error: Error) => { if (!isMountedRef.current) return; clearPolling(); setError(error); setState('error'); options.onError?.(error); }, [clearPolling, options.onError]); // 运行模板 const executeTemplate = useCallback(async ( templateId: string, data: RunTemplateData ) => { if (state === 'submitting' || state === 'running') { console.warn('模板正在运行中,请勿重复提交'); return; } try { // 重置状态 setState('submitting'); setError(null); setResult(null); setProgress({ status: 'pending', progress: 0, message: '正在提交任务...', }); // 提交模板运行 const runResponse = await runTemplate(templateId, data); if (!runResponse.success || !runResponse.data) { throw new Error('提交任务失败'); } const generationId = runResponse.data; setState('running'); setProgress({ status: 'pending', progress: 10, message: '任务已提交,开始生成...', }); // 开始轮询 pollTemplateGeneration( generationId, handlePollComplete, handlePollError ); } catch (error) { const err = error as Error; console.error('运行模板失败:', err); clearPolling(); setError(err); setState('error'); setProgress({ status: 'failed', progress: 0, message: err.message || '运行失败', }); options.onError?.(err); } }, [state, clearPolling, handlePollComplete, handlePollError, options.onError]); // 手动刷新状态 const refreshStatus = useCallback(async (generationId: string) => { try { const response = await getTemplateGeneration(generationId); if (response.success && response.data) { updateProgress(response.data); if (response.data.status === 'completed') { handlePollComplete(response.data); } else if (response.data.status === 'failed') { handlePollError(new Error('任务执行失败')); } } } catch (error) { console.error('刷新状态失败:', error); } }, [updateProgress, handlePollComplete, handlePollError]); // 取消运行 const cancelRun = useCallback(() => { if (state === 'submitting' || state === 'running') { clearPolling(); setState('idle'); setProgress({ status: 'pending', progress: 0, message: '已取消', }); Alert.alert('提示', '任务已取消'); } }, [state, clearPolling]); // 重置状态 const reset = useCallback(() => { clearPolling(); setState('idle'); setProgress({ status: 'pending', progress: 0, message: '准备开始...', }); setResult(null); setError(null); }, [clearPolling]); // 重试 const retry = useCallback((templateId: string, data: RunTemplateData) => { reset(); executeTemplate(templateId, data); }, [reset, executeTemplate]); // 组件卸载时清理 const cleanup = useCallback(() => { isMountedRef.current = false; clearPolling(); }, [clearPolling]); return { // 状态 state, progress, result, error, isLoading: state === 'submitting' || state === 'running', isCompleted: state === 'completed', isError: state === 'error', // 操作 executeTemplate, refreshStatus, cancelRun, reset, retry, cleanup, }; } // 便捷的 Hook 用于表单数据管理 export function useTemplateFormData(initialData: RunTemplateData = {}) { const [formData, setFormData] = useState(initialData); const [errors, setErrors] = useState>({}); const updateField = useCallback((fieldName: string, value: any) => { setFormData(prev => ({ ...prev, [fieldName]: value })); // 清除该字段的错误 if (errors[fieldName]) { setErrors(prev => { const newErrors = { ...prev }; delete newErrors[fieldName]; return newErrors; }); } }, [errors]); const setFieldError = useCallback((fieldName: string, error: string) => { setErrors(prev => ({ ...prev, [fieldName]: error })); }, []); const clearErrors = useCallback(() => { setErrors({}); }, []); const resetForm = useCallback(() => { setFormData(initialData); clearErrors(); }, [initialData, clearErrors]); const validateForm = useCallback((validation: Record = {}) => { const newErrors: Record = {}; let isValid = true; Object.entries(validation).forEach(([fieldName, rules]) => { const value = formData[fieldName]; // 检查必填 if (rules.required && (value === undefined || value === null || value === '')) { newErrors[fieldName] = `${fieldName} 是必填项`; isValid = false; return; } // 如果值为空且不是必填,跳过其他验证 if (value === undefined || value === null || value === '') { return; } // 其他验证规则 if (rules.min && typeof value === 'string' && value.length < rules.min) { newErrors[fieldName] = `${fieldName} 至少需要 ${rules.min} 个字符`; isValid = false; } if (rules.max && typeof value === 'string' && value.length > rules.max) { newErrors[fieldName] = `${fieldName} 最多 ${rules.max} 个字符`; isValid = false; } if (rules.pattern && typeof value === 'string' && !new RegExp(rules.pattern).test(value)) { newErrors[fieldName] = rules.message || `${fieldName} 格式不正确`; isValid = false; } }); setErrors(newErrors); return { isValid, errors: newErrors }; }, [formData]); return { formData, errors, updateField, setFieldError, clearErrors, resetForm, validateForm, isValid: Object.keys(errors).length === 0, }; }