feat: 添加食物编辑功能,支持修改食物名称、重量和卡路里
This commit is contained in:
@@ -15,9 +15,11 @@ import {
|
|||||||
BackHandler,
|
BackHandler,
|
||||||
Image,
|
Image,
|
||||||
Modal,
|
Modal,
|
||||||
|
Pressable,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
@@ -84,6 +86,8 @@ export default function FoodAnalysisResultScreen() {
|
|||||||
const [isRecording, setIsRecording] = useState(false);
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
const [showImagePreview, setShowImagePreview] = useState(false);
|
const [showImagePreview, setShowImagePreview] = useState(false);
|
||||||
const [animationTrigger, setAnimationTrigger] = useState(0);
|
const [animationTrigger, setAnimationTrigger] = useState(0);
|
||||||
|
const [editingFood, setEditingFood] = useState<string | null>(null);
|
||||||
|
const [editFormData, setEditFormData] = useState({ name: '', amount: '', calories: '' });
|
||||||
const { imageUri, recognitionId, hideRecordBar } = params;
|
const { imageUri, recognitionId, hideRecordBar } = params;
|
||||||
|
|
||||||
// 判断是否隐藏记录栏(默认显示)
|
// 判断是否隐藏记录栏(默认显示)
|
||||||
@@ -199,6 +203,39 @@ export default function FoodAnalysisResultScreen() {
|
|||||||
setFoodItems(prev => prev.filter(item => item.id !== itemId));
|
setFoodItems(prev => prev.filter(item => item.id !== itemId));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 打开编辑弹窗
|
||||||
|
const handleEditFood = (item: typeof mockFoodItems[0]) => {
|
||||||
|
setEditFormData({
|
||||||
|
name: item.name,
|
||||||
|
amount: item.amount.toString(),
|
||||||
|
calories: item.calories.toString(),
|
||||||
|
});
|
||||||
|
setEditingFood(item.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存编辑
|
||||||
|
const handleSaveEdit = () => {
|
||||||
|
if (!editingFood) return;
|
||||||
|
|
||||||
|
const amount = parseFloat(editFormData.amount) || 0;
|
||||||
|
const calories = parseFloat(editFormData.calories) || 0;
|
||||||
|
|
||||||
|
setFoodItems(prev => prev.map(item =>
|
||||||
|
item.id === editingFood
|
||||||
|
? { ...item, name: editFormData.name, amount, calories }
|
||||||
|
: item
|
||||||
|
));
|
||||||
|
|
||||||
|
setEditingFood(null);
|
||||||
|
setEditFormData({ name: '', amount: '', calories: '' });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭编辑弹窗
|
||||||
|
const handleCloseEdit = () => {
|
||||||
|
setEditingFood(null);
|
||||||
|
setEditFormData({ name: '', amount: '', calories: '' });
|
||||||
|
};
|
||||||
|
|
||||||
// 获取随机食物emoji
|
// 获取随机食物emoji
|
||||||
function getRandomFoodEmoji(): string {
|
function getRandomFoodEmoji(): string {
|
||||||
const foodEmojis = ['🍎', '🍌', '🍞', '🥛', '🥗', '🍗', '🍖', '🥕', '🥦', '🥬', '🍅', '🥒', '🍇', '🥝', '🍓'];
|
const foodEmojis = ['🍎', '🍌', '🍞', '🥛', '🥗', '🍗', '🍖', '🥕', '🥦', '🥬', '🍅', '🥒', '🍇', '🥝', '🍓'];
|
||||||
@@ -367,7 +404,10 @@ export default function FoodAnalysisResultScreen() {
|
|||||||
|
|
||||||
<View style={styles.foodIntakeCalories}>
|
<View style={styles.foodIntakeCalories}>
|
||||||
<Text style={styles.foodIntakeCaloriesValue}>{item.calories}千卡</Text>
|
<Text style={styles.foodIntakeCaloriesValue}>{item.calories}千卡</Text>
|
||||||
{shouldHideRecordBar ? null : <TouchableOpacity style={styles.editButton}>
|
{shouldHideRecordBar ? null : <TouchableOpacity
|
||||||
|
style={styles.editButton}
|
||||||
|
onPress={() => handleEditFood(item)}
|
||||||
|
>
|
||||||
<Ionicons name="create-outline" size={16} color="#666" />
|
<Ionicons name="create-outline" size={16} color="#666" />
|
||||||
</TouchableOpacity>}
|
</TouchableOpacity>}
|
||||||
{shouldHideRecordBar ? null : <TouchableOpacity
|
{shouldHideRecordBar ? null : <TouchableOpacity
|
||||||
@@ -508,10 +548,109 @@ export default function FoodAnalysisResultScreen() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* 编辑食物弹窗 */}
|
||||||
|
<FoodEditModal
|
||||||
|
visible={!!editingFood}
|
||||||
|
formData={editFormData}
|
||||||
|
onFormDataChange={setEditFormData}
|
||||||
|
onClose={handleCloseEdit}
|
||||||
|
onSave={handleSaveEdit}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 编辑食物弹窗组件
|
||||||
|
function FoodEditModal({
|
||||||
|
visible,
|
||||||
|
formData,
|
||||||
|
onFormDataChange,
|
||||||
|
onClose,
|
||||||
|
onSave
|
||||||
|
}: {
|
||||||
|
visible: boolean;
|
||||||
|
formData: { name: string; amount: string; calories: string };
|
||||||
|
onFormDataChange: (data: { name: string; amount: string; calories: string }) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
onSave: () => void;
|
||||||
|
}) {
|
||||||
|
const handleFieldChange = (field: string, value: string) => {
|
||||||
|
onFormDataChange({ ...formData, [field]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
transparent
|
||||||
|
animationType="fade"
|
||||||
|
onRequestClose={onClose}
|
||||||
|
>
|
||||||
|
<Pressable style={styles.modalBackdrop} onPress={onClose} />
|
||||||
|
<View style={styles.editModalSheet}>
|
||||||
|
<View style={styles.modalHandle} />
|
||||||
|
|
||||||
|
<Text style={styles.modalTitle}>编辑食物信息</Text>
|
||||||
|
|
||||||
|
{/* 食物名称 */}
|
||||||
|
<View style={styles.editFieldContainer}>
|
||||||
|
<Text style={styles.editFieldLabel}>食物名称</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.editInput}
|
||||||
|
placeholder="输入食物名称"
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
value={formData.name}
|
||||||
|
onChangeText={(value) => handleFieldChange('name', value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 重量/数量 */}
|
||||||
|
<View style={styles.editFieldContainer}>
|
||||||
|
<Text style={styles.editFieldLabel}>重量 (克)</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.editInput}
|
||||||
|
placeholder="输入重量"
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
value={formData.amount}
|
||||||
|
onChangeText={(value) => handleFieldChange('amount', value)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 卡路里 */}
|
||||||
|
<View style={styles.editFieldContainer}>
|
||||||
|
<Text style={styles.editFieldLabel}>卡路里 (千卡)</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.editInput}
|
||||||
|
placeholder="输入卡路里"
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
value={formData.calories}
|
||||||
|
onChangeText={(value) => handleFieldChange('calories', value)}
|
||||||
|
keyboardType="numeric"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 按钮区域 */}
|
||||||
|
<View style={styles.modalButtons}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={onClose}
|
||||||
|
style={styles.modalCancelBtn}
|
||||||
|
>
|
||||||
|
<Text style={styles.modalCancelText}>取消</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={onSave}
|
||||||
|
style={[styles.modalSaveBtn, { backgroundColor: Colors.light.primary }]}
|
||||||
|
>
|
||||||
|
<Text style={[styles.modalSaveText, { color: Colors.light.onPrimary }]}>保存</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 营养圆环组件
|
// 营养圆环组件
|
||||||
function NutritionRing({
|
function NutritionRing({
|
||||||
label,
|
label,
|
||||||
@@ -1039,4 +1178,84 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
|
modalBackdrop: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||||
|
},
|
||||||
|
// 编辑弹窗样式
|
||||||
|
editModalSheet: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
borderTopLeftRadius: 20,
|
||||||
|
borderTopRightRadius: 20,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingBottom: 40,
|
||||||
|
paddingTop: 20,
|
||||||
|
},
|
||||||
|
modalHandle: {
|
||||||
|
width: 36,
|
||||||
|
height: 4,
|
||||||
|
backgroundColor: '#E0E0E0',
|
||||||
|
borderRadius: 2,
|
||||||
|
alignSelf: 'center',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
modalTitle: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: 24,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
editFieldContainer: {
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
editFieldLabel: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '500',
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
editInput: {
|
||||||
|
height: 50,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#E0E0E0',
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
fontSize: 16,
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
color: '#333333',
|
||||||
|
},
|
||||||
|
modalButtons: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 12,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
modalCancelBtn: {
|
||||||
|
flex: 1,
|
||||||
|
height: 50,
|
||||||
|
backgroundColor: '#F0F0F0',
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
modalCancelText: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#666666',
|
||||||
|
},
|
||||||
|
modalSaveBtn: {
|
||||||
|
flex: 1,
|
||||||
|
height: 50,
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
modalSaveText: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user