fix: 商品三视图回填改用 in_library:all 专拉,刷新真回显 (ZWQ#5 续3)
真根因(比标记更基础):商品库生成的三视图 in_library=False(工作台生成默认不入资产库),
而商品详情原回填读的是 assetsPage({product}) 默认只返 in_library=True 的结果 → 三视图被过滤掉、
压根不在前端数据里 → 回填永远看不到,刷新即「尚未生成」。
修:三视图面板单独拉一次 in_library:"all" 的商品资产(asset_type=image)做回填,
不影响 AI 素材 grid(仍只显已入库)。依赖 assetReload 重拉,生成后即时回显;
配合上一commit的 view=three_view 标记,刷新/跨模块都认得出。
注:存量旧三视图(标记前生成)无 view 标记仍认不出,需重新生成一次。
构建 tsc=0/build=0。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -677,33 +677,41 @@ export function ProductDetailPage({ product, projects, initialTab = "assets", na
|
||||
|
||||
// ZWQ#row25/三视图同步:该商品已存在的「商品三视图」资产(在【视频项目】基础资产阶段生成、
|
||||
// 或此前在商品库生成并落库)回填到三视图面板,避免「明明已生成却显示尚未生成」。
|
||||
// ⚠️ row28:必须只取「商品」三视图,排除「角色/人物三视图」——二者 category 都是 tri_view、名字都含「三视图」,
|
||||
// 唯一可靠区分是 metadata.triview_of(角色三视图指向其立绘 asset id;商品三视图没有)+ kind=person。
|
||||
// 漏掉这层过滤就会把角色三视图塞进商品三视图面板(row28「商品三视图显示成角色三视图」)。
|
||||
// ⚠️ 关键:商品库生成的三视图 in_library=False(未加入资产库),普通 assetsPage 默认只返已入库的会漏掉它,
|
||||
// 必须 in_library:"all" 专门拉一次(不影响 AI 素材 grid 仍只显已入库)。
|
||||
// ⚠️ row28:只取「商品」三视图,排除「角色/人物三视图」——二者 category 都是 tri_view、名字都含「三视图」,
|
||||
// 唯一可靠区分是 metadata.triview_of(角色三视图指向其立绘;商品三视图没有)+ kind=person。
|
||||
useEffect(() => {
|
||||
const triAssets = productAssets.filter((a) => {
|
||||
const m = (a.metadata as { triview_of?: unknown; kind?: string; view?: string } | undefined) || {};
|
||||
if (m.triview_of) return false; // 角色/人物/模特三视图:绝不进商品三视图面板
|
||||
if (m.kind === "person" || m.kind === "scene") return false;
|
||||
return a.category === "tri_view" || a.asset_type === "tri_view"
|
||||
|| m.view === "three_view" || (a.name || "").includes("商品三视图");
|
||||
});
|
||||
if (!triAssets.length) return;
|
||||
const serverVersions: TriVersion[] = triAssets
|
||||
.map((a) => ({ id: a.id, url: a.files?.[0]?.preview_url || "", asset: a, createdAt: a.created_at || "" }))
|
||||
.filter((v) => v.url);
|
||||
if (!serverVersions.length) return;
|
||||
setTriVersions((prev) => {
|
||||
const seen = new Set(prev.map((v) => v.id));
|
||||
const merged = [...prev, ...serverVersions.filter((v) => !seen.has(v.id))];
|
||||
// 最新版本在前(与生成态 prepend 一致)
|
||||
merged.sort((x, y) => (y.createdAt || "").localeCompare(x.createdAt || ""));
|
||||
return merged.length === prev.length ? prev : merged;
|
||||
});
|
||||
// 已被采用为商品封面的那版三视图 → 标记「已采用」
|
||||
const adopted = triAssets.find((a) => a.id === product.cover_asset);
|
||||
if (adopted) setTriAdoptedId((cur) => cur || adopted.id);
|
||||
}, [productAssets, product.cover_asset]);
|
||||
if (!product?.id) return;
|
||||
let alive = true;
|
||||
api.assetsPage({ product: product.id, in_library: "all", asset_type: "image", pageSize: 200 })
|
||||
.then((res) => {
|
||||
if (!alive) return;
|
||||
const triAssets = (res.results || []).filter((a) => {
|
||||
const m = (a.metadata as { triview_of?: unknown; kind?: string; view?: string } | undefined) || {};
|
||||
if (m.triview_of) return false; // 角色/人物/模特三视图:绝不进商品三视图面板
|
||||
if (m.kind === "person" || m.kind === "scene") return false;
|
||||
return a.category === "tri_view" || a.asset_type === "tri_view"
|
||||
|| m.view === "three_view" || (a.name || "").includes("商品三视图");
|
||||
});
|
||||
const serverVersions: TriVersion[] = triAssets
|
||||
.map((a) => ({ id: a.id, url: a.files?.[0]?.preview_url || "", asset: a, createdAt: a.created_at || "" }))
|
||||
.filter((v) => v.url);
|
||||
if (serverVersions.length) {
|
||||
setTriVersions((prev) => {
|
||||
const seen = new Set(prev.map((v) => v.id));
|
||||
const merged = [...prev, ...serverVersions.filter((v) => !seen.has(v.id))];
|
||||
merged.sort((x, y) => (y.createdAt || "").localeCompare(x.createdAt || "")); // 最新在前
|
||||
return merged.length === prev.length ? prev : merged;
|
||||
});
|
||||
}
|
||||
// 已被采用为商品封面的那版三视图 → 标记「已采用」
|
||||
const adopted = triAssets.find((a) => a.id === product.cover_asset);
|
||||
if (adopted) setTriAdoptedId((cur) => cur || adopted.id);
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => { alive = false; };
|
||||
}, [product?.id, product.cover_asset, assetReload]);
|
||||
|
||||
async function onPickProductImage(event: ChangeEvent<HTMLInputElement>) {
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
Reference in New Issue
Block a user