import React, { useState, useEffect, useMemo } from 'react'; import { View, ScrollView, TouchableOpacity, TextInput, Modal, Alert } from 'react-native'; import { useTheme } from '@/hooks/useTheme'; import { Screen } from '@/components/Screen'; import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; import { FontAwesome6 } from '@expo/vector-icons'; import { createStyles } from './styles'; const MOCK_USER_ID = 'mock-user-001'; interface WeightRecord { id: string; weight: number; note: string; recordedAt: string; } export default function StatsScreen() { const { theme, isDark } = useTheme(); const styles = useMemo(() => createStyles(theme), [theme]); const [weightRecords, setWeightRecords] = useState([]); const [modalVisible, setModalVisible] = useState(false); const [newWeight, setNewWeight] = useState(''); const [newNote, setNewNote] = useState(''); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); // 获取体重记录 const fetchWeightRecords = async () => { setLoading(true); try { /** * 服务端文件:server/src/routes/weight-records.ts * 接口:GET /api/v1/weight-records * Query 参数:userId: string */ const response = await fetch( `${process.env.EXPO_PUBLIC_BACKEND_BASE_URL}/api/v1/weight-records?userId=${MOCK_USER_ID}` ); const data = await response.json(); if (data.success) { setWeightRecords(data.data); } } catch (error) { console.error('Failed to fetch weight records:', error); } finally { setLoading(false); } }; useEffect(() => { fetchWeightRecords(); }, []); // 添加体重记录 const addWeightRecord = async () => { if (!newWeight) { Alert.alert('提示', '请输入体重'); return; } setSaving(true); try { const recordData = { userId: MOCK_USER_ID, weight: parseFloat(newWeight), note: newNote, recordedAt: new Date().toISOString(), }; /** * 服务端文件:server/src/routes/weight-records.ts * 接口:POST /api/v1/weight-records * Body 参数:userId: string, weight: number, note?: string, recordedAt: string */ const response = await fetch( `${process.env.EXPO_PUBLIC_BACKEND_BASE_URL}/api/v1/weight-records`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(recordData), } ); const data = await response.json(); if (data.success) { Alert.alert('成功', '记录已保存', [ { text: '确定', onPress: () => resetModal() }, ]); fetchWeightRecords(); } else { Alert.alert('失败', data.error || '保存失败,请重试'); } } catch (error) { console.error('Save error:', error); Alert.alert('错误', '保存失败,请检查网络连接'); } finally { setSaving(false); } }; const resetModal = () => { setModalVisible(false); setNewWeight(''); setNewNote(''); }; // 删除记录 const deleteRecord = async (id: string) => { Alert.alert('确认删除', '确定要删除这条记录吗?', [ { text: '取消', style: 'cancel' }, { text: '删除', style: 'destructive', onPress: async () => { try { /** * 服务端文件:server/src/routes/weight-records.ts * 接口:DELETE /api/v1/weight-records/:id * Path 参数:id: string */ const response = await fetch( `${process.env.EXPO_PUBLIC_BACKEND_BASE_URL}/api/v1/weight-records/${id}`, { method: 'DELETE', } ); const data = await response.json(); if (data.success) { fetchWeightRecords(); } } catch (error) { console.error('Delete error:', error); } }, }, ]); }; return ( 数据统计 setModalVisible(true)} > {/* 体重趋势 */} 体重记录 {weightRecords.length > 0 && ( {weightRecords.length} 条记录 )} {weightRecords.length === 0 ? ( 暂无记录,点击右上角添加 ) : ( {weightRecords.map((record, index) => ( {record.weight} kg {new Date(record.recordedAt).toLocaleDateString('zh-CN', { month: 'short', day: 'numeric', })} {record.note && ( {record.note} )} {index > 0 && ( {Math.abs(record.weight - weightRecords[index - 1].weight).toFixed(1)} )} deleteRecord(record.id)}> ))} )} {/* 统计信息 */} {weightRecords.length >= 2 && ( 统计信息 初始体重 {weightRecords[weightRecords.length - 1].weight} kg 当前体重 {weightRecords[0].weight} kg 累计变化 {weightRecords[0].weight < weightRecords[weightRecords.length - 1].weight ? '-' : '+'} {Math.abs( weightRecords[0].weight - weightRecords[weightRecords.length - 1].weight ).toFixed(1)}{' '} kg )} {/* 添加记录 Modal */} 添加体重记录 体重 (kg) 备注 (可选) 取消 {saving ? '保存中...' : '保存'} ); }