Files
digital-pilates/app/food-library.tsx
2025-08-28 19:24:22 +08:00

465 lines
12 KiB
TypeScript

import type { FoodItem } from '@/components/model/food/FoodDetailModal';
import { FoodDetailModal } from '@/components/model/food/FoodDetailModal';
import { Ionicons } from '@expo/vector-icons';
import { useLocalSearchParams, useRouter } from 'expo-router';
import React, { useState } from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
// 食物分类类型
export interface FoodCategory {
id: string;
name: string;
foods: FoodItem[];
}
// 模拟食物数据
const FOOD_DATA: FoodCategory[] = [
{
id: 'common',
name: '常见',
foods: [
{ id: '1', name: '无糖美式咖啡', emoji: '☕', calories: 1, unit: '100克' },
{ id: '2', name: '荷包蛋(油煎)', emoji: '🍳', calories: 195, unit: '100克' },
{ id: '3', name: '鸡蛋', emoji: '🥚', calories: 139, unit: '100克' },
{ id: '4', name: '香蕉', emoji: '🍌', calories: 93, unit: '100克' },
{ id: '5', name: '猕猴桃', emoji: '🥝', calories: 61, unit: '100克' },
{ id: '6', name: '苹果', emoji: '🍎', calories: 53, unit: '100克' },
{ id: '7', name: '草莓', emoji: '🍓', calories: 32, unit: '100克' },
{ id: '8', name: '蛋烧麦', emoji: '🥟', calories: 157, unit: '100克' },
{ id: '9', name: '米饭', emoji: '🍚', calories: 116, unit: '100克' },
{ id: '10', name: '鲜玉米', emoji: '🌽', calories: 112, unit: '100克' },
]
},
{
id: 'custom',
name: '自定义',
foods: []
},
{
id: 'favorite',
name: '收藏',
foods: []
},
{
id: 'fruits',
name: '水果蔬菜',
foods: [
{ id: '11', name: '苹果', emoji: '🍎', calories: 53, unit: '100克' },
{ id: '12', name: '香蕉', emoji: '🍌', calories: 93, unit: '100克' },
{ id: '13', name: '草莓', emoji: '🍓', calories: 32, unit: '100克' },
{ id: '14', name: '猕猴桃', emoji: '🥝', calories: 61, unit: '100克' },
]
},
{
id: 'meat',
name: '肉蛋奶',
foods: [
{ id: '15', name: '鸡蛋', emoji: '🥚', calories: 139, unit: '100克' },
{ id: '16', name: '荷包蛋(油煎)', emoji: '🍳', calories: 195, unit: '100克' },
]
},
{
id: 'beans',
name: '豆类坚果',
foods: []
},
{
id: 'drinks',
name: '零食饮料',
foods: [
{ id: '17', name: '无糖美式咖啡', emoji: '☕', calories: 1, unit: '100克' },
]
},
{
id: 'staple',
name: '主食',
foods: [
{ id: '18', name: '米饭', emoji: '🍚', calories: 116, unit: '100克' },
{ id: '19', name: '鲜玉米', emoji: '🌽', calories: 112, unit: '100克' },
{ id: '20', name: '蛋烧麦', emoji: '🥟', calories: 157, unit: '100克' },
]
},
{
id: 'vegetables',
name: '菜肴',
foods: []
}
];
// 餐次映射
const MEAL_TYPE_MAP = {
breakfast: '早餐',
lunch: '午餐',
dinner: '晚餐',
snack: '加餐'
};
export default function FoodLibraryScreen() {
const router = useRouter();
const params = useLocalSearchParams<{ mealType?: string }>();
const mealType = (params.mealType as 'breakfast' | 'lunch' | 'dinner' | 'snack') || 'breakfast';
const [selectedCategoryId, setSelectedCategoryId] = useState('common');
const [searchText, setSearchText] = useState('');
const [selectedFood, setSelectedFood] = useState<FoodItem | null>(null);
const [showFoodDetail, setShowFoodDetail] = useState(false);
// 获取当前选中的分类
const selectedCategory = FOOD_DATA.find(cat => cat.id === selectedCategoryId);
// 过滤食物列表
const filteredFoods = selectedCategory?.foods.filter(food =>
food.name.toLowerCase().includes(searchText.toLowerCase())
) || [];
// 处理食物选择 - 显示详情弹窗
const handleSelectFood = (food: FoodItem) => {
console.log('选择食物:', food);
setSelectedFood(food);
setShowFoodDetail(true);
console.log('设置弹窗状态:', {
showFoodDetail: true,
selectedFood: food,
foodName: food.name,
foodId: food.id
});
};
// 处理食物保存
const handleSaveFood = (food: FoodItem, amount: number, unit: string) => {
// 这里可以处理保存逻辑,比如添加到营养记录
console.log('保存食物:', food, amount, unit);
setShowFoodDetail(false);
router.back(); // 返回上一页
};
// 关闭详情弹窗
const handleCloseFoodDetail = () => {
setShowFoodDetail(false);
setSelectedFood(null);
};
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#F8F9FA" />
{/* 头部 */}
<View style={styles.header}>
<TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
<Ionicons name="chevron-back" size={24} color="#333" />
</TouchableOpacity>
<Text style={styles.headerTitle}></Text>
<TouchableOpacity
style={styles.customButton}
onPress={() => {
const testFood: FoodItem = {
id: 'test',
name: '测试食物',
emoji: '🍎',
calories: 100,
unit: '100克'
};
handleSelectFood(testFood);
}}
>
<Text style={styles.customButtonText}></Text>
</TouchableOpacity>
</View>
{/* 搜索框 */}
<View style={styles.searchContainer}>
<Ionicons name="search" size={20} color="#999" style={styles.searchIcon} />
<TextInput
style={styles.searchInput}
placeholder="搜索食物"
value={searchText}
onChangeText={setSearchText}
placeholderTextColor="#999"
/>
</View>
{/* 主要内容区域 - 卡片样式 */}
<View style={styles.mainContentCard}>
<View style={styles.mainContent}>
{/* 左侧分类导航 */}
<View style={styles.categoryContainer}>
<ScrollView showsVerticalScrollIndicator={false}>
{FOOD_DATA.map((category) => (
<TouchableOpacity
key={category.id}
style={[
styles.categoryItem,
selectedCategoryId === category.id && styles.categoryItemActive
]}
onPress={() => setSelectedCategoryId(category.id)}
>
<Text style={[
styles.categoryText,
selectedCategoryId === category.id && styles.categoryTextActive
]}>
{category.name}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
{/* 右侧食物列表 */}
<View style={styles.foodContainer}>
<ScrollView showsVerticalScrollIndicator={false}>
{filteredFoods.map((food) => (
<View key={food.id} style={styles.foodItem}>
<View style={styles.foodInfo}>
<Text style={styles.foodEmoji}>{food.emoji}</Text>
<View style={styles.foodDetails}>
<Text style={styles.foodName}>{food.name}</Text>
<Text style={styles.foodCalories}>
{food.calories}/{food.unit}
</Text>
</View>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={() => handleSelectFood(food)}
>
<Ionicons name="add" size={20} color="#666" />
</TouchableOpacity>
</View>
))}
{filteredFoods.length === 0 && (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}></Text>
</View>
)}
</ScrollView>
</View>
</View>
</View>
{/* 底部餐次选择和记录按钮 */}
<View style={styles.bottomContainer}>
<View style={styles.mealSelector}>
<View style={styles.mealIndicator} />
<Text style={styles.mealText}>{MEAL_TYPE_MAP[mealType]}</Text>
<Ionicons name="chevron-down" size={16} color="#333" />
</View>
<TouchableOpacity style={styles.recordButton}>
<Text style={styles.recordButtonText}></Text>
</TouchableOpacity>
</View>
{/* 食物详情弹窗 */}
<FoodDetailModal
visible={showFoodDetail}
food={selectedFood}
onClose={handleCloseFoodDetail}
onSave={handleSaveFood}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F8F9FA',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#F8F9FA',
borderBottomWidth: 1,
borderBottomColor: '#E5E5E5',
},
backButton: {
padding: 4,
},
headerTitle: {
fontSize: 18,
fontWeight: '600',
color: '#333',
},
customButton: {
paddingHorizontal: 8,
paddingVertical: 4,
},
customButtonText: {
fontSize: 16,
color: '#4CAF50',
fontWeight: '500',
},
searchContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF',
marginHorizontal: 16,
marginVertical: 12,
paddingHorizontal: 12,
paddingVertical: 10,
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
searchIcon: {
marginRight: 8,
},
searchInput: {
flex: 1,
fontSize: 16,
color: '#333',
},
mainContentCard: {
flex: 1,
marginHorizontal: 16,
marginBottom: 16,
backgroundColor: '#FFFFFF',
borderRadius: 16,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4,
overflow: 'hidden',
},
mainContent: {
flex: 1,
flexDirection: 'row',
},
categoryContainer: {
width: 100,
backgroundColor: 'transparent',
borderRightWidth: 1,
borderRightColor: '#E5E5E5',
},
categoryItem: {
paddingVertical: 16,
paddingHorizontal: 12,
alignItems: 'center',
},
categoryItemActive: {
backgroundColor: '#F0F9FF',
borderRightWidth: 2,
borderRightColor: '#4CAF50',
},
categoryText: {
fontSize: 14,
color: '#666',
textAlign: 'center',
},
categoryTextActive: {
color: '#4CAF50',
fontWeight: '500',
},
foodContainer: {
flex: 1,
backgroundColor: 'transparent',
},
foodItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
},
foodInfo: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
foodEmoji: {
fontSize: 32,
marginRight: 12,
},
foodDetails: {
flex: 1,
},
foodName: {
fontSize: 16,
color: '#333',
fontWeight: '500',
marginBottom: 2,
},
foodCalories: {
fontSize: 14,
color: '#999',
},
addButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: '#F5F5F5',
alignItems: 'center',
justifyContent: 'center',
},
emptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 40,
},
emptyText: {
fontSize: 16,
color: '#999',
},
bottomContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#FFF',
borderTopWidth: 1,
borderTopColor: '#E5E5E5',
},
mealSelector: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 12,
paddingVertical: 8,
backgroundColor: '#F8F9FA',
borderRadius: 20,
},
mealIndicator: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: '#FF6B35',
marginRight: 8,
},
mealText: {
fontSize: 14,
color: '#333',
marginRight: 4,
},
recordButton: {
backgroundColor: '#4CAF50',
paddingHorizontal: 24,
paddingVertical: 10,
borderRadius: 20,
},
recordButtonText: {
fontSize: 16,
color: '#FFF',
fontWeight: '500',
},
});