Add Chinese translations for medication management and personal settings
- Introduced new translation files for medication, personal, and weight management in Chinese. - Updated the main index file to include the new translation modules. - Enhanced the medication type definitions to include 'ointment'. - Refactored workout type labels to utilize i18n for better localization support. - Improved sleep quality descriptions and recommendations with i18n integration.
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { useAppSelector } from '@/hooks/redux';
|
||||
import { useCosUpload } from '@/hooks/useCosUpload';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { Image } from 'expo-image';
|
||||
import * as ImagePicker from 'expo-image-picker';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
@@ -15,11 +20,11 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { useAppSelector } from '@/hooks/redux';
|
||||
import { useCosUpload } from '@/hooks/useCosUpload';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
|
||||
const CTA_GRADIENT: [string, string] = ['#5E8BFF', '#6B6CFF'];
|
||||
const CTA_DISABLED_GRADIENT: [string, string] = ['#d3d7e8', '#c1c6da'];
|
||||
|
||||
export interface CreateCustomFoodModalProps {
|
||||
visible: boolean;
|
||||
@@ -43,9 +48,10 @@ export function CreateCustomFoodModal({
|
||||
onClose,
|
||||
onSave
|
||||
}: CreateCustomFoodModalProps) {
|
||||
const { t } = useI18n();
|
||||
const [foodName, setFoodName] = useState('');
|
||||
const [defaultAmount, setDefaultAmount] = useState('100');
|
||||
const [caloriesUnit, setCaloriesUnit] = useState('千卡');
|
||||
const [caloriesUnit, setCaloriesUnit] = useState(t('createCustomFood.units.kcal'));
|
||||
const [calories, setCalories] = useState('100');
|
||||
const [imageUrl, setImageUrl] = useState<string>('');
|
||||
const [protein, setProtein] = useState('0');
|
||||
@@ -93,7 +99,7 @@ export function CreateCustomFoodModal({
|
||||
if (visible) {
|
||||
setFoodName('');
|
||||
setDefaultAmount('100');
|
||||
setCaloriesUnit('千卡');
|
||||
setCaloriesUnit(t('createCustomFood.units.kcal'));
|
||||
setCalories('100');
|
||||
setImageUrl('');
|
||||
setProtein('0');
|
||||
@@ -102,16 +108,16 @@ export function CreateCustomFoodModal({
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
// 选择热量单位
|
||||
|
||||
|
||||
// 选择图片
|
||||
const handleSelectImage = async () => {
|
||||
try {
|
||||
const resp = await ImagePicker.requestMediaLibraryPermissionsAsync();
|
||||
const libGranted = resp.status === 'granted' || (resp as any).accessPrivileges === 'limited';
|
||||
if (!libGranted) {
|
||||
Alert.alert('权限不足', '需要相册权限以选择照片');
|
||||
Alert.alert(
|
||||
t('createCustomFood.alerts.permissionDenied.title'),
|
||||
t('createCustomFood.alerts.permissionDenied.message')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -137,11 +143,17 @@ export function CreateCustomFoodModal({
|
||||
setImageUrl(url);
|
||||
} catch (e) {
|
||||
console.warn('上传照片失败', e);
|
||||
Alert.alert('上传失败', '照片上传失败,请重试');
|
||||
Alert.alert(
|
||||
t('createCustomFood.alerts.uploadFailed.title'),
|
||||
t('createCustomFood.alerts.uploadFailed.message')
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Alert.alert('发生错误', '选择照片失败,请重试');
|
||||
Alert.alert(
|
||||
t('createCustomFood.alerts.error.title'),
|
||||
t('createCustomFood.alerts.error.message')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,12 +163,18 @@ export function CreateCustomFoodModal({
|
||||
// 保存自定义食物
|
||||
const handleSave = () => {
|
||||
if (!foodName.trim()) {
|
||||
Alert.alert('提示', '请输入食物名称');
|
||||
Alert.alert(
|
||||
t('createCustomFood.alerts.validation.title'),
|
||||
t('createCustomFood.alerts.validation.nameRequired')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!calories.trim() || parseFloat(calories) <= 0) {
|
||||
Alert.alert('提示', '请输入有效的热量值');
|
||||
Alert.alert(
|
||||
t('createCustomFood.alerts.validation.title'),
|
||||
t('createCustomFood.alerts.validation.caloriesRequired')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,75 +193,99 @@ export function CreateCustomFoodModal({
|
||||
onClose();
|
||||
};
|
||||
|
||||
const isSaveDisabled = !foodName.trim() || !calories.trim();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
animationType="fade"
|
||||
animationType="slide"
|
||||
transparent={true}
|
||||
onRequestClose={onClose}
|
||||
presentationStyle="overFullScreen"
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<BlurView intensity={20} tint="dark" style={styles.overlay}>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
style={styles.keyboardAvoidingView}
|
||||
>
|
||||
<View style={[
|
||||
styles.modalContainer,
|
||||
keyboardHeight > 0 && {
|
||||
height: screenHeight - keyboardHeight,
|
||||
maxHeight: screenHeight - keyboardHeight,
|
||||
}
|
||||
]}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={onClose} style={styles.dismissArea} />
|
||||
<View
|
||||
style={[
|
||||
styles.modalContainer,
|
||||
keyboardHeight > 0 && {
|
||||
height: screenHeight - keyboardHeight - 60,
|
||||
maxHeight: screenHeight - keyboardHeight - 60,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.modalHeaderBar}>
|
||||
<View style={styles.dragIndicator} />
|
||||
</View>
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
paddingBottom: keyboardHeight > 0 ? 20 : 0
|
||||
paddingBottom: keyboardHeight > 0 ? 20 : 40,
|
||||
}}
|
||||
>
|
||||
{/* 头部 */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={onClose} style={styles.backButton}>
|
||||
<Ionicons name="chevron-back" size={24} color="#333" />
|
||||
<TouchableOpacity onPress={onClose} style={styles.backButton} activeOpacity={0.7}>
|
||||
<Ionicons name="close-circle" size={32} color="#E2E8F0" />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.headerTitle}>创建自定义食物</Text>
|
||||
<Text style={styles.headerTitle}>{t('createCustomFood.title')}</Text>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.saveButton,
|
||||
(!foodName.trim() || !calories.trim()) && styles.saveButtonDisabled
|
||||
]}
|
||||
style={[styles.saveButton, isSaveDisabled && styles.saveButtonDisabled]}
|
||||
onPress={handleSave}
|
||||
disabled={!foodName.trim() || !calories.trim()}
|
||||
disabled={isSaveDisabled}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={[
|
||||
styles.saveButtonText,
|
||||
(!foodName.trim() || !calories.trim()) && styles.saveButtonTextDisabled
|
||||
]}>保存</Text>
|
||||
<LinearGradient
|
||||
colors={isSaveDisabled ? CTA_DISABLED_GRADIENT : CTA_GRADIENT}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.saveButtonGradient}
|
||||
>
|
||||
<Text style={styles.saveButtonText}>{t('createCustomFood.save')}</Text>
|
||||
</LinearGradient>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* 效果预览区域 */}
|
||||
<View style={styles.previewSection}>
|
||||
<Text style={styles.sectionTitle}>效果预览</Text>
|
||||
<View style={styles.previewCard}>
|
||||
<LinearGradient
|
||||
colors={['#ffffff', '#F8F9FF']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 0, y: 1 }}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
<View style={styles.previewHeader}>
|
||||
<Text style={styles.sectionTitle}>{t('createCustomFood.preview.title')}</Text>
|
||||
</View>
|
||||
<View style={styles.previewContent}>
|
||||
{imageUrl ? (
|
||||
<Image style={styles.previewImage} source={{ uri: imageUrl }} />
|
||||
) : (
|
||||
<View style={styles.previewImagePlaceholder}>
|
||||
<Ionicons name="restaurant" size={20} color="#999" />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.imageWrapper}>
|
||||
{imageUrl ? (
|
||||
<Image style={styles.previewImage} source={{ uri: imageUrl }} />
|
||||
) : (
|
||||
<View style={styles.previewImagePlaceholder}>
|
||||
<Ionicons name="restaurant" size={24} color="#94A3B8" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.previewInfo}>
|
||||
<Text style={styles.previewName}>
|
||||
{foodName || '食物名称'}
|
||||
</Text>
|
||||
<Text style={styles.previewCalories}>
|
||||
{actualCalories}{caloriesUnit}/{defaultAmount}g
|
||||
<Text style={styles.previewName} numberOfLines={1}>
|
||||
{foodName || t('createCustomFood.preview.defaultName')}
|
||||
</Text>
|
||||
<View style={styles.previewBadge}>
|
||||
<Ionicons name="flame" size={14} color="#F59E0B" />
|
||||
<Text style={styles.previewCalories}>
|
||||
{actualCalories} {caloriesUnit} / {defaultAmount}
|
||||
{t('createCustomFood.units.g')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -252,21 +294,21 @@ export function CreateCustomFoodModal({
|
||||
{/* 基本信息 */}
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionTitle}>基本信息</Text>
|
||||
<Text style={styles.sectionTitle}>{t('createCustomFood.basicInfo.title')}</Text>
|
||||
<Text style={styles.requiredIndicator}>*</Text>
|
||||
</View>
|
||||
<View style={styles.sectionCard}>
|
||||
{/* 食物名称和单位 */}
|
||||
{/* 食物名称 */}
|
||||
<View style={styles.inputRowContainer}>
|
||||
<Text style={styles.inputRowLabel}>食物名称</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.basicInfo.name')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={foodName}
|
||||
onChangeText={setFoodName}
|
||||
placeholder="例如,汉堡"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholder={t('createCustomFood.basicInfo.namePlaceholder')}
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -274,36 +316,36 @@ export function CreateCustomFoodModal({
|
||||
|
||||
{/* 默认数量 */}
|
||||
<View style={styles.inputRowContainer}>
|
||||
<Text style={styles.inputRowLabel}>默认数量</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.basicInfo.defaultAmount')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={defaultAmount}
|
||||
onChangeText={setDefaultAmount}
|
||||
keyboardType="numeric"
|
||||
placeholder="100"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
<Text style={styles.unitText}>g</Text>
|
||||
<Text style={styles.unitText}>{t('createCustomFood.units.g')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 食物热量 */}
|
||||
<View style={[styles.inputRowContainer, { marginBottom: 0 }]}>
|
||||
<Text style={styles.inputRowLabel}>食物热量</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.basicInfo.calories')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={calories}
|
||||
onChangeText={setCalories}
|
||||
keyboardType="numeric"
|
||||
placeholder="100"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholder="0"
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
<Text style={styles.unitText}>千卡</Text>
|
||||
<Text style={styles.unitText}>{t('createCustomFood.units.kcal')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -312,23 +354,26 @@ export function CreateCustomFoodModal({
|
||||
|
||||
{/* 可选信息 */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>可选信息</Text>
|
||||
<Text style={styles.sectionTitle}>{t('createCustomFood.optionalInfo.title')}</Text>
|
||||
<View style={styles.sectionCard}>
|
||||
{/* 照片 */}
|
||||
<View style={styles.inputRowContainer}>
|
||||
<Text style={styles.inputRowLabel}>照片</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.optionalInfo.photo')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<TouchableOpacity
|
||||
style={styles.modernImageSelector}
|
||||
<TouchableOpacity
|
||||
style={styles.modernImageSelector}
|
||||
onPress={handleSelectImage}
|
||||
disabled={uploading}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{imageUrl ? (
|
||||
<Image style={styles.selectedImage} source={{ uri: imageUrl }} />
|
||||
) : (
|
||||
<View style={styles.modernImagePlaceholder}>
|
||||
<Ionicons name="camera" size={28} color="#A0A0A0" />
|
||||
<Text style={styles.imagePlaceholderText}>添加照片</Text>
|
||||
<Ionicons name="camera-outline" size={28} color="#94A3B8" />
|
||||
<Text style={styles.imagePlaceholderText}>
|
||||
{t('createCustomFood.optionalInfo.addPhoto')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
{uploading && (
|
||||
@@ -342,54 +387,56 @@ export function CreateCustomFoodModal({
|
||||
|
||||
{/* 蛋白质 */}
|
||||
<View style={styles.inputRowContainer}>
|
||||
<Text style={styles.inputRowLabel}>蛋白质</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.optionalInfo.protein')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={protein}
|
||||
onChangeText={setProtein}
|
||||
keyboardType="numeric"
|
||||
placeholder="0"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
<Text style={styles.unitText}>克</Text>
|
||||
<Text style={styles.unitText}>{t('createCustomFood.units.gram')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 脂肪 */}
|
||||
<View style={styles.inputRowContainer}>
|
||||
<Text style={styles.inputRowLabel}>脂肪</Text>
|
||||
<Text style={styles.inputRowLabel}>{t('createCustomFood.optionalInfo.fat')}</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={fat}
|
||||
onChangeText={setFat}
|
||||
keyboardType="numeric"
|
||||
placeholder="0"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
<Text style={styles.unitText}>克</Text>
|
||||
<Text style={styles.unitText}>{t('createCustomFood.units.gram')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 碳水化合物 */}
|
||||
<View style={[styles.inputRowContainer, { marginBottom: 0 }]}>
|
||||
<Text style={styles.inputRowLabel}>碳水化合物</Text>
|
||||
<Text style={styles.inputRowLabel}>
|
||||
{t('createCustomFood.optionalInfo.carbohydrate')}
|
||||
</Text>
|
||||
<View style={styles.inputRowContent}>
|
||||
<View style={styles.numberInputContainer}>
|
||||
<View style={styles.modernInputContainer}>
|
||||
<TextInput
|
||||
style={styles.modernNumberInput}
|
||||
value={carbohydrate}
|
||||
onChangeText={setCarbohydrate}
|
||||
keyboardType="numeric"
|
||||
placeholder="0"
|
||||
placeholderTextColor="#A0A0A0"
|
||||
placeholderTextColor="#94A3B8"
|
||||
/>
|
||||
<Text style={styles.unitText}>克</Text>
|
||||
<Text style={styles.unitText}>{t('createCustomFood.units.gram')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -398,7 +445,7 @@ export function CreateCustomFoodModal({
|
||||
</ScrollView>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
</BlurView>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -408,331 +455,272 @@ const { height: screenHeight } = Dimensions.get('window');
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
keyboardAvoidingView: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
dismissArea: {
|
||||
flex: 1,
|
||||
},
|
||||
modalContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#FFFFFF',
|
||||
marginTop: 50,
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 20,
|
||||
backgroundColor: '#F1F5F9', // Slate 100
|
||||
borderTopLeftRadius: 32,
|
||||
borderTopRightRadius: 32,
|
||||
height: '90%',
|
||||
maxHeight: '90%',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: -4,
|
||||
},
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 12,
|
||||
elevation: 20,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
modalHeaderBar: {
|
||||
width: '100%',
|
||||
height: 24,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#F1F5F9',
|
||||
},
|
||||
dragIndicator: {
|
||||
width: 40,
|
||||
height: 4,
|
||||
backgroundColor: '#CBD5E1',
|
||||
borderRadius: 2,
|
||||
},
|
||||
scrollView: {
|
||||
flex: 1,
|
||||
backgroundColor: '#F1F5F9',
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
paddingHorizontal: 20,
|
||||
paddingBottom: 20,
|
||||
},
|
||||
backButton: {
|
||||
padding: 4,
|
||||
marginLeft: -8,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: '800',
|
||||
color: '#1E293B',
|
||||
textAlign: 'center',
|
||||
marginHorizontal: 20,
|
||||
fontFamily: 'AliBold',
|
||||
},
|
||||
saveButton: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 16,
|
||||
borderRadius: 20,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
saveButtonDisabled: {
|
||||
opacity: 0.5,
|
||||
opacity: 0.6,
|
||||
},
|
||||
saveButtonGradient: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
},
|
||||
saveButtonText: {
|
||||
fontSize: 16,
|
||||
color: Colors.light.primary,
|
||||
fontWeight: '500',
|
||||
},
|
||||
saveButtonTextDisabled: {
|
||||
color: Colors.light.textMuted,
|
||||
fontSize: 14,
|
||||
color: '#FFFFFF',
|
||||
fontWeight: '700',
|
||||
fontFamily: 'AliBold',
|
||||
},
|
||||
previewSection: {
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 16,
|
||||
paddingHorizontal: 20,
|
||||
marginBottom: 24,
|
||||
},
|
||||
previewCard: {
|
||||
backgroundColor: '#F8F9FA',
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginTop: 8,
|
||||
borderRadius: 24,
|
||||
padding: 20,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#FFFFFF',
|
||||
shadowColor: 'rgba(30, 41, 59, 0.08)',
|
||||
shadowOffset: { width: 0, height: 8 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 12,
|
||||
elevation: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,0.6)',
|
||||
},
|
||||
previewHeader: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
previewContent: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
imageWrapper: {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
previewImage: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 4,
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#F8FAFC',
|
||||
},
|
||||
previewImagePlaceholder: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 4,
|
||||
backgroundColor: '#E5E5E5',
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#F1F5F9',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1,
|
||||
borderColor: '#E2E8F0',
|
||||
},
|
||||
previewInfo: {
|
||||
flex: 1,
|
||||
marginLeft: 12,
|
||||
marginLeft: 16,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
previewName: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
color: '#333',
|
||||
marginBottom: 2,
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: '#1E293B',
|
||||
marginBottom: 6,
|
||||
fontFamily: 'AliBold',
|
||||
},
|
||||
previewBadge: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#FFFBEB',
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 8,
|
||||
alignSelf: 'flex-start',
|
||||
gap: 4,
|
||||
},
|
||||
previewCalories: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
fontSize: 13,
|
||||
color: '#D97706',
|
||||
fontWeight: '600',
|
||||
fontFamily: 'AliRegular',
|
||||
},
|
||||
section: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 20,
|
||||
marginBottom: 24,
|
||||
},
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 4,
|
||||
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 14,
|
||||
color: '#333',
|
||||
marginLeft: 8
|
||||
fontWeight: '700',
|
||||
color: '#64748B',
|
||||
fontFamily: 'AliBold',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
requiredIndicator: {
|
||||
fontSize: 16,
|
||||
color: '#FF4444',
|
||||
fontSize: 14,
|
||||
color: '#EF4444',
|
||||
marginLeft: 4,
|
||||
},
|
||||
inputGroup: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
inputRowGroup: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
marginBottom: 20,
|
||||
},
|
||||
inputRowItem: {
|
||||
flex: 1,
|
||||
},
|
||||
inputLabel: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
fontWeight: '500',
|
||||
},
|
||||
modernTextInput: {
|
||||
flex: 1,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
fontSize: 16,
|
||||
marginLeft: 20,
|
||||
color: '#333',
|
||||
backgroundColor: '#FFFFFF',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
},
|
||||
numberInputContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderRadius: 12,
|
||||
|
||||
backgroundColor: '#FFFFFF',
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
},
|
||||
modernNumberInput: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
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: 14,
|
||||
color: 'gray',
|
||||
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,
|
||||
fontWeight: '700',
|
||||
},
|
||||
sectionCard: {
|
||||
backgroundColor: '#F8F9FA',
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginTop: 8,
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderRadius: 24,
|
||||
padding: 20,
|
||||
shadowColor: 'rgba(30, 41, 59, 0.05)',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 10,
|
||||
elevation: 2,
|
||||
},
|
||||
// 新增行布局样式
|
||||
inputRowContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
marginBottom: 16,
|
||||
},
|
||||
inputRowLabel: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
fontWeight: '500',
|
||||
width: 80,
|
||||
fontSize: 15,
|
||||
color: '#475569',
|
||||
fontWeight: '600',
|
||||
width: 90,
|
||||
marginRight: 12,
|
||||
fontFamily: 'AliRegular',
|
||||
},
|
||||
inputRowContent: {
|
||||
flex: 1,
|
||||
},
|
||||
imageLoadingOverlay: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
modernInputContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderRadius: 16,
|
||||
backgroundColor: '#F8FAFC',
|
||||
borderWidth: 1,
|
||||
borderColor: '#E2E8F0',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
modernNumberInput: {
|
||||
flex: 1,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
fontSize: 16,
|
||||
color: '#1E293B',
|
||||
textAlign: 'right',
|
||||
fontFamily: 'AliRegular',
|
||||
},
|
||||
unitText: {
|
||||
fontSize: 14,
|
||||
color: '#94A3B8',
|
||||
paddingRight: 16,
|
||||
minWidth: 40,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
},
|
||||
modernImageSelector: {
|
||||
alignSelf: 'flex-end',
|
||||
borderRadius: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
},
|
||||
selectedImage: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 20,
|
||||
},
|
||||
modernImagePlaceholder: {
|
||||
width: 72,
|
||||
height: 72,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#F8FAFC',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: '#E2E8F0',
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
imagePlaceholderText: {
|
||||
fontSize: 11,
|
||||
color: '#94A3B8',
|
||||
marginTop: 4,
|
||||
fontWeight: '600',
|
||||
textAlign: 'center',
|
||||
},
|
||||
imageLoadingOverlay: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(0,0,0,0.3)',
|
||||
borderRadius: 20,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user