3327551f6b
Co-Authored-By: Claude Code <noreply@anthropic.com>
217 lines
12 KiB
Markdown
217 lines
12 KiB
Markdown
# 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 方案](2026-09-21-UI02-观众页效果图落地实施方案.md)和[设计概念](../designs/viewer-mobile-concept-2026-09-21.png)完成观众页视觉重构:蓝白活动首页(品牌栏+宣传图+信息+筛选+瀑布流)+ 深色沉浸式大图查看器(缩略图导航+点赞粒子+分享下载)——不改变任何业务逻辑。
|
||
|
||
**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 精确值):
|
||
```css
|
||
.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`;Commit `docs: 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
|