From b8fc1a074901b37be23ff607531e42e0a005bb63 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 20 Jun 2026 19:03:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(product-create):=20=E6=96=B0=E5=BB=BA?= =?UTF-8?q?=E5=95=86=E5=93=81=E4=B8=8A=E4=BC=A0=E5=81=A5=E5=A3=AE=E6=80=A7?= =?UTF-8?q?=20=E2=80=94=20loading/=E5=A4=B1=E8=B4=A5=E6=8F=90=E7=A4=BA/?= =?UTF-8?q?=E7=AD=89=E9=BD=90=E5=86=8D=E5=85=B3=E6=8A=BD=E5=B1=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 原 close() 在图片上传之前执行 + 上传无 catch → 失败静默,商品建了却无图 - 改:create 后用 Promise.all 等全部图传完,saving 态贯穿整个上传(抽屉不提前关, 「创建中…」可见) - 上传失败 → flash「图片上传失败 · 已保留可重试」,不关抽屉 - createdRef 暂存已建商品:重试只补传图,不重复建商品 - onCreate 失败也给提示(不再静默) Co-Authored-By: Claude Opus 4.8 --- .../src/components/product-create-drawer.tsx | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/core/frontend/src/components/product-create-drawer.tsx b/core/frontend/src/components/product-create-drawer.tsx index 71b45b6..4cc90e0 100644 --- a/core/frontend/src/components/product-create-drawer.tsx +++ b/core/frontend/src/components/product-create-drawer.tsx @@ -48,6 +48,8 @@ export function ProductCreateDrawer({ open, close, onCreate, onUploadImage, onCr const [toast, setToast] = useState<{ title: string; sub: string } | null>(null); const imgInputRef = useRef(null); const toastTimer = useRef | null>(null); + // 已建商品句柄:onCreate 成功但图片上传失败时暂存,重试只补传图不重复建商品 + const createdRef = useRef(null); function flash(title: string, sub: string) { if (toastTimer.current) clearTimeout(toastTimer.current); @@ -68,6 +70,7 @@ export function ProductCreateDrawer({ open, close, onCreate, onUploadImage, onCr setImages((list) => { list.forEach((item) => URL.revokeObjectURL(item.url)); return []; }); setDragOver(false); setTitleError(false); + createdRef.current = null; } // 每次打开抽屉时复位表单(含商品库「继续创建商品」二次打开) useEffect(() => { if (open) resetForm(); }, [open]); @@ -132,23 +135,35 @@ export function ProductCreateDrawer({ open, close, onCreate, onUploadImage, onCr setSaving(true); try { const files = images.map((item) => item.file); - const created = await onCreate({ - title: name, - category: category || catOptions[0], - target_audience: target.trim() || undefined, - selling_points: finalBullets.map((item, index) => ({ title: item, detail: item, sort_order: index })) - }); - close(); - if (!created) return; - // 把抽屉里选的主图逐张随商品一起上传持久化(否则卡片/详情看不到图) + // 若上次已建好商品(仅图片上传失败),本次重试只补传图,不重复建商品 + let created = createdRef.current; + if (!created) { + created = (await onCreate({ + title: name, + category: category || catOptions[0], + target_audience: target.trim() || undefined, + selling_points: finalBullets.map((item, index) => ({ title: item, detail: item, sort_order: index })) + })) || null; + if (!created) { flash("商品创建失败", "请稍后重试"); return; } // 不静默:建库失败也给提示 + createdRef.current = created; + } + // 主图随商品上传持久化:等全部传完再关抽屉(原先 close 在上传前 + 无 catch → 图悄悄传失败,商品却已建) if (files.length && onUploadImage) { - for (let i = 0; i < files.length; i += 1) { - const fd = new FormData(); - fd.append("file", files[i]); - fd.append("name", `${name}-主图${files.length > 1 ? `-${i + 1}` : ""}`); - await onUploadImage(created.id, fd); + try { + await Promise.all(files.map((file, i) => { + const fd = new FormData(); + fd.append("file", file); + fd.append("name", `${name}-主图${files.length > 1 ? `-${i + 1}` : ""}`); + return onUploadImage(created!.id, fd); + })); + } catch { + // 不关抽屉、不重复建商品:留着让用户点「创建商品」重试上传(网络恢复后即成) + flash("图片上传失败", "已保留 · 点「创建商品」可重试上传"); + return; } } + createdRef.current = null; + close(); onCreated?.(created); } finally { setSaving(false);