perf: 优化食物选择
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { FoodDetailModal } from '@/components/model/food/FoodDetailModal';
|
import { FoodDetailModal } from '@/components/model/food/FoodDetailModal';
|
||||||
import { useFoodLibrary, useFoodSearch } from '@/hooks/useFoodLibrary';
|
import { useFoodLibrary, useFoodSearch } from '@/hooks/useFoodLibrary';
|
||||||
import type { FoodItem, MealType, SelectedFoodItem } from '@/types/food';
|
import type { FoodItem, MealType, SelectedFoodItem } from '@/types/food';
|
||||||
|
import { addDietRecord, type CreateDietRecordDto } from '@/services/dietRecords';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { Image } from 'expo-image';
|
import { Image } from 'expo-image';
|
||||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
@@ -45,6 +46,7 @@ export default function FoodLibraryScreen() {
|
|||||||
const [selectedFoodItems, setSelectedFoodItems] = useState<SelectedFoodItem[]>([]);
|
const [selectedFoodItems, setSelectedFoodItems] = useState<SelectedFoodItem[]>([]);
|
||||||
const [showMealSelector, setShowMealSelector] = useState(false);
|
const [showMealSelector, setShowMealSelector] = useState(false);
|
||||||
const [currentMealType, setCurrentMealType] = useState<MealType>(mealType);
|
const [currentMealType, setCurrentMealType] = useState<MealType>(mealType);
|
||||||
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
|
|
||||||
// 获取当前选中的分类
|
// 获取当前选中的分类
|
||||||
const selectedCategory = categories.find(cat => cat.id === selectedCategoryId);
|
const selectedCategory = categories.find(cat => cat.id === selectedCategoryId);
|
||||||
@@ -127,6 +129,47 @@ export default function FoodLibraryScreen() {
|
|||||||
setSelectedFood(null);
|
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) => {
|
const handleMealTypeSelect = (selectedMealType: MealType) => {
|
||||||
setCurrentMealType(selectedMealType);
|
setCurrentMealType(selectedMealType);
|
||||||
@@ -324,20 +367,19 @@ export default function FoodLibraryScreen() {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.recordButton,
|
styles.recordButton,
|
||||||
selectedFoodItems.length === 0 && styles.recordButtonDisabled
|
(selectedFoodItems.length === 0 || isRecording) && styles.recordButtonDisabled
|
||||||
]}
|
]}
|
||||||
disabled={selectedFoodItems.length === 0}
|
disabled={selectedFoodItems.length === 0 || isRecording}
|
||||||
onPress={() => {
|
onPress={handleRecordDiet}
|
||||||
// 这里可以处理记录逻辑
|
|
||||||
console.log('记录食物:', selectedFoodItems);
|
|
||||||
// 记录成功后可以清空选择列表或返回上一页
|
|
||||||
router.back();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
|
{isRecording ? (
|
||||||
|
<ActivityIndicator size="small" color="#FFF" />
|
||||||
|
) : (
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.recordButtonText,
|
styles.recordButtonText,
|
||||||
selectedFoodItems.length === 0 && styles.recordButtonTextDisabled
|
selectedFoodItems.length === 0 && styles.recordButtonTextDisabled
|
||||||
]}>记录</Text>
|
]}>记录</Text>
|
||||||
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,27 @@ import { api } from '@/services/api';
|
|||||||
export type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack' | 'other';
|
export type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack' | 'other';
|
||||||
export type RecordSource = 'manual' | 'vision' | '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 = {
|
export type DietRecord = {
|
||||||
id: number;
|
id: number;
|
||||||
mealType: MealType;
|
mealType: MealType;
|
||||||
@@ -68,6 +89,10 @@ export async function getDietRecords({
|
|||||||
}>(`/diet-records${params}`);
|
}>(`/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> {
|
export async function deleteDietRecord(recordId: number): Promise<void> {
|
||||||
await api.delete(`/diet-records/${recordId}`);
|
await api.delete(`/diet-records/${recordId}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user