"use client"; import { memo } from "react"; import { ChevronDown, Grid2x2, CircleDot } from "lucide-react"; import { cn } from "@/lib/utils"; type ColorTheme = "purple" | "blue" | "green" | "orange" | "red"; interface ImageGenerationLoadingProps { imageCount?: number; maxImages?: number; colorTheme?: ColorTheme; modelName?: string; providerType?: string; phase?: "idle" | "queued" | "processing" | "finalizing" | "failed"; elapsedSeconds?: number; queuePosition?: number; sameModelRunningCount?: number; concurrencyLimit?: number; estimatedWaitMs?: number; } const PROVIDER_MODEL_LABEL: Record = { "alibaba-image": "千问图片", "ruxa-image": "RUXA 图片", "fal-image": "FAL 图片", "openai-image": "OpenAI 图片", "stepfun-image": "StepFun 图片", midjourney: "Midjourney", "volcano-vision": "即梦 4.5", gemini: "Gemini 图片", krea: "Krea 图片", }; const PHASE_HINT: Record, string> = { idle: "任务已创建,正在初始化生成参数。", queued: "任务已进入队列,请稍候。", processing: "模型正在生成图片,请稍候。", finalizing: "图片已生成完成,正在同步到对话。", failed: "生成失败,请重试。", }; function formatImageQueueWaitLabel(estimatedWaitMs?: number) { const numeric = Number(estimatedWaitMs || 0); if (!Number.isFinite(numeric) || numeric <= 0) { return "预计即将开始"; } const seconds = Math.max(1, Math.round(numeric / 1000)); if (seconds < 60) { return `预计 ${seconds} 秒`; } const minutes = Math.floor(seconds / 60); const remainSeconds = seconds % 60; return remainSeconds > 0 ? `预计 ${minutes} 分 ${remainSeconds} 秒` : `预计 ${minutes} 分钟`; } export const ImageGenerationLoading = memo( ({ imageCount = 1, maxImages = 6, modelName, providerType, phase = "processing", elapsedSeconds = 0, queuePosition, sameModelRunningCount, concurrencyLimit, estimatedWaitMs, }: ImageGenerationLoadingProps) => { const count = Math.min(Math.max(imageCount, 1), maxImages); const resolvedModelName = modelName?.trim() || PROVIDER_MODEL_LABEL[providerType || ""] || "AI 图片模型"; const estimateRangeLabel = "60~120秒"; const elapsedLabel = elapsedSeconds > 0 ? `已等待 ${elapsedSeconds}s` : `预计 ${estimateRangeLabel}`; const isFailed = phase === "failed"; const queueLine = phase === "queued" && Number.isFinite(queuePosition) ? Math.max(0, Number(queuePosition || 0)) > 0 ? `排队中,前方 ${Math.max(0, Number(queuePosition || 0))} 个` : "排队中,等待进入执行" : null; const queueDetailLine = phase === "queued" ? `当前模型运行 ${Math.max( 0, Number(sameModelRunningCount || 0) )}/${Math.max(1, Number(concurrencyLimit || 0) || 1)},${formatImageQueueWaitLabel( estimatedWaitMs )}` : null; return (
开始生成
{resolvedModelName}
1 && "grid-cols-2" )} > {count === 1 ? ( <>
) : ( Array.from({ length: count }).map((_, index) => (
)) )}
{isFailed ? "生成失败" : "设计图处理中"}
使用 {resolvedModelName} 生成图片,预计 {estimateRangeLabel}
{queueLine ?
{queueLine}
: null} {queueDetailLine ?
{queueDetailLine}
: null} {(phase !== "processing" || elapsedSeconds > 0) && (
{PHASE_HINT[phase]} {elapsedLabel}
)}
); } ); ImageGenerationLoading.displayName = "ImageGenerationLoading";