chore: vendor client into main repo

This commit is contained in:
吕新雨
2026-01-28 22:54:21 +08:00
parent 7d743e78ea
commit 6a598f0a98
79 changed files with 12952 additions and 1 deletions

343
client/README.md Normal file
View File

@@ -0,0 +1,343 @@
# 正念 APP客户端
本目录用于存放「正念 APP」客户端工程定位是面向宝妈人群的情绪价值与正念练习支持应用。
## 功能概览(一期)
- **APP-PUSH 推送**:定时推送情绪文字(固定时间 / 用户自定义时间),推送内容可由后端配置更新
- **iOS 小组件Widget**:展示当前情绪文字与背景图/渐变色,支持多尺寸与可配置刷新频率
- **情绪卡片滑动**:卡片列表左右滑动切换(类似 Tinder收藏/分享可作为后续迭代
## 技术栈
- **React Native + Expo**
- **TypeScript**
- **React Navigation**:页面路由
- **状态管理**Zustand或 Redux Toolkit
- **网络请求**Axios或 Fetch
- **推送**Expo Notifications
- **环境区分**Expo App Config + `.env.dev` / `.env.prod`
- **iOS 打包**EAS Build
## 开发环境要求
- **Node.js**:建议使用 **20+LTS**
- **包管理器**pnpm
- **Expo CLI**:推荐通过 `npx expo` 使用(避免全局版本漂移)
- **iOS 真机调试**macOS + Xcode如需
> 本项目已内置 `postinstall` 补丁脚本用于兼容(安装依赖后会自动修复),但仍建议升级到 Node 20+。
## 快速开始
> 说明:本仓库已包含 Expo 工程文件;如果你只是想把客户端跑起来,从「安装依赖」开始即可。
### 1初始化工程若尚未创建
`client/` 目录下创建 Expo 工程(示例):
```bash
cd client
npx create-expo-app@latest .
```
执行后按提示选择模板即可;如果你希望固定使用 TypeScript 模板,也可以在创建时选择对应模板(以实际 CLI 提示为准)。
### 2安装依赖
```bash
cd client
pnpm install
```
### 3配置环境变量dev / prod
按根目录约定,客户端使用 `.env.dev``.env.prod` 区分环境;`.env` 文件不提交到 Git仓库内应提供 `.env.example` 作为模板。
#### 本地开发如何注入环境变量(推荐)
Expo 在本地开发时会自动读取当前目录下的 `.env` / `.env.local`(仅以 `EXPO_PUBLIC_` 前缀变量注入到客户端运行时)。推荐做法:
```bash
cd client
cp .env.example .env.local
# 然后按需修改 .env.local
pnpm start
```
> 修改 `.env*` 后需要重启 `pnpm start` 才会生效。
#### 本地开发如何区分 dev / prod可选
如果你希望坚持使用 `.env.dev` / `.env.prod` 文件名可以在启动前导出环境变量zsh 示例):
```bash
cd client
set -a
source .env.dev
set +a
pnpm start
```
> 注意:`source` 方式要求 `.env.dev` 内容是合法的 shell 格式(例如 `KEY=value`),不要带 `export` 以外的复杂语法;值包含空格时需要加引号。
#### 必填环境变量
- **`EXPO_PUBLIC_API_BASE_URL`**:后端 API 基地址
- 本地:`http://localhost:8000`
- 真机联调:改为电脑局域网 IP例如 `http://192.168.1.10:8000`
- **`EXPO_PUBLIC_ENV`**:环境标识(建议 `dev` / `prod`
#### 可选环境变量
- **`EXPO_PUBLIC_DEFAULT_LANGUAGE`**:默认语言策略
- `auto`:优先设备语言(默认)
- `zh-CN/en/es/pt/zh-TW`:固定默认语言(仍允许用户在设置中切换并持久化)
建议字段(示例):
```env
EXPO_PUBLIC_API_BASE_URL=http://localhost:8000
EXPO_PUBLIC_ENV=dev
EXPO_PUBLIC_DEFAULT_LANGUAGE=auto
```
> 提示:在 Expo 中建议使用 `EXPO_PUBLIC_` 前缀,方便在运行时读取并支持 EAS 构建注入。
### 4启动开发服务器
```bash
cd client
pnpm start
```
启动后你会看到终端输出的二维码QR Code与可用命令提示。
### 5运行到 iOS / Android / 真机 / Web
- **iOS 模拟器macOS + Xcode**
- 方式一:在 `pnpm start` 的终端里按 `i`
- 方式二:直接运行:
```bash
cd client
pnpm ios
```
- **Android 模拟器Android Studio**
- 方式一:在 `pnpm start` 的终端里按 `a`
- 方式二:直接运行:
```bash
cd client
pnpm android
```
- **真机(推荐使用 Expo Go**
- 手机安装 **Expo Go**
- 确保手机与电脑在**同一局域网**
- 运行 `pnpm start` 后,用 Expo Go 扫描终端二维码即可打开 App
- **Web可选**
```bash
cd client
pnpm web
```
常用命令(脚本):
```bash
pnpm ios
pnpm android
pnpm web
```
### 6常见问题
- **真机打不开 / 扫码后卡住**
- 检查手机与电脑是否同一 Wi-Fi
- 尝试重启 `pnpm start -- --clear` 清缓存
- **启动时提示 `Networking has been disabled` / `Network connection is unreliable`**
- 这是 Expo CLI 的网络探测/请求失败导致的,按下面方式跳过网络请求即可:
```bash
cd client
EXPO_OFFLINE=1 pnpm start
```
- 如果你本机设置了代理(例如 `HTTP_PROXY/ALL_PROXY`),也可以临时关闭代理后再启动:
```bash
cd client
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY
pnpm start
```
- **Watchman 提示 Recrawl不影响运行但建议处理**
- 按提示执行一次即可清除警告:
```bash
watchman watch-del '/Users/jojo/Desktop/lxy/gitea/mindfulness/client' ; watchman watch-project '/Users/jojo/Desktop/lxy/gitea/mindfulness/client'
```
- **联调时请求打到 localhost**
- 真机上 `localhost` 指向手机自身,请把 `EXPO_PUBLIC_API_BASE_URL` 改成电脑的局域网 IP例如 `http://192.168.1.10:8000`),或使用内网穿透方案
## 与后端联调
- **后端基座**FastAPI详见 `server/README.md`
- **API Base URL**:通过 `EXPO_PUBLIC_API_BASE_URL` 配置
- **本地真机注意**:如果在手机上调试,请将 `localhost` 替换为电脑局域网 IP或使用内网穿透方案
## 多语言i18nCN / EN / ES / PT / TC
### 语言码约定
- **CN简体中文**`zh-CN`
- **EN英语**`en`
- **ES西班牙语**`es`
- **PT葡萄牙语**`pt`
- **TC繁体中文**`zh-TW`
### 推荐选型Expo/RN 常用组合)
- **文案管理**`i18next` + `react-i18next`
- **系统语言读取**`expo-localization`
### 建议目录结构
```text
src/
└── i18n/
├── index.ts # i18n 初始化(默认语言、回退语言、资源注册)
├── locales/
│ ├── zh-CN.json
│ ├── en.json
│ ├── es.json
│ ├── pt.json
│ └── zh-TW.json
└── types.ts #(可选)语言码与 key 的类型定义
```
### 约定与最佳实践
- **key 规则**:使用稳定的点分层 key例如 `common.ok``push.permissionTitle``cards.swipeHint`
- **默认与回退**:默认优先跟随用户当前设备语言(在支持列表内时生效);设备语言不支持时回退到 `zh-CN`(或团队指定默认)
- **插值与复数**:优先使用 i18next 的插值(例如 `{{name}}`),避免在代码里拼字符串
- **动态切换**:提供“语言设置”入口允许手动切换;切换后持久化(例如 AsyncStorage后续启动优先生效并立即刷新文案
- **语言优先级**:用户设置(若存在)> 设备语言(在支持列表内)> 默认回退(`zh-CN` 或团队指定默认)
- **设置入口建议**:设置页 -> 语言(或 设置页 -> 通用 -> 语言)
- **与服务端一致性**:若后端也需要多语言(推送文案等),建议统一语言码(`zh-CN/en/es/pt/zh-TW`)并在接口里明确 `lang`
## 推送Expo Notifications
客户端侧通常需要:
- **申请通知权限**
- **获取 Expo Push Token**
- **将 Token 上报后端**(用于定时推送任务)
后端侧通常需要:
- **保存用户 Push Token**
- **按策略触发推送**(固定时间 / 用户自定义时间)
- **支持后台配置推送文案并更新**
> 具体实现以客户端工程代码为准;当代码接入后,建议在 README 补充「Token 上报接口」与「字段定义」。
## iOS 小组件Widget
小组件通常需要:
- **数据源**:来自本地缓存或后端拉取(需要设计刷新策略与缓存)
- **展示内容**:当前情绪文字 + 背景图/渐变
- **尺寸**:小 / 中 / 大
- **刷新频率**:如每小时/每天iOS 对频率有系统限制,以实际效果为准)
> 若你计划使用 Expo 的相关能力,请在工程中明确选型与实现路径,并补充到本 README。
## EAS BuildiOS 打包)
建议按根目录约定区分 dev/prod
- **dev**`com.damer.mindfulness.dev`
- **prod**`com.damer.mindfulness`
常见流程(示例):
```bash
cd client
eas login
eas build --platform ios --profile development
```
> 实际 profile、证书、bundle id、环境变量注入方式以工程内 `eas.json` / app config 为准。
## 目录结构(建议)
当客户端工程落地后建议使用更标准、可扩展的目录结构Expo + TypeScript 常见组织方式):
```text
client/
├── app/ #推荐expo-router 路由目录(若使用 expo-router
│ ├── (tabs)/ # Tabs 分组(可选)
│ ├── _layout.tsx # 根布局
│ └── index.tsx # 首页
├── src/ # 业务源码(与路由/平台代码解耦)
│ ├── components/ # 通用 UI 组件
│ ├── features/ # 按功能域拆分(推荐)
│ │ ├── push/ # 推送相关权限、token、上报等
│ │ ├── widget/ # 小组件数据与样式相关
│ │ └── cards/ # 情绪卡片滑动相关
│ ├── hooks/ # 自定义 hooks
│ ├── navigation/ #(若不用 expo-routerReact Navigation 配置
│ ├── screens/ #(若不用 expo-router页面
│ ├── services/ # API 封装request、接口定义
│ ├── store/ # 状态管理Zustand/RTK
│ ├── utils/ # 工具函数(时间、格式化、校验等)
│ ├── constants/ # 常量与配置(主题、枚举等)
│ └── types/ # 全局类型声明
├── assets/ # 静态资源(图片/字体/音频等)
├── app.json / app.config.ts # Expo 配置(环境区分可在此处理)
├── eas.json # EAS Build 配置(如使用)
├── package.json
└── README.md
```
> 说明:如果你不使用 `expo-router`,可以删除 `app/`,并以 `src/navigation/` + `src/screens/` 作为主路由结构;其余目录保持不变即可。
## iOS 小组件开发V1写死文案
> 重要:**Expo Go 不支持 WidgetKit**。要做真正的小组件,必须预构建 iOS 工程并用 Xcode 跑。
### 1生成 iOS 工程
```bash
cd client
npx expo prebuild -p ios --no-install
```
生成后会得到 `client/ios/`Xcode 工程文件都在里面)。
### 2在 Xcode 创建 Widget Extension
- 用 Xcode 打开:`client/ios/client.xcworkspace`
- 菜单:`File -> New -> Target... -> Widget Extension`
- Target 名称示例:`MindfulnessWidget`
### 3写死文案与三尺寸布局
仓库里已提供 SwiftUI 代码骨架(写死文案 + Small/Medium/Large + 点击跳转):
- `client/ios/情绪小组件/EmotionWidget.swift`
创建 Widget target 后,把该文件加入到 Widget target 中即可Target Membership 勾选:`情绪小组件`)。
### 4点击跳转到 HomeDeep Link
Widget 点击跳转使用:
- `client:///(app)/home`
代码里已通过 `widgetURL` 设置(见 `MindfulnessWidget.swift`)。