From b051acc8b4d2b57bde63a04997c53e37ffabfef7 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 27 Jul 2026 21:58:54 +0800 Subject: [PATCH] =?UTF-8?q?refactor(editing-planner):=20=E6=8B=86=E5=88=86?= =?UTF-8?q?=20GenerationProgressModal=EF=BC=8C=E6=8C=89=E9=98=B6=E6=AE=B5?= =?UTF-8?q?=E6=8B=86=E4=B8=BA=203=20=E4=B8=AA=E5=AD=90=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=20(#1017)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: xiaoxia Co-committed-by: xiaoxia --- .../components/GenerationProgressModal.tsx | 240 +----------------- .../ProgressPhase.tsx | 65 +++++ .../generation-progress-modal/ResultPhase.tsx | 82 ++++++ .../generation-progress-modal/SetupPhase.tsx | 53 ++++ .../generation-progress-modal/constants.ts | 31 +++ .../generation-progress-modal/index.tsx | 70 +++++ .../generation-progress-modal/types.ts | 23 ++ 7 files changed, 328 insertions(+), 236 deletions(-) create mode 100644 apps/web/src/pages/editing-planner/components/generation-progress-modal/ProgressPhase.tsx create mode 100644 apps/web/src/pages/editing-planner/components/generation-progress-modal/ResultPhase.tsx create mode 100644 apps/web/src/pages/editing-planner/components/generation-progress-modal/SetupPhase.tsx create mode 100644 apps/web/src/pages/editing-planner/components/generation-progress-modal/constants.ts create mode 100644 apps/web/src/pages/editing-planner/components/generation-progress-modal/index.tsx create mode 100755 apps/web/src/pages/editing-planner/components/generation-progress-modal/types.ts diff --git a/apps/web/src/pages/editing-planner/components/GenerationProgressModal.tsx b/apps/web/src/pages/editing-planner/components/GenerationProgressModal.tsx index df7605e93..2f66ce31e 100644 --- a/apps/web/src/pages/editing-planner/components/GenerationProgressModal.tsx +++ b/apps/web/src/pages/editing-planner/components/GenerationProgressModal.tsx @@ -1,238 +1,6 @@ /** - * 生成进度弹窗 — 任务 2.17 - * 三阶段 UI:setup(配置)→ progress(进度轮询)→ completed / failed(结果) - * V21 设计系统,CSS 类名前缀 ep-gen- + * 生成进度弹窗 — 入口文件(向后兼容) + * 实际实现已移至 ./generation-progress-modal/ 目录 */ -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 +export { default } from "./generation-progress-modal" +export type { GenPhase, GenerationProgressModalProps } from "./generation-progress-modal" diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/ProgressPhase.tsx b/apps/web/src/pages/editing-planner/components/generation-progress-modal/ProgressPhase.tsx new file mode 100644 index 000000000..0e2597af5 --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/ProgressPhase.tsx @@ -0,0 +1,65 @@ +import React from "react" +import { Modal } from "@/components/ui" +import type { TaskItem } from "@/api/tasks" +import { getStepLabel, getStatusColor } from "./constants" + +interface ProgressPhaseProps { + open: boolean + task: TaskItem | null + onCancel: () => void +} + +/** progress(进度轮询)阶段弹窗 */ +export const ProgressPhase: React.FC = ({ open, task, onCancel }) => { + const progress = task?.progress ?? 0 + const status = task?.status ?? "" + const currentStep = task?.current_step ?? "" + const userMessage = task?.user_message ?? "" + const stepColor = getStatusColor(status, currentStep) + + return ( + +
+ {/* 进度环 */} +
+ + + + + + {progress}% + +
+ + {/* 当前步骤 */} +
+ {userMessage || getStepLabel(currentStep) || "处理中…"} +
+ + {/* 进度条 */} +
+
+
+ + {/* 任务 ID */} + {task?.id &&
任务 ID: {task.id}
} +
+ + ) +} diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/ResultPhase.tsx b/apps/web/src/pages/editing-planner/components/generation-progress-modal/ResultPhase.tsx new file mode 100644 index 000000000..14af7f83f --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/ResultPhase.tsx @@ -0,0 +1,82 @@ +import React from "react" +import { Modal, Button } from "@/components/ui" +import type { TaskItem } from "@/api/tasks" + +interface ResultPhaseProps { + open: boolean + phase: "completed" | "failed" + task: TaskItem | null + onCancel: () => void + onRetry?: () => void + onClose?: () => void +} + +/** completed / failed(结果)阶段弹窗 */ +export const ResultPhase: React.FC = ({ + open, + phase, + task, + onCancel, + onRetry, + onClose, +}) => { + const userMessage = task?.user_message ?? "" + const errorMessage = task?.error_message ?? "" + const retryable = task?.retryable ?? false + const handleClose = onClose || onCancel + + if (phase === "completed") { + return ( + +
+
🎉
+
视频生成完成!
+ {userMessage &&
{userMessage}
} +
+ +
+
+
+ ) + } + + return ( + +
+
😥
+
视频生成失败
+ {(errorMessage || userMessage) && ( +
+ {errorMessage || userMessage} +
+ )} +
+ {retryable && onRetry && ( + + )} + +
+
+
+ ) +} diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/SetupPhase.tsx b/apps/web/src/pages/editing-planner/components/generation-progress-modal/SetupPhase.tsx new file mode 100644 index 000000000..0c7e3f799 --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/SetupPhase.tsx @@ -0,0 +1,53 @@ +import React from "react" +import { Modal } from "@/components/ui" + +interface SetupPhaseProps { + open: boolean + voiceoverDuration: number | null + estimatedDuration: number + submitting: boolean + onDurationChange: (v: number | null) => void + onGenerate: () => void + onCancel: () => void +} + +/** setup(配置)阶段弹窗 */ +export const SetupPhase: React.FC = ({ + open, + voiceoverDuration, + estimatedDuration, + submitting, + onDurationChange, + onGenerate, + onCancel, +}) => ( + +
+ + { + const v = e.target.value ? Number(e.target.value) : null + onDurationChange(v) + }} + min={1} + max={600} + /> +
+ 预估总时长:{estimatedDuration}s +
+
+
+) diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/constants.ts b/apps/web/src/pages/editing-planner/components/generation-progress-modal/constants.ts new file mode 100644 index 000000000..eeecef559 --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/constants.ts @@ -0,0 +1,31 @@ +/* ──────────── 步骤文案映射 ──────────── */ + +export const STEP_LABELS: Record = { + queued: "排队中…", + preparing: "准备素材…", + generating_video: "渲染视频中…", + adding_effects: "添加特效…", + composing: "合成中…", + encoding: "编码输出中…", + completed: "生成完成!", + failed: "生成失败", +} + +export const getStepLabel = (step: string) => STEP_LABELS[step] || step.replace(/_/g, " ") + +/* ──────────── 状态徽标颜色 ──────────── */ + +export const STATUS_COLOR: Record = { + queued: "#6b7280", + pending: "#6b7280", + preparing: "#f59e0b", + generating_video: "#4f46e5", + adding_effects: "#7c3aed", + composing: "#2563eb", + encoding: "#0891b2", + completed: "#10b981", + failed: "#ef4444", +} + +export const getStatusColor = (status: string, currentStep: string) => + STATUS_COLOR[status] || STATUS_COLOR[currentStep] || "#4f46e5" diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/index.tsx b/apps/web/src/pages/editing-planner/components/generation-progress-modal/index.tsx new file mode 100644 index 000000000..14b4f94b5 --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/index.tsx @@ -0,0 +1,70 @@ +/** + * 生成进度弹窗 — 任务 2.17 + * 三阶段 UI:setup(配置)→ progress(进度轮询)→ completed / failed(结果) + * V21 设计系统,CSS 类名前缀 ep-gen- + */ +import React, { useEffect, useRef } from "react" +import type { GenPhase, GenerationProgressModalProps } from "./types" +import { SetupPhase } from "./SetupPhase" +import { ProgressPhase } from "./ProgressPhase" +import { ResultPhase } from "./ResultPhase" + +/* 重新导出类型,保持向后兼容 */ +export type { GenPhase, GenerationProgressModalProps } + +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]) + + /* setup 阶段 */ + if (phase === "setup") { + return ( + + ) + } + + /* progress 阶段 */ + if (phase === "progress") { + return + } + + /* completed / failed 阶段 */ + return ( + + ) +} + +export default GenerationProgressModal diff --git a/apps/web/src/pages/editing-planner/components/generation-progress-modal/types.ts b/apps/web/src/pages/editing-planner/components/generation-progress-modal/types.ts new file mode 100755 index 000000000..5e0b3990d --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/generation-progress-modal/types.ts @@ -0,0 +1,23 @@ +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 +}