feat: 重构电商指令栏布局,模式标签外置、精简结果标签、优化生成记录交互
本次修改对电商图片工作台的指令栏(composer)进行了全面重构,主要包含以下变更: 一、指令栏布局重构(EcommercePage.tsx): - 新增生成模式标签页(ecom-command-mode-tabs),5种模式(套图/详情图/模特图/视频/爆款图)以标签形式外置于输入区上方,每种模式配有独立图标和配色 - 设置行(平台/语种/比例/设置)移入输入区内部,采用圆角胶囊按钮排列 - 上传按钮从输入区移到底部工具栏,改为"上传素材"紧凑样式 - 精简生成结果画布:移除所有文字标签(套图/详情图/模特图/爆款图 标签、原图素材标签、结果卡片标签),让图片成为绝对视觉焦点 - 灵感行"AI团队"更名为"作品记录",更新描述文案为"沉淀最近生成的高转化素材,随时回看与复用" 二、样式系统升级(ecommerce-standalone.css): - 新增模式标签页完整样式:5列等宽网格、磨砂玻璃背板、各模式独立主题色 · 套图 set:翠绿 #0f8f72 · 详情图 detail:紫色 #7a5af8 · 模特图 model:蓝色 #1073cc · 视频 video:暖橙 #cc6b14 · 爆款图 hot:玫红 #c04468 - hover/active 状态带径向光晕和上浮微动效(translateY(-1px)) - 隐藏生成结果中的所有文字标签(display:none),减少视觉噪音 - 修复历史记录删除按钮定位:改为绝对居中定位,不受网格布局影响 - 输入区改为单列布局,增大最小高度(214-286px),增加内边距 变更文件: - src/features/ecommerce/EcommercePage.tsx (+87/-51) - src/styles/ecommerce-standalone.css (+456)
This commit is contained in:
@@ -106,8 +106,8 @@ const ecommerceInspirationAssets = ossAssets.ecommerce.inspiration;
|
||||
|
||||
const ecommerceInspirationRows = [
|
||||
{
|
||||
title: "AI团队",
|
||||
desc: "不止作图,更懂转化。",
|
||||
title: "作品记录",
|
||||
desc: "沉淀最近生成的高转化素材,随时回看与复用。",
|
||||
variant: "team",
|
||||
cards: [
|
||||
{ title: "指定ASIN,优化listing", meta: "竞品拆解 · 卖点重排 · 图文建议", mediaUrl: ecommerceInspirationAssets.asinListing, mediaType: "image" },
|
||||
@@ -4487,14 +4487,12 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
onClick={() => openProductSetPreview(setPreviewCards[0] ?? productSetPreviewCards[0])}
|
||||
>
|
||||
<img src={setImages[0]?.src ?? (setPreviewCards[0]?.src ?? productSetPreviewCards[0].src)} alt="商品原图" />
|
||||
<span>原图素材</span>
|
||||
</button>
|
||||
<div className="product-set-flow-arrow" aria-hidden="true" />
|
||||
<div className="product-set-card-grid result-reveal">
|
||||
{setPreviewCards.map((card) => (
|
||||
<button key={card.id} type="button" onClick={() => openProductSetPreview(card)}>
|
||||
<img src={card.src} alt={card.label} />
|
||||
<span>{card.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -4985,9 +4983,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="clone-ai-node-label">{node.mode === "set" ? "套图" : node.mode === "detail" ? "详情图" : node.mode === "model" ? "模特图" : node.mode === "hot" ? "爆款图" : node.mode}</span>
|
||||
</div>
|
||||
/>
|
||||
{node.sourceImage ? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -4995,7 +4991,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
onClick={() => openProductSetPreview({ src: node.sourceImage!, label: "原图素材" })}
|
||||
>
|
||||
<img src={node.sourceImage} alt="原图素材" />
|
||||
<span>原图素材</span>
|
||||
</button>
|
||||
) : null}
|
||||
<div className="clone-ai-flow-arrow" aria-hidden="true" />
|
||||
@@ -5003,7 +4998,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
{node.results.map((card) => (
|
||||
<button key={card.id} type="button" style={{ aspectRatio: parseRatioToAspectCss(ratio) }} onClick={() => openProductSetPreview(card, { nodeId: node.id, removable: true })}>
|
||||
<img src={card.src} alt={card.label} />
|
||||
<span>{card.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -5078,27 +5072,20 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
onChange={handleSmartCutoutUpload}
|
||||
aria-label="上传智能抠图素材"
|
||||
/>
|
||||
<div className="ecom-command-mode-tabs" aria-label="生成模式">
|
||||
{cloneOutputOptions.map((option) => (
|
||||
<button
|
||||
key={option.key}
|
||||
type="button"
|
||||
className={cloneOutput === option.key ? "is-active" : ""}
|
||||
onClick={() => handleCloneOutputChange(option.key)}
|
||||
>
|
||||
<span className={`ecom-command-mode-icon ecom-command-mode-icon--${option.key}`} aria-hidden="true">{option.icon}</span>
|
||||
<strong>{option.label}</strong>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="clone-ai-input-wrapper ecom-command-composer">
|
||||
<button
|
||||
type="button"
|
||||
className={`ecom-command-reference${productImages.length ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||
onClick={() => productInputRef.current?.click()}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault();
|
||||
setIsProductUploadDragging(true);
|
||||
}}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDragLeave={() => setIsProductUploadDragging(false)}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
setIsProductUploadDragging(false);
|
||||
const files = Array.from(event.dataTransfer.files);
|
||||
if (files.length) addComposerAssets(files);
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true"><CloudUploadOutlined /></span>
|
||||
<strong>上传商品图</strong>
|
||||
</button>
|
||||
{productImages.length ? (
|
||||
<div className="ecom-command-asset-popover" aria-label="已上传素材">
|
||||
{productImages.map((image) => (
|
||||
@@ -5115,6 +5102,24 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
<button type="button" className="ecom-command-asset-add" onClick={() => productInputRef.current?.click()} aria-label="继续上传">+</button>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="ecom-command-option-row ecom-command-option-row--settings">
|
||||
<button type="button" className={composerMenu === "platform" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("platform", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><GlobalOutlined /></span>
|
||||
<span>平台</span>{platform}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "language" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("language", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><FileImageOutlined /></span>
|
||||
<span>语种</span>{language}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "ratio" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("ratio", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><TableOutlined /></span>
|
||||
<span>比例</span>{formatRatioDisplayValue(ratio)}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "settings" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("settings", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><SettingOutlined /></span>
|
||||
<span>设置</span>{composerSettingLabel}
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
ref={requirementTextareaRef}
|
||||
value={requirement}
|
||||
@@ -5135,10 +5140,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
<ImageMentionMenu images={ecommerceMentionImages} query={requirementImageMentionQuery} onSelect={insertRequirementImageMention} />
|
||||
) : null}
|
||||
<div className="ecom-command-toolbar" aria-label="生成设置">
|
||||
<div className="ecom-command-option-row">
|
||||
<div className="ecom-command-composer-actions">
|
||||
<button
|
||||
type="button"
|
||||
className={`ecom-command-reference ecom-command-reference--inline${productImages.length ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||
className={`ecom-command-reference ecom-command-reference--bottom${productImages.length ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||
onClick={() => productInputRef.current?.click()}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault();
|
||||
@@ -5156,26 +5161,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
||||
<span aria-hidden="true"><PaperClipOutlined /></span>
|
||||
<strong>上传素材</strong>
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "mode" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("mode", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><AppstoreOutlined /></span>
|
||||
{selectedCloneOutput.label}<span>模式</span>
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "platform" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("platform", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><GlobalOutlined /></span>
|
||||
<span>平台</span>{platform}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "language" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("language", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><FileImageOutlined /></span>
|
||||
<span>语种</span>{language}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "ratio" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("ratio", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><TableOutlined /></span>
|
||||
<span>比例</span>{formatRatioDisplayValue(ratio)}
|
||||
</button>
|
||||
<button type="button" className={composerMenu === "settings" ? "is-active" : ""} onClick={(event) => toggleComposerMenu("settings", event)}>
|
||||
<span className="ecom-command-option-icon" aria-hidden="true"><SettingOutlined /></span>
|
||||
<span>设置</span>{composerSettingLabel}
|
||||
</button>
|
||||
</div>
|
||||
<div className="ecom-command-submit-row">
|
||||
<button type="button" className="clone-ai-send-button ecom-command-send" disabled={commandGenerateDisabled} onClick={handleCommandGenerate} aria-label={clonePrimaryLabel}>
|
||||
|
||||
Reference in New Issue
Block a user