refactor(editing-planner): 拆分 GenerationProgressModal,按阶段拆为 3 个子组件 (#1017)
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m6s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m59s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m43s
CI/CD Pipeline / Unit Tests (push) Failing after 6m44s
CI/CD Pipeline / Integration Tests (push) Successful in 3m43s
CI/CD Pipeline / Frontend Lint (push) Failing after 2m15s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m5s
CI/CD Pipeline / Build Staging API Image (push) Successful in 16m2s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 2m50s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m25s
CI/CD Pipeline / Canary Release to Production (push) Failing after 1217h0m41s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 1217h0m42s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1217h0m43s
CI/CD Pipeline / CI Gate (push) Failing after 1217h4m1s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 1217h0m45s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1217h7m15s
CI/CD Pipeline / Deploy Production (push) Failing after 1217h8m0s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1217h15m39s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1217h15m41s
CI/CD Pipeline / Build Production API Image (push) Failing after 1217h15m43s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1217h16m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 1217h16m51s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1217h17m0s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 1217h33m34s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1217h49m42s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m6s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m59s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m43s
CI/CD Pipeline / Unit Tests (push) Failing after 6m44s
CI/CD Pipeline / Integration Tests (push) Successful in 3m43s
CI/CD Pipeline / Frontend Lint (push) Failing after 2m15s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m5s
CI/CD Pipeline / Build Staging API Image (push) Successful in 16m2s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 2m50s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m25s
CI/CD Pipeline / Canary Release to Production (push) Failing after 1217h0m41s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 1217h0m42s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1217h0m43s
CI/CD Pipeline / CI Gate (push) Failing after 1217h4m1s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 1217h0m45s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1217h7m15s
CI/CD Pipeline / Deploy Production (push) Failing after 1217h8m0s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1217h15m39s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1217h15m41s
CI/CD Pipeline / Build Production API Image (push) Failing after 1217h15m43s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1217h16m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 1217h16m51s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1217h17m0s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 1217h33m34s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1217h49m42s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #1017.
This commit is contained in:
@@ -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<string, string> = {
|
||||
queued: "排队中…",
|
||||
preparing: "准备素材…",
|
||||
generating_video: "渲染视频中…",
|
||||
adding_effects: "添加特效…",
|
||||
composing: "合成中…",
|
||||
encoding: "编码输出中…",
|
||||
completed: "生成完成!",
|
||||
failed: "生成失败",
|
||||
}
|
||||
|
||||
const getStepLabel = (step: string) => STEP_LABELS[step] || step.replace(/_/g, " ")
|
||||
|
||||
/* ──────────── 状态徽标颜色 ──────────── */
|
||||
|
||||
const STATUS_COLOR: Record<string, string> = {
|
||||
queued: "#6b7280",
|
||||
pending: "#6b7280",
|
||||
preparing: "#f59e0b",
|
||||
generating_video: "#4f46e5",
|
||||
adding_effects: "#7c3aed",
|
||||
composing: "#2563eb",
|
||||
encoding: "#0891b2",
|
||||
completed: "#10b981",
|
||||
failed: "#ef4444",
|
||||
}
|
||||
|
||||
/* ──────────── 组件 ──────────── */
|
||||
|
||||
const GenerationProgressModal: React.FC<GenerationProgressModalProps> = ({
|
||||
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 (
|
||||
<Modal
|
||||
open={open}
|
||||
title="使用模板生成视频"
|
||||
confirmLoading={submitting}
|
||||
onOk={onGenerate}
|
||||
onCancel={onCancel}
|
||||
okText="开始生成"
|
||||
cancelText="取消"
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-setup">
|
||||
<label className="ep-gen-field-label">配音时长(秒)</label>
|
||||
<input
|
||||
type="number"
|
||||
className="ep-gen-duration-input"
|
||||
placeholder="请输入配音时长"
|
||||
value={voiceoverDuration ?? ""}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value ? Number(e.target.value) : null
|
||||
onDurationChange(v)
|
||||
}}
|
||||
min={1}
|
||||
max={600}
|
||||
/>
|
||||
<div className="ep-gen-estimate">
|
||||
预估总时长:<strong>{estimatedDuration}s</strong>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
/* ── progress 阶段 ── */
|
||||
if (phase === "progress") {
|
||||
const stepColor = STATUS_COLOR[status] || STATUS_COLOR[currentStep] || "#4f46e5"
|
||||
|
||||
return (
|
||||
<Modal open={open} title="视频生成中" footer={null} onCancel={onCancel} closable width={480}>
|
||||
<div className="ep-gen-progress">
|
||||
{/* 进度环 */}
|
||||
<div className="ep-gen-progress-ring-wrap">
|
||||
<svg className="ep-gen-progress-ring" viewBox="0 0 120 120">
|
||||
<circle className="ep-gen-progress-ring-bg" cx="60" cy="60" r="52" />
|
||||
<circle
|
||||
className="ep-gen-progress-ring-fill"
|
||||
cx="60"
|
||||
cy="60"
|
||||
r="52"
|
||||
style={{
|
||||
strokeDasharray: `${2 * Math.PI * 52}`,
|
||||
strokeDashoffset: `${2 * Math.PI * 52 * (1 - progress / 100)}`,
|
||||
stroke: stepColor,
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
<span className="ep-gen-progress-pct" style={{ color: stepColor }}>
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 当前步骤 */}
|
||||
<div className="ep-gen-step-text">
|
||||
{userMessage || getStepLabel(currentStep) || "处理中…"}
|
||||
</div>
|
||||
|
||||
{/* 进度条 */}
|
||||
<div className="ep-gen-progress-bar">
|
||||
<div
|
||||
className="ep-gen-progress-bar-fill"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
backgroundColor: stepColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 任务 ID */}
|
||||
{task?.id && <div className="ep-gen-task-id">任务 ID: {task.id}</div>}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
/* ── completed 阶段 ── */
|
||||
if (phase === "completed") {
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="✅ 生成完成"
|
||||
footer={null}
|
||||
onCancel={onClose || onCancel}
|
||||
closable
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-result">
|
||||
<div className="ep-gen-result-icon">🎉</div>
|
||||
<div className="ep-gen-result-title">视频生成完成!</div>
|
||||
{userMessage && <div className="ep-gen-result-msg">{userMessage}</div>}
|
||||
<div className="ep-gen-result-actions">
|
||||
<Button buttonType="primary" onClick={onClose || onCancel}>
|
||||
查看结果
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
/* ── failed 阶段 ── */
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="❌ 生成失败"
|
||||
footer={null}
|
||||
onCancel={onClose || onCancel}
|
||||
closable
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-result ep-gen-result--error">
|
||||
<div className="ep-gen-result-icon">😥</div>
|
||||
<div className="ep-gen-result-title">视频生成失败</div>
|
||||
{(errorMessage || userMessage) && (
|
||||
<div className="ep-gen-result-msg ep-gen-result-msg--error">
|
||||
{errorMessage || userMessage}
|
||||
</div>
|
||||
)}
|
||||
<div className="ep-gen-result-actions">
|
||||
{retryable && onRetry && (
|
||||
<Button buttonType="primary" onClick={onRetry}>
|
||||
🔄 重试
|
||||
</Button>
|
||||
)}
|
||||
<Button buttonType="secondary" onClick={onClose || onCancel}>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default GenerationProgressModal
|
||||
export { default } from "./generation-progress-modal"
|
||||
export type { GenPhase, GenerationProgressModalProps } from "./generation-progress-modal"
|
||||
|
||||
+65
@@ -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<ProgressPhaseProps> = ({ 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 (
|
||||
<Modal open={open} title="视频生成中" footer={null} onCancel={onCancel} closable width={480}>
|
||||
<div className="ep-gen-progress">
|
||||
{/* 进度环 */}
|
||||
<div className="ep-gen-progress-ring-wrap">
|
||||
<svg className="ep-gen-progress-ring" viewBox="0 0 120 120">
|
||||
<circle className="ep-gen-progress-ring-bg" cx="60" cy="60" r="52" />
|
||||
<circle
|
||||
className="ep-gen-progress-ring-fill"
|
||||
cx="60"
|
||||
cy="60"
|
||||
r="52"
|
||||
style={{
|
||||
strokeDasharray: `${2 * Math.PI * 52}`,
|
||||
strokeDashoffset: `${2 * Math.PI * 52 * (1 - progress / 100)}`,
|
||||
stroke: stepColor,
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
<span className="ep-gen-progress-pct" style={{ color: stepColor }}>
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 当前步骤 */}
|
||||
<div className="ep-gen-step-text">
|
||||
{userMessage || getStepLabel(currentStep) || "处理中…"}
|
||||
</div>
|
||||
|
||||
{/* 进度条 */}
|
||||
<div className="ep-gen-progress-bar">
|
||||
<div
|
||||
className="ep-gen-progress-bar-fill"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
backgroundColor: stepColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 任务 ID */}
|
||||
{task?.id && <div className="ep-gen-task-id">任务 ID: {task.id}</div>}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
+82
@@ -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<ResultPhaseProps> = ({
|
||||
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 (
|
||||
<Modal
|
||||
open={open}
|
||||
title="✅ 生成完成"
|
||||
footer={null}
|
||||
onCancel={handleClose}
|
||||
closable
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-result">
|
||||
<div className="ep-gen-result-icon">🎉</div>
|
||||
<div className="ep-gen-result-title">视频生成完成!</div>
|
||||
{userMessage && <div className="ep-gen-result-msg">{userMessage}</div>}
|
||||
<div className="ep-gen-result-actions">
|
||||
<Button buttonType="primary" onClick={handleClose}>
|
||||
查看结果
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="❌ 生成失败"
|
||||
footer={null}
|
||||
onCancel={handleClose}
|
||||
closable
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-result ep-gen-result--error">
|
||||
<div className="ep-gen-result-icon">😥</div>
|
||||
<div className="ep-gen-result-title">视频生成失败</div>
|
||||
{(errorMessage || userMessage) && (
|
||||
<div className="ep-gen-result-msg ep-gen-result-msg--error">
|
||||
{errorMessage || userMessage}
|
||||
</div>
|
||||
)}
|
||||
<div className="ep-gen-result-actions">
|
||||
{retryable && onRetry && (
|
||||
<Button buttonType="primary" onClick={onRetry}>
|
||||
🔄 重试
|
||||
</Button>
|
||||
)}
|
||||
<Button buttonType="secondary" onClick={handleClose}>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
+53
@@ -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<SetupPhaseProps> = ({
|
||||
open,
|
||||
voiceoverDuration,
|
||||
estimatedDuration,
|
||||
submitting,
|
||||
onDurationChange,
|
||||
onGenerate,
|
||||
onCancel,
|
||||
}) => (
|
||||
<Modal
|
||||
open={open}
|
||||
title="使用模板生成视频"
|
||||
confirmLoading={submitting}
|
||||
onOk={onGenerate}
|
||||
onCancel={onCancel}
|
||||
okText="开始生成"
|
||||
cancelText="取消"
|
||||
width={440}
|
||||
>
|
||||
<div className="ep-gen-setup">
|
||||
<label className="ep-gen-field-label">配音时长(秒)</label>
|
||||
<input
|
||||
type="number"
|
||||
className="ep-gen-duration-input"
|
||||
placeholder="请输入配音时长"
|
||||
value={voiceoverDuration ?? ""}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value ? Number(e.target.value) : null
|
||||
onDurationChange(v)
|
||||
}}
|
||||
min={1}
|
||||
max={600}
|
||||
/>
|
||||
<div className="ep-gen-estimate">
|
||||
预估总时长:<strong>{estimatedDuration}s</strong>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
/* ──────────── 步骤文案映射 ──────────── */
|
||||
|
||||
export const STEP_LABELS: Record<string, string> = {
|
||||
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<string, string> = {
|
||||
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"
|
||||
@@ -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<GenerationProgressModalProps> = ({
|
||||
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 (
|
||||
<SetupPhase
|
||||
open={open}
|
||||
voiceoverDuration={voiceoverDuration}
|
||||
estimatedDuration={estimatedDuration}
|
||||
submitting={submitting}
|
||||
onDurationChange={onDurationChange}
|
||||
onGenerate={onGenerate}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/* progress 阶段 */
|
||||
if (phase === "progress") {
|
||||
return <ProgressPhase open={open} task={task} onCancel={onCancel} />
|
||||
}
|
||||
|
||||
/* completed / failed 阶段 */
|
||||
return (
|
||||
<ResultPhase
|
||||
open={open}
|
||||
phase={phase as "completed" | "failed"}
|
||||
task={task}
|
||||
onCancel={onCancel}
|
||||
onRetry={onRetry}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default GenerationProgressModal
|
||||
+23
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user