feat(product): 商品图删除接通 + 至少保留一张
- 详情页编辑态的删除叉子原是纯 CSS ::after 装饰、没绑事件,且列表用的是 asset id 而非删除接口要的 ProductImage id —— 接通:编辑态点图真调删除接口、用 ProductImage id,非编辑态仍点击放大 - 商品至少保留一张图(与「创建必须带图」一致):后端删到最后一张返回 400;前端只剩一张时那张不显示 叉子 + 提示「商品至少保留一张图;要移除请删除整个商品」;封面无对应 ProductImage(如三视图采用为封面) 则只展示、不可删 - 加 2 个后端测试(多张可删 200 / 删最后一张 400) 真实接口验:两张删一张 200、再删最后一张 400 并保留一张;后端测试通过,tsc/build 通过。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f02c8053a1
commit
f9c52a171c
@@ -738,11 +738,15 @@ export function ProductDetailPage({ product, projects, initialTab = "assets", na
|
||||
|
||||
// 商品图网格 · 用后端内嵌的 preview_url(images[].preview_url / cover_preview_url),不再反查全局 assets
|
||||
const imageRefs = [...(product.images || [])].sort((a, b) => a.sort_order - b.sort_order);
|
||||
const productImages: Array<{ id: string; url: string }> = [];
|
||||
// imageId = ProductImage.id(删除接口要的就是它,不是 asset id);封面若无对应 ProductImage
|
||||
// (如三视图采用为封面)则只展示、不可删(删除走 ProductImage 关联)。
|
||||
const productImages: Array<{ key: string; url: string; imageId?: string }> = [];
|
||||
if (product.cover_asset && !imageRefs.some((ref) => ref.asset === product.cover_asset)) {
|
||||
productImages.push({ id: product.cover_asset, url: product.cover_preview_url || "" });
|
||||
productImages.push({ key: product.cover_asset, url: product.cover_preview_url || "" });
|
||||
}
|
||||
for (const ref of imageRefs) productImages.push({ id: ref.asset, url: ref.preview_url || "" });
|
||||
for (const ref of imageRefs) productImages.push({ key: ref.id, url: ref.preview_url || "", imageId: ref.id });
|
||||
// 可删的图(有 ProductImage 关联)数量:只剩一张时锁住最后一张,不允许删到没图
|
||||
const deletableImageCount = productImages.filter((im) => im.imageId).length;
|
||||
|
||||
// AI 生成素材 · 服务端已按 ?product 把「该商品」的素材(metadata.product_id / origin_task→project→product /
|
||||
// ProductImage 关联)懒加载到 productAssets;这里只按可显示的图片类型过滤。
|
||||
@@ -981,17 +985,24 @@ export function ProductDetailPage({ product, projects, initialTab = "assets", na
|
||||
<span className="ct">({productImages.length})</span>
|
||||
</div>
|
||||
<div className="grid" id="ov-images-grid">
|
||||
{productImages.map((image) => (
|
||||
image.url ? (
|
||||
<div className="thumb placeholder" key={image.id} role="button" tabIndex={0} title="点击放大" style={{ cursor: "zoom-in" }} onClick={() => setPreview({ src: image.url, name: realName })} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); setPreview({ src: image.url, name: realName }); } }}>
|
||||
<img src={image.url} alt={realName} loading="lazy" />
|
||||
{productImages.map((image) => {
|
||||
// 商品至少保留一张图:只剩一张可删图时,那张不显示叉子、不可删(要移除请删整个商品)
|
||||
const canDelete = editing && Boolean(image.imageId) && deletableImageCount > 1;
|
||||
const lastLocked = editing && Boolean(image.imageId) && deletableImageCount <= 1;
|
||||
const onThumb = () => {
|
||||
if (canDelete) void onDeleteImage?.(image.imageId as string);
|
||||
else if (image.url) setPreview({ src: image.url, name: realName });
|
||||
};
|
||||
return (
|
||||
<div className={`thumb placeholder${canDelete ? " is-del" : ""}${lastLocked ? " is-locked" : ""}`} key={image.key} role="button" tabIndex={0}
|
||||
title={canDelete ? "点击移除该图" : lastLocked ? "商品至少保留一张图;要移除请删除整个商品" : image.url ? "点击放大" : undefined}
|
||||
style={{ cursor: canDelete ? "pointer" : image.url ? "zoom-in" : "default" }}
|
||||
onClick={onThumb}
|
||||
onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); onThumb(); } }}>
|
||||
{image.url ? <img src={image.url} alt={realName} loading="lazy" /> : <span className="ph-frame">1:1</span>}
|
||||
</div>
|
||||
) : (
|
||||
<div className="thumb placeholder" key={image.id}>
|
||||
<span className="ph-frame">1:1</span>
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
);
|
||||
})}
|
||||
<div className="img-upload" id="ov-img-add" title="上传图片" role="button" tabIndex={0} onClick={() => imgInputRef.current?.click()} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); imgInputRef.current?.click(); } }}>
|
||||
{uploading ? (
|
||||
<span className="ph-frame" style={{ fontSize: 12 }}>上传中…</span>
|
||||
|
||||
Reference in New Issue
Block a user