feat: 集成 Redux 状态管理和用户目标管理功能
- 添加 Redux 状态管理,支持用户登录和个人信息的持久化 - 新增目标管理页面,允许用户设置每日卡路里和步数目标 - 更新首页,移除旧的活动展示,改为固定的热点功能卡片 - 修改布局以适应新功能的展示和交互 - 更新依赖,添加 @reduxjs/toolkit 和 react-redux 库以支持状态管理 - 新增 API 服务模块,处理与后端的交互
This commit is contained in:
88
services/api.ts
Normal file
88
services/api.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { buildApiUrl } from '@/constants/Api';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
||||
|
||||
let inMemoryToken: string | null = null;
|
||||
|
||||
export async function setAuthToken(token: string | null): Promise<void> {
|
||||
inMemoryToken = token;
|
||||
}
|
||||
|
||||
export function getAuthToken(): string | null {
|
||||
return inMemoryToken;
|
||||
}
|
||||
|
||||
export type ApiRequestOptions = {
|
||||
method?: HttpMethod;
|
||||
headers?: Record<string, string>;
|
||||
body?: any;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type ApiResponse<T> = {
|
||||
data: T;
|
||||
};
|
||||
|
||||
async function doFetch<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {
|
||||
const url = buildApiUrl(path);
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
};
|
||||
|
||||
const token = getAuthToken();
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: options.method ?? 'GET',
|
||||
headers,
|
||||
body: options.body != null ? JSON.stringify(options.body) : undefined,
|
||||
signal: options.signal,
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
let json: any = null;
|
||||
try {
|
||||
json = text ? JSON.parse(text) : null;
|
||||
} catch {
|
||||
// 非 JSON 响应
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorMessage = (json && (json.message || json.error)) || `HTTP ${response.status}`;
|
||||
const error = new Error(errorMessage);
|
||||
// @ts-expect-error augment
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 支持后端返回 { data: ... } 或直接返回对象
|
||||
return (json && (json.data ?? json)) as T;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
get: <T>(path: string, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'GET' }),
|
||||
post: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'POST', body }),
|
||||
put: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'PUT', body }),
|
||||
patch: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'PATCH', body }),
|
||||
delete: <T>(path: string, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'DELETE' }),
|
||||
};
|
||||
|
||||
export const STORAGE_KEYS = {
|
||||
authToken: '@auth_token',
|
||||
userProfile: '@user_profile',
|
||||
} as const;
|
||||
|
||||
export async function loadPersistedToken(): Promise<string | null> {
|
||||
try {
|
||||
const t = await AsyncStorage.getItem(STORAGE_KEYS.authToken);
|
||||
return t || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user