Browse Source

Merge remote changes

main
unknown 4 weeks ago
parent
commit
e382443a94
  1. 2
      app.json
  2. 5
      app/constants/config.ts
  3. 104
      app/locales/en/translation.json
  4. 104
      app/locales/fr/translation.json
  5. 104
      app/locales/zh/translation.json
  6. 7
      app/screens/HomeScreen.tsx
  7. 4
      app/screens/LoginScreen.tsx
  8. 164
      app/screens/ProductCard.tsx
  9. 13
      app/screens/ProductDetailScreen.tsx
  10. 8
      app/services/api/addressApi.ts
  11. 108
      app/services/api/apiClient.ts
  12. 10
      app/services/api/cart.ts
  13. 20
      app/services/api/orders.ts
  14. 4
      app/services/api/payApi.ts
  15. 4
      app/services/api/productApi.ts
  16. 12
      app/services/api/setting.ts
  17. 10
      app/services/api/userApi.ts
  18. 9
      app/types/global.d.ts
  19. 27
      app/utils/languageUtils.ts
  20. BIN
      assets/img/banner en (3)_compressed.png
  21. BIN
      assets/img/banner en (4)_compressed.png
  22. BIN
      assets/img/banner en (5)_compressed.png
  23. 12080
      package-lock.json
  24. 6
      package.json
  25. 4812
      yarn.lock

2
app.json

