Files
photo-live/docs/plans/progress/UI01-验收记录.md
T
2026-09-21 20:15:36 +08:00

13 KiB
Raw Blame History

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.mjs 4173 端口、Playwright MCP/Codex 进程保留);仅清理本仓库残留 dev 服务(e2e 首跑失败的根因,见偏差 3)。
  • 演示数据写入本地 dev 库(demo-seed + 封面资产),不影响任何远程/云资源。

8. 方案偏差与剩余事项

偏差:

  1. cover 契约无 width/height:方案 Task 2 文稿为 {url, expiresAt, width, height},实现为既有 SignedUrl 引用(url/expiresAt)。理由:卡片封面按固定宽高比占位(无图回退中性占位),客户端不需要服务端尺寸;避免契约膨胀。旧客户端零影响。
  2. 基线嵌入组件/后台截图缺失:U00 时 Playwright 环境冲突未采集;本次 after 侧补齐(09–13),基线对照沿用 P4b 验收记录的组件行为描述。
  3. e2e 首跑 sync 套件 9/27 失败:根因是上一会话残留 dev:worker/dev:sync 进程消费了 BullMQ 任务与 outbox 事件(processPhoto 命中 'already' 快路径、picked 数被抢),属环境干扰非代码回归;清理后全量第二跑 364 全绿。
  4. 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 全部为本地开发环境验证)