From 31bf103d7ccb21721b1471cb3b4400e00337b0ea Mon Sep 17 00:00:00 2001 From: Stringadmin Date: Wed, 3 Jun 2026 23:43:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=A0=91=E5=8A=A8=E6=80=81=E8=8A=82=E7=82=B9=E3=80=81=E5=85=A8?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=B5=81=E6=B0=B4=E7=BA=BF=E3=80=81=E5=9B=BE?= =?UTF-8?q?=E7=89=87/=E8=A7=86=E9=A2=91=E7=82=B9=E5=87=BB=E6=94=BE?= =?UTF-8?q?=E5=A4=A7=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 一键策划后自动连续执行完整流程(策划→图片→视频),无需手动点继续 - 节点数量跟随 API 返回的分镜数动态生成,策划前只显示 1 个占位节点 - 分镜图片和视频可点击弹出全屏预览浮层 Co-Authored-By: Claude Opus 4.7 --- .../ecommerce/EcommerceVideoWorkspace.tsx | 86 ++++++++++--------- src/styles/pages/ecommerce-video.css | 44 ++++++++++ 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/src/features/ecommerce/EcommerceVideoWorkspace.tsx b/src/features/ecommerce/EcommerceVideoWorkspace.tsx index 79c8a92..897a37c 100644 --- a/src/features/ecommerce/EcommerceVideoWorkspace.tsx +++ b/src/features/ecommerce/EcommerceVideoWorkspace.tsx @@ -1,5 +1,6 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { + CloseOutlined, CopyOutlined, DownloadOutlined, FolderAddOutlined, @@ -111,6 +112,7 @@ export default function EcommerceVideoWorkspace({ const [failedStep, setFailedStep] = useState(null); const [error, setError] = useState(null); const [actionNotice, setActionNotice] = useState(null); + const [previewMedia, setPreviewMedia] = useState<{ url: string; type: "image" | "video" } | null>(null); const abortControllerRef = useRef(null); const renderAbortRef = useRef({ current: false }); const setView = useAppStore((s) => s.setView); @@ -145,24 +147,17 @@ export default function EcommerceVideoWorkspace({ saveEcommerceVideoState({ inputFingerprint, stage, completedSteps, planResult, planProgress, scenes, sourceImageUrls }); }, [inputFingerprint, stage, completedSteps, planResult, planProgress, scenes, sourceImageUrls]); - // ── Auto-advance: skip manual "next step" clicks ───────── - const autoAdvanceTriggeredRef = useRef(false); + // ── Auto-advance: automatically run the full pipeline ───────── useEffect(() => { - if (autoAdvanceTriggeredRef.current) return; const delay = 600; if (stage === "planned" && planResult && scenes.length > 0) { - autoAdvanceTriggeredRef.current = true; const timer = setTimeout(() => { void handleGenerateImages(); }, delay); return () => clearTimeout(timer); } if (stage === "imaged" && scenes.every((s) => s.imageUrl)) { - autoAdvanceTriggeredRef.current = true; const timer = setTimeout(() => { void handleRenderVideos(); }, delay); return () => clearTimeout(timer); } - if (stage === "idle" || stage === "cancelled") { - autoAdvanceTriggeredRef.current = false; - } }, [stage, scenes, planResult]); // ── Keep-alive: resume polling for running tasks ────────── @@ -638,11 +633,7 @@ export default function EcommerceVideoWorkspace({ {scenes.length > 0 ? scenes.map((s) => (
)) : ( - <> -
-
-
- +
)}
@@ -672,7 +663,7 @@ export default function EcommerceVideoWorkspace({
-
+
setPreviewMedia({ url: scene.imageUrl!, type: "image" }) : undefined} style={imgReady ? { cursor: "pointer" } : undefined}> {imgReady ? ( {`分镜${scene.sceneId}`} ) : ( @@ -688,7 +679,7 @@ export default function EcommerceVideoWorkspace({
-
+
setPreviewMedia({ url: scene.resultUrl!, type: "video" }) : undefined} style={vidReady ? { cursor: "pointer" } : undefined}> {vidReady ? (
); }) : ( - [1, 2, 3].map((n) => ( -
-
-
- 分镜文本{n} - {stage === "planning" ? "策划中..." : "等待策划"} -
-
-
@@ -753,6 +742,19 @@ export default function EcommerceVideoWorkspace({ ) : null} {actionNotice ?
{actionNotice}
: null} + + {previewMedia ? ( +
setPreviewMedia(null)}> + + {previewMedia.type === "image" ? ( + 预览 e.stopPropagation()} /> + ) : ( +
+ ) : null} ); } diff --git a/src/styles/pages/ecommerce-video.css b/src/styles/pages/ecommerce-video.css index 2230115..7355f58 100644 --- a/src/styles/pages/ecommerce-video.css +++ b/src/styles/pages/ecommerce-video.css @@ -1101,3 +1101,47 @@ 70% { opacity: 0.5; } 100% { opacity: 0; transform: translateX(100%); } } + +/* ── Preview lightbox overlay ────────────────────── */ +.ecom-video-preview-overlay { + position: fixed; + inset: 0; + z-index: 9999; + display: grid; + place-items: center; + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(8px); + cursor: zoom-out; + animation: ecom-preview-fade-in 200ms ease; +} + +.ecom-video-preview-overlay__close { + position: absolute; + top: 24px; + right: 24px; + z-index: 10; + display: grid; + width: 40px; + height: 40px; + place-items: center; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 999px; + background: rgba(0, 0, 0, 0.6); + color: #fff; + font-size: 18px; + cursor: pointer; +} + +.ecom-video-preview-overlay img, +.ecom-video-preview-overlay video { + max-width: 90vw; + max-height: 85vh; + border-radius: 8px; + object-fit: contain; + cursor: default; +} + +@keyframes ecom-preview-fade-in { + from { opacity: 0; } + to { opacity: 1; } +}