新增文档说明

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 关闭 → 列表项点击占位反馈
- **验收**:弹窗不穿透、关闭稳定、占位反馈明确
- [ ] **喜欢/讨厌动效与逻辑自测**
- **状态**:未开始
- **路径**:连续点击喜欢/讨厌 → 按钮动效可见 → 卡片索引推进 → 收藏/反应写入正常
- **验收**:无报错、不卡顿、动效不被切换吞掉

View File

@@ -0,0 +1,28 @@
# Onboarding UI Optimization (补充说明)
## Colors
```typescript
export const OnboardingColors = {
background: '#FFF4EA',
textPrimary: '#4A3B32',
textSecondary: '#8C8C8C', // 占位符颜色
buttonStart: '#F69F7B',
buttonEnd: '#F99CC0',
cardBackground: '#FFFFFF',
cursor: '#F99CC0', // 假设光标颜色与按钮结束色一致
};
```
## SVG Assets (Next Arrow)
```xml
<!-- 简单的右箭头 SVG 路径参考 -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
```
## Intent Icons (Emojis as placeholders)
- 爱情: ❤️
- 生活: ⛅
- 旅游: 🌴
- 职场: 💼

View File

@@ -0,0 +1,44 @@
# Onboarding UI Optimization (技术计划)
## 1. 基础设施与样式定义 (Foundation)
- [ ] **定义 Design Tokens**
-`constants/Colors.ts` 或新文件 `constants/OnboardingTheme.ts` 中定义新色值:
- `background`: `#FFF4EA`
- `textPrimary`: `#4A3B32` (深棕)
- `buttonGradientStart`: `#F69F7B`
- `buttonGradientEnd`: `#F99CC0`
- `inputBackground`: `#FFFFFF`
- [ ] **创建基础组件**
- `SerifText`: 封装 `Text` 组件,默认应用 `fontFamily: Platform.select({ ios: 'Georgia', android: 'serif', default: 'serif' })` 及主题色。
- `OnboardingLayout`: 通用容器,包含 `SafeAreaView`、背景色设置、Header (Skip 按钮) 与 Footer 占位。
## 2. UI 组件开发 (UI Components)
- [ ] **下一步按钮 (NextButton)**
- 使用 `expo-linear-gradient` 实现渐变背景。
- 绘制 SVG 箭头 (使用 `react-native-svg` 或直接嵌入 SVG xml string 如果不引入新库,首选 `react-native-svg` 如果项目已有,否则用简单 View/Image 替代或引入库)。
- *注:检查 `package.json` 发现未安装 `react-native-svg`,将使用 Image 或 Text Icon 替代,或者建议安装。鉴于需求“你自己生成 SVG”推荐安装 `react-native-svg`。*
- [ ] **称呼页 (NameInputStep)**
- 居中白色圆角容器。
- `TextInput` 样式定制:隐藏默认下划线,自定义光标颜色 (selectionColor),字体样式。
- [ ] **意图选择页 (IntentSelectionStep)**
- 2x2 Grid 布局。
- `SelectionCard` 组件:支持选中/未选中状态切换 (边框/背景变化)。
- 实现多选逻辑。
## 3. 页面整合与逻辑 (Integration)
- [ ] **重构 `app/(onboarding)/onboarding.tsx`**
- 引入状态管理:`currentStep` (0: Name, 1: Intent)。
- 状态收集:`name` (string), `selectedIntents` (string[])。
- 步骤切换逻辑:
- Step 1 -> Next -> Step 2。
- Step 2 -> Next -> Finish (持久化 & 跳转)。
- [ ] **持久化逻辑更新**
- 确保 `appStorage.ts` 支持存储新的字段(如果需要)。
- 完成时写入 `isOnboardingCompleted`
## 4. 依赖检查
- 需确认是否允许安装 `react-native-svg` 用于渲染高质量图标。若不允许,将导出为 PNG 使用。

View File

