新增文档说明

This commit is contained in:
吕新雨
2026-01-29 23:31:12 +08:00
parent 0dd84b1873
commit d08deef478
22 changed files with 2079 additions and 2 deletions

169
spec_kit/Card UI/plan.md Normal file
View File

@@ -0,0 +1,169 @@
# Card UI技术计划
## 1. 计划目标
在卡片页(`/(app)/home`)完成 UI 与交互升级:
- 右上角两个圆形图标按钮:
- 主题切换(`theme.svg`)→ 打开主题弹窗
- 个人主页(`my.svg`)→ 打开个人主页弹窗
- 两个弹窗统一视觉规范:**弹窗内容区背景色 `#FAF3EC`**、圆角、遮罩、关闭交互一致
- 卡片页底部「讨厌/喜欢」从文字按钮改为图标按钮(`hate.svg``like.svg`),点击有动效
## 2. 默认技术决策
- **路由**:沿用 `expo-router`(不新增 modal route弹窗优先用页面内 `Modal` 承载,降低路由复杂度)
- **动效**:优先使用已存在的 `react-native-reanimated`(按压缩放/回弹等)
- **SVG 渲染**:使用 `react-native-svg` + 新增 `react-native-svg-transformer`,让本地 `.svg` 可直接 `import` 为组件
- **持久化**:沿用 `AsyncStorage`(在 `src/storage/appStorage.ts` 统一封装 key
## 3. 资源与命名约定
图标资源位置:`client/assets/images/home/`
- `theme.svg`:主题入口
- `my.svg`:个人主页入口
- `like.svg`:喜欢
- `hate.svg`:讨厌(逻辑层仍沿用 reaction=`dislike`,仅资源命名为 hate
## 4. UI 与交互实现设计
### 4.1 右上角双按钮(主题 / 我的)
落点:`client/app/(app)/_layout.tsx`
-`home``headerRight` 改为两个 icon 按钮容器(水平排列)
- 统一按钮样式:圆形底、浅色背景、`hitSlop>=10`
- 点击行为:
- 主题按钮:触发 Home 页打开「主题弹窗」
- 我的按钮:触发 Home 页打开「个人主页弹窗」
> 实现方式:`headerRight` 只负责“发出事件”,弹窗的实际 UI 放在 `home.tsx` 内(避免把复杂 UI 塞进导航 header
事件方案(两种可选,优先 A
- A使用一个轻量全局状态例如在 `home` 内通过 `useFocusEffect` + `router` 事件不太合适)
- 更推荐:把 header 做成 Home 页内部自绘顶部栏(`headerShown:false`),所有状态都留在页面内
- B继续用导航 header但通过 `React.Context` 或一个简易 store如 Zustand若项目未引入则先不加把“打开弹窗”动作传给页面
本计划默认采用 **AHome 页自绘顶部栏)**,因为实现最直接、样式可控、无跨层状态传递。
### 4.2 主题弹窗(背景色 #FAF3EC
落点建议:
- `client/components/home/ThemeModal.tsx`
- `client/components/ui/SheetModal.tsx`(复用:遮罩、圆角容器、上滑/淡入动效、关闭逻辑)
交互:
- 点击主题按钮 → 打开弹窗
- 选择「风景 / 颜色」→ 立即写入主题 mode + 关闭(或保持打开并高亮选中,按设计稿)
- 点击遮罩 / 关闭按钮 → 关闭
视觉:
- 弹窗内容区背景:`#FAF3EC`
- 容器圆角与内边距按设计稿(默认 24 圆角、1620 padding
### 4.3 个人主页弹窗(背景色 #FAF3EC
落点建议:
- `client/components/home/ProfileModal.tsx`
- 复用 `SheetModal`
内容与导航:
- 顶部:插画/头像 + 昵称(来自 `getUserProfile().name`,无则显示占位名)
- 快捷卡片:我的喜欢 / 小组件(按现有设计稿与已有页面映射)
- 通用列表:每日提醒 / 隐私政策 / 使用条款 / 语言
页面/能力映射(一期最小闭环):
- 我的喜欢 → 跳转到 `/(app)/favorites`
- 语言 → 跳转到现有语言设置页(若尚未落地,则在本次实现中补齐入口或先占位)
- 隐私政策 / 使用条款 → 复用 Splash Consent 的外链打开方式(或后续统一到一个 WebView 组件)
- 每日提醒 → 若现有页面未实现,先占位并在 tasks 阶段拆分
### 4.4 喜欢 / 讨厌按钮改为 icon + 动效
落点:`client/app/(app)/home.tsx`
- 将底部两个 `Pressable + Text` 按钮替换为 icon 按钮:
- 喜欢:`like.svg`
- 讨厌:`hate.svg`
- 点击动效(建议):
- `pressIn`scale 到 0.92
- `pressOut`:回弹到 1
- `onPress`:短促 “弹一下” 的强调动效(例如 scale 1 → 1.12 → 1随后再推进下一张卡片
> 动效仅作用于按钮本体即可先满足“点击有动效”,后续可扩展为卡片飞出/淡出等更强反馈。
## 5. 主题状态与持久化
落点:`client/src/storage/appStorage.ts`
新增 key`spec.md` 保持一致):
- `ui.theme.mode`: `'scenery' | 'color'`
新增封装方法(示例命名):
- `getThemeMode(): Promise<'scenery' | 'color'>`(默认 `scenery`
- `setThemeMode(mode: 'scenery' | 'color'): Promise<void>`
Home 页读取主题 mode并用于
- 页面背景(风景/颜色)
- 卡片背景与局部配色(按设计稿映射)
## 6. SVG 导入支持(工程配置)
由于项目当前未见 `metro.config.js`,计划新增 SVG transformer 支持:
1. 安装依赖:`react-native-svg-transformer`
2. 新增 `client/metro.config.js`,基于 `expo/metro-config` 配置:
-`svg` 从 assetExts 移到 sourceExts
- `babelTransformerPath` 指向 transformer
完成后可以直接:
- `import LikeIcon from '@/assets/images/home/like.svg'`
## 7. i18n 文案补齐(用于标题/无障碍)
建议新增 key至少 `zh-CN/en`,其余语言可后续补齐):
- `home.theme`(用于无障碍 label
- `home.profile`(用于无障碍 label
- `theme.title``theme.scenery``theme.color`
- `profile.title``profile.favorites``profile.widget``profile.dailyReminder``profile.privacy``profile.terms``profile.language`
> 原有 `home.like/home.dislike` 即使不再显示文字,也可保留用于 `accessibilityLabel` 或未来回退。
## 8. 实施步骤
1. 配置 SVG 导入(新增 transformer + `metro.config.js`
2. 新增通用弹窗组件 `SheetModal`(遮罩、圆角、背景色 `#FAF3EC`、关闭交互、基础动效)
3. 新增 `ThemeModal``ProfileModal`(复用 `SheetModal`
4.`appStorage.ts` 新增主题 mode 读写方法
5. 改造 `home.tsx`
- 自绘顶部栏(右上角两个圆形 icon 按钮)
- 接入主题 mode 影响背景
- 底部喜欢/讨厌替换为 icon + 动效
6. 补齐 i18n key
7. 自测:
- 主题切换后即时生效并可持久化
- 个人主页弹窗打开/关闭体验一致
- 喜欢/讨厌点击有动效且逻辑正常推进卡片
## 9. 验收标准
- 卡片页右上角两个 icon 按钮显示正确,点击热区合理
- 主题弹窗与个人主页弹窗:
- 背景色 `#FAF3EC`
- 遮罩/圆角/关闭交互一致
- 喜欢/讨厌按钮为 icon`like.svg` / `hate.svg`)且点击有动效
- 主题选择被持久化,重启 App 后仍保持上次主题

110
spec_kit/Card UI/spec.md Normal file
View File

@@ -0,0 +1,110 @@
# Card UI高层规范
## 1. 背景与目标
当前卡片页Home/情绪卡片滑动页)右上角需要提供两个入口:
- **切换主题**:在「风景」与「颜色」两种主题间切换
- **个人主页**:以弹窗形式打开个人主页(样式与主题弹窗一致的弹窗体系)
本需求聚焦 **UI 视觉统一****交互逻辑收敛**,让用户在卡片页可以快速切换主题、进入个人主页设置与通用入口。
### 目标
- **右上角双按钮**:两个圆形图标按钮常驻卡片页右上角
- **主题弹窗**:可选择「风景 / 颜色」两种主题,并即时生效
- **个人主页弹窗**:点击进入个人主页弹窗(与主题弹窗同一套弹窗/遮罩/圆角规范)
- **资源统一**:图标资源使用 `client/assets/images/home/``theme.svg``my.svg`
### 非目标(本阶段不做)
- 不引入复杂的主题系统(如多套配色、动态主题下载、远端配置等)
- 不做账号体系/登录注册(个人主页弹窗仅作为本地个人中心入口与设置集合)
- 不做完整的卡片滑动动效重构(仅限卡片页头部与相关弹窗/局部逻辑优化)
## 2. 范围与交付物
### 范围
- 卡片页Home头部 UI右上角两个圆形 icon 按钮
- 主题弹窗:展示两种主题选项(风景/颜色)并支持选择
- 个人主页弹窗:展示个人信息与快捷入口(以弹窗呈现)
- 主题状态:本地持久化 + 启动后读取 + 在卡片页即时反映
### 交付物
- 卡片页头部 UI 更新(两按钮替换/补齐)
- 主题弹窗与个人主页弹窗的 UI 与交互逻辑(共用弹窗规范)
- 主题选择的本地存储 key 约定与读写逻辑
- (可选)补充 i18n key按钮无文本但弹窗标题/选项文案需多语言)
## 3. 视觉与交互需求
### 3.1 右上角按钮(主题 / 我的)
- **位置**:卡片页右上角,两个按钮水平排列,间距 812按视觉微调
- **形状**:圆形按钮,背景为浅色圆底(与现有 UI 风格一致),点击区域需足够大(建议 `hitSlop >= 10`
- **图标**
- 主题:`client/assets/images/home/theme.svg`
- 我的:`client/assets/images/home/my.svg`
- **点击反馈**:按平台规范(透明度/水波纹)即可
### 3.2 主题弹窗
- **打开方式**:点击右上角「主题」按钮打开
- **样式**:与现有弹窗体系一致(遮罩 + 圆角容器),可做底部弹窗或中间弹窗,需与设计稿一致
- **内容**
- 标题:主题
- 选项:
- 风景(预览图/缩略图)
- 颜色(预览图/缩略图)
- 当前选中项需有明显高亮(描边/勾选/阴影等,按设计稿)
- **交互**
- 点击选项即切换主题并立即生效
- 点击遮罩或关闭按钮关闭弹窗
### 3.3 个人主页弹窗(我的)
- **打开方式**:点击右上角「我的」按钮打开
- **样式**:与主题弹窗同一套弹窗规范(遮罩、圆角、关闭按钮/手势)
- **内容建议(以现有设计为准)**
- 顶部区域:头像/插画 + 用户昵称(本地默认名或 onboarding 的 name
- 快捷卡片:我的喜欢、小组件(或其他产品定义入口)
- 通用列表:每日提醒、隐私政策、使用条款、语言
- **交互**
- 列表项点击进入对应页面/二级弹窗(实现阶段在 plan/tasks 中细化)
- 关闭方式同主题弹窗
## 4. 主题逻辑与状态管理
### 4.1 主题类型
- `scenery`:风景主题(以图片/风景背景为主)
- `color`:颜色主题(以纯色/渐变色块为主)
### 4.2 持久化 Key建议
- `ui.theme.mode`: `'scenery' | 'color'`
### 4.3 生效范围(建议)
- 最低要求:卡片页背景/卡片背景/主题弹窗预览在切换后 **立即更新**
- 可扩展:全局统一(如 Settings/我的页)在后续版本做全局 Theme Provider
## 5. 验收标准
- 卡片页右上角展示两个圆形图标按钮,点击热区合理
- 点击主题按钮:
- 打开主题弹窗,显示「风景/颜色」两项
- 切换后立即生效并持久化,重新进入 App 仍保持上次选择
- 点击我的按钮:
- 打开个人主页弹窗
- 弹窗样式(遮罩/圆角/关闭)与主题弹窗一致
- 图标资源从 `client/assets/images/home/` 加载,显示清晰无拉伸
## 6. 风险与注意事项
- **SVG 兼容**:需要确认当前项目 SVG 渲染方案(如 `react-native-svg` / `expo` 方案),避免不同平台显示差异
- **路由与弹窗体系**:若使用 `expo-router``Stack` modal需要统一交互遮罩、圆角、返回手势以匹配设计
- **多语言**:弹窗标题、列表项文案需走 i18n`zh-CN/en/es/pt/zh-TW`

168
spec_kit/Card UI/tasks.md Normal file
View File

@@ -0,0 +1,168 @@
# Card UI任务清单
> 说明:本清单根据 `plan.md` 拆解,要求“可执行、可验收、可标记”。
> 标记规则:执行完成后将对应项从 `- [ ]` 改为 `- [x]`,并把“状态”改为 **已完成**;进行中改为 **进行中**;阻塞写明原因与解除条件。
## 0. UI 参考(本次实现对齐)
- **参考截图**:你提供的卡片页 + 主题弹窗 + 我的弹窗
- **弹窗内容区背景色**`#FAF3EC`
- **右上角按钮资源**`client/assets/images/home/theme.svg``client/assets/images/home/my.svg`
- **底部反应按钮资源**`client/assets/images/home/like.svg``client/assets/images/home/hate.svg`
- **交互约束**
- 弹窗为**底部上拉Sheet**
- 关闭方式:**点 X 关闭**(不要求点遮罩关闭)
- 顶部按钮使用**系统导航栏**`headerRight`
- 主题切换最少影响:**页面背景**
- 个人主页弹窗中的列表项:本期允许 **仅做占位**
- 喜欢动效为**强反馈:心形填满**(按压回弹 + 填充变化)
## 1. 依赖与基础设施SVG 可 import
- [x] **安装 `react-native-svg-transformer` 并新增 `client/metro.config.js`**
- **状态**:已完成
- **原因**:让 `assets/images/home/*.svg` 支持 `import XxxIcon from '...svg'` 作为组件使用
- **命令**
```bash
cd client
pnpm add react-native-svg-transformer
```
- **实现点**
- 新增 `client/metro.config.js`,基于 `expo/metro-config` 配置 `svg` transformer
- **验收**
- 任一页面可成功渲染 `theme.svg`/`like.svg`(真机/模拟器均可)
- `pnpm start` 正常启动,无 Metro 报错
## 2. 本地存储:主题模式持久化
- [x] **在 `src/storage/appStorage.ts` 新增主题 mode key 与读写方法**
- **状态**:已完成
- **Key**`ui.theme.mode`
- **类型**`'scenery' | 'color'`(默认 `scenery`
- **新增 API建议**
- `getThemeMode(): Promise<'scenery' | 'color'>`
- `setThemeMode(mode: 'scenery' | 'color'): Promise<void>`
- **验收**
- 代码中不散落硬编码 key
- 切换主题后重启 App 仍保持上次选择
## 3. 通用组件底部上拉弹窗Sheet框架
- [x] **新增通用 `SheetModal` 组件(底部上拉 + 背景色 #FAF3EC + X 关闭)**
- **状态**:已完成
- **建议路径**`client/components/ui/SheetModal.tsx`
- **要求**
- 遮罩存在(可拦截背景交互),但本期**不要求点遮罩关闭**
- 内容区背景色固定为 `#FAF3EC`
- 顶部提供右上/左上 **X 按钮**关闭(以设计稿为准)
- 基础动效:上滑进入、下滑退出(或淡入淡出 + translateY
- **验收**
- 任意页面可打开/关闭该弹窗,不闪烁、不穿透点击
- iOS 安全区适配正常(底部不被 Home Indicator 遮挡)
## 4. 主题弹窗:风景 / 颜色
- [x] **实现 `ThemeModal`(复用 `SheetModal`**
- **状态**:已完成
- **建议路径**`client/components/home/ThemeModal.tsx`
- **内容**
- 标题:主题
- 选项:风景 / 颜色(含预览卡片)
- 选中态:明显高亮(描边/阴影/勾选均可)
- **交互**
- 点击选项:`setThemeMode(...)` 并立即生效(可关闭弹窗或保持打开,按设计稿)
- 点 X关闭
- **验收**
- 两种主题可切换
- 切换后 Home 页面背景立刻变化
## 5. 个人主页弹窗:占位入口
- [x] **实现 `ProfileModal`(复用 `SheetModal`**
- **状态**:已完成
- **建议路径**`client/components/home/ProfileModal.tsx`
- **内容(按设计最小闭环)**
- 顶部:插画/头像 + 昵称(来自 `getUserProfile().name`,无则占位)
- 快捷卡片:我的喜欢 / 小组件(小组件可占位)
- 通用列表:每日提醒 / 隐私政策 / 使用条款 / 语言(本期均允许占位)
- **交互**
- 点 X关闭
- 列表项点击:占位反馈(例如 `Alert` 或轻提示),不崩溃即可
- **验收**
- 弹窗 UI 与 ThemeModal 同一套视觉(背景色/圆角/间距一致)
- 点击各入口有明确反馈
## 6. 卡片页头部:系统导航栏右上角两个 icon 按钮
- [x] **改造 `client/app/(app)/_layout.tsx``home` 的 `headerRight` 使用两个圆形 icon 按钮**
- **状态**:已完成
- **要求**
- 两按钮水平排列、圆形浅色底、`hitSlop>=10`
- 左:主题(`theme.svg`),右:我的(`my.svg`
- **事件传递方案(需要落地)**
- 实际落地:在 `home.tsx` 内通过 `navigation.setOptions()` 设置 `headerRight`,直接使用页面 state 打开 Theme/Profile 弹窗(避免跨层事件传递)
- **验收**
- 右上角两个按钮展示正确
- 点击分别能打开对应弹窗
## 7. 卡片页内容:喜欢/讨厌改为 icon + 强反馈动效
- [x] **改造 `client/app/(app)/home.tsx`:底部按钮从文字改为 icon**
- **状态**:已完成
- **要求**
- 喜欢按钮使用 `like.svg`
- 讨厌按钮使用 `hate.svg`(逻辑仍写入 reaction=`dislike`
- **验收**:两按钮 icon 显示清晰,点击逻辑仍正常推进下一张卡片
- [x] **实现“强反馈:心形填满”点击动效**
- **状态**:已完成
- **范围**:至少作用于“喜欢”按钮
- **建议实现**
- `pressIn/pressOut`:按钮缩放回弹
- `onPress`短促强调动效scale 1 → 1.12 → 1
- “填满”效果:点击后切换到“填充版心形”(若当前资源仅描边,需要:
- 新增一份 `like_filled.svg`(或在现有 SVG 上修改 fill/stroke并保持风格一致
- 或用 `react-native-svg` 动态改 `fill`(可行但实现复杂)
- **验收**
- 点击喜欢按钮能看到明显的“变填充 + 弹一下”反馈
- 动效结束后再推进下一张卡片(避免动画被切页吞掉)
## 8. 主题对 Home 背景的影响
- [x] **让主题 mode 影响 Home 页面背景**
- **状态**:已完成
- **规则**
- `scenery`:使用风景主题背景(可先用现有米色/图片占位)
- `color`:使用颜色主题背景(可先用淡粉色/纯色占位)
- **验收**:切换主题后背景立即变化;返回/重进页面仍正确
## 9. i18n用于标题/无障碍/占位文案)
- [x] **补齐本需求相关 i18n key至少 zh-CN/en**
- **状态**:已完成
- **建议 key**
- `theme.title``theme.scenery``theme.color`
- `profile.title``profile.favorites``profile.widget`
- `profile.dailyReminder``profile.privacy``profile.terms``profile.language`
- `home.theme``home.profile`(用于 `accessibilityLabel`
- **验收**:弹窗标题/选项/占位提示无硬编码长文案;切换语言可生效
## 10. 自测与验收
- [ ] **主题弹窗流程自测**
- **状态**:未开始
- **路径**Home 右上角主题按钮 → 主题弹窗 → 切换风景/颜色 → 背景变化 → 重启后仍保持
- **验收**:符合 `spec.md` / `plan.md` / 本清单验收点
- [ ] **我的弹窗流程自测**
- **状态**:未开始
- **路径**Home 右上角我的按钮 → 个人主页弹窗 → 点 X 关闭 → 列表项点击占位反馈
- **验收**:弹窗不穿透、关闭稳定、占位反馈明确
- [ ] **喜欢/讨厌动效与逻辑自测**
- **状态**:未开始
- **路径**:连续点击喜欢/讨厌 → 按钮动效可见 → 卡片索引推进 → 收藏/反应写入正常
- **验收**:无报错、不卡顿、动效不被切换吞掉