Files
photo-live/docs/plans/2026-09-21-UI01-App与悬浮组件体验优化实施方案.md
T
2026-09-21 10:17:16 +08:00

483 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI-01:App 与 JS 悬浮组件体验优化实施方案
版本:1.0
日期:2026-09-21
状态:待执行;本文件中的复选框均表示待完成工作,不代表已经实施。
目标执行者:Claude 或其他接手本仓库的开发者。
关联:[实施总纲](2026-09-17-实施总纲.md)、[需求说明书](../01-需求说明书.md)、[技术实施方案](../03-技术实施方案.md)、[P3 验收记录](progress/P3-验收记录.md)、[P4b 验收记录](progress/P4b-验收记录.md)。
## 1. 执行目标与使用方法
在现有功能基础上完成 App 与嵌入组件的产品化体验升级:工作人员能凭图片识别相册与照片,快速拍摄上传、定位异常;网站观众能在不被遮挡的情况下浏览轮播、打开相册。保留现有权限、上传恢复、多语言、海外读取和版本同步机制。
执行顺序:先阅读本文件与当前仓库说明,执行 U00 基线核对,再依次完成 U01–U09。每个任务交付可运行增量,记录改动、验证和剩余问题。不要只修改颜色后宣布完成,也不要把本方案写成另一份计划代替实现。
本文件给出了无需再次询问的设计默认值。常规组件划分、命名和排版由执行者决定。只有缺少真实资源、现有权限语义发生实质冲突或必须突破本文件范围时,列出具体依赖;继续完成不依赖该条件的工作。不得虚构接口、数据、图片或测试结果。
本文是范围明确的执行计划,不要求安装指定插件或使用特定子代理。实际执行仍遵循用户当次授权和适用的仓库规则。
### 1.1 当前基线
- 总纲登记 P1–P4b 本地阶段完成,P5 准备件完成;这不表示真实 OSS/CDN、用户源及双平台真机验收已完成。
- Flutter 已有登录、相册、拍摄、上传队列、上传历史、语言设置及原生上传桥接。
- App 主题仅定义 Material 种子色;相册卡片以字段和开关为主;管理照片列表使用占位图标、照片编号和文件大小。
- 嵌入组件已有 loader、iframe 轮播、收起、语言、轮询退避和版本判断。
- 管理后台已有展示位设置表单,但没有本方案要求的完整视觉预览。
- 本方案基于代码与文档静态核对。运行时截图、真机表现和性能数据必须在实施时补证据。
### 1.2 范围
| 必须完成 | 本阶段不包含 |
|---|---|
| App 统一主题、相册列表/详情、图片管理、上传中心、拍摄/表单/分享/设置页整理 | 重写上传引擎、原生传输和国内海外架构 |
| 受权限保护的管理照片预览、相册封面摘要等必要契约补齐 | AI 修图、自动翻译、视频、公众账号体系 |
| 悬浮组件布局、交互、状态、可访问性、宿主适配 | 全站观众页和管理后台全面重新设计 |
| 展示位视觉配置与本地预览 | 真实部署、域名变更、云资源操作 |
| 中英文、截图检查、针对性回归 | 全局品牌重命名、替换应用包名与桥接通道 |
保留当前英文品牌显示,若仓库已有正式品牌资产则复用。没有确定资产时使用现有英文名称和通用图片图标,不自行把 Framevia 等候选名称批量写入产品。界面仍支持中英文;“英文品牌”不等于强制全部界面使用英文。
## 2. 实施保护规则
1. 开始前检查 Git 状态并记录已有改动。编制时已有 AndroidManifest.xml 修改以及 demo-seed.mjs、g1-oss-smoke.mjs 未跟踪文件;实施时重新核对,不覆盖、清理或提交他人改动。
2. 保留账号隔离、权限校验、上传幂等标识、数据库持久队列、凭证续签、原生事件确认和恢复逻辑。
3. 不把“上传完成”改成“已公开”。传输、核验、处理、公开是不同事实。
4. 不通过观众公开 API 获取管理端隐藏照片,不把永久 OSS 地址放进页面。
5. 不新增重型 UI 框架;Flutter 复用 Material 与 Riverpod,Web 复用 Vue;loader 保持零运行时依赖。
6. 兼容已有 API 消费者、已有展示位与脚本。新增字段采用兼容扩展,更新生成类型,不直接编辑生成文件。
7. 所有用户文案进入现有语言资源。日志、代码变量、数据库状态不能直接作为用户提示。
8. 测试使用隔离环境;运行前检查脚本是否清库、seed 或覆盖配置。禁止对真实业务数据库执行测试初始化。
9. 此阶段不自动发布、推送、修改云配置或创建正式版本标签。提交行为按用户授权执行。
10. 旧的后端正确性问题单独登记:补漏分页未推进、发布覆盖删除状态、同步签名防重放等。本阶段不把它们默认视为已解决,也不顺手扩大成后端全面重构。
## 3. 视觉规范(默认执行值)
### 3.1 基础样式
| 元素 | 浅色默认值 | 使用要求 |
|---|---|---|
| 品牌/主要操作 | #1A73E8 | 主要 CTA、选中态;每个操作区一个主要动作 |
| 页面背景 | #F6F8FB | 列表与工作区背景 |
| 内容表面 | #FFFFFF | 卡片、表单、底部操作区 |
| 主文字 | #172033 | 标题、关键数字 |
| 次文字 | #5E6878 | 时间、地点、说明 |
| 分隔线 | #E5EAF1 | 轻分隔,不用粗边框 |
| 成功 | #16794B | 已上传等完成事实 |
| 等待 | #996300 | 等待网络、等待调度 |
| 错误 | #B42318 | 需要用户处理的失败 |
- 间距:4/8/12/16/24/32;页面左右默认 16。
- 卡片圆角 16,输入框与按钮 12,小标签 6–8;避免所有元素都做成胶囊。
- 页面标题 22–24,分组标题 17–18,正文 14–16,辅助文字 12–13。字号必须随系统缩放。
- App 主要按钮高 48–52;图标按钮有效触控区至少 44×44;图标本身可为 20–24。
- 状态同时用文案和图标表达,不能仅靠颜色。
- 深色主题使用对应语义色,不直接反转图片;图片不加彩色滤镜。沿用已有跟随系统逻辑。
- App 轻量状态动效约 150–220ms;轮播约 250–350ms;减少动态效果开启时禁用非必要动效。
- 正文对比度目标 4.5:1,重要控件和焦点清晰可见;实施时实际验证。
### 3.2 组件组织
修改 `apps/mobile/lib/app/theme.dart`,建立统一 ColorScheme、TextTheme、按钮、输入框、AppBar、NavigationBar、Dialog、BottomSheet 主题。可在 `apps/mobile/lib/ui/` 新增 tokens 与公共组件,例如 StateBadge、EmptyState、ErrorState、PhotoThumbnail、SectionHeader。
只抽取真正跨页面复用的组件,避免为每个 SizedBox 建一层封装。Web 在 viewer 样式目录建立 embed 专用变量,loader 外层通过受控参数同步;禁止污染宿主全局 CSS。
## 4. App 页面规格
### 4.1 导航与相册首页
保留三个页签,显示为“相册 / 上传 / 设置”,英文为 Albums / Uploads / Settings。沿用 IndexedStack,保持页面状态。
相册卡片顺序:封面 → 活动标题 → 时间/地点 → 公开与上传状态。标题最多两行,正文完整标题在详情可见。移除主卡片的 publicToken 以及下载/原图四开关堆叠,完整配置仍可在详情设置访问。
```text
Albums +
┌───────────────────────────────────────┐
│ 活动封面 │
├───────────────────────────────────────┤
│ Canton Fair · Autumn │
│ Sep 21–25 · Guangzhou │
│ Public Uploads on │
└───────────────────────────────────────┘
Albums Uploads · 3 Settings
```
- 手机单列卡片;平板按可用宽度使用两列,每张卡片不得挤压英文标题。
- 无封面显示固定比例中性占位,网络失败显示替代图与可重试状态。
- 不为每张卡片单独请求相册详情。摘要响应批量提供封面信息。
- 标题按当前界面语言 → 相册默认语言 → 首个有效翻译回退。
- 活动时间使用相册时区,不用设备时区悄悄改变日期;详情明确时区。
- 上传页签角标表示当前账号尚未结束的任务数,明确统计范围;不跨账号显示。
- 不添加没有后端支持的全量搜索或“我的/协作”筛选。本轮保留现有分页,若增加筛选必须另列兼容 API 任务,禁止只筛已加载页却声称全量。
### 4.2 相册详情
布局:紧凑封面与活动信息 → 上传/公开状态 → 照片入口或照片预览 → 次级管理入口;底部固定“拍摄上传 / 从相册选择”,避让系统安全区。
- 拍摄是主要按钮,选择照片为次按钮;目标相册与分类清晰可见。
- 编辑、成员、分类、分享进入工具菜单或次级区域,不再全部用同等强调的按钮。
- 公开关闭不等于上传关闭,分别显示;上传关闭时说明原因并禁用上传操作。
- 创建者和管理员可管理相册;协作者仅显示自己被授权的内容与动作。
- App 列表统计如果使用海外回传值,附最近更新时间,不声称实时在线人数。
- 无照片时显示“拍摄第一张照片”入口;正在处理不显示为空且无解释。
### 4.3 照片管理与大图
将 `album_photos_page.dart` 文本列表替换为默认三列网格;极窄屏两列,平板按最小格宽扩列。
- 网格使用缩略图,允许 cover 裁切;大图使用 contain 完整展示。网格角标仅提示处理中、失败、隐藏、删除等必要状态。
- 点开大图,支持已加载照片间左右切换,临近边缘按现有游标加载更多;关闭后回到原网格位置。
- 大图详情显示上传者、活动时区时间、尺寸/大小,ID 放诊断区域。
- 隐藏、恢复、删除操作沿用权限;删除确认明确影响云端照片,与本机任务记录清理区分。
- 本轮单张操作必做。批量照片管理不新增到 App,以免牵连现有批量权限与部分成功语义;后续单独排期。
- 隐藏照片允许有权限的管理者预览。已删除照片默认不签发预览,显示删除占位;后续若需要回收站预览,独立定义权限。
- 未准备好缩略图时显示处理状态。签名 URL 过期允许一次受控刷新,不无限重试。
- 协作者“自己的照片”通过受权限控制的数据接口实现;不能先获取他人数据再前端过滤。现有列表若只允许创建者/管理员,按第 6 节增加明确 own 范围支持。
### 4.4 上传中心
保留当前相册/时间/状态筛选、缩略图、重试、跨设备历史。顶部增加当前筛选范围的任务摘要,并标注范围。
```text
Uploads History
Today · This device
82 completed 3 uploading 13 waiting 2 need attention
All / In progress / Needs attention / Completed
[图] IMG_001.jpg Uploading 68%
━━━━━━━━━━━
[图] IMG_002.jpg Waiting for network
[图] IMG_003.jpg File unavailable Select again
```
| 内部状态 | 界面含义 | 行为 |
|---|---|---|
| preparing | 正在准备照片 | 保留已有取消能力 |
| queued | 等待上传 | 保留取消 |
| uploading | 正在上传,传输进度 | 不伪造暂停能力 |
| uploadedConfirming | 正在确认上传 | 不确定进度,不显示已公开 |
| completed | 上传完成 | 可移除本机记录;不表示海外已可见 |
| waitingNetwork | 等待网络 | 橙色说明,自动恢复,保留已有重试 |
| preparationFailed | 无法准备文件 | 重新选择,不能普通重试冒充恢复 |
| retryableFailed | 上传失败 | 本地化原因与重试 |
| cancelled | 已取消 | 保留清理记录 |
- 摘要按任务数统计,单任务百分比按字节进度;不把两者混成不明口径的总百分比。
- “一键重试”仅作用于已有可重试任务,文案说明范围。
- 已完成任务清理按钮改为“移除记录”,明确不会删除 OSS 照片。
- 不把网络等待和上传成功使用相同状态颜色。
- 摘要使用现有聚合能力或本地数据库聚合,不把全部任务加载到内存计数。
- 进度保持行级订阅,不让整个列表随每个字节回调重建。
- 本机队列与服务端历史采用不同标题和说明;不要将两者相加成去重后的全局数量。
### 4.5 拍摄、表单、分享、设置
- 拍摄:深色取景,顶部相册/分类,底部清晰快门与入队计数。入队成功用短暂反馈,不每张弹确认。保留现有权限拒绝和相机失败恢复。
- 创建/编辑:分组为活动信息、多语言内容、访问设置;默认语言必填,另一语言页签按需填写。沿用现有校验,不把未填写文案自动翻译。保存按钮在键盘打开时可达。
- 分享:名称、二维码、链接操作清晰分层;无现成封面也能使用。复制链接必做;系统分享仅在现有依赖可用或新增依赖经过实际兼容验证时实施,否则明确登记,不放无效按钮。
- 设置:账号信息、语言、密码、上传帮助、版本分组;退出登录保留任务数据与账号隔离语义。不要在退出弹窗承诺后台会继续上传。
- 后台帮助说明区分系统限制、用户强制退出和网络等待;根据真实权限状态提示,不固定显示“已开启后台上传”。
## 5. 悬浮组件规格
### 5.1 两种形态
展开卡片:标题栏 → 4:3 图片舞台 → 活动名称与页码/入口。桌面默认宽 320px,小档 280px。旧配置 sm/md 保留兼容语义;本轮 md 统一为 320px,列入视觉变更说明。
```text
┌────────────────────────────┐
│ Event photos − │
├────────────────────────────┤
│ │
│ 活动图片 │
│ │
├────────────────────────────┤
│ Canton Fair · Autumn │
│ 03 / 12 Pause View album│
└────────────────────────────┘
```
收起入口:图片图标 + Photos/照片;有效高度至少 44px。窄屏默认收起,用户展开后不因轮询再次收起。记住会话内偏好,配置默认值只在没有用户选择时生效。
照片点击打开当前单图深链;View album 打开相册首页。继续保持用户手势触发新页、noopener/noreferrer 和相同语言参数。不要自动跳转宿主。
### 5.2 图片与动效
- 默认 object-fit: contain,中性深色舞台,完整显示合影和海报;cover 作为配置可选项。
- 舞台比例固定,高度不随图片横竖改变。标题允许两行,入口文本不能被裁掉。
- 轮播默认 5000ms,合法范围保持 3000–30000ms;淡入默认 300ms。
- 用“当前序号 / 当前轮播队列数量”替代无限圆点;不得把队列数量标为相册照片总数。
- 仅当前图和下一张预加载;旧图最多保留一个过渡层,避免同时请求整队列图片。
- 新图 decode 成功再切换;失败保留上一张并尝试后续图片,循环失败有上限和退避。
- 用户暂停、指针悬停、键盘焦点在组件内时停止自动切换;离开后仅在用户未手动暂停时恢复。
- 减少动态效果时取消过渡,默认暂停自动切换并提供手动上一张/下一张。
- 页面隐藏、组件收起/不可见停止轮询和切换;恢复立即校准一次,避免重复启动计时器。
### 5.3 状态与可访问性
| 状态 | 展示要求 |
|---|---|
| 初次加载 | 等比例静态骨架,收起入口可用 |
| 成功 | 图片、活动名、序号、暂停和相册入口 |
| 空相册 | 照片即将上线/Photos coming soon,精简占位 |
| 临时断网,有缓存 | 保留可用图片、安静退避,不显示技术错误 |
| 临时断网,无缓存 | 紧凑暂不可用状态,可收起,不永久骨架 |
| 签名失效 | 刷新元数据后有限重试,避免破图闪烁 |
| 停用/解绑/相册不可公开 | 移除卡片和收起入口,不留无效按钮 |
- 将“用户收起”与“服务端终止展示”区分为不同消息语义;新增 destroy 消息时双向严格检查 origin/source/type。
- 隐藏轮播帧不能进入 Tab 顺序或读屏内容;优先只渲染当前可交互帧,过渡层设置不可交互及辅助技术隐藏。
- 所有操作使用 button/a 语义,提供中英文标签,保留可见焦点,不用轮播消息持续打断读屏。
- 图片没有真实描述时用本地化的“活动照片,第 N 张”等上下文标签,不编造画面描述。
- 修复 loader 内联 SVG 的 fill="%23ffffff" 为有效颜色;关闭按钮视觉可小,有效点击区至少 44×44。
### 5.4 宿主适配与配置
配置优先级:系统默认 < 展示位配置 < script 显式 data-* 覆盖;用户会话收起/暂停选择高于默认值。禁止解析器把“参数缺省”立即变为显式覆盖,导致后台设置永远不生效。
| 配置 | 默认值 | 生效范围 |
|---|---|---|
| position | right | left/right,兼容现有 |
| size | md | sm=280、md=320 |
| intervalMs | 5000 | 图片切换,兼容现有 data-interval |
| lang | auto | 保留现有相册默认语言回退 |
| collapsedDefault | false | 手机小于 480px 默认 true;用户选择优先 |
| imageFit(新增) | contain | contain/cover |
| offsetX(新增) | 16 | 0–120px |
| offsetBottom(新增) | 24 | 0–240px,叠加设备安全区 |
| zIndex(新增) | 1000 | 1–2147483000;用于宿主协调 |
- CSS 宽度取配置宽与视口剩余宽度的较小值,保证 320px 宿主无溢出;窗口变化和横竖屏切换时重新布局。
- zIndex 无法自动解决所有第三方弹窗冲突,文档提供宿主配置方法;禁止承诺自动识别所有客服或 Cookie 横幅。
- 多 slot 同侧时纵向排列并保留间隔;可用高度不足时收起后续实例,不让多张卡片重叠。维持独立用户偏好。
- loader 不向宿主注入全局字体和标签选择器,使用独立容器内联样式;可聚焦控件显式设置必要基础样式,测试宿主 button 全局 CSS 干扰。
- 外层位置/尺寸等后台配置经 iframe 校验后通过白名单 config 消息交给 loader;只允许上述字段和范围,不传任意 CSS/HTML。loader 再验证一次。
- 明确 configVersion 变化后的动态应用规则,旧请求不能覆盖新配置。脚本显式覆盖的字段不会被后端刷新冲掉。
### 5.5 轮询与版本兼容
- 轮播与取数彻底解耦:生产轮询基准 30000ms,沿用现有退避;轮播 3000ms 不能导致每 3 秒取数。
- 保留 ETag、configVersion、旧响应丢弃以及相册换绑清队列语义。
- 304 路径不能导致签名图片永久失效:核对签名有效期与 ETag 策略,过期刷新必须能取得新 URL;必要时提供明确的强制刷新路径及请求上限。
- 新增配置 JSON 字段使用默认回退,不要求全量改写旧记录。更新契约、验证、国内管理与海外投影消费者。
- 展示位未获授权的用户不能通过预览或配置接口读取内部数据。保持当前 admin-only 管理范围,不在本轮悄悄扩展展示位授权体系。
- 老 loader、老 iframe 与新服务过渡期仍可工作;新增消息被旧端忽略时基础展示不应崩溃。
- loader 构建并 sync 到 viewer,不手改 dist。检查现有 Nginx 缓存策略:旧 v1 URL 若长期缓存,不直接假定部署后全部刷新。采用带内容哈希的发布文件及生成代码引用,保留旧 v1 兼容入口;正式 CDN 刷新由 P5 执行,不在本轮操作云资源。
## 6. 必要接口与数据调整
先检查 `packages/contracts/openapi.yaml`,优先复用已有能力。以下是目标语义;只有不存在等价字段时才新增。新接口最终路径与 operationId 在 U01 完成后登记到验收记录。
### 6.1 相册封面摘要
- 在 AlbumSummary 兼容增加可选的 cover 字段(推荐 `{url, expiresAt, width, height}` 或 null,与已有 SignedUrl 结构复用)。
- 数据源优先已配置的有效封面;缺失时返回 null,本轮不对每张卡片扫描最新照片自动生成封面。
- 时间地点使用现有翻译字段,未填不占一整行空白。
- 列表按页批量查询资产,不引入每相册一次数据库/HTTP 查询。
- Flutter 模型允许旧响应无 cover;旧客户端忽略新字段不受影响。
### 6.2 管理照片预览
- 管理列表条目兼容增加 nullable preview,包含 thumb 与 browse 的短期签名 URL、过期时间;复用现有照片宽高。
- 权限:管理员全部、创建者本相册、协作者仅自己上传。若现有列表限定管理者,新增 `scope=own` 的显式查询语义;旧请求权限保持不变。
- scope=own 过滤在服务端完成,相册成员资格每次检查;无权查看的照片不签发 URL。
- 增加或复用有鉴权的单图预览刷新端点,大图分页仍使用管理列表,不改走观众公开接口。
- 没有有效变体、已删除照片返回 null 与真实状态。隐藏照片只给有权限的管理者签名地址。
- 日志不记录完整签名 URL。签名有效期遵循现有策略;取消权限后停止新签发,旧地址有效期是明确的有限撤销窗口。
- 不新增 OSS 公共读权限,不把原图下载能力混入浏览预览;水印等策略沿用现有授权和变体约定。
### 6.3 不在本轮虚构的数据
- 没有照片数/浏览数摘要接口时,不在卡片上放假数字,也不以已加载条数冒充总数。
- 没有海外逐照片确认状态时,不显示“海外已同步”。
- 没有可靠活动运行状态时,用 Event photos,不自动显示 LIVE。
- 没有新的业务批次标识时,上传摘要标注“今天/当前筛选”,不称为“本次活动全部上传”。
## 7. 文件改动地图
| 区域 | 主要文件/目录 | 目标 |
|---|---|---|
| 契约 | packages/contracts/openapi.yaml 及生成脚本/类型 | 预览、封面、组件配置兼容扩展 |
| 国内相册 | services/api/src/albums/albums.service.ts、assets.service.ts、dto.ts | 封面摘要、批量资产组装 |
| 国内照片 | services/api/src/photos/photos.service.ts、album-photos.controller.ts、photos.controller.ts、dto.ts | own 范围和受控预览 |
| 组件服务 | services/api/src/embed/、services/api/src/embed-ov/ | 新配置校验及公开响应 |
| 同步 | services/api/src/sync/、sync-inbox/ 中相关展示位投影 | 新配置完整传递、旧事件兼容 |
| App 主题 | apps/mobile/lib/app/theme.dart、app.dart、新增 ui/ | 主题与公共组件 |
| App 相册 | lib/pages/albums_tab.dart、album_detail_page.dart、album_photos_page.dart | 图片主导布局、大图、权限操作 |
| App 上传 | lib/pages/home_page.dart、queue_tab.dart、server_history_page.dart、capture_page.dart | 导航、汇总、反馈 |
| App 辅助页 | lib/pages/album_form_page.dart、share_qr_dialog.dart、login_page.dart、password_change_page.dart | 表单及样式统一 |
| App 数据 | lib/core/api_client.dart、features/albums/models.dart、features/photos/models.dart | 新字段与刷新调用 |
| App 语言 | apps/mobile/lib/l10n/app_zh.arb、app_en.arb | 双语文案,按工具链生成 |
| loader | packages/embed-loader/src/params.js、loader.js、README-embed.md | 配置、响应式、生命周期 |
| iframe | apps/viewer/src/pages/EmbedPage.vue、embed/useEmbedFeed.ts、feed.ts、postMessage.ts | 轮播、焦点、取数与展示解耦 |
| 后台预览 | apps/admin/src/views/system/embed-slots/SlotFormDialog.vue、EmbedCodeDialog.vue,相关 types/api/utils | 配置、预览、代码生成 |
表中 App 的 lib 相对路径均以 apps/mobile 为根。文件如果在实施前已移动,追踪实际调用链,不复制一套旧实现。
## 8. 可执行任务清单
### U00:基线与现状截图
- [ ] 读取总纲、P3/P4b 验收、当前 scripts/package.json;记录 Git 状态。
- [ ] 运行现有本地环境,采集相册首页、详情、照片页、上传、拍摄、表单、分享、设置、组件展开/收起、后台配置截图。
- [ ] 建立验收记录 `docs/plans/progress/UI01-验收记录.md`,写入实际基线 commit、环境与外部关口。
- [ ] 如果模拟器/服务不可用,说明原因并继续契约与静态实现,不伪造截图。
出口:当前行为、已有改动、可用环境明确。
### U01:契约与数据配合
- [ ] 列出复用字段与必须新增字段,落实第 6 节及配置优先级。
- [ ] 修改 OpenAPI、校验、实现、生成类型、Flutter 数据模型。
- [ ] 完成权限测试:管理员/创建者/协作者/移除成员/跨相册访问/隐藏/删除。
- [ ] 检查封面和预览批量组装、URL 刷新及旧字段缺省兼容。
- [ ] 对组件新配置完成国内→海外投影验证。
出口:有真实数据支撑后续界面,旧消费端可用;不得先用假图片掩盖缺失接口。
### U02:设计基础与导航
- [ ] 实现主题、公共状态/空态/缩略图组件、中英文基础文案。
- [ ] 调整底部导航与任务角标,保留页面/账号状态。
- [ ] 相册列表完成一页可运行样板,同时检查浅色/深色、中英文。
出口:视觉规范已在真实页面体现;后续页面复用同一套组件。
### U03:相册与照片工作流
- [ ] 完成封面卡片、详情主次操作、底部上传入口。
- [ ] 完成照片网格、大图切换、签名刷新与返回原位置。
- [ ] 完成有权限的隐藏/恢复/删除,协作者 own 列表。
- [ ] 校验分页、图片缺失、长标题、处理失败、上传关闭。
出口:工作人员可凭图找照片,上传入口清楚,权限不退化。
### U04:上传与拍摄
- [ ] 完成当前筛选摘要、状态分组、明确错误操作、移除记录文案。
- [ ] 优化拍摄界面和入队反馈;保留原生通道与任务状态机。
- [ ] 验证准备失败、断网、重试、确认中、退出切账号、历史记录。
- [ ] 检查列表滚动与上传并行时,不发生整页高频重建。
出口:异常可理解且可处理,没有“上传完成=公开”的误导。
### U05:辅助页面统一
- [ ] 整理登录、密码、创建/编辑、分享、成员/分类、设置及服务端历史页样式。
- [ ] 表单键盘、双语切换、默认语言回退、保存失败均有可用反馈。
- [ ] 深色、大字体和窄屏无溢出;系统分享按第 4.5 节处理。
出口:App 所有主要路径风格统一,不只优化首页。
### U06:嵌入轮播与 loader
- [ ] 完成两形态、完整图片模式、页码、暂停/手动切换、加载和失败状态。
- [ ] 完成配置合并、动态 config 消息、收起偏好和 destroy 生命周期。
- [ ] 解耦轮播与轮询;处理焦点、减少动态效果、预加载和过期 URL。
- [ ] 实现窄屏/安全区/窗口变化/多实例布局;修复图标颜色。
- [ ] 保留并测试消息白名单、iframe sandbox、外链安全。
- [ ] build + sync loader,落实带哈希产物与旧入口兼容,更新接入说明。
出口:第三方页面可接入,不遮挡关键内容、不泄漏焦点到隐藏帧。
### U07:后台配置与预览
- [ ] 编辑表单提供左右设置、尺寸、轮播间隔、语言、收起默认、图片适配、偏移和层级。
- [ ] 表单旁或下方提供桌面/手机预览,未保存修改即时更新本地预览。
- [ ] 预览使用本地示例数据或授权管理数据,不写展示位、不上报浏览次数、不跳转真实观众页。
- [ ] 预览与生产组件共享展示结构,避免复制两套不同样式;数据获取与展示拆分。
- [ ] 明确“保存”才更新线上配置;接入代码只包含用户选择的显式覆盖,默认让后台配置生效。
出口:后台预览与实际效果一致,配置修改确实传到现有宿主页。
### U08:回归与视觉验收
- [ ] 完成第 9 节矩阵,修复实现造成的回归。
- [ ] 对照基线采集优化后截图;记录设备/视口/语言/主题。
- [ ] Android 可用环境执行真实链路冒烟;iOS 没有 macOS/设备时明确待验收,不能以 Dart 测试代替。
- [ ] 记录结果、未执行项、原因、影响与下一步;不引用旧阶段测试数量作为本阶段结果。
### U09:文档与交接
- [ ] 更新总纲增加 UI-01 状态,不将 P5 标记完成。
- [ ] 修正直接涉及的旧状态描述;D06/D07/D11 等分别标注“已有工作默认”与“待正式确认”,不擅自替用户确认。
- [ ] 完成验收记录、截图索引、API 变更、脚本兼容和已知问题列表。
- [ ] 总结文件、运行方式、测试、剩余真机关口。无需依赖对话上下文即可接手。
任务依赖:U00 → U01 → U02 → U03/U04/U05;U06 依赖 U01 配置契约;U07 依赖 U06;U08 汇总全部;U09 收尾。这里表达工作依赖,不要求并行代理。
## 9. 验收矩阵与命令
### 9.1 验收编号
| 编号 | 必须验证的结果 |
|---|---|
| UI-A01 | App 浅色/深色一致,无硬编码浅色导致不可读 |
| UI-A02 | 相册有/无封面、长标题、中英文回退、时间地点正确 |
| UI-A03 | 图片网格与大图真实可用,分页及返回位置正确 |
| UI-A04 | 隐藏照片权限受控,删除照片不继续签预览,协作者无法看他人照片 |
| UI-A05 | 上传状态、筛选汇总、重试、清理记录不改变队列语义 |
| UI-A06 | 连拍与上传同时进行,成功提示不打断拍摄 |
| UI-A07 | 320/390/430 逻辑宽度与平板、文字 1.0/1.3/2.0 倍主要操作可达 |
| UI-A08 | 组件 320/390/768/1440px 宿主无横向溢出,安全区与偏移生效 |
| UI-A09 | 横图/竖图/全景/文字海报可看完整,换图不导致卡片跳高 |
| UI-A10 | 收起/展开/暂停、后台、键盘焦点、减少动态效果行为正确 |
| UI-A11 | 相册换绑、配置修改、旧响应、304、URL 过期、404/410 正确 |
| UI-A12 | 双来源真实跨源测试通过,非法 origin/source/config 消息被拒绝 |
| UI-A13 | 后台未保存预览不改变线上数据,不增加浏览统计 |
| UI-A14 | 老脚本与旧记录缺字段不崩溃,显式覆盖与后台配置优先级正确 |
| UI-A15 | 各端中英文键与占位符一致,运行时长文案无裁切 |
| UI-A16 | 未运行的真机/云资源项明确列为待验证,不宣称生产验收完成 |
### 9.2 有意义的自动化
- API 集成:管理预览权限矩阵、own 过滤、签名刷新、旧响应兼容、无 N+1 查询。
- Flutter widget:主要布局和关键状态;有稳定渲染环境后给核心页面增加少量 golden,不为每个间距写脆弱测试。
- Flutter 现有集成:登录→相册→拍摄/选择→队列→历史;修改视觉后维护语义定位,不靠坐标点击。
- loader 单测:缺省/显式参数区分、上下界、合并优先级、配置消息校验。
- viewer 单测:暂停和恢复、请求计时器独立、失败图片、签名刷新、非当前帧不可聚焦。
- Playwright:真实不同 origin 的宿主与 iframe(两个端口即可),至少一次模拟全局 button CSS、客服浮层和多 slot;不能继续只用同源测试替代跨源验证。
- 性能记录:保存改造前后首屏请求数、组件预加载数、loader gzip 大小;上传中列表滚动使用 profile 观察。不得捏造“提升百分比”。
### 9.3 执行入口
在仓库根运行现有 Web 检查;先定向再全量,环境缺失与真实失败分开记录:
```text
pnpm lint
pnpm typecheck
pnpm test
pnpm --filter @photo/embed-loader build
pnpm --filter @photo/embed-loader sync
pnpm --filter @photo/viewer build
pnpm --filter @photo/viewer test:e2e
```
Flutter 在 `apps/mobile`,使用仓库固定的 Flutter 工具链:
```text
flutter gen-l10n
flutter analyze
flutter test
```
OpenAPI 生成、后端集成、admin E2E、mobile IT 的命令从对应 package.json 与现有 scripts 核对后运行;不猜不存在的脚本。全量测试可能初始化数据库,必须先确认指向专用测试环境。记录实际命令、退出码、结果和跳过原因。
## 10. 验收记录模板
在 `docs/plans/progress/UI01-验收记录.md` 填写:
1. 基线与完成 commit(未提交则记录工作树状态),日期与执行环境。
2. U00–U09 完成表,未完成项原因。
3. UI-A01–UI-A16:通过/失败/待验证、证据位置。
4. API 新字段与权限变化、旧客户端兼容结果。
5. App 各页、组件两形态、后台预览的前后截图索引。
6. 测试命令与真实结果,性能观察而非推测。
7. 既有改动保护情况、未触碰的外部环境。
8. 方案偏差及理由;仍需真机、OSS/CDN、用户接口的事项。
完成定义:规定页面和组件实现可运行、必要接口已落地、针对性回归通过、视觉证据可核查、剩余外部关口清晰。不以“代码生成完”“本地构建通过”单独替代完成定义。
## 11. 可直接交给 Claude 的执行指令
> 请执行 `docs/plans/2026-09-21-UI01-App与悬浮组件体验优化实施方案.md`,从 U00 开始核对当前仓库,按 U01–U09 实际完成 App 与 JS 悬浮组件的界面和体验调整。沿用文档默认设计,不重复询问常规样式选择。保护当前未提交改动,不重写上传引擎,不绕过管理预览权限,不虚构数据或测试结论。接口缺口按文档兼容扩展;每步执行相应验证。遇到真机或外部环境依赖,明确登记并继续不受影响的工作。最终提供可运行实现、UI01 验收记录、截图证据和剩余事项,不只返回计划。未经另外授权不要发布、推送、修改云资源或创建正式版本标签。