diff --git a/App.tsx b/App.tsx index b408e33..0ce63dc 100644 --- a/App.tsx +++ b/App.tsx @@ -4,16 +4,27 @@ import { useEffect, useState } from "react"; import { userApi } from "./app/services/api/userApi"; import useUserStore from "./app/store/user"; import useBurialPointStore from "./app/store/burialPoint"; -import { AuthProvider, useAuth } from "./app/contexts/AuthContext"; +import { AuthProvider, useAuth, AUTH_EVENTS } from "./app/contexts/AuthContext"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { AppNavigator } from "./app/navigation/AppNavigator"; import { View, ActivityIndicator, Alert } from "react-native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import "./app/i18n"; -import * as Linking from 'expo-linking'; +import * as Linking from "expo-linking"; +import { EventEmitter } from 'events'; + +// 声明全局事件发射器类型 +declare global { + var EventEmitter: EventEmitter; +} + +// 创建全局事件发射器 +if (!global.EventEmitter) { + global.EventEmitter = new EventEmitter(); +} // 定义全局事件处理支付成功 -export const PAYMENT_SUCCESS_EVENT = 'PAYMENT_SUCCESS_EVENT'; +export const PAYMENT_SUCCESS_EVENT = "PAYMENT_SUCCESS_EVENT"; function AppContent() { const { setUser } = useUserStore(); @@ -21,12 +32,22 @@ function AppContent() { const { logAppLaunch } = useBurialPointStore(); const [isLoading, setIsLoading] = useState(true); + // 获取用户资料的函数 + const fetchUserProfile = async () => { + try { + const user = await userApi.getProfile(); + setUser(user); + return true; + } catch (error) { + console.error('Failed to fetch user profile:', error); + return false; + } + }; + useEffect(() => { const initApp = async () => { try { - - const user = await userApi.getProfile(); - setUser(user); + await fetchUserProfile(); // 记录应用启动成功埋点 logAppLaunch(true); @@ -42,24 +63,38 @@ function AppContent() { initApp(); }, []); + // 监听登录成功事件,刷新用户资料 + useEffect(() => { + const handleLoginSuccess = () => { + fetchUserProfile(); + }; + + // 注册事件监听器 + global.EventEmitter.on(AUTH_EVENTS.LOGIN_SUCCESS, handleLoginSuccess); + + // 清理函数 + return () => { + global.EventEmitter.off(AUTH_EVENTS.LOGIN_SUCCESS, handleLoginSuccess); + }; + }, []); + // 添加深度链接处理 useEffect(() => { // 处理深度链接 const handleDeepLink = ({ url }: { url: string }) => { - console.log('Global deep link received:', url); if ( - url.startsWith('myapp://payment-success') || - url.startsWith('exp://192.168.0.101:8084/--/payment-success') + url.startsWith("myapp://payment-success") || + url.startsWith("exp://192.168.0.101:8084/--/payment-success") ) { // 解析参数 const parsed = Linking.parse(url); const params = parsed.queryParams || {}; - const paymentId = params.paymentId || ''; - const token = params.token || ''; - const payerId = params.PayerID || ''; - + const paymentId = params.paymentId || ""; + const token = params.token || ""; + const payerId = params.PayerID || ""; + Alert.alert( - '支付成功!', + "支付成功!", `支付ID: ${paymentId}\nToken: ${token}\nPayerID: ${payerId}` ); // 这里可以做页面跳转或业务处理 @@ -67,11 +102,11 @@ function AppContent() { }; // 注册深度链接监听器 - const subscription = Linking.addEventListener('url', handleDeepLink); + const subscription = Linking.addEventListener("url", handleDeepLink); // 处理应用冷启动的深度链接 Linking.getInitialURL().then((url) => { - if (url && url.startsWith('myapp://payment-success')) { + if (url && url.startsWith("myapp://payment-success")) { handleDeepLink({ url }); } }); @@ -81,7 +116,7 @@ function AppContent() { if (isLoading) { return ( - + ); diff --git a/app/constants/productStatus.ts b/app/constants/productStatus.ts index 6e9c73c..b2f0419 100644 --- a/app/constants/productStatus.ts +++ b/app/constants/productStatus.ts @@ -12,9 +12,9 @@ export const productStatus: ProductStatus[] = [ { icon: PdfDocumentIcon, text: '待付款', textKey: 'order.status.waiting_payment', status: 0 }, // { icon: DocumentClockIcon, text: '付运费', textKey: 'order.status.pay_shipping', status: 6 }, { icon: DocumentClockIcon, text: '待发货', textKey: 'order.status.waiting_shipment', status: 1 }, - { icon: DocumentApprovedIcon, text: '运输中', textKey: 'order.status.in_transit', status: 7 }, - { icon: DocumentApprovedIcon, text: '代收货', textKey: 'order.status.waiting_receipt', status: 2 }, - { icon: PdfDocumentIcon, text: '已完成', textKey: 'order.status.completed', status: 3 }, - { icon: DocumentClockIcon, text: '已取消', textKey: 'order.status.cancelled', status: 4 }, - { icon: DocumentClockIcon, text: '已退款', textKey: 'order.status.refunded', status: 5 } + { icon: DocumentApprovedIcon, text: '运输中', textKey: 'order.status.in_transit', status: 2 }, + { icon: DocumentApprovedIcon, text: '代收货', textKey: 'order.status.waiting_receipt', status: 3 }, + { icon: PdfDocumentIcon, text: '已完成', textKey: 'order.status.completed', status: 4 }, + { icon: DocumentClockIcon, text: '已取消', textKey: 'order.status.cancelled', status: 5 }, + { icon: DocumentClockIcon, text: '已退款', textKey: 'order.status.refunded', status: 6 } ] \ No newline at end of file diff --git a/app/contexts/AuthContext.tsx b/app/contexts/AuthContext.tsx index b5eeaa6..a68ce5e 100644 --- a/app/contexts/AuthContext.tsx +++ b/app/contexts/AuthContext.tsx @@ -1,5 +1,16 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import { EventEmitter } from 'events'; + +// 声明全局事件发射器类型 +declare global { + var EventEmitter: EventEmitter; +} + +// 添加自定义事件 +export const AUTH_EVENTS = { + LOGIN_SUCCESS: 'LOGIN_SUCCESS' +}; type AuthContextType = { isLoggedIn: boolean; @@ -30,6 +41,11 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children try { await AsyncStorage.setItem('isLoggedIn', 'true'); setIsLoggedIn(true); + + // 发出登录成功事件通知 + if (global.EventEmitter) { + global.EventEmitter.emit(AUTH_EVENTS.LOGIN_SUCCESS); + } } catch (error) { console.error('Error logging in:', error); } diff --git a/app/i18n/index.ts b/app/i18n/index.ts index 0a6626d..cad4663 100644 --- a/app/i18n/index.ts +++ b/app/i18n/index.ts @@ -12,6 +12,9 @@ const LANGUAGE_KEY = '@app_language'; // 获取设备语言 const deviceLanguage = Localization.locale.split('-')[0]; +// 确定使用的语言:仅在系统语言为法语时使用法语,其他语言一律使用英语 +const initialLanguage = deviceLanguage === 'fr' ? 'fr' : 'en'; + // 初始化 i18n i18n .use(initReactI18next) @@ -27,7 +30,7 @@ i18n translation: frTranslation } }, - lng: deviceLanguage, // 使用设备语言 + lng: initialLanguage, // 根据逻辑设置初始语言 fallbackLng: 'en', // 如果找不到翻译,使用英语 interpolation: { escapeValue: false // 不需要转义 HTML @@ -40,7 +43,9 @@ const loadLanguage = async () => { try { const savedLanguage = await AsyncStorage.getItem(LANGUAGE_KEY); if (savedLanguage) { - i18n.changeLanguage(savedLanguage); + // 仅接受法语或英语作为有效选择 + const validLanguage = savedLanguage === 'fr' ? 'fr' : 'en'; + i18n.changeLanguage(validLanguage); } } catch (error) { console.error('加载语言设置失败:', error); @@ -58,10 +63,12 @@ const saveLanguage = async (language: string) => { // 修改语言切换函数 export const changeLanguage = async (language: string) => { - await saveLanguage(language); - i18n.changeLanguage(language); + // 仅接受法语或英语作为有效选择 + const validLanguage = language === 'fr' ? 'fr' : 'en'; + await saveLanguage(validLanguage); + i18n.changeLanguage(validLanguage); - console.log("切换语言:" + language); + console.log("切换语言:" + validLanguage); }; diff --git a/app/navigation/TabNavigator.tsx b/app/navigation/TabNavigator.tsx index 1fe27a8..99b0ffe 100644 --- a/app/navigation/TabNavigator.tsx +++ b/app/navigation/TabNavigator.tsx @@ -163,7 +163,7 @@ export const TabNavigator = () => { }} /> ; export const BalanceScreen = () => { + const { user } = useUserStore(); const navigation = useNavigation(); const [isModalVisible, setIsModalVisible] = useState(false); + const [rechargeHistory, setRechargeHistory] = useState([]); const handleOpenModal = () => { setIsModalVisible(true); @@ -26,6 +29,14 @@ export const BalanceScreen = () => { setIsModalVisible(false); }; + useEffect(() => { + const fetchRechargeHistory = async () => { + const response = await payApi.getRechargeHistory(); + setRechargeHistory(response); + } + fetchRechargeHistory(); + }, []); + return ( @@ -60,13 +71,13 @@ export const BalanceScreen = () => { - 650,000 + {user?.balance} {/* SVG or icon replacement */} - Solde Total (FCFA) + Solde Total ({user?.currency}) 50,000 @@ -90,87 +101,28 @@ export const BalanceScreen = () => { Détail du solde - - {/* Repeated Transaction Details */} - - - - Frais de transport - - -58 FCFA - - - 2025-02-12 - BR-11-00010 - - - {/* Additional transaction panels can be added here */} - - - {/* Repeated Transaction Details */} - - - - Frais de transport - - -58 FCFA - - - 2025-02-12 - BR-11-00010 - - - {/* Additional transaction panels can be added here */} - - - {/* Repeated Transaction Details */} - - - - Frais de transport - - -58 FCFA - - - 2025-02-12 - BR-11-00010 - - - {/* Additional transaction panels can be added here */} - - - {/* Repeated Transaction Details */} - - - - Frais de transport - - -58 FCFA - - - 2025-02-12 - BR-11-00010 - - - {/* Additional transaction panels can be added here */} - - - {/* Repeated Transaction Details */} - - - - Frais de transport - - -58 FCFA - - - 2025-02-12 - BR-11-00010 - - - {/* Additional transaction panels can be added here */} - + {rechargeHistory.map((item) => ( + + {/* Repeated Transaction Details */} + + + + {item.payment_method} + + {item.amount} {item.currency} + + + {item.create_time} + {item.transaction_id} + + + {/* Additional transaction panels can be added here */} + + ))} + + + diff --git a/app/screens/CategoryScreen.tsx b/app/screens/CategoryScreen.tsx index 90b1e44..d9c61cb 100644 --- a/app/screens/CategoryScreen.tsx +++ b/app/screens/CategoryScreen.tsx @@ -16,8 +16,20 @@ import { import fontSize from '../utils/fontsizeUtils'; import widthUtils from '../utils/widthUtils'; import { categoriesApi, Category } from '../services/api/categories'; +import { useNavigation } from '@react-navigation/native'; +import { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { RootStackParamList } from '../types/navigation'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const MENU_WIDTH = widthUtils(120, 120).width; +const AVAILABLE_WIDTH = SCREEN_WIDTH - MENU_WIDTH - 20; // 20 for padding +const NUM_COLUMNS = 4; +const ITEM_MARGIN = '2.66%'; +const ITEM_WIDTH = (AVAILABLE_WIDTH / NUM_COLUMNS) - (AVAILABLE_WIDTH * 0.0266); + export const CategoryScreen = () => { + const navigation = useNavigation>(); const [mainCategories, setMainCategories] = useState([]); const [subCategories, setSubCategories] = useState([]); const [activeMainCategory, setActiveMainCategory] = useState(null); @@ -85,18 +97,25 @@ export const CategoryScreen = () => { const renderSubCategoryItem: ListRenderItem = ({ item }) => ( {}} + style={styles.subCategoryItem} + onPress={() => { + navigation.navigate('SearchResult', { keyword: item.name_en.trim() }); + }} > - - {item.name_cn} - + + + + {item.name_cn} + + ); @@ -130,8 +149,9 @@ export const CategoryScreen = () => { data={subCategories} renderItem={renderSubCategoryItem} keyExtractor={(item) => item.category_id.toString()} - numColumns={3} + numColumns={NUM_COLUMNS} contentContainerStyle={styles.productGrid} + columnWrapperStyle={styles.columnWrapper} /> )} @@ -155,7 +175,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', }, leftMenu: { - width: widthUtils(100,100).width, + width: MENU_WIDTH, backgroundColor: '#fff', borderRightWidth: 1, borderColor: '#eee', @@ -179,7 +199,7 @@ const styles = StyleSheet.create({ }, rightContent: { flex: 1, - backgroundColor: '#f8f8f8', + backgroundColor: '#ffffff', paddingHorizontal: 10, paddingTop: 12, }, @@ -192,4 +212,28 @@ const styles = StyleSheet.create({ alignItems: 'center', backgroundColor: '#fff', }, + columnWrapper: { + justifyContent: 'flex-start', + marginBottom: 15, + }, + subCategoryItem: { + width: ITEM_WIDTH, + overflow: 'hidden', + marginRight: ITEM_MARGIN, + }, + subCategoryImage: { + width: '100%', + height: ITEM_WIDTH, + }, + subCategoryInfo: { + padding: 8, + height: 26, + justifyContent: 'center', + alignItems: 'center', + }, + subCategoryName: { + fontSize: fontSize(12), + color: '#333', + textAlign: 'center', + }, }); diff --git a/app/screens/ChatScreen.tsx b/app/screens/ChatScreen.tsx index 3599699..911313d 100644 --- a/app/screens/ChatScreen.tsx +++ b/app/screens/ChatScreen.tsx @@ -29,7 +29,7 @@ interface Message { mimetype: string; userWs: string; app_id: string; - country: string; + country: number; body: string; text: string; type: string; @@ -130,10 +130,10 @@ export const ChatScreen = () => { mimetype: "text/plain", userWs: "unknown", app_id: user.user_id ? user.user_id.toString() : "", - country: country, + country: user.country_code, body: "", text: inputText, - type: "chat", + type: "text", isMe: true, timestamp: new Date(), id: Date.now().toString(), // Add unique id for keyExtractor @@ -160,7 +160,7 @@ export const ChatScreen = () => { mimetype: "text/plain", userWs: "system", app_id: "system", - country: country, + country: user.country_code, body: "", text: `${t('typingMessage')}...`, type: "chat", @@ -174,8 +174,11 @@ export const ChatScreen = () => { setMessages(prevMessages => [...prevMessages, simulatedResponse]); }, 800); + const data = { + newMessage:chatServiceMessage, + } // Send actual message to API - chatService.sendMessage(chatServiceMessage) + chatService.sendMessage(data) .then(response => { // When real response arrives, replace simulated message setMessages(prevMessages => { @@ -187,7 +190,7 @@ export const ChatScreen = () => { mimetype: "text/plain", userWs: "system", app_id: "system", - country: country, + country: user.country_code, body: "", text: response?.text || t('defaultResponse'), type: "chat", @@ -209,7 +212,7 @@ export const ChatScreen = () => { mimetype: "text/plain", userWs: "system", app_id: "system", - country: country, + country: user.country_code, body: "", text: t('errorResponse'), type: "chat", diff --git a/app/screens/ProfileScreen.tsx b/app/screens/ProfileScreen.tsx index 83bdc53..e1984a3 100644 --- a/app/screens/ProfileScreen.tsx +++ b/app/screens/ProfileScreen.tsx @@ -94,15 +94,17 @@ export const ProfileScreen = () => { ID: {user?.user_id} - navigation.navigate("Balance")} - > - - - - 余额 - + {user.user_id && ( + navigation.navigate("Balance")} + > + + + + 余额 + + )} @@ -136,7 +138,7 @@ export const ProfileScreen = () => { - {t('profile.vip.next_level_info')} + {t("profile.vip.next_level_info")} { } > - {t('profile.learn_more')} + {t("profile.learn_more")} @@ -213,14 +215,18 @@ export const ProfileScreen = () => { - {t("order.title")} + + {t("order.title")} + navigation.navigate("Status", { status: null }) } > - {t("all")} + + {t("all")} + @@ -253,7 +259,9 @@ export const ProfileScreen = () => { - {t("tool.title")} + + {t("tool.title")} + @@ -267,7 +275,9 @@ export const ProfileScreen = () => { color="#707070" /> - {t("browse.history")} + + {t("browse.history")} + @@ -277,7 +287,9 @@ export const ProfileScreen = () => { > - {t("collection")} + + {t("collection")} + { - {t("address.management")} + + {t("address.management")} + @@ -295,8 +309,8 @@ export const ProfileScreen = () => { {/* Login Button at bottom of screen */} - {t("login.now")} diff --git a/app/screens/RechargeScreen.tsx b/app/screens/RechargeScreen.tsx index 0d1cc81..0c5621f 100644 --- a/app/screens/RechargeScreen.tsx +++ b/app/screens/RechargeScreen.tsx @@ -23,7 +23,7 @@ import CheckIcon from "../components/CheckIcon"; import BackIcon from "../components/BackIcon"; // 添加导航相关导入 -import { useNavigation } from '@react-navigation/native'; +import { useNavigation } from "@react-navigation/native"; import { navigationRef } from "../navigation/RootNavigation"; // 添加API服务 @@ -43,13 +43,15 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { // 指定导航类型为any const navigation = useNavigation(); - const [convertedAmount, setConvertedAmount] = useState<{ - converted_amount: number; - item_key: string; - original_amount: number; - }[]>([]); + const [convertedAmount, setConvertedAmount] = useState< + { + converted_amount: number; + item_key: string; + original_amount: number; + }[] + >([]); const [currentCurrency, setCurrentCurrency] = useState("USD"); - const [phoneNumber, setPhoneNumber] = useState('22660962235'); + const [phoneNumber, setPhoneNumber] = useState("22660962235"); const [isSubmitting, setIsSubmitting] = useState(false); const [paymentParams, setPaymentParams] = useState<{ originalAmount: number; @@ -61,7 +63,7 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { const handlePriceSelect = (price: string) => { setSelectedPrice(price); - + // 如果当前已选择了货币转换,则重新计算转换后的金额 if (selectedOperator === "currency" && currentCurrency !== "FCFA") { handleCurrencyConversion(price, currentCurrency); @@ -70,7 +72,7 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { const handleOperatorSelect = (operator: string) => { setSelectedOperator(operator === selectedOperator ? null : operator); - + // 如果选择了货币支付方式,立即进行货币转换 if (operator === "currency" && operator !== selectedOperator) { // 触发货币转换,使用默认的USD @@ -86,13 +88,13 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { if (selectedOperator) { // 准备支付参数,方便后续发送 const params = { - originalAmount: parseFloat(selectedPrice.replace(/,/g, '')), - amount: parseFloat(selectedPrice.replace(/,/g, '')), + originalAmount: parseFloat(selectedPrice.replace(/,/g, "")), + amount: parseFloat(selectedPrice.replace(/,/g, "")), currency: "FCFA", payment_method: "", - selectedPriceLabel: selectedPrice + " FCFA" // 选择的充值面额 + selectedPriceLabel: selectedPrice + " FCFA", // 选择的充值面额 }; - + // 根据selectedOperator确定支付方式 if (selectedOperator === "orange") { params.payment_method = "Orange Money"; @@ -104,27 +106,22 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { // 当选择了货币转换时 params.payment_method = "paypal"; params.currency = currentCurrency; // 使用选择的货币 - + // 使用转换后的金额,如果有 if (convertedAmount.length > 0) { - const convertedTotal = convertedAmount.find(item => item.item_key === 'total_amount'); + const convertedTotal = convertedAmount.find( + (item) => item.item_key === "total_amount" + ); if (convertedTotal) { params.amount = convertedTotal.converted_amount; } } } - + // 保存支付参数 setPaymentParams(params); - - // 打印支付信息 - console.log("Payment Information:"); - console.log("Selected Recharge Amount:", params.selectedPriceLabel); - console.log("Original Amount:", params.originalAmount, "FCFA"); - console.log("Converted Amount:", params.amount); - console.log("Currency:", params.currency); - console.log("Payment Method:", params.payment_method); - + + setShowBlankPage(true); } }; @@ -132,22 +129,22 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { // 提取一个专门用于货币转换的函数 const handleCurrencyConversion = (price: string, currency: string) => { setIsConverting(true); - + // 格式化金额,去除逗号 - const amount = parseFloat(price.replace(/,/g, '')); - + const amount = parseFloat(price.replace(/,/g, "")); + // 如果金额为0或无效,则不进行转换 if (!amount || isNaN(amount)) { console.warn("Invalid amount for currency conversion"); setIsConverting(false); return; } - + console.log(`Converting ${amount} FCFA to ${currency}...`); - + // 调用货币转换API const data = { - from_currency: 'FCFA', + from_currency: "FCFA", to_currency: currency, amounts: { total_amount: amount, @@ -155,10 +152,15 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { shipping_fee: 0, }, }; - - payApi.convertCurrency(data) + + payApi + .convertCurrency(data) .then((res) => { - if (res && res.converted_amounts_list && res.converted_amounts_list.length > 0) { + if ( + res && + res.converted_amounts_list && + res.converted_amounts_list.length > 0 + ) { console.log("Conversion successful:", res.converted_amounts_list); setConvertedAmount(res.converted_amounts_list); } else { @@ -166,38 +168,30 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { // 使用近似汇率作为备用 const fallbackRate = currency === "USD" ? 580.0 : 655.96; // 1 USD = 580 FCFA, 1 EUR = 655.96 FCFA const convertedValue = amount / fallbackRate; - - setConvertedAmount([{ - converted_amount: convertedValue, - item_key: 'total_amount', - original_amount: amount - }]); - - if (Platform.OS === 'web') { - console.warn("Using fallback conversion rate due to API error"); - } else { - Alert.alert("Info", "Taux de conversion approximatif utilisé"); - } + + setConvertedAmount([ + { + converted_amount: convertedValue, + item_key: "total_amount", + original_amount: amount, + }, + ]); } }) .catch((error) => { console.error("Currency conversion failed:", error); - + // 使用近似汇率作为备用 const fallbackRate = currency === "USD" ? 580.0 : 655.96; const convertedValue = amount / fallbackRate; - - setConvertedAmount([{ - converted_amount: convertedValue, - item_key: 'total_amount', - original_amount: amount - }]); - - if (Platform.OS === 'web') { - console.warn("Using fallback conversion rate due to API error"); - } else { - Alert.alert("Info", "Taux de conversion approximatif utilisé"); - } + + setConvertedAmount([ + { + converted_amount: convertedValue, + item_key: "total_amount", + original_amount: amount, + }, + ]); }) .finally(() => { setIsConverting(false); @@ -213,109 +207,73 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { // 更新处理支付提交的函数 const handlePaySubmit = async () => { if (!paymentParams) { - if (Platform.OS === 'web') { - alert("Veuillez sélectionner un mode de paiement"); - } else { - Alert.alert("Erreur", "Veuillez sélectionner un mode de paiement"); - } return; } - + // 验证电话号码(添加更严格的验证) if (!phoneNumber || phoneNumber.length < 8) { - if (Platform.OS === 'web') { - alert("Veuillez entrer un numéro de téléphone valide (au moins 8 chiffres)"); - } else { - Alert.alert("Erreur", "Veuillez entrer un numéro de téléphone valide (au moins 8 chiffres)"); - } + Alert.alert( + "Erreur", + "Veuillez entrer un numéro de téléphone valide (au moins 8 chiffres)" + ); return; } - + // 显示提交中状态 setIsSubmitting(true); - + try { // 准备请求数据,添加电话号码 const rechargeData = { amount: paymentParams.amount, currency: paymentParams.currency, payment_method: paymentParams.payment_method, - phone: phoneNumber // 注意:如果后端API不支持此参数,可能需要调整 + phone: phoneNumber, // 注意:如果后端API不支持此参数,可能需要调整 }; - + console.log("Submitting recharge request:", rechargeData); - + // 调用充值接口(使用可选链避免错误) const response = await payApi.initiateRecharge(rechargeData); - - console.log("Recharge response:", response); - if (response && response.success) { const paymentInfo = response.payment; - - // 存储交易ID,以便后续查询 - if (Platform.OS === 'web') { - sessionStorage.setItem('recharge_transaction_id', paymentInfo.transaction_id || ''); - sessionStorage.setItem('recharge_id', response.recharge_id?.toString() || ''); - } - + // 检查是否有支付URL if (paymentInfo && paymentInfo.payment_url) { - // 记录重要信息到控制台 - console.log("Transaction ID:", paymentInfo.transaction_id); - console.log("Recharge ID:", response.recharge_id); - console.log("Payment URL:", paymentInfo.payment_url); - + onClose(); // 打开支付页面 - navigation.navigate("Pay", { - payUrl: paymentInfo.payment_url, - }); - - // Web平台添加额外提示,因为可能会被浏览器拦截 - if (Platform.OS === 'web') { - setTimeout(() => { - alert(`Si la page de paiement ne s'ouvre pas automatiquement, veuillez cliquer sur le bouton "Continuer" pour procéder au paiement. Vous pouvez également copier ce lien: ${paymentInfo.payment_url}`); - }, 1500); - } + setTimeout(() => { + navigation.navigate("Pay", { + payUrl: paymentInfo.payment_url, + }); + }, 1000); } else { - // 没有支付URL但交易成功的情况 - if (Platform.OS === 'web') { - alert("Votre recharge a été traitée avec succès!"); - onClose(); // 关闭充值页面 - } else { - Alert.alert( - "Succès", - "Votre recharge a été traitée avec succès!", - [{ text: "OK", onPress: onClose }] - ); - } + + Alert.alert("Succès", "Votre recharge a été traitée avec succès!", [ + { text: "OK", onPress: onClose }, + ]); } } else { // 处理失败情况,显示错误消息 - const errorMessage = response?.msg || "Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer."; - - if (Platform.OS === 'web') { - alert(errorMessage); - } else { - Alert.alert("Erreur", errorMessage); - } + const errorMessage = + response?.msg || + "Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer."; + + Alert.alert("Erreur", errorMessage); } } catch (error) { // 处理异常 console.error("Recharge error:", error); - - let errorMessage = "Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer."; - + + let errorMessage = + "Une erreur s'est produite lors du traitement de la recharge. Veuillez réessayer."; + // 尝试从错误对象中提取更具体的错误信息 if (error instanceof Error) { errorMessage = error.message || errorMessage; } - - if (Platform.OS === 'web') { - alert(errorMessage); - } else { - Alert.alert("Erreur", errorMessage); - } + + Alert.alert("Erreur", errorMessage); } finally { // 无论成功失败,都取消提交状态 setIsSubmitting(false); @@ -324,7 +282,9 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { return ( - + Recharger {!showBlankPage && ( @@ -333,9 +293,12 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { )} {showBlankPage && ( - + - + )} @@ -512,24 +475,36 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {/* 支付方式标题 */} - Choisissez votre mode de paiement + + Choisissez votre mode de paiement + {/* Tab Bar */} - setActiveTab(0)} > - + Mode de paiement - setActiveTab(1)} > - + Autre @@ -546,7 +521,9 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { justifyContent: "space-between", }} > - + { Nous ne supportons que{"\n"}les opérateurs affichés. - handleOperatorSelect("orange")}> + handleOperatorSelect("orange")} + > {selectedOperator === "orange" && ( @@ -599,12 +582,18 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {/* 右侧圆圈图标 */} - handleOperatorSelect("mtn")}> + handleOperatorSelect("mtn")} + > {selectedOperator === "mtn" && ( @@ -630,14 +619,22 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {/* 圆形图标(右边) */} - handleOperatorSelect("balance")}> + handleOperatorSelect("balance")} + > {selectedOperator === "balance" && ( @@ -654,15 +651,21 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { source={require("../../assets/img/image_8786995c.png")} style={styles.currencyImage} /> - handleOperatorSelect("currency")}> + handleOperatorSelect("currency")} + > {selectedOperator === "currency" && ( @@ -675,31 +678,77 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {selectedOperator === "currency" && ( <> - Choisissez la devise + + Choisissez la devise + - handleCurrencySelect("USD")} > - USD + + USD + - handleCurrencySelect("EUR")} > - EUR + + EUR + {isConverting ? ( - + - Converting... + + Converting... + ) : ( - Total: {convertedAmount.length > 0 - ? `${currentCurrency === "USD" ? "$" : "€"}${convertedAmount.find(item => item.item_key === 'total_amount')?.converted_amount.toFixed(2) || '0.00'}` - : `$${(parseFloat(selectedPrice.replace(/,/g, '')) / 580).toFixed(2)}`} + Total:{" "} + {convertedAmount.length > 0 + ? `${currentCurrency === "USD" ? "$" : "€"}${ + convertedAmount + .find( + (item) => item.item_key === "total_amount" + ) + ?.converted_amount.toFixed(2) || "0.00" + }` + : `$${( + parseFloat(selectedPrice.replace(/,/g, "")) / + 580 + ).toFixed(2)}`} )} @@ -715,12 +764,18 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {/* 右侧圆圈图标 */} - handleOperatorSelect("mtn")}> + handleOperatorSelect("mtn")} + > {selectedOperator === "mtn" && ( @@ -735,24 +790,37 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { - - handleOperatorSelect("mtn")}> + handleOperatorSelect("mtn")} + > {selectedOperator === "mtn" && ( - + )} @@ -763,24 +831,37 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { - - handleOperatorSelect("mtn")}> + handleOperatorSelect("mtn")} + > {selectedOperator === "mtn" && ( - + )} @@ -798,7 +879,10 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { Annuler - + Confirmer @@ -808,33 +892,39 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { {/* 充值金额信息 */} - Résumé de la recharge - + + Résumé de la recharge + + Montant: {paymentParams?.selectedPriceLabel || selectedPrice + " FCFA"} - + {paymentParams?.currency !== "FCFA" && ( - Montant converti: + + Montant converti: + {paymentParams?.currency === "USD" ? "$" : "€"} {paymentParams?.amount.toFixed(2) || "0.00"} )} - + - Méthode de paiement: + + Méthode de paiement: + {paymentParams?.payment_method || "Non sélectionné"} - + {/* 电话号码输入 */} Numéro de téléphone @@ -852,10 +942,12 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { /> - + {/* 支持的运营商 */} - Opérateurs pris en charge + + Opérateurs pris en charge + { /> - + {/* 支付按钮 */} @@ -882,12 +977,14 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => { ) : ( - PAYER {paymentParams?.currency === "FCFA" - ? paymentParams.originalAmount.toLocaleString() + " " + paymentParams.currency - : (paymentParams?.currency === "USD" - ? "$" + paymentParams?.amount.toFixed(2) - : paymentParams?.amount.toFixed(2) + " €") - } + PAYER{" "} + {paymentParams?.currency === "FCFA" + ? paymentParams.originalAmount.toLocaleString() + + " " + + paymentParams.currency + : paymentParams?.currency === "USD" + ? "$" + paymentParams?.amount.toFixed(2) + : paymentParams?.amount.toFixed(2) + " €"} )} @@ -1044,7 +1141,7 @@ const styles = StyleSheet.create({ flexDirection: "row", alignItems: "center", justifyContent: "space-between", - paddingRight:6 + paddingRight: 6, }, currencyImage: { width: 80, @@ -1250,17 +1347,17 @@ const styles = StyleSheet.create({ }, container2: { - width: '100%', + width: "100%", marginTop: 20, }, amountRechargeContainer: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - width: '100%', + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + width: "100%", height: 60, - backgroundColor: 'white', + backgroundColor: "white", borderRadius: 5, }, rechargePromptTextStyle: { @@ -1269,13 +1366,13 @@ const styles = StyleSheet.create({ margin: 0, fontSize: 14, lineHeight: 14, - color: '#747474', + color: "#747474", // Note: PingFang SC font might not be available by default in React Native // You may need to load custom fonts using expo-font or other methods }, tabContainer: { - flexDirection: 'row', - backgroundColor: 'white', + flexDirection: "row", + backgroundColor: "white", borderRadius: 5, marginTop: 10, padding: 5, @@ -1283,51 +1380,50 @@ const styles = StyleSheet.create({ tab: { flex: 1, paddingVertical: 10, - alignItems: 'center', + alignItems: "center", borderRadius: 5, }, activeTab: { - backgroundColor: '#002fa7', + backgroundColor: "#002fa7", }, tabText: { fontSize: fontSize(14), - color: '#333', - fontWeight: '500', + color: "#333", + fontWeight: "500", }, activeTabText: { - color: 'white', - fontWeight: '600', + color: "white", + fontWeight: "600", }, emptyTab: { - backgroundColor: 'white', + backgroundColor: "white", borderRadius: 5, padding: 20, marginTop: 10, - alignItems: 'center', - justifyContent: 'center', + alignItems: "center", + justifyContent: "center", height: 200, }, emptyTabText: { - color: '#666', + color: "#666", fontSize: fontSize(14), }, outerContainer: { - width: '100%', + width: "100%", }, flexContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-start', + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-start", height: 50, paddingRight: 16, paddingLeft: 16, - backgroundColor: 'white', + backgroundColor: "white", borderRadius: 5, marginTop: 18, - }, imageContainer: { - width: '26.54%', + width: "26.54%", }, imageStyle: { width: 80, @@ -1335,9 +1431,9 @@ const styles = StyleSheet.create({ borderWidth: 0, }, verticalAlignEndContent: { - flexDirection: 'column', - alignItems: 'flex-end', - width: '73.46%', + flexDirection: "column", + alignItems: "flex-end", + width: "73.46%", }, svgContainer: { width: 24, @@ -1368,18 +1464,18 @@ const styles = StyleSheet.create({ paddingRight: 20, paddingBottom: 333, paddingLeft: 20, - backgroundColor: 'white', + backgroundColor: "white", borderTopLeftRadius: 10, borderTopRightRadius: 10, }, paymentSectionContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-start', - width: '100%', + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-start", + width: "100%", }, paymentSection: { - width: '8.48%', + width: "8.48%", paddingRight: 15, }, svgContainer1: { @@ -1387,10 +1483,10 @@ const styles = StyleSheet.create({ height: 18, }, paymentSection1: { - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'flex-start', - width: '91.52%', + flexDirection: "column", + alignItems: "center", + justifyContent: "flex-start", + width: "91.52%", paddingRight: 33, }, paymentHeadingStyle: { @@ -1398,24 +1494,25 @@ const styles = StyleSheet.create({ margin: 0, fontSize: 24, lineHeight: 22, - color: 'black', - textTransform: 'capitalize', - fontFamily: 'Montserrat-Bold', + color: "black", + textTransform: "capitalize", + fontFamily: "Montserrat-Bold", }, transactionSummaryContainer1: { - width: '100%', + width: "100%", }, transactionSummaryContainer: { - width: '100%', + width: "100%", paddingRight: 15, paddingLeft: 15, - backgroundColor: 'linear-gradient(90deg, rgba(206, 229, 255, 1) 0%, rgba(238, 244, 255, 1) 100%', + backgroundColor: + "linear-gradient(90deg, rgba(206, 229, 255, 1) 0%, rgba(238, 244, 255, 1) 100%", borderRadius: 5, }, flexContainerWithImages: { - flexDirection: 'row', - alignItems: 'flex-start', - justifyContent: 'flex-start', + flexDirection: "row", + alignItems: "flex-start", + justifyContent: "flex-start", }, imageContainerStyled: { width: 99, @@ -1429,13 +1526,13 @@ const styles = StyleSheet.create({ borderWidth: 0, }, amountContainer: { - width: '100%', + width: "100%", paddingTop: 8, paddingRight: 11, paddingBottom: 10, paddingLeft: 11, marginTop: -83, - backgroundColor: 'white', + backgroundColor: "white", borderWidth: 1, borderRadius: 5, }, @@ -1443,13 +1540,13 @@ const styles = StyleSheet.create({ padding: 0, margin: 0, fontSize: 12, - color: '#7f7e7e', - fontFamily: 'PingFangSC-Medium', + color: "#7f7e7e", + fontFamily: "PingFangSC-Medium", }, amountContainer1: { - flexDirection: 'row', - alignItems: 'flex-start', - justifyContent: 'flex-start', + flexDirection: "row", + alignItems: "flex-start", + justifyContent: "flex-start", marginTop: 1, }, priceHeading: { @@ -1458,8 +1555,8 @@ const styles = StyleSheet.create({ margin: 0, fontSize: 24, lineHeight: 22, - color: '#161616', - fontFamily: 'Montserrat-Bold', + color: "#161616", + fontFamily: "Montserrat-Bold", }, priceLabel: { padding: 0, @@ -1467,41 +1564,41 @@ const styles = StyleSheet.create({ margin: 0, marginLeft: 3, fontSize: 12, - color: '#7f7e7e', - fontFamily: 'PingFangSC-Medium', + color: "#7f7e7e", + fontFamily: "PingFangSC-Medium", }, mobileInfoSection: { - width: '100%', + width: "100%", marginTop: 30, }, mobileNumberSection: { - width: '100%', + width: "100%", }, mobileNumberLabel: { padding: 0, margin: 0, fontSize: 14, lineHeight: 18, - color: 'black', - fontFamily: 'PingFangSC-Regular', + color: "black", + fontFamily: "PingFangSC-Regular", }, infoCard: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-start', - width: '100%', + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-start", + width: "100%", height: 50, paddingRight: 12, paddingLeft: 12, marginTop: 12, borderWidth: 1, - borderColor: '#d8d8d8', + borderColor: "#d8d8d8", borderRadius: 25, }, flexRowWithIcon: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-start', + flexDirection: "row", + alignItems: "center", + justifyContent: "flex-start", }, maskedImageWithText: { width: 20, @@ -1514,8 +1611,8 @@ const styles = StyleSheet.create({ marginLeft: 3, fontSize: 16, lineHeight: 22, - color: '#1c284e', - fontFamily: 'PingFangSC-Semibold', + color: "#1c284e", + fontFamily: "PingFangSC-Semibold", }, svgContainer2: { width: 12, @@ -1527,7 +1624,7 @@ const styles = StyleSheet.create({ height: 30, marginLeft: 8.5, borderLeftWidth: 1, - borderColor: '#b1b1b1', + borderColor: "#b1b1b1", }, statisticText: { padding: 0, @@ -1535,18 +1632,18 @@ const styles = StyleSheet.create({ marginLeft: 19.5, fontSize: 16, lineHeight: 22, - color: '#1c284e', - fontFamily: 'PingFangSC-Semibold', + color: "#1c284e", + fontFamily: "PingFangSC-Semibold", }, mobileOperatorsContainer: { - width: '100%', + width: "100%", marginTop: 20, }, operatorSupportContainer: { - flexDirection: 'row', + flexDirection: "row", gap: 10, - alignItems: 'center', - justifyContent: 'flex-start', + alignItems: "center", + justifyContent: "flex-start", marginTop: 12, }, imageContainerWithBorder1: { @@ -1555,26 +1652,26 @@ const styles = StyleSheet.create({ borderWidth: 0, }, blueBoxContainer: { - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'flex-start', - width: '100%', + flexDirection: "column", + alignItems: "center", + justifyContent: "flex-start", + width: "100%", marginTop: 50, - backgroundColor: '#002fa7', + backgroundColor: "#002fa7", borderRadius: 25, }, paymentNotice1: { - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', + flexDirection: "column", + alignItems: "center", + justifyContent: "center", height: 50, }, paymentNotice: { padding: 0, margin: 0, fontSize: 16, - color: 'white', - fontFamily: 'Montserrat-Bold', + color: "white", + fontFamily: "Montserrat-Bold", }, paymentConfirmContainer: { flex: 1, diff --git a/app/screens/login/Google.tsx b/app/screens/login/Google.tsx index 853f6ed..e29cb35 100644 --- a/app/screens/login/Google.tsx +++ b/app/screens/login/Google.tsx @@ -4,9 +4,10 @@ import { WebView } from "react-native-webview"; import { useNavigation } from "@react-navigation/native"; import { loginApi } from "../../services/api/login"; import { useAuth } from "../../contexts/AuthContext"; +import AsyncStorage from "@react-native-async-storage/async-storage"; export const GoogleScreen = () => { - const navigation = useNavigation(); + const navigation = useNavigation(); const { login } = useAuth(); const [loading, setLoading] = useState(true); const [loginUrl, setLoginUrl] = useState(null); @@ -15,27 +16,29 @@ export const GoogleScreen = () => { const fetchLoginUrl = async () => { try { const response = await loginApi.google(); - if (response.data.url) { - setLoginUrl(response.data.url); + + + if (response.url) { + setLoginUrl(response.url); } } catch (error) { - console.error('Failed to fetch login URL:', error); + console.error("Failed to fetch login URL:", error); } }; fetchLoginUrl(); }, []); - const handleNavigationStateChange = async (navState: any) => { + const handleNavigationStateChange = async (navState: any) => { console.log(navState.url); - + // 检查URL是否包含重定向URI - if (navState.url.includes('localhost:8000')) { + if (navState.url.includes("localhost:8000")) { try { await login(); - navigation.navigate('MainTabs' as never); + navigation.navigate("MainTabs" as never); } catch (error) { - console.error('Login failed:', error); + console.error("Login failed:", error); } } }; @@ -60,13 +63,38 @@ export const GoogleScreen = () => { domStorageEnabled={true} startInLoadingState={true} scalesPageToFit={true} - originWhitelist={['*']} + originWhitelist={["*"]} + incognito={true} + thirdPartyCookiesEnabled={false} userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36" onShouldStartLoadWithRequest={(request) => { - console.log(request); - - // 允许所有请求 - return true; + const { url } = request; + // 拦截 myapp://login-success + if (url.startsWith("myapp://login-success")) { + console.log("拦截成功!跳转地址:", url); + + // 提取 token 参数 + const urlParams = new URLSearchParams(url.split('?')[1]); + const token = urlParams.get('token'); + + // 如果有 token 则登录成功,否则跳转回登录页 + if (token) { + const tokens = "bearer " + token; + AsyncStorage.setItem("token", tokens); + // 登录成功,更新认证状态 + login().then(() => { + // 跳转到主页 + navigation.replace("MainTabs", { screen: "Home" }); + }); + } else { + // 登录失败,跳转回登录页 + navigation.replace("Login"); + } + + return false; // 阻止 WebView 加载这个链接 + } + + return true; // 其他 URL 继续加载 }} /> @@ -79,9 +107,9 @@ const styles = StyleSheet.create({ }, loadingContainer: { flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'white', + justifyContent: "center", + alignItems: "center", + backgroundColor: "white", }, webview: { flex: 1, diff --git a/app/screens/loginList/PhoneLoginModal.tsx b/app/screens/loginList/PhoneLoginModal.tsx index ba61d11..2c61d93 100644 --- a/app/screens/loginList/PhoneLoginModal.tsx +++ b/app/screens/loginList/PhoneLoginModal.tsx @@ -240,7 +240,7 @@ const PhoneLoginModal = ({ visible, onClose }: PhoneLoginModalProps) => { const user = await userApi.getProfile(); setUser(user); setLoading(false); - navigation.navigate("MainTabs", { screen: "Home" }); + navigation.replace("MainTabs", { screen: "Home" }); onClose(); logLogin(true, "phone"); diff --git a/app/screens/pay/Pay.tsx b/app/screens/pay/Pay.tsx index 34f7e0c..231c8b2 100644 --- a/app/screens/pay/Pay.tsx +++ b/app/screens/pay/Pay.tsx @@ -21,12 +21,8 @@ export const Pay = () => { const [payInfo, setPayInfo] = useState(); useEffect(() => { - console.log(route.params); - console.log(payUrl); - // 设置处理深度链接的监听器 const handleDeepLink = ({ url }: { url: string }) => { - console.log("Deep link received:", url); if ( url.includes("myapp://payment-success") || url.includes("exp://192.168.0.101:8084/--/payment-success") @@ -47,8 +43,6 @@ export const Pay = () => { }, []); const handleNavigationStateChange = (navState: any) => { - console.log(navState); - // 检查URL是否包含支付成功的回调参数 const { url } = navState; if (url && url.includes("payment_success=true")) { @@ -60,30 +54,22 @@ export const Pay = () => { // 导航辅助函数,尝试使用多种方式导航 const safeNavigate = (routeName: string, params: any) => { - console.log(`尝试导航到 ${routeName},参数:`, params); - console.log(`navigationRef准备状态: ${navigationRef.isReady()}`); try { // 尝试使用组件内的navigation - console.log("使用组件内navigation导航"); // @ts-ignore 忽略可能的类型错误 navigation.navigate(routeName, params); } catch (e) { - console.log("组件内导航失败:", e); try { // 尝试使用全局navigation - console.log("使用全局navigate导航"); navigate(routeName, params); } catch (e) { - console.log("全局导航也失败:", e); // 最后尝试使用setTimeout延迟导航 - console.log("尝试延迟导航"); setTimeout(() => { try { // @ts-ignore 忽略可能的类型错误 navigation.navigate(routeName, params); } catch (e) { - console.log("所有导航方式都失败"); Alert.alert("导航失败", "无法跳转到目标页面"); } }, 500); @@ -107,21 +93,15 @@ export const Pay = () => { originWhitelist={["*"]} userAgent="Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36" onShouldStartLoadWithRequest={(request) => { - console.log("Load request:", request); - // 检查URL是否包含支付成功的参数 - const { url } = request; - + const { url } = request; if (url) { // 解析参数 const parsed = Linking.parse(url); const params = parsed.queryParams || {}; - console.log("解析的URL参数:", params); // 检查是否存在paymentId参数并且不为null if (params.paymentId && params.paymentId !== "null") { - console.log("检测到有效的paymentId:", params.paymentId); - Alert.alert("支付成功"); if (params.PayerID && params.PayerID !== "null") { payApi .paySuccessCallback( diff --git a/app/screens/previewOrder/PaymentMethod.tsx b/app/screens/previewOrder/PaymentMethod.tsx index e7067d2..012884c 100644 --- a/app/screens/previewOrder/PaymentMethod.tsx +++ b/app/screens/previewOrder/PaymentMethod.tsx @@ -510,8 +510,10 @@ export const PaymentMethod = () => { .createOrder(createOrderData as unknown as CreateOrderRequest) .then((res) => { setCreateLoading(false); - logPaymentConfirm(data); + logPaymentConfirm(data,navigation.getState().routes[navigation.getState().index - 1]?.name as string); // go to payment preview + console.log(getBurialPointData()); + navigation.navigate("PreviewOrder", { data: res, payMethod: selectedPayment, diff --git a/app/screens/previewOrder/PreviewAddress.tsx b/app/screens/previewOrder/PreviewAddress.tsx index db45a3f..2044336 100644 --- a/app/screens/previewOrder/PreviewAddress.tsx +++ b/app/screens/previewOrder/PreviewAddress.tsx @@ -225,7 +225,8 @@ export const PreviewAddress = () => { phone_number: Number(formData.receiver_phone), whatsApp_number: Number(formData.whatsapp_phone), } - logAddressInfo(logData); + logAddressInfo(logData,navigation.getState().routes[navigation.getState().index - 1]?.name as string); + console.log(getBurialPointData()); navigation.navigate("ShippingFee",{ cart_item_id: route.params, diff --git a/app/screens/previewOrder/ShippingFee.tsx b/app/screens/previewOrder/ShippingFee.tsx index bd089e3..232ade9 100644 --- a/app/screens/previewOrder/ShippingFee.tsx +++ b/app/screens/previewOrder/ShippingFee.tsx @@ -68,13 +68,9 @@ export const ShippingFee = () => { const [selectedWarehouse, setSelectedWarehouse] = useState
(); const [domesticShippingFeeData, setDomesticShippingFeeData] = useState(); - const [isDomesticShippingLoading, setIsDomesticShippingLoading] = + const [isShippingFeeLoading, setIsShippingFeeLoading] = useState(false); const [count, setCount] = useState(); - const [apiResponses, setApiResponses] = useState({ - shippingFees: false, - domesticShippingFees: false, - }); const { setOrderData, orderData, items } = useCreateOrderStore(); const [countryCode, setCountryCode] = useState(); @@ -110,24 +106,21 @@ export const ShippingFee = () => { useEffect(() => { if (state.shippingFees) { setShippingFeeData(state.shippingFees); - setCount(t("order.shipping.calculating")); - setApiResponses((prev) => ({ ...prev, shippingFees: true })); } - }, [state.shippingFees, t]); + }, [state.shippingFees]); useEffect(() => { if (state.domesticShippingFees) { setDomesticShippingFeeData(state.domesticShippingFees); - setApiResponses((prev) => ({ ...prev, domesticShippingFees: true })); } }, [state.domesticShippingFees]); - // Effect to handle loading state based on both API responses + // 统一处理loading状态 useEffect(() => { - if (apiResponses.shippingFees && apiResponses.domesticShippingFees) { - setIsDomesticShippingLoading(false); + if (state.shippingFees && state.domesticShippingFees) { + setIsShippingFeeLoading(false); } - }, [apiResponses]); + }, [state.shippingFees, state.domesticShippingFees]); // Call changeCountryHandel when warehouse changes useEffect(() => { @@ -152,14 +145,9 @@ export const ShippingFee = () => { // Only calculate if we have the necessary data if (data.items && data.freight_forwarder_address_id) { - // Set loading state to true before making API calls - setIsDomesticShippingLoading(true); + // 设置loading状态为true,开始计算 + setIsShippingFeeLoading(true); setCount(t("order.shipping.calculating")); - // Reset API response tracking - setApiResponses({ - shippingFees: false, - domesticShippingFees: false, - }); calculateShippingFee(data); calculateDomesticShippingFee(data); @@ -177,7 +165,7 @@ export const ShippingFee = () => { const handleSubmit = () => { if ( - !isDomesticShippingLoading && + !isShippingFeeLoading && domesticShippingFeeData?.total_shipping_fee != null ) { setOrderData({ @@ -202,8 +190,8 @@ export const ShippingFee = () => { country_city: selectedWarehouseLabel, timestamp: new Date().toISOString(), }; - logShippingConfirm(data); - + logShippingConfirm(data,navigation.getState().routes[navigation.getState().index - 1]?.name as string); + console.log(getBurialPointData()); navigation.navigate("PaymentMethod", { freight_forwarder_address_id: selectedWarehouse?.address_id || 0, }); @@ -379,84 +367,61 @@ export const ShippingFee = () => { - - - {t("order.shipping.domestic_fee")}:{" "} - - - {isDomesticShippingLoading ? ( + {isShippingFeeLoading ? ( + // 统一显示一个加载状态 + + + {count} + + + + ) : ( + // 加载完成后同时显示两个费用 + <> + + + {t("order.shipping.domestic_fee")}:{" "} + - {count}{" "} - + + {domesticShippingFeeData?.total_shipping_fee || + 0}{" "} + {userStore.user?.currency} + - ) : ( - - {domesticShippingFeeData?.total_shipping_fee || - 0}{" "} + + + + {t("order.shipping.international_fee")}:{" "} + + + {shippingMethod === "sea" + ? shippingFeeData?.total_shipping_fee_sea + : shippingFeeData?.total_shipping_fee_air}{" "} {userStore.user?.currency} - )} - - - {isDomesticShippingLoading ? ( - - - {t("order.shipping.international_fee")}:{" "} - - - {count}{" "} - - - - ) : ( - - - {t("order.shipping.international_fee")}:{" "} - - - {shippingMethod === "sea" - ? shippingFeeData?.total_shipping_fee_sea - : shippingFeeData?.total_shipping_fee_air}{" "} - {userStore.user?.currency} - - + + )} )} @@ -467,14 +432,14 @@ export const ShippingFee = () => { @@ -755,4 +720,15 @@ const styles = StyleSheet.create({ disabledButtonStyle: { backgroundColor: "#ccc", }, + loadingFeesContainer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginVertical: 15, + }, + calculatingText: { + color: "#ff6000", + fontSize: fontSize(14), + fontWeight: "500" + }, }); diff --git a/app/screens/previewOrder/perviewOrder.tsx b/app/screens/previewOrder/perviewOrder.tsx index 7b70cb7..565857d 100644 --- a/app/screens/previewOrder/perviewOrder.tsx +++ b/app/screens/previewOrder/perviewOrder.tsx @@ -23,6 +23,8 @@ import { Order } from "../../services/api/orders"; import { payApi } from "../../services/api/payApi"; import { useTranslation } from "react-i18next"; import fontSize from "../../utils/fontsizeUtils"; +import { getBurialPointData } from "../../store/burialPoint"; +import useBurialPointStore from "../../store/burialPoint"; // Define the param list for navigation type RootStackParamList = { @@ -46,7 +48,7 @@ export const PreviewOrder = () => { const [loading, setLoading] = useState(false); const { user } = useUserStore(); const { t } = useTranslation(); - + const { logPreviewOrder } = useBurialPointStore(); useEffect(() => { if (!user.user_id) { return Alert.alert(t("order.preview.login_required")); @@ -97,8 +99,10 @@ export const PreviewOrder = () => { .getPayInfo(data) .then((res) => { if (res.success) { + logPreviewOrder(navigation.getState().routes[navigation.getState().index - 1]?.name as string); + console.log(getBurialPointData()); - navigation.navigate("Pay", { + navigation.replace("Pay", { payUrl: res.payment_url, }); } diff --git a/app/screens/productStatus/OrderDatails.tsx b/app/screens/productStatus/OrderDatails.tsx index 89f9dc4..fe5fad9 100644 --- a/app/screens/productStatus/OrderDatails.tsx +++ b/app/screens/productStatus/OrderDatails.tsx @@ -248,7 +248,7 @@ export const OrderDetails = () => { = ({ statuses, labels = [] }) => { {index < labels.length - 1 && ( )} diff --git a/app/services/api/categories.ts b/app/services/api/categories.ts index 75194b6..4f0c161 100644 --- a/app/services/api/categories.ts +++ b/app/services/api/categories.ts @@ -7,6 +7,7 @@ export interface Category { name_en: string; level: number; is_leaf: boolean; + image_url?: string; } export const categoriesApi = { diff --git a/app/services/api/chat.ts b/app/services/api/chat.ts index 45bdf3b..bbb831c 100644 --- a/app/services/api/chat.ts +++ b/app/services/api/chat.ts @@ -4,15 +4,19 @@ export interface ChatMessage { mimetype: string; userWs: string; app_id: string; - country: string; + country: number; body: string; text: string; } + +export interface ChatMessageData { + newMessage:ChatMessage; +} // API methods export const chatService = { // Send message with retry mechanism - async sendMessage(newMessage:ChatMessage): Promise { - return apiService.post('https://api.brainnel.com/app_chat/chat/',newMessage); + async sendMessage(newMessage:ChatMessageData): Promise { + return apiService.post('https://api.brainnel.com/app_chat/chat',newMessage); }, }; diff --git a/app/services/api/login.tsx b/app/services/api/login.tsx index 1cd6910..61a1afd 100644 --- a/app/services/api/login.tsx +++ b/app/services/api/login.tsx @@ -1,8 +1,10 @@ -import axios from "axios"; - +import apiService from "./apiClient"; +const GOOGLE_CLIENT_ID = '529750832779-osemooqkar78m8lhrn9dvbvi98fr4g08.apps.googleusercontent.com'; +const GOOGLE_CLIENT_SECRET = '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'; +const GOOGLE_REDIRECT_URI = 'https://api.brainnel.com/backend/api/users/auth/callback/google'; export const loginApi = { google:() => { - return axios.get<{url:string}>('http://124.70.102.7:8000/api/auth/google') + return apiService.get<{url:string}>(`/api/users/auth/google`) } }; \ No newline at end of file diff --git a/app/services/api/payApi.ts b/app/services/api/payApi.ts index 80d49f0..e0123ff 100644 --- a/app/services/api/payApi.ts +++ b/app/services/api/payApi.ts @@ -72,6 +72,18 @@ export interface PaySuccessCallbackBody { status: number; } +export interface rechargeHistory { + recharge_id: number; + user_id: number; + amount: number; + currency: string; + payment_method: string; + status: number; + transaction_id: string; + create_time: string; + update_time: string; +} + export const payApi = { // 获取当前国家支付方式 @@ -91,11 +103,16 @@ export const payApi = { // 支付成功的回调 paySuccessCallback: (paymentId:string,PayerID:string) => { - return apiService.get(`/api/payment/paypal/execute/?paymentId=${paymentId}&PayerID=${PayerID}`); + return apiService.post(`/api/payment/paypal/execute/`,{paymentId,PayerID}); }, // 新增充值接口 initiateRecharge: (data: RechargeInitiateBody) => { return apiService.post('/api/recharge/initiate/', data); }, + + // 获取充值历史 + getRechargeHistory: () => { + return apiService.get('/api/recharge/records/'); + }, }; diff --git a/app/services/api/userApi.ts b/app/services/api/userApi.ts index 9c8eb68..9bf026a 100644 --- a/app/services/api/userApi.ts +++ b/app/services/api/userApi.ts @@ -10,7 +10,9 @@ export interface User { "user_id": number, "last_login": string, "create_time": string, - "update_time": string + "update_time": string, + country_code:number, + balance:number, currency:string, country:string, country_en:string, diff --git a/app/store/burialPoint.ts b/app/store/burialPoint.ts index f828da6..cd102b2 100644 --- a/app/store/burialPoint.ts +++ b/app/store/burialPoint.ts @@ -229,8 +229,8 @@ const useBurialPointStore = create((set, get) => ({ // 记录浏览商品事件埋点 logViewProduct: (productInfo: ProductProperty, fromPage = "home") => { const viewProductEvent: BurialEvent = { - event_name: "view-product", - page_name: "product", + event_name: "product_view", + page_name: "ProductDetail", referre_page: fromPage, event_properties: [productInfo] }; @@ -259,7 +259,7 @@ const useBurialPointStore = create((set, get) => ({ logAddressInfo: (addressInfo: Omit, fromPage = "cart") => { const addressEvent: BurialEvent = { event_name: "fill_information", - page_name: "address", + page_name: "PreviewAddress", referre_page: fromPage, event_properties: [ { @@ -276,7 +276,7 @@ const useBurialPointStore = create((set, get) => ({ logShippingConfirm: (shippingInfo: Omit, fromPage = "address") => { const shippingEvent: BurialEvent = { event_name: "shipping_confirm", - page_name: "shipping", + page_name: "ShippingFee", referre_page: fromPage, event_properties: [ { @@ -293,7 +293,7 @@ const useBurialPointStore = create((set, get) => ({ logPaymentConfirm: (paymentInfo: Omit, fromPage = "shipping") => { const paymentEvent: BurialEvent = { event_name: "payment_confirm", - page_name: "pay_method", + page_name: "PaymentMethod", referre_page: fromPage, event_properties: [ { @@ -310,7 +310,7 @@ const useBurialPointStore = create((set, get) => ({ logPreviewOrder: (fromPage = "pay_method") => { const previewEvent: BurialEvent = { event_name: "preview_order", - page_name: "preview", + page_name: "PreviewOrder", referre_page: fromPage, event_properties: [ { diff --git a/assets/img/1034058.png b/assets/img/1034058.png new file mode 100644 index 0000000..393c2d0 Binary files /dev/null and b/assets/img/1034058.png differ diff --git a/assets/img/category_24.jpg b/assets/img/category_24.jpg new file mode 100644 index 0000000..71d44d7 Binary files /dev/null and b/assets/img/category_24.jpg differ