From 62c2e2302c444fa5f0cde0857306ba3bec0517d3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 14 May 2025 17:19:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=88=9B=E5=BB=BA=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + App.tsx | 11 + app.json | 7 +- app/screens/CartScreen.tsx | 17 +- app/screens/ChatScreen.tsx | 1 + app/screens/ProductCard.tsx | 6 + app/screens/ProductDetailScreen.tsx | 4 +- app/screens/previewOrder/PaymentMethod.tsx | 528 ++++++++++++++++++--- app/screens/previewOrder/ShippingFee.tsx | 51 +- app/screens/previewOrder/perviewOrder.tsx | 185 ++++++++ app/services/api/apiClient.ts | 12 +- app/services/api/orders.ts | 2 +- app/services/api/payApi.ts | 12 + app/services/api/productApi.ts | 10 +- app/store/createOrder.ts | 2 +- app/store/previewShipping.ts | 4 +- app/types/createOrder.ts | 113 +++++ package.json | 1 + yarn.lock | 10 +- 19 files changed, 881 insertions(+), 96 deletions(-) create mode 100644 app/screens/previewOrder/perviewOrder.tsx create mode 100644 app/types/createOrder.ts diff --git a/.gitignore b/.gitignore index d16e1ef..15c7049 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ yarn-error.* # typescript *.tsbuildinfo +.aider* diff --git a/App.tsx b/App.tsx index 534762d..a6c3237 100644 --- a/App.tsx +++ b/App.tsx @@ -47,6 +47,7 @@ import { AddAddress } from "./app/screens/address/AddAddress"; import { EditAddress } from "./app/screens/address/EditAddress"; import { PaymentMethod } from "./app/screens/previewOrder/PaymentMethod"; import { ShippingFee } from "./app/screens/previewOrder/ShippingFee"; +import { PreviewOrder } from "./app/screens/previewOrder/perviewOrder"; export type RootStackParamList = { CountrySelect: undefined; @@ -84,6 +85,7 @@ export type RootStackParamList = { EditAddress: undefined; PaymentMethod: undefined; ShippingFee: undefined; + PreviewOrder: undefined; }; const Stack = createNativeStackNavigator(); @@ -427,6 +429,15 @@ function AppContent() { gestureDirection: "horizontal", }} /> + diff --git a/app.json b/app.json index 6185afb..1e97d03 100644 --- a/app.json +++ b/app.json @@ -53,7 +53,12 @@ "favicon": "./assets/favicon.png" }, "plugins": [ - "expo-video" + "expo-video", + ["expo-build-properties", { + "android": { + "usesCleartextTraffic": true + } + }] ], "extra": { "eas": { diff --git a/app/screens/CartScreen.tsx b/app/screens/CartScreen.tsx index aa88e10..18cde05 100644 --- a/app/screens/CartScreen.tsx +++ b/app/screens/CartScreen.tsx @@ -28,8 +28,11 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { useFocusEffect } from "@react-navigation/native"; import { useCallback } from "react"; import useCreateOrderStore from "../store/createOrder"; +import useUserStore from "../store/user"; + export const CartScreen = () => { const [cartList, setCartList] = useState([]); + const {user:{user_id}} = useUserStore(); const [selectedItems, setSelectedItems] = useState<{ [key: string]: boolean; }>({}); @@ -205,6 +208,10 @@ export const CartScreen = () => { }; const getCart = async () => { + if(!user_id){ + Alert.alert("暂无数据", "请先登录"); + return; + } const res = await getCartList(); setCartList(res.items); calculateTotalAmount(res.items); @@ -323,7 +330,7 @@ export const CartScreen = () => { }; // useEffect(() => { - // getCart(); + // }, []); useFocusEffect( useCallback(() => { @@ -332,6 +339,10 @@ export const CartScreen = () => { ); const gotoOrder = () => { + if(!user_id){ + Alert.alert("添加失败", "请先登录"); + return; + } const items: { cart_item_id: number }[] = []; cartList.forEach((item) => { item.skus.forEach((sku) => { @@ -344,6 +355,10 @@ export const CartScreen = () => { } }); }); + if(items.length === 0){ + Alert.alert("添加失败", "请先选择商品"); + return; + } setItems(items); navigation.navigate("PreviewAddress"); }; diff --git a/app/screens/ChatScreen.tsx b/app/screens/ChatScreen.tsx index 5707d05..d320482 100644 --- a/app/screens/ChatScreen.tsx +++ b/app/screens/ChatScreen.tsx @@ -16,6 +16,7 @@ import { useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RootStackParamList } from "../../App"; + interface Message { id: string; text: string; diff --git a/app/screens/ProductCard.tsx b/app/screens/ProductCard.tsx index 3a1b5a3..9c2d200 100644 --- a/app/screens/ProductCard.tsx +++ b/app/screens/ProductCard.tsx @@ -27,6 +27,7 @@ import { useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import ImageView from "react-native-image-viewing"; import useProductCartStore from "../store/productCart"; +import useUserStore from "../store/user"; interface ProductCardProps { onClose: () => void; @@ -48,6 +49,7 @@ const ProductCard: React.FC = ({ const [images, setImages] = useState([]); const [currentImageIndex, setCurrentImageIndex] = useState(0); const [imageViewerVisible, setImageViewerVisible] = useState(false); + const {user:{user_id}} = useUserStore(); const { product, @@ -86,6 +88,10 @@ const ProductCard: React.FC = ({ // 加入购物车 const addCartHandel = () => { + if(!user_id){ + Alert.alert("添加失败", "请先登录"); + return; + } if (totalPrice === 0) { Alert.alert("添加失败", "请选择商品"); return; diff --git a/app/screens/ProductDetailScreen.tsx b/app/screens/ProductDetailScreen.tsx index 42406f1..9ec6c54 100644 --- a/app/screens/ProductDetailScreen.tsx +++ b/app/screens/ProductDetailScreen.tsx @@ -261,7 +261,7 @@ export const ProductDetailScreen = () => { console.log('开始时间:', startTime); setIsLoading(true); try { - const res = await productApi.getProductDetail(route.params.offer_id,userStore.user.user_id); + const res = await productApi.getProductDetail(route.params.offer_id, userStore.user?.user_id); console.log('API 请求完成'); if (res.skus != null) { const priceSelectedSku = res.skus.find( @@ -325,7 +325,7 @@ export const ProductDetailScreen = () => { } }; const getSimilars = () => { - productApi.getSimilarProducts(route.params.offer_id,userStore.user.user_id).then((res) => { + productApi.getSimilarProducts(route.params.offer_id, userStore.user?.user_id).then((res) => { setSimilars(res); setIsSimilarsFlag(true); }); diff --git a/app/screens/previewOrder/PaymentMethod.tsx b/app/screens/previewOrder/PaymentMethod.tsx index 8b981d8..09cd816 100644 --- a/app/screens/previewOrder/PaymentMethod.tsx +++ b/app/screens/previewOrder/PaymentMethod.tsx @@ -12,22 +12,33 @@ import { import { payApi, PaymentMethodsResponse } from "../../services/api/payApi"; import fontSize from "../../utils/fontsizeUtils"; import BackIcon from "../../components/BackIcon"; -import { useNavigation } from "@react-navigation/native"; +import { useNavigation, NavigationProp } from "@react-navigation/native"; import widthUtils from "../../utils/widthUtils"; import useOrderStore from "../../store/order"; import useCreateOrderStore from "../../store/createOrder"; import { useRoute, RouteProp } from "@react-navigation/native"; import useUserStore from "../../store/user"; +import { createOrderDataType } from "../../types/createOrder"; import { ordersApi, - OrderData, - AddressDataItem, - DomesticShippingFeeData, + OrderData, CreateOrderRequest, } from "../../services/api/orders"; + // Define route params type type PaymentMethodRouteParams = { - countryCode?: string; + freight_forwarder_address_id?: number; +}; + +// Define the root navigation params +type RootStackParamList = { + PreviewOrder: undefined; + Pay: { order_id: string }; + ShippingFee: { freight_forwarder_address_id?: number }; + PaymentMethod: { freight_forwarder_address_id?: number }; + PreviewAddress: undefined; + AddressList: undefined; + // Add other routes as needed }; interface PaymentOption { @@ -43,45 +54,126 @@ interface PaymentTab { options: PaymentOption[]; } -interface PaymentMethodProps { - onSelectPayment?: (paymentMethod: string) => void; - selectedPayment?: string | null; -} - const PaymentMethodItem = ({ option, isSelected, onSelect, + selectedCurrency, + onSelectCurrency, + exchangeRates, + totalAmount, + convertedAmount, + isConverting, }: { option: PaymentOption; isSelected: boolean; onSelect: () => void; + selectedCurrency?: string; + onSelectCurrency?: (currency: string) => void; + exchangeRates?: { + usd: number; + eur: number; + }; + totalAmount?: number; + convertedAmount?: number; + isConverting?: boolean; }) => ( - - - - {option.icon} - {option.label} - - {Array.isArray(option.value) && option.value.length > 0 && ( - - {option.value.map((op: string) => ( - - {op} - - ))} + + + + + {option.icon} + {option.label} - )} - - - 0 && ( + + {option.value.map((op: string) => ( + + {op} + + ))} + + )} + + + + + + + {/* Show currency selector directly under PayPal when selected */} + {isSelected && option.label === "Paypal" && selectedCurrency && onSelectCurrency && exchangeRates && totalAmount && ( + + )} + +); + +// Currency selector component +interface CurrencySelectorProps { + selectedCurrency: string; + onSelectCurrency: (currency: string) => void; + exchangeRates: { + usd: number; + eur: number; + }; + totalAmount: number; + convertedAmount?: number; + isConverting?: boolean; +} + +const CurrencySelector = ({ + selectedCurrency, + onSelectCurrency, + exchangeRates, + totalAmount, + convertedAmount = 0, + isConverting = false, +}: CurrencySelectorProps) => ( + + Select Currency + + onSelectCurrency("USD")} + > + USD + + onSelectCurrency("EUR")} + > + EUR + + + + + {!isConverting && ( + + {selectedCurrency === "USD" ? "$" : "€"}{convertedAmount} + + )} + {isConverting && ( + + )} - + ); export const PaymentMethod = () => { @@ -101,27 +193,77 @@ export const PaymentMethod = () => { const [paymentMethods, setPaymentMethods] = useState(); const [selectedPayment, setSelectedPayment] = useState(null); - const navigation = useNavigation(); + const navigation = useNavigation>(); const route = useRoute, string>>(); const [expanded, setExpanded] = useState(false); const order = useOrderStore((state) => state.order); const [previewOrder, setPreviewOrder] = useState(); const [loading, setLoading] = useState(false); - const state = useUserStore(); - - const { items,orderData } = useCreateOrderStore(); + const { user } = useUserStore(); + const [createOrderData, setCreateOrderData] = useState(); + const { items, orderData,setOrderData } = useCreateOrderStore(); + const [selectedCurrency, setSelectedCurrency] = useState("USD"); + const [convertedAmount, setConvertedAmount] = useState(0); + const [isConverting, setIsConverting] = useState(false); + const [exchangeRates] = useState({ + usd: 580.00, + eur: 655.96 + }); + const [totalAmount, setTotalAmount] = useState(121.97); const toggleExpanded = () => { setExpanded(!expanded); }; const onSelectPayment = (paymentId: string) => { + if(paymentId === "Paypal"){ + setIsConverting(true); + payApi.convertCurrency({ + from_currency: user.currency, + to_currency: selectedCurrency, + amount: ( + (previewOrder?.total_amount || 0) + + (createOrderData?.domestic_shipping_fee || 0) + + (createOrderData?.shipping_fee || 0) + ), + }).then((res) => { + setConvertedAmount(res.converted_amount); + setIsConverting(false); + }).catch(error => { + console.error("Currency conversion failed:", error); + setIsConverting(false); + }); + } + setSelectedPayment(paymentId); }; + const onSelectCurrency = (currency: string) => { + setSelectedCurrency(currency); + + // Call the API to convert the currency + setIsConverting(true); + payApi.convertCurrency({ + from_currency: user.currency, + to_currency: currency, + amount: ( + (previewOrder?.total_amount || 0) + + (createOrderData?.domestic_shipping_fee || 0) + + (createOrderData?.shipping_fee || 0) + ), + }).then((res) => { + setConvertedAmount(res.converted_amount); + setIsConverting(false); + }).catch(error => { + console.error("Currency conversion failed:", error); + setIsConverting(false); + }); + }; + const getPaymentMethods = async () => { try { const response = await payApi.getCountryPaymentMethods(); + setPaymentMethods(response); // 设置默认支付方式选项 setTabs([ @@ -167,9 +309,9 @@ export const PaymentMethod = () => { useEffect(() => { setLoading(true); - if (route.params?.countryCode) { + if (route.params?.freight_forwarder_address_id) { const data = { - country_code: route.params.countryCode, + country_code: route.params.freight_forwarder_address_id, items: items, }; ordersApi @@ -183,10 +325,62 @@ export const PaymentMethod = () => { Alert.alert("Error", "Failed to get preview order"); }); } - }, [route.params?.countryCode]); + }, [route.params?.freight_forwarder_address_id]); + + useEffect(() => { + setCreateOrderData({ + ...orderData, + address_id: orderData.address_id, + domestic_shipping_fee: orderData.domestic_shipping_fee, + shipping_fee: orderData.shipping_fee, + transport_type: orderData.transport_type, + currency: user.currency, + }); + console.log("orderData", orderData); + }, [orderData]); + + const handleSubmit = async () => { + if (!selectedPayment) { + Alert.alert("请选择支付方式"); + return; + } + const items = + previewOrder?.items.map((item) => ({ + offer_id: item.offer_id, + cart_item_id: item.cart_item_id, + sku_id: item.sku_id, + product_name: item.product_name, + product_name_en: item.product_name_en, + product_name_ar: item.product_name_ar, + product_name_fr: item.product_name_fr, + sku_attributes: item.attributes.map((attr) => ({ + attribute_name: attr.attribute_name, + attribute_value: attr.value, + })), + sku_image:item.sku_image_url, + quantity: item.quantity, + unit_price: item.unit_price, + total_price: item.total_price, + })) || []; + if (createOrderData) { + createOrderData.items = items; + createOrderData.payment_method = selectedPayment; + createOrderData.total_amount = selectedPayment === 'Paypal' ? convertedAmount : + Number(((previewOrder?.total_amount || 0) + (orderData?.domestic_shipping_fee || 0) + + (orderData?.shipping_fee || 0)).toFixed(2)); + createOrderData.actual_amount = Number((previewOrder?.total_amount || 0).toFixed(2)); + createOrderData.currency = selectedPayment === 'Paypal' ? selectedCurrency : user.currency; + + } + setOrderData(createOrderData || {}); + + const res = await ordersApi.createOrder(createOrderData as CreateOrderRequest) + console.log(res) + navigation.navigate("PreviewOrder"); + }; return ( - <> + navigation.goBack()}> @@ -202,7 +396,12 @@ export const PaymentMethod = () => { ) : ( - + 💳 Payment Method @@ -217,7 +416,11 @@ export const PaymentMethod = () => { styles.tabButton, currentTab === tab.id && styles.tabButtonActive, ]} - onPress={() => setCurrentTab(tab.id)} + onPress={() => { + console.log(tab.id); + + setCurrentTab(tab.id) + }} > { option={option} isSelected={selectedPayment === option.id} onSelect={() => onSelectPayment(option.id)} + selectedCurrency={selectedCurrency} + onSelectCurrency={onSelectCurrency} + exchangeRates={exchangeRates} + totalAmount={totalAmount} + convertedAmount={convertedAmount} + isConverting={isConverting} /> ))} @@ -310,22 +519,29 @@ export const PaymentMethod = () => { - Subtotal + 商品总价 - {previewOrder?.total_amount || 0} + + {previewOrder?.total_amount || 0} {previewOrder?.currency} + - Domestic Shipping + 中国运费 - {previewOrder?.shipping_fee || 0} + + {createOrderData?.domestic_shipping_fee || 0}{" "} + {previewOrder?.currency} + - Estimated International Shipping + 预计国际运费 - {orderData.transport_type === 1 ?previewOrder?.shipping_fee_sea : previewOrder?.shipping_fee_air || 0} + + {createOrderData?.shipping_fee || 0} {previewOrder?.currency} + @@ -342,20 +558,106 @@ export const PaymentMethod = () => { > Total - - ${previewOrder?.actual_amount?.toFixed(2)} - + + { + selectedPayment === 'Paypal' && selectedCurrency !== user.currency && ( + + + + {( + (previewOrder?.total_amount || 0) + + (createOrderData?.domestic_shipping_fee || 0) + + (createOrderData?.shipping_fee || 0) + ).toFixed(2)}{" "} + {previewOrder?.currency} + + + + {convertedAmount}{selectedCurrency === "USD" ? "USD" : "EUR"} + + + ) + } + + + { + selectedPayment === 'Paypal' && selectedCurrency === user.currency && ( + + + + {( + (previewOrder?.total_amount || 0) + + (createOrderData?.domestic_shipping_fee || 0) + + (createOrderData?.shipping_fee || 0) + ).toFixed(2)}{" "} + {previewOrder?.currency} + + + + + ) + } + + + { + selectedPayment !== 'Paypal'&& ( + + + {( + (previewOrder?.total_amount || 0) + + (createOrderData?.domestic_shipping_fee || 0) + + (createOrderData?.shipping_fee || 0) + ).toFixed(2)}{" "} + {previewOrder?.currency} + + ) + } + + + + + 创建订单 + + )} - + ); }; @@ -363,6 +665,7 @@ const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", + marginTop: 10, }, sectionHeader: { flexDirection: "row", @@ -442,12 +745,13 @@ const styles = StyleSheet.create({ marginTop: 8, }, operatorBox: { - backgroundColor: "#EEEEEE", + backgroundColor: "#fff", paddingVertical: 4, paddingHorizontal: 8, borderRadius: 4, marginRight: 8, marginBottom: 4, + }, operatorText: { fontSize: fontSize(12), @@ -478,11 +782,14 @@ const styles = StyleSheet.create({ alignItems: "center", justifyContent: "center", position: "relative", - marginBottom: 10, + backgroundColor: "#fff", + }, backIconContainer: { position: "absolute", left: 15, + backgroundColor: "#fff", + }, titleHeading: { fontWeight: "600", @@ -490,6 +797,7 @@ const styles = StyleSheet.create({ lineHeight: 22, fontFamily: "PingFang SC", color: "black", + }, // Order Summary Styles section: { @@ -631,5 +939,105 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "center", alignItems: "center", + backgroundColor: "#fff", + }, + submitButtonContainer: { + paddingRight: 11, + paddingLeft: 11, + marginTop: 20, + marginBottom: 20, + }, + primaryButtonStyle: { + width: "100%", + height: 50, + justifyContent: "center", + alignItems: "center", + fontWeight: "600", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + color: "white", + backgroundColor: "#002fa7", + borderWidth: 0, + borderRadius: 25, + }, + buttonText: { + color: "white", + fontWeight: "600", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + }, + selectedCountryText: { + padding: 0, + margin: 0, + fontWeight: "500", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + color: "#646472", + }, + disabledButtonStyle: { + backgroundColor: "#ccc", + }, + currencySelectorContainer: { + padding: 15, + backgroundColor: "#f9f9f9", + borderRadius: 8, + marginTop: 5, + marginBottom: 15, + }, + currencySelectorTitle: { + fontSize: fontSize(16), + fontWeight: "600", + color: "#000", + marginBottom: 15, + }, + currencyOptions: { + flexDirection: "row", + marginBottom: 15, + }, + currencyOption: { + flex: 1, + padding: 15, + borderWidth: 1, + borderColor: "#DDDDDD", + borderRadius: 4, + marginRight: 10, + alignItems: "center", + justifyContent: "center", + }, + selectedCurrencyOption: { + borderColor: "#002fa7", + backgroundColor: "#fff", + }, + currencyText: { + fontSize: fontSize(16), + fontWeight: "500", + color: "#000", + }, + exchangeRateContainer: { + marginBottom: 15, + }, + exchangeRateText: { + fontSize: fontSize(14), + color: "#666", + marginBottom: 5, + }, + totalContainer: { + marginTop: 10, + borderTopWidth: 1, + borderTopColor: "#EEEEEE", + paddingTop: 10, + flexDirection: 'row', + alignItems: 'center', + }, + totalText: { + fontSize: fontSize(16), + fontWeight: "600", + color: "#ff6000", + }, + loadingIndicator: { + marginLeft: 10, }, }); diff --git a/app/screens/previewOrder/ShippingFee.tsx b/app/screens/previewOrder/ShippingFee.tsx index df8eb1a..672163b 100644 --- a/app/screens/previewOrder/ShippingFee.tsx +++ b/app/screens/previewOrder/ShippingFee.tsx @@ -15,6 +15,7 @@ import LocationPinIcon from "../../components/LocationPinIcon"; import { AddressDataItem, OrderData, + Address, CartShippingFeeData, DomesticShippingFeeData, } from "../../services/api/orders"; @@ -27,7 +28,7 @@ import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import useUserStore from "../../store/user"; type RootStackParamList = { ShippingFee: undefined; - PaymentMethod: { countryCode: number }; + PaymentMethod: { freight_forwarder_address_id: number }; }; type ShippingFeeParams = { cart_item_id: { @@ -56,14 +57,21 @@ export const ShippingFee = () => { const [modalVisible, setModalVisible] = useState(false); const [selectedWarehouseLabel, setSelectedWarehouseLabel] = useState(""); + const [selectedWarehouse, setSelectedWarehouse] = useState
(); const [domesticShippingFeeData, setDomesticShippingFeeData] = useState(); const [isDomesticShippingLoading, setIsDomesticShippingLoading] = useState(false); + const [count,setCount] = useState(); + const [apiResponses, setApiResponses] = useState({ + shippingFees: false, + domesticShippingFees: false + }); + const { setOrderData ,orderData,items} = useCreateOrderStore(); const [countryCode,setCountryCode] = useState(); const userStore = useUserStore(); const getFreightForwarderAddress = async () => { - await fetchFreightForwarderAddress(1); + await fetchFreightForwarderAddress(0); }; useEffect(() => { @@ -90,16 +98,25 @@ export const ShippingFee = () => { useEffect(() => { if (state.shippingFees) { setShippingFeeData(state.shippingFees); + setCount('正在计算国际价格'); + setApiResponses(prev => ({...prev, shippingFees: true})); } }, [state.shippingFees]); useEffect(() => { if (state.domesticShippingFees) { setDomesticShippingFeeData(state.domesticShippingFees); - setIsDomesticShippingLoading(false); + setApiResponses(prev => ({...prev, domesticShippingFees: true})); } }, [state.domesticShippingFees]); + // Effect to handle loading state based on both API responses + useEffect(() => { + if (apiResponses.shippingFees && apiResponses.domesticShippingFees) { + setIsDomesticShippingLoading(false); + } + }, [apiResponses]); + // Call changeCountryHandel when warehouse changes useEffect(() => { if (warehouse && freightForwarderAddress?.other_addresses) { @@ -113,16 +130,24 @@ export const ShippingFee = () => { (item) => item.country + "|" + item.city === value ); + setSelectedWarehouse(selectedWarehouse); + if (selectedWarehouse && items) { const data = { items: items, - country_code: selectedWarehouse.country_code, + freight_forwarder_address_id: selectedWarehouse.address_id, }; // Only calculate if we have the necessary data - if (data.items && data.country_code) { + if (data.items && data.freight_forwarder_address_id) { // Set loading state to true before making API calls setIsDomesticShippingLoading(true); + setCount('正在计算国内价格'); + // Reset API response tracking + setApiResponses({ + shippingFees: false, + domesticShippingFees: false + }); calculateShippingFee(data); calculateDomesticShippingFee(data); @@ -142,23 +167,14 @@ export const ShippingFee = () => { if (!isDomesticShippingLoading && domesticShippingFeeData?.total_shipping_fee != null) { setOrderData({ ...orderData, - transport_type: shippingMethod === "sea" ? 1 : 2, + transport_type: shippingMethod === "sea" ? 0 : 1, domestic_shipping_fee: domesticShippingFeeData?.total_shipping_fee, shipping_fee: shippingMethod === "sea" ? shippingFeeData?.total_shipping_fee_sea : shippingFeeData?.total_shipping_fee_air, receiver_address:selectedWarehouseLabel }); - // Get the first warehouse's country code as default if countryCode is undefined - let defaultCountryCode = countryCode; - if (defaultCountryCode === undefined) { - // Safely access the first warehouse's country code - const firstWarehouse = freightForwarderAddress?.other_addresses?.[0]; - if (firstWarehouse) { - defaultCountryCode = firstWarehouse.country_code; - } - } - navigation.navigate("PaymentMethod", {countryCode: defaultCountryCode || 0}); + navigation.navigate("PaymentMethod", {freight_forwarder_address_id: selectedWarehouse?.address_id || 0}); }else{ Alert.alert("请选择运输方式"); } @@ -325,7 +341,7 @@ export const ShippingFee = () => { }} > {isDomesticShippingLoading ? ( - 报价中... + {count} ) : ( {((domesticShippingFeeData?.total_shipping_fee || 0) + (shippingMethod === "sea" @@ -564,6 +580,7 @@ const styles = StyleSheet.create({ color: "#333", flexDirection: "row", justifyContent: "space-between", + alignItems: "center", }, shippingInfoLabel: { color: "#777", diff --git a/app/screens/previewOrder/perviewOrder.tsx b/app/screens/previewOrder/perviewOrder.tsx new file mode 100644 index 0000000..24b15d3 --- /dev/null +++ b/app/screens/previewOrder/perviewOrder.tsx @@ -0,0 +1,185 @@ +import { View, Text, TouchableOpacity, StyleSheet, TextInput } from "react-native"; +import useCreateOrderStore from "../../store/createOrder"; +import BackIcon from "../../components/BackIcon"; +import { useNavigation } from "@react-navigation/native"; +import { useState, useEffect } from "react"; + +export const PreviewOrder = () => { + const {orderData, setOrderData} = useCreateOrderStore(); + const navigation = useNavigation(); + const [phoneNumber, setPhoneNumber] = useState(""); + const [showPhoneInput, setShowPhoneInput] = useState(false); + + useEffect(() => { + if (orderData?.payment_method === "Brainnel Pay(Mobile Money)") { + setShowPhoneInput(true); + } else { + setShowPhoneInput(false); + } + }, [orderData?.payment_method]); + + const handleSubmit = () => { + if (showPhoneInput && !phoneNumber) { + // Show error or alert if needed + console.log("Phone number is required for Mobile Money"); + return; + } + + if (showPhoneInput) { + setOrderData({ ...orderData, mobile_money_phone: phoneNumber }); + } + + console.log("orderData", orderData); + // Add your submission logic here + } + + return ( + + + + navigation.goBack()}> + + + + + 立即支付 + + + {/* Payment Details */} + + + {showPhoneInput && ( + + 请输入手机号码 + + + )} + + + + + 提交 + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "white", + }, + submitButtonContainer: { + paddingRight: 11, + paddingLeft: 11, + marginTop: 20, + marginBottom: 20, + }, + primaryButtonStyle: { + width: "100%", + height: 50, + justifyContent: "center", + alignItems: "center", + fontWeight: "600", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + color: "white", + backgroundColor: "#002fa7", + borderWidth: 0, + borderRadius: 25, + }, + buttonText: { + color: "white", + fontWeight: "600", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + }, + selectedCountryText: { + padding: 0, + margin: 0, + fontWeight: "500", + fontSize: 16, + lineHeight: 22, + fontFamily: "PingFang SC", + color: "#646472", + }, + disabledButtonStyle: { + backgroundColor: "#ccc", + }, + titleContainer: { + width: "100%", + padding: 15, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + position: "relative", + backgroundColor: "#fff", + + }, + backIconContainer: { + position: "absolute", + left: 15, + backgroundColor: "#fff", + + }, + titleHeading: { + fontWeight: "600", + fontSize: 20, + lineHeight: 22, + fontFamily: "PingFang SC", + color: "black", + + }, + // Order Summary Styles + section: { + backgroundColor: "#fff", + borderRadius: 8, + paddingHorizontal: 15, + marginTop: 15, + paddingVertical: 12, + }, + sectionTitle: { + fontWeight: "600", + fontSize: 16, + marginBottom: 8, + color: "#333", + }, + paymentMethod: { + fontSize: 15, + color: "#666", + marginBottom: 10, + }, + phoneInputContainer: { + marginTop: 10, + marginBottom: 10, + }, + phoneInputLabel: { + fontSize: 14, + color: "#333", + marginBottom: 5, + }, + phoneInput: { + borderWidth: 1, + borderColor: "#ccc", + borderRadius: 8, + padding: 10, + fontSize: 16, + backgroundColor: "#f9f9f9", + }, +}); diff --git a/app/services/api/apiClient.ts b/app/services/api/apiClient.ts index 0965450..e30e346 100644 --- a/app/services/api/apiClient.ts +++ b/app/services/api/apiClient.ts @@ -61,12 +61,12 @@ apiClient.interceptors.request.use( fullUrl += (fullUrl.includes('?') ? '&' : '?') + params.toString(); } - console.log("环境:", __DEV__ ? "开发环境" : "生产环境"); - console.log("请求方法:", config.method); - console.log("完整URL:", fullUrl); - console.log("请求头:", config.headers); - console.log("请求参数:", config.params); - console.log("请求数据:", config.data); + // console.log("环境:", __DEV__ ? "开发环境" : "生产环境"); + // console.log("请求方法:", config.method); + // console.log("完整URL:", fullUrl); + // console.log("请求头:", config.headers); + // console.log("请求参数:", config.params); + // console.log("请求数据:", config.data); // 从AsyncStorage获取token const token = await AsyncStorage.getItem("token"); diff --git a/app/services/api/orders.ts b/app/services/api/orders.ts index b3f88cc..6ada4c6 100644 --- a/app/services/api/orders.ts +++ b/app/services/api/orders.ts @@ -2,7 +2,7 @@ import apiService from './apiClient'; // 地址类型 -interface Address { +export interface Address { address_id: number; user_id: number; receiver_first_name: string; diff --git a/app/services/api/payApi.ts b/app/services/api/payApi.ts index 42cc8d9..eedcad9 100644 --- a/app/services/api/payApi.ts +++ b/app/services/api/payApi.ts @@ -30,6 +30,13 @@ export interface PayInfoBody { currency: string; } +export interface ConvertCurrencyBody { + from_currency: string; + to_currency: string; + amount: number; +} + + export const payApi = { // 获取当前国家支付方式 getCountryPaymentMethods: () => { @@ -40,4 +47,9 @@ export const payApi = { getPayInfo: (data: PayInfoBody) => { return apiService.post(`/api/payment/initiate/`, data); }, + + // 货币转换 + convertCurrency: (data: ConvertCurrencyBody) => { + return apiService.post(`/api/currency/convert/`, data); + }, }; diff --git a/app/services/api/productApi.ts b/app/services/api/productApi.ts index b917af3..b9b78fc 100644 --- a/app/services/api/productApi.ts +++ b/app/services/api/productApi.ts @@ -183,12 +183,14 @@ export type Products = Product[] return apiService.get('/api/search/', params); }, // 获取商品详情 - getProductDetail: (offer_id: string,user_id:number) => { - return apiService.get(`/api/products/${offer_id}/?user_id=${user_id}`); + getProductDetail: (offer_id: string, user_id?: number) => { + const url = user_id ? `/api/products/${offer_id}/?user_id=${user_id}` : `/api/products/${offer_id}/`; + return apiService.get(url); }, // 获取相似商品 - getSimilarProducts: (offer_id: string,user_id:number) => { - return apiService.get(`/api/products/${offer_id}/similar/?limit=5&user_id=${user_id}`); + getSimilarProducts: (offer_id: string, user_id?: number) => { + const url = user_id ? `/api/products/${offer_id}/similar/?limit=5&user_id=${user_id}` : `/api/products/${offer_id}/similar/?limit=5`; + return apiService.get(url); } } diff --git a/app/store/createOrder.ts b/app/store/createOrder.ts index 5359bae..e99d708 100644 --- a/app/store/createOrder.ts +++ b/app/store/createOrder.ts @@ -131,7 +131,7 @@ const initialOrderData: OrderCreateRequest = { items: [], buyer_message: '', payment_method: '', - create_payment: false, + create_payment: true, actual_amount: 0, discount_amount: 0, shipping_fee: 0, diff --git a/app/store/previewShipping.ts b/app/store/previewShipping.ts index 3f976b3..a577bee 100644 --- a/app/store/previewShipping.ts +++ b/app/store/previewShipping.ts @@ -61,7 +61,7 @@ const usePreviewShippingStore = create((set) => ({ calculateShippingFee: async (data: ShippingFeeData) => { set((state) => ({ - state: { ...state.state, isLoading: true, error: null } + state: { ...state.state, isLoading: false, error: null } })); try { @@ -87,7 +87,7 @@ const usePreviewShippingStore = create((set) => ({ calculateDomesticShippingFee: async (data: ShippingFeeData) => { set((state) => ({ - state: { ...state.state, isLoading: true, error: null } + state: { ...state.state, isLoading: false, error: null } })); try { diff --git a/app/types/createOrder.ts b/app/types/createOrder.ts new file mode 100644 index 0000000..552e483 --- /dev/null +++ b/app/types/createOrder.ts @@ -0,0 +1,113 @@ +/** + * OrderCreate,订单创建模型 + */ +export interface createOrderDataType { + /** + * Actual Amount,实际支付金额 + */ + actual_amount?: number | null; + /** + * Address Id,收货地址ID + */ + address_id: number; + /** + * Buyer Message,买家留言 + */ + buyer_message?: null | string; + /** + * Create Payment,是否创建支付记录 + */ + create_payment?: boolean | null; + /** + * Currency,货币 + */ + currency?: null | string; + /** + * Discount Amount,优惠金额 + */ + discount_amount?: number | null; + /** + * Domestic Shipping Fee,国内运费 + */ + domestic_shipping_fee?: number | null; + /** + * Items,订单项 + */ + items: OrderItemBase[]; + /** + * Payment Method,支付方式 + */ + payment_method?: null | string; + /** + * Receiver Address,货代地址 + */ + receiver_address: string; + /** + * Shipping Fee,运费 + */ + shipping_fee?: number | null; + /** + * Total Amount,订单总金额 + */ + total_amount?: number | null; + /** + * Transport Type,运输方式 1-海运 2-空运 + */ + transport_type?: number | null; + [property: string]: any; +} + +/** + * OrderItemBase,订单项基础信息模型 + */ +export interface OrderItemBase { + /** + * Cart Item Id,购物车项ID(如来源于购物车则必填) + */ + cart_item_id?: number | null; + /** + * Offer Id,商品ID + */ + offer_id: number; + /** + * Product Image,商品图片 + */ + product_image?: null | string; + /** + * Product Name,商品名称 + */ + product_name: string; + /** + * Product Name Ar,商品阿拉伯语名称 + */ + product_name_ar?: null | string; + /** + * Product Name En,商品法语名称 + */ + product_name_en?: null | string; + /** + * Product Name Fr,商品中文名称 + */ + product_name_fr?: null | string; + /** + * Quantity,商品数量 + */ + quantity: number; + /** + * Sku Attributes,SKU属性 + */ + sku_attributes?: { [key: string]: any }[] | null; + /** + * Sku Id,SKU ID + */ + sku_id?: number | null; + /** + * Total Price,商品总价 + */ + total_price: number; + /** + * Unit Price,商品单价 + */ + unit_price: number; + [property: string]: any; +} diff --git a/package.json b/package.json index e999efc..22ca796 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "events": "^3.3.0", "expo": "~52.0.41", "expo-auth-session": "~6.0.3", + "expo-build-properties": "~0.13.3", "expo-image": "~2.0.7", "expo-linear-gradient": "~14.0.2", "expo-localization": "^16.0.1", diff --git a/yarn.lock b/yarn.lock index b204130..d15ab64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3328,7 +3328,7 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.11.0, ajv@^8.9.0: version "8.17.1" resolved "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -5688,6 +5688,14 @@ expo-auth-session@~6.0.3: expo-web-browser "~14.0.2" invariant "^2.2.4" +expo-build-properties@~0.13.3: + version "0.13.3" + resolved "https://registry.npmmirror.com/expo-build-properties/-/expo-build-properties-0.13.3.tgz#6b96d0486148fca6e74e62c7c502c0a9990931aa" + integrity sha512-gw7AYP+YF50Gr912BedelRDTfR4GnUEn9p5s25g4nv0hTJGWpBZdCYR5/Oi2rmCHJXxBqhPjxzV7JRh72fntLg== + dependencies: + ajv "^8.11.0" + semver "^7.6.0" + expo-constants@~17.0.5, expo-constants@~17.0.8: version "17.0.8" resolved "https://registry.npmmirror.com/expo-constants/-/expo-constants-17.0.8.tgz"