@ -27,6 +27,8 @@
}
},
"android": {
"permissions": ["INTERNET", "ACCESS_NETWORK_STATE"],
"usesCleartextTraffic": true,
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"

5
app/constants/config.ts

@ -1,5 +1,8 @@
// API配置
export const API_BASE_URL = 'http://ec2-13-245-224-109.af-south-1.compute.amazonaws.com:8080/'; // 替换为您的实际API基础URL
const DEV_API_URL = 'https://api.brainnel.com/backend';
const PROD_API_URL = 'https://api.brainnel.com/backend';
export const API_BASE_URL = PROD_API_URL
// 环境变量配置
export const ENV = {

104
app/locales/en/translation.json

@ -0,0 +1,104 @@
{
"selectCountry": "Select your country",
"subtitle": "You can change the country & language in your profile settings anytime.",
"welcomeTitle": "Welcome!",
"welcomeMessage": "Thank you for choosing your country. You can now log in and use the app.",
"loginNow": "Log in now",
"mainAppTitle": "Welcome to MainApp",
"mainAppText": "This is the main application screen.",
"resetCountry": "Reset Country Selection",
"loginTitle": "Login For brainnel",
"loginSubtitle": "Login to start your business",
"continueWithGoogle": "Continue with Google",
"continueWithFacebook": "Continue with Facebook",
"continueWithApple": "Continue with Apple",
"continueWithInstagram": "Continue with Instagram",
"continueWithEmail": "Continue with Email",
"continueWithPhone": "Continue with phone number",
"orContinueWith": "Or continue with",
"instagram": "Instagram",
"email": "Email",
"phone": "Phone",
"forgotPassword": "Forget your password?",
"termsText": "By continuing, you agree to our",
"termsOfUse": "Terms of Use",
"and": "and",
"privacyPolicy": "Privacy Policy",
"wholesalePrice": "Wholesale price",
"fastShipping": "Fast shipping",
"shipping": "Shipping",
"quote": "Quote",
"tiktok": "TikTok",
"howToBuy": "How to Buy",
"all": "All",
"electronics": "Electronics",
"clothing": "Clothing",
"home": "Home",
"beauty": "Beauty",
"kids": "Kids",
"logInOrSignUp": "Log in or sign up",
"phoneNumber": "Phone number",
"enterPassword": "Please re-enter your password",
"passwordIncorrect": "Password incorrect, please confirm your password.",
"verificationCodeInfo": "We will send a verification code on your number to confirm it's you.",
"continue": "Continue",
"pleaseEnterEmail": "Please enter your e-mail address",
"searchProducts": "Search products",
"priceRange": "Price range",
"minPrice": "Min price",
"maxPrice": "Max price",
"reset": "Reset",
"apply": "Apply",
"price": "Price",
"lowToHigh": "Low to high",
"highToLow": "High to low",
"time": "Time",
"oldest": "Oldest",
"newest": "Newest",
"noResults": "No results found for",
"tryDifferentKeywords": "Try using different keywords or check your spelling",
"loadingMore": "Loading more...",
"noMoreData": "No more data",
"monthlySales": "ventes",
"search": "Search",
"searchPlaceholder": "Search products",
"cancel": "Cancel",
"searchHistory": "Search History",
"hotSearch": "Hot Search",
"noRecentSearches": "You have not recent searches",
"headphones": "Headphones",
"computer": "Computer",
"tablet": "Tablet",
"watch": "Watch",
"camera": "Camera",
"homeAppliance": "Home Appliance",
"food": "Food",
"summerWomenClothes": "Summer women clothes",
"plusSizeWomen": "Plus size women",
"sexyUnderwear": "Sexy underwear",
"homeDecor": "Home decor",
"unusualToys": "Unusual toys",
"productDetail": "Product Detail",
"addToCart": "Add to Cart",
"buyNow": "Buy Now",
"color": "Color",
"size": "Size",
"moreFromStore": "More from this Store",
"viewAll": "View All",
"loadingProductInfo": "Loading product information...",
"productNotAvailable": "Product is not available or has been removed",
"customerService": "Customer Service",
"productDetails": "Product Details",
"loadingMoreProducts": "Loading more products...",
"noMoreProducts": "No more products",
"chatNow": "Chat Now",
"popularCategories": "Popular Categories",
"setting": {
"title": "Select Language and Currency",
"country": "Country",
"currency": "Currency",
"language": "Language",
"confirm": "Confirm",
"success": "Settings saved successfully"
}
}

104
app/locales/fr/translation.json

@ -0,0 +1,104 @@
{
"selectCountry": "Sélectionnez votre pays",
"subtitle": "Vous pouvez modifier le pays et la langue dans vos paramètres de profil à tout moment.",
"welcomeTitle": "Bienvenue !",
"welcomeMessage": "Merci d'avoir choisi votre pays. Vous pouvez maintenant vous connecter et utiliser l'application.",
"loginNow": "Connectez-vous maintenant",
"mainAppTitle": "Bienvenue sur MainApp",
"mainAppText": "Ceci est l'écran principal de l'application.",
"resetCountry": "Réinitialiser la sélection du pays",
"loginTitle": "Connexion à Brainnel",
"loginSubtitle": "Connectez-vous pour démarrer votre activité",
"continueWithGoogle": "Continuer avec Google",
"continueWithFacebook": "Continuer avec Facebook",
"continueWithApple": "Continuer avec Apple",
"continueWithInstagram": "Continuer avec Instagram",
"continueWithEmail": "Continuer avec l'email",
"continueWithPhone": "Continuer avec le numéro de téléphone",
"orContinueWith": "Ou continuer avec",
"instagram": "Instagram",
"email": "Email",
"phone": "Téléphone",
"forgotPassword": "Mot de passe oublié ?",
"termsText": "En continuant, vous acceptez nos",
"termsOfUse": "Conditions d'utilisation",
"and": "et",
"privacyPolicy": "Politique de confidentialité",
"wholesalePrice": "Prix de gros",
"fastShipping": "Livraison rapide",
"shipping": "Livraison",
"quote": "Devis",
"tiktok": "TikTok",
"howToBuy": "Comment acheter",
"all": "Tout",
"electronics": "Électronique",
"clothing": "Vêtements",
"home": "Maison",
"beauty": "Beauté",
"kids": "Enfants",
"logInOrSignUp": "Se connecter ou s'inscrire",
"phoneNumber": "Numéro de téléphone",
"enterPassword": "Veuillez réentrer votre mot de passe",
"passwordIncorrect": "Mot de passe incorrect, veuillez confirmer votre mot de passe.",
"verificationCodeInfo": "Nous enverrons un code de vérification sur votre numéro pour confirmer que c'est vous.",
"continue": "Continuer",
"pleaseEnterEmail": "Veuillez entrer votre adresse e-mail",
"searchProducts": "Rechercher des produits",
"priceRange": "Fourchette de prix",
"minPrice": "Prix minimum",
"maxPrice": "Prix maximum",
"reset": "Réinitialiser",
"apply": "Appliquer",
"price": "Prix",
"lowToHigh": "Du plus bas au plus élevé",
"highToLow": "Du plus élevé au plus bas",
"time": "Temps",
"oldest": "Plus ancien",
"newest": "Plus récent",
"noResults": "Aucun résultat trouvé",
"tryDifferentKeywords": "Essayez d'utiliser des mots-clés différents ou vérifiez votre orthographe",
"loadingMore": "Chargement en cours...",
"noMoreData": "Plus de données",
"monthlySales": "Ventes mensuelles",
"search": "Rechercher",
"searchPlaceholder": "Rechercher des produits",
"cancel": "Annuler",
"searchHistory": "Historique de recherche",
"hotSearch": "Recherches populaires",
"noRecentSearches": "Vous n'avez pas de recherches récentes",
"headphones": "Casques",
"computer": "Ordinateur",
"tablet": "Tablette",
"watch": "Montre",
"camera": "Appareil photo",
"homeAppliance": "Électroménager",
"food": "Alimentation",
"summerWomenClothes": "Vêtements d'été pour femmes",
"plusSizeWomen": "Grandes tailles pour femmes",
"sexyUnderwear": "Lingerie sexy",
"homeDecor": "Décoration d'intérieur",
"unusualToys": "Jouets insolites",
"productDetail": "Détails du produit",
"addToCart": "Ajouter au panier",
"buyNow": "Acheter maintenant",
"color": "Couleur",
"size": "Taille",
"moreFromStore": "Plus de ce magasin",
"viewAll": "Voir tout",
"loadingProductInfo": "Chargement des informations du produit...",
"productNotAvailable": "Le produit n'est pas disponible ou a été supprimé",
"customerService": "Service client",
"productDetails": "Détails du produit",
"loadingMoreProducts": "Chargement de plus de produits...",
"noMoreProducts": "Plus de produits",
"chatNow": "Discuter maintenant",
"popularCategories": "Catégories populaires",
"setting": {
"title": "Sélectionner la langue et la devise",
"country": "Pays",
"currency": "Devise",
"language": "Langue",
"confirm": "Confirmer",
"success": "Paramètres enregistrés avec succès"
}
}

104
app/locales/zh/translation.json

@ -0,0 +1,104 @@
{
"selectCountry": "选择您的国家",
"subtitle": "您可以随时在个人资料设置中更改国家和语言。",
"welcomeTitle": "欢迎!",
"welcomeMessage": "感谢您选择国家。您现在可以登录并使用该应用。",
"loginNow": "立即登录",
"mainAppTitle": "欢迎使用 MainApp",
"mainAppText": "这是主应用界面。",
"resetCountry": "重置国家选择",
"loginTitle": "登录 Brainnel",
"loginSubtitle": "登录开始您的业务",
"continueWithGoogle": "使用 Google 继续",
"continueWithFacebook": "使用 Facebook 继续",
"continueWithApple": "使用 Apple 继续",
"continueWithInstagram": "使用 Instagram 继续",
"continueWithEmail": "使用邮箱继续",
"continueWithPhone": "使用手机号继续",
"orContinueWith": "或使用",
"instagram": "Instagram",
"email": "邮箱",
"phone": "手机",
"forgotPassword": "忘记密码?",
"termsText": "继续即表示您同意我们的",
"termsOfUse": "使用条款",
"and": "和",
"privacyPolicy": "隐私政策",
"wholesalePrice": "批发价",
"fastShipping": "快速配送",
"shipping": "配送",
"quote": "报价",
"tiktok": "抖音",
"howToBuy": "如何购买",
"all": "全部",
"electronics": "电子产品",
"clothing": "服装",
"home": "家居",
"beauty": "美妆",
"kids": "儿童",
"logInOrSignUp": "登录或注册",
"phoneNumber": "手机号码",
"enterPassword": "请重新输入密码",
"passwordIncorrect": "密码错误,请确认您的密码。",
"verificationCodeInfo": "我们将向您的号码发送验证码以确认是您本人。",
"continue": "继续",
"pleaseEnterEmail": "请输入您的电子邮箱地址",
"searchProducts": "搜索商品",
"priceRange": "价格范围",
"minPrice": "最低价",
"maxPrice": "最高价",
"reset": "重置",
"apply": "应用",
"price": "价格",
"lowToHigh": "从低到高",
"highToLow": "从高到低",
"time": "时间",
"oldest": "最早",
"newest": "最新",
"noResults": "未找到结果",
"tryDifferentKeywords": "请尝试使用不同的关键词或检查拼写",
"loadingMore": "加载更多...",
"noMoreData": "没有更多数据",
"monthlySales": "月销量",
"search": "搜索",
"searchPlaceholder": "搜索商品",
"cancel": "取消",
"searchHistory": "搜索历史",
"hotSearch": "热门搜索",
"noRecentSearches": "您没有最近的搜索记录",
"headphones": "耳机",
"computer": "电脑",
"tablet": "平板",
"watch": "手表",
"camera": "相机",
"homeAppliance": "家用电器",
"food": "食品",
"summerWomenClothes": "夏季女装",
"plusSizeWomen": "大码女装",
"sexyUnderwear": "性感内衣",
"homeDecor": "家居装饰",
"unusualToys": "新奇玩具",
"productDetail": "商品详情",
"addToCart": "加入购物车",
"buyNow": "立即购买",
"color": "颜色",
"size": "尺寸",
"moreFromStore": "更多来自该店铺",
"viewAll": "查看全部",
"loadingProductInfo": "正在加载商品信息...",
"productNotAvailable": "商品不可用或已被移除",
"customerService": "客服",
"productDetails": "商品详情",
"loadingMoreProducts": "正在加载更多商品...",
"noMoreProducts": "没有更多商品",
"chatNow": "立即聊天",
"popularCategories": "热门分类",
"setting": {
"title": "选择语言和货币",
"country": "国家",
"currency": "货币",
"language": "语言",
"confirm": "确认",
"success": "设置成功"
}
}

7
app/screens/HomeScreen.tsx

@ -69,16 +69,15 @@ export const HomeScreen = () => {
page: 1,
page_size: 20,
sort_order: "desc",
category_id: null,
sort_by: "default",
language: "en",
});
const [products, setProducts] = useState<Product[]>([]);
const flatListRef = useRef<FlatList>(null);
const data = [
{ imgUrl: require("../../assets/img/banner en (5).png"),add:'TikTokScreen' },
{ imgUrl: require("../../assets/img/banner en (3).png"),add:'MemberIntroduction' },
{ imgUrl: require("../../assets/img/banner en (4).png"),add:'CompanyScreen' },
{ imgUrl: require("../../assets/img/banner en (5)_compressed.png"),add:'TikTokScreen' },
{ imgUrl: require("../../assets/img/banner en (3)_compressed.png"),add:'MemberIntroduction' },
{ imgUrl: require("../../assets/img/banner en (4)_compressed.png"),add:'CompanyScreen' },
];
const getProductData = async () => {

4
app/screens/LoginScreen.tsx

@ -341,8 +341,8 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
if (res.access_token) {
const token = res.token_type + " " + res.access_token;
await AsyncStorage.setItem("token", token);
const data = await settingApi.postFirstLogin(221);
setSettings(data);
// const data = await settingApi.postFirstLogin(221);
// setSettings(data);
const user = await userApi.getProfile()
setUser(user);
navigation.navigate("MainTabs", { screen: "Home" });

164
app/screens/ProductCard.tsx

@ -286,84 +286,86 @@ const ProductCard: React.FC<ProductCardProps> = ({
<View style={styles.productTit}>
<Text style={styles.productTitText}>{sizeTitle}</Text>
</View>
<ScrollView
style={styles.sizePrice}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
>
<View style={styles.sizePriceBox}>
{hasImg &&
hasImg.attributes
?.find((item) => item.has_color)
?.list.map((list, index1) => (
<View style={styles.sizePriceBoxItems} key={index1}>
<View style={styles.sizePriceBoxItem}>
<View style={styles.sizePriceBoxItemTextBox}>
{(list?.size ?? 0) > 0 && (
<Text style={styles.selectedNumText}>
x{list?.size}
<View style={{ flex: 1 }}>
<ScrollView
style={styles.sizePrice}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
>
<View style={styles.sizePriceBox}>
{hasImg &&
hasImg.attributes
?.find((item) => item.has_color)
?.list.map((list, index1) => (
<View style={styles.sizePriceBoxItems} key={index1}>
<View style={styles.sizePriceBoxItem}>
<View style={styles.sizePriceBoxItemTextBox}>
{(list?.size ?? 0) > 0 && (
<Text style={styles.selectedNumText}>
x{list?.size}
</Text>
)}
<Text style={styles.priceText}>
{list.offer_price || price}
</Text>
)}
<Text style={styles.priceText}>
{list.offer_price || price}
<Text
style={styles.sizePriceBoxItemText}
numberOfLines={1}
ellipsizeMode="tail"
>
{list.attributes[0].value}
</Text>
</View>
<Text style={styles.amountText}>
{list?.amount_on_sale}
</Text>
<Text
style={styles.sizePriceBoxItemText}
numberOfLines={1}
ellipsizeMode="tail"
</View>
<View style={styles.sizePriceBoxStepForward}>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
handleSizeSelect(
list?.attributes[0]?.value,
"-",
index1,
list.amount_on_sale
)
}
>
{list.attributes[0].value}
</Text>
<Text>-</Text>
</TouchableOpacity>
<TextInput
style={styles.sizePriceBoxStepForwardInput}
value={list.size?.toString() ?? "0"}
keyboardType="numeric"
onChangeText={(text) =>
handleSizeSelect(
list?.attributes[0]?.value,
text,
index1,
list.amount_on_sale
)
}
/>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
handleSizeSelect(
list?.attributes[0]?.value,
"+",
index1,
list.amount_on_sale
)
}
>
<Text>+</Text>
</TouchableOpacity>
</View>
<Text style={styles.amountText}>
{list?.amount_on_sale}
</Text>
</View>
<View style={styles.sizePriceBoxStepForward}>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
handleSizeSelect(
list?.attributes[0]?.value,
"-",
index1,
list.amount_on_sale
)
}
>
<Text>-</Text>
</TouchableOpacity>
<TextInput
style={styles.sizePriceBoxStepForwardInput}
value={list.size?.toString() ?? "0"}
keyboardType="numeric"
onChangeText={(text) =>
handleSizeSelect(
list?.attributes[0]?.value,
text,
index1,
list.amount_on_sale
)
}
/>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
handleSizeSelect(
list?.attributes[0]?.value,
"+",
index1,
list.amount_on_sale
)
}
>
<Text>+</Text>
</TouchableOpacity>
</View>
</View>
))}
</View>
</ScrollView>
))}
</View>
</ScrollView>
</View>
</View>
)}
{/* 两个都是文字 */}
@ -397,7 +399,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<Text style={styles.productTitText}>{sizeTitle}</Text>
</View>
<ScrollView
style={styles.sizePrice}
style={[styles.sizePrice, { maxHeight: 200 }]}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
>
@ -483,7 +485,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<Text style={styles.productTitText}>{sizeTitle}</Text>
</View>
<ScrollView
style={styles.sizePriceBox}
style={[styles.sizePrice, { maxHeight: 200 }]}
showsVerticalScrollIndicator={false}
>
{noImgList &&
@ -565,7 +567,7 @@ const ProductCard: React.FC<ProductCardProps> = ({
<Text style={styles.productTitText}>{sizeTitle}</Text>
</View>
<ScrollView
style={styles.sizePriceBox}
style={[styles.sizePrice, { maxHeight: 200 }]}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
>
@ -846,6 +848,8 @@ const styles = StyleSheet.create({
productBox: {
marginTop: 10,
flex: 1,
display: 'flex',
flexDirection: 'column',
},
productTit: {
flexDirection: "row",
@ -885,11 +889,13 @@ const styles = StyleSheet.create({
height: "100%",
borderRadius: 10,
},
sizePrice: {},
sizePrice: {
flex: 1,
width: '100%',
},
sizePriceBox: {
width: "100%",
height: "100%",
flex: 1,
paddingBottom: 10,
},
sizePriceBoxItem: {
width: "70%",

13
app/screens/ProductDetailScreen.tsx

@ -248,9 +248,12 @@ export const ProductDetailScreen = () => {
};
const getProductDetail = async () => {
if (!route.params?.offer_id) return;
const startTime = Date.now();
console.log('开始时间:', startTime);
setIsLoading(true);
try {
const res = await productApi.getProductDetail(route.params.offer_id);
console.log('API 请求完成');
if (res.skus != null) {
const priceSelectedSku = res.skus.find(
(item) => item.offer_price === route.params.price
@ -294,10 +297,14 @@ export const ProductDetailScreen = () => {
const limitedImageUrls = imageUrls.length > 5 ? imageUrls.slice(0, 5) : imageUrls;
setImageUrls(limitedImageUrls);
setGroupList(list);
console.log('数据处理完成');
} catch (error) {
console.error("Error fetching product details:", error);
} finally {
setIsLoading(false);
const endTime = Date.now();
console.log('结束时间:', endTime);
console.log('总执行时间:', endTime - startTime, '毫秒');
}
};
const getSimilars = () => {
@ -348,11 +355,7 @@ export const ProductDetailScreen = () => {
},
[navigation]
);
useEffect(() => {
if (showBottomSheet) {
console.log(123);
}
}, [showBottomSheet])
return (
<View style={{ flex: 1 }}>
{isLoading ? (

8
app/services/api/addressApi.ts

@ -38,20 +38,20 @@ export interface addressData {
export const addressApi = {
getAddress: () => {
return apiService.get<Address>("/api/addresses");
return apiService.get<Address>("/api/addresses/");
},
postAddress: (data: any) => {
return apiService.post("/api/addresses", data );
return apiService.post("/api/addresses/", data );
},
addressesDefault: () => {
return apiService.get<addressData>("/api/addresses/default/");
},
updateAddress: (data: any) => {
return apiService.put(`/api/addresses/${data.address_id}`, data);
return apiService.put(`/api/addresses/${data.address_id}/`, data);
},
deleteAddress: (address_id: number) => {
return apiService.delete(`/api/addresses/${address_id}`);
return apiService.delete(`/api/addresses/${address_id}/`);
},
};

108
app/services/api/apiClient.ts

@ -11,6 +11,9 @@ import {
DEFAULT_HEADERS,
STORAGE_KEYS,
} from "../../constants/config";
import { Platform } from "react-native";
// import https from 'https';
// import { Platform } from "react-native";
// 定义响应类型接口
export interface ApiResponse<T = any> {
@ -27,6 +30,12 @@ export interface ApiError {
data?: any;
}
// 扩展 AxiosRequestConfig 类型
interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
retry?: number;
retryDelay?: (retryCount: number) => number;
}
// 创建axios实例
const apiClient: AxiosInstance = axios.create({
baseURL: API_BASE_URL,
@ -34,11 +43,26 @@ const apiClient: AxiosInstance = axios.create({
headers: DEFAULT_HEADERS,
});
// if (__DEV__ && Platform.OS === "android") {
// apiClient.defaults.httpsAgent = new https.Agent({ rejectUnauthorized: false });
// }
// 请求拦截器
apiClient.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
const fullUrl = `${config.baseURL}${config.url}`;
console.log("完整请求地址:", fullUrl); // 例如: https://api.example.com/api/user
const baseUrl = config.baseURL || '';
let fullUrl = baseUrl + config.url;
if (config.params) {
const params = new URLSearchParams(config.params);
fullUrl += (fullUrl.includes('?') ? '&' : '?') + params.toString();
}
// console.log("环境:", __DEV__ ? "开发环境" : "生产环境");
// console.log("平台:", Platform.OS);
// 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");
@ -49,6 +73,7 @@ apiClient.interceptors.request.use(
return config;
},
(error: AxiosError) => {
console.error("请求拦截器错误:", error);
return Promise.reject(error);
}
);
@ -57,19 +82,53 @@ apiClient.interceptors.request.use(
apiClient.interceptors.response.use(
(response: AxiosResponse) => {
// 成功响应处理
// console.log("响应成功:", {
// status: response.status,
// statusText: response.statusText,
// headers: response.headers,
// data: response.data,
// });
return response;
},
async (error: AxiosError<any>) => {
// 错误响应处理
const { response } = error;
const { response, config } = error;
console.error("响应错误:", {
status: response?.status,
statusText: response?.statusText,
data: response?.data,
url: config?.url,
method: config?.method,
message: error.message,
code: error.code,
stack: error.stack,
});
// 处理网络错误
if (!response) {
console.error("网络错误:", {
message: error.message,
code: error.code,
stack: error.stack,
});
// 如果是网络错误,尝试重试
const customConfig = config as CustomAxiosRequestConfig;
if (customConfig && customConfig.retry) {
customConfig.retry -= 1;
if (customConfig.retry > 0) {
const delay = customConfig.retryDelay ? customConfig.retryDelay(customConfig.retry) : 1000;
console.log(`重试请求 (${customConfig.retry}): ${customConfig.url}`);
await new Promise(resolve => setTimeout(resolve, delay));
return apiClient(customConfig);
}
}
}
// 处理401错误 (未授权)
if (response && response.status === 401) {
// 清除存储的token
console.log("未授权错误,清除token");
await AsyncStorage.removeItem(STORAGE_KEYS.AUTH_TOKEN);
// 这里可以添加重定向到登录页面的逻辑
// 例如使用事件发射器或全局状态管理
}
// 构建标准化的错误对象
@ -88,7 +147,12 @@ export const apiService = {
// GET请求
async get<T>(url: string, params?: any, config?: any): Promise<T> {
try {
const response = await apiClient.get<T>(url, { params, ...config });
const customConfig: CustomAxiosRequestConfig = {
...config,
retry: 3,
retryDelay: (retryCount) => retryCount * 1000,
};
const response = await apiClient.get<T>(url, { params, ...customConfig });
return response.data;
} catch (error) {
throw error;
@ -98,7 +162,12 @@ export const apiService = {
// POST请求
async post<T>(url: string, data?: any, config?: any): Promise<T> {
try {
const response = await apiClient.post<T>(url, data, config);
const customConfig: CustomAxiosRequestConfig = {
...config,
retry: 3,
retryDelay: (retryCount) => retryCount * 1000,
};
const response = await apiClient.post<T>(url, data, customConfig);
return response.data;
} catch (error) {
throw error;
@ -108,7 +177,12 @@ export const apiService = {
// PUT请求
async put<T>(url: string, data?: any, config?: any): Promise<T> {
try {
const response = await apiClient.put<T>(url, data, config);
const customConfig: CustomAxiosRequestConfig = {
...config,
retry: 3,
retryDelay: (retryCount) => retryCount * 1000,
};
const response = await apiClient.put<T>(url, data, customConfig);
return response.data;
} catch (error) {
throw error;
@ -118,7 +192,12 @@ export const apiService = {
// PATCH请求
async patch<T>(url: string, data?: any, config?: any): Promise<T> {
try {
const response = await apiClient.patch<T>(url, data, config);
const customConfig: CustomAxiosRequestConfig = {
...config,
retry: 3,
retryDelay: (retryCount) => retryCount * 1000,
};
const response = await apiClient.patch<T>(url, data, customConfig);
return response.data;
} catch (error) {
throw error;
@ -128,7 +207,12 @@ export const apiService = {
// DELETE请求
async delete<T>(url: string, config?: any): Promise<T> {
try {
const response = await apiClient.delete<T>(url, config);
const customConfig: CustomAxiosRequestConfig = {
...config,
retry: 3,
retryDelay: (retryCount) => retryCount * 1000,
};
const response = await apiClient.delete<T>(url, customConfig);
return response.data;
} catch (error) {
throw error;

10
app/services/api/cart.ts

@ -50,23 +50,23 @@ export interface GetCartList {
skus:CartSku[]
}
export const cartApi = (data: AddToCartParams) => {
return apiService.post('/api/cart', data);
return apiService.post('/api/cart/', data);
}
export const getCartList = () => {
return apiService.get<GetCartListResponse>('/api/cart');
return apiService.get<GetCartListResponse>('https://api.brainnel.com/backend/api/cart/');
}
export const updateCartItem = (cart_id:number,data?:{cart_item_id?:number | null,selected:number | null,quantity:number | null}) => {
return apiService.put(`/api/cart/${cart_id}`,data);
return apiService.put(`/api/cart/${cart_id}/`,data);
}
// 批量更新选中状态
export const updateBatchCartSelected = (data?:{cart_id?:number | null,selected:number | null,offer_ids:Array<number> | null}) => {
return apiService.patch(`/api/cart/selected`,data);
return apiService.patch(`/api/cart/selected/`,data);
}
export const deleteCartItem = (cart_id:number,cart_item_id:number) => {
return apiService.delete(`/api/cart/${cart_id}?cart_item_id=${cart_item_id}`);
return apiService.delete(`/api/cart/${cart_id}/?cart_item_id=${cart_item_id}`);
}

20
app/services/api/orders.ts

@ -339,43 +339,43 @@ interface Address {
}
}
export const ordersApi = {
getOrders: (data:OrderPreviewData) => apiService.post<OrderData>("/api/orders/preview",data),
getOrders: (data:OrderPreviewData) => apiService.post<OrderData>("/api/orders/preview/",data),
// 获得价格
freightForwarderAddress: (transport_mode:number | null) =>
apiService.get<AddressDataItem>(`/api/freight_forwarder_address/?transport_mode=${transport_mode}`),
// 获得价格
// 获得价格
calcShippingFee: (data:ShippingFeeData) =>
apiService.post<CartShippingFeeData>(`/api/orders/calc_shipping_fee`,data),
apiService.post<CartShippingFeeData>(`/api/orders/calc_shipping_fee/`,data),
// 获得国内价格
calcDomesticShippingFee: (data:ShippingFeeData) =>
apiService.post<CartShippingFeeData>(`/api/orders/calc_domestic_shipping`,data),
apiService.post<CartShippingFeeData>(`/api/orders/calc_domestic_shipping/`,data),
// 创建订单
createOrder: (data: CreateOrderRequest) =>
apiService.post<Order>('/api/orders/cart', data),
apiService.post<Order>('/api/orders/cart/', data),
// 获取所有订单
getAllOrders: (data:PaginatedOrderRequest) =>
apiService.get<PaginatedOrderResponse>(`/api/orders`,data),
apiService.get<PaginatedOrderResponse>(`/api/orders/`,data),
// 获取订单指定信息
getOrderDetails: (order_id:string) =>
apiService.get<OrderDetailsType>(`/api/orders/${order_id}`),
apiService.get<OrderDetailsType>(`/api/orders/${order_id}/`),
// 删除订单
deleteOrder: (order_id:string) =>
apiService.delete<void>(`/api/orders/${order_id}`),
apiService.delete<void>(`/api/orders/${order_id}/`),
// 修改订单
changeOrder: (order_id:string,status:number) =>
apiService.patch<void>(`/api/orders/${order_id}/status?status=${status}`),
apiService.patch<void>(`/api/orders/${order_id}/status/?status=${status}`),
// 修改物流信息
updateOrderShippingInfo: (order_id:string,data:UpdateOrderShippingInfo) =>
apiService.patch<void>(`/api/orders/${order_id}/shipping`,data),
apiService.patch<void>(`/api/orders/${order_id}/shipping/`,data),
};

4
app/services/api/payApi.ts

@ -33,11 +33,11 @@ export interface PayInfoBody {
export const payApi = {
// 获取当前国家支付方式
getCountryPaymentMethods: () => {
return apiService.get<PaymentMethodsResponse>('/api/payment/country_payment_methods');
return apiService.get<PaymentMethodsResponse>('/api/payment/country_payment_methods/');
},
// 获取支付信息
getPayInfo: (data: PayInfoBody) => {
return apiService.post<PaymentInfoResponse>(`/api/payment/initiate`, data);
return apiService.post<PaymentInfoResponse>(`/api/payment/initiate/`, data);
},
};

4
app/services/api/productApi.ts

@ -175,11 +175,11 @@ export type Products = Product[]
},
// 获取商品详情
getProductDetail: (offer_id: string) => {
return apiService.get<ProductDetailParams>('/api/products/'+offer_id);
return apiService.get<ProductDetailParams>(`/api/products/${offer_id}/`);
},
// 获取相似商品
getSimilarProducts: (offer_id: string) => {
return apiService.get<Similars>('/api/products/'+offer_id+'/similar?limit=5');
return apiService.get<Similars>(`/api/products/${offer_id}/similar/?limit=5`);
}
}

12
app/services/api/setting.ts

@ -42,10 +42,10 @@ export interface FirstLogin {
export const settingApi = {
getCountryList: () => apiService.get<Country[]>('/api/user_settings/countries'),
getCurrencyList: () => apiService.get<string[]>('/api/user_settings/currencies'),
getLanguageList: () => apiService.get<string[]>('/api/user_settings/languages'),
getMySetting: () => apiService.get<MySetting>('/api/user_settings/me'),
postFirstLogin: (country: number) => apiService.post<FirstLogin>(`/api/user_settings/first_login?country=${country}`),
putSetting: (setting: object) => apiService.put<MySetting>('/api/user_settings/me', setting),
getCountryList: () => apiService.get<Country[]>('/api/user_settings/countries/'),
getCurrencyList: () => apiService.get<string[]>('/api/user_settings/currencies/'),
getLanguageList: () => apiService.get<string[]>('/api/user_settings/languages/'),
getMySetting: () => apiService.get<MySetting>('/api/user_settings/me/'),
postFirstLogin: (country: number) => apiService.post<FirstLogin>(`/api/user_settings/first_login/?country=${country}`),
putSetting: (setting: object) => apiService.put<MySetting>('/api/user_settings/me/', setting),
}

10
app/services/api/userApi.ts

@ -84,27 +84,27 @@ export const userApi = {
// 获取用户信息
getProfile: () => {
return apiService.get<User>('/api/users/me');
return apiService.get<User>('/api/users/me/');
},
// 更新用户信息
updateProfile: (data: Partial<User>) => {
return apiService.put<User>('/user/profile', data);
return apiService.put<User>('/user/profile/', data);
},
// 更新用户头像
updateAvatar: (file: FormData) => {
return apiService.upload<{avatar: string}>('/user/avatar', file);
return apiService.upload<{avatar: string}>('/user/avatar/', file);
},
// 退出登录
logout: () => {
return apiService.post('/auth/logout');
return apiService.post('/auth/logout/');
},
// 检查邮箱是否可用
checkEmailAvailability: (email: string) => {
return apiService.get<{available: boolean}>('/auth/check-email', { email });
return apiService.get<{available: boolean}>('/auth/check-email/', { email });
}
};

9
app/types/global.d.ts vendored

@ -0,0 +1,9 @@
import { i18n } from 'i18next';
declare global {
interface Window {
i18n: i18n;
}
const t: (key: string, options?: any) => string;
}

27
app/utils/languageUtils.ts

@ -0,0 +1,27 @@
import { getCurrentLanguage } from '../i18n';
export const getSystemLanguage = <T extends Record<string, any>>(data: T): string => {
// 获取当前i18n语言
const currentLang = getCurrentLanguage();
// 特殊处理中文
if (currentLang === 'zh' && 'subject' in data) {
return data.subject as string;
}
// 获取所有subject_trans开头的字段
const translationFields = Object.keys(data).filter(key =>
key.startsWith('subject_trans')
);
// 查找匹配的字段
const matchedField = translationFields.find(field => {
// 从字段名中提取语言代码
const langCode = field.replace('subject_trans_', '');
// 如果没有后缀,则为法语
return langCode === '' ? currentLang === 'fr' : langCode === currentLang;
});
// 返回匹配的翻译值,如果没有匹配则返回法语
return (data[matchedField || 'subject_trans'] as string) || '';
};

BIN
assets/img/banner en (3)_compressed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
assets/img/banner en (4)_compressed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
assets/img/banner en (5)_compressed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

12080
package-lock.json generated

File diff suppressed because it is too large Load Diff

6
package.json

@ -7,10 +7,12 @@
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
"web": "expo start --web",
"build:web": "expo export:web"
},
"dependencies": {
"@expo/metro-runtime": "~4.0.1",
"@expo/webpack-config": "^19.0.1",
"@react-native-async-storage/async-storage": "^2.1.2",
"@react-native-community/checkbox": "^0.5.17",
"@react-native-community/datetimepicker": "8.2.0",
@ -22,7 +24,6 @@
"events": "^3.3.0",
"expo": "~52.0.41",
"expo-auth-session": "~6.0.3",
"expo-dev-client": "~5.0.20",
"expo-image": "~2.0.7",
"expo-linear-gradient": "~14.0.2",
"expo-localization": "^16.0.1",
@ -64,6 +65,7 @@
"@hancleee/babel-plugin-react-native-pxtodp": "^1.0.8",
"@types/react": "~18.3.12",
"@types/react-native-vector-icons": "^6.4.18",
"expo-module-scripts": "^4.1.7",
"react-native-svg-transformer": "^1.5.0",
"typescript": "^5.3.3"
},

4812
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save