import { Ionicons } from '@expo/vector-icons'; import { Image } from 'expo-image'; import * as ImagePicker from 'expo-image-picker'; import React, { useEffect, useState } from 'react'; import { Alert, Dimensions, Keyboard, KeyboardAvoidingView, Modal, Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; export interface CreateCustomFoodModalProps { visible: boolean; onClose: () => void; onSave: (foodData: CustomFoodData) => void; } export interface CustomFoodData { name: string; unit: string; defaultAmount: number; caloriesUnit: string; calories: number; imageUrl?: string; protein?: number; fat?: number; carbohydrate?: number; } export function CreateCustomFoodModal({ visible, onClose, onSave }: CreateCustomFoodModalProps) { const [foodName, setFoodName] = useState(''); const [foodUnit, setFoodUnit] = useState(''); const [defaultAmount, setDefaultAmount] = useState('100'); const [caloriesUnit, setCaloriesUnit] = useState('千卡'); const [calories, setCalories] = useState('100'); const [imageUrl, setImageUrl] = useState(''); const [protein, setProtein] = useState('0'); const [fat, setFat] = useState('0'); const [carbohydrate, setCarbohydrate] = useState('0'); const [keyboardHeight, setKeyboardHeight] = useState(0); // 键盘监听 useEffect(() => { const keyboardDidShowListener = Keyboard.addListener( Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow', (e) => { setKeyboardHeight(e.endCoordinates.height); } ); const keyboardDidHideListener = Keyboard.addListener( Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide', () => { setKeyboardHeight(0); } ); return () => { keyboardDidShowListener?.remove(); keyboardDidHideListener?.remove(); }; }, []); // 重置表单 useEffect(() => { if (visible) { setFoodName(''); setFoodUnit(''); setDefaultAmount('100'); setCaloriesUnit('千卡'); setCalories('100'); setImageUrl(''); setProtein('0'); setFat('0'); setCarbohydrate('0'); } }, [visible]); // 选择热量单位 // 选择图片 const handleSelectImage = async () => { const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (status !== 'granted') { Alert.alert('需要相册权限', '请在设置中开启相册权限'); return; } const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [1, 1], quality: 1, }); if (!result.canceled && result.assets[0]) { setImageUrl(result.assets[0].uri); } }; // 计算实际热量预览 const actualCalories = Math.round((parseFloat(calories) || 0) * (parseFloat(defaultAmount) || 0) / 100); // 保存自定义食物 const handleSave = () => { if (!foodName.trim()) { Alert.alert('提示', '请输入食物名称'); return; } if (!foodUnit.trim()) { Alert.alert('提示', '请输入食物单位'); return; } if (!calories.trim() || parseFloat(calories) <= 0) { Alert.alert('提示', '请输入有效的热量值'); return; } const foodData: CustomFoodData = { name: foodName.trim(), unit: foodUnit.trim(), defaultAmount: parseFloat(defaultAmount) || 100, caloriesUnit, calories: parseFloat(calories) || 0, imageUrl: imageUrl || undefined, protein: parseFloat(protein) || undefined, fat: parseFloat(fat) || undefined, carbohydrate: parseFloat(carbohydrate) || undefined, }; onSave(foodData); onClose(); }; return ( 0 && { height: screenHeight - keyboardHeight, maxHeight: screenHeight - keyboardHeight, } ]}> 0 ? 20 : 0 }} > {/* 头部 */} 创建自定义食物 保存 {/* 效果预览区域 */} 效果预览 {imageUrl ? ( ) : ( )} {foodName || '食物名称'} {actualCalories}{caloriesUnit}/{defaultAmount}{foodUnit} {/* 基本信息 */} 基本信息 * {/* 食物名称和单位 */} 食物名称 食物单位 {/* 默认数量 */} 默认数量 {foodUnit || '单位'} {/* 热量单位 */} 热量单位 千卡 {/* 食物热量 */} 食物热量 千卡 {/* 可选信息 */} 可选信息 {/* 照片 */} 照片 {imageUrl ? ( ) : ( 添加照片 )} {/* 蛋白质 */} 蛋白质 {/* 脂肪 */} 脂肪 {/* 碳水化合物 */} 碳水化合物 {/* 底部提示 */} *为了避免历史饮食数据混乱,自定义食物创建后不支持修改。 ); } const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', }, keyboardAvoidingView: { flex: 1, }, modalContainer: { flex: 1, backgroundColor: '#FFFFFF', marginTop: 50, borderTopLeftRadius: 20, borderTopRightRadius: 20, }, scrollView: { flex: 1, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 16, borderBottomWidth: 1, borderBottomColor: '#E5E5E5', }, backButton: { padding: 4, }, headerTitle: { fontSize: 18, fontWeight: '600', color: '#333', flex: 1, textAlign: 'center', marginHorizontal: 20, }, saveButton: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 16, }, saveButtonDisabled: { opacity: 0.5, }, saveButtonText: { fontSize: 16, color: '#4CAF50', fontWeight: '500', }, saveButtonTextDisabled: { color: '#999', }, previewSection: { paddingHorizontal: 16, paddingTop: 20, paddingBottom: 16, }, previewCard: { backgroundColor: '#F8F9FA', borderRadius: 12, padding: 16, marginTop: 8, }, previewContent: { flexDirection: 'row', alignItems: 'center', }, previewImage: { width: 32, height: 32, borderRadius: 4, }, previewImagePlaceholder: { width: 32, height: 32, borderRadius: 4, backgroundColor: '#E5E5E5', alignItems: 'center', justifyContent: 'center', }, previewInfo: { flex: 1, marginLeft: 12, }, previewName: { fontSize: 16, fontWeight: '500', color: '#333', marginBottom: 2, }, previewCalories: { fontSize: 14, color: '#666', }, section: { paddingHorizontal: 16, paddingVertical: 12, }, sectionHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 16, }, sectionTitle: { fontSize: 18, fontWeight: '600', color: '#333', }, requiredIndicator: { fontSize: 18, color: '#FF4444', marginLeft: 4, }, inputGroup: { marginBottom: 20, }, inputRowGroup: { flexDirection: 'row', gap: 12, marginBottom: 20, }, inputRowItem: { flex: 1, }, inputLabel: { fontSize: 14, color: '#666', marginBottom: 8, fontWeight: '500', }, modernTextInput: { borderWidth: 1.5, borderColor: '#E8E8E8', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 14, fontSize: 16, color: '#333', backgroundColor: '#FFFFFF', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, elevation: 1, }, numberInputContainer: { flexDirection: 'row', alignItems: 'center', borderWidth: 1.5, borderColor: '#E8E8E8', borderRadius: 12, backgroundColor: '#FFFFFF', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, elevation: 1, }, modernNumberInput: { flex: 1, paddingHorizontal: 16, paddingVertical: 14, fontSize: 16, color: '#333', textAlign: 'right', }, unitText: { fontSize: 14, color: '#666', paddingRight: 16, minWidth: 40, textAlign: 'center', }, modernSelectButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderWidth: 1.5, borderColor: '#E8E8E8', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 14, backgroundColor: '#FFFFFF', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.05, shadowRadius: 2, elevation: 1, }, selectButtonText: { fontSize: 16, color: '#333', fontWeight: '500', }, modernImageSelector: { alignSelf: 'flex-end', borderRadius: 16, overflow: 'hidden', }, selectedImage: { width: 80, height: 80, borderRadius: 16, }, modernImagePlaceholder: { width: 80, height: 80, borderRadius: 16, backgroundColor: '#F8F8F8', alignItems: 'center', justifyContent: 'center', borderWidth: 1.5, borderColor: '#E8E8E8', borderStyle: 'dashed', }, imagePlaceholderText: { fontSize: 12, color: '#A0A0A0', marginTop: 4, fontWeight: '500', }, nutritionRow: { flexDirection: 'row', gap: 12, marginBottom: 20, }, nutritionItem: { flex: 1, }, // 保留旧样式以防兼容性问题 textInput: { borderWidth: 1, borderColor: '#E5E5E5', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 12, fontSize: 16, color: '#333', backgroundColor: '#FFFFFF', }, numberInput: { flex: 1, borderWidth: 1, borderColor: '#E5E5E5', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 12, fontSize: 16, color: '#333', backgroundColor: '#FFFFFF', textAlign: 'right', }, inputWithUnit: { flexDirection: 'row', alignItems: 'center', gap: 8, }, inputUnit: { fontSize: 16, color: '#666', minWidth: 30, }, selectButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderWidth: 1, borderColor: '#E5E5E5', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 12, backgroundColor: '#FFFFFF', }, imageSelector: { alignSelf: 'flex-end', borderRadius: 12, overflow: 'hidden', }, imagePlaceholder: { width: 60, height: 60, borderRadius: 12, backgroundColor: '#F0F0F0', alignItems: 'center', justifyContent: 'center', borderWidth: 1, borderColor: '#E5E5E5', }, disclaimer: { paddingHorizontal: 16, paddingVertical: 20, paddingBottom: 40, }, disclaimerText: { fontSize: 12, color: '#999', lineHeight: 18, }, sectionCard: { backgroundColor: '#F8F9FA', borderRadius: 12, padding: 16, marginTop: 8, }, // 新增行布局样式 inputRowContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: 20, }, inputRowLabel: { fontSize: 14, color: '#666', fontWeight: '500', width: 80, marginRight: 12, }, inputRowContent: { flex: 1, }, });