/** * 卡路里计算工具 * 基于标准 MET (Metabolic Equivalent of Task) 公式 */ export interface FoodItem { name: string emoji: string calories: number unit: string } export interface FoodComparison { food: FoodItem ratio: number message: string } export interface CalorieCalculationConfig { totalReps: number bpm: number userWeight?: number // 默认 60kg } // 食物卡路里数据库 export const FOOD_CALORIES: Record = { apple: { name: '苹果', emoji: '🍎', calories: 52, unit: '个(100g)' }, egg: { name: '鸡蛋', emoji: '🥚', calories: 70, unit: '个' }, chocolate: { name: '巧克力', emoji: '🍫', calories: 54, unit: '块(10g)' }, banana: { name: '香蕉', emoji: '🍌', calories: 89, unit: '根' }, rice: { name: '米饭', emoji: '🍚', calories: 116, unit: '碗(100g)' }, } /** * 计算卡路里消耗 * 公式:卡路里 = MET × 体重(kg) × 时长(小时) * * @param config 计算配置 * @returns 卡路里消耗(kcal,保留一位小数) */ export function calculateCalories(config: CalorieCalculationConfig): number { const MET = 3.5 // 握力环训练的标准 MET 值 const weight = config.userWeight || 60 // 默认 60kg // 边界情况:无效输入 if (config.totalReps <= 0 || config.bpm <= 0) { return 0 } const secondsPerRep = 60 / config.bpm const activeTimeHours = (config.totalReps * secondsPerRep) / 3600 const calories = MET * weight * activeTimeHours return Math.round(calories * 10) / 10 // 保留一位小数 } /** * 自动匹配最接近的食物对比 * * @param burnedCalories 消耗的卡路里 * @returns 食物对比信息 */ export function getFoodComparison(burnedCalories: number): FoodComparison { const foods = Object.values(FOOD_CALORIES) // 边界情况:食物数据库为空 if (foods.length === 0) { throw new Error('食物数据库为空') } // 边界情况:卡路里为 0 或负数 if (burnedCalories <= 0) { return { food: foods[0], ratio: 0, message: `暂无消耗`, } } // 找到卡路里最接近的食物 const closest = foods.reduce((prev, curr) => { const prevDiff = Math.abs(prev.calories - burnedCalories) const currDiff = Math.abs(curr.calories - burnedCalories) return currDiff < prevDiff ? curr : prev }) const ratio = burnedCalories / closest.calories const ratioRounded = Math.round(ratio * 100) / 100 // 保留两位小数 // 格式化显示:ratio < 1 时显示分数形式更直观 let displayText: string if (ratioRounded < 1) { displayText = `相当于 ${ratioRounded.toFixed(2)} 个${closest.name} ${closest.emoji}` } else { displayText = `相当于 ${ratioRounded.toFixed(2)} ${closest.unit}${closest.name} ${closest.emoji}` } return { food: closest, ratio: ratioRounded, message: displayText, } } /** * 格式化时长(秒 → 分:秒) * * @param seconds 秒数 * @returns 格式化字符串(如 "3:45") */ export function formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins}:${secs.toString().padStart(2, '0')}` }