feat(ecommerce): add one-click video quick tool page

- Add '一键视频' button left of '更多功能' in quick action board

- Create EcommerceOneClickVideoPanel with hot-clone-like UI

- Reuse EcommerceVideoWorkspace on the right for video flow

- Add light-theme CSS matching quick-set/hot-clone pages
This commit is contained in:
Codex
2026-06-17 14:25:18 +08:00
parent 65be92ba43
commit 2bc6fb7ab1
3 changed files with 766 additions and 4 deletions
+65 -4
View File
@@ -51,6 +51,7 @@ import EcommerceSetPanel from "./panels/EcommerceSetPanel";
import EcommerceTryOnPanel from "./panels/EcommerceTryOnPanel";
import EcommerceClonePanel from "./panels/EcommerceClonePanel";
import EcommerceCopywritingPanel from "./panels/EcommerceCopywritingPanel";
import EcommerceOneClickVideoPanel from "./panels/EcommerceOneClickVideoPanel";
import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
import { downloadResultAsset } from "../workbench/workbenchDownload";
import type { CloneOutputKey, ProductSetOutputKey } from "./utils/platformRules";
@@ -1885,7 +1886,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<ProductSetPreviewSelection | null>(null);
const [showHostingModal, setShowHostingModal] = useState(false);
const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | "hot" | "quick-set" | "copywriting" | null>(null);
const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | "hot" | "quick-set" | "copywriting" | "oneClickVideo" | null>(null);
const [smartCutoutImage, setSmartCutoutImage] = useState<SmartCutoutImageItem | null>(null);
const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
@@ -5019,6 +5020,26 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
setComposerMenu(null);
};
const openOneClickVideoPage = () => {
clearSmartCutoutTransition();
setActiveQuickTool("oneClickVideo");
setComposerMenu(null);
setIsCloneSettingsCollapsed(false);
setIsQuickPanelCollapsed(false);
};
const closeOneClickVideoPage = () => {
setActiveQuickTool(null);
setComposerMenu(null);
};
const handleOneClickVideoPlatformChange = (nextPlatform: string) => {
const normalizedPlatform = normalizePlatform(nextPlatform);
setPlatform(normalizedPlatform);
setRatio((current) => normalizeRatioForPlatform(normalizedPlatform, current, "video"));
setLanguage(getPlatformDefaultLanguage(normalizedPlatform, market));
};
const resetTask = () => {
setSetImages([]);
setProductSetRequirement("");
@@ -5084,6 +5105,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const isHotCloneTool = isCloneTool && activeQuickTool === "hot";
const isQuickSetTool = isCloneTool && activeQuickTool === "quick-set";
const isCopywritingTool = isCloneTool && activeQuickTool === "copywriting";
const isOneClickVideoTool = isCloneTool && activeQuickTool === "oneClickVideo";
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
const setPrimaryLabel =
setImages.length === 0
@@ -6587,6 +6609,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
{ label: "图片翻译", tone: "translate", icon: <GlobalOutlined />, onClick: openImageTranslatePage },
{ label: "商品套图", tone: "product", icon: <AppstoreOutlined />, onClick: openQuickSetPage },
{ label: "一键文案", tone: "copywriting", icon: <EditOutlined />, onClick: openCopywritingPage },
{ label: "一键视频", tone: "video", icon: <VideoCameraOutlined />, onClick: openOneClickVideoPage },
{ label: "更多功能", tone: "more", icon: <SettingOutlined />, disabled: true },
].map((item) => (
<button
@@ -8430,6 +8453,42 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
</div>
);
const oneClickVideoPreview = (
<div key="oneClickVideo" className="ecom-quick-page-wrap ecom-tool-page-enter">
<EcommerceOneClickVideoPanel
onClose={closeOneClickVideoPage}
isAuthenticated={isAuthenticated}
onRequestLogin={requestLogin}
productImages={productImages}
productInputRef={productInputRef}
isProductUploadDragging={isProductUploadDragging}
setIsProductUploadDragging={setIsProductUploadDragging}
handleProductDrop={handleProductDrop}
handleProductUpload={handleProductUpload}
removeProductImage={removeProductImage}
maxProductImages={maxCloneProductImages}
requirement={requirement}
onRequirementChange={setRequirement}
platform={platform}
platformOptions={platformOptions}
onPlatformChange={handleOneClickVideoPlatformChange}
ratio={ratio}
ratioOptions={getPlatformRatioOptions(platform, "video")}
onRatioChange={setRatio}
videoQuality={cloneVideoQuality}
videoQualityOptions={cloneVideoQualityOptions}
onVideoQualityChange={setCloneVideoQuality}
videoDuration={cloneVideoDuration}
videoDurationMin={cloneVideoDurationMin}
videoDurationMax={cloneVideoDurationMax}
onVideoDurationChange={setCloneVideoDuration}
videoSmart={cloneVideoSmart}
onVideoSmartChange={setCloneVideoSmart}
onOpenHistory={() => setVideoHistoryVisible(true)}
/>
</div>
);
const activePreview = isSetTool
? setPreview
: isDetail
@@ -8465,9 +8524,11 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
)
: isCopywritingTool
? copywritingPreview
: clonePreview
: isOneClickVideoTool
? oneClickVideoPreview
: clonePreview
: placeholderPreview;
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isQuickSetTool && !isCopywritingTool;
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isQuickSetTool && !isCopywritingTool && !isOneClickVideoTool;
const isRecordDetailWorkspace = isMainCloneWorkspace && Boolean(activeHistoryRecordId);
const currentResultCount = canvasNodes.reduce((count, node) => count + node.results.length, 0);
const activeHistoryRecord = activeHistoryRecordId ? ecommerceHistoryRecords.find((record) => record.id === activeHistoryRecordId) : null;
@@ -8503,7 +8564,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
return (
<section
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isRecordDetailWorkspace && isCloneConversationCollapsed ? " is-conversation-collapsed" : ""}${isRecordDetailWorkspace ? " is-history-detail" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isTranslateTool ? " is-translate-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotCloneTool ? " is-hot-clone-page" : ""}${isQuickSetTool ? " is-quick-set-page" : ""}${isCopywritingTool ? " is-copywriting-page" : ""}`}
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isRecordDetailWorkspace && isCloneConversationCollapsed ? " is-conversation-collapsed" : ""}${isRecordDetailWorkspace ? " is-history-detail" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isTranslateTool ? " is-translate-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotCloneTool ? " is-hot-clone-page" : ""}${isQuickSetTool ? " is-quick-set-page" : ""}${isCopywritingTool ? " is-copywriting-page" : ""}${isOneClickVideoTool ? " is-one-click-video-page" : ""}`}
data-tool={activeTool}
aria-label={pageLabel}
>