From 8cc0dc5218a5832bcdc926047d7ad756367f0432 Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Thu, 25 Jun 2026 15:56:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(ai-white-background):=20=E6=97=A0=E8=8A=B1?= =?UTF-8?q?=E8=89=B2=E5=80=9F=E6=BA=90=E9=A2=9C=E8=89=B2=E6=B1=A1=E6=9F=93?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20+=20=E5=B7=A5=E4=BD=9C=E5=8C=BA=E9=87=8D?= =?UTF-8?q?=E6=8E=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:无花色 SKU 借源生图时,"未配置花色"占位符被当真实色传入, 后端 normalize 后 prompt 退化为"保持源图原色",AI 跟随源条码原图色调 生成错误颜色(SZ024203V01 案例:借胡桃色源图生成出胡桃色结果)。 A 方案(解开死结): - Workspace 新增 inlineTargetColor 状态,用户可手动指定本次生图颜色 - isEffectiveAiWhiteBackgroundTargetColor 识别"未配置花色"等占位符为无效色 - targetColor 改为 effectiveTargetColor,占位符不再绕过空值校验 - Panel 目标花色区改为条件可输入:SKU 无色时显示输入框 B 方案(借源必填): - 借源模式 + 无色时给出明确警示"必须指定目标颜色,否则跟随源条码色调" - 输入框用警示色高亮 工作区重排(解决"操作小小一块"): - 设置区加宽 310px→380px,字号放大 10/12px→11/13px - 视图卡片放大 164/190px→180/210px,间距 gap-2→gap-3 - "目标与源图"小节默认展开(最常用不该藏) 验证:type-check 通过,白底图 61 个测试全过(含新增 3 个 A/B 方案验证) --- ...nIntegratedAiWhiteBackgroundPanel.test.tsx | 81 +++++++++++++++++ .../NonIntegratedAiWhiteBackgroundPanel.tsx | 91 +++++++++++++++---- ...onIntegratedAiWhiteBackgroundWorkspace.tsx | 36 +++++++- 3 files changed, 187 insertions(+), 21 deletions(-) diff --git a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedAiWhiteBackgroundPanel.test.tsx b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedAiWhiteBackgroundPanel.test.tsx index c8a93a0c..a7bfe342 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedAiWhiteBackgroundPanel.test.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/NonIntegratedAiWhiteBackgroundPanel.test.tsx @@ -76,6 +76,9 @@ describe("NonIntegratedAiWhiteBackgroundPanel", () => { targetBarcode="BAR-001" targetLabel="BAR-001 / 批次1" targetColor="胡桃木色" + skuTargetColor="胡桃木色" + inlineTargetColor="" + onInlineTargetColorChange={() => {}} targetHasOriginalImages sourceMode="self" selectedSourceBarcode="" @@ -349,6 +352,9 @@ describe("NonIntegratedAiWhiteBackgroundPanel", () => { targetBarcode="BAR-002" targetLabel="BAR-002 / 批次1" targetColor="浅橡木色" + skuTargetColor="浅橡木色" + inlineTargetColor="" + onInlineTargetColorChange={() => {}} targetHasOriginalImages={false} sourceMode="borrow" selectedSourceBarcode="BAR-001" @@ -404,4 +410,79 @@ describe("NonIntegratedAiWhiteBackgroundPanel", () => { expect(screen.queryByText("BAR-001 · 胡桃木色")).not.toBeInTheDocument(); expect(screen.queryByTitle("测试产品一")).not.toBeInTheDocument(); }); + + it("renders a color input when the SKU has no real finish (A 方案)", () => { + const onInlineTargetColorChange = jest.fn(); + renderPanel({ + targetColor: "", + skuTargetColor: "未配置花色", + inlineTargetColor: "", + onInlineTargetColorChange, + }); + + fireEvent.click(screen.getByTestId("ai-white-background-settings-source")); + + const input = screen.getByTestId( + "ai-white-background-inline-target-color", + ); + expect(input).toBeInTheDocument(); + expect(input).toHaveAttribute( + "placeholder", + expect.stringContaining("本次生图颜色"), + ); + expect( + screen.getByText(/该条码未配置花色|借源生图必须指定目标颜色/), + ).toBeInTheDocument(); + }); + + it("shows the borrow-source color warning when borrowing without a target color (B 方案)", () => { + renderPanel({ + targetColor: "", + skuTargetColor: "未配置花色", + inlineTargetColor: "", + sourceMode: "borrow", + targetHasOriginalImages: false, + selectedSourceBarcode: "BAR-001", + sourceBarcodeOptions: [ + { + key: "BAR-001", + skuItemId: "sku-1", + barcode: "BAR-001", + label: "测试产品一", + secondaryLabel: "胡桃木色", + batchNo: 1, + hasOriginalImages: true, + generatedWhiteBackgroundCount: 3, + productPhotoSlotFiles: {} as never, + aiSourceSlotFiles: {} as never, + availableProductPhotoSlotCount: 6, + availableAiSourceSlotCount: 6, + files: [], + }, + ], + availableSourceViewCount: 1, + sourceReadyByView: buildViewRecord((viewKey) => viewKey === "front"), + }); + + fireEvent.click(screen.getByTestId("ai-white-background-settings-source")); + + expect( + screen.getByText(/借源生图必须指定目标颜色/), + ).toBeInTheDocument(); + }); + + it("keeps the readonly finish field when the SKU has a real color", () => { + renderPanel({ + targetColor: "胡桃木色", + skuTargetColor: "胡桃木色", + inlineTargetColor: "", + }); + + fireEvent.click(screen.getByTestId("ai-white-background-settings-source")); + + expect( + screen.queryByTestId("ai-white-background-inline-target-color"), + ).not.toBeInTheDocument(); + expect(screen.getByText("胡桃木色")).toBeInTheDocument(); + }); }); diff --git a/furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundPanel.tsx b/furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundPanel.tsx index 2e7864f9..1cc75043 100644 --- a/furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundPanel.tsx +++ b/furniture-3d-system/src/app/intake/non-integrated/workspaces/ai-white-background/NonIntegratedAiWhiteBackgroundPanel.tsx @@ -45,6 +45,9 @@ type NonIntegratedAiWhiteBackgroundPanelProps = { targetBarcode: string; targetLabel: string; targetColor: string; + skuTargetColor: string; + inlineTargetColor: string; + onInlineTargetColorChange: (value: string) => void; targetHasOriginalImages: boolean; sourceMode: "self" | "borrow"; selectedSourceBarcode: string; @@ -727,6 +730,9 @@ function AiWhiteBackgroundPartGroupEditor({ export default function NonIntegratedAiWhiteBackgroundPanel({ targetColor, + skuTargetColor, + inlineTargetColor, + onInlineTargetColorChange, sourceMode, selectedSourceBarcode, sourceBarcodeOptions, @@ -769,6 +775,19 @@ export default function NonIntegratedAiWhiteBackgroundPanel({ useState(null); const [refinementPrompt, setRefinementPrompt] = useState(""); + // 花色来源判断:SKU 自带真实色 vs 占位符 vs 用户手动输入 + const trimmedSkuTargetColor = (skuTargetColor ?? "").trim(); + const skuHasRealColor = (() => { + if (!trimmedSkuTargetColor) return false; + const compact = trimmedSkuTargetColor.replace(/\s+/g, ""); + return !["未配置花色", "未配置颜色", "缺少花色/颜色信息", "不改色/选择目标花色"] + .some((p) => p.replace(/\s+/g, "") === compact); + })(); + // 当 SKU 无真实色时,展示输入框让用户填写本次生图颜色 + const showInlineColorInput = !skuHasRealColor; + const isBorrowWithoutColor = + sourceMode === "borrow" && !targetColor.trim(); + const generatedViewOptions = useMemo( () => AI_WHITE_BACKGROUND_VIEW_KEYS.filter((viewKey) => @@ -868,11 +887,11 @@ export default function NonIntegratedAiWhiteBackgroundPanel({ data-testid="ai-white-background-panel" className="flex h-full min-h-0 flex-col overflow-hidden rounded-[8px] bg-[#f1f4f5] text-[#2d3335]" > -
+
{AI_WHITE_BACKGROUND_VIEW_KEYS.map((viewKey) => { const generatedFile = generatedFiles[viewKey]; @@ -1046,20 +1065,20 @@ export default function NonIntegratedAiWhiteBackgroundPanel({ data-testid="ai-white-background-control-card" className="flex min-h-0 flex-col overflow-hidden rounded-[8px] bg-white shadow-[0_4px_16px_rgba(45,51,53,0.055)] ring-1 ring-[#adb3b5]/12" > -
+
-

+

生成设置

-
- +
+ {getPartGroupingSourceLabel(partGroupingSource)} @@ -1069,28 +1088,62 @@ export default function NonIntegratedAiWhiteBackgroundPanel({
-
-
+
+
setSettingsSectionExpanded("source", isExpanded) } > -
- +
+ {showInlineColorInput ? ( +
+ + + onInlineTargetColorChange(event.currentTarget.value) + } + placeholder="输入本次生图颜色,如:白色 / 原木色 / 胡桃色" + maxLength={60} + className={cn( + "h-10 w-full rounded-[6px] bg-white px-3 text-[13px] font-bold text-[#2d3335] outline-none ring-1 transition focus:ring-2", + isBorrowWithoutColor + ? "ring-[#c47a3a]/55 focus:ring-[#c47a3a]" + : "ring-[#adb3b5]/22 focus:ring-[#47646b]/45", + )} + /> + {isBorrowWithoutColor ? ( +

+ 借源生图必须指定目标颜色,否则会跟随源条码原图色调。 +

+ ) : ( +

+ 该条码未配置花色,请填写本次想生成的颜色。 +

+ )} +
+ ) : ( + + )} {sourceMode === "borrow" ? (
-