feat(product-detail): 素材区重做为结构化 — 角色逐个 / 商品图平铺 / 图片成品平铺 / 场景分镜视频按项目打包

按"东西的性质"分,不再一锅炖:
- 后端 /api/products/{id}/materials/:角色=立绘+配对三视图(triview_of)逐个、带「被几个项目引用」
  (BaseAssetGroup 去重计数);商品图平铺;图片成品(上身图/套图/创作)平铺;场景/分镜/视频素材按视频项目打包
- 前端商品详情素材区重做:角色卡(左上标签+左下「N 个项目引用」/「// 暂未引用」,点开看形象图+三视图)、
  商品图平铺、图片成品平铺带类型标签、项目包卡(点开看该项目场景+分镜+视频素材);通用明细弹窗
- 单测 ProductMaterialsTests(角色配对+引用数=1 / 商品图 / 图片成品 / 项目包2素材)

验收:products+assets 22 测绿;tsc+build 绿;无头核对(角色卡「1 个项目引用」+商品图平铺、0 console error)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-22 13:34:39 +08:00
parent 708b9ea572
commit 1d40e89418
7 changed files with 246 additions and 98 deletions
+20
View File
@@ -0,0 +1,20 @@
import { chromium } from "playwright";
import path from "node:path"; import fs from "node:fs";
const PID="53d7a7c3-2f41-431b-82d3-3acc97900310";
const OUT=path.resolve("../../../_qa_shots/pd"); fs.mkdirSync(OUT,{recursive:true});
const b=await chromium.launch({headless:true}); const r={consoleErrors:[]};
const p=await (await b.newContext({viewport:{width:1440,height:1000}})).newPage();
p.on("console",m=>{if(m.type()==="error")r.consoleErrors.push(m.text())}); p.on("pageerror",e=>r.consoleErrors.push("PE:"+e.message));
await p.goto("http://localhost:5200/login"); await p.waitForTimeout(300);
await p.fill("#auth-username","airshelf"); await p.fill("#auth-pwd","Restraint2026");
await p.click("button.btn-cta"); await p.waitForFunction(()=>!location.pathname.startsWith("/login"),{timeout:12000});
await p.goto("http://localhost:5200/products/"+PID); await p.waitForTimeout(3000);
r.roleCards=await p.locator(".pd-role-card").count();
r.roleFoot=await p.locator(".pd-role-card .pd-role-foot .mono").allInnerTexts().catch(()=>[]);
r.packCards=await p.locator(".pd-pack-card").count();
r.flatCards=await p.locator(".pd-mat-grid .asset-card:not(.pd-role-card):not(.pd-pack-card)").count();
r.typePills=await p.locator(".pd-mat-grid .type-pill").allInnerTexts().catch(()=>[]);
await p.screenshot({path:path.join(OUT,"pd-materials-v2.png"),fullPage:true});
// 点角色卡看弹窗
if(r.roleCards>0){await p.locator(".pd-role-card").first().click();await p.waitForTimeout(1000);r.roleModalItems=await p.locator(".pd-group-item").count();await p.screenshot({path:path.join(OUT,"pd-role-modal.png")});}
await b.close(); console.log(JSON.stringify(r,null,2));