import { ROUTES } from '@/constants/Routes'; import { useAppSelector } from '@/hooks/redux'; import { selectUserAge, selectUserProfile } from '@/store/userSlice'; import { fetchBasalEnergyBurned } from '@/utils/health'; import dayjs from 'dayjs'; import { Image } from 'expo-image'; import { router } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; interface BasalMetabolismCardProps { selectedDate?: Date; style?: any; } export function BasalMetabolismCard({ selectedDate, style }: BasalMetabolismCardProps) { const [basalMetabolism, setBasalMetabolism] = useState(null); const [loading, setLoading] = useState(false); const [modalVisible, setModalVisible] = useState(false); // 获取用户基本信息 const userProfile = useAppSelector(selectUserProfile); const userAge = useAppSelector(selectUserAge); // 缓存和防抖相关 const cacheRef = useRef>(new Map()); const loadingRef = useRef>>(new Map()); const CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存 // 使用 useMemo 缓存 BMR 计算,避免每次渲染重复计算 const bmrRange = useMemo(() => { const { gender, weight, height } = userProfile; // 检查是否有足够的信息来计算BMR if (!gender || !weight || !height || !userAge) { return null; } // 将体重和身高转换为数字 const weightNum = parseFloat(weight); const heightNum = parseFloat(height); if (isNaN(weightNum) || isNaN(heightNum) || weightNum <= 0 || heightNum <= 0 || userAge <= 0) { return null; } // 使用Mifflin-St Jeor公式计算BMR let bmr: number; if (gender === 'male') { bmr = 10 * weightNum + 6.25 * heightNum - 5 * userAge + 5; } else { bmr = 10 * weightNum + 6.25 * heightNum - 5 * userAge - 161; } // 计算正常范围(±15%) const minBMR = Math.round(bmr * 0.85); const maxBMR = Math.round(bmr * 1.15); return { min: minBMR, max: maxBMR, base: Math.round(bmr) }; }, [userProfile.gender, userProfile.weight, userProfile.height, userAge]); // 优化的数据获取函数,包含缓存和去重复请求 const fetchBasalMetabolismData = useCallback(async (date: Date): Promise => { const dateKey = dayjs(date).format('YYYY-MM-DD'); const now = Date.now(); // 检查缓存 const cached = cacheRef.current.get(dateKey); if (cached && (now - cached.timestamp) < CACHE_DURATION) { return cached.data; } // 检查是否已经在请求中(防止重复请求) const existingRequest = loadingRef.current.get(dateKey); if (existingRequest) { return existingRequest; } // 创建新的请求 const request = (async () => { try { const options = { startDate: dayjs(date).startOf('day').toDate().toISOString(), endDate: dayjs(date).endOf('day').toDate().toISOString() }; const basalEnergy = await fetchBasalEnergyBurned(options); const result = basalEnergy || null; // 更新缓存 cacheRef.current.set(dateKey, { data: result, timestamp: now }); return result; } catch (error) { console.error('BasalMetabolismCard: 获取基础代谢数据失败:', error); return null; } finally { // 清理请求记录 loadingRef.current.delete(dateKey); } })(); // 记录请求 loadingRef.current.set(dateKey, request); return request; }, []); // 获取基础代谢数据 useEffect(() => { if (!selectedDate) return; let isCancelled = false; const loadData = async () => { setLoading(true); try { const result = await fetchBasalMetabolismData(selectedDate); if (!isCancelled) { setBasalMetabolism(result); } } finally { if (!isCancelled) { setLoading(false); } } }; loadData(); // 清理函数,防止组件卸载后的状态更新 return () => { isCancelled = true; }; }, [selectedDate, fetchBasalMetabolismData]); // 使用 useMemo 优化状态描述计算 const status = useMemo(() => { if (basalMetabolism === null || basalMetabolism === 0) { return { text: '未知', color: '#9AA3AE' }; } // 基于常见的基础代谢范围来判断状态 if (basalMetabolism >= 1800) { return { text: '高代谢', color: '#10B981' }; } else if (basalMetabolism >= 1400) { return { text: '正常', color: '#3B82F6' }; } else if (basalMetabolism >= 1000) { return { text: '偏低', color: '#F59E0B' }; } else { return { text: '较低', color: '#EF4444' }; } }, [basalMetabolism]); return ( <> setModalVisible(true)} activeOpacity={0.8} > {/* 头部区域 */} 基础代谢 {status.text} {/* 数值显示区域 */} {loading ? '加载中...' : (basalMetabolism != null && basalMetabolism > 0 ? Math.round(basalMetabolism).toString() : '--')} 千卡/日 {/* 基础代谢详情弹窗 */} setModalVisible(false)} > {/* 关闭按钮 */} setModalVisible(false)} > × {/* 标题 */} 基础代谢 {/* 基础代谢定义 */} 基础代谢,也称基础代谢率(BMR),是指人体在完全静息状态下维持基本生命功能(心跳、呼吸、体温调节等)所需的最低能量消耗,通常以卡路里为单位。 {/* 为什么重要 */} 为什么重要? 基础代谢占总能量消耗的60-75%,是能量平衡的基础。了解您的基础代谢有助于制定科学的营养计划、优化体重管理策略,以及评估代谢健康状态。 {/* 正常范围 */} 正常范围 - 男性:BMR = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄 + 5 - 女性:BMR = 10 × 体重(kg) + 6.25 × 身高(cm) - 5 × 年龄 - 161 {bmrRange ? ( <> 您的正常区间:{bmrRange.min}-{bmrRange.max}千卡/天 (在公式基础计算值上下浮动15%都属于正常范围) 基于您的信息:{userProfile.gender === 'male' ? '男性' : '女性'},{userAge}岁,{userProfile.height}cm,{userProfile.weight}kg ) : ( <> 请完善基本信息以计算您的代谢率 { setModalVisible(false); router.push(ROUTES.PROFILE_EDIT); }} > 前往完善资料 )} {/* 提高代谢率的策略 */} 提高代谢率的策略 科学研究支持以下方法: 1.增加肌肉量 (每周2-3次力量训练) 2.高强度间歇训练 (HIIT) 3.充分蛋白质摄入 (体重每公斤1.6-2.2g) 4.保证充足睡眠 (7-9小时/晚) 5.避免过度热量限制 (不低于BMR的80%) ); } const styles = StyleSheet.create({ container: { backgroundColor: '#FFFFFF', borderRadius: 20, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 4, }, shadowOpacity: 0.08, shadowRadius: 12, elevation: 4, position: 'relative', overflow: 'hidden', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, zIndex: 1, }, leftSection: { flexDirection: 'row', alignItems: 'center', }, iconContainer: { width: 32, height: 32, borderRadius: 10, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginRight: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 1, }, shadowOpacity: 0.1, shadowRadius: 2, elevation: 1, }, fireIcon: { width: 14, height: 18, backgroundColor: '#EF4444', borderTopLeftRadius: 7, borderTopRightRadius: 7, borderBottomLeftRadius: 2, borderBottomRightRadius: 2, }, title: { fontSize: 14, color: '#0F172A', fontWeight: '600', }, titleIcon: { width: 16, height: 16, marginRight: 4, }, statusBadge: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, }, statusText: { fontSize: 11, fontWeight: '600', }, valueSection: { flexDirection: 'row', alignItems: 'center', zIndex: 1, }, value: { fontSize: 16, fontWeight: '600', color: '#0F172A', lineHeight: 28, }, unit: { fontSize: 12, color: '#64748B', marginLeft: 6, }, // Modal styles modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'flex-end', }, modalContent: { backgroundColor: '#FFFFFF', borderTopLeftRadius: 20, borderTopRightRadius: 20, padding: 24, maxHeight: '90%', width: '100%', shadowColor: '#000', shadowOffset: { width: 0, height: -5, }, shadowOpacity: 0.25, shadowRadius: 20, elevation: 10, }, closeButton: { position: 'absolute', top: 16, right: 16, width: 32, height: 32, borderRadius: 16, backgroundColor: '#F1F5F9', alignItems: 'center', justifyContent: 'center', zIndex: 1, }, closeButtonText: { fontSize: 20, color: '#64748B', fontWeight: '600', }, modalTitle: { fontSize: 24, fontWeight: '700', color: '#0F172A', marginBottom: 16, textAlign: 'center', }, modalDescription: { fontSize: 15, color: '#475569', lineHeight: 22, marginBottom: 24, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: '#0F172A', marginBottom: 12, marginTop: 8, }, sectionContent: { fontSize: 15, color: '#475569', lineHeight: 22, marginBottom: 20, }, formulaText: { fontSize: 14, color: '#64748B', fontFamily: 'monospace', marginBottom: 4, paddingLeft: 8, }, rangeText: { fontSize: 16, fontWeight: '600', color: '#059669', marginTop: 12, marginBottom: 4, textAlign: 'center', }, rangeNote: { fontSize: 12, color: '#9CA3AF', textAlign: 'center', marginBottom: 20, }, userInfoText: { fontSize: 13, color: '#6B7280', textAlign: 'center', marginTop: 8, marginBottom: 16, fontStyle: 'italic', }, strategyText: { fontSize: 15, color: '#475569', marginBottom: 12, }, strategyList: { marginBottom: 20, }, strategyItem: { fontSize: 14, color: '#64748B', lineHeight: 20, marginBottom: 8, paddingLeft: 8, }, completeInfoButton: { backgroundColor: '#7a5af8', borderRadius: 12, paddingVertical: 12, paddingHorizontal: 24, marginTop: 16, alignItems: 'center', alignSelf: 'center', }, completeInfoButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, });