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);