Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86e0f83f73 | |||
| 2bc6fb7ab1 | |||
| 65be92ba43 | |||
| 98acb79a20 | |||
| d819cecfc6 | |||
| 2c3c6eb2c9 | |||
| d83ad25be3 | |||
| eb7b769155 | |||
| ad38a4a0e3 |
@@ -50,6 +50,8 @@ import EcommerceDetailPanel from "./panels/EcommerceDetailPanel";
|
|||||||
import EcommerceSetPanel from "./panels/EcommerceSetPanel";
|
import EcommerceSetPanel from "./panels/EcommerceSetPanel";
|
||||||
import EcommerceTryOnPanel from "./panels/EcommerceTryOnPanel";
|
import EcommerceTryOnPanel from "./panels/EcommerceTryOnPanel";
|
||||||
import EcommerceClonePanel from "./panels/EcommerceClonePanel";
|
import EcommerceClonePanel from "./panels/EcommerceClonePanel";
|
||||||
|
import EcommerceCopywritingPanel from "./panels/EcommerceCopywritingPanel";
|
||||||
|
import EcommerceOneClickVideoPanel from "./panels/EcommerceOneClickVideoPanel";
|
||||||
import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
|
import { ecommerceOssScopes, saveUnifiedEcommerceGenerationRecord, deleteEcommerceGenerationRecord } from "./ecommerceGenerationPersistence";
|
||||||
import { downloadResultAsset } from "../workbench/workbenchDownload";
|
import { downloadResultAsset } from "../workbench/workbenchDownload";
|
||||||
import type { CloneOutputKey, ProductSetOutputKey } from "./utils/platformRules";
|
import type { CloneOutputKey, ProductSetOutputKey } from "./utils/platformRules";
|
||||||
@@ -1884,7 +1886,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<ProductSetPreviewSelection | null>(null);
|
const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<ProductSetPreviewSelection | null>(null);
|
||||||
const [showHostingModal, setShowHostingModal] = useState(false);
|
const [showHostingModal, setShowHostingModal] = useState(false);
|
||||||
const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
|
const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
|
||||||
const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | "hot" | 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 [smartCutoutImage, setSmartCutoutImage] = useState<SmartCutoutImageItem | null>(null);
|
||||||
const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
|
const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
|
||||||
const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
|
const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
|
||||||
@@ -1959,6 +1961,11 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const [cloneVideoSmart, setCloneVideoSmart] = useState(true);
|
const [cloneVideoSmart, setCloneVideoSmart] = useState(true);
|
||||||
const [isCloneSettingsCollapsed, setIsCloneSettingsCollapsed] = useState(false);
|
const [isCloneSettingsCollapsed, setIsCloneSettingsCollapsed] = useState(false);
|
||||||
const [isCloneConversationCollapsed, setIsCloneConversationCollapsed] = useState(false);
|
const [isCloneConversationCollapsed, setIsCloneConversationCollapsed] = useState(false);
|
||||||
|
const [quickSetStatus, setQuickSetStatus] = useState<"idle" | "generating" | "done" | "failed">("idle");
|
||||||
|
const [quickSetResultUrls, setQuickSetResultUrls] = useState<string[]>([]);
|
||||||
|
const [quickSetProgress, setQuickSetProgress] = useState(0);
|
||||||
|
const [quickSetRequirement, setQuickSetRequirement] = useState("");
|
||||||
|
const quickSetProgressRef = useRef<number | null>(null);
|
||||||
const [previewZoom, setPreviewZoom] = useState(1);
|
const [previewZoom, setPreviewZoom] = useState(1);
|
||||||
const quickSetSelectTimerRef = useRef<number | null>(null);
|
const quickSetSelectTimerRef = useRef<number | null>(null);
|
||||||
const openQuickSetSelectRef = useRef<CloneBasicSelectKey | null>(null);
|
const openQuickSetSelectRef = useRef<CloneBasicSelectKey | null>(null);
|
||||||
@@ -2456,6 +2463,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const canGenerateTryOn = garmentImages.length > 0 && tryOnStatus !== "generating" && tryOnStatus !== "modeling";
|
const canGenerateTryOn = garmentImages.length > 0 && tryOnStatus !== "generating" && tryOnStatus !== "modeling";
|
||||||
const canGenerateDetail = detailProductImages.length > 0 && detailStatus !== "generating";
|
const canGenerateDetail = detailProductImages.length > 0 && detailStatus !== "generating";
|
||||||
const canGenerateHot = cloneReferenceImages.length > 0 && hotStatus !== "generating";
|
const canGenerateHot = cloneReferenceImages.length > 0 && hotStatus !== "generating";
|
||||||
|
const canGenerateQuickSet = productImages.length > 0 && quickSetStatus !== "generating";
|
||||||
const cloneVideoDurationProgress =
|
const cloneVideoDurationProgress =
|
||||||
((cloneVideoDuration - cloneVideoDurationMin) / (cloneVideoDurationMax - cloneVideoDurationMin)) * 100;
|
((cloneVideoDuration - cloneVideoDurationMin) / (cloneVideoDurationMax - cloneVideoDurationMin)) * 100;
|
||||||
const cloneVideoDurationStyle: CSSProperties = useMemo(
|
const cloneVideoDurationStyle: CSSProperties = useMemo(
|
||||||
@@ -4810,6 +4818,12 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleQuickSetAiWrite = () => {
|
||||||
|
setQuickSetRequirement(
|
||||||
|
"1.产品名称:无线降噪蓝牙耳机\n2.核心卖点:主动降噪、24H续航、低延迟连接、舒适佩戴\n3.适用人群:通勤、办公、运动和旅行用户\n4.期望场景:地铁通勤、居家办公、户外运动\n5.具体参数:蓝牙5.3、IPX4防水、快充10分钟使用2小时",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const stopHotProgress = () => {
|
const stopHotProgress = () => {
|
||||||
if (hotProgressRef.current !== null) {
|
if (hotProgressRef.current !== null) {
|
||||||
window.clearInterval(hotProgressRef.current);
|
window.clearInterval(hotProgressRef.current);
|
||||||
@@ -4817,6 +4831,27 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stopQuickSetProgress = () => {
|
||||||
|
if (quickSetProgressRef.current !== null) {
|
||||||
|
window.clearInterval(quickSetProgressRef.current);
|
||||||
|
quickSetProgressRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startQuickSetProgress = () => {
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setQuickSetProgress(0);
|
||||||
|
quickSetProgressRef.current = window.setInterval(() => {
|
||||||
|
setQuickSetProgress((prev) => {
|
||||||
|
if (prev >= 90) {
|
||||||
|
stopQuickSetProgress();
|
||||||
|
return 90;
|
||||||
|
}
|
||||||
|
return prev + (90 - prev) * 0.06;
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
const startHotProgress = () => {
|
const startHotProgress = () => {
|
||||||
stopHotProgress();
|
stopHotProgress();
|
||||||
setHotProgress(0);
|
setHotProgress(0);
|
||||||
@@ -4854,6 +4889,42 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleQuickSetGenerate = () => {
|
||||||
|
if (!canGenerateQuickSet) return;
|
||||||
|
imageAbortRef.current = { current: false };
|
||||||
|
lastFailedActionRef.current = null;
|
||||||
|
startQuickSetProgress();
|
||||||
|
setQuickSetStatus("generating");
|
||||||
|
void generateSetImages(
|
||||||
|
productImages, cloneSetCounts, quickSetRequirement,
|
||||||
|
platform, ratio, language, market,
|
||||||
|
(s) => {
|
||||||
|
setQuickSetStatus(s as ProductCloneStatus);
|
||||||
|
if (s === "done") {
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setQuickSetProgress(100);
|
||||||
|
} else if (s === "failed") {
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setQuickSetProgress(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(urls) => {
|
||||||
|
setQuickSetResultUrls(urls);
|
||||||
|
const validUrls = urls.filter(Boolean);
|
||||||
|
if (validUrls.length) {
|
||||||
|
setQuickSetStatus("done");
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setQuickSetProgress(100);
|
||||||
|
} else {
|
||||||
|
setQuickSetStatus("failed");
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setQuickSetProgress(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
lastFailedActionRef.current = () => handleQuickSetGenerate();
|
||||||
|
};
|
||||||
|
|
||||||
const handleHotMaterialMouseEnter = (src: string, event: ReactMouseEvent<HTMLElement>) => {
|
const handleHotMaterialMouseEnter = (src: string, event: ReactMouseEvent<HTMLElement>) => {
|
||||||
const rect = event.currentTarget.getBoundingClientRect();
|
const rect = event.currentTarget.getBoundingClientRect();
|
||||||
const previewHalfWidth = 150;
|
const previewHalfWidth = 150;
|
||||||
@@ -4918,6 +4989,57 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
setComposerMenu(null);
|
setComposerMenu(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openQuickSetPage = () => {
|
||||||
|
clearSmartCutoutTransition();
|
||||||
|
setActiveQuickTool("quick-set");
|
||||||
|
setComposerMenu(null);
|
||||||
|
setIsCloneSettingsCollapsed(false);
|
||||||
|
setIsQuickPanelCollapsed(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeQuickSetPage = () => {
|
||||||
|
stopQuickSetProgress();
|
||||||
|
setActiveQuickTool(null);
|
||||||
|
setQuickSetStatus("idle");
|
||||||
|
setQuickSetResultUrls([]);
|
||||||
|
setQuickSetProgress(0);
|
||||||
|
setQuickSetRequirement("");
|
||||||
|
setComposerMenu(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCopywritingPage = () => {
|
||||||
|
clearSmartCutoutTransition();
|
||||||
|
setActiveQuickTool("copywriting");
|
||||||
|
setComposerMenu(null);
|
||||||
|
setIsCloneSettingsCollapsed(false);
|
||||||
|
setIsQuickPanelCollapsed(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeCopywritingPage = () => {
|
||||||
|
setActiveQuickTool(null);
|
||||||
|
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 = () => {
|
const resetTask = () => {
|
||||||
setSetImages([]);
|
setSetImages([]);
|
||||||
setProductSetRequirement("");
|
setProductSetRequirement("");
|
||||||
@@ -4981,6 +5103,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
|
const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
|
||||||
const isTranslateTool = isCloneTool && activeQuickTool === "translate";
|
const isTranslateTool = isCloneTool && activeQuickTool === "translate";
|
||||||
const isHotCloneTool = isCloneTool && activeQuickTool === "hot";
|
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 pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
|
||||||
const setPrimaryLabel =
|
const setPrimaryLabel =
|
||||||
setImages.length === 0
|
setImages.length === 0
|
||||||
@@ -5406,6 +5531,19 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(hotRatio), options: quickSetRatioOptions, onChange: setHotRatio },
|
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(hotRatio), options: quickSetRatioOptions, onChange: setHotRatio },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const quickSetBasicSelects: Array<{
|
||||||
|
key: CloneBasicSelectKey;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
options: string[];
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}> = [
|
||||||
|
{ key: "platform", label: "平台", value: platform, options: platformOptions, onChange: setPlatform },
|
||||||
|
{ key: "market", label: "国家", value: market, options: marketOptions, onChange: setMarket },
|
||||||
|
{ key: "language", label: "语种", value: language, options: cloneLanguageOptions, onChange: setLanguage },
|
||||||
|
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(ratio), options: quickSetRatioOptions, onChange: setRatio },
|
||||||
|
];
|
||||||
|
|
||||||
const cloneModelSelects: Array<{
|
const cloneModelSelects: Array<{
|
||||||
key: CloneModelSelectKey;
|
key: CloneModelSelectKey;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -6469,15 +6607,24 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{ label: "智能抠图", tone: "cutout", icon: <ScissorOutlined />, onClick: openSmartCutoutUpload },
|
{ label: "智能抠图", tone: "cutout", icon: <ScissorOutlined />, onClick: openSmartCutoutUpload },
|
||||||
{ label: "去除水印", tone: "watermark", icon: <ClearOutlined />, onClick: openWatermarkRemovalPage },
|
{ label: "去除水印", tone: "watermark", icon: <ClearOutlined />, onClick: openWatermarkRemovalPage },
|
||||||
{ label: "图片翻译", tone: "translate", icon: <GlobalOutlined />, onClick: openImageTranslatePage },
|
{ 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) => (
|
].map((item) => (
|
||||||
<button
|
<button
|
||||||
key={item.label}
|
key={item.label}
|
||||||
type="button"
|
type="button"
|
||||||
className={`ecom-command-quick-card ecom-command-quick-card--${item.tone}`}
|
className={`ecom-command-quick-card ecom-command-quick-card--${item.tone}`}
|
||||||
|
disabled={item.disabled}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
if (item.disabled) {
|
||||||
|
toast.info("更多功能即将上线,敬请期待!");
|
||||||
|
} else {
|
||||||
item.onClick?.();
|
item.onClick?.();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">{item.icon}</span>
|
<span aria-hidden="true">{item.icon}</span>
|
||||||
@@ -7483,6 +7630,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
|
|
||||||
const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
||||||
const quickHotVisibleSelect = quickHotBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
const quickHotVisibleSelect = quickHotBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
||||||
|
const quickSetVisibleSelect = quickSetBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
||||||
|
|
||||||
const quickDetailPreview = (
|
const quickDetailPreview = (
|
||||||
<main key="quick-detail" className={`ecom-quick-set-page ecom-quick-detail-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
|
<main key="quick-detail" className={`ecom-quick-set-page ecom-quick-detail-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
|
||||||
@@ -7944,6 +8092,266 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const quickSetGenPreview = (
|
||||||
|
<main key="quick-set" className={`ecom-quick-set-page ecom-quick-hot-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="电商套图生成">
|
||||||
|
<div className="ecom-quick-set-body">
|
||||||
|
<aside className="ecom-quick-set-panel" aria-label="电商套图设置" onWheel={handleQuickPanelWheel}>
|
||||||
|
<header className="ecom-quick-set-panel-head">
|
||||||
|
<strong className="ecom-quick-set-page-title">电商套图</strong>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={closeQuickSetPage}>首页</button>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={closeQuickSetPage}>上一页</button>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<strong><FileImageOutlined /> 上传商品原图</strong>
|
||||||
|
{productImages.length ? (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material has-images${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => productInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, productInputRef)}
|
||||||
|
onDragOver={(event) => { event.preventDefault(); event.stopPropagation(); if (event.dataTransfer.types.includes("Files")) setIsProductUploadDragging(true); }}
|
||||||
|
onDragLeave={(event) => { event.preventDefault(); event.stopPropagation(); if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) setIsProductUploadDragging(false); }}
|
||||||
|
onDrop={(event) => { event.preventDefault(); event.stopPropagation(); setIsProductUploadDragging(false); const files = Array.from(event.dataTransfer.files); if (files.length) addProductImages(files); }}
|
||||||
|
>
|
||||||
|
{renderQuickUploadThumbs(productImages, removeProductImage)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-hot-add-btn"
|
||||||
|
aria-label="添加更多素材"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
productInputRef.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => productInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, productInputRef)}
|
||||||
|
onDragOver={(event) => { event.preventDefault(); event.stopPropagation(); if (event.dataTransfer.types.includes("Files")) setIsProductUploadDragging(true); }}
|
||||||
|
onDragLeave={(event) => { event.preventDefault(); event.stopPropagation(); if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) setIsProductUploadDragging(false); }}
|
||||||
|
onDrop={(event) => { event.preventDefault(); event.stopPropagation(); setIsProductUploadDragging(false); const files = Array.from(event.dataTransfer.files); if (files.length) addProductImages(files); }}
|
||||||
|
>
|
||||||
|
<FileImageOutlined />
|
||||||
|
<span>拖拽或点击上传</span>
|
||||||
|
<em>同一产品,最多 7 张</em>
|
||||||
|
<b>+ 上传图片</b>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={productInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
className="ecom-command-hidden-file"
|
||||||
|
onChange={handleProductUpload}
|
||||||
|
aria-label="上传商品图片"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section className="ecom-quick-set-basic-section">
|
||||||
|
<span className="ecom-quick-set-label">基础设置</span>
|
||||||
|
<div className="ecom-quick-set-select-anchor">
|
||||||
|
<div className="ecom-quick-set-selects">
|
||||||
|
{quickSetBasicSelects.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.key}
|
||||||
|
type="button"
|
||||||
|
className={openQuickSetSelect === item.key ? "is-active" : ""}
|
||||||
|
onClick={() => toggleQuickSetSelect(item.key)}
|
||||||
|
>
|
||||||
|
<span>{item.label}</span><strong>{formatRatioDisplayValue(item.value)}</strong><em>⌄</em>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{quickSetVisibleSelect ? (
|
||||||
|
<div
|
||||||
|
className={`ecom-quick-set-dropdown ecom-quick-set-dropdown--${quickSetVisibleSelect.key}${isQuickSetSelectClosing ? " is-closing" : ""}`}
|
||||||
|
role="listbox"
|
||||||
|
aria-label={quickSetVisibleSelect.label}
|
||||||
|
>
|
||||||
|
{quickSetVisibleSelect.options.map((option) => (
|
||||||
|
<button
|
||||||
|
key={option}
|
||||||
|
type="button"
|
||||||
|
className={quickSetVisibleSelect.value === option ? "is-active" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
quickSetVisibleSelect.onChange(option);
|
||||||
|
closeQuickSetSelect();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatRatioDisplayValue(option)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="ecom-quick-set-count-section">
|
||||||
|
<span className="ecom-quick-set-label">图片数量</span>
|
||||||
|
<p className="ecom-quick-set-hint">可自由调整各类型图片数量,总数 1-16 张</p>
|
||||||
|
<div className="ecom-quick-set-counts">
|
||||||
|
{cloneSetCountOptions.map((item) => {
|
||||||
|
const count = cloneSetCounts[item.key];
|
||||||
|
const decrementDisabled = count <= 0 || cloneSetTotal <= minCloneSetTotal;
|
||||||
|
const incrementDisabled = cloneSetTotal >= maxCloneSetTotal;
|
||||||
|
return (
|
||||||
|
<div key={item.key} className="ecom-quick-set-count-row">
|
||||||
|
<div className="ecom-quick-set-count-info">
|
||||||
|
<strong>{item.title}</strong>
|
||||||
|
<span>{item.desc}</span>
|
||||||
|
</div>
|
||||||
|
<div className="clone-ai-count-stepper" aria-label={`${item.title}数量`}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={decrementDisabled}
|
||||||
|
onPointerDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
startCloneSetCountHold(item.key, -1, decrementDisabled);
|
||||||
|
}}
|
||||||
|
onPointerUp={clearCloneSetCountHold}
|
||||||
|
onPointerLeave={clearCloneSetCountHold}
|
||||||
|
onPointerCancel={clearCloneSetCountHold}
|
||||||
|
onBlur={clearCloneSetCountHold}
|
||||||
|
aria-label={`减少${item.title}`}
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<b>{count}</b>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={incrementDisabled}
|
||||||
|
onPointerDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
startCloneSetCountHold(item.key, 1, incrementDisabled);
|
||||||
|
}}
|
||||||
|
onPointerUp={clearCloneSetCountHold}
|
||||||
|
onPointerLeave={clearCloneSetCountHold}
|
||||||
|
onPointerCancel={clearCloneSetCountHold}
|
||||||
|
onBlur={clearCloneSetCountHold}
|
||||||
|
aria-label={`增加${item.title}`}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="ecom-quick-hot-requirement">
|
||||||
|
<div className="ecom-quick-hot-requirement__head">
|
||||||
|
<strong>商品卖点 & 需求</strong>
|
||||||
|
<button type="button" className="ecom-quick-hot-requirement__ai" onClick={handleQuickSetAiWrite}>AI 帮写</button>
|
||||||
|
</div>
|
||||||
|
<div className="ecom-quick-hot-requirement__input">
|
||||||
|
<textarea
|
||||||
|
value={quickSetRequirement}
|
||||||
|
onChange={(event) => setQuickSetRequirement(event.target.value.slice(0, 500))}
|
||||||
|
placeholder="建议包含以下信息:产品名称、核心卖点、期望场景、具体参数"
|
||||||
|
maxLength={500}
|
||||||
|
/>
|
||||||
|
<span>{quickSetRequirement.length}/500</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div className="ecom-quick-hot-actions">
|
||||||
|
<button type="button" className="ecom-quick-set-primary ecom-quick-hot-generate" onClick={handleQuickSetGenerate} disabled={!canGenerateQuickSet}>
|
||||||
|
{quickSetStatus === "generating" ? <LoadingOutlined /> : "✦"} 开始生成
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`ecom-quick-set-primary ecom-quick-set-primary--cancel${quickSetStatus !== "generating" ? " is-disabled" : ""}`}
|
||||||
|
onClick={quickSetStatus === "generating" ? handleCancelGenerate : undefined}
|
||||||
|
disabled={quickSetStatus !== "generating"}
|
||||||
|
>
|
||||||
|
取消生成
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<section className="ecom-quick-set-stage">
|
||||||
|
<header className="ecom-quick-set-preview-head">
|
||||||
|
<h1>预览</h1>
|
||||||
|
<p>上传商品图,AI 即刻生成 <span>符合多电商平台规范</span> 的高转化率商品套图。</p>
|
||||||
|
<div>
|
||||||
|
<button type="button" onClick={() => setPreviewZoom((value) => Math.max(0.25, value - 0.1))}>-</button>
|
||||||
|
<strong>{Math.round(previewZoom * 100)}%</strong>
|
||||||
|
<button type="button" onClick={() => setPreviewZoom((value) => Math.min(2, value + 0.1))}>+</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div className="ecom-quick-set-canvas" onWheel={handleQuickPreviewWheel}>
|
||||||
|
{quickSetStatus === "done" && quickSetResultUrls.length > 0 ? (
|
||||||
|
<section className="ecom-quick-detail-result" style={{ transform: `scale(${previewZoom})` }}>
|
||||||
|
<div className="ecom-quick-set-result-grid">
|
||||||
|
{quickSetResultUrls.map((url, index) => (
|
||||||
|
<figure key={`quick-set-${index}`}>
|
||||||
|
<img src={url} alt={`套图 ${index + 1}`} />
|
||||||
|
<span>套图 {index + 1}</span>
|
||||||
|
</figure>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-detail-download"
|
||||||
|
onClick={() => {
|
||||||
|
quickSetResultUrls.forEach((url, index) => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = `电商套图-${index + 1}-${Date.now()}.png`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloudUploadOutlined />
|
||||||
|
保存本地
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
) : quickSetStatus === "generating" ? (
|
||||||
|
<section className="ecom-quick-set-generating">
|
||||||
|
<LoadingOutlined />
|
||||||
|
<strong>正在生成电商套图</strong>
|
||||||
|
<span>AI 正在根据您的商品图和设置生成套图素材,请稍候...</span>
|
||||||
|
<div className="ecom-quick-set-progress">
|
||||||
|
<div className="ecom-quick-set-progress-bar" style={{ width: `${Math.round(quickSetProgress)}%` }} />
|
||||||
|
</div>
|
||||||
|
<em className="ecom-quick-set-progress-text">{Math.round(quickSetProgress)}%</em>
|
||||||
|
</section>
|
||||||
|
) : quickSetStatus === "failed" ? (
|
||||||
|
<section className="ecom-quick-set-failed">
|
||||||
|
<FrownOutlined />
|
||||||
|
<strong>生成失败</strong>
|
||||||
|
<span>请检查网络或重试,如余额不足请先充值。</span>
|
||||||
|
<button type="button" onClick={handleQuickSetGenerate} disabled={!canGenerateQuickSet}>重新生成</button>
|
||||||
|
</section>
|
||||||
|
) : productImages.length ? (
|
||||||
|
<section className="ecom-quick-detail-preview-card" style={{ transform: `scale(${previewZoom})` }}>
|
||||||
|
{detailGridSamples.slice(0, 6).map((src, index) => (
|
||||||
|
<figure key={src}>
|
||||||
|
<img src={src} alt={`套图预览 ${index + 1}`} />
|
||||||
|
<span>{detailModules[index]?.title ?? "套图模块"}</span>
|
||||||
|
</figure>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
) : (
|
||||||
|
<section className="ecom-quick-set-empty">
|
||||||
|
<FileImageOutlined />
|
||||||
|
<strong>等待生成</strong>
|
||||||
|
<span>上传商品原图并填写信息后,AI 将在这里展示生成结果。</span>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传商品图后,选择平台和套图数量即可生成电商套图。")}>?</button>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
|
||||||
const detailPreview = (
|
const detailPreview = (
|
||||||
<main className="product-clone-preview product-clone-preview--detail" aria-label="A+详情预览" onWheel={handlePreviewWheel}>
|
<main className="product-clone-preview product-clone-preview--detail" aria-label="A+详情预览" onWheel={handlePreviewWheel}>
|
||||||
<div className="product-clone-preview__headline">
|
<div className="product-clone-preview__headline">
|
||||||
@@ -8039,6 +8447,48 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const copywritingPreview = (
|
||||||
|
<div key="copywriting" className="ecom-quick-page-wrap ecom-tool-page-enter">
|
||||||
|
<EcommerceCopywritingPanel onClose={closeCopywritingPage} />
|
||||||
|
</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
|
const activePreview = isSetTool
|
||||||
? setPreview
|
? setPreview
|
||||||
: isDetail
|
: isDetail
|
||||||
@@ -8066,9 +8516,19 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{hotClonePreview}
|
{hotClonePreview}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
: isQuickSetTool
|
||||||
|
? (
|
||||||
|
<div key={`quick-${activeQuickTool}`} className="ecom-quick-page-wrap ecom-tool-page-enter">
|
||||||
|
{quickSetGenPreview}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: isCopywritingTool
|
||||||
|
? copywritingPreview
|
||||||
|
: isOneClickVideoTool
|
||||||
|
? oneClickVideoPreview
|
||||||
: clonePreview
|
: clonePreview
|
||||||
: placeholderPreview;
|
: placeholderPreview;
|
||||||
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool;
|
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isQuickSetTool && !isCopywritingTool && !isOneClickVideoTool;
|
||||||
const isRecordDetailWorkspace = isMainCloneWorkspace && Boolean(activeHistoryRecordId);
|
const isRecordDetailWorkspace = isMainCloneWorkspace && Boolean(activeHistoryRecordId);
|
||||||
const currentResultCount = canvasNodes.reduce((count, node) => count + node.results.length, 0);
|
const currentResultCount = canvasNodes.reduce((count, node) => count + node.results.length, 0);
|
||||||
const activeHistoryRecord = activeHistoryRecordId ? ecommerceHistoryRecords.find((record) => record.id === activeHistoryRecordId) : null;
|
const activeHistoryRecord = activeHistoryRecordId ? ecommerceHistoryRecords.find((record) => record.id === activeHistoryRecordId) : null;
|
||||||
@@ -8104,7 +8564,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<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" : ""}`}
|
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}
|
data-tool={activeTool}
|
||||||
aria-label={pageLabel}
|
aria-label={pageLabel}
|
||||||
>
|
>
|
||||||
@@ -8125,10 +8585,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
aria-label={`${pageLabel}参数`}
|
aria-label={`${pageLabel}参数`}
|
||||||
aria-hidden={isCloneTool && isCloneSettingsCollapsed ? true : undefined}
|
aria-hidden={isCloneTool && isCloneSettingsCollapsed ? true : undefined}
|
||||||
>
|
>
|
||||||
{isSetTool ? setPanel : isDetail ? detailPanel : isTryOn ? tryOnPanel : isCloneTool ? clonePanel : placeholderPanel}
|
{isSetTool ? setPanel : isDetail ? detailPanel : isTryOn ? tryOnPanel : isCopywritingTool ? placeholderPanel : isCloneTool ? clonePanel : placeholderPanel}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isHotCloneTool ? (
|
{isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isHotCloneTool && !isQuickSetTool && !isCopywritingTool ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="clone-ai-settings-toggle"
|
className="clone-ai-settings-toggle"
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
AppstoreOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
|
FireOutlined,
|
||||||
|
GlobalOutlined,
|
||||||
|
MessageOutlined,
|
||||||
|
SmileOutlined,
|
||||||
|
ThunderboltOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
export type CopywritingType =
|
||||||
|
| "self-media"
|
||||||
|
| "universal"
|
||||||
|
| "original"
|
||||||
|
| "imitate"
|
||||||
|
| "wechat"
|
||||||
|
| "crossborder"
|
||||||
|
| "emoji"
|
||||||
|
| "more";
|
||||||
|
|
||||||
|
interface CopywritingTypeOption {
|
||||||
|
key: CopywritingType;
|
||||||
|
label: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const copywritingTypes: CopywritingTypeOption[] = [
|
||||||
|
{ key: "self-media", label: "自媒体文案", icon: <MessageOutlined />, description: "小红书/抖音/公众号风格" },
|
||||||
|
{ key: "universal", label: "万能写作", icon: <EditOutlined />, description: "通用场景长文短句" },
|
||||||
|
{ key: "original", label: "一键原创", icon: <ThunderboltOutlined />, description: "快速改写去重" },
|
||||||
|
{ key: "imitate", label: "文案仿写", icon: <CopyOutlined />, description: "参照爆款风格重写" },
|
||||||
|
{ key: "wechat", label: "微信营销文案", icon: <FileTextOutlined />, description: "朋友圈/社群转化文案" },
|
||||||
|
{ key: "crossborder", label: "跨境商品文案", icon: <GlobalOutlined />, description: "Amazon/Temu 卖点描述" },
|
||||||
|
{ key: "emoji", label: "文案加Emoji", icon: <SmileOutlined />, description: "自动插入表情符号" },
|
||||||
|
{ key: "more", label: "更多场景", icon: <AppstoreOutlined />, description: "持续更新中" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const wordCountOptions = ["不限", "100字", "300字", "500字", "800字"];
|
||||||
|
|
||||||
|
const exampleResults: Record<CopywritingType, Array<{ title: string; body: string; points: string[] }>> = {
|
||||||
|
"self-media": [
|
||||||
|
{
|
||||||
|
title: "超值干发神器,吸水力 MAX!",
|
||||||
|
body: "家人们,我发现了一款干发帽,双层加厚吸水力超强!而且只要个位数就能到手啊!",
|
||||||
|
points: [
|
||||||
|
"超强吸水力:这款干发帽采用微纤维材质,轻轻一裹,水分立马被吸走,头发快速告别湿漉漉。",
|
||||||
|
"柔软亲肤:触感超级柔软,对皮肤和头发都是温柔的抚摸,不会有摩擦伤害哦。",
|
||||||
|
"加厚设计:比普通干发帽更厚实,吸水效果自然更胜一筹,长发妹子的福音!",
|
||||||
|
"方便携带:轻巧不占空间,不论是去健身房还是旅行,携带都毫无负担。",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
universal: [
|
||||||
|
{
|
||||||
|
title: "直接抄作业!科学的减重方法必试!",
|
||||||
|
body: "姐妹们冲鸭!有很多科学有效的方式可以帮助我们实现理想体重,今天就来分享一下必试的方法!",
|
||||||
|
points: [
|
||||||
|
"快乐有氧运动:科学的减重方式,通过有氧运动如慢跑、游泳等,能够促进脂肪燃烧,让身体更健康!",
|
||||||
|
"均衡饮食规划:摄入足够的蛋白质、蔬果以及谷物,避免过多的高糖和高脂食物,帮助达到减重目标!",
|
||||||
|
"科学计算热量:了解自己每日所需的卡路里摄入量,合理安排每餐的热量搭配,控制总摄入量。",
|
||||||
|
"坚持低强度运动:逐渐增加日常活动量,如步行、瑜伽等,通过持续的轻度运动,加速代谢!",
|
||||||
|
"合理休息调节:不要忽视睡眠的重要性,保证每晚充足的睡眠时间,帮助恢复体力和新陈代谢。",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
original: [
|
||||||
|
{
|
||||||
|
title: "原创种草|这款干发帽真的值得入!",
|
||||||
|
body: "洗完头最烦的就是湿哒哒滴水?试试这条双层加厚干发帽,吸水速度真的惊艳到我。",
|
||||||
|
points: [
|
||||||
|
"加厚材质,吸水更快更彻底",
|
||||||
|
"柔软不勒头,长发短发都能用",
|
||||||
|
"轻便好收纳,差旅党必备",
|
||||||
|
"性价比超高,入手不亏",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
imitate: [
|
||||||
|
{
|
||||||
|
title: "仿写爆款|让头发速干的小心机",
|
||||||
|
body: "姐妹们有没有发现,最近超火的干发帽真的太好用了!轻轻一裹,几分钟头发就半干了。",
|
||||||
|
points: [
|
||||||
|
"双层加厚,吸水力翻倍",
|
||||||
|
"柔软亲肤,不伤发质",
|
||||||
|
"小巧便携,出门也能带",
|
||||||
|
"颜值在线,多色可选",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
wechat: [
|
||||||
|
{
|
||||||
|
title: "朋友圈文案|个位数到手的干发神器",
|
||||||
|
body: "今天必须给大家安利这个干发帽!双层加厚,吸水超强,个位数就能到手,真的不冲吗?",
|
||||||
|
points: [
|
||||||
|
"微纤维材质,轻柔速干",
|
||||||
|
"加厚设计,吸水更彻底",
|
||||||
|
"小巧便携,旅行出差都能带",
|
||||||
|
"限时好价,手慢无",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
crossborder: [
|
||||||
|
{
|
||||||
|
title: "Amazon Listing|Super Absorbent Hair Turban",
|
||||||
|
body: "Made with ultra-soft microfiber, this double-layer hair turban dries hair quickly while protecting delicate strands.",
|
||||||
|
points: [
|
||||||
|
"Double-layer microfiber for maximum absorbency",
|
||||||
|
"Gentle on hair and skin, no frizz or breakage",
|
||||||
|
"Lightweight and travel-friendly design",
|
||||||
|
"Secure button closure stays in place",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
emoji: [
|
||||||
|
{
|
||||||
|
title: "✨个位数到手的干发神器,吸水力 MAX!",
|
||||||
|
body: "家人们👋,我发现了一款超棒的干发帽💧,双层加厚吸水力超强!而且只要个位数就能到手啊🛒!",
|
||||||
|
points: [
|
||||||
|
"💦 超强吸水力:微纤维材质,轻轻一裹水分吸走",
|
||||||
|
"☁️ 柔软亲肤:触感温柔,不伤头发和皮肤",
|
||||||
|
"🎒 方便携带:轻巧不占空间,旅行健身都能带",
|
||||||
|
"💰 超值价格:个位数到手,性价比拉满",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
more: [
|
||||||
|
{
|
||||||
|
title: "更多场景示例",
|
||||||
|
body: "选择左侧具体文案类型即可生成对应场景内容,更多场景持续更新中。",
|
||||||
|
points: ["选择合适的文案类型", "填写内容需求", "选择生成字数", "点击开始生成"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface EcommerceCopywritingPanelProps {
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EcommerceCopywritingPanel({ onClose }: EcommerceCopywritingPanelProps) {
|
||||||
|
const [selectedType, setSelectedType] = useState<CopywritingType>("self-media");
|
||||||
|
const [requirement, setRequirement] = useState("");
|
||||||
|
const [wordCount, setWordCount] = useState("不限");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [results, setResults] = useState<typeof exampleResults["self-media"]>([]);
|
||||||
|
|
||||||
|
const handleGenerate = () => {
|
||||||
|
setLoading(true);
|
||||||
|
setResults([]);
|
||||||
|
// 模拟生成延迟
|
||||||
|
window.setTimeout(() => {
|
||||||
|
setResults(exampleResults[selectedType]);
|
||||||
|
setLoading(false);
|
||||||
|
}, 1200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedTypeLabel = copywritingTypes.find((item) => item.key === selectedType)?.label ?? "文案";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="ecom-copywriting-page ecom-tool-page-enter" aria-label="一键文案">
|
||||||
|
<div className="ecom-copywriting-body">
|
||||||
|
<aside className="ecom-copywriting-panel" aria-label="文案设置">
|
||||||
|
<header className="ecom-copywriting-panel-head">
|
||||||
|
<strong className="ecom-copywriting-page-title">一键文案</strong>
|
||||||
|
<button type="button" className="ecom-copywriting-back" onClick={onClose}>
|
||||||
|
首页
|
||||||
|
</button>
|
||||||
|
<button type="button" className="ecom-copywriting-back" onClick={onClose}>
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="ecom-copywriting-section">
|
||||||
|
<strong className="ecom-copywriting-section-title">选择文案类型</strong>
|
||||||
|
<div className="ecom-copywriting-type-grid">
|
||||||
|
{copywritingTypes.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.key}
|
||||||
|
type="button"
|
||||||
|
className={`ecom-copywriting-type-card${selectedType === item.key ? " is-active" : ""}`}
|
||||||
|
onClick={() => setSelectedType(item.key)}
|
||||||
|
>
|
||||||
|
<span className="ecom-copywriting-type-icon" aria-hidden="true">
|
||||||
|
{item.icon}
|
||||||
|
</span>
|
||||||
|
<span className="ecom-copywriting-type-label">{item.label}</span>
|
||||||
|
<span className="ecom-copywriting-type-desc">{item.description}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="ecom-copywriting-section">
|
||||||
|
<strong className="ecom-copywriting-section-title">内容需求</strong>
|
||||||
|
<textarea
|
||||||
|
className="ecom-copywriting-textarea"
|
||||||
|
value={requirement}
|
||||||
|
onChange={(event) => setRequirement(event.target.value)}
|
||||||
|
placeholder="例如:主题、核心卖点、适用人群、期望场景等"
|
||||||
|
rows={5}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="ecom-copywriting-section">
|
||||||
|
<strong className="ecom-copywriting-section-title">生成字数</strong>
|
||||||
|
<div className="ecom-copywriting-wordcount">
|
||||||
|
{wordCountOptions.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item}
|
||||||
|
type="button"
|
||||||
|
className={wordCount === item ? "is-active" : ""}
|
||||||
|
onClick={() => setWordCount(item)}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-copywriting-generate"
|
||||||
|
onClick={handleGenerate}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<>
|
||||||
|
<span className="ecom-copywriting-spinner" />
|
||||||
|
生成中…
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ThunderboltOutlined />
|
||||||
|
开始生成
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section className="ecom-copywriting-stage" aria-label="生成文案预览">
|
||||||
|
<header className="ecom-copywriting-preview-head">
|
||||||
|
<h1>生成文案</h1>
|
||||||
|
<p>
|
||||||
|
基于 <span>{selectedTypeLabel}</span> 风格,AI 为你生成高转化文案。
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="ecom-copywriting-results">
|
||||||
|
{results.length === 0 && !loading ? (
|
||||||
|
<div className="ecom-copywriting-empty">
|
||||||
|
<FileTextOutlined />
|
||||||
|
<strong>等待生成</strong>
|
||||||
|
<em>填写需求后点击「开始生成」即可查看文案结果</em>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="ecom-copywriting-loading">
|
||||||
|
<span className="ecom-copywriting-spinner" />
|
||||||
|
<span>AI 正在生成文案,请稍候…</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{results.map((item, index) => (
|
||||||
|
<article key={index} className="ecom-copywriting-result-card">
|
||||||
|
<header>
|
||||||
|
<span>示例 {index + 1}</span>
|
||||||
|
<strong>{item.title}</strong>
|
||||||
|
</header>
|
||||||
|
<p className="ecom-copywriting-result-body">{item.body}</p>
|
||||||
|
<ul className="ecom-copywriting-result-points">
|
||||||
|
{item.points.map((point, pointIndex) => (
|
||||||
|
<li key={pointIndex}>
|
||||||
|
<span>{pointIndex + 1}</span>
|
||||||
|
{point}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,408 @@
|
|||||||
|
import {
|
||||||
|
FileImageOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
ThunderboltOutlined,
|
||||||
|
VideoCameraOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { useMemo, useRef, useState, type ChangeEvent, type DragEvent, type KeyboardEvent, type RefObject } from "react";
|
||||||
|
import EcommerceVideoWorkspace from "../EcommerceVideoWorkspace";
|
||||||
|
|
||||||
|
interface CloneImageItem {
|
||||||
|
id: string;
|
||||||
|
src: string;
|
||||||
|
name: string;
|
||||||
|
file?: File;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloneVideoQualityKey = "standard" | "high" | "ultra";
|
||||||
|
|
||||||
|
interface EcommerceOneClickVideoPanelProps {
|
||||||
|
onClose: () => void;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
onRequestLogin: () => void;
|
||||||
|
productImages: CloneImageItem[];
|
||||||
|
productInputRef: RefObject<HTMLInputElement>;
|
||||||
|
isProductUploadDragging: boolean;
|
||||||
|
setIsProductUploadDragging: (value: boolean) => void;
|
||||||
|
handleProductDrop: (event: DragEvent<HTMLDivElement>) => void;
|
||||||
|
handleProductUpload: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
removeProductImage: (imageId: string) => void;
|
||||||
|
maxProductImages: number;
|
||||||
|
requirement: string;
|
||||||
|
onRequirementChange: (value: string) => void;
|
||||||
|
platform: string;
|
||||||
|
platformOptions: string[];
|
||||||
|
onPlatformChange: (value: string) => void;
|
||||||
|
ratio: string;
|
||||||
|
ratioOptions: string[];
|
||||||
|
onRatioChange: (value: string) => void;
|
||||||
|
videoQuality: CloneVideoQualityKey;
|
||||||
|
videoQualityOptions: Array<{ key: CloneVideoQualityKey; label: string; desc: string }>;
|
||||||
|
onVideoQualityChange: (value: CloneVideoQualityKey) => void;
|
||||||
|
videoDuration: number;
|
||||||
|
videoDurationMin: number;
|
||||||
|
videoDurationMax: number;
|
||||||
|
onVideoDurationChange: (value: number) => void;
|
||||||
|
videoSmart: boolean;
|
||||||
|
onVideoSmartChange: (value: boolean) => void;
|
||||||
|
onOpenHistory: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideoAspectRatio(ratio: string): string {
|
||||||
|
if (ratio.includes("9:16")) return "9:16";
|
||||||
|
if (ratio.includes("16:9")) return "16:9";
|
||||||
|
if (ratio.includes("3:4")) return "3:4";
|
||||||
|
return "9:16";
|
||||||
|
}
|
||||||
|
|
||||||
|
function openQuickUploadWithKeyboard(
|
||||||
|
event: KeyboardEvent<HTMLDivElement>,
|
||||||
|
inputRef: { current: HTMLInputElement | null },
|
||||||
|
) {
|
||||||
|
if (event.key !== "Enter" && event.key !== " ") return;
|
||||||
|
event.preventDefault();
|
||||||
|
inputRef.current?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EcommerceOneClickVideoPanel({
|
||||||
|
onClose,
|
||||||
|
isAuthenticated,
|
||||||
|
onRequestLogin,
|
||||||
|
productImages,
|
||||||
|
productInputRef,
|
||||||
|
isProductUploadDragging,
|
||||||
|
setIsProductUploadDragging,
|
||||||
|
handleProductDrop,
|
||||||
|
handleProductUpload,
|
||||||
|
removeProductImage,
|
||||||
|
maxProductImages,
|
||||||
|
requirement,
|
||||||
|
onRequirementChange,
|
||||||
|
platform,
|
||||||
|
platformOptions,
|
||||||
|
onPlatformChange,
|
||||||
|
ratio,
|
||||||
|
ratioOptions,
|
||||||
|
onRatioChange,
|
||||||
|
videoQuality,
|
||||||
|
videoQualityOptions,
|
||||||
|
onVideoQualityChange,
|
||||||
|
videoDuration,
|
||||||
|
videoDurationMin,
|
||||||
|
videoDurationMax,
|
||||||
|
onVideoDurationChange,
|
||||||
|
videoSmart,
|
||||||
|
onVideoSmartChange,
|
||||||
|
onOpenHistory,
|
||||||
|
}: EcommerceOneClickVideoPanelProps) {
|
||||||
|
const [openSelect, setOpenSelect] = useState<"platform" | "ratio" | null>(null);
|
||||||
|
const [planTrigger, setPlanTrigger] = useState(0);
|
||||||
|
const selectAnchorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const productImageDataUrls = useMemo(() => productImages.map((img) => img.src), [productImages]);
|
||||||
|
const productImageFiles = useMemo(() => productImages.map((img) => img.file), [productImages]);
|
||||||
|
|
||||||
|
const canGenerate = productImages.length > 0 || requirement.trim().length > 0;
|
||||||
|
|
||||||
|
const handleGenerate = () => {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
onRequestLogin();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPlanTrigger((value) => value + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePlatformSelect = (value: string) => {
|
||||||
|
onPlatformChange(value);
|
||||||
|
setOpenSelect(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRatioSelect = (value: string) => {
|
||||||
|
onRatioChange(value);
|
||||||
|
setOpenSelect(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelect = (key: "platform" | "ratio") => {
|
||||||
|
setOpenSelect((current) => (current === key ? null : key));
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderThumbs = () => (
|
||||||
|
<div className="ecom-quick-upload-thumbs" aria-label="已上传商品原图">
|
||||||
|
{productImages.map((item) => (
|
||||||
|
<figure key={item.id} className="ecom-command-asset-thumb ecom-quick-upload-thumb">
|
||||||
|
<img src={item.src} alt={item.name} />
|
||||||
|
<span className="ecom-command-asset-zoom" aria-hidden="true">
|
||||||
|
<img src={item.src} alt="" />
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="删除图片"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
removeProductImage(item.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</figure>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="ecom-one-click-video-page ecom-quick-hot-page ecom-quick-set-page ecom-tool-page-enter" aria-label="一键视频">
|
||||||
|
<div className="ecom-quick-set-body">
|
||||||
|
<aside className="ecom-quick-set-panel" aria-label="一键视频设置">
|
||||||
|
<header className="ecom-quick-set-panel-head">
|
||||||
|
<strong className="ecom-quick-set-page-title">
|
||||||
|
<VideoCameraOutlined /> 一键视频
|
||||||
|
</strong>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={onClose}>
|
||||||
|
首页
|
||||||
|
</button>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={onClose}>
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<strong><FileImageOutlined /> 上传商品原图</strong>
|
||||||
|
{productImages.length ? (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material has-images${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => productInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, productInputRef)}
|
||||||
|
onDragOver={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.dataTransfer.types.includes("Files")) setIsProductUploadDragging(true);
|
||||||
|
}}
|
||||||
|
onDragLeave={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) {
|
||||||
|
setIsProductUploadDragging(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onDrop={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
setIsProductUploadDragging(false);
|
||||||
|
handleProductDrop(event);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderThumbs()}
|
||||||
|
{productImages.length < maxProductImages ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-hot-add-btn"
|
||||||
|
aria-label="添加更多素材"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
productInputRef.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material${isProductUploadDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => productInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, productInputRef)}
|
||||||
|
onDragOver={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.dataTransfer.types.includes("Files")) setIsProductUploadDragging(true);
|
||||||
|
}}
|
||||||
|
onDragLeave={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) {
|
||||||
|
setIsProductUploadDragging(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onDrop={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
setIsProductUploadDragging(false);
|
||||||
|
handleProductDrop(event);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FileImageOutlined />
|
||||||
|
<span>拖拽或点击上传</span>
|
||||||
|
<em>上传商品素材图,最多 {maxProductImages} 张</em>
|
||||||
|
<b>+ 上传图片</b>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={productInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
className="ecom-command-hidden-file"
|
||||||
|
onChange={handleProductUpload}
|
||||||
|
aria-label="上传商品图片"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="ecom-quick-hot-requirement">
|
||||||
|
<div className="ecom-quick-hot-requirement__head">
|
||||||
|
<strong>视频需求</strong>
|
||||||
|
</div>
|
||||||
|
<div className="ecom-quick-hot-requirement__input">
|
||||||
|
<textarea
|
||||||
|
value={requirement}
|
||||||
|
onChange={(event) => onRequirementChange(event.target.value.slice(0, 500))}
|
||||||
|
placeholder="建议包含以下信息:产品名称、核心卖点、期望场景、口播风格、具体参数"
|
||||||
|
maxLength={500}
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
<span>{requirement.length}/500</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="ecom-quick-set-basic-section">
|
||||||
|
<span className="ecom-quick-set-label">基础设置</span>
|
||||||
|
<div className="ecom-quick-set-select-anchor" ref={selectAnchorRef}>
|
||||||
|
<div className="ecom-quick-set-selects">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={openSelect === "platform" ? "is-active" : ""}
|
||||||
|
onClick={() => toggleSelect("platform")}
|
||||||
|
>
|
||||||
|
<span>平台</span>
|
||||||
|
<strong>{platform}</strong>
|
||||||
|
<em>⌄</em>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={openSelect === "ratio" ? "is-active" : ""}
|
||||||
|
onClick={() => toggleSelect("ratio")}
|
||||||
|
>
|
||||||
|
<span>尺寸比例</span>
|
||||||
|
<strong>{ratio.replace(/\s+/g, " ").trim()}</strong>
|
||||||
|
<em>⌄</em>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{openSelect ? (
|
||||||
|
<div
|
||||||
|
className="ecom-quick-set-dropdown"
|
||||||
|
role="listbox"
|
||||||
|
aria-label={openSelect === "platform" ? "平台" : "尺寸比例"}
|
||||||
|
>
|
||||||
|
{(openSelect === "platform" ? platformOptions : ratioOptions).map((option) => (
|
||||||
|
<button
|
||||||
|
key={option}
|
||||||
|
type="button"
|
||||||
|
className={
|
||||||
|
(openSelect === "platform" ? platform === option : ratio === option) ? "is-active" : ""
|
||||||
|
}
|
||||||
|
role="option"
|
||||||
|
aria-selected={openSelect === "platform" ? platform === option : ratio === option}
|
||||||
|
onClick={() => {
|
||||||
|
if (openSelect === "platform") {
|
||||||
|
handlePlatformSelect(option);
|
||||||
|
} else {
|
||||||
|
handleRatioSelect(option);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.replace(/\s+/g, " ").trim()}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<strong>视频画质</strong>
|
||||||
|
<div className="ecom-quick-detail-modules">
|
||||||
|
{videoQualityOptions.map((option) => (
|
||||||
|
<button
|
||||||
|
key={option.key}
|
||||||
|
type="button"
|
||||||
|
className={videoQuality === option.key ? "is-active" : ""}
|
||||||
|
aria-pressed={videoQuality === option.key}
|
||||||
|
onClick={() => onVideoQualityChange(option.key)}
|
||||||
|
>
|
||||||
|
<strong>{option.label}</strong>
|
||||||
|
<span>{option.desc}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<strong>视频时长</strong>
|
||||||
|
<div className="ecom-one-click-video-duration">
|
||||||
|
<span>{videoDuration} 秒</span>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
className="ecom-one-click-video-range"
|
||||||
|
min={videoDurationMin}
|
||||||
|
max={videoDurationMax}
|
||||||
|
step={5}
|
||||||
|
value={videoDuration}
|
||||||
|
onChange={(event) => onVideoDurationChange(Number(event.target.value))}
|
||||||
|
aria-label="视频时长"
|
||||||
|
/>
|
||||||
|
<div className="ecom-one-click-video-duration-scale" aria-hidden="true">
|
||||||
|
<span>{videoDurationMin}秒</span>
|
||||||
|
<span>{videoDurationMax}秒</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`ecom-one-click-video-smart${videoSmart ? " is-on" : ""}`}
|
||||||
|
aria-pressed={videoSmart}
|
||||||
|
onClick={() => onVideoSmartChange(!videoSmart)}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<strong>智能优化</strong>
|
||||||
|
<em>根据平台、商品图和尺寸自动匹配推荐参数</em>
|
||||||
|
</span>
|
||||||
|
<i aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="ecom-quick-hot-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-set-primary ecom-one-click-video-generate"
|
||||||
|
onClick={handleGenerate}
|
||||||
|
disabled={!canGenerate}
|
||||||
|
>
|
||||||
|
<ThunderboltOutlined /> {isAuthenticated ? "一键生成视频" : "登录后生成"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section className="ecom-quick-set-stage">
|
||||||
|
<EcommerceVideoWorkspace
|
||||||
|
isAuthenticated={isAuthenticated}
|
||||||
|
productImageDataUrls={productImageDataUrls}
|
||||||
|
productImageFiles={productImageFiles}
|
||||||
|
requirement={requirement}
|
||||||
|
platform={platform}
|
||||||
|
aspectRatio={getVideoAspectRatio(ratio)}
|
||||||
|
durationSeconds={videoDuration}
|
||||||
|
resolution={videoQuality === "standard" ? "720P" : "1080P"}
|
||||||
|
onRequestLogin={onRequestLogin}
|
||||||
|
onOpenHistory={onOpenHistory}
|
||||||
|
triggerPlan={planTrigger}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12168,6 +12168,24 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
--quick-shadow: rgba(232, 89, 12, 0.1);
|
--quick-shadow: rgba(232, 89, 12, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--product {
|
||||||
|
--quick-accent: #0f8f72;
|
||||||
|
--quick-bg: #e6f7f3;
|
||||||
|
--quick-text: #0a4d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--copywriting {
|
||||||
|
--quick-accent: #4f46e5;
|
||||||
|
--quick-bg: #eef2ff;
|
||||||
|
--quick-text: #312e81;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--more {
|
||||||
|
--quick-accent: #6b7280;
|
||||||
|
--quick-bg: #f3f4f6;
|
||||||
|
--quick-text: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button > span,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button > span,
|
||||||
@@ -18785,3 +18803,704 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
||||||
0 2px 6px color-mix(in srgb, var(--mode-accent) 12%, transparent) !important;
|
0 2px 6px color-mix(in srgb, var(--mode-accent) 12%, transparent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── One-click copywriting page ── */
|
||||||
|
.ecommerce-standalone .ecom-copywriting-page {
|
||||||
|
position: relative !important;
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-rows: minmax(0, 1fr) !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 720px !important;
|
||||||
|
color: #111827 !important;
|
||||||
|
background: #f3f5f8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-body {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: 360px minmax(0, 1fr) !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-panel {
|
||||||
|
position: relative !important;
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-rows: auto auto auto auto minmax(0, 1fr) auto !important;
|
||||||
|
gap: 16px !important;
|
||||||
|
padding: 18px 16px 18px !important;
|
||||||
|
border-right: 1px solid rgba(15, 23, 42, 0.06) !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-panel-head {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
min-height: 34px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-page-title {
|
||||||
|
margin-right: auto !important;
|
||||||
|
color: #10202c !important;
|
||||||
|
font-size: 17px !important;
|
||||||
|
font-weight: 950 !important;
|
||||||
|
letter-spacing: 0.02em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-back {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
min-width: 70px !important;
|
||||||
|
min-height: 30px !important;
|
||||||
|
padding: 0 10px !important;
|
||||||
|
border: 1px solid rgba(16, 115, 204, 0.12) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: #526474 !important;
|
||||||
|
background: #f7fafc !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: color 180ms ease, background 180ms ease, border-color 180ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-back:hover {
|
||||||
|
border-color: rgba(30, 189, 219, 0.32) !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
background: #f0f9ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-section {
|
||||||
|
display: grid !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-section-title {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 5px !important;
|
||||||
|
color: #132435 !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-grid {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-card {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: auto 1fr !important;
|
||||||
|
grid-template-rows: auto auto !important;
|
||||||
|
gap: 2px 8px !important;
|
||||||
|
align-items: center !important;
|
||||||
|
min-height: 56px !important;
|
||||||
|
padding: 10px 12px !important;
|
||||||
|
border: 1px solid rgba(15, 23, 42, 0.06) !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
color: #111827 !important;
|
||||||
|
background: #fbfdff !important;
|
||||||
|
text-align: left !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-card:hover {
|
||||||
|
border-color: rgba(16, 115, 204, 0.22) !important;
|
||||||
|
background: #f7fbff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-card.is-active {
|
||||||
|
border-color: #1073cc !important;
|
||||||
|
background: #eef8ff !important;
|
||||||
|
box-shadow: 0 0 0 1px #1073cc, 0 6px 16px rgba(16, 115, 204, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-icon {
|
||||||
|
grid-row: 1 / 3 !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
width: 32px !important;
|
||||||
|
height: 32px !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
background: rgba(16, 115, 204, 0.08) !important;
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-card.is-active .ecom-copywriting-type-icon {
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: #1073cc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-label {
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-type-desc {
|
||||||
|
font-size: 11px !important;
|
||||||
|
color: #6b7c88 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-textarea {
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 96px !important;
|
||||||
|
padding: 12px !important;
|
||||||
|
border: 1px solid rgba(15, 23, 42, 0.08) !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
color: #111827 !important;
|
||||||
|
background: #fbfdff !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
resize: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-textarea::placeholder {
|
||||||
|
color: #9aa6b0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-textarea:focus {
|
||||||
|
border-color: #1073cc !important;
|
||||||
|
box-shadow: 0 0 0 3px rgba(16, 115, 204, 0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-wordcount {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-wordcount button {
|
||||||
|
min-height: 32px !important;
|
||||||
|
padding: 0 14px !important;
|
||||||
|
border: 1px solid rgba(15, 23, 42, 0.08) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: #344250 !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-wordcount button:hover {
|
||||||
|
border-color: rgba(16, 115, 204, 0.22) !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
background: #f7fbff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-wordcount button.is-active {
|
||||||
|
border-color: #1073cc !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: #1073cc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-generate {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 42px !important;
|
||||||
|
margin-top: auto !important;
|
||||||
|
border: 0 !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: linear-gradient(135deg, #1073cc, #1ebddb) !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
box-shadow: 0 8px 20px rgba(16, 115, 204, 0.22) !important;
|
||||||
|
transition: transform 180ms ease, box-shadow 180ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-generate:hover:not(:disabled) {
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
box-shadow: 0 12px 26px rgba(16, 115, 204, 0.28) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-generate:disabled {
|
||||||
|
opacity: 0.65 !important;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-stage {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr) !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
padding: 20px 24px 24px !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-preview-head {
|
||||||
|
display: grid !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-preview-head h1 {
|
||||||
|
margin: 0 !important;
|
||||||
|
color: #172636 !important;
|
||||||
|
font-size: 21px !important;
|
||||||
|
font-weight: 950 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-preview-head p {
|
||||||
|
margin: 0 !important;
|
||||||
|
color: #657686 !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 750 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-preview-head p span {
|
||||||
|
color: #1073cc !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-results {
|
||||||
|
display: grid !important;
|
||||||
|
align-content: start !important;
|
||||||
|
gap: 16px !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-empty,
|
||||||
|
.ecommerce-standalone .ecom-copywriting-loading {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
min-height: 320px !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
color: #6b7c88 !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-empty .anticon {
|
||||||
|
font-size: 36px !important;
|
||||||
|
color: rgba(16, 115, 204, 0.18) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-empty strong,
|
||||||
|
.ecommerce-standalone .ecom-copywriting-loading span:last-child {
|
||||||
|
font-size: 14px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
color: #344250 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-empty em {
|
||||||
|
font-size: 12px !important;
|
||||||
|
color: #8a9baa !important;
|
||||||
|
font-style: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-card {
|
||||||
|
padding: 18px 20px !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-card header {
|
||||||
|
display: grid !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
margin-bottom: 12px !important;
|
||||||
|
padding-bottom: 12px !important;
|
||||||
|
border-bottom: 1px solid rgba(15, 23, 42, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-card header span {
|
||||||
|
font-size: 12px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-card header strong {
|
||||||
|
font-size: 15px !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
color: #111827 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-body {
|
||||||
|
margin: 0 0 14px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
line-height: 1.7 !important;
|
||||||
|
color: #344250 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-points {
|
||||||
|
display: grid !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
list-style: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-points li {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
color: #344250 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-result-points li span {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
flex: 0 0 auto !important;
|
||||||
|
width: 18px !important;
|
||||||
|
height: 18px !important;
|
||||||
|
border-radius: 5px !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: #1073cc !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-spinner {
|
||||||
|
display: inline-block !important;
|
||||||
|
width: 16px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.35) !important;
|
||||||
|
border-top-color: #ffffff !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
animation: ecom-copywriting-spin 0.8s linear infinite !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ecom-copywriting-spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.ecommerce-standalone .ecom-copywriting-body {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-copywriting-panel {
|
||||||
|
border-right: 0 !important;
|
||||||
|
border-bottom: 1px solid rgba(15, 23, 42, 0.06) !important;
|
||||||
|
max-height: 45vh !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Logo containment override: ensure brand logo never overflows its box ── */
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .ecommerce-standalone__logo {
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .ecommerce-standalone__logo img {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
object-fit: contain !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Quick action: 一键视频 card theme ── */
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--video {
|
||||||
|
--quick-accent: #1073cc;
|
||||||
|
--quick-bg: #eaf5ff;
|
||||||
|
--quick-text: #123454;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── One-click video panel: hot-clone style layout + light video workspace ── */
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page.ecom-hot-video-page .ecom-quick-set-body {
|
||||||
|
grid-template-columns: minmax(386px, 420px) minmax(0, 1fr) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-quick-set-panel-head .ecom-quick-set-page-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-duration {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-duration > span {
|
||||||
|
color: #1073cc;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-duration-scale {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: #6b7c88;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-range {
|
||||||
|
width: 100%;
|
||||||
|
accent-color: #1073cc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 52px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid rgba(16, 115, 204, 0.14);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8fbfc;
|
||||||
|
color: #162535;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart.is-on {
|
||||||
|
border-color: rgba(16, 115, 204, 0.34);
|
||||||
|
background: linear-gradient(180deg, #edf8ff, #f8fdff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart > span {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart strong {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart em {
|
||||||
|
color: #6b7c88;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart i {
|
||||||
|
width: 40px;
|
||||||
|
height: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #d0dbe3;
|
||||||
|
position: relative;
|
||||||
|
transition: background 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart.is-on i {
|
||||||
|
background: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart i::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
top: 2px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #fff;
|
||||||
|
transition: transform 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-one-click-video-smart.is-on i::after {
|
||||||
|
transform: translateX(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light-themed video workspace overrides */
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page.ecom-hot-video-page .ecom-video-workspace {
|
||||||
|
color: #162535;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flowbar.ecom-video-preview-head {
|
||||||
|
background: #ffffff;
|
||||||
|
border-bottom-color: rgba(16, 115, 204, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-preview-copy h1 {
|
||||||
|
color: #162535;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-preview-copy p {
|
||||||
|
color: #6b7c88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-preview-copy p span {
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-step-dot {
|
||||||
|
background: rgba(16, 115, 204, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-step-dot.is-done {
|
||||||
|
background: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-step-dot.is-active {
|
||||||
|
background: #1ebddb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flowbar__stage-label {
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flowbar__error {
|
||||||
|
color: #e03131;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flowbar__zoom button {
|
||||||
|
border: 1px solid rgba(16, 115, 204, 0.1) !important;
|
||||||
|
background: #edf8ff !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-action {
|
||||||
|
border-color: rgba(16, 115, 204, 0.2);
|
||||||
|
background: #edf8ff;
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-action--ghost {
|
||||||
|
background: #f8fbfc;
|
||||||
|
color: #6b7c88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-action--danger {
|
||||||
|
border-color: rgba(224, 49, 49, 0.3);
|
||||||
|
background: #fff0f0;
|
||||||
|
color: #e03131;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-canvas {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-empty {
|
||||||
|
color: #6b7c88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-dock button {
|
||||||
|
border-color: rgba(16, 115, 204, 0.2);
|
||||||
|
background: #edf8ff;
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-flow-notice {
|
||||||
|
border-color: rgba(16, 115, 204, 0.2);
|
||||||
|
background: #ffffff;
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light tree nodes */
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node {
|
||||||
|
border-color: rgba(16, 115, 204, 0.16);
|
||||||
|
background: #f7f9fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--source {
|
||||||
|
border-color: rgba(16, 115, 204, 0.2);
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--text {
|
||||||
|
border-color: rgba(16, 115, 204, 0.14);
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--text.is-completed {
|
||||||
|
border-color: rgba(16, 115, 204, 0.34);
|
||||||
|
background: #edf8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--text.is-active {
|
||||||
|
border-color: #1ebddb;
|
||||||
|
background: #f0fdff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node__title {
|
||||||
|
color: #162535;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node__desc {
|
||||||
|
color: #6b7c88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node__label {
|
||||||
|
color: #6b7c88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--image,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--video {
|
||||||
|
border-color: rgba(16, 115, 204, 0.16);
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--image.is-completed,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--video.is-completed {
|
||||||
|
border-color: rgba(16, 115, 204, 0.34);
|
||||||
|
background: #edf8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--image.is-active,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--video.is-active {
|
||||||
|
border-color: #1ebddb;
|
||||||
|
background: #f0fdff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node--video.is-failed {
|
||||||
|
border-color: rgba(224, 49, 49, 0.4);
|
||||||
|
background: #fff0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node__placeholder {
|
||||||
|
background: #edf1f4;
|
||||||
|
color: #9badb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree-node__tag {
|
||||||
|
border-color: rgba(16, 115, 204, 0.12);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #1073cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__arrow {
|
||||||
|
color: #9badb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__trunk-line,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__branches-line::before,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__branch-tap::before {
|
||||||
|
background: rgba(16, 115, 204, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__trunk-line::after,
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page .ecom-video-tree__branch-tap::after {
|
||||||
|
background: linear-gradient(90deg, transparent, #1ebddb, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page.ecom-hot-video-page .ecom-quick-set-body {
|
||||||
|
grid-template-columns: minmax(330px, 370px) minmax(0, 1fr) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.ecommerce-standalone .ecom-one-click-video-page.ecom-hot-video-page .ecom-quick-set-body {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -371,3 +371,14 @@
|
|||||||
border-color: rgba(var(--accent-rgb), 0.42);
|
border-color: rgba(var(--accent-rgb), 0.42);
|
||||||
background: var(--bg-panel);
|
background: var(--bg-panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Product set count stepper: align with local light theme ── */
|
||||||
|
html body #root .ecommerce-standalone .product-clone-page[data-tool="clone"] .clone-ai-count-stepper {
|
||||||
|
border-color: var(--border-subtle) !important;
|
||||||
|
background: var(--bg-inset) !important;
|
||||||
|
color: var(--fg-body) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone .product-clone-page[data-tool="clone"] .clone-ai-count-stepper b {
|
||||||
|
color: var(--fg-body) !important;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user