@@ -0,0 +1,68 @@
# Onboarding UI Optimization (高层规范)
## 1. 背景与目标
本需求旨在根据最新的设计稿Visual References对现有的 Onboarding 流程界面进行视觉升级与体验优化。目标是打造一个温暖、极简且高质感的用户引导流程。
### 核心目标
- **视觉升级**:实现设计稿中的配色、排版与组件样式(暖色调、衬线字体风格)。
- **组件复用**:提取通用的布局容器、按钮与输入组件。
- **交互流畅**:确保输入、选择与页面切换的流畅体验。
## 2. 视觉设计规范 (Visual Specifications)
基于提供的设计稿与确认信息,定义以下视觉基调:
### 2.1 整体风格
- **背景色**`#FFF4EA` (Warm Beige)。
- **字体**
- 使用系统默认衬线字体 (System Serif) 作为主要字体。
- 预留字体配置接口,方便后续替换自定义字体文件。
- 颜色主要为深棕色 (例如 `#4A3B32`)。
- **按钮**
- **下一步按钮**
- 底部居中,圆形。
- **背景渐变**`#F69F7B` (起) → `#F99CC0` (止)。
- **图标**:白色箭头 SVG (需生成)。
- **Skip 按钮**:右上角,纯文字 "Skip ->",衬线体。
### 2.2 页面布局
- **Header**:包含状态栏占位与 "Skip" 按钮。
- **Body**:垂直居中分布主要内容(问题 + 交互区域)。
- **Footer**:底部居中放置圆形下一步按钮。
## 3. 页面详细需求
### 3.1 称呼页 (Name Input)
- **标题**:“我可以怎么称呼你?” (Serif)
- **交互区域**
- 白色圆角矩形容器。
- 输入框居中,占位符或默认值显示 "Hao"。
- 光标样式需定制(粉色竖线)。
- **逻辑**
- 输入非空时,键盘回车或点击底部按钮进入下一页。
- 支持跳过。
### 3.2 意图选择页 (Intent Selection)
- **标题**:“你希望得到什么帮助?” (Serif)
- **交互区域**
- 2x2 网格布局。
- **选项卡片**
- 白色圆角矩形背景。
- 内容:左侧 Icon (占位) + 右侧/居中 文字。
- 示例选项:❤️ 爱情, ⛅ 生活, 🌴 旅游, 💼 职场
- **选中状态**:需补充选中态设计(如边框高亮或背景微变)。
- **逻辑**
- **多选**。
- 选择后点击底部按钮进入下一页。
## 4. 技术实现建议
- **布局容器**:创建一个通用的 `OnboardingLayout` 组件处理背景、SafeArea、Skip 按钮与底部按钮。
- **字体**:封装 `SerifText` 组件,目前映射到系统 Serif后续可一键替换。
- **图标**:使用 `expo-vector-icons` 或简单 View 占位,箭头图标使用内联 SVG。
- **渐变**:使用 `expo-linear-gradient` 实现按钮背景。
## 5. 待确认事项 (Open Questions)
- 已确认:字体使用默认,图标暂用占位,意图页为多选,背景色与渐变色已定。

View File

@@ -0,0 +1,17 @@
# Onboarding UI Optimization (任务清单)
## Setup & Styles
- [ ] 安装 `react-native-svg` (如果尚未安装,需确认) <!-- id: 0 -->
- [ ] 创建 `client/constants/OnboardingTheme.ts` 定义色值 <!-- id: 1 -->
- [ ] 创建 `client/components/onboarding/SerifText.tsx` <!-- id: 2 -->
- [ ] 创建 `client/components/onboarding/OnboardingLayout.tsx` (含 Skip 按钮) <!-- id: 3 -->
## Components
- [ ] 创建 `client/components/onboarding/NextButton.tsx` (渐变 + 箭头) <!-- id: 4 -->
- [ ] 创建 `client/components/onboarding/NameInputStep.tsx` <!-- id: 5 -->
- [ ] 创建 `client/components/onboarding/IntentSelectionStep.tsx` (含 Grid & Card) <!-- id: 6 -->
## Integration
- [ ] 修改 `client/app/(onboarding)/onboarding.tsx` 实现多步逻辑 <!-- id: 7 -->
- [ ] 调试并验证 iOS 模拟器显示效果 <!-- id: 8 -->
- [ ] 验证持久化存储逻辑 <!-- id: 9 -->

