feat: enhance scenario tabs with more/expand toggle, template carousel navigation, and 16 new templates
- package.json: Add @phosphor-icons/react ^2.1.10 dependency for additional icon set
- package-lock.json: Sync lockfile with new dependency and clean peer:true markers
- EcommercePage.tsx:
- Reorder scenario tabs: model (模特图) moved to position 3 after mainImage
- Add primaryCommerceScenarioKeys to define first 4 visible scenarios (popular/poster/mainImage/model)
- Change activeCommerceScenario initial state from "popular" to null — no scenario auto-selected on mount
- Add isCommerceScenarioMoreOpen state to toggle expanded scenario list
- Add templateStripRef for programmatic scroll control
- visibleCommerceScenarioOptions: filter to primary keys by default, show all when expanded
- "更多/收起" toggle button with dashed border, dynamic icon (··· or CloseOutlined)
- isCloneTemplateStripVisible defaults to false — template strip hidden until scenario clicked
- activeCommerceScenarioTemplates: returns empty array when no scenario selected
- Template strip wrapped in ecom-command-template-carousel with prev/next navigation arrows (‹ ›)
- scrollCommerceTemplateStrip(direction): smooth scroll by card width or viewport step
- handleCommerceScenarioMoreToggle: expand/collapse scenario list
- handleCloneTemplateCardClick: now also sets activeCommerceScenario to card.scenario
- Scroll hint text: "点击更多查看全部场景" when collapsed, "左右滑动查看全部场景" when expanded
- Auto-scroll template strip to left on scenario/visibility change via useEffect
- Add 16 new CommerceScenarioTemplate cards:
- poster: 节日礼赠海报, 奢品香水海报
- mainImage: 模特展示主图, 细节质感主图
- model: 男装夹克模特, 帽子配饰模特
- scene: 户外露营场景, 美妆喷雾场景
- festival: 父亲节礼盒图, 香薰蜡烛礼盒
- background: 高级灰背景, 居家背景
- retouch: 色彩统一精修, 细节锐化精修
- salesVideo: 痛点种草视频, 温馨开箱视频
- ecommerce-standalone.css (+559 lines):
- Scenario shell (.ecom-command-scenario-shell): centered flex wrapper with padding
- Scenario tabs: pill-shaped buttons (border-radius 999px), gradient backgrounds, scroll-snap
- "更多" button: dashed border in collapsed state, solid when expanded
- Template carousel (.ecom-command-template-carousel): horizontal scroll with snap, smooth scroll-behavior, hidden scrollbar
- Carousel fade edges: ::before/::after gradient masks (54px width)
- Navigation arrows (.ecom-command-template-nav): circular buttons positioned absolute at edges, hover/focus reveal with scale transition, opacity 0→1 on carousel hover
- Template cards: flexible sizing (clamp 260px-312px), 96px media thumbnail, scroll-snap-align start
- Per-scenario color mapping via --mode-accent: popular pink, poster/festival orange, mainImage/scene/background green, model/retouch/salesVideo blue, more blue
- Active state: radial gradient glow + color-mix border/shadow from --mode-accent
- Scroll hint: animated ← → arrows (ecom-scroll-hint-left/right keyframes at 1.6s infinite)
- Responsive: ≤900px left-aligned tabs + persistent nav arrows, ≤640px compact sizing, full-width carousel with overflow hidden
This commit is contained in:
@@ -25,6 +25,17 @@ import {
|
||||
TableOutlined,
|
||||
VideoCameraOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
ArrowsCounterClockwise,
|
||||
Fire,
|
||||
FrameCorners,
|
||||
Gift,
|
||||
MagicWand,
|
||||
Mountains,
|
||||
ShoppingBag,
|
||||
User,
|
||||
VideoCamera,
|
||||
} from "@phosphor-icons/react";
|
||||
import { Fragment, useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useTypewriter } from "../../hooks/useTypewriter";
|
||||
@@ -1044,16 +1055,17 @@ const cloneOutputOptions: Array<{ key: ProductSetOutputKey; label: string; desc:
|
||||
...productSetOutputOptions,
|
||||
];
|
||||
const commerceScenarioOptions: Array<{ key: CommerceScenarioKey; label: string; desc: string; icon: ReactNode }> = [
|
||||
{ key: "popular", label: "热门", desc: "高频模板", icon: <FireOutlined /> },
|
||||
{ key: "poster", label: "海报生成", desc: "活动视觉", icon: <LayoutOutlined /> },
|
||||
{ key: "mainImage", label: "商品主图", desc: "主图转化", icon: <FileImageOutlined /> },
|
||||
{ key: "scene", label: "场景图", desc: "生活氛围", icon: <AppstoreOutlined /> },
|
||||
{ key: "festival", label: "节日风格图", desc: "节点营销", icon: <GlobalOutlined /> },
|
||||
{ key: "model", label: "模特图", desc: "真人展示", icon: <SkinOutlined /> },
|
||||
{ key: "background", label: "更换背景", desc: "背景重构", icon: <ClearOutlined /> },
|
||||
{ key: "retouch", label: "无痕改图", desc: "精修优化", icon: <EditOutlined /> },
|
||||
{ key: "salesVideo", label: "带货视频", desc: "短视频脚本", icon: <VideoCameraOutlined /> },
|
||||
{ key: "popular", label: "热门", desc: "高频模板", icon: <span role="img" aria-label="fire">🔥</span> },
|
||||
{ key: "poster", label: "海报生成", desc: "活动视觉", icon: <span role="img" aria-label="poster">🎨</span> },
|
||||
{ key: "mainImage", label: "商品主图", desc: "主图转化", icon: <span role="img" aria-label="product">🛍️</span> },
|
||||
{ key: "model", label: "模特图", desc: "真人展示", icon: <span role="img" aria-label="model">🕴️</span> },
|
||||
{ key: "scene", label: "场景图", desc: "生活氛围", icon: <span role="img" aria-label="scene">🌅</span> },
|
||||
{ key: "festival", label: "节日风格图", desc: "节点营销", icon: <span role="img" aria-label="festival">🎉</span> },
|
||||
{ key: "background", label: "更换背景", desc: "背景重构", icon: <span role="img" aria-label="background">✨</span> },
|
||||
{ key: "retouch", label: "无痕改图", desc: "精修优化", icon: <span role="img" aria-label="retouch">🪄</span> },
|
||||
{ key: "salesVideo", label: "带货视频", desc: "短视频脚本", icon: <span role="img" aria-label="video">🎬</span> },
|
||||
];
|
||||
const primaryCommerceScenarioKeys: CommerceScenarioKey[] = ["popular", "poster", "mainImage", "model"];
|
||||
const commerceScenarioOutputMap: Record<Exclude<CommerceScenarioKey, "popular">, ProductSetOutputKey> = {
|
||||
poster: "set",
|
||||
mainImage: "set",
|
||||
@@ -1225,6 +1237,166 @@ const commerceScenarioTemplates: CommerceScenarioTemplate[] = [
|
||||
prompt: "生成商品使用演示短视频分镜,围绕使用过程、关键卖点和效果对比展开,节奏清晰,适合带货转化。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.asinListing,
|
||||
},
|
||||
{
|
||||
id: "poster-festival-gift",
|
||||
scenario: "poster",
|
||||
output: "set",
|
||||
title: "节日礼赠海报",
|
||||
desc: "适合父亲节、母亲节等节点礼赠氛围",
|
||||
badge: "节点营销",
|
||||
prompt: "生成一张节日礼赠风格电商海报,突出礼盒质感、温馨氛围和送礼情绪,画面高级克制,适合节日活动投放。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.fathersDaySet,
|
||||
},
|
||||
{
|
||||
id: "poster-luxury-perfume",
|
||||
scenario: "poster",
|
||||
output: "set",
|
||||
title: "奢品香水海报",
|
||||
desc: "高端质感,适合美妆香氛品牌",
|
||||
badge: "品牌感",
|
||||
prompt: "生成一张奢品香水电商海报,突出瓶身质感、光影层次和高端氛围,画面精致有艺术感,适合品牌旗舰店投放。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.perfumeSet,
|
||||
},
|
||||
{
|
||||
id: "main-image-model",
|
||||
scenario: "mainImage",
|
||||
output: "set",
|
||||
title: "模特展示主图",
|
||||
desc: "真人上身,提升列表点击率",
|
||||
badge: "点击率优先",
|
||||
prompt: "生成一张真人模特展示商品主图,突出上身效果、版型和搭配,背景简洁,适合提升搜索列表点击率和转化。",
|
||||
mediaUrl: ossAssets.ecommerce.productSet.model,
|
||||
},
|
||||
{
|
||||
id: "main-image-detail",
|
||||
scenario: "mainImage",
|
||||
output: "set",
|
||||
title: "细节质感主图",
|
||||
desc: "材质特写,强化购买信心",
|
||||
badge: "转化优先",
|
||||
prompt: "生成一张商品细节质感主图,突出材质纹理、工艺细节和真实触感,画面聚焦主体,适合强化用户购买信心。",
|
||||
mediaUrl: ossAssets.ecommerce.productSet.detail,
|
||||
},
|
||||
{
|
||||
id: "model-jacket",
|
||||
scenario: "model",
|
||||
output: "model",
|
||||
title: "男装夹克模特",
|
||||
desc: "硬朗风格,突出版型和质感",
|
||||
badge: "风格推荐",
|
||||
prompt: "生成男装夹克模特展示图,模特姿态自然有型,突出夹克版型、面料质感和整体搭配,适合男装电商详情和主图。",
|
||||
mediaUrl: ossAssets.ecommerce.tryOn.jacketResultA,
|
||||
},
|
||||
{
|
||||
id: "model-hat",
|
||||
scenario: "model",
|
||||
output: "model",
|
||||
title: "帽子配饰模特",
|
||||
desc: "细节展示,适合配饰品类",
|
||||
badge: "高频推荐",
|
||||
prompt: "生成帽子配饰模特展示图,突出帽型、佩戴效果和搭配细节,模特姿态自然,适合配饰、帽饰电商详情与主图。",
|
||||
mediaUrl: ossAssets.ecommerce.tryOn.hatResultA,
|
||||
},
|
||||
{
|
||||
id: "scene-camping",
|
||||
scenario: "scene",
|
||||
output: "set",
|
||||
title: "户外露营场景",
|
||||
desc: "把商品放进自然野趣环境",
|
||||
badge: "生活方式",
|
||||
prompt: "生成户外露营风格商品场景图,把产品自然融入露营环境,突出使用场景、自由氛围和生活品质,适合户外品类推广。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.campingCart,
|
||||
},
|
||||
{
|
||||
id: "scene-beauty-spray",
|
||||
scenario: "scene",
|
||||
output: "set",
|
||||
title: "美妆喷雾场景",
|
||||
desc: "捕捉使用瞬间,增强氛围感",
|
||||
badge: "氛围感",
|
||||
prompt: "生成美妆喷雾使用场景图,捕捉产品使用瞬间和细腻喷雾,突出清爽感、仪式感和大片氛围,适合美妆护肤品类。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.sprayScene,
|
||||
},
|
||||
{
|
||||
id: "festival-fathers-gift",
|
||||
scenario: "festival",
|
||||
output: "set",
|
||||
title: "父亲节礼盒图",
|
||||
desc: "礼赠场景,适合节日送礼营销",
|
||||
badge: "父亲节",
|
||||
prompt: "生成父亲节礼赠风格商品图,突出礼盒质感、沉稳色调和送礼仪式感,画面温暖有格调,适合父亲节活动投放。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.fathersDaySet,
|
||||
},
|
||||
{
|
||||
id: "festival-candle-gift",
|
||||
scenario: "festival",
|
||||
output: "set",
|
||||
title: "香薰蜡烛礼盒",
|
||||
desc: "温暖氛围,适合节日礼赠场景",
|
||||
badge: "热门模板",
|
||||
prompt: "生成香薰蜡烛节日礼盒图,突出温暖烛光、包装质感和治愈氛围,画面柔和高级,适合节日礼赠和家居品类营销。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.etsyScentedCandleSet,
|
||||
},
|
||||
{
|
||||
id: "background-premium-gray",
|
||||
scenario: "background",
|
||||
output: "set",
|
||||
title: "高级灰背景",
|
||||
desc: "简约商业,提升产品高级感",
|
||||
badge: "高频推荐",
|
||||
prompt: "为商品更换高级灰商业背景,保留产品主体和细节,背景简约有层次,突出产品轮廓和质感,适合电商主图和广告。",
|
||||
mediaUrl: ossAssets.ecommerce.detail.productA,
|
||||
},
|
||||
{
|
||||
id: "background-home-living",
|
||||
scenario: "background",
|
||||
output: "set",
|
||||
title: "居家背景",
|
||||
desc: "温馨生活场景,增强代入感",
|
||||
badge: "场景增强",
|
||||
prompt: "为商品更换温馨居家背景,保持主体自然融入,增强生活气息和使用代入感,适合家居、日用和生活方式品类。",
|
||||
mediaUrl: ossAssets.ecommerce.productSet.hosting,
|
||||
},
|
||||
{
|
||||
id: "retouch-color-correction",
|
||||
scenario: "retouch",
|
||||
output: "set",
|
||||
title: "色彩统一精修",
|
||||
desc: "多色校正,保持系列一致",
|
||||
badge: "精修模板",
|
||||
prompt: "对商品图进行色彩统一精修,校正色偏、统一光影和色调,保持系列素材一致性,画面自然真实,适合电商套图。",
|
||||
mediaUrl: ossAssets.ecommerce.detail.productB,
|
||||
},
|
||||
{
|
||||
id: "retouch-detail-sharpen",
|
||||
scenario: "retouch",
|
||||
output: "set",
|
||||
title: "细节锐化精修",
|
||||
desc: "纹理增强,提升商品质感",
|
||||
badge: "高频推荐",
|
||||
prompt: "对商品图进行细节锐化精修,增强纹理、边缘和材质细节,保持自然不过度,画面干净高级,适合主图和详情页。",
|
||||
mediaUrl: ossAssets.ecommerce.productSet.detail,
|
||||
},
|
||||
{
|
||||
id: "sales-video-painpoint",
|
||||
scenario: "salesVideo",
|
||||
output: "video",
|
||||
title: "痛点种草视频",
|
||||
desc: "直击痛点,快速建立购买动机",
|
||||
badge: "转化优先",
|
||||
prompt: "生成痛点种草风格带货短视频脚本和分镜,先抛出生活痛点再展示产品解决方案,节奏紧凑,适合清洁家电和功能性产品。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.cleanerPainpointDouyin,
|
||||
},
|
||||
{
|
||||
id: "sales-video-unboxing",
|
||||
scenario: "salesVideo",
|
||||
output: "video",
|
||||
title: "温馨开箱视频",
|
||||
desc: "氛围产品,增强情感连接",
|
||||
badge: "热门模板",
|
||||
prompt: "生成温馨开箱风格带货短视频脚本和分镜,围绕拆箱仪式感、产品外观和初体验展开,画面温暖治愈,适合氛围类产品。",
|
||||
mediaUrl: ossAssets.ecommerce.inspiration.nightLightUnboxingDouyin,
|
||||
},
|
||||
];
|
||||
const popularCommerceScenarioTemplates = commerceScenarioOptions
|
||||
.filter((option): option is { key: Exclude<CommerceScenarioKey, "popular">; label: string; desc: string; icon: ReactNode } => option.key !== "popular")
|
||||
@@ -1673,6 +1845,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
const composerMenuCloseTimeoutRef = useRef<number | null>(null);
|
||||
const requirementTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const commandComposerWrapRef = useRef<HTMLElement | null>(null);
|
||||
const templateStripRef = useRef<HTMLElement | null>(null);
|
||||
const garmentInputRef = useRef<HTMLInputElement>(null);
|
||||
const detailInputRef = useRef<HTMLInputElement>(null);
|
||||
const detailProgressRef = useRef<number | null>(null);
|
||||
@@ -1747,9 +1920,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
const [imageWorkbenchResultUrl, setImageWorkbenchResultUrl] = useState<string | null>(null);
|
||||
const [imageWorkbenchProgress, setImageWorkbenchProgress] = useState(0);
|
||||
const [isProductUploadDragging, setIsProductUploadDragging] = useState(false);
|
||||
const [activeCommerceScenario, setActiveCommerceScenario] = useState<CommerceScenarioKey>("popular");
|
||||
const [activeCommerceScenario, setActiveCommerceScenario] = useState<CommerceScenarioKey | null>(null);
|
||||
const [isCommerceScenarioMoreOpen, setIsCommerceScenarioMoreOpen] = useState(false);
|
||||
const [cloneOutput, setCloneOutput] = useState<CloneOutputKey>(defaultCloneOutput);
|
||||
const [isCloneTemplateStripVisible, setIsCloneTemplateStripVisible] = useState(true);
|
||||
const [isCloneTemplateStripVisible, setIsCloneTemplateStripVisible] = useState(false);
|
||||
const [videoHistoryVisible, setVideoHistoryVisible] = useState(false);
|
||||
const [isVideoWorkspaceVisible, setIsVideoWorkspaceVisible] = useState(false);
|
||||
const [videoPlanTrigger, setVideoPlanTrigger] = useState(0);
|
||||
@@ -2253,9 +2427,21 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
const selectedProductSetOutput =
|
||||
productSetOutputOptions.find((option) => option.key === productSetOutput) ?? productSetOutputOptions[0]!;
|
||||
const selectedCloneOutput = cloneOutputOptions.find((option) => option.key === cloneOutput) ?? cloneOutputOptions[1]!;
|
||||
const activeCommerceScenarioTemplates = activeCommerceScenario === "popular"
|
||||
? popularCommerceScenarioTemplates
|
||||
: commerceScenarioTemplates.filter((template) => template.scenario === activeCommerceScenario);
|
||||
const visibleCommerceScenarioOptions = useMemo(
|
||||
() =>
|
||||
isCommerceScenarioMoreOpen
|
||||
? commerceScenarioOptions
|
||||
: commerceScenarioOptions.filter((option) => primaryCommerceScenarioKeys.includes(option.key)),
|
||||
[isCommerceScenarioMoreOpen],
|
||||
);
|
||||
const activeCommerceScenarioTemplates = activeCommerceScenario === null
|
||||
? []
|
||||
: activeCommerceScenario === "popular"
|
||||
? popularCommerceScenarioTemplates
|
||||
: commerceScenarioTemplates.filter((template) => template.scenario === activeCommerceScenario);
|
||||
useEffect(() => {
|
||||
templateStripRef.current?.scrollTo({ left: 0, behavior: "auto" });
|
||||
}, [activeCommerceScenario, isCloneTemplateStripVisible]);
|
||||
const cloneRequirementPlaceholder =
|
||||
cloneOutput === "model"
|
||||
? "建议包含以下信息:产品名称、核心卖点、期望场景、模特外貌描述(如小麦色皮肤、齐刘海、眼角有泪痣)、具体参数"
|
||||
@@ -3663,6 +3849,23 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
if (mappedOutput !== cloneOutput) handleCloneOutputChange(mappedOutput);
|
||||
};
|
||||
|
||||
const handleCommerceScenarioMoreToggle = () => {
|
||||
setIsCommerceScenarioMoreOpen((visible) => !visible);
|
||||
setComposerMenu(null);
|
||||
};
|
||||
|
||||
const scrollCommerceTemplateStrip = (direction: -1 | 1) => {
|
||||
const strip = templateStripRef.current;
|
||||
if (!strip) return;
|
||||
const firstCard = strip.querySelector<HTMLElement>(".ecom-command-template-card");
|
||||
const cardStep = firstCard ? firstCard.offsetWidth + 14 : 0;
|
||||
const viewportStep = Math.max(280, strip.clientWidth * 0.78);
|
||||
strip.scrollBy({
|
||||
left: direction * Math.max(cardStep, viewportStep),
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
const handleCloneMarketChange = (nextMarket: string) => {
|
||||
const normalizedMarket = normalizeMarket(nextMarket);
|
||||
setMarket(normalizedMarket);
|
||||
@@ -5745,6 +5948,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
};
|
||||
|
||||
const handleCloneTemplateCardClick = (card: CommerceScenarioTemplate) => {
|
||||
setActiveCommerceScenario(card.scenario);
|
||||
if (card.output !== cloneOutput) handleCloneOutputChange(card.output);
|
||||
setIsCloneTemplateStripVisible(true);
|
||||
setComposerMenu(null);
|
||||
@@ -6084,23 +6288,38 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
onChange={handleSmartCutoutUpload}
|
||||
aria-label="上传智能抠图素材"
|
||||
/>
|
||||
<div className="ecom-command-mode-tabs ecom-command-scenario-tabs" aria-label="电商场景">
|
||||
{commerceScenarioOptions.map((option) => (
|
||||
<div className="ecom-command-scenario-shell" data-expanded={isCommerceScenarioMoreOpen ? "true" : "false"}>
|
||||
<div className="ecom-command-mode-tabs ecom-command-scenario-tabs" aria-label="电商场景">
|
||||
{visibleCommerceScenarioOptions.map((option) => (
|
||||
<button
|
||||
key={option.key}
|
||||
type="button"
|
||||
className={`${activeCommerceScenario === option.key ? "is-active" : ""}${activeCommerceScenario === option.key && isCloneTemplateStripVisible ? " is-open" : ""}`}
|
||||
onClick={() => handleCommerceScenarioClick(option.key)}
|
||||
>
|
||||
<span className={`ecom-command-mode-icon ecom-command-mode-icon--${option.key}`} aria-hidden="true">{option.icon}</span>
|
||||
<strong>{option.label}</strong>
|
||||
{activeCommerceScenario === option.key && isCloneTemplateStripVisible ? (
|
||||
<span className="ecom-command-scenario-close" aria-hidden="true"><CloseOutlined /></span>
|
||||
) : null}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
key={option.key}
|
||||
type="button"
|
||||
className={`${activeCommerceScenario === option.key ? "is-active" : ""}${activeCommerceScenario === option.key && isCloneTemplateStripVisible ? " is-open" : ""}`}
|
||||
onClick={() => handleCommerceScenarioClick(option.key)}
|
||||
className={`ecom-command-scenario-more${isCommerceScenarioMoreOpen ? " is-open" : ""}`}
|
||||
onClick={handleCommerceScenarioMoreToggle}
|
||||
aria-expanded={isCommerceScenarioMoreOpen}
|
||||
>
|
||||
<span className={`ecom-command-mode-icon ecom-command-mode-icon--${option.key}`} aria-hidden="true">{option.icon}</span>
|
||||
<strong>{option.label}</strong>
|
||||
{activeCommerceScenario === option.key && isCloneTemplateStripVisible ? (
|
||||
<span className="ecom-command-scenario-close" aria-hidden="true"><CloseOutlined /></span>
|
||||
) : null}
|
||||
<span className="ecom-command-mode-icon ecom-command-mode-icon--more" aria-hidden="true">
|
||||
{isCommerceScenarioMoreOpen ? <CloseOutlined /> : "···"}
|
||||
</span>
|
||||
<strong>{isCommerceScenarioMoreOpen ? "收起" : "更多"}</strong>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span className="ecom-command-scenario-scroll-hint" aria-hidden="true">左右滑动查看更多</span>
|
||||
<span className="ecom-command-scenario-scroll-hint" aria-hidden="true">
|
||||
{isCommerceScenarioMoreOpen ? "左右滑动查看全部场景" : "点击更多查看全部场景"}
|
||||
</span>
|
||||
<div className="clone-ai-input-wrapper ecom-command-composer">
|
||||
{productImages.length ? (
|
||||
<div className="ecom-command-asset-popover" aria-label={`已上传素材,${productImages.length}/${maxCloneProductImages}张`}>
|
||||
@@ -6193,31 +6412,53 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
</div>
|
||||
{renderComposerMenu()}
|
||||
</div>
|
||||
{(status === "idle" || status === "ready") && !showMainVideoWorkspace && isCloneTemplateStripVisible ? (
|
||||
<section className={`ecom-command-template-strip ecom-command-template-strip--${activeCommerceScenario}`} aria-label="模板卡片">
|
||||
{activeCommerceScenarioTemplates.map((card) => (
|
||||
<button
|
||||
key={card.id}
|
||||
type="button"
|
||||
className="ecom-command-template-card"
|
||||
aria-label={card.title}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleCloneTemplateCardClick(card);
|
||||
}}
|
||||
>
|
||||
<span className="ecom-command-template-card__media" aria-hidden="true">
|
||||
<img src={card.mediaUrl} alt="" loading="lazy" />
|
||||
</span>
|
||||
<span className="ecom-command-template-card__body">
|
||||
<span className="ecom-command-template-card__badge">{card.badge}</span>
|
||||
<strong>{card.title}</strong>
|
||||
<em>{card.desc}</em>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</section>
|
||||
{(status === "idle" || status === "ready") && !showMainVideoWorkspace && activeCommerceScenario !== null && isCloneTemplateStripVisible ? (
|
||||
<div className={`ecom-command-template-carousel ecom-command-template-carousel--${activeCommerceScenario}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="ecom-command-template-nav ecom-command-template-nav--prev"
|
||||
onClick={() => scrollCommerceTemplateStrip(-1)}
|
||||
aria-label="查看上一组模板"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<section
|
||||
ref={templateStripRef}
|
||||
className={`ecom-command-template-strip ecom-command-template-strip--${activeCommerceScenario}`}
|
||||
aria-label="模板卡片"
|
||||
>
|
||||
{activeCommerceScenarioTemplates.map((card) => (
|
||||
<button
|
||||
key={card.id}
|
||||
type="button"
|
||||
className="ecom-command-template-card"
|
||||
aria-label={card.title}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleCloneTemplateCardClick(card);
|
||||
}}
|
||||
>
|
||||
<span className="ecom-command-template-card__media" aria-hidden="true">
|
||||
<img src={card.mediaUrl} alt="" loading="lazy" />
|
||||
</span>
|
||||
<span className="ecom-command-template-card__body">
|
||||
<span className="ecom-command-template-card__badge">{card.badge}</span>
|
||||
<strong>{card.title}</strong>
|
||||
<em>{card.desc}</em>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</section>
|
||||
<button
|
||||
type="button"
|
||||
className="ecom-command-template-nav ecom-command-template-nav--next"
|
||||
onClick={() => scrollCommerceTemplateStrip(1)}
|
||||
aria-label="查看下一组模板"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{(status === "idle" || status === "ready") && !showMainVideoWorkspace ? (
|
||||
<section className="ecom-command-quick-board" aria-label="快捷功能">
|
||||
|
||||
Reference in New Issue
Block a user