import { Colors } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; import { fetchWeightHistory } from '@/store/userSlice'; import { BMI_CATEGORIES } from '@/utils/bmi'; import { Ionicons } from '@expo/vector-icons'; import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useState } from 'react'; import { Dimensions, Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import Svg, { Circle, Path } from 'react-native-svg'; const { width: screenWidth } = Dimensions.get('window'); const CARD_WIDTH = screenWidth - 40; // 减去左右边距 const CHART_WIDTH = CARD_WIDTH - 36; // 减去卡片内边距 const CHART_HEIGHT = 60; const PADDING = 10; export function WeightHistoryCard() { const dispatch = useAppDispatch(); const userProfile = useAppSelector((s) => s.user.profile); const weightHistory = useAppSelector((s) => s.user.weightHistory); const [showBMIModal, setShowBMIModal] = useState(false); const { pushIfAuthedElseLogin, isLoggedIn } = useAuthGuard(); const colorScheme = useColorScheme(); const themeColors = Colors[colorScheme ?? 'light']; const hasWeight = userProfile?.weight && parseFloat(userProfile.weight) > 0; useEffect(() => { if (isLoggedIn) { loadWeightHistory(); } }, [userProfile?.weight, isLoggedIn]); const loadWeightHistory = async () => { try { await dispatch(fetchWeightHistory() as any).unwrap(); } catch (error) { console.error('加载体重历史失败:', error); } }; const navigateToCoach = () => { pushIfAuthedElseLogin(ROUTES.WEIGHT_RECORDS); }; const handleHideBMIModal = () => { setShowBMIModal(false); }; const navigateToWeightRecords = () => { pushIfAuthedElseLogin(ROUTES.WEIGHT_RECORDS); }; // 处理体重历史数据 const sortedHistory = [...weightHistory] .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()) .slice(-7); // 只显示最近7条记录 // return ( // // // 体重记录 // // // // 暂无体重记录,点击下方按钮开始记录 // // { // e.stopPropagation(); // navigateToCoach(); // }} // activeOpacity={0.8} // > // // 记录体重 // // // // ); // } // 生成图表数据 const weights = sortedHistory.map(item => parseFloat(item.weight)); const minWeight = Math.min(...weights); const maxWeight = Math.max(...weights); const weightRange = maxWeight - minWeight || 1; const points = sortedHistory.map((item, index) => { const x = PADDING + (index / Math.max(sortedHistory.length - 1, 1)) * (CHART_WIDTH - 2 * PADDING); const normalizedWeight = (parseFloat(item.weight) - minWeight) / weightRange; // 减少顶部边距,压缩留白 const y = PADDING + 8 + (1 - normalizedWeight) * (CHART_HEIGHT - 2 * PADDING - 16); return { x, y, weight: item.weight, date: item.createdAt }; }); // 生成路径 const pathData = points.map((point, index) => { if (index === 0) return `M ${point.x} ${point.y}`; return `L ${point.x} ${point.y}`; }).join(' '); // 如果只有一个数据点,显示为水平线 const singlePointPath = points.length === 1 ? `M ${PADDING} ${points[0].y} L ${CHART_WIDTH - PADDING} ${points[0].y}` : pathData; return ( 体重记录 { e.stopPropagation(); navigateToCoach(); }} activeOpacity={0.8} > {/* 默认显示图表 */} {sortedHistory.length > 0 && ( {/* 背景网格线 */} {/* 更抽象的折线 - 减小线宽和显示的细节 */} {/* 简化的数据点 - 更小更精致 */} {points.map((point, index) => { const isLastPoint = index === points.length - 1; return ( ); })} {/* 精简的图表信息 */} {userProfile.weight}kg {sortedHistory.length}天 {minWeight.toFixed(1)}-{maxWeight.toFixed(1)}kg )} {/* BMI 信息弹窗 */} {/* 标题 */} BMI 指数说明 {/* 介绍部分 */} BMI(身体质量指数)是评估体重与身高关系的国际通用健康指标 计算公式:体重(kg) ÷ 身高²(m) {/* BMI 分类标准 */} BMI 分类标准 {BMI_CATEGORIES.map((category, index) => { const colors = [ { bg: '#FEF3C7', text: '#B45309', border: '#F59E0B' }, // 偏瘦 { bg: '#E8F5E8', text: Colors.light.accentGreen, border: Colors.light.accentGreen }, // 正常 { bg: '#FEF3C7', text: '#B45309', border: '#F59E0B' }, // 超重 { bg: '#FEE2E2', text: '#B91C1C', border: '#EF4444' } // 肥胖 ][index]; return ( {category.name} {category.range} {category.advice} ); })} {/* 健康建议 */} 健康建议 保持均衡饮食,控制热量摄入 每周至少150分钟中等强度运动 保证7-9小时充足睡眠 定期监测体重变化,及时调整 {/* 免责声明 */} BMI 仅供参考,不能反映肌肉量、骨密度等指标。如有健康疑问,请咨询专业医生。 {/* 底部继续按钮 */} 继续 ); } const styles = StyleSheet.create({ card: { backgroundColor: '#FFFFFF', borderRadius: 22, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 3, marginTop: 16 }, cardHeader: { flexDirection: 'row', alignItems: 'center', }, iconSquare: { width: 14, height: 14, borderRadius: 8, alignItems: 'center', justifyContent: 'center', marginRight: 4, }, cardTitle: { fontSize: 14, color: '#192126', flex: 1, fontWeight: '600' }, headerButtons: { flexDirection: 'row', alignItems: 'center', gap: 8, }, chartToggleButton: { width: 28, height: 28, borderRadius: 14, alignItems: 'center', justifyContent: 'center', }, addButton: { width: 28, height: 28, borderRadius: 14, alignItems: 'center', justifyContent: 'center', }, emptyContent: { alignItems: 'center', }, emptyTitle: { fontSize: 16, fontWeight: '700', color: '#192126', marginBottom: 6, }, emptyDescription: { fontSize: 14, color: '#687076', textAlign: 'center', marginBottom: 16, lineHeight: 20, }, recordButton: { flexDirection: 'row', alignItems: 'center', backgroundColor: Colors.light.accentGreen, paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, gap: 6, }, recordButtonText: { color: '#192126', fontSize: 14, fontWeight: '700', }, chartContainer: { width: '100%', alignItems: 'center', marginTop: 12, }, chartInfo: { flexDirection: 'row', justifyContent: 'space-around', width: '100%', }, infoItem: { alignItems: 'center', }, infoLabel: { fontSize: 11, color: '#687076', fontWeight: '500', }, infoValue: { fontSize: 14, fontWeight: '700', color: '#192126', }, // BMI 弹窗样式 bmiModalContainer: { flex: 1, }, bmiModalContent: { flex: 1, padding: 20, }, bmiModalTitle: { fontSize: 28, fontWeight: '800', color: '#111827', textAlign: 'center', marginBottom: 24, letterSpacing: -0.5, }, bmiModalIntroSection: { marginBottom: 32, }, bmiModalDescription: { fontSize: 16, color: '#374151', lineHeight: 24, textAlign: 'center', marginBottom: 16, }, bmiModalFormulaContainer: { backgroundColor: '#F3F4F6', borderRadius: 12, padding: 16, alignItems: 'center', }, bmiModalFormulaText: { fontSize: 14, fontWeight: '600', color: '#374151', }, bmiModalSectionTitle: { fontSize: 20, fontWeight: '700', color: '#111827', marginBottom: 16, letterSpacing: -0.5, }, bmiModalStatsCard: { marginBottom: 32, }, bmiModalStatItem: { borderRadius: 12, padding: 16, marginBottom: 12, borderWidth: 1, }, bmiModalStatHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8, }, bmiModalStatTitle: { fontSize: 16, fontWeight: '700', }, bmiModalStatRange: { fontSize: 14, fontWeight: '600', }, bmiModalStatAdvice: { fontSize: 14, lineHeight: 20, }, bmiModalHealthTips: { marginBottom: 32, }, bmiModalTipsItem: { flexDirection: 'row', alignItems: 'center', marginBottom: 16, paddingHorizontal: 16, paddingVertical: 12, backgroundColor: '#F8FAFC', borderRadius: 12, }, bmiModalTipsText: { fontSize: 14, color: '#374151', marginLeft: 12, flex: 1, lineHeight: 20, }, bmiModalDisclaimer: { flexDirection: 'row', alignItems: 'flex-start', backgroundColor: '#FEF3C7', borderRadius: 12, padding: 16, marginBottom: 20, }, bmiModalDisclaimerText: { fontSize: 13, color: '#B45309', marginLeft: 8, flex: 1, lineHeight: 18, }, bmiModalBottomContainer: { padding: 20, paddingBottom: 34, }, bmiModalContinueButton: { marginBottom: 8, }, bmiModalButtonBackground: { backgroundColor: '#192126', borderRadius: 16, paddingVertical: 16, alignItems: 'center', }, bmiModalButtonText: { fontSize: 16, fontWeight: '700', color: '#FFFFFF', }, bmiModalHomeIndicator: { height: 5, backgroundColor: '#D1D5DB', borderRadius: 3, alignSelf: 'center', width: 36, }, });