Co-Authored-By: Claude Code <noreply@anthropic.com>
13 KiB
UI-02 观众页视觉重构 验收记录
基线 commit:f31388c(tag ui01)· 完成 commit:本提交(tag ui02)· 日期:2026-09-21 · 执行环境:Windows 11 + Docker Desktop(mysql/redis×2 + minio)+ Playwright Chromium(1440×900 / 390×844 / 320×568)
1. 基线与完成 commit
| 项 | 值 |
|---|---|
| 基线 | f31388c(UI-01 完成提交,tag ui01) |
| 完成 | Task 6 验收提交(含代码修正 + 本记录 + after 截图 + 总纲注记),tag ui02 |
| 工作树 | 基线后首个提交为方案文档(3327551);验收提交仅含 apps/viewer 代码/测试、本记录、总纲注记与 after 截图目录 |
任务提交链:3327551(方案)→ 2cbd167 → 9c3915b → 3d89fcd → 89f4589 → 160e7e5 → 本提交,代码提交共 6 个。
2. 任务完成表(Task 1–6)
| 任务 | 状态 | commit | 备注 |
|---|---|---|---|
| Task 1: 基线截图与验收骨架 | ✅ | 2cbd167 |
改版前观众页 4 张基线截图(首页/大图 × 中/英)+ 本记录骨架 |
| Task 2: V01+V02 视觉基础与活动信息 | ✅ | 9c3915b |
作用域 token / 顶栏(品牌+语言+减少动画)/ 宣传图 / 信息区(标题/说明/元信息/分享)/ 排序与分类工具栏(AlbumHeader 提取) |
| Task 3: V03 瀑布流 | ✅ | 3d89fcd |
响应式列数 / 真实宽高 / 点赞角标 / 新图提示 / 懒加载与占位 |
| Task 4: V04 深色大图查看器 | ✅ | 89f4589 |
深色沉浸式布局 / 缩略图条 / 操作栏 / 保留缩放·导航·路由编排 |
| Task 5: V05 互动与分享下载 | ✅ | 160e7e5 |
心形粒子 / 互动轻提示 / 相册与单图分享(navigator.share + 复制回退)/ 下载菜单 |
| Task 6: V06+V07 状态国际化与验收 | ✅ | 本提交 | 全量回归 / 状态矩阵核验 / l10n 补齐(?lang= 参数)/ a11y 抽查 / 截图 / 本记录 / tag |
3. 验收矩阵(方案 §9.2)
| 编号 | 状态 | 证据 |
|---|---|---|
| V-A01 | ✅ | App.vue 无手机外壳/假内容/底部导航;层级=顶栏→宣传图→信息→工具栏→瀑布流;截图 after/home-390-zh.png、home-1440.png;album-header.spec.ts(标题/说明/元信息结构) |
| V-A02 | ✅ | PhotoFeed 瀑布流(真实宽高绝对定位,非等高网格);feed.spec.ts(列数/占位/懒加载/身份保持);截图 home-320(2 列)/ home-390 / home-1440 |
| V-A03 | ✅ | album-header.spec.ts:宣传图缺失/加载失败收起且信息可用、标题两行钳制+展开、说明三行+展开/收起、日期地点缺失自然省略、窄屏元信息换行 |
| V-A04 | ✅ | 浏览数=album.viewCount 初值 + 上报回调/stats.updated 校准(ActivityPage useVisitReport);e2e journey.spec '1 次浏览';分页/点赞/切图不重复计访问(visitor 幂等键,A14 沿用) |
| V-A05 | ✅ | e2e album.spec(sort-tabs 文案 ['最新','热门']、分类筛选列收窄断言);likes.spec(热门快照重取+锁定);feed.spec 新图提示(不强制跳顶);分类 ID 跨语言保留(activity.spec) |
| V-A06 | ✅ | lightbox.spec.ts(完整显示/左右切换/越过末尾自动翻页/位置指示/缩略图条同步);截图 after/lightbox-390-zh.png、lightbox-1440.png;缩放交互沿用 Task 8 既有实现 |
| V-A07 | ✅ | activity.spec.ts:深链直达(solo 兜底+相邻页归位)、push/back 关闭回位(滚动锚点恢复)、浏览器后退关闭、照片移除(WS/404)轻提示+邻居回退;lightbox.spec 'layers above sticky topbar'(z=200) |
| V-A08 | ✅ | 分享 URL 携带 ?lang=(ActivityPage photoShareUrl);接收端首载优先参数——i18n.spec 'initial locale honors the ?lang= share parameter';复制失败回退显示 URL(shareState 'failed',album-header.spec);分享菜单(lightbox share-btn + 头部分享) |
| V-A09 | ✅ | e2e download.spec(.download-main 菜单 ['浏览图','原图']、相册开关关闭时入口隐藏);useDownload 单测(签名 URL 过期→受控刷新有限重试→占位);失败仅本地反馈(InteractionNotice) |
| V-A10 | ✅ | e2e likes.spec(连续点赞真实累计、自己回声不重复计入、WS 实时多观众可见、断连降级轮询后恢复);useLikes 批次+校准单测 |
| V-A11 | ✅ | likes.spec(他人点赞不污染当前数值);LikeBubbles 数量上限+点击定位;大图打开时页面级气泡清空(ActivityPage lightboxPhotoId === null ? bubbles : []),改由查看器内互动提示承载(§6.2) |
| V-A12 | ✅ | 减少动画:HeartParticles/LikeBubbles/过渡尊重 prefers-reduced-motion 与顶栏开关(heart-particles 单测);lightbox.spec 'traps Tab focus inside the dialog'(含首尾环绕);:focus-visible 全套主色描边;aria-modal/role=dialog/aria-label |
| V-A13 | ✅ | viewer/position.ts 诚实口径单测:solo/未定位→'照片预览'、无总数且还有页→'已加载 N 张'、到底→'i / n'、总数仅服务端提供才显示;新图提示数量来自 realtime 实际合并数 |
| V-A14 | ✅ | 中英模板零硬编码(i18n 键集相等守护 error-coverage.spec);html lang 随 locale(App.vue watchEffect);320/390/1440 截图无横向溢出(320 实测 scrollWidth=clientWidth);lightbox 100dvh+safe-area、数字 tabular-nums;⚠️ 横屏与系统字体放大留 G2 真机(同 UI-01 A07 口径) |
| V-A15 | ✅ | git diff ui01..HEAD 范围=apps/viewer + docs(业务后端/移动端/嵌入链零改动);embed 页独立外壳无顶栏无外边距(embed.spec 全绿);loader 构建字节一致(9462 B);api-cn/sync/worker/admin e2e 全绿 |
4. 状态矩阵核验(方案 §7)
| 状态 | 结果 | 证据 |
|---|---|---|
| 加载中 | ✅ | 相册状态文本 + PhotoFeed 骨架占位(feed.spec) |
| 相册不存在/未公开 | ✅ | 本地化不可用提示 + 重试(ActivityPage loadError;activity.spec) |
| 无照片 | ✅ | '照片即将上线' 空态,无上传按钮(e2e 实拍 after/empty-album-390.png;feed.spec) |
| 首载网络失败 | ✅ | 错误提示 + 重试按钮(activity.spec) |
| 加载更多失败 | ✅ | 已加载照片保留 + 底部重试(Task 6 新增 feed.spec 'load-more failure keeps the loaded photos and offers a bottom retry') |
| URL 过期 | ✅ | 受控刷新有限重试→占位(useDownload/lightbox 单测) |
| 照片下线/删除 | ✅ | 移除 + 邻居回退 + 轻提示(z=210 盖查看器;activity.spec) |
| 深链照片不存在 | ✅ | 本地化提示 + 返回相册链接(deep-link-notice;activity.spec) |
| WS 断连 | ✅ | 降级轮询继续合并新图(e2e likes.spec 'WS loss degrades to polling'),提示为连接态而非'无新照片' |
| 分享/下载失败 | ✅ | 仅本地轻提示/回退(shareState failed 显示 URL;下载失败 InteractionNotice),不弹全局错误 |
5. 截图索引
基线(2026-09-21,commit 2cbd167):ui02-screenshots/baseline/(改版前观众页)
| 编号 | 页面 | 文件 |
|---|---|---|
| 01 | 首页(中/英) | baseline/01-home-zh-CN.png、01-home-en.png |
| 02 | 大图(中/英) | baseline/02-lightbox-zh-CN.png、02-lightbox-en.png |
优化后(2026-09-21,本提交):ui02-screenshots/after/
| 编号 | 页面 | 文件 | 验证点 |
|---|---|---|---|
| 01 | 首页·390·中 | after/home-390-zh.png | 层级/对齐(标题=tab=胶囊 16px)/无横向滚动 |
| 02 | 首页·390·英 | after/home-390-en.png | ?lang=en 直达英文(html lang=en) |
| 03 | 大图·390·中 | after/lightbox-390-zh.png | 深色沉浸/顶栏关闭/位置指示/操作栏/缩略图条 |
| 04 | 大图·390·英 | after/lightbox-390-en.png | 英文文案(Share 等) |
| 05 | 首页·320 | after/home-320.png | 最窄无溢出(实测 scrollWidth=clientWidth=305) |
| 06 | 大图·320 | after/lightbox-320.png | 操作栏/缩略图条不溢出 |
| 07 | 首页·1440 | after/home-1440.png | 居中 1120 列 + 对齐 168.5px(列边+16) |
| 08 | 大图·1440 | after/lightbox-1440.png | 桌面深色大图 + 悬停控件 |
| 09 | 空相册·390 | after/empty-album-390.png | '照片即将上线' 空态无上传按钮 |
采集环境:demo-seed 12 张渐变真实上传图 + 本地 dev 栈(cn 8080 / ov 8081 / worker / sync 2s 推送 / viewer 5175);320/390 检查含 JS 实测文档横向溢出(修复后均为 0)。
与效果图差异:效果图中的手机外壳/编号/投影为展示板元素未带入(§3.1 约定);分类胶囊数、浏览数等为真实接口数据,与效果图的假数据不同属预期。
6. 测试命令与真实结果
命令序列:清场(杀残留 dev 进程)→ pnpm lint → pnpm typecheck → pnpm test → pnpm build → pnpm e2e → source scripts/flutter-env.sh && cd apps/mobile && flutter analyze && flutter test → pnpm --filter @photo/embed-loader build + 字节比对。
| 门 | 结果 |
|---|---|
| pnpm lint | 全部任务通过(Task 6 后 viewer 单包复跑 ✓) |
| pnpm typecheck | 全部任务通过(含 Task 6 新增/修改测试;CSS 修正后 viewer 复跑 ✓) |
| pnpm test(单元) | 426 用例全绿:viewer 222(28 文件)/ admin 77(14)/ embed-loader 27 / contracts 29 / storage 6 / api-cn 51 / worker 14 |
| pnpm build | 全部任务通过(viewer dist 223.85 kB JS / 78.42 gzip;CSS 27.24 kB / 5.25 gzip) |
| pnpm e2e | api-cn 257/257(24 套件)/ sync 27/27 / worker 13/13 / admin 全绿 / viewer 44:41 通过 + 1 环境性抖动(隔离复跑 6/6 ✓,见偏差 4)+ 2 未运行(同文件串行连带,复跑含于 6/6) |
| flutter analyze | No issues found!(0 问题) |
| flutter test | 341/341 全绿(UI-02 未触及移动端,回归保护) |
| loader build+比对 | dist/v1/loader.js 9462 B 与 apps/viewer/public/embed/v1/loader.js 字节一致 |
测试统计对比(UI-01 → UI-02):
| 维度 | 前(UI-01) | 后(UI-02) | Δ |
|---|---|---|---|
| Web 单元 | 359(viewer 155/20 文件) | 426(viewer 222/28 文件) | +67 |
| Web e2e | 364(viewer 44/9 specs) | 364(viewer 44/9 specs,选择器全面迁移至新结构) | 0 |
| Mobile | 341/341 | 341/341 | 0(保护性回归) |
7. 既有改动保护与外部环境
- 纯 viewer 前端改造:
git diff ui01..HEAD= apps/viewer + docs(73 文件),业务数据流/API/同步/嵌入链零变更;EmbedPage 独立外壳未受 token 作用域影响(embed e2e 全绿)。 - 无关未跟踪文件未纳入提交:
.playwright-mcp/、docs/designs/app-redesign-2026-09-21/保持原状。 - 未触碰用户自有进程(scripts/serve.mjs 4173、Playwright MCP);截图用 dev 栈用毕已清理(8080/8081/5175 释放)。
- 演示数据(demo-seed 相册 + 空相册)写入本地 dev 库,不涉及远程/云资源。
8. 方案偏差与剩余事项
偏差:
- 顶栏 z-index 压盖大图(Task 6 发现并修复):sticky 顶栏 z=120 盖住 .photo-viewer z=100,关闭按钮不可点(e2e 首跑 4 项失败定位)。修复:查看器 z=200、下线轻提示 z=210,并补 lightbox.spec 源码断言防回归。
- 工具栏对齐与 1px 横向溢出(Task 6 截图核验发现并修复):.chips-track 横向负边距在通栏父级上把盒子推出视口(390 视口 scrollWidth 391→页面横向滚动),且排序 tab/分类胶囊与信息区 16px 缩进不齐(效果图三者同缩进)。修复:去掉横向负边距、.sort-tabs 补 16px 内边距;320/390/1440 实测零溢出并对齐,截图为修复后版本。
- e2e 选择器迁移收敛在 Task 6:Tasks 2–5 专注实现与单元测试,5 个 e2e spec 仍指向旧结构(首跑 15 项失败);Task 6 完成 album/download/likes/viewer/journey 全量迁移(含两步语言切换、下载菜单文案)。
- embed-crossorigin 环境性抖动:全量并行下 outbox_events.updateMany 偶发 MySQL 写冲突/死锁(PrismaClientKnownRequestError,种子并行 flushSync 竞争),两轮全量各命中 1 例且不同用例;隔离复跑 6/6 通过。与 UI-01 偏差 3 同类的测试基建问题,非代码回归;后续可为 flushOutbox 加重试(独立小改,未夹带)。
?lang=分享参数(Task 6 补齐):方案 §5.4 要求分享链接携带语言,Task 5 已带参但接收端未优先解析;Task 6 在 resolveInitialLocale 增加 ?lang= 最高优先(不写 localStorage——接收者未做选择),配 i18n.spec 用例。- 26 个 .playwright-mcp 快照文件误入 3327551(方案文档提交):不影响运行;建议后续以一次
git rm清理(本次不重写历史)。 - 加载中状态形态:相册加载为诚实状态文本 + 瀑布流骨架占位,未做信息区整块骨架(字段未知时无法诚实渲染占位文案);与方案 §7 'loading→skeleton' 的合理收敛。
剩余(交接):
- G2 真机追加:横屏实拍、系统字体放大 1.3/2.0、真实全景/长图样本(沿 UI-01 清单)。
- flushOutbox 死锁重试(测试基建小改,见偏差 4)。
- .playwright-mcp 历史文件清理(见偏差 6)。
9. 外部关口(不变)
- G2 真机(未执行,不阻塞 UI-02 本地验收)
- G4/D08 用户接口、P5 真实环境接入按总纲推进;UI-02 全部为本地开发环境验证。