View File

@@ -0,0 +1,112 @@
# Splash Consent技术计划
## 1. 计划目标
实现客户端开屏页Splash/Welcome与首次协议同意逻辑
- 使用 `client/assets/images/index/` 的图片作为主视觉(例如 `index_flowers.png`
- 底部渐变按钮颜色:`#F69F7B → #F99CC0`
- 按钮下方提供「隐私协议」「用户使用协议」外部链接入口(先用占位链接)
- **仅首次出现按钮**:当 `consent.accepted=true` 后不再展示同意按钮与协议入口
- 同意后进入现有启动分发:`app/index.tsx`(继续走 Onboarding/App Shell
## 2. 默认技术决策
- **路由**:沿用 `expo-router`
- **持久化**:沿用 `AsyncStorage`
- **协议打开方式**:外部链接(`expo-web-browser``Linking.openURL`
- **渐变实现**:使用 `expo-linear-gradient`Expo 官方)
## 3. 路由与流程设计
### 3.1 新增路由
- `app/(splash)/splash.tsx`:开屏页(图片 + 首次同意按钮/协议入口)
- `app/(splash)/_layout.tsx`:可选(一般无需 header
### 3.2 启动分发逻辑(与现有流程集成)
当前已存在启动分发 `app/index.tsx`(根据 `onboarding.completed` 跳转)。
本需求将启动分发扩展为两段判断:
1.`consent.accepted !== true`:跳转到 `/(splash)/splash`
2.`consent.accepted === true`:沿用原逻辑(进入 Onboarding 或 `/(app)/home`
> 注意:你确认的是“隐藏按钮而不是隐藏页面”。因此当 `consent.accepted=true` 时,开屏页不需要停留;可直接跳过该页进入原启动分发。
## 4. 数据与持久化
### 4.1 Key
- `consent.accepted`: boolean
### 4.2 存储封装
在现有 `src/storage/appStorage.ts` 中补充:
- `getConsentAccepted(): Promise<boolean>`
- `setConsentAccepted(accepted: boolean): Promise<void>`
并确保所有 UI 不直接硬编码 key。
## 5. UI 细节与交互
### 5.1 开屏布局(建议)
- 主视觉图片:
- 使用 `index_flowers.png`
- 适配:`resizeMode="contain"` 或按实际视觉调整(不裁切关键内容)
- 底部区域(贴近安全区):
- 渐变按钮(圆角、阴影/投影轻微)
- 协议入口(两段文字按钮,左右排列或一行内间隔)
### 5.2 渐变按钮(仅首次出现)
- 展示条件:`consent.accepted !== true`
- 点击行为:
- 写入 `consent.accepted=true`
- 跳转到 `/`(触发 `app/index.tsx` 继续分发)
### 5.3 协议入口(外部链接,先占位)
- 「隐私协议」:`https://example.com/privacy`
- 「用户使用协议」:`https://example.com/terms`
打开方式:
- 优先:`expo-web-browser` 打开外部链接
- 兜底:`Linking.openURL`
## 6. i18n 文案
新增 key至少 `zh-CN/en`,其余语言可逐步补齐):
- `consent.title`(可选)
- `consent.agree`(按钮文案:同意并继续)
- `consent.privacy`(隐私协议)
- `consent.terms`(用户使用协议)
## 7. 实施步骤
1. 新增 `consent.accepted` 的存储读写方法
2. 新增 `/(splash)/splash` 页面(图片 + 渐变按钮 + 协议入口)
3. 修改 `app/index.tsx`:在 onboarding 分发前插入 consent gate
4. 接入 `expo-linear-gradient` 并完成按钮 UI
5. 接入外部链接打开(先占位 URL
6. 补齐 i18n key至少 zh-CN/en
7. 自测:
- 首次启动:显示按钮与入口;同意后进入 Onboarding/App
- 再次启动:不再出现同意按钮,直接进入后续流程
## 8. 验收标准
- 首次启动:
- 展示开屏图片
- 渐变按钮颜色正确(`#F69F7B → #F99CC0`
- 协议入口可点击并打开外部链接
- 点击同意后进入后续流程
- 非首次启动:
- 不再出现同意按钮与协议入口
- 直接进入后续流程Onboarding/App Shell

View File

@@ -0,0 +1,103 @@
# Splash Consent高层规范
## 1. 背景与目标
客户端需要一个开屏页Splash/Welcome用于展示品牌视觉并在**首次加载**时让用户同意隐私协议与用户使用协议。用户同意后进入后续流程Onboarding/App
### 目标
- **视觉开屏**:使用 `client/assets/images/index/` 下的图片资源作为主视觉
- **首次同意**:底部渐变按钮仅在首次加载时出现,用于“同意并继续”
- **协议入口**:按钮下方提供「隐私协议」「用户使用协议」入口,用户可点击查看
- **状态持久化**:同意后不再显示该按钮(后续启动直接进入后续流程)
### 非目标(本阶段不做)
- 不实现复杂的协议版本管理(如协议更新后重新弹出),后续可扩展
- 不实现后端拉取协议内容(可先使用本地静态页面或外链)
## 2. 范围与交付物
### 范围
- 新增开屏页 UI 与路由接入
- 新增“首次同意”状态的本地持久化
- 协议入口(跳转到 WebView/外部浏览器/内置页面,方案在实现阶段确定)
### 交付物
- 开屏页(含图片、按钮、协议入口)
- 本地持久化 key 约定与读写逻辑
- README/文档更新(可选)
## 3. 视觉与交互需求
### 3.1 主视觉
- 使用 `client/assets/images/index/` 下的图片资源(例如 `index_flowers.png`
- 布局建议:
- 图片居中或铺满上半部分(按实际设计微调)
- 适配不同屏幕尺寸,确保不变形/不裁切关键内容
### 3.2 底部渐变按钮(仅首次出现)
- **出现条件**:仅在首次加载且未同意协议时显示
- **按钮文案**:建议“同意并继续”(文案可后续调整,需支持 i18n
- **按钮渐变色**:从 `#F69F7B``#F99CC0`
- **点击行为**
- 写入本地状态:用户已同意
- 跳转到后续流程入口(例如 Onboarding 或 Home 的启动分发)
### 3.3 协议入口
- 位于按钮下方,展示:
- **隐私协议**
- **用户使用协议**
- 点击行为:打开对应协议内容
- 支持外链或内置页面(实现阶段确认)
## 4. 首次加载判定与持久化
### 4.1 判定规则
- **首次加载定义**App 首次启动且用户未完成协议同意
### 4.2 存储 Key建议
- `consent.accepted`: boolean
> 说明:后续如需协议版本化,可扩展为 `consent.versionAccepted = <version>`。
## 5. 路由与流程(与现有客户端集成)
### 5.1 流程顺序(建议)
1. App 启动
2.`consent.accepted !== true`:进入开屏页(展示按钮与协议入口)
3. 用户点击“同意并继续”后:
- 写入 `consent.accepted=true`
- 进入后续流程(例如 Onboarding/App Shell 的启动分发)
### 5.2 与现有功能的关系
- 开屏页位于 Onboarding 之前(更符合“先同意协议再使用”的要求)
- 同意后按既有规则继续:
- 已完成 onboarding进入 Home
- 未完成 onboarding进入 Onboarding
## 6. 验收标准
- 首次启动:
- 出现开屏页,展示图片
- 底部渐变按钮可见,颜色正确(`#F69F7B``#F99CC0`
- “隐私协议/用户使用协议”可点击打开
- 点击“同意并继续”后进入后续流程
- 非首次启动:
- 不再出现“同意按钮”(且开屏页按产品要求可直接跳过或不再展示)
- 直接进入后续流程
## 7. 风险与注意事项
- 协议内容来源若为外链,需要保证链接长期可用;若为本地页面,需要考虑后续更新机制
- iOS 不同机型安全区(底部 Home Indicator需保证按钮与协议入口不被遮挡

View File

@@ -0,0 +1,109 @@
# Splash Consent任务清单
> 说明:本清单根据 `plan.md` 拆解,要求“可执行、可验收、可标记”。
> 标记规则:执行完成后将对应项从 `- [ ]` 改为 `- [x]`,并把“状态”改为 **已完成**;进行中改为 **进行中**;阻塞写明原因与解除条件。
## 0. UI 参考(本次实现对齐)
- **参考截图**:你提供的开屏页样式(花束插画 + 文案 + 圆角渐变按钮 + 协议入口)
- **图片资源**`client/assets/images/index/index_flowers.png`
- **按钮渐变色**`#F69F7B``#F99CC0`
- **交互约束**
- 仅首次(未同意)显示“同意按钮 + 协议入口”
- 同意后写入本地 `consent.accepted=true`,并进入既有启动分发 `app/index.tsx`
## 1. 依赖与基础设施
- [ ] **安装并接入渐变组件Expo 官方)**
- **状态**:未开始
- **命令**
```bash
cd client
pnpm add expo-linear-gradient
```
- **验收**:项目可正常启动,页面可渲染渐变按钮
- [ ] **确认外部链接打开方式expo-web-browser可用**
- **状态**:未开始
- **说明**:已存在依赖则跳过安装;否则安装 `expo-web-browser`(当前项目初始化模板通常已包含)
- **验收**:能通过代码打开 `https://example.com/*` 外链
## 2. 本地存储:协议同意状态
- [ ] **在 `src/storage/appStorage.ts` 增加 consent 读写方法**
- **状态**:未开始
- **Key**`consent.accepted`
- **新增 API建议**
- `getConsentAccepted(): Promise<boolean>`
- `setConsentAccepted(accepted: boolean): Promise<void>`
- **验收**:不在 UI 代码里硬编码 key重启后状态仍然保留
## 3. 路由与启动分发接入
- [ ] **新增开屏路由:`app/(splash)/splash.tsx`**
- **状态**:未开始
- **要求**
- 展示主视觉图片(`index_flowers.png`
- 底部区域包含:渐变按钮 + 协议入口(仅未同意时显示)
- **验收**:路由可访问并正常渲染 UI
- [ ] **修改 `app/index.tsx`:增加 consent gate**
- **状态**:未开始
- **规则**
-`consent.accepted !== true`:跳转到 `/(splash)/splash`
- 否则沿用原逻辑Onboarding / Home
- **验收**:首次启动先到开屏;同意后进入既有分发
## 4. 开屏页 UI 落地(对齐参考图)
- [ ] **布局与样式实现(适配安全区)**
- **状态**:未开始
- **布局建议(可微调)**
- 顶部:插画居中,`resizeMode="contain"`
- 中部:主文案(支持 i18n
- 底部:渐变按钮(圆角/阴影轻微)+ 协议入口(文本按钮)
- 注意底部 Home Indicator 安全区
- **验收**:不同屏幕尺寸下不遮挡、不溢出,整体视觉接近参考图
- [ ] **渐变按钮(仅首次出现)**
- **状态**:未开始
- **颜色**`#F69F7B``#F99CC0`
- **点击行为**
- `setConsentAccepted(true)`
- `router.replace('/')`(回到启动分发)
- **验收**:点击后按钮不再出现(重启仍不出现),并进入后续流程
- [ ] **协议入口(外部链接,占位 URL**
- **状态**:未开始
- **链接**
- 隐私协议:`https://example.com/privacy`
- 用户使用协议:`https://example.com/terms`
- **打开方式**:优先 `expo-web-browser`,兜底 `Linking.openURL`
- **验收**:点击可打开外链;失败时不崩溃(可 toast/alert 提示)
## 5. i18n 文案补齐
- [ ] **新增开屏相关 i18n key至少 zh-CN/en尽量补齐 5 语)**
- **状态**:未开始
- **建议 key**
- `consent.title`(可选)
- `consent.subtitle`(可选)
- `consent.agree`
- `consent.privacy`
- `consent.terms`
- **验收**:开屏页不出现硬编码长句;切换语言可生效
## 6. 自测与验收
- [ ] **首次启动流程自测**
- **状态**:未开始
- **路径**Splash显示按钮/协议入口)→ 同意 → 进入 `app/index.tsx` 分发Onboarding/Home
- **验收**:与 `spec.md` / `plan.md` 验收一致
- [ ] **非首次启动流程自测**
- **状态**:未开始
- **路径**:直接进入 `app/index.tsx` 分发(不再出现同意按钮与协议入口)
- **验收**:同意状态持久化生效

View File

@@ -65,6 +65,7 @@ npx expo prebuild -p ios
- **排查**
- 桌面搜索不到:通常是没有 Run 安装过主 App或 Widget target 未加入编译
- 点击不跳转:确认 Widget 里 `widgetURL``client:///(app)/home`,且 `app.json``scheme``client`
- 仍然搜不到:检查 Widget Extension 的 Bundle Identifier 是否有效(本仓库已修正为 `com.anonymous.client.emotionwidget`),然后 Clean + 重新安装 App
## 6. 文档补充(可选但建议)

View File

@@ -47,3 +47,31 @@
- `spec_kit/iOS Widget/spec.md`
- `spec_kit/iOS Widget/plan.md`
- `spec_kit/iOS Widget/tasks.md`
## Splash Consent
- **目标**:实现开屏页(使用 `client/assets/images/index/` 图片资源),首次加载展示渐变同意按钮并提供隐私/协议入口
- **核心范围**:开屏 UI、首次同意状态持久化`consent.accepted`)、点击同意后进入后续 Onboarding/App 流程
- **阶段产物**
- `spec_kit/Splash Consent/spec.md`
- `spec_kit/Splash Consent/plan.md`
- `spec_kit/Splash Consent/tasks.md`
## Card UI
- **目标**:优化卡片页 UI 与交互,支持右上角主题切换与个人主页弹窗,并将喜欢/讨厌改为 icon + 动效
- **核心范围**
- 系统导航栏右上角两个圆形 icon主题`theme.svg`/ 我的(`my.svg`
- 底部上拉弹窗Sheet统一内容区背景 `#FAF3EC`,点 X 关闭
- 主题两种模式:`scenery` / `color`(本期最少影响 Home 页面背景),并持久化 `ui.theme.mode`
- 喜欢/讨厌按钮:使用 `like.svg` / `hate.svg`,喜欢提供“心形填满”强反馈动效(`like_filled.svg`
- 每日提醒弹窗:次数 +/-、Push 提醒开关、渐变 Ok 按钮,并持久化设置
- **阶段产物**
- `spec_kit/Card UI/spec.md`
- `spec_kit/Card UI/plan.md`
- `spec_kit/Card UI/tasks.md`
- **已完成编码(阶段性)**
- 已接入 `react-native-svg-transformer` + `client/metro.config.js`
- 新增 `SheetModal``ThemeModal``ProfileModal` 并在 Home 中接入
- 新增 `DailyReminderModal` 并从个人主页弹窗打开
- Home右上角 icon 按钮、主题切换背景、喜欢/讨厌 icon + 动效