6.6 KiB
6.6 KiB
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),所有状态都留在页面内
- 更推荐:把 header 做成 Home 页内部自绘顶部栏(
- B:继续用导航 header,但通过
React.Context或一个简易 store(如 Zustand,若项目未引入则先不加)把“打开弹窗”动作传给页面
本计划默认采用 A(Home 页自绘顶部栏),因为实现最直接、样式可控、无跨层状态传递。
4.2 主题弹窗(背景色 #FAF3EC)
落点建议:
client/components/home/ThemeModal.tsxclient/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.92pressOut:回弹到 1onPress:短促 “弹一下” 的强调动效(例如 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 支持:
- 安装依赖:
react-native-svg-transformer - 新增
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.colorprofile.title、profile.favorites、profile.widget、profile.dailyReminder、profile.privacy、profile.terms、profile.language
原有
home.like/home.dislike即使不再显示文字,也可保留用于accessibilityLabel或未来回退。
8. 实施步骤
- 配置 SVG 导入(新增 transformer +
metro.config.js) - 新增通用弹窗组件
SheetModal(遮罩、圆角、背景色#FAF3EC、关闭交互、基础动效) - 新增
ThemeModal与ProfileModal(复用SheetModal) - 在
appStorage.ts新增主题 mode 读写方法 - 改造
home.tsx:- 自绘顶部栏(右上角两个圆形 icon 按钮)
- 接入主题 mode 影响背景
- 底部喜欢/讨厌替换为 icon + 动效
- 补齐 i18n key
- 自测:
- 主题切换后即时生效并可持久化
- 个人主页弹窗打开/关闭体验一致
- 喜欢/讨厌点击有动效且逻辑正常推进卡片
9. 验收标准
- 卡片页右上角两个 icon 按钮显示正确,点击热区合理
- 主题弹窗与个人主页弹窗:
- 背景色
#FAF3EC - 遮罩/圆角/关闭交互一致
- 背景色
- 喜欢/讨厌按钮为 icon(
like.svg/hate.svg)且点击有动效 - 主题选择被持久化,重启 App 后仍保持上次主题