170 lines
6.6 KiB
Markdown
170 lines
6.6 KiB
Markdown
# 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,若项目未引入则先不加)把“打开弹窗”动作传给页面
|
||
|
||
本计划默认采用 **A(Home 页自绘顶部栏)**,因为实现最直接、样式可控、无跨层状态传递。
|
||
|
||
### 4.2 主题弹窗(背景色 #FAF3EC)
|
||
|
||
落点建议:
|
||
|
||
- `client/components/home/ThemeModal.tsx`
|
||
- `client/components/ui/SheetModal.tsx`(复用:遮罩、圆角容器、上滑/淡入动效、关闭逻辑)
|
||
|
||
交互:
|
||
|
||
- 点击主题按钮 → 打开弹窗
|
||
- 选择「风景 / 颜色」→ 立即写入主题 mode + 关闭(或保持打开并高亮选中,按设计稿)
|
||
- 点击遮罩 / 关闭按钮 → 关闭
|
||
|
||
视觉:
|
||
|
||
- 弹窗内容区背景:`#FAF3EC`
|
||
- 容器圆角与内边距按设计稿(默认 24 圆角、16–20 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 后仍保持上次主题
|
||
|