From 2cd76ec3a59440076f16fb20a21c901355c4f551 Mon Sep 17 00:00:00 2001 From: ludan <251918489@qq.com> Date: Wed, 17 Jun 2026 14:52:42 +0800 Subject: [PATCH] feat: add composer toolbelt with asset library, work mode selector, AI-powered prompt writing, and scenario settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EcommercePage.tsx (+260): - Add ComposerAssetTabKey and ComposerWorkModeKey types; extend ComposerMenuKey with assetLibrary/workMode/aiWrite - Add composerTooltip/composerAssetTab/composerWorkMode/aiWriteDraft state - Add composerAssetTabs (最近保存/套图配方/模特库), composerWorkModeOptions (快捷/思考), and composerRatioOptions (7 presets with display dimensions) - Add scenarioSettingsKeys and scenarioAdvancedSettingsKeys for conditional settings panel display - Add PaperPlaneRight icon import for AI writing send button - Reorder salesVideo tag position in commerceScenarioOptions; emoji icons replace Ant Design icons - handleCommerceScenarioClick: second click on active scenario now deselects (sets null) instead of toggling visibility - shouldShowScenarioSettings: settings panel visible for poster/mainImage/model/scene/festival/salesVideo but not popular - renderComposerAssetPanel: asset library popover with tab selector (recent/recipe/model) and grid display - renderComposerWorkModePanel: work mode radio popover with description cards - renderComposerAiWritePanel: AI prompt auto-complete panel with text input and send button; applyAiWriteSuggestion merges keyword + mode hint + platform context into composer prompt - Toolbar restructured with .ecom-command-tool pill buttons (upload/assets/mode/AI write) in .ecom-command-composer-actions - ecommerce-standalone.css (+937): - Composer toolbar: horizontal flex row with space-between, overflow-x scroll with hidden scrollbar - .ecom-command-tool: 40px pill-shaped buttons with gradient backgrounds, hover/active/dragging states with glow transition and lift - .ecom-command-tool--upload: icon+label layout for upload button - .ecom-command-tool--icon: 40px square icon-only button variant - Asset panel: tab selector row, 3-column recipe grid with aspect-ratio cards, hover scale effect - Work mode panel: radio-style card selector with description text - AI write panel: text input area with send button, responsive sizing - Tooltip: positioned above toolbar buttons with arrow pointer - pages/ecommerce.css (+490): - Composer input focus-within: green glow border + deepened shadow + lift transition - Asset library, work mode, AI write panel styles with consistent tokenized spacing and transitions - standalone/overrides.css (+7): - ≤420px settings option row: switch from grid to flex with flex:1 on buttons for tight viewport fit --- src/features/ecommerce/EcommercePage.tsx | 260 ++++++- src/styles/ecommerce-standalone.css | 937 ++++++++++++++++++++++- src/styles/pages/ecommerce.css | 490 +++++++++++- src/styles/standalone/overrides.css | 7 +- 4 files changed, 1626 insertions(+), 68 deletions(-) diff --git a/src/features/ecommerce/EcommercePage.tsx b/src/features/ecommerce/EcommercePage.tsx index 8561020..81d5cc7 100644 --- a/src/features/ecommerce/EcommercePage.tsx +++ b/src/features/ecommerce/EcommercePage.tsx @@ -32,6 +32,7 @@ import { Gift, MagicWand, Mountains, + PaperPlaneRight, ShoppingBag, User, VideoCamera, @@ -289,7 +290,9 @@ type CloneModelPanelTab = "scene" | "model"; type CloneVideoQualityKey = "standard" | "high" | "ultra"; type ProductSetStatus = "idle" | "ready" | "generating" | "done" | "failed"; type ProductKitToolKey = "set" | "detail" | "wear" | "clone"; -type ComposerMenuKey = "mode" | "platform" | "language" | "ratio" | "settings"; +type ComposerMenuKey = "mode" | "platform" | "language" | "ratio" | "settings" | "assetLibrary" | "workMode" | "aiWrite"; +type ComposerAssetTabKey = "recent" | "recipe" | "model"; +type ComposerWorkModeKey = "quick" | "think"; type CloneBasicSelectKey = "platform" | "market" | "language" | "ratio"; type CloneModelSelectKey = "gender" | "age" | "ethnicity" | "body"; type CloneReferenceMode = "upload" | "link"; @@ -1062,11 +1065,13 @@ const commerceScenarioOptions: Array<{ key: CommerceScenarioKey; label: string; { key: "model", label: "模特图", desc: "真人展示", icon: 🕴️ }, { key: "scene", label: "场景图", desc: "生活氛围", icon: 🌅 }, { key: "festival", label: "节日风格图", desc: "节点营销", icon: 🎉 }, + { key: "salesVideo", label: "带货视频", desc: "短视频脚本", icon: 🎬 }, { key: "background", label: "更换背景", desc: "背景重构", icon: }, { key: "retouch", label: "无痕改图", desc: "精修优化", icon: 🪄 }, - { key: "salesVideo", label: "带货视频", desc: "短视频脚本", icon: 🎬 }, ]; const primaryCommerceScenarioKeys: CommerceScenarioKey[] = ["popular", "poster", "mainImage", "model"]; +const scenarioSettingsKeys: CommerceScenarioKey[] = ["poster", "mainImage", "model", "scene", "festival", "salesVideo"]; +const scenarioAdvancedSettingsKeys: CommerceScenarioKey[] = ["model", "salesVideo"]; const commerceScenarioOutputMap: Record, ProductSetOutputKey> = { poster: "set", mainImage: "set", @@ -1937,6 +1942,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { const [isComposerMenuClosing, setIsComposerMenuClosing] = useState(false); const [composerPopoverLeft, setComposerPopoverLeft] = useState(0); const [composerPopoverTop, setComposerPopoverTop] = useState(0); + const [composerTooltip, setComposerTooltip] = useState<{ text: string; left: number; top: number } | null>(null); + const [composerAssetTab, setComposerAssetTab] = useState("recent"); + const [composerWorkMode, setComposerWorkMode] = useState("quick"); + const [aiWriteDraft, setAiWriteDraft] = useState(""); const [isCommandHistoryCollapsed, setIsCommandHistoryCollapsed] = useState(true); const [inspirationPreview, setInspirationPreview] = useState<{ mediaUrl: string; mediaType: "image" | "video"; prompt: string } | null>(null); const [isQuickPanelCollapsed, setIsQuickPanelCollapsed] = useState(false); @@ -2400,6 +2409,18 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { [cloneOutput, platform], ); const cloneRatioOptions = baseCloneRatioOptions; + const composerRatioOptions = useMemo( + () => [ + "1000×1000px 1:1", + "800×1200px 2:3", + "1200×800px 3:2", + "1200×900px 4:3", + "900×1200px 3:4", + "1080×1920px 9:16", + "1920×1080px 16:9", + ], + [], + ); const productSetLanguageOptions = useMemo( () => getPlatformLanguageOptions(productSetPlatform, productSetMarket), [productSetMarket, productSetPlatform], @@ -2445,6 +2466,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { : activeCommerceScenario === "popular" ? popularCommerceScenarioTemplates : commerceScenarioTemplates.filter((template) => template.scenario === activeCommerceScenario); + const shouldShowScenarioSettings = activeCommerceScenario !== null && scenarioSettingsKeys.includes(activeCommerceScenario); useEffect(() => { templateStripRef.current?.scrollTo({ left: 0, behavior: "auto" }); }, [activeCommerceScenario, isCloneTemplateStripVisible]); @@ -3844,7 +3866,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { const handleCommerceScenarioClick = (nextScenario: CommerceScenarioKey) => { if (nextScenario === activeCommerceScenario) { - setIsCloneTemplateStripVisible((visible) => !visible); + setActiveCommerceScenario(null); + setIsCloneTemplateStripVisible(false); setComposerMenu(null); return; } @@ -5803,6 +5826,96 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { ? String(cloneVideoDuration) + "秒 " + (cloneVideoQuality === "standard" ? "720P" : "1080P") : "换装素材"; + const composerAssetTabs: Array<{ key: ComposerAssetTabKey; label: string }> = [ + { key: "recent", label: "最近保存" }, + { key: "recipe", label: "套图配方" }, + { key: "model", label: "模特库" }, + ]; + const composerWorkModeOptions: Array<{ key: ComposerWorkModeKey; label: string; desc: string }> = [ + { key: "quick", label: "快捷", desc: "快速整理提示词,适合常规商品图生成。" }, + { key: "think", label: "思考", desc: "更强调卖点拆解、场景规划和图文一致性。" }, + ]; + + const applyAiWriteSuggestion = () => { + const keyword = aiWriteDraft.trim(); + if (!keyword) { + toast.info("请输入产品关键词或卖点"); + return; + } + const modeHint = composerWorkMode === "think" ? "先拆解目标人群、核心卖点和使用场景," : ""; + const nextValue = `${keyword}。${modeHint}请生成适合${platform}的高转化电商素材,画面干净高级,突出产品主体、核心卖点、使用场景和购买理由。`.slice(0, 500); + setRequirement(nextValue); + setComposerMenu(null); + }; + + const renderComposerAssetPanel = () => { + const renderEmpty = (label: string) => ( +
+ + 暂无数据 + {label} +
+ ); + + let content: ReactNode; + if (composerAssetTab === "recent") { + content = ecommerceHistoryRecords.length ? ( +
+ {ecommerceHistoryRecords.slice(0, 4).map((record) => { + const outputLabel = cloneOutputOptions.find((option) => option.key === record.output)?.label || "生成记录"; + return ( + + ); + })} +
+ ) : renderEmpty("生成后保存的素材会沉淀在这里"); + } else if (composerAssetTab === "recipe") { + content = ( +
+ {commerceScenarioTemplates.slice(0, 4).map((template) => ( + + ))} +
+ ); + } else { + content = ( +
+ {tryOnScenes.slice(0, 4).map((scene) => ( + + ))} +
+ ); + } + + return ( + <> +
+ 资产库 + +
+
+ {composerAssetTabs.map((tab) => ( + + ))} +
+ {content} + + ); + }; + const renderComposerMenu = () => { const composerLanguageOptions = Array.from(new Set(marketLanguageOptions.flatMap((option) => option.languages))).map((item) => ({ language: item, @@ -5816,6 +5929,37 @@ function ProductClonePage(_props: ProductClonePageProps = {}) { if (!menuToRender) return null; const popoverClosingClass = !composerMenu && isComposerMenuClosing ? " is-closing" : ""; const composerPopoverKey = `${menuToRender}-${cloneOutput}-${popoverClosingClass ? "closing" : "open"}`; + if (menuToRender === "assetLibrary") { + return ( +
+ {renderComposerAssetPanel()} +
+ ); + } + if (menuToRender === "workMode") { + return ( +
+
模式仅调整创作体验,不改变接口
+ {composerWorkModeOptions.map((option) => ( + + ))} +
+ ); + } + if (menuToRender === "aiWrite") { + return ( +
+
AI 帮写把关键词扩写成商业提示词
+