perf: 优化食物选择
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { FoodDetailModal } from '@/components/model/food/FoodDetailModal';
|
||||
import { useFoodLibrary, useFoodSearch } from '@/hooks/useFoodLibrary';
|
||||
import type { FoodItem, MealType, SelectedFoodItem } from '@/types/food';
|
||||
import { addDietRecord, type CreateDietRecordDto } from '@/services/dietRecords';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { Image } from 'expo-image';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
@@ -45,6 +46,7 @@ export default function FoodLibraryScreen() {
|
||||
const [selectedFoodItems, setSelectedFoodItems] = useState<SelectedFoodItem[]>([]);
|
||||
const [showMealSelector, setShowMealSelector] = useState(false);
|
||||
const [currentMealType, setCurrentMealType] = useState<MealType>(mealType);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
|
||||
// 获取当前选中的分类
|
||||
const selectedCategory = categories.find(cat => cat.id === selectedCategoryId);
|
||||
@@ -127,6 +129,47 @@ export default function FoodLibraryScreen() {
|
||||
setSelectedFood(null);
|
||||
};
|
||||
|
||||
// 处理饮食记录
|
||||
const handleRecordDiet = async () => {
|
||||
if (selectedFoodItems.length === 0) return;
|
||||
|
||||
setIsRecording(true);
|
||||
|
||||
try {
|
||||
// 逐个记录选中的食物
|
||||
for (const item of selectedFoodItems) {
|
||||
const dietRecordData: CreateDietRecordDto = {
|
||||
mealType: currentMealType,
|
||||
foodName: item.food.name,
|
||||
foodDescription: item.food.description,
|
||||
portionDescription: `${item.amount}${item.unit}`,
|
||||
estimatedCalories: item.calories,
|
||||
proteinGrams: item.food.protein ? Number(((item.food.protein * item.amount) / 100).toFixed(2)) : undefined,
|
||||
carbohydrateGrams: item.food.carbohydrate ? Number(((item.food.carbohydrate * item.amount) / 100).toFixed(2)) : undefined,
|
||||
fatGrams: item.food.fat ? Number(((item.food.fat * item.amount) / 100).toFixed(2)) : undefined,
|
||||
fiberGrams: item.food.fiber ? Number(((item.food.fiber * item.amount) / 100).toFixed(2)) : undefined,
|
||||
sugarGrams: item.food.sugar ? Number(((item.food.sugar * item.amount) / 100).toFixed(2)) : undefined,
|
||||
sodiumMg: item.food.sodium ? Number(((item.food.sodium * item.amount) / 100).toFixed(2)) : undefined,
|
||||
additionalNutrition: item.food.additionalNutrition,
|
||||
source: 'manual',
|
||||
mealTime: new Date().toISOString(),
|
||||
imageUrl: item.food.imageUrl,
|
||||
};
|
||||
|
||||
await addDietRecord(dietRecordData);
|
||||
}
|
||||
|
||||
// 记录成功后清空选择列表并返回
|
||||
setSelectedFoodItems([]);
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.error('记录饮食失败:', error);
|
||||
// 这里可以显示错误提示
|
||||
} finally {
|
||||
setIsRecording(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理餐次选择
|
||||
const handleMealTypeSelect = (selectedMealType: MealType) => {
|
||||
setCurrentMealType(selectedMealType);
|
||||
@@ -324,20 +367,19 @@ export default function FoodLibraryScreen() {
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.recordButton,
|
||||
selectedFoodItems.length === 0 && styles.recordButtonDisabled
|
||||
(selectedFoodItems.length === 0 || isRecording) && styles.recordButtonDisabled
|
||||
]}
|
||||
disabled={selectedFoodItems.length === 0}
|
||||
onPress={() => {
|
||||
// 这里可以处理记录逻辑
|
||||
console.log('记录食物:', selectedFoodItems);
|
||||
// 记录成功后可以清空选择列表或返回上一页
|
||||
router.back();
|
||||
}}
|
||||
disabled={selectedFoodItems.length === 0 || isRecording}
|
||||
onPress={handleRecordDiet}
|
||||
>
|
||||
{isRecording ? (
|
||||
<ActivityIndicator size="small" color="#FFF" />
|
||||
) : (
|
||||
<Text style={[
|
||||
styles.recordButtonText,
|
||||
selectedFoodItems.length === 0 && styles.recordButtonTextDisabled
|
||||
]}>记录</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -3,6 +3,27 @@ import { api } from '@/services/api';
|
||||
export type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack' | 'other';
|
||||
export type RecordSource = 'manual' | 'vision' | 'other';
|
||||
|
||||
export type CreateDietRecordDto = {
|
||||
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?: Record<string, any>;
|
||||
source?: RecordSource;
|
||||
mealTime?: string;
|
||||
imageUrl?: string;
|
||||
aiAnalysisResult?: Record<string, any>;
|
||||
notes?: string;
|
||||
};
|
||||
|
||||
export type DietRecord = {
|
||||
id: number;
|
||||
mealType: MealType;
|
||||
@@ -68,6 +89,10 @@ export async function getDietRecords({
|
||||
}>(`/diet-records${params}`);
|
||||
}
|
||||
|
||||
export async function addDietRecord(dto: CreateDietRecordDto): Promise<DietRecord> {
|
||||
return await api.post<DietRecord>('/diet-records', dto);
|
||||
}
|
||||
|
||||
export async function deleteDietRecord(recordId: number): Promise<void> {
|
||||
await api.delete(`/diet-records/${recordId}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user