资产库(P0,sub-agent):多选+吸底 bulk-bar、资产详情弹窗(立绘+三视图+标签/属性+商品媒体画廊)、 上传 Drawer→居中 Modal(类型分段+按类型字段+拖拽预览)、缺三视图徽标(复用现成类) AI图片工具(P0,sub-agent):接入现成 ActorLibrary 模特库弹窗、gen-card 三件套(再生成/就地反馈/ 已采用绿角标,采用反馈去全局弹窗改就地)、平台套图恢复多选+分组、商品库 pl-modal 选择器(restraint重写) 验证: tsc 0 · build 0 · 走查 资产详情/上传弹窗开关ESC + 模特库/pl-modal 打开 全过 · 0 console error ⚠️ 真实数据缺(未造假):资产「移动到」改分类需 api.updateAsset 支持 category 字段; 详情三视图版本历史 V1 是 mock 无真实源;平台无 platform 字段→前端组织分批。均移交监督。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
3.2 KiB
JavaScript
77 lines
3.2 KiB
JavaScript
// Wave 3 batch2 walkthrough (scratch): library 详情/上传/批量 + ai-tools 模特库/pl-modal.
|
|
import { chromium } from "playwright";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const BASE = process.env.BASE || "http://localhost:5173";
|
|
const TOKEN = process.env.TOKEN || "";
|
|
const OUT = path.resolve("../../../_qa_shots/wave3-batch2");
|
|
fs.mkdirSync(OUT, { recursive: true });
|
|
|
|
const browser = await chromium.launch({ headless: true });
|
|
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, colorScheme: "light" });
|
|
const seed = await ctx.newPage();
|
|
await seed.goto(BASE + "/", { waitUntil: "domcontentloaded" });
|
|
await seed.evaluate((t) => localStorage.setItem("airshelf_token", t), TOKEN);
|
|
await seed.close();
|
|
|
|
const r = { library: {}, aitools: {}, consoleErrors: [] };
|
|
|
|
// ---- LIBRARY ----
|
|
{
|
|
const p = await ctx.newPage();
|
|
p.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push("lib:" + m.text()); });
|
|
p.on("pageerror", (e) => r.consoleErrors.push("lib:PAGEERR:" + e.message));
|
|
await p.goto(BASE + "/library", { waitUntil: "load" });
|
|
await p.waitForTimeout(1800);
|
|
r.library.assetCards = await p.locator(".asset-card").count();
|
|
await p.screenshot({ path: path.join(OUT, "library.png") });
|
|
// 详情弹窗:点首张卡
|
|
if (r.library.assetCards > 0) {
|
|
await p.locator(".asset-card").first().click();
|
|
await p.waitForTimeout(500);
|
|
r.library.detailModal = await p.locator(".asset-detail-modal, .modal-bg.show").count();
|
|
await p.screenshot({ path: path.join(OUT, "library-detail.png") });
|
|
await p.keyboard.press("Escape");
|
|
await p.waitForTimeout(400);
|
|
r.library.detailClosedByEsc = (await p.locator(".asset-detail-modal").count()) === 0;
|
|
}
|
|
// 上传弹窗:找含「上传」的按钮
|
|
const upBtn = p.locator("button:has-text('上传')").first();
|
|
if (await upBtn.count()) {
|
|
await upBtn.click();
|
|
await p.waitForTimeout(450);
|
|
r.library.uploadModal = await p.locator(".upload-modal, .modal-bg.show").count();
|
|
await p.screenshot({ path: path.join(OUT, "library-upload.png") });
|
|
await p.keyboard.press("Escape");
|
|
await p.waitForTimeout(300);
|
|
}
|
|
await p.close();
|
|
}
|
|
|
|
// ---- AI-TOOLS (model-photo) ----
|
|
{
|
|
const p = await ctx.newPage();
|
|
p.on("console", (m) => { if (m.type() === "error") r.consoleErrors.push("ai:" + m.text()); });
|
|
p.on("pageerror", (e) => r.consoleErrors.push("ai:PAGEERR:" + e.message));
|
|
await p.goto(BASE + "/model-photo", { waitUntil: "load" });
|
|
await p.waitForTimeout(1800);
|
|
r.aitools.actorLibBtn = await p.locator(".iw-actor-lib-btn").count();
|
|
r.aitools.plBtn = await p.locator(".iw-pl-btn").count();
|
|
await p.screenshot({ path: path.join(OUT, "model-photo.png") });
|
|
// 商品库 pl-modal
|
|
if (await p.locator(".iw-pl-btn").count()) {
|
|
await p.locator(".iw-pl-btn").first().click();
|
|
await p.waitForTimeout(500);
|
|
r.aitools.plModal = await p.locator(".pl-modal, .pl-modal-bg").count();
|
|
await p.screenshot({ path: path.join(OUT, "model-photo-plmodal.png") });
|
|
await p.keyboard.press("Escape");
|
|
await p.waitForTimeout(400);
|
|
}
|
|
await p.close();
|
|
}
|
|
|
|
await browser.close();
|
|
console.log(JSON.stringify(r, null, 2));
|
|
fs.writeFileSync(path.join(OUT, "summary.json"), JSON.stringify(r, null, 2));
|