Co-Authored-By: Claude Code <noreply@anthropic.com>
13 KiB
UI-01 App 与悬浮组件体验优化 验收记录
基线 commit:6754cd4 · 完成 commit:本提交(tag ui01)· 日期:2026-09-21 · 执行环境:Windows 11 + Docker Desktop(mysql/redis×2 + minio)+ MuMu 模拟器(Android 15,540×960@240dpi ≈ 360dp 逻辑宽)+ Playwright Chromium(1440×900 / 390×844)
1. 基线与完成 commit
| 项 | 值 |
|---|---|
| 基线 | 6754cd4(UI-01 实施计划提交后、U00 执行前) |
| 完成 | Task 8 文档提交(含本记录 + after 截图),tag ui01 |
| 工作树 | 基线时清洁;验收提交仅含本记录、总纲注记与 after 截图目录 |
任务提交链:3b5f8d2 → 8f4ad11 → c94b9e5 → f6929ae → 1f67b7c → ddf26a1 → ac23a4c → 本提交,共 8 个。
2. 任务完成表(U00–U09)
| 任务 | 状态 | commit | 备注 |
|---|---|---|---|
| Task 1: U00 基线 | ✅ | 3b5f8d2 |
6 张 App 基线截图;嵌入组件/后台基线因当时 Playwright 环境冲突未采集(见偏差 2) |
| Task 2: U01 契约与数据 | ✅ | 8f4ad11 |
cover/preview/scope=own/embed 新配置字段 + 权限矩阵 e2e(services/api/test/ui01-contracts.e2e-spec.ts) |
| Task 3: U02 主题与导航 | ✅ | c94b9e5 |
tokens + ColorScheme 浅/深 + 公共组件 + 三 tab 导航 + 角标 |
| Task 4: U03 相册与照片 | ✅ | f6929ae |
封面卡片列表 / 紧凑详情 / 三列网格 / 大图查看器 / 权限操作 / URL 刷新 |
| Task 5: U04+U05 上传与辅助页 | ✅ | 1f67b7c |
上传中心摘要+筛选+状态映射 / 深色取景 / 分组表单 / 分享分层 / 设置分组 |
| Task 6: U06 嵌入组件 | ✅ | ddf26a1 |
loader 新配置+收起重设计+destroy;轮播页组件拆分+预加载+暂停恢复+可访问性;跨源宿主测试 |
| Task 7: U07 后台配置与预览 | ✅ | ac23a4c |
新字段表单 + 桌面/手机双预览(本地数据)+ 嵌入代码卡仅显式覆盖 |
| Task 8: U08+U09 验收交接 | ✅ | 本提交 | 全量回归 + 13 张 after 截图 + 本记录 + 总纲注记 + tag ui01 |
3. 验收矩阵(方案 §9.1)
| 编号 | 状态 | 证据 |
|---|---|---|
| UI-A01 | ✅ | apps/mobile/test/app/theme_test.dart(11 用例:浅色 §3.1 精确值、深色语义对应、字阶、动效时长/reducedMotion 归零);截图 01–08(设备浅色实拍) |
| UI-A02 | ✅ | albums_tab_test(封面有无占位、标题回退、390 单列/900 双列);截图 02(封面卡片:图→标题→时间地点→状态 badge) |
| UI-A03 | ✅ | album_photos_page_test(350→2/390→3/800→5 列、大图切换、返回位置、角标);截图 05/06(网格 + contain 大图 1/12) |
| UI-A04 | ✅ | services/api/test/ui01-contracts.e2e-spec.ts 权限矩阵(admin/owner/collab-own/collab-other × cover/preview/scope;隐藏照片有权限可见无权限 404;删除照片 preview=null) |
| UI-A05 | ✅ | queue_tab_test(摘要计算、状态映射、行级进度订阅);截图 07(摘要条"今天·本设备"+ 状态 chips + 服务端历史分区) |
| UI-A06 | ✅ | capture_page 测试(连拍模式、入队反馈不弹窗、拍摄不被上传打断)——Task 5 提交内用例 |
| UI-A07 | ⚠️ 部分验证 | 自动化覆盖 350/390/800/900 逻辑宽(widget 测试)+ 设备 ≈360dp 实拍;320/430 宽度与文字 1.0/1.3/2.0 缩放未系统覆盖,列入 G2 真机清单 |
| UI-A08 | ⚠️ 部分验证 | 窄屏 375(playwright embed.spec "narrow viewport starts collapsed and downgrades to sm size")+ 390/1440 实拍(截图 10/09)+ admin 预览 320px 框(截图 12);320/768 宿主未逐一实拍 |
| UI-A09 | ✅ | EmbedStage 固定 4:3 舞台 + object-fit contain(换图不跳高由舞台固定保证);失败图片保留上一张(viewer vitest);演示 12 张渐变图 contain 实拍(截图 09/11);真实全景/文字海报样本列入 G2 |
| UI-A10 | ✅ | embed.spec 6 项暂停恢复(按钮/悬停/键盘焦点/页面隐藏/reducedMotion/收起)+ collapse 持久化;截图 10(390 默认收起)/11(展开) |
| UI-A11 | ✅ | embed.spec(rebinding resets the feed、admin settings apply live via config message、ghost slot 404 destroy);304/签名过期刷新 viewer vitest(ETag + 受控刷新有限重试) |
| UI-A12 | ✅ | embed-crossorigin.spec.ts(真实双端口跨源宿主 7 用例:渲染、非法 source 消息忽略、ghost destroy、多 slot 纵向 8px 间距、全局 button CSS 干扰、窄屏收起) |
| UI-A13 | ✅ | admin e2e embed-slot-config(表单交互窗口零 /v1/ 请求);截图 12(未保存修改即时反映在本地预览,含"未保存"标记) |
| UI-A14 | ✅ | embed-loader 单测 27 用例(缺省/显式/config 消息合并+边界+优先级 data-* > config > 后台 > 默认);v1 兼容入口保留(dist/embed/v1/loader.js);截图 13(代码卡仅含显式覆盖) |
| UI-A15 | ✅ | P4b 三端语言守护(键集相等 + 错误码枚举全覆盖)延续有效;UI-01 新键入 arb 双语;embed.spec "lang=en renders English copy";运行时长文案无裁切由 footer 布局测试覆盖 |
| UI-A16 | ✅ | 本记录 §8 外部关口明确列 G2/G4/P5 为待验证,未宣称生产验收完成 |
4. API 新字段与权限变化(全部可选/nullable,旧客户端零 break)
| 契约 | 变更 | 服务端 |
|---|---|---|
| AlbumSummary | cover: SignedUrl | null |
albums 列表按页批量查 album_assets(kind=cover, effective)+ 短期签名 URL,无 N+1 |
| AdminPhoto | preview: {thumb: SignedUrl, browse: SignedUrl} | null |
管理列表按权限逐张判断(admin 全部/owner 本相册/collab 仅自己);已删除返回 null |
| listAlbumPhotos | query scope: 'all' | 'own'(默认 all 行为不变) |
own=服务端过滤仅本人上传 + 成员资格逐次检查 |
| EmbedSlotSettings | imageFit: 'contain'|'cover'、offsetX: 0-120、offsetBottom: 0-240、zIndex: 1-2147483000(均带默认) |
cn 管理 PATCH 校验 → slot-state payload → ov 公开响应投影完整传递(sync 验证) |
旧客户端兼容:全部字段缺省不 break(ui01-contracts.e2e-spec 含旧请求无新字段用例);ov 侧旧响应消费者不受影响。
5. 截图索引
基线(2026-09-21 采集,commit 3b5f8d2):ui01-screenshots/baseline/
| 编号 | 页面 | 文件 | 备注 |
|---|---|---|---|
| 01 | 登录页 | baseline/01-login.png | Flutter App |
| 02 | 相册列表 | baseline/02-albums.png | 当前卡片无封面图 |
| 03 | 相册详情(上半) | baseline/03-album-detail.png | 字段堆叠布局 |
| 04 | 相册详情(滚动后) | baseline/04-album-detail-scrolled.png | 按钮均等强调 |
| 05 | 上传队列 | baseline/05-queue.png | 状态筛选/进度/操作 |
| 06 | 设置 | baseline/06-settings.png | 分组/语言/密码 |
| 07-09 | 嵌入组件/后台 | (未采集) | 当时 Playwright 环境冲突;after 侧本次补齐,基线以 P4b 验收记录组件描述为对照 |
优化后(2026-09-21 采集,本次提交):ui01-screenshots/after/
| 编号 | 页面 | 文件 | 验证点 |
|---|---|---|---|
| 01 | 登录页 | after/01-login.png | 主题化登录(logo/品牌/输入/主按钮) |
| 02 | 相册列表 | after/02-albums.png | 封面卡片:图→标题→时间地点→状态 badge;无 publicToken 堆叠 |
| 03 | 相册详情 | after/03-album-detail.png | 大图封面+信息+公开/允许上传 badge+照片条预览 |
| 04 | 详情滚动后 | after/04-album-detail-scrolled.png | 照片区(12 张·3 分类 + 管理入口)/分类 chips/底部"从相册选择 + 拍摄上传"双按钮 |
| 05 | 照片网格 | after/05-photo-grid.png | 三列网格 + scope chips(全部/我的)+ 排序/筛选 |
| 06 | 大图查看器 | after/06-photo-viewer.png | contain 展示 + 1/12 页码 + 深色舞台 + 操作入口 |
| 07 | 上传中心 | after/07-queue.png | 摘要条(今天·本设备)+ 状态 chips + 服务端历史分区 |
| 08 | 设置 | after/08-settings.png | 账号/通用/安全/上传帮助/关于分组 + 退出登录 |
| 09 | 组件·桌面 1440 | after/09-embed-desktop-1440.png | md 320px 卡片、offsetX16/offsetBottom24、页码 1/12、收起把手、外链 |
| 10 | 组件·390 收起 | after/10-embed-mobile-390-collapsed.png | 窄屏默认收起 + sm 尺寸把手(≥44px 高) |
| 11 | 组件·390 展开 | after/11-embed-mobile-390-expanded.png | sm 280px 卡片无横向溢出 |
| 12 | 后台配置+预览 | after/12-admin-slot-config.png | 新字段全套(位置/尺寸/间隔/语言/收起/适配/偏移/层级)+ 桌面 320/手机 375 双预览 + 未保存标记 |
| 13 | 嵌入代码卡 | after/13-admin-embed-code.png | 仅 data-slot 显式覆盖 + 参数说明表 |
采集环境:01–08 MuMu 强杀冷启 + 本地 dev 栈(cn 8080 / ov 8081 / worker / MinIO 9000 + adb reverse);09–13 vite preview dist + 独立宿主页(http.server 8090)+ 真实跨源 loader 接入,数据为 demo-seed 12 张渐变图 + 上传封面资产。
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 + sync 校验。
| 门 | 结果 |
|---|---|
| pnpm lint | 10/10 任务通过 |
| pnpm typecheck | 13/13 任务通过 |
| pnpm test(单元) | 12/12 任务、359 用例全绿:embed-loader 27 / contracts 29 / storage 6 / api-cn 51 / worker 14 / viewer 155(20 文件)/ admin 77(14 文件) |
| pnpm build | 10/10 任务通过 |
| pnpm e2e | 364 全绿(第二跑;磁盘用例计数 api-cn 255(24 套件,含 ui01-contracts)+ viewer 44(9 specs,含 embed-crossorigin)+ admin 23(7 specs,含 embed-slot-config)+ sync 27 + worker 13 = 362,运行总数含 it.each 展开) |
| flutter analyze | No issues found!(0 问题) |
| flutter test | 341/341 全绿(+61 相对 P4b 280) |
| loader build+sync | dist/v1/loader.js 与 apps/viewer/public/embed/v1/loader.js 字节一致(cmp 相同) |
测试统计对比(P4b 基线 → UI-01 后):
| 维度 | 前(P4b) | 后(UI-01) | Δ |
|---|---|---|---|
| Web 单元 | 316(embed-loader 11 / viewer 136 / admin 69 / 其余 100) | 359(embed-loader 27 / viewer 155 / admin 77 / 其余 100) | +43 |
| Web e2e | 339(api-cn 247/23 套件 / viewer 32/8 / admin 20/6 / sync 27 / worker 13) | 364(api-cn 24 套件 / viewer 9 specs / admin 7 specs / sync 4 / worker 2) | +25 |
| Mobile | 280/280 | 341/341 | +61 |
| 合计 | 935 | 1064 | +129 |
性能观察(实测记录,不做百分比推算):
- loader 体积:dist/v1/loader.js 9462 B 原始 / 3728 B gzip(P4b 基线未记录 gzip 值,无从对比,不编数字)
- 相册列表封面:每页 1 次批量 album_assets 查询 + 按页签发,无 N+1(实现 + e2e 断言)
- 组件预加载窗口:仅当前 + 下一张(2 张),decode 成功才切换
- 轮播 intervalMs 与取数轮询(30s 基准)解耦,互不牵引
- 组件首屏(ov 日志实测):slot-state 1 次 + visits 信标 1 次,之后按帧取图
7. 既有改动保护与外部环境
- 基线工作树清洁;全程未覆盖任何未提交改动。
- 无关未跟踪文件未纳入提交:
.playwright-mcp/、docs/designs/、docs/plans/2026-09-21-UI02-*.md保持原状。 - 未触碰用户自有进程(
scripts/serve.mjs4173 端口、Playwright MCP/Codex 进程保留);仅清理本仓库残留 dev 服务(e2e 首跑失败的根因,见偏差 3)。 - 演示数据写入本地 dev 库(demo-seed + 封面资产),不影响任何远程/云资源。
8. 方案偏差与剩余事项
偏差:
- cover 契约无 width/height:方案 Task 2 文稿为
{url, expiresAt, width, height},实现为既有SignedUrl引用(url/expiresAt)。理由:卡片封面按固定宽高比占位(无图回退中性占位),客户端不需要服务端尺寸;避免契约膨胀。旧客户端零影响。 - 基线嵌入组件/后台截图缺失:U00 时 Playwright 环境冲突未采集;本次 after 侧补齐(09–13),基线对照沿用 P4b 验收记录的组件行为描述。
- e2e 首跑 sync 套件 9/27 失败:根因是上一会话残留 dev:worker/dev:sync 进程消费了 BullMQ 任务与 outbox 事件(processPhoto 命中 'already' 快路径、picked 数被抢),属环境干扰非代码回归;清理后全量第二跑 364 全绿。
- 320/430 逻辑宽、文字 1.3/2.0 缩放、深色模式真机截图未系统采集(A07/A08 部分验证),随 G2 真机一并执行。
剩余(交接):
- G2 真机关口(既定强制关口):追加 UI-01 视觉项——深色模式实拍、320/430 宽度、文字缩放 1.0/1.3/2.0、横图/竖图/全景/文字海报真实样本。
- G4/D08 用户接口、P5 真实环境接入(G5/G6)按总纲推进;P4b 移交项(真实域名/CDN 预热/embed 页 frame-ancestors CSP/展示位授权 UI 按需/轮询参数冻结)继续有效。
- A22 展示位授权(embed_slot_members 建而未用)维持 P4b 状态,未在 UI-01 范围。
9. 外部关口(不变)
- G2 真机(未执行,不阻塞 UI-01 本地验收)
- G4/D08 用户接口(未到位,不阻塞 UI-01)
- P5 真实部署(测试服务器 219.157.255.213 已部署;UI-01 全部为本地开发环境验证)