Files
photo-live/docs/designs/ui06-implementation/capture-before.cjs
T

187 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* UI06 B0:管理后台「改动前」截图脚本。
* 目标:测试服务器上当前部署的后台(= HEAD 的 admin 产物),1440×1000 全页截图,
* 命名与 admin-preview 示例编号对齐,存入同目录 before/。
* 用法:ADMIN_PW=<密码> node capture-before.cjs [--out before] [--base http://219.157.255.213:32005]
* 凭据不写入本文件;仅浏览器会话内存。
*/
const fs = require('fs');
const path = require('path');
const { chromium } = require(
require.resolve('@playwright/test', { paths: [path.resolve(__dirname, '../../../apps/admin')] })
);
const args = process.argv.slice(2);
const argOf = (name, def) => {
const i = args.indexOf(name);
return i >= 0 ? args[i + 1] : def;
};
const BASE = argOf('--base', 'http://219.157.255.213:32005');
const OUT = path.join(__dirname, argOf('--out', 'before'));
const ACCOUNT = process.env.ADMIN_ACCOUNT || 'admin';
const PASSWORD = process.env.ADMIN_PW;
if (!PASSWORD) {
console.error('missing ADMIN_PW env');
process.exit(1);
}
const notes = [];
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
(async () => {
fs.mkdirSync(OUT, { recursive: true });
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
const errors = [];
page.on('pageerror', (e) => errors.push(e.message));
const shot = async (name) => {
await sleep(1200); // 等数据渲染;无动画依赖
await page.screenshot({ path: path.join(OUT, name + '.png'), fullPage: true });
};
const gotoHash = async (hash) => {
await page.goto(`${BASE}/admin/#${hash}`);
await page.waitForLoadState('networkidle').catch(() => {});
await sleep(800);
};
// 01 登录页(未登录态)
await gotoHash('/auth/login');
await shot('01-login');
// 登录
await page.fill('input[autocomplete="username"]', ACCOUNT);
await page.fill('input[autocomplete="current-password"]', PASSWORD);
await page.click('.auth-right-wrap .el-button--primary');
await page.waitForURL((u) => !u.href.includes('/auth/login'), { timeout: 15000 });
await sleep(1200);
// 02 相册列表
await gotoHash('/albums/manage');
await shot('02-albums');
// 进入第一个相册详情(03–07、15、16 的宿主)——UI06 起默认卡片视图(点封面),表格模式保留 album-detail-btn
const detailBtn = page.locator('[data-test="album-detail-btn"]').first();
const coverBtn = page.locator('[data-test^="album-cover-"]').first();
const entry = (await detailBtn.count()) ? detailBtn : coverBtn;
if (await entry.count()) {
await entry.click();
await page.waitForURL('**/albums/detail/**');
await sleep(1200);
await shot('03-photos'); // photos 为默认页签(UI06 起默认照片网格)
// 04–07 各页签
for (const [name, tab] of [
['04-members', 'members'],
['05-categories', 'categories'],
['06-watermark', 'watermark'],
['07-share', 'share'],
]) {
const tabEl = page.locator(`#tab-${tab}`);
if (await tabEl.count()) {
await tabEl.click();
await sleep(900);
await shot(name);
} else {
notes.push(`${name}: tab #tab-${tab} not found`);
}
}
// 09 编辑对话框(详情页入口)
const editBtn = page.locator('[data-test="album-detail-edit"]').first();
if (await editBtn.count()) {
// 回到 photos 页签,避免页签状态干扰
const photosTab = page.locator('#tab-photos');
if (await photosTab.count()) await photosTab.click().catch(() => {});
await sleep(400);
await editBtn.click();
await sleep(900);
await shot('09-edit');
await page.keyboard.press('Escape');
await sleep(400);
} else {
notes.push('09-edit: album-detail-edit not found');
}
// 15 品牌资产(现状:详情卡内 banner 上传区,未独立成页签)——整页含该区
await shot('15-branding');
notes.push('15-branding: before 状态 banner 位于详情卡内,未独立页签');
// 16 照片大图(现状:表格视图,无大图预览)
await shot('16-photo-viewer');
notes.push('16-photo-viewer: before 状态无大图预览,以照片表格代替');
} else {
notes.push('03-07/15/16: 无相册数据,未截详情系');
}
// 08 新建对话框(列表页入口)
await gotoHash('/albums/manage');
const createBtn = page.locator('[data-test="album-create-btn"]').first();
if (await createBtn.count()) {
await createBtn.click();
await sleep(900);
await shot('08-create');
await page.keyboard.press('Escape');
await sleep(300);
} else {
notes.push('08-create: album-create-btn not found');
}
// 10–12 治理页
await gotoHash('/system/users');
await shot('10-users');
await gotoHash('/system/user-sync');
await shot('11-user-sync');
// 20 同步批次失败明细(现状:点历史行打开抽屉;无失败批次则记录)
const failRow = page.locator('[data-test^="sync-run"]').first();
if (await failRow.count()) {
await failRow.click().catch(() => {});
await sleep(900);
await shot('20-sync-detail');
} else {
await shot('20-sync-detail');
notes.push('20-sync-detail: 未找到可点历史行,截页面原状');
}
await gotoHash('/system/usage');
await shot('12-usage');
// 13 展示位列表 + 14 配置对话框
await gotoHash('/system/embed-slots');
await shot('13-embed-slots');
const slotEdit = page.locator('[data-test="slot-edit-btn"]').first();
if (await slotEdit.count()) {
await slotEdit.click();
await sleep(900);
await shot('14-embed-editor');
await page.keyboard.press('Escape');
await sleep(300);
} else {
notes.push('14-embed-editor: slot-edit-btn not found');
}
// 17–19 异常页
await gotoHash('/403');
await shot('17-403');
await gotoHash('/ui06-nonexistent');
await shot('18-404');
await gotoHash('/500');
await shot('19-500');
const result = {
date: new Date().toISOString(),
base: BASE,
viewport: '1440x1000',
errors,
notes,
};
fs.writeFileSync(
path.join(OUT, 'capture.json'),
JSON.stringify(result, null, 2)
);
console.log(JSON.stringify(result, null, 2));
await browser.close();
if (errors.length) process.exitCode = 1;
})().catch((e) => {
console.error(e);
process.exit(1);
});