/** * 生成进度弹窗 — 任务 2.17 * 三阶段 UI:setup(配置)→ progress(进度轮询)→ completed / failed(结果) * V21 设计系统,CSS 类名前缀 ep-gen- */ import React, { useEffect, useRef } from "react" import { Modal, Button } from "@/components/ui" import type { TaskItem } from "@/api/tasks" /* ──────────── 类型 ──────────── */ export type GenPhase = "setup" | "progress" | "completed" | "failed" export interface GenerationProgressModalProps { open: boolean phase: GenPhase /* setup 阶段 */ voiceoverDuration: number | null estimatedDuration: number onDurationChange: (v: number | null) => void onGenerate: () => void /* progress / 结果阶段 */ task: TaskItem | null /* 通用 */ submitting: boolean onCancel: () => void onRetry?: () => void onClose?: () => void } /* ──────────── 步骤文案映射 ──────────── */ const STEP_LABELS: Record = { queued: "排队中…", preparing: "准备素材…", generating_video: "渲染视频中…", adding_effects: "添加特效…", composing: "合成中…", encoding: "编码输出中…", completed: "生成完成!", failed: "生成失败", } const getStepLabel = (step: string) => STEP_LABELS[step] || step.replace(/_/g, " ") /* ──────────── 状态徽标颜色 ──────────── */ const STATUS_COLOR: Record = { queued: "#6b7280", pending: "#6b7280", preparing: "#f59e0b", generating_video: "#4f46e5", adding_effects: "#7c3aed", composing: "#2563eb", encoding: "#0891b2", completed: "#10b981", failed: "#ef4444", } /* ──────────── 组件 ──────────── */ const GenerationProgressModal: React.FC = ({ open, phase, voiceoverDuration, estimatedDuration, onDurationChange, onGenerate, task, submitting, onCancel, onRetry, onClose, }) => { /* 关闭弹窗时重置(避免下次打开残留旧状态) */ const prevOpen = useRef(false) useEffect(() => { if (prevOpen.current && !open) { /* modal just closed — parent handles reset */ } prevOpen.current = open }, [open]) const progress = task?.progress ?? 0 const status = task?.status ?? "" const currentStep = task?.current_step ?? "" const userMessage = task?.user_message ?? "" const errorMessage = task?.error_message ?? "" const retryable = task?.retryable ?? false /* ── setup 阶段 ── */ if (phase === "setup") { return (
{ const v = e.target.value ? Number(e.target.value) : null onDurationChange(v) }} min={1} max={600} />
预估总时长:{estimatedDuration}s
) } /* ── progress 阶段 ── */ if (phase === "progress") { const stepColor = STATUS_COLOR[status] || STATUS_COLOR[currentStep] || "#4f46e5" return (
{/* 进度环 */}
{progress}%
{/* 当前步骤 */}
{userMessage || getStepLabel(currentStep) || "处理中…"}
{/* 进度条 */}
{/* 任务 ID */} {task?.id &&
任务 ID: {task.id}
}
) } /* ── completed 阶段 ── */ if (phase === "completed") { return (
🎉
视频生成完成!
{userMessage &&
{userMessage}
}
) } /* ── failed 阶段 ── */ return (
😥
视频生成失败
{(errorMessage || userMessage) && (
{errorMessage || userMessage}
)}
{retryable && onRetry && ( )}
) } export default GenerationProgressModal