fix(product-create): 新建商品上传健壮性 — loading/失败提示/等齐再关抽屉
- 原 close() 在图片上传之前执行 + 上传无 catch → 失败静默,商品建了却无图 - 改:create 后用 Promise.all 等全部图传完,saving 态贯穿整个上传(抽屉不提前关, 「创建中…」可见) - 上传失败 → flash「图片上传失败 · 已保留可重试」,不关抽屉 - createdRef 暂存已建商品:重试只补传图,不重复建商品 - onCreate 失败也给提示(不再静默) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2e880714b1
commit
b8fc1a0749
@@ -48,6 +48,8 @@ export function ProductCreateDrawer({ open, close, onCreate, onUploadImage, onCr
|
|||||||
const [toast, setToast] = useState<{ title: string; sub: string } | null>(null);
|
const [toast, setToast] = useState<{ title: string; sub: string } | null>(null);
|
||||||
const imgInputRef = useRef<HTMLInputElement | null>(null);
|
const imgInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const toastTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const toastTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
// 已建商品句柄:onCreate 成功但图片上传失败时暂存,重试只补传图不重复建商品
|
||||||
|
const createdRef = useRef<Product | null>(null);
|
||||||
|
|
||||||
function flash(title: string, sub: string) {
|
function flash(title: string, sub: string) {
|
||||||
if (toastTimer.current) clearTimeout(toastTimer.current);
|
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 []; });
|
setImages((list) => { list.forEach((item) => URL.revokeObjectURL(item.url)); return []; });
|
||||||
setDragOver(false);
|
setDragOver(false);
|
||||||
setTitleError(false);
|
setTitleError(false);
|
||||||
|
createdRef.current = null;
|
||||||
}
|
}
|
||||||
// 每次打开抽屉时复位表单(含商品库「继续创建商品」二次打开)
|
// 每次打开抽屉时复位表单(含商品库「继续创建商品」二次打开)
|
||||||
useEffect(() => { if (open) resetForm(); }, [open]);
|
useEffect(() => { if (open) resetForm(); }, [open]);
|
||||||
@@ -132,23 +135,35 @@ export function ProductCreateDrawer({ open, close, onCreate, onUploadImage, onCr
|
|||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
const files = images.map((item) => item.file);
|
const files = images.map((item) => item.file);
|
||||||
const created = await onCreate({
|
// 若上次已建好商品(仅图片上传失败),本次重试只补传图,不重复建商品
|
||||||
title: name,
|
let created = createdRef.current;
|
||||||
category: category || catOptions[0],
|
if (!created) {
|
||||||
target_audience: target.trim() || undefined,
|
created = (await onCreate({
|
||||||
selling_points: finalBullets.map((item, index) => ({ title: item, detail: item, sort_order: index }))
|
title: name,
|
||||||
});
|
category: category || catOptions[0],
|
||||||
close();
|
target_audience: target.trim() || undefined,
|
||||||
if (!created) return;
|
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) {
|
if (files.length && onUploadImage) {
|
||||||
for (let i = 0; i < files.length; i += 1) {
|
try {
|
||||||
const fd = new FormData();
|
await Promise.all(files.map((file, i) => {
|
||||||
fd.append("file", files[i]);
|
const fd = new FormData();
|
||||||
fd.append("name", `${name}-主图${files.length > 1 ? `-${i + 1}` : ""}`);
|
fd.append("file", file);
|
||||||
await onUploadImage(created.id, fd);
|
fd.append("name", `${name}-主图${files.length > 1 ? `-${i + 1}` : ""}`);
|
||||||
|
return onUploadImage(created!.id, fd);
|
||||||
|
}));
|
||||||
|
} catch {
|
||||||
|
// 不关抽屉、不重复建商品:留着让用户点「创建商品」重试上传(网络恢复后即成)
|
||||||
|
flash("图片上传失败", "已保留 · 点「创建商品」可重试上传");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
createdRef.current = null;
|
||||||
|
close();
|
||||||
onCreated?.(created);
|
onCreated?.(created);
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user