Co-Authored-By: Claude Code <noreply@anthropic.com>
12 KiB
UI-02 观众页视觉重构 实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 按 UI02 方案和设计概念完成观众页视觉重构:蓝白活动首页(品牌栏+宣传图+信息+筛选+瀑布流)+ 深色沉浸式大图查看器(缩略图导航+点赞粒子+分享下载)——不改变任何业务逻辑。
Architecture: 纯 viewer 前端改造(apps/viewer)。不新增 API/契约变更(UI-02 §9.3 明确:如果实现发现必须扩展,先记录再兼容变更)。不修改 App/admin/embed。样式作用域隔离(.viewer-shell / .album-page / .photo-viewer),不影响 EmbedPage。
Tech Stack: Vue 3 + TypeScript + CSS(新 CSS 变量文件),零新框架依赖。
对应验收: V-A01 至 V-A15(方案 §9.2)。
依赖基线: 当前 HEAD(含 UI-01 tag ui01)。
方案分析
| 维度 | 评估 |
|---|---|
| 复杂度 | 高——PhotoLightbox 完全重构(缩略图条+底部操作栏+粒子动效),PhotoFeed 重排(响应式列数+点赞角标+新图提示) |
| 风险点 | ① EmbedPage 共用 viewer 工程——样式必须作用域隔离 ② 缩略图条与 usePhotoSequence 的集成 ③ 粒子动效的 DOM 数量控制 ④ 位置计数的真实回退逻辑(不伪造总数) |
| 与 UI-01 的关系 | UI-01 已改 embed 组件样式——UI-02 只改普通相册路由的样式,通过 CSS 作用域隔离 |
| 不变项 | usePhotoSequence 单一数据源、useLikes 业务逻辑、useAlbumRealtime WS/轮询、useDownload 权限、路由语义、浏览去重上报 |
Task 1: V00 基线与代码核对
- Git 状态核对(UI-01 已提交,工作树应清洁);记录当前 HEAD
- 启动 viewer dev server + 后端;截图当前首页(zh/en)+ 大图(zh/en)作为基线
- 阅读 ActivityPage.vue / PhotoFeed.vue / PhotoLightbox.vue / usePhotoSequence / useLikes / useAlbumRealtime / useDownload / zoom.ts——记录每个文件的职责和现有数据流
- 确认 EmbedPage 的样式不受影响(检查 App.vue 中的布局条件)
- 创建
docs/plans/progress/UI02-验收记录.md骨架 - Commit
docs(ui02): baseline screenshots and acceptance skeleton
Task 2: V01+V02 视觉基础 + 首页信息区
Files:
-
Create:
apps/viewer/src/styles/viewer-tokens.css(全部 CSS 变量) -
Modify:
apps/viewer/src/App.vue(普通布局顶栏+容器) -
Modify:
apps/viewer/src/components/LocaleSwitcher.vue(紧凑化) -
Create:
apps/viewer/src/components/album/AlbumHeader.vue(宣传图+信息区) -
Modify:
apps/viewer/src/pages/ActivityPage.vue(使用新组件) -
CSS 变量(方案 §3.1 精确值):
.viewer-shell { --viewer-primary: #1A73E8; --viewer-surface: #FFFFFF; --viewer-page-bg: #F3F6FA; --viewer-text: #172033; --viewer-text-secondary: #647084; --viewer-divider: #E5EAF1; --viewer-dark-bg: #0E1218; --viewer-like-accent: #FF667D; --viewer-radius-img: 4px; --viewer-radius-pill: 20px; --viewer-padding-info: 16px; --viewer-padding-photo: 4px; } -
App.vue 顶栏:品牌名(左)+ 语言切换(右),高 48-52px,白色背景,底部细线。减少动画入口放语言旁的轻量菜单(可发现但不占整行)。普通路由有顶栏;embed 路由无顶栏无外边距(验证不被破坏)
-
LocaleSwitcher:紧凑 "EN ▾" 形态(非全宽下拉)
-
AlbumHeader.vue(新组件):
- 宣传图全宽(约 2.4:1 手机,桌面限高 320px,contain 优先不裁切海报文字;无图收起)
- 标题(≤2 行 + 展开查看)+ 分享按钮(右侧,不压缩标题)
- 说明(≤3 行 + 展开/收起,保留换行)
- 元信息行:浏览次数(真实 viewCount, Intl.NumberFormat,未知显示 "—")+ 日期(相册时区)+ 地点;窄屏可换行
- 缺字段自然省略(null/undefined 不显示)
-
排序/分类工具栏:
- Latest / Popular tab(高 44px,选中蓝字+底部 2px 线)
- 分类胶囊(选中蓝底白字;横向滚动+溢出提示;键盘可访问)
- 无分类省略分类条;All = 不传分类筛选
-
vitest:token 值断言、AlbumHeader 各字段渲染/缺失省略/展开收起、工具栏 tab 切换回调
-
Commit
feat(viewer): UI-02 visual foundation, top bar and album info
Task 3: V03 瀑布流重排
Files:
-
Modify:
apps/viewer/src/components/PhotoFeed.vue -
Modify:
apps/viewer/src/feed/layout.ts -
响应式列数:
- <600px: 2 列, gap 4px
- 600-959px: 3 列, gap 6px
- ≥960px: 4 列, gap 8px(主内容 max-width 1120px)
- 窗口 resize 重新布局(现有 ResizeObserver 或 LayoutBuilder)
-
图片卡片重设计:
- 无白色厚卡片、无常驻文件名、无额外标题
- 圆角 4-6px
- 右下角:心形图标 + 真实点赞数(半透明暗色底确保可读)
- 点赞控件独立交互(阻止冒泡,不嵌套 button)
- 图片点击 → 打开大图(现有行为保留)
-
新照片提示:
- 白色 pill "有新照片 ↑",点击加载合并(现有行为)
- 只有确切数量时才显示数字("8 new photos"),否则只写"有新照片"
- 不在用户浏览时强制跳到顶部
-
骨架与加载:
- 首屏骨架按真实瀑布流比例占位(不是均匀网格)
- 翻页加载失败底部重试(不清空已有图片)
- "已到底" 文案
-
宽高安全:缺失/异常宽高用安全占位比例(避免 NaN/无限高度)
-
vitest:列数计算(320/600/960 断点)、点赞角标渲染+冒泡阻止、新图提示显隐、骨架占位比例
-
Commit
feat(viewer): UI-02 responsive waterfall with like badges
Task 4: V04 深色大图查看器
Files:
-
Rewrite:
apps/viewer/src/components/PhotoLightbox.vue -
Create:
apps/viewer/src/components/viewer/ThumbnailStrip.vue(新) -
Create:
apps/viewer/src/components/viewer/PhotoActionBar.vue(新) -
布局层级(方案 §5.1 ASCII 图):
顶栏(52-56px,深色半透明): × 位置 ⋯菜单 图片舞台(flex:1): 完整大图 contain + ‹ › 切换 粒子层: pointer-events:none 缩略图条(~56px): [thumb][current][thumb]... 底部操作栏(88-104px+安全区): ♥ Like Share Download -
顶栏:
- 关闭 ×(左,有效区≥44px)
- 位置计数(中)——真实回退:有 total 显示 "12 / 248";无 total 显示 "已加载 24 张";深链未定位显示 "照片预览";全部加载完成显示当前筛选序列 i/n(方案 §5.2 精确规则)
- 更多菜单 ⋯(右)——至少包含"减少动画"开关;无真实动作则不渲染
-
图片舞台:
- contain 完整显示,不裁切
- browse 变体 URL(不默认加载原图)
- 左右按钮靠近舞台边界
- 双指缩放 + 单指平移(复用 zoom.ts;放大时平移不误触切图)
- 键盘:← → 切换,Escape 关闭
-
缩略图条(新组件):
- 显示当前附近最多 7 项(手机可见 5)
- 当前项蓝色 2px 描边
- 宽 56-64px,高 44-52px
- 点击切换到该 photoId(不新增浏览器历史)
- 切换后选中项滚动到可见范围
- 使用 thumb 变体
-
底部操作栏(新组件):
- Like: coral (#FF667D) 心形 + 累计数 + "Like/点赞" + 下方提示 "连续点击,为照片点赞"
- Share: 白色分享图标 + "Share/分享"
- Download: 白色下载图标 + "Download/下载"(权限关闭时移除)
- 三个均匀分布,有效触控≥44px
- 原图下载:Download 长按或二级菜单(仅 originalDownloadEnabled 时)
-
深色主题:#0E1218 背景,白色/半透明文字
-
安全区:dvh + safe-area-inset 处理
-
焦点管理:打开后焦点进入对话框,Escape 关闭,关闭后回到触发图片
-
背景滚动锁定:打开时锁住 body 滚动
-
vitest (~15):位置回退三种模式、缩略图渲染+点击切换、操作栏权限显隐、焦点管理、键盘导航、安全区
-
Commit
feat(viewer): UI-02 dark immersive photo viewer with thumbnail strip
Task 5: V05 分享下载与互动粒子
Files:
-
Modify:
apps/viewer/src/components/PhotoLightbox.vue(集成粒子) -
Create:
apps/viewer/src/components/viewer/HeartParticles.vue(新) -
Create:
apps/viewer/src/components/viewer/InteractionNotice.vue(新) -
Modify:
apps/viewer/src/likes/useLikes.ts(暴露本地点击动效事件) -
心形粒子(HeartParticles.vue):
- 触发:当前照片本地点击 或 该照片收到他人点赞 WS 事件
- 视觉:18-30px coral 心形,2-3 条漂移轨迹,持续 1.2-1.8s
- 大图内靠右侧上浮;首页在照片区右侧
- pointer-events: none, aria-hidden: true
- 同时最多 12 个装饰粒子(超出丢弃最旧)
- 页面隐藏/关闭上下文时清理
- 减少动画时不漂浮(计数正常更新)
-
跨照片互动提示(InteractionNotice.vue):
- 保留现有缩略图+心形+delta 可识别提示及点击定位
- 大图内放底部操作栏上方安全区
- 同时最多 3 条
- 不把其他照片计数加到当前照片
-
分享:
- navigator.share 优先;不支持时复制链接
- 相册分享:相册路由;照片分享:当前 photoId 深链+语言参数
- 复制失败展示可选中文本
- 用户取消原生分享不显示错误
-
下载(复用 useDownload):
- 权限关闭时移除下载按钮(不留可绕过直链)
- 原图仅在 originalDownloadEnabled 时提供二级选项
- 下载中防重复;异步错误归属正确 photoId
- 微信等环境提示保留
-
自身回声去重:同一批次 WS 回传不重复累计、不再制造"他人点赞"粒子
-
vitest (~12):粒子生成/清理/上限、减少动画禁用、自身回声过滤、分享复制回退、下载权限组合、异步错误归属
-
Commit
feat(viewer): UI-02 heart particles, interaction notices, share and download
Task 6: V06+V07 状态国际化与验收
- 全部新文案 zh-CN + en(方案 §3-§7 所有 UI 文字)
- 状态矩阵(方案 §7 表格逐项验证):加载中骨架/不可公开/空相册/网络失败/加载更多失败/URL 过期/隐藏删除/深链不存在/WS 断线/分享下载失败
- 无障碍:aria 标签、焦点可见、模态焦点不穿透、tabular-nums 数字宽度
- EmbedPage 回归:确认全局样式不影响嵌入组件(截图+Playwright)
- 全量测试:
pnpm --filter @photo/viewer lint && typecheck && test && build && test:e2e - 截图矩阵(方案 §9.1):390×844 / 320×568 / 430×932 / 844×390 / 1440×900 × zh/en × 首页/大图
- V-A01 至 V-A15 逐项验证 + 验收记录完成
- 总纲 UI-02 注记;tag
ui02;Commitdocs: UI-02 acceptance record
执行注意
- 样式隔离红线:所有新 CSS 在
.viewer-shell/.album-page/.photo-viewer作用域内;不用全局button/img选择器;验证 EmbedPage 不受影响 - 业务零变更:usePhotoSequence/useLikes/useAlbumRealtime/useDownload 的数据流不改(仅 UI 层消费方式变化)
- 数据诚实:不伪造总数/在线人数/准确位置——按方案 §5.2 的回退规则
- 性能红线:粒子 DOM 有上限、缩略图不加载整相册、不预加载全部 browse 图
- 每任务 TDD + 提交带 Co-Authored-By trailer