feat(ui06): B0 baseline — before screenshots (20 pages) + contract checks + acceptance skeleton
- capture-before.cjs: Playwright 脚本(env 注入凭据),对测试服务器 32005 后台 1440×1000 全页截图 - 契约核对四项全为纯前端工作:cover/preview 服务端已实现(签名 URL 900s), CreatePayload 仅下载开关,embed v2 字段 E06 已落地 - 主题链路确认:systemMainColor[0] → 运行时 --el-color-primary 派生 Co-Authored-By: Claude Code <noreply@anthropic.com>
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"date": "2026-09-22T14:45:36.894Z",
|
||||
"base": "http://219.157.255.213:32005",
|
||||
"viewport": "1440x1000",
|
||||
"errors": [],
|
||||
"notes": [
|
||||
"15-branding: before 状态 banner 位于详情卡内,未独立页签",
|
||||
"16-photo-viewer: before 状态无大图预览,以照片表格代替"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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 的宿主)
|
||||
const detailBtn = page.locator('[data-test="album-detail-btn"]').first();
|
||||
if (await detailBtn.count()) {
|
||||
await detailBtn.click();
|
||||
await page.waitForURL('**/albums/detail/**');
|
||||
await sleep(1200);
|
||||
await shot('03-photos'); // photos 为默认页签
|
||||
|
||||
// 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);
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
# UI-06 全端视觉统一 验收记录
|
||||
|
||||
方案:`docs/plans/2026-09-22-UI06-全端视觉统一实施方案.md`(commit `37d88da`)
|
||||
日期:2026-09-22 起 · 执行环境:Windows 11 + 测试服务器 219.157.255.213(入口 32005)
|
||||
视觉基准:`docs/designs/admin-preview-2026-09-22/`(后台 20 页)+ `docs/designs/app-preview-2026-09-22/`(App 20 页),冲突一律以示例为准。
|
||||
|
||||
## 1. 任务完成表
|
||||
|
||||
| 阶段 | 内容 | Commit | 状态 |
|
||||
|---|---|---|---|
|
||||
| B0 | 基线提交 + before 截图 + 契约核对 | 见下 | ✅ 完成 |
|
||||
| B1 | 主题 token + 布局壳 + 登录页 | | 待执行 |
|
||||
| B2 | 共享业务组件 | | 待执行 |
|
||||
| B3a–B3e | 页面批次(每批部署 32005 验收) | | 待执行 |
|
||||
| B4 | 后台全量回归验收 | | 待执行 |
|
||||
| A0–A7 | App 线(B4 后启动) | | 待执行 |
|
||||
|
||||
## 2. B0 记录(2026-09-22)
|
||||
|
||||
### 2.1 基线提交
|
||||
|
||||
`37d88da`:admin-preview 20 页原型+截图+validation、app-preview 截图/脚本、UI05/UI06 方案文档、概念图、g4-sync-once 脚本(85 文件)。工作树干净后开工。
|
||||
|
||||
### 2.2 Before 截图
|
||||
|
||||
- 工具:`docs/designs/ui06-implementation/capture-before.cjs`(Playwright,凭据经 env 注入不入库)
|
||||
- 对象:测试服务器已部署后台(= HEAD admin 产物),1440×1000 全页,20 张 → `docs/designs/ui06-implementation/before/`
|
||||
- 页面错误:0。注记 2 条(结构性,符合预期):
|
||||
- `15-branding`:现状 banner 位于详情卡内未独立页签(B3c 改)
|
||||
- `16-photo-viewer`:现状无大图预览,以照片表格代替(B2 新增 PhotoPreview)
|
||||
- 关键现状确认:02 相册为**文字表格**(无封面卡);侧栏为深色窄栏(非示例 216px 白底);10 用户页分页器 15/页共 230 条 + 同步档案列在位(G4 成果,保留)。
|
||||
|
||||
### 2.3 契约核对结论(方案 §2.2 四项)
|
||||
|
||||
| # | 项 | 测试服务器实测结论 | 处理 |
|
||||
|---|---|---|---|
|
||||
| 1 | Album `cover` | ✅ **服务端已实现**:`GET /v1/albums` 每项含 `cover: { url(签名OSS), expiresAt }`,900s 过期 | 分支 (a)——前端仅补 `Summary.cover` 类型 + 卡片使用;过期经受控重取 |
|
||||
| 2 | `AdminPhoto.preview` | ✅ **服务端已实现**:`GET /v1/albums/:id/photos` 每项含 `preview.thumb/browse`(签名 URL) | 前端补类型;为空呈现真实空态 |
|
||||
| 3 | CreatePayload 载荷 | ✅ 代码确认:仅 translations/defaultLocale/startAt/endAt/timezone/下载开关;公开/上传开关在 UpdatePayload | 整页表单按此禁用或解释,不塞进创建请求 |
|
||||
| 4 | 展示位 v2 字段 | ✅ 类型已有且 SlotFormDialog **已呈现** displayMode/scrollSpeed/reactionsEnabled(E06/E07 已落地;UI05 §4.4 的担心已过时) | B3e 仅视觉对齐,无需补字段 |
|
||||
|
||||
无服务端缺口:**全部四项均为纯前端工作**,无需用户确认分支 (b)。
|
||||
|
||||
### 2.4 主题入口核对(B1 准备)
|
||||
|
||||
- 主色链路:`config/index.ts` `systemMainColor[0]`(现 `#5D87FF`)→ `config/setting.ts:45` `systemThemeColor` → `utils/ui/colors.ts` 运行时生成 `--el-color-primary` 系。B1 改预设首位为 `#1A73E8` 并同步派生。
|
||||
- SCSS 静态层:`assets/styles/core/el-light.scss`(@forward var.scss,success/warning/danger 已定制,messagebox 12px 圆角已接近示例);`el-ui.scss` 定义 `--main-color` 映射。
|
||||
- 布局壳:`components/core/layouts/`(art-header-bar、art-menus/art-sidebar-menu)。
|
||||
- 菜单:`router/modules/albums.ts|system.ts`,meta 驱动;分组重组 = 调 meta(B1)。
|
||||
|
||||
## 3. B1 记录
|
||||
|
||||
(待执行)
|
||||
|
||||
## 4. B2 记录
|
||||
|
||||
(待执行)
|
||||
|
||||
## 5. B3 批次记录
|
||||
|
||||
(待执行;每批:对照截图 → 部署 tag → 用户验收结论)
|
||||
|
||||
## 6. B4 / App 线记录
|
||||
|
||||
(待执行)
|
||||