feat: 新增营养摄入分析卡片并优化相关页面
- 在统计页面中引入营养摄入分析卡片,展示用户的营养数据 - 更新探索页面,增加营养数据加载逻辑,确保用户体验一致性 - 移除不再使用的推荐文章逻辑,简化代码结构 - 更新路由常量,确保路径管理集中化 - 优化体重历史记录卡片,提升用户交互体验
This commit is contained in:
115
services/dietRecords.ts
Normal file
115
services/dietRecords.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { api } from '@/services/api';
|
||||
|
||||
export type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack' | 'other';
|
||||
export type RecordSource = 'manual' | 'vision' | 'other';
|
||||
|
||||
export type DietRecord = {
|
||||
id: number;
|
||||
mealType: MealType;
|
||||
foodName: string;
|
||||
foodDescription?: string;
|
||||
weightGrams?: number;
|
||||
portionDescription?: string;
|
||||
estimatedCalories?: number;
|
||||
proteinGrams?: number;
|
||||
carbohydrateGrams?: number;
|
||||
fatGrams?: number;
|
||||
fiberGrams?: number;
|
||||
sugarGrams?: number;
|
||||
sodiumMg?: number;
|
||||
additionalNutrition?: any;
|
||||
source: RecordSource;
|
||||
mealTime?: string;
|
||||
imageUrl?: string;
|
||||
notes?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type NutritionSummary = {
|
||||
totalCalories: number;
|
||||
totalProtein: number;
|
||||
totalCarbohydrate: number;
|
||||
totalFat: number;
|
||||
totalFiber: number;
|
||||
totalSugar: number;
|
||||
totalSodium: number;
|
||||
};
|
||||
|
||||
export async function getDietRecords({
|
||||
startDate,
|
||||
endDate,
|
||||
}: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}): Promise<{
|
||||
records: DietRecord[]
|
||||
total: number
|
||||
page: number
|
||||
limit: number
|
||||
}> {
|
||||
const params = startDate && endDate ? `?startDate=${startDate}&endDate=${endDate}` : '';
|
||||
return await api.get<{
|
||||
records: DietRecord[]
|
||||
total: number
|
||||
page: number
|
||||
limit: number
|
||||
}>(`/users/diet-records${params}`);
|
||||
}
|
||||
|
||||
export function calculateNutritionSummary(records: DietRecord[]): NutritionSummary {
|
||||
if (records?.length === 0) {
|
||||
return {
|
||||
totalCalories: 0,
|
||||
totalProtein: 0,
|
||||
totalCarbohydrate: 0,
|
||||
totalFat: 0,
|
||||
totalFiber: 0,
|
||||
totalSugar: 0,
|
||||
totalSodium: 0,
|
||||
};
|
||||
}
|
||||
|
||||
return records.reduce(
|
||||
(summary, record) => ({
|
||||
totalCalories: summary.totalCalories + (record.estimatedCalories || 0),
|
||||
totalProtein: summary.totalProtein + (record.proteinGrams || 0),
|
||||
totalCarbohydrate: summary.totalCarbohydrate + (record.carbohydrateGrams || 0),
|
||||
totalFat: summary.totalFat + (record.fatGrams || 0),
|
||||
totalFiber: summary.totalFiber + (record.fiberGrams || 0),
|
||||
totalSugar: summary.totalSugar + (record.sugarGrams || 0),
|
||||
totalSodium: summary.totalSodium + (record.sodiumMg || 0),
|
||||
}),
|
||||
{
|
||||
totalCalories: 0,
|
||||
totalProtein: 0,
|
||||
totalCarbohydrate: 0,
|
||||
totalFat: 0,
|
||||
totalFiber: 0,
|
||||
totalSugar: 0,
|
||||
totalSodium: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 将营养数据转换为雷达图数据(0-5分制)
|
||||
export function convertToRadarData(summary: NutritionSummary): number[] {
|
||||
// 基于推荐日摄入量计算分数
|
||||
const recommendations = {
|
||||
calories: 2000, // 卡路里
|
||||
protein: 50, // 蛋白质(g)
|
||||
carbohydrate: 300, // 碳水化合物(g)
|
||||
fat: 65, // 脂肪(g)
|
||||
fiber: 25, // 膳食纤维(g)
|
||||
sodium: 2300, // 钠(mg)
|
||||
};
|
||||
|
||||
return [
|
||||
Math.min(5, (summary.totalCalories / recommendations.calories) * 5),
|
||||
Math.min(5, (summary.totalProtein / recommendations.protein) * 5),
|
||||
Math.min(5, (summary.totalCarbohydrate / recommendations.carbohydrate) * 5),
|
||||
Math.min(5, (summary.totalFat / recommendations.fat) * 5),
|
||||
Math.min(5, (summary.totalFiber / recommendations.fiber) * 5),
|
||||
Math.min(5, Math.max(0, 5 - (summary.totalSodium / recommendations.sodium) * 5)), // 钠含量越低越好
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user