Merge branch 'main' into codex/ecommerce-history-sync
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-06-18 08:32:46 +00:00
4 changed files with 450 additions and 49 deletions
+55 -11
View File
@@ -1,5 +1,7 @@
import {
AppstoreOutlined,
AppstoreAddOutlined,
BorderOuterOutlined,
ClearOutlined,
CloudUploadOutlined,
CloseOutlined,
@@ -7,14 +9,17 @@ import {
EditOutlined,
FireOutlined,
FileImageOutlined,
FileTextOutlined,
FolderOpenOutlined,
FrownOutlined,
GlobalOutlined,
HighlightOutlined,
LayoutOutlined,
LoadingOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
PaperClipOutlined,
PlayCircleOutlined,
PlusOutlined,
QuestionCircleOutlined,
ReloadOutlined,
@@ -22,6 +27,7 @@ import {
SettingOutlined,
SkinOutlined,
TableOutlined,
TranslationOutlined,
VideoCameraOutlined,
} from "@ant-design/icons";
import {
@@ -257,6 +263,7 @@ import {
interface ProductClonePageProps {
onWorkspaceChromeChange?: (state: { isToolPage: boolean }) => void;
[key: string]: unknown;
}
@@ -865,7 +872,7 @@ const cloneSetCountOptions: Array<{
const cloneSetCountKeys = cloneSetCountOptions.map((option) => option.key);
const minCloneSetTotal = 1;
const maxCloneSetTotal = 16;
const maxCloneProductImages = 20;
const maxCloneProductImages = 10;
const maxCloneReferenceImages = 20;
const cloneVideoDurationMin = 5;
const cloneVideoDurationMax = 45;
@@ -1138,6 +1145,7 @@ function mergeEcommerceHistoryRecords(...recordGroups: EcommerceHistoryRecord[][
}
function ProductClonePage(_props: ProductClonePageProps = {}) {
const { onWorkspaceChromeChange } = _props;
const setInputRef = useRef<HTMLInputElement>(null);
const productInputRef = useRef<HTMLInputElement>(null);
const cloneReferenceInputRef = useRef<HTMLInputElement>(null);
@@ -3023,7 +3031,13 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const imageFiles = notifyRejectedImages(files);
if (!imageFiles.length) return;
const remainingSlots = maxCloneProductImages - productImages.length;
if (remainingSlots <= 0) return;
if (remainingSlots <= 0) {
toast.info(`最多上传 ${maxCloneProductImages} 张素材`);
return;
}
if (imageFiles.length > remainingSlots) {
toast.info(`最多上传 ${maxCloneProductImages} 张素材,已自动保留前 ${remainingSlots}`);
}
const localItems = createLocalImageItems(imageFiles, remainingSlots, "product");
setProductImages((current) => [...current, ...localItems].slice(0, maxCloneProductImages));
@@ -4752,6 +4766,29 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const isQuickSetTool = isCloneTool && activeQuickTool === "quick-set";
const isCopywritingTool = isCloneTool && activeQuickTool === "copywriting";
const isOneClickVideoTool = isCloneTool && activeQuickTool === "oneClickVideo";
const isWorkspaceToolPage =
!isCloneTool ||
isSmartCutoutTool ||
isQuickDetailTool ||
isWatermarkTool ||
isTranslateTool ||
isImageEditTool ||
isHotCloneTool ||
isQuickSetTool ||
isCopywritingTool ||
isOneClickVideoTool ||
isVideoWorkspaceVisible ||
Boolean(activeHistoryRecordId);
useEffect(() => {
onWorkspaceChromeChange?.({ isToolPage: isWorkspaceToolPage });
}, [isWorkspaceToolPage, onWorkspaceChromeChange]);
useEffect(() => {
return () => {
onWorkspaceChromeChange?.({ isToolPage: false });
};
}, [onWorkspaceChromeChange]);
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
const setPrimaryLabel =
setImages.length === 0
@@ -6445,7 +6482,13 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
<button
type="button"
className={`ecom-command-reference ecom-command-reference--bottom ecom-command-tool ecom-command-tool--upload${productImages.length ? " has-images" : ""}${isProductUploadDragging ? " is-dragging" : ""}`}
onClick={() => productInputRef.current?.click()}
onClick={() => {
if (productImages.length >= maxCloneProductImages) {
toast.info(`最多上传 ${maxCloneProductImages} 张素材`);
return;
}
productInputRef.current?.click();
}}
onDragEnter={(event) => {
event.preventDefault();
setIsProductUploadDragging(true);
@@ -6458,8 +6501,9 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
const files = Array.from(event.dataTransfer.files);
if (files.length) addComposerAssets(files);
}}
aria-label="上传素材"
title="上传素材"
aria-disabled={productImages.length >= maxCloneProductImages}
aria-label={productImages.length >= maxCloneProductImages ? `最多上传 ${maxCloneProductImages} 张素材` : "上传素材"}
title={productImages.length >= maxCloneProductImages ? `最多上传 ${maxCloneProductImages} 张素材` : "上传素材"}
>
<span aria-hidden="true"><PaperClipOutlined /></span>
<strong></strong>
@@ -6582,15 +6626,15 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
{(status === "idle" || status === "ready") && !showMainVideoWorkspace ? (
<section className="ecom-command-quick-board" aria-label="快捷功能">
{[
{ label: "A+/详情页", tone: "detail", icon: <LayoutOutlined />, onClick: openQuickDetailPage },
{ label: "A+/详情页", tone: "detail", icon: <BorderOuterOutlined />, onClick: openQuickDetailPage },
{ label: "爆款复刻", tone: "hot", icon: <FireOutlined />, onClick: openHotClonePage },
{ label: "图片修改", tone: "edit", icon: <EditOutlined />, onClick: openImageWorkbenchPage },
{ label: "图片修改", tone: "edit", icon: <HighlightOutlined />, onClick: openImageWorkbenchPage },
{ label: "智能抠图", tone: "cutout", icon: <ScissorOutlined />, onClick: openSmartCutoutUpload },
{ label: "去除水印", tone: "watermark", icon: <ClearOutlined />, onClick: openWatermarkRemovalPage },
{ 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: "translate", icon: <TranslationOutlined />, onClick: openImageTranslatePage },
{ label: "商品套图", tone: "product", icon: <AppstoreAddOutlined />, onClick: openQuickSetPage },
{ label: "一键文案", tone: "copywriting", icon: <FileTextOutlined />, onClick: openCopywritingPage },
{ label: "一键视频", tone: "video", icon: <PlayCircleOutlined />, onClick: openOneClickVideoPage },
{ label: "更多功能", tone: "more", icon: <SettingOutlined />, disabled: true },
].map((item) => (
<button