You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
636 lines
19 KiB
636 lines
19 KiB
import React, { useState, useEffect } from 'react'; |
|
import { View, Text, Image, TouchableOpacity, StyleSheet, ImageBackground, ActivityIndicator, SafeAreaView, StatusBar, Platform, Modal, TextInput, FlatList } from 'react-native'; |
|
import BackIcon from '../../components/BackIcon'; |
|
import fontSize from '../../utils/fontsizeUtils'; |
|
import widthUtils from '../../utils/widthUtils'; |
|
import { useNavigation } from '@react-navigation/native'; |
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; |
|
import { settingApi } from '../../services/api/setting'; |
|
import { CountryList } from '../../constants/countries'; |
|
|
|
type RootStackParamList = { |
|
Home: undefined; |
|
ShippingDetails: undefined; |
|
Main: undefined; |
|
// Add other screens as needed |
|
}; |
|
|
|
type NavigationProp = NativeStackNavigationProp<RootStackParamList>; |
|
|
|
export const ShippingDetailsSection = () => { |
|
const [shippingMethod, setShippingMethod] = useState('maritime'); // 'maritime' or 'airway' |
|
const [isLoading, setIsLoading] = useState(true); |
|
const [showCountryModal, setShowCountryModal] = useState(false); |
|
const [countries, setCountries] = useState<CountryList[]>([]); |
|
const [selectedCountry, setSelectedCountry] = useState<CountryList | null>(null); |
|
const [parcelVolume, setParcelVolume] = useState(''); |
|
const [isCalculating, setIsCalculating] = useState(false); |
|
const [showResultModal, setShowResultModal] = useState(false); |
|
const [shippingFee, setShippingFee] = useState<number | null>(null); |
|
const [shippingCurrency, setShippingCurrency] = useState<string>(''); |
|
const navigation = useNavigation<NavigationProp>(); |
|
|
|
useEffect(() => { |
|
const preloadImages = async () => { |
|
try { |
|
await Promise.all([ |
|
Image.prefetch(Image.resolveAssetSource(require('../../../assets/img/image_f67fe9bc.png')).uri), |
|
Image.prefetch(Image.resolveAssetSource(require('../../../assets/img/feijiback.png')).uri) |
|
]); |
|
setIsLoading(false); |
|
} catch (error) { |
|
console.error('Error preloading images:', error); |
|
setIsLoading(false); |
|
} |
|
}; |
|
|
|
const fetchCountries = async () => { |
|
try { |
|
const response = await settingApi.getCountryList(); |
|
setCountries(response); |
|
} catch (error) { |
|
console.error('Error fetching countries:', error); |
|
} |
|
}; |
|
|
|
preloadImages(); |
|
fetchCountries(); |
|
}, []); |
|
|
|
const handleShippingMethodChange = (method: 'maritime' | 'airway') => { |
|
setShippingMethod(method); |
|
}; |
|
|
|
const handleCountrySelect = (country: CountryList) => { |
|
setSelectedCountry(country); |
|
setShowCountryModal(false); |
|
}; |
|
|
|
// Get background image based on shipping method |
|
const getBackgroundImage = () => { |
|
return shippingMethod === 'maritime' |
|
? require('../../../assets/img/image_f67fe9bc.png') |
|
: require('../../../assets/img/feijiback.png'); |
|
}; |
|
|
|
const handleCalculateShipping = async () => { |
|
if (!selectedCountry || !parcelVolume) { |
|
// TODO: Show error message |
|
return; |
|
} |
|
|
|
setIsCalculating(true); |
|
try { |
|
const volume = parseFloat(parcelVolume); |
|
if (isNaN(volume)) { |
|
// TODO: Show error message for invalid input |
|
return; |
|
} |
|
|
|
const shippingData = { |
|
weight_kg: shippingMethod === 'airway' ? volume : 0, |
|
volume_m3: shippingMethod === 'maritime' ? volume : 0, |
|
country_code: selectedCountry.country |
|
}; |
|
|
|
const response = await settingApi.getShippingFee(shippingData); |
|
const fee = shippingMethod === 'maritime' |
|
? response.estimated_shipping_fee_sea |
|
: response.estimated_shipping_fee_air; |
|
setShippingFee(fee); |
|
setShippingCurrency(response.currency); |
|
setShowResultModal(true); |
|
} catch (error) { |
|
console.error('Error calculating shipping fee:', error); |
|
// TODO: Show error message |
|
} finally { |
|
setIsCalculating(false); |
|
} |
|
}; |
|
|
|
const renderCountryItem = ({ item }: { item: CountryList }) => ( |
|
<TouchableOpacity |
|
style={styles.countryItem} |
|
onPress={() => handleCountrySelect(item)} |
|
> |
|
<Text style={styles.countryItemText}>{item.name}</Text> |
|
{selectedCountry?.country === item.country && ( |
|
<Text style={styles.checkIcon}>✓</Text> |
|
)} |
|
</TouchableOpacity> |
|
); |
|
|
|
const renderResultModal = () => ( |
|
<Modal |
|
visible={showResultModal} |
|
transparent={true} |
|
animationType="fade" |
|
onRequestClose={() => setShowResultModal(false)} |
|
> |
|
<View style={styles.resultModalContainer}> |
|
<View style={styles.resultModalContent}> |
|
<Text style={styles.resultTitle}>运费计算结果</Text> |
|
<Text style={styles.resultText}> |
|
预计运费:{shippingFee?.toFixed(2)} {shippingCurrency} |
|
</Text> |
|
<TouchableOpacity |
|
style={styles.resultCloseButton} |
|
onPress={() => setShowResultModal(false)} |
|
> |
|
<Text style={styles.resultCloseButtonText}>确定</Text> |
|
</TouchableOpacity> |
|
</View> |
|
</View> |
|
</Modal> |
|
); |
|
|
|
return ( |
|
<SafeAreaView style={styles.safeArea}> |
|
<StatusBar barStyle="light-content" backgroundColor="#005EE4" /> |
|
<View style={styles.safeAreaContent}> |
|
{isLoading ? ( |
|
<View style={styles.loadingContainer}> |
|
<ActivityIndicator size="large" color="#005EE4" /> |
|
</View> |
|
) : ( |
|
<ImageBackground |
|
source={getBackgroundImage()} |
|
style={styles.backgroundImage} |
|
> |
|
<View style={styles.contentContainer}> |
|
<View style={styles.timeShippingSection}> |
|
<View style={styles.shippingCostContainer}> |
|
<View style={styles.svgContainer}> |
|
<TouchableOpacity |
|
style={styles.backButton} |
|
onPress={() => { |
|
navigation.goBack(); |
|
}} |
|
> |
|
<BackIcon size={fontSize(18)} color="#ffffff"/> |
|
</TouchableOpacity> |
|
</View> |
|
<Text style={styles.shippingCostLabelTextStyle}>运费</Text> |
|
</View> |
|
</View> |
|
|
|
{/* Calculator Form */} |
|
<View style={styles.shippingCalculatorContainer}> |
|
<View style={styles.flexColumnCenteredWithSelect}> |
|
{/* Shipping Method Selection */} |
|
<View style={styles.shippingMethodContainer}> |
|
{/* Maritime Option */} |
|
<TouchableOpacity |
|
style={[ |
|
styles.maritimeInfoBox, |
|
shippingMethod === 'maritime' ? styles.activeShippingMethod : {} |
|
]} |
|
onPress={() => handleShippingMethodChange('maritime')} |
|
> |
|
<View style={styles.svgContainer1}> |
|
<Image |
|
source={require('../../../assets/img/海运 (1) 1.png')} |
|
style={{width: widthUtils(24, 24).width, height: widthUtils(24, 24).height, |
|
tintColor: shippingMethod === 'maritime' ? '#fff' : '#747474'}} |
|
/> |
|
</View> |
|
<Text style={[ |
|
styles.maritimeHeadingStyle, |
|
shippingMethod === 'maritime' ? {} : {color: '#747474'} |
|
]}>海运</Text> |
|
</TouchableOpacity> |
|
|
|
{/* Airway Option */} |
|
<TouchableOpacity |
|
style={[ |
|
styles.airwayInfoContainer, |
|
shippingMethod === 'airway' ? styles.activeShippingMethod : {} |
|
]} |
|
onPress={() => handleShippingMethodChange('airway')} |
|
> |
|
<View style={styles.svgContainer2}> |
|
<Image |
|
source={require('../../../assets/img/空运 1.png')} |
|
style={{width: widthUtils(24, 24).width, height: widthUtils(24, 24).height, tintColor: shippingMethod === 'airway' ? '#fff' : '#747474'}} |
|
/> |
|
</View> |
|
<Text style={[ |
|
styles.flightModeLabel, |
|
shippingMethod === 'airway' ? {color: '#fff'} : {} |
|
]}>空运</Text> |
|
</TouchableOpacity> |
|
</View> |
|
|
|
{/* Parcel Volume Form */} |
|
<View style={styles.parcelVolumeFormContainer}> |
|
{/* Country Selection */} |
|
<View style={styles.parcelVolumeContainer}> |
|
<Text style={styles.parcelVolumeLabel}>选择国家</Text> |
|
<TouchableOpacity |
|
style={styles.volumeInputContainer} |
|
onPress={() => setShowCountryModal(true)} |
|
> |
|
<Text style={styles.volumePromptTextStyle}> |
|
{selectedCountry ? selectedCountry.name : '请选择国家'} |
|
</Text> |
|
</TouchableOpacity> |
|
</View> |
|
|
|
{/* Parcel Volume Input */} |
|
<View style={styles.parcelVolumeContainer}> |
|
<Text style={styles.parcelVolumeLabel}>包裹体积</Text> |
|
<View style={styles.volumeInputContainer}> |
|
<TextInput |
|
style={styles.volumeInput} |
|
value={parcelVolume} |
|
onChangeText={(text) => { |
|
// 只允许输入数字和小数点 |
|
const numericValue = text.replace(/[^0-9.]/g, ''); |
|
// 确保只有一个小数点 |
|
const parts = numericValue.split('.'); |
|
if (parts.length > 2) { |
|
setParcelVolume(parts[0] + '.' + parts.slice(1).join('')); |
|
} else { |
|
setParcelVolume(numericValue); |
|
} |
|
}} |
|
placeholder="请输入包裹体积" |
|
keyboardType="decimal-pad" |
|
placeholderTextColor="#807e7e" |
|
/> |
|
<Text style={styles.volumeLabel}>{shippingMethod === 'maritime' ? 'm³' : 'kg'}</Text> |
|
</View> |
|
</View> |
|
</View> |
|
|
|
{/* Submit Button */} |
|
<TouchableOpacity |
|
style={[styles.primaryButton, isCalculating && styles.disabledButton]} |
|
onPress={handleCalculateShipping} |
|
disabled={isCalculating || !selectedCountry || !parcelVolume} |
|
> |
|
{isCalculating ? ( |
|
<ActivityIndicator color="white" /> |
|
) : ( |
|
<Text style={{color: 'white', fontSize: fontSize(16), fontFamily: 'PingFang SC', fontWeight: '600'}}> |
|
计算运费 |
|
</Text> |
|
)} |
|
</TouchableOpacity> |
|
</View> |
|
</View> |
|
</View> |
|
</ImageBackground> |
|
)} |
|
|
|
{/* Country Selection Modal */} |
|
<Modal |
|
visible={showCountryModal} |
|
animationType="slide" |
|
transparent={true} |
|
onRequestClose={() => setShowCountryModal(false)} |
|
> |
|
<View style={styles.modalContainer}> |
|
<View style={styles.modalContent}> |
|
<View style={styles.modalHeader}> |
|
<Text style={styles.modalTitle}>选择国家</Text> |
|
<TouchableOpacity onPress={() => setShowCountryModal(false)}> |
|
<Text style={styles.modalCloseButton}>关闭</Text> |
|
</TouchableOpacity> |
|
</View> |
|
<FlatList |
|
data={countries} |
|
renderItem={renderCountryItem} |
|
keyExtractor={(item) => item.country.toString()} |
|
style={styles.countryList} |
|
/> |
|
</View> |
|
</View> |
|
</Modal> |
|
|
|
{renderResultModal()} |
|
</View> |
|
</SafeAreaView> |
|
); |
|
}; |
|
|
|
const styles = StyleSheet.create({ |
|
safeArea: { |
|
flex: 1, |
|
backgroundColor: '#005EE4', |
|
}, |
|
safeAreaContent: { |
|
flex: 1, |
|
paddingTop: 0, |
|
}, |
|
container: { |
|
width: '100%', |
|
flex: 1, |
|
}, |
|
backgroundImage: { |
|
width: '100%', |
|
height: '100%', |
|
resizeMode: 'cover', |
|
}, |
|
contentContainer: { |
|
flex: 1, |
|
padding: 15, |
|
}, |
|
timeShippingSection: { |
|
flexDirection: 'column', |
|
}, |
|
timeAndImageContainer: { |
|
flexDirection: 'row', |
|
gap: 8, |
|
alignItems: 'center', |
|
justifyContent: 'space-between', |
|
paddingLeft: 36, |
|
}, |
|
timeDisplay: { |
|
width: widthUtils(42, 42).width, |
|
fontSize: fontSize(17), |
|
fontFamily: 'SF Pro', |
|
fontWeight: '600', |
|
color: 'white', |
|
textAlign: 'center', |
|
}, |
|
timeImageContainer: { |
|
width: widthUtils(54, 154).width, |
|
height: widthUtils(54, 154).height, |
|
}, |
|
shippingCostContainer: { |
|
flexDirection: 'row', |
|
alignItems: 'center', |
|
marginTop: 20, |
|
justifyContent: 'center', |
|
}, |
|
svgContainer: { |
|
width: widthUtils(18, 18).width, |
|
height: widthUtils(18, 18).height, |
|
position: 'absolute', |
|
left: 0, |
|
}, |
|
shippingCostLabelTextStyle: { |
|
fontSize: fontSize(20), |
|
lineHeight: 22, |
|
fontFamily: 'Microsoft YaHei UI', |
|
fontWeight: '700', |
|
color: 'white', |
|
textAlign: 'center', |
|
textTransform: 'capitalize', |
|
width: '100%', |
|
}, |
|
shippingCalculatorContainer: { |
|
marginTop: widthUtils(100,100).height, |
|
paddingTop: 18, |
|
paddingRight: 20, |
|
paddingBottom: 28, |
|
paddingLeft: 20, |
|
backgroundColor: 'white', |
|
borderRadius: 5, |
|
justifyContent: 'center', |
|
}, |
|
flexColumnCenteredWithSelect: { |
|
flexDirection: 'column', |
|
justifyContent: 'center', |
|
}, |
|
shippingMethodContainer: { |
|
flexDirection: 'row', |
|
alignItems: 'center', |
|
justifyContent: 'center', |
|
height: widthUtils(80, 80).height, |
|
paddingRight: 10, |
|
paddingLeft: 10, |
|
backgroundColor: '#f2f6ff', |
|
borderRadius: 5, |
|
}, |
|
maritimeInfoBox: { |
|
height: widthUtils(60, 60).height, |
|
paddingRight: 19, |
|
paddingLeft: 20, |
|
backgroundColor: '#f2f6ff', |
|
borderRadius: 5, |
|
justifyContent: 'center', |
|
alignItems: 'center', |
|
}, |
|
airwayInfoContainer: { |
|
height: widthUtils(60, 60).height, |
|
paddingRight: 24, |
|
paddingLeft: 25, |
|
marginLeft: 10, |
|
backgroundColor: '#f2f6ff', |
|
borderRadius: 5, |
|
justifyContent: 'center', |
|
alignItems: 'center', |
|
}, |
|
activeShippingMethod: { |
|
backgroundColor: '#005EE4', |
|
}, |
|
svgContainer1: { |
|
width: widthUtils(32, 32).width, |
|
height: widthUtils(32, 32).height, |
|
}, |
|
maritimeHeadingStyle: { |
|
marginTop: -5, |
|
fontSize: fontSize(14), |
|
fontFamily: 'Segoe UI', |
|
fontWeight: '700', |
|
color: 'white', |
|
textTransform: 'capitalize', |
|
}, |
|
svgContainer2: { |
|
width: widthUtils(24, 24).width, |
|
height: widthUtils(24, 24).height, |
|
}, |
|
flightModeLabel: { |
|
marginTop: -2, |
|
fontSize: fontSize(14), |
|
fontFamily: 'Segoe UI', |
|
fontWeight: '400', |
|
color: '#747474', |
|
textTransform: 'capitalize', |
|
}, |
|
parcelVolumeFormContainer: { |
|
marginTop: 16, |
|
backgroundColor: 'white', |
|
borderWidth: 1, |
|
borderColor: '#dbdce0', |
|
}, |
|
optionContainer: { |
|
flexDirection: 'row', |
|
alignItems: 'center', |
|
justifyContent: 'center', |
|
height: widthUtils(60, 60).height, |
|
paddingRight: 6, |
|
paddingLeft: 8, |
|
backgroundColor: 'white', |
|
borderWidth: 1, |
|
borderColor: '#dbdce0', |
|
borderTopLeftRadius: 5, |
|
borderTopRightRadius: 5, |
|
}, |
|
pingfangTextSelect: { |
|
fontSize: fontSize(12), |
|
fontFamily: 'PingFang SC', |
|
fontWeight: '500', |
|
color: '#646472', |
|
}, |
|
iconSelectDropdown: { |
|
width: widthUtils(24, 24).width, |
|
height: widthUtils(24, 24).height, |
|
marginLeft: 10, |
|
}, |
|
parcelVolumeContainer: { |
|
width: '100%', |
|
paddingTop: 6, |
|
paddingRight: 8, |
|
paddingBottom: 10, |
|
paddingLeft: 8, |
|
marginTop: -1, |
|
backgroundColor: 'white', |
|
borderWidth: 1, |
|
borderColor: '#dbdce0', |
|
borderBottomLeftRadius: 5, |
|
borderBottomRightRadius: 5, |
|
}, |
|
parcelVolumeLabel: { |
|
fontSize: fontSize(12), |
|
fontFamily: 'PingFang SC', |
|
fontWeight: '600', |
|
color: '#646472', |
|
}, |
|
volumeInputContainer: { |
|
flexDirection: 'row', |
|
gap: 8, |
|
alignItems: 'center', |
|
justifyContent: 'space-between', |
|
width: '100%', |
|
}, |
|
volumePromptTextStyle: { |
|
fontSize: fontSize(16), |
|
lineHeight: 22, |
|
fontFamily: 'PingFang SC', |
|
fontWeight: '400', |
|
color: '#807e7e', |
|
}, |
|
volumeLabel: { |
|
fontSize: fontSize(16), |
|
lineHeight: 22, |
|
fontFamily: 'PingFang SC', |
|
fontWeight: '600', |
|
color: '#1c284e', |
|
}, |
|
primaryButton: { |
|
width:'100%', |
|
height: widthUtils(50, 50).height, |
|
marginTop: 34, |
|
backgroundColor: '#002fa7', |
|
borderRadius: 25, |
|
justifyContent: 'center', |
|
alignItems: 'center', |
|
}, |
|
backButton: { |
|
padding: 10, |
|
borderRadius: 20, |
|
zIndex: 10, |
|
}, |
|
loadingContainer: { |
|
flex: 1, |
|
justifyContent: 'center', |
|
alignItems: 'center', |
|
backgroundColor: '#f2f6ff', |
|
}, |
|
modalContainer: { |
|
flex: 1, |
|
backgroundColor: 'rgba(0, 0, 0, 0.5)', |
|
justifyContent: 'flex-end', |
|
}, |
|
modalContent: { |
|
backgroundColor: 'white', |
|
borderTopLeftRadius: 20, |
|
borderTopRightRadius: 20, |
|
paddingBottom: Platform.OS === 'ios' ? 34 : 20, |
|
maxHeight: '80%', |
|
}, |
|
modalHeader: { |
|
flexDirection: 'row', |
|
justifyContent: 'space-between', |
|
alignItems: 'center', |
|
padding: 16, |
|
borderBottomWidth: 1, |
|
borderBottomColor: '#dbdce0', |
|
}, |
|
modalTitle: { |
|
fontSize: fontSize(18), |
|
fontWeight: '600', |
|
color: '#1c284e', |
|
}, |
|
modalCloseButton: { |
|
fontSize: fontSize(16), |
|
color: '#005EE4', |
|
}, |
|
countryList: { |
|
paddingHorizontal: 16, |
|
}, |
|
countryItem: { |
|
paddingVertical: 16, |
|
borderBottomWidth: 1, |
|
borderBottomColor: '#dbdce0', |
|
flexDirection: 'row', |
|
justifyContent: 'space-between', |
|
alignItems: 'center', |
|
}, |
|
countryItemText: { |
|
fontSize: fontSize(16), |
|
color: '#1c284e', |
|
}, |
|
checkIcon: { |
|
fontSize: fontSize(20), |
|
color: '#005EE4', |
|
fontWeight: 'bold', |
|
}, |
|
volumeInput: { |
|
flex: 1, |
|
fontSize: fontSize(16), |
|
color: '#1c284e', |
|
padding: 0, |
|
}, |
|
disabledButton: { |
|
opacity: 0.6, |
|
}, |
|
resultModalContainer: { |
|
flex: 1, |
|
backgroundColor: 'rgba(0, 0, 0, 0.5)', |
|
justifyContent: 'center', |
|
alignItems: 'center', |
|
}, |
|
resultModalContent: { |
|
backgroundColor: 'white', |
|
borderRadius: 10, |
|
padding: 20, |
|
width: '80%', |
|
alignItems: 'center', |
|
}, |
|
resultTitle: { |
|
fontSize: fontSize(18), |
|
fontWeight: '600', |
|
color: '#1c284e', |
|
marginBottom: 15, |
|
}, |
|
resultText: { |
|
fontSize: fontSize(16), |
|
color: '#1c284e', |
|
marginBottom: 20, |
|
}, |
|
resultCloseButton: { |
|
backgroundColor: '#005EE4', |
|
paddingVertical: 10, |
|
paddingHorizontal: 30, |
|
borderRadius: 5, |
|
}, |
|
resultCloseButtonText: { |
|
color: 'white', |
|
fontSize: fontSize(16), |
|
fontWeight: '600', |
|
}, |
|
});
|
|
|