fix: align workflow material upload limits
This commit is contained in:
parent
01748fe501
commit
e7cc0c9555
@ -17,6 +17,15 @@ const payloadSchema = z.object({
|
||||
productModel: z.string().trim().max(128).optional(),
|
||||
});
|
||||
|
||||
function getOptionalFormString(value: FormDataEntryValue | null) {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalizedValue = value.trim();
|
||||
return normalizedValue.length > 0 ? normalizedValue : undefined;
|
||||
}
|
||||
|
||||
function isUploadLikeFile(value: FormDataEntryValue | null) {
|
||||
return (
|
||||
Boolean(value) &&
|
||||
@ -73,12 +82,12 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
const payload = payloadSchema.parse({
|
||||
source: formData.get("source"),
|
||||
query: formData.get("query"),
|
||||
queryType: formData.get("queryType"),
|
||||
matchedSku: formData.get("matchedSku"),
|
||||
matchedSpu: formData.get("matchedSpu"),
|
||||
productModel: formData.get("productModel"),
|
||||
source: getOptionalFormString(formData.get("source")),
|
||||
query: getOptionalFormString(formData.get("query")),
|
||||
queryType: getOptionalFormString(formData.get("queryType")),
|
||||
matchedSku: getOptionalFormString(formData.get("matchedSku")),
|
||||
matchedSpu: getOptionalFormString(formData.get("matchedSpu")),
|
||||
productModel: getOptionalFormString(formData.get("productModel")),
|
||||
});
|
||||
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
@ -1801,6 +1801,39 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
}
|
||||
return 2;
|
||||
}, [selectedAgentPresetData?.type, selectedAgentPresetModeConfig]);
|
||||
const materialModelImageRequirement = useMemo(() => {
|
||||
if (selectedAgentPresetData?.type !== "image") {
|
||||
return 0;
|
||||
}
|
||||
if (selectedAgentPresetModeConfig.modeType === "scene_generation") {
|
||||
return selectedAgentPresetModeConfig.scene.includeModelSlot ? 1 : 0;
|
||||
}
|
||||
if (selectedAgentPresetModeConfig.modeType === "detail_replacement") {
|
||||
return selectedAgentPresetModeConfig.detail.includeModelSlot ? 1 : 0;
|
||||
}
|
||||
if (selectedAgentPresetModeConfig.modeType === "sku_replacement") {
|
||||
return selectedAgentPresetModeConfig.sku.includeModelSlot ? 1 : 0;
|
||||
}
|
||||
return 1;
|
||||
}, [selectedAgentPresetData?.type, selectedAgentPresetModeConfig]);
|
||||
const materialTotalUploadRequirement = useMemo(() => {
|
||||
if (selectedAgentPresetData?.type !== "image") {
|
||||
return null;
|
||||
}
|
||||
const explicitCount = Number(selectedAgentPresetData.requiredUploadCount);
|
||||
if (Number.isFinite(explicitCount) && explicitCount > 0) {
|
||||
return Math.max(
|
||||
materialProductSelectionLimit || 0,
|
||||
Math.max(1, Math.round(explicitCount))
|
||||
);
|
||||
}
|
||||
return (materialProductSelectionLimit || 0) + materialModelImageRequirement;
|
||||
}, [
|
||||
materialModelImageRequirement,
|
||||
materialProductSelectionLimit,
|
||||
selectedAgentPresetData?.requiredUploadCount,
|
||||
selectedAgentPresetData?.type,
|
||||
]);
|
||||
const workflowProgress = useMemo(
|
||||
() =>
|
||||
getAgentWorkflowProgress({
|
||||
@ -2091,6 +2124,8 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
materials={workflowMaterials}
|
||||
onMaterialsChange={handleWorkflowMaterialsChange}
|
||||
productSelectionLimit={materialProductSelectionLimit}
|
||||
totalUploadRequirement={materialTotalUploadRequirement}
|
||||
modelImageRequirement={materialModelImageRequirement}
|
||||
disabled={!selectedAgentPresetId}
|
||||
theme="dark"
|
||||
/>
|
||||
|
||||
@ -426,7 +426,7 @@ export function AgentWorkflowMaterialPicker({
|
||||
直接上传商品图
|
||||
</div>
|
||||
<div className={cn("text-xs leading-5", isDark ? "text-white/58" : "text-muted-foreground")}>
|
||||
搜索不到合适白底图时,可以直接上传原图。系统会自动抠图后再加入当前候选素材。
|
||||
搜索不到合适白底图时,可以直接上传原图。这里只统计商品图,模特图会在后续单独处理;系统会自动抠图后再加入当前候选素材。
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 text-xs">
|
||||
@ -437,7 +437,7 @@ export function AgentWorkflowMaterialPicker({
|
||||
isDark ? "bg-white/8 text-white/82" : "bg-muted text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
当前模式需 {resolvedSelectionLimit} 张产品图
|
||||
本步骤需 {resolvedSelectionLimit} 张商品图
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
|
||||
@ -19,6 +19,8 @@ interface AgentWorkflowMaterialSelectionPanelProps {
|
||||
materials: AgentWorkflowMaterialSelections;
|
||||
onMaterialsChange: (next: AgentWorkflowMaterialSelections) => void;
|
||||
productSelectionLimit?: number | null;
|
||||
totalUploadRequirement?: number | null;
|
||||
modelImageRequirement?: number | null;
|
||||
title?: string;
|
||||
description?: string;
|
||||
disabled?: boolean;
|
||||
@ -136,6 +138,8 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
materials,
|
||||
onMaterialsChange,
|
||||
productSelectionLimit = null,
|
||||
totalUploadRequirement = null,
|
||||
modelImageRequirement = 0,
|
||||
title,
|
||||
description,
|
||||
disabled = false,
|
||||
@ -205,15 +209,49 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
const [isSecondaryMaterialVisible, setIsSecondaryMaterialVisible] = useState(
|
||||
hasSecondaryMaterialContent
|
||||
);
|
||||
const normalizedRawProductSelectionLimit =
|
||||
typeof productSelectionLimit === "number" && Number.isFinite(productSelectionLimit) && productSelectionLimit > 0
|
||||
? Math.max(1, Math.round(productSelectionLimit))
|
||||
: null;
|
||||
const normalizedTotalUploadRequirement =
|
||||
typeof totalUploadRequirement === "number" &&
|
||||
Number.isFinite(totalUploadRequirement) &&
|
||||
totalUploadRequirement > 0
|
||||
? Math.max(1, Math.round(totalUploadRequirement))
|
||||
: null;
|
||||
const normalizedModelImageRequirement =
|
||||
typeof modelImageRequirement === "number" && Number.isFinite(modelImageRequirement) && modelImageRequirement > 0
|
||||
? Math.max(0, Math.round(modelImageRequirement))
|
||||
: 0;
|
||||
const normalizedDerivedProductSelectionLimit =
|
||||
normalizedTotalUploadRequirement !== null
|
||||
? Math.max(normalizedTotalUploadRequirement - normalizedModelImageRequirement, 0)
|
||||
: null;
|
||||
const normalizedProductSelectionLimit =
|
||||
normalizedRawProductSelectionLimit !== null
|
||||
? normalizedDerivedProductSelectionLimit !== null &&
|
||||
normalizedDerivedProductSelectionLimit > 0 &&
|
||||
normalizedDerivedProductSelectionLimit < normalizedRawProductSelectionLimit
|
||||
? normalizedDerivedProductSelectionLimit
|
||||
: normalizedRawProductSelectionLimit
|
||||
: normalizedDerivedProductSelectionLimit;
|
||||
const hasAdjustedProductSelectionLimit =
|
||||
normalizedRawProductSelectionLimit !== null &&
|
||||
normalizedProductSelectionLimit !== null &&
|
||||
normalizedProductSelectionLimit !== normalizedRawProductSelectionLimit;
|
||||
const primarySelectedCount = materials.primary.selectedImageIds.length;
|
||||
const secondarySelectedCount = materials.secondary.selectedImageIds.length;
|
||||
const primarySelectionLimit =
|
||||
typeof productSelectionLimit === "number" && Number.isFinite(productSelectionLimit) && productSelectionLimit > 0
|
||||
? Math.max(productSelectionLimit - secondarySelectedCount, primarySelectedCount, 0)
|
||||
typeof normalizedProductSelectionLimit === "number" &&
|
||||
Number.isFinite(normalizedProductSelectionLimit) &&
|
||||
normalizedProductSelectionLimit > 0
|
||||
? Math.max(normalizedProductSelectionLimit - secondarySelectedCount, primarySelectedCount, 0)
|
||||
: null;
|
||||
const secondarySelectionLimit =
|
||||
typeof productSelectionLimit === "number" && Number.isFinite(productSelectionLimit) && productSelectionLimit > 0
|
||||
? Math.max(productSelectionLimit - primarySelectedCount, secondarySelectedCount, 0)
|
||||
typeof normalizedProductSelectionLimit === "number" &&
|
||||
Number.isFinite(normalizedProductSelectionLimit) &&
|
||||
normalizedProductSelectionLimit > 0
|
||||
? Math.max(normalizedProductSelectionLimit - primarySelectedCount, secondarySelectedCount, 0)
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
@ -480,6 +518,12 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
});
|
||||
};
|
||||
const isDark = theme === "dark";
|
||||
const selectionLimitLabel =
|
||||
normalizedProductSelectionLimit !== null
|
||||
? normalizedModelImageRequirement > 0
|
||||
? `当前步骤商品图上限 ${normalizedProductSelectionLimit} 张(模特图 ${normalizedModelImageRequirement} 张另算)`
|
||||
: `当前步骤商品图上限 ${normalizedProductSelectionLimit} 张`
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -514,6 +558,36 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!disabled &&
|
||||
(normalizedTotalUploadRequirement !== null ||
|
||||
normalizedProductSelectionLimit !== null ||
|
||||
normalizedModelImageRequirement > 0) ? (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-[22px] border px-4 py-3 text-xs leading-6",
|
||||
isDark ? "border-emerald-500/18 bg-emerald-500/[0.08] text-emerald-100" : "border-emerald-200 bg-emerald-50 text-emerald-800"
|
||||
)}
|
||||
>
|
||||
{[
|
||||
normalizedTotalUploadRequirement !== null
|
||||
? `当前模式共需 ${normalizedTotalUploadRequirement} 张素材`
|
||||
: "",
|
||||
normalizedProductSelectionLimit !== null
|
||||
? `其中本步骤商品图最多 ${normalizedProductSelectionLimit} 张`
|
||||
: "",
|
||||
normalizedModelImageRequirement > 0
|
||||
? `模特图 ${normalizedModelImageRequirement} 张在后续单独带入,不占这里的商品图上传位`
|
||||
: "",
|
||||
hasAdjustedProductSelectionLimit
|
||||
? "已按总素材数扣除模特图后的商品图数量限制上传槽位"
|
||||
: "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(",")}
|
||||
。
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<AgentWorkflowMaterialPicker
|
||||
value={inputs.primarySku}
|
||||
queryMode={materialSearchMode.primary}
|
||||
@ -531,11 +605,7 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
result={materials.primary.result}
|
||||
selectedImageIds={materials.primary.selectedImageIds}
|
||||
selectionLimit={primarySelectionLimit}
|
||||
selectionLimitLabel={
|
||||
typeof productSelectionLimit === "number" && Number.isFinite(productSelectionLimit) && productSelectionLimit > 0
|
||||
? `当前模式产品图总上限 ${productSelectionLimit} 张`
|
||||
: undefined
|
||||
}
|
||||
selectionLimitLabel={selectionLimitLabel}
|
||||
onValueChange={(value) => handleMaterialInputChange("primary", value)}
|
||||
onQueryModeChange={(nextMode) =>
|
||||
setMaterialSearchMode((prev) => ({
|
||||
@ -642,11 +712,7 @@ export function AgentWorkflowMaterialSelectionPanel({
|
||||
result={materials.secondary.result}
|
||||
selectedImageIds={materials.secondary.selectedImageIds}
|
||||
selectionLimit={secondarySelectionLimit}
|
||||
selectionLimitLabel={
|
||||
typeof productSelectionLimit === "number" && Number.isFinite(productSelectionLimit) && productSelectionLimit > 0
|
||||
? `当前模式产品图总上限 ${productSelectionLimit} 张`
|
||||
: undefined
|
||||
}
|
||||
selectionLimitLabel={selectionLimitLabel}
|
||||
onValueChange={(value) => handleMaterialInputChange("secondary", value)}
|
||||
onQueryModeChange={(nextMode) =>
|
||||
setMaterialSearchMode((prev) => ({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user