Browse Source

切换货币首页加载

main
Mac 2 weeks ago
parent
commit
0ad46eef3a
  1. 34
      app.json
  2. 1
      app/locales/en/translation.json
  3. 4
      app/locales/fr/translation.json
  4. 1
      app/screens/BalanceScreen/PhoneNumberInputModal.tsx
  5. 6
      app/screens/CartScreen.tsx
  6. 549
      app/screens/HomeScreen.tsx
  7. 210
      app/screens/ProductCard.tsx
  8. 82
      app/screens/loginList/index.tsx
  9. 1
      app/screens/productStatus/OrderDatails.tsx
  10. 7
      app/screens/setting/CountrySetting.tsx

34
app.json

@ -7,7 +7,7 @@
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"newArchEnabled": true,
"scheme": "myapp",
"scheme": "com.brainnel.app",
"owner": "brainnel",
"deepLinking": true,
"splash": {
@ -22,10 +22,19 @@
"CFBundleURLTypes": [
{
"CFBundleURLSchemes": [
"myapp"
"com.brainnel.app",
"myapp",
"fb164237502913710"
]
}
]
},
"config": {
"facebook": {
"scheme": "fb164237502913710",
"appId": "164237502913710",
"displayName": "newBrainnelApp"
}
}
},
"android": {
@ -39,10 +48,20 @@
},
"googleServicesFile": "./google-services.json",
"package": "com.brainnel.app",
"config": {
"facebook": {
"scheme": "fb164237502913710",
"appId": "164237502913710",
"displayName": "newBrainnelApp"
}
},
"intentFilters": [
{
"action": "VIEW",
"data": [
{
"scheme": "com.brainnel.app"
},
{
"scheme": "myapp"
}
@ -68,7 +87,16 @@
}
}
],
"expo-localization"
"expo-localization",
[
"react-native-fbsdk-next",
{
"appID": "995035562798467",
"displayName": "newBrainnelApp",
"scheme": "fb995035562798467",
"clientToken": "2005c337398b1c286e2a189e4e4417ac"
}
]
],
"extra": {
"eas": {

1
app/locales/en/translation.json

@ -280,6 +280,7 @@
"order.select_payment": "Select Payment Method",
"order.select_currency": "Select Currency",
"order.confirm_payment": "Confirm Payment",
"order.add_cart_success": "Add to cart successfully",
"member.introduction": "Member Benefits",
"member.client_service": "Client Service",
"member.vip.spend_more_text": "Spend more to upgrade your VIP level",

4
app/locales/fr/translation.json

@ -166,7 +166,7 @@
"order.error.payment_update": "Échec de la mise à jour du mode de paiement",
"order.details": "Détails de la commande",
"order.information": "Informations de commande",
"order.error.add_cart": "Échec de l'ajout au panier",
"order.id": "ID de commande",
"order.create_time": "Date de commande",
"order.shipping_type": "Méthode d'expédition",
@ -211,6 +211,8 @@
"order.select_payment": "Moyens de paiement",
"order.select_currency": "Sélectionner la devise",
"order.confirm_payment": "Confirmer le paiement",
"order.add_cart_success": "Ajout au panier réussi",
"order.error.add_cart": "Échec de l'ajout au panier",
"member.introduction": "Avantages pour les membres",
"member.client_service": "Service Client",
"member.vip.spend_more_text": "Dépensez plus pour améliorer votre niveau VIP",

1
app/screens/BalanceScreen/PhoneNumberInputModal.tsx

@ -199,7 +199,6 @@ const PhoneNumberInputModal = ({
<Text style={styles.countryCodeText}>
{displayCountryCode || '+243'}
</Text>
<Text style={styles.countryCodeArrow}></Text>
</TouchableOpacity>
<TextInput
style={styles.phoneInput}

6
app/screens/CartScreen.tsx

@ -663,6 +663,8 @@ export const CartScreen = () => {
<View style={styles.safeAreaContent}>
<View style={styles.container}>
<ScrollView
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
style={styles.scrollContainer}
contentContainerStyle={styles.scrollContentContainer}
>
@ -817,7 +819,7 @@ export const CartScreen = () => {
style={styles.VipImg}
/>
<Text style={styles.discountPercentageTextStyle}>
{((1 - vip_discount) * 100).toFixed(0)}%
-{((1 - vip_discount) * 100).toFixed(0)}%
</Text>
</View>
</View>
@ -1327,8 +1329,8 @@ const styles = StyleSheet.create({
orderQuantityContainer: {
flexDirection: "row",
alignItems: "center",
marginLeft: 15,
alignSelf: "flex-end",
marginRight: 10,
},
borderBoxDivider1: {
width: widthUtils(9, 9).width,

549
app/screens/HomeScreen.tsx

@ -43,6 +43,7 @@ import * as ImagePicker from "expo-image-picker";
import * as FileSystem from "expo-file-system";
import { useGlobalStore } from "../store/useGlobalStore";
import { getCurrentLanguage } from '../i18n';
import { eventBus } from '../utils/eventBus';
// 为图标定义类型
type IconProps = {
name: string;
@ -260,7 +261,6 @@ type StylesType = {
productCardList: ViewStyle;
productCardGroup: ViewStyle;
beautyProductCard1: ViewStyle;
productCardGroup1: ViewStyle;
beautyCardContainer1: ViewStyle;
vipButtonContainer: ViewStyle;
vipButton: ViewStyle;
@ -296,20 +296,14 @@ type StylesType = {
imagePickerCancelButton: ViewStyle;
imagePickerCancelText: TextStyle;
};
export const HomeScreen = () => {
const [activeIndex, setActiveIndex] = useState(0);
// 轮播图组件 - 独立提取,避免重复渲染
const CarouselBanner = React.memo(({ onCameraPress }: { onCameraPress: () => void }) => {
const screenWidth = Dimensions.get("window").width;
const navigation = useNavigation<NativeStackNavigationProp<any>>();
const { t } = useTranslation();
const [showCategoryModal, setShowCategoryModal] = useState(false);
const [showImagePickerModal, setShowImagePickerModal] = useState(false);
const [selectedCategory, setSelectedCategory] = useState("Bijoux");
const [selectedHorizontalCategory, setSelectedHorizontalCategory] = useState("Tous");
const userStore = useUserStore();
const { country, currency } = useGlobalStore();
const flatListRef = useRef<FlatList>(null);
const horizontalScrollRef = useRef<ScrollView>(null);
const data = [
const [currentIndex, setCurrentIndex] = useState(0);
const data = useMemo(() => [
{
imgUrl: require("../../assets/img/banner en (5)_compressed.png"),
add: "TikTokScreen",
@ -322,7 +316,106 @@ export const HomeScreen = () => {
imgUrl: require("../../assets/img/banner en (4)_compressed.png"),
add: "CompanyScreen",
},
];
], []);
const navigateToSearch = useCallback(() => {
InteractionManager.runAfterInteractions(() => {
navigation.navigate("Search");
});
}, [navigation]);
const handleBannerPress = useCallback((screenName: string) => {
navigation.navigate(screenName);
}, [navigation]);
const onSnapToItem = useCallback((index: number) => {
setCurrentIndex(index);
}, []);
return (
<View style={styles.swiperContainer}>
<Carousel
loop
width={screenWidth}
data={data}
height={widthUtils(286, 286).height}
modeConfig={{
parallaxScrollingScale: 0.9,
parallaxScrollingOffset: 50,
}}
onSnapToItem={onSnapToItem}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => handleBannerPress(item.add)}
key={item.imgUrl}
activeOpacity={1}
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f2f2f2",
borderRadius: 0,
overflow: "hidden",
}}
>
<Image
source={item.imgUrl}
style={{ width: "100%", height: "100%" }}
resizeMode="cover"
defaultSource={require("../../assets/img/banner en (3).png")}
/>
</TouchableOpacity>
)}
/>
{/* 轮播图指示灯 */}
<View style={styles.indicatorContainer}>
{data.map((_, index) => (
<View
key={index}
style={[
styles.indicator,
index === currentIndex ? styles.activeIndicator : styles.inactiveIndicator,
]}
/>
))}
</View>
<View style={styles.searchOverlay}>
<TouchableOpacity
style={styles.searchBar}
activeOpacity={0.7}
onPress={navigateToSearch}
>
<IconComponent name="search-outline" size={20} color="#999" />
<Text style={styles.searchPlaceholder}>{t("homePage.searchPlaceholder")}</Text>
<TouchableOpacity
style={styles.cameraButton}
onPress={onCameraPress}
>
<IconComponent name="camera-outline" size={24} color="#333" />
</TouchableOpacity>
</TouchableOpacity>
</View>
</View>
);
}, (prevProps, nextProps) => {
// 自定义比较函数,只有当onCameraPress真正改变时才重新渲染
return prevProps.onCameraPress === nextProps.onCameraPress;
});
export const HomeScreen = () => {
const [activeIndex, setActiveIndex] = useState(0);
const screenWidth = Dimensions.get("window").width;
const navigation = useNavigation<NativeStackNavigationProp<any>>();
const { t } = useTranslation();
const [showCategoryModal, setShowCategoryModal] = useState(false);
const [showImagePickerModal, setShowImagePickerModal] = useState(false);
const [selectedCategory, setSelectedCategory] = useState("Bijoux");
const [selectedHorizontalCategory, setSelectedHorizontalCategory] = useState("Tous");
const userStore = useUserStore();
const { country, currency } = useGlobalStore();
const flatListRef = useRef<FlatList>(null);
const horizontalScrollRef = useRef<ScrollView>(null);
const [galleryUsed, setGalleryUsed] = useState(false);
const [hotTerms, setHotTerms] = useState<string[]>([]);
const [isLoadingHotTerms, setIsLoadingHotTerms] = useState(false);
@ -381,6 +474,94 @@ export const HomeScreen = () => {
initApp();
}, []);
// 监听设置变更事件,重新加载产品数据
useEffect(() => {
const handleRefreshSetting = async () => {
console.log("接收到refreshSetting事件,重新加载产品数据");
try {
// 重新获取热门关键词
const response = await productApi.getHotTerms();
const terms = response.terms || [];
setHotTerms(terms);
// 使用新的随机关键词重新加载数据
if (terms.length > 0) {
const randomIndex = Math.floor(Math.random() * terms.length);
const randomKeyword = terms[randomIndex];
// 更新参数
setParams(prev => ({
...prev,
keyword: randomKeyword,
language: getCurrentLanguage(), // 更新语言参数
}));
// 重置状态并重新加载
setLoading(true);
setProducts([]);
setCurrentPage(1);
setHasMore(true);
// 简化的产品加载逻辑
const initialParams = {
keyword: randomKeyword,
sort_order: "desc",
sort_by: "default",
language: getCurrentLanguage(),
page: 1,
page_size: 10,
};
const firstPageRes = await productApi.getSearchProducts(initialParams);
setProducts(firstPageRes.products);
setTotalItems(firstPageRes.total || 0);
setCurrentPage(1);
setHasMore(firstPageRes.products.length < (firstPageRes.total || 0));
setLoading(false);
} else {
// 如果没有热门关键词,使用默认关键词
setParams(prev => ({
...prev,
language: getCurrentLanguage(), // 更新语言参数
}));
setLoading(true);
setProducts([]);
setCurrentPage(1);
setHasMore(true);
const initialParams = {
keyword: "pen",
sort_order: "desc",
sort_by: "default",
language: getCurrentLanguage(),
page: 1,
page_size: 10,
};
const firstPageRes = await productApi.getSearchProducts(initialParams);
setProducts(firstPageRes.products);
setTotalItems(firstPageRes.total || 0);
setCurrentPage(1);
setHasMore(firstPageRes.products.length < (firstPageRes.total || 0));
setLoading(false);
}
} catch (error) {
console.error("重新加载产品数据失败:", error);
setLoading(false);
}
};
// 添加事件监听器
eventBus.on("refreshSetting", handleRefreshSetting);
// 清理函数,移除事件监听器
return () => {
eventBus.off("refreshSetting", handleRefreshSetting);
};
}, []); // 空依赖数组,因为我们只需要在组件挂载时添加监听器
// 获取随机关键词
const getRandomKeyword = useCallback(() => {
if (hotTerms.length === 0) return "pen";
@ -746,9 +927,8 @@ export const HomeScreen = () => {
cleanupImagePickerCache();
Alert.alert("已重置", "现在您可以使用相机功能了");
}, []);
// 优化轮播图切换回调
const handleCarouselSnap = useCallback((index: number) => {
setActiveIndex(index);
const handleCameraPress = useCallback(() => {
setShowImagePickerModal(true);
}, []);
const renderItem = ({ item, index }: { item: Product; index: number }) => {
if (index >= products.length && index < products.length + loadingPlaceholders) {
@ -756,70 +936,8 @@ export const HomeScreen = () => {
}
return renderProductItem({ item });
};
const renderHeader = useCallback(() => (
const renderHeader = () => (
<>
<View style={styles.swiperContainer}>
<Carousel
key="carousel-header"
width={screenWidth}
data={data}
height={widthUtils(286, 286).height}
modeConfig={{
parallaxScrollingScale: 0,
parallaxScrollingOffset: 0,
}}
onSnapToItem={handleCarouselSnap}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate(item.add)}
activeOpacity={1}
key={item.imgUrl}
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f2f2f2",
borderRadius: 0,
overflow: "hidden",
}}
>
<Image
source={item.imgUrl}
style={{ width: "100%", height: "100%" }}
resizeMode="cover"
defaultSource={require("../../assets/img/banner en (3).png")}
/>
</TouchableOpacity>
)}
/>
<View style={styles.indicatorContainer}>
{data.map((_, index) => (
<View
key={index}
style={[
styles.indicator,
index === activeIndex ? styles.activeIndicator : styles.inactiveIndicator,
]}
/>
))}
</View>
<View style={styles.searchOverlay}>
<TouchableOpacity
style={styles.searchBar}
activeOpacity={0.7}
onPress={navigateToSearch}
>
<IconComponent name="search-outline" size={20} color="#999" />
<Text style={styles.searchPlaceholder}>{t("homePage.searchPlaceholder")}</Text>
<TouchableOpacity
style={styles.cameraButton}
onPress={() => setShowImagePickerModal(true)}
>
<IconComponent name="camera-outline" size={24} color="#333" />
</TouchableOpacity>
</TouchableOpacity>
</View>
</View>
<View style={styles.bannerContainer}>
<View style={styles.leftContainer}>
<TouchableOpacity
@ -924,20 +1042,14 @@ export const HomeScreen = () => {
</View>
) : null}
</>
), [activeIndex, selectedHorizontalCategory]);
);
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<View style={styles.safeAreaContent}>
<View style={styles.container}>
{loading ? (
<View>
{renderHeader()}
{renderSkeletonGrid()}
</View>
) : (
<ScrollView
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={refreshing}
@ -947,236 +1059,51 @@ export const HomeScreen = () => {
progressBackgroundColor="transparent"
/>
}
onScroll={({ nativeEvent }) => {
const { layoutMeasurement, contentOffset, contentSize } = nativeEvent;
const paddingToBottom = 20;
if (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom) {
handleLoadMore();
}
}}
scrollEventThrottle={400}
>
{/* 轮播图区域 */}
<View style={styles.swiperContainer}>
<Carousel
key="carousel-header"
width={screenWidth}
data={data}
height={widthUtils(286, 286).height}
modeConfig={{
parallaxScrollingScale: 0,
parallaxScrollingOffset: 0,
}}
onSnapToItem={handleCarouselSnap}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate(item.add)}
activeOpacity={1}
key={item.imgUrl}
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f2f2f2",
borderRadius: 0,
overflow: "hidden",
}}
>
<Image
source={item.imgUrl}
style={{ width: "100%", height: "100%" }}
resizeMode="cover"
defaultSource={require("../../assets/img/banner en (3).png")}
/>
</TouchableOpacity>
)}
/>
<View style={styles.indicatorContainer}>
{data.map((_, index) => (
<View
key={index}
style={[
styles.indicator,
index === activeIndex ? styles.activeIndicator : styles.inactiveIndicator,
]}
/>
))}
</View>
<View style={styles.searchOverlay}>
<TouchableOpacity
style={styles.searchBar}
activeOpacity={0.7}
onPress={navigateToSearch}
>
<IconComponent name="search-outline" size={20} color="#999" />
<Text style={styles.searchPlaceholder}>{t("homePage.searchPlaceholder")}</Text>
<TouchableOpacity
style={styles.cameraButton}
onPress={() => setShowImagePickerModal(true)}
>
<IconComponent name="camera-outline" size={24} color="#333" />
</TouchableOpacity>
</TouchableOpacity>
</View>
</View>
{/* Banner区域 */}
<View style={styles.bannerContainer}>
<View style={styles.leftContainer}>
<TouchableOpacity
style={styles.leftTopItem}
onPress={navigateToShippingDetails}
>
<Image
source={require("../../assets/img/a_计算运费.png")}
style={styles.bannerIcon}
/>
</TouchableOpacity>
<TouchableOpacity
style={styles.leftBottomItem}
onPress={() => navigation.navigate("TikTokScreen")}
>
<Image
source={require("../../assets/img/a_tiktok.png")}
style={styles.bannerIcon}
/>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.rightContainer}
onPress={navigateToInquiry}
>
<Image
source={require("../../assets/img/a_VIP.png")}
style={styles.bigbannerIcon}
/>
</TouchableOpacity>
</View>
{/* 分类区域 */}
<View style={styles.category}>
<View style={styles.categoryScrollContainer}>
<ScrollView
bounces={false}
overScrollMode="never"
ref={horizontalScrollRef}
horizontal
showsHorizontalScrollIndicator={false}
style={styles.categoryScroll}
>
{categories.map((category, index) => (
<TouchableOpacity
key={index}
style={[
styles.categoryItem,
selectedHorizontalCategory === category && styles.categoryItemActive,
]}
onPress={() => setSelectedHorizontalCategory(category)}
>
<Text
style={[
styles.categoryText,
selectedHorizontalCategory === category && styles.categoryTextActive,
]}
>
{t(`homePage.${category.toLowerCase()}`)}
</Text>
</TouchableOpacity>
))}
</ScrollView>
<LinearGradient
colors={["rgba(255,255,255,0)", "rgba(255,255,255,1)"]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={styles.fadeGradient}
/>
</View>
<View style={styles.categoryArrowContainer}>
<TouchableOpacity onPress={() => setShowCategoryModal(true)}>
<DownArrowIcon size={fontSize(18)} color="#666" rotation={360} />
</TouchableOpacity>
</View>
</View>
{/* 子分类区域 */}
{selectedHorizontalCategory &&
categoryContent[selectedHorizontalCategory] &&
categoryContent[selectedHorizontalCategory].length > 0 ? (
<View style={styles.subcategoryContainer}>
<ScrollView
bounces={false}
overScrollMode="never"
horizontal
showsHorizontalScrollIndicator={false}
style={styles.subcategoryScroll}
contentContainerStyle={styles.subcategoryContent}
>
{categoryContent[selectedHorizontalCategory].map((item) => (
<TouchableOpacity
key={item.id}
style={styles.subcategoryItem}
onPress={() => {
// Handle subcategory selection
}}
>
<View style={styles.subcategoryImagePlaceholder}>
<IconComponent name={item.icon} size={24} color="#666" />
</View>
<Text style={styles.subcategoryText}>{item.title}</Text>
</TouchableOpacity>
))}
<CarouselBanner onCameraPress={handleCameraPress} />
{renderHeader()}
{renderSkeletonGrid()}
</ScrollView>
</View>
) : null}
{/* 产品网格 */}
<View style={styles.productContainer}>
<View style={styles.productCardList}>
{(() => {
const allItems = [...products, ...Array(loadingPlaceholders).fill(null)];
const rows = [];
for (let i = 0; i < allItems.length; i += 2) {
const leftItem = allItems[i];
const rightItem = allItems[i + 1];
rows.push(
<View key={`row-${i}`} style={styles.productCardGroup1}>
{/* 左侧商品 */}
{leftItem ? (
i >= products.length ? (
<ProductSkeleton />
) : (
renderProductItem({ item: leftItem })
)
) : null}
{/* 右侧商品 */}
{rightItem ? (
i + 1 >= products.length ? (
<ProductSkeleton />
) : (
renderProductItem({ item: rightItem })
)
) : (
<View style={{ width: "48%" }} />
)}
</View>
);
<FlatList
ref={flatListRef}
data={[...products, ...Array(loadingPlaceholders).fill(null)]}
numColumns={2}
showsVerticalScrollIndicator={false}
columnWrapperStyle={styles.productCardGroup}
renderItem={renderItem}
keyExtractor={(item, index) =>
(item?.offer_id ? `${item.offer_id}-${currentPage}-${index}` : `placeholder-${currentPage}-${index}`)
}
return rows;
})()}
</View>
{/* 底部提示 */}
{!hasMore && !loadingPlaceholders && (
contentContainerStyle={{
paddingBottom: 15,
backgroundColor: "transparent",
}}
ListHeaderComponent={() => (
<>
<CarouselBanner onCameraPress={handleCameraPress} />
{renderHeader()}
</>
)}
onEndReached={handleLoadMore}
onEndReachedThreshold={3}
ListFooterComponent={() => (
!hasMore && !loadingPlaceholders ? (
<View style={{ padding: 10, alignItems: 'center' }}>
<Text></Text>
</View>
) : null
)}
</View>
</ScrollView>
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
colors={["#ff5100"]}
tintColor="#ff5100"
progressBackgroundColor="transparent"
/>
}
/>
)}
<Modal
visible={showCategoryModal}
@ -1566,16 +1493,10 @@ const styles = StyleSheet.create<StylesType>({
paddingTop: 0,
},
productCardGroup: {
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 15,
paddingHorizontal: 15,
},
productCardGroup1:{
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 15,
},
beautyProductCard1: {
width: "48%",
},

210
app/screens/ProductCard.tsx

@ -55,6 +55,18 @@ const ProductCard: React.FC<ProductCardProps> = ({
const [images, setImages] = useState<string[]>([]);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const [imageViewerVisible, setImageViewerVisible] = useState(false);
// 添加数量输入弹窗相关状态
const [quantityInputVisible, setQuantityInputVisible] = useState(false);
const [editingItem, setEditingItem] = useState<{
type: 'hasImg' | 'noImg';
index: number;
currentQuantity: number;
maxQuantity: number;
attributeValue?: string;
} | null>(null);
const [quantityInput, setQuantityInput] = useState("");
const {
user: { user_id, vip_level, currency },
} = useUserStore();
@ -100,6 +112,56 @@ const ProductCard: React.FC<ProductCardProps> = ({
setAlertModalVisible(true);
};
// 处理数量输入弹窗确认
const handleQuantityInputConfirm = () => {
if (!editingItem) return;
const newQuantity = parseInt(quantityInput);
if (isNaN(newQuantity) || newQuantity < 0) {
showCustomAlert("输入错误", "请输入有效的数量");
return;
}
if (newQuantity > editingItem.maxQuantity) {
showCustomAlert("库存不足", `最大可选数量为 ${editingItem.maxQuantity}`);
return;
}
// 根据类型调用相应的处理函数
if (editingItem.type === 'hasImg') {
handleSizeSelect(
editingItem.attributeValue || "",
newQuantity.toString(),
editingItem.index,
editingItem.maxQuantity
);
} else {
handleNoImgSizeSelect(
editingItem.attributeValue || "",
newQuantity.toString(),
editingItem.index,
editingItem.maxQuantity
);
}
setQuantityInputVisible(false);
setEditingItem(null);
setQuantityInput("");
};
// 处理点击数量显示
const handleQuantityPress = (
type: 'hasImg' | 'noImg',
index: number,
currentQuantity: number,
maxQuantity: number,
attributeValue?: string
) => {
setEditingItem({ type, index, currentQuantity, maxQuantity, attributeValue });
setQuantityInput(currentQuantity.toString());
setQuantityInputVisible(true);
};
// 加入购物车
const addCartHandel = () => {
if (!user_id) {
@ -421,19 +483,22 @@ const ProductCard: React.FC<ProductCardProps> = ({
>
<Text>-</Text>
</TouchableOpacity>
<TextInput
<TouchableOpacity
style={styles.sizePriceBoxStepForwardInput}
value={list?.size?.toString() ?? "0"}
keyboardType="numeric"
onChangeText={(text) =>
handleSizeSelect(
list?.attributes?.[0]?.value,
text,
onPress={() =>
handleQuantityPress(
'hasImg',
index1,
list?.amount_on_sale ?? 0
list?.size ?? 0,
list?.amount_on_sale ?? 0,
list?.attributes?.[0]?.value
)
}
/>
>
<Text style={styles.quantityDisplayText}>
{list?.size?.toString() ?? "0"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
@ -541,19 +606,22 @@ const ProductCard: React.FC<ProductCardProps> = ({
>
<Text>-</Text>
</TouchableOpacity>
<TextInput
<TouchableOpacity
style={styles.sizePriceBoxStepForwardInput}
value={list?.size?.toString() ?? "0"}
keyboardType="numeric"
onChangeText={(text) =>
handleSizeSelect(
list?.attributes?.[0]?.value,
text,
onPress={() =>
handleQuantityPress(
'hasImg',
index1,
list?.amount_on_sale ?? 0
list?.size ?? 0,
list?.amount_on_sale ?? 0,
list?.attributes?.[0]?.value
)
}
/>
>
<Text style={styles.quantityDisplayText}>
{list?.size?.toString() ?? "0"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
@ -625,19 +693,22 @@ const ProductCard: React.FC<ProductCardProps> = ({
>
<Text>-</Text>
</TouchableOpacity>
<TextInput
<TouchableOpacity
style={styles.sizePriceBoxStepForwardInput}
value={list?.size?.toString() ?? "0"}
keyboardType="numeric"
onChangeText={(text) =>
handleNoImgSizeSelect(
list?.attributes?.[0]?.value,
text,
onPress={() =>
handleQuantityPress(
'noImg',
index1,
list?.amount_on_sale ?? 0
list?.size ?? 0,
list?.amount_on_sale ?? 0,
list?.attributes?.[0]?.value
)
}
/>
>
<Text style={styles.quantityDisplayText}>
{list?.size?.toString() ?? "0"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
@ -726,19 +797,22 @@ const ProductCard: React.FC<ProductCardProps> = ({
>
<Text>-</Text>
</TouchableOpacity>
<TextInput
<TouchableOpacity
style={styles.sizePriceBoxStepForwardInput}
keyboardType="numeric"
value={list?.size?.toString() ?? "0"}
onChangeText={(text) =>
handleNoImgSizeSelect(
list.attributes?.[0]?.value,
text,
onPress={() =>
handleQuantityPress(
'noImg',
index1,
list?.amount_on_sale ?? 0
list?.size ?? 0,
list?.amount_on_sale ?? 0,
list?.attributes?.[0]?.value
)
}
/>
>
<Text style={styles.quantityDisplayText}>
{list?.size?.toString() ?? "0"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.sizePriceBoxStepForwardButton}
onPress={() =>
@ -846,6 +920,48 @@ const ProductCard: React.FC<ProductCardProps> = ({
</View>
</View>
</Modal>
{/* 数量输入弹窗 */}
<Modal
visible={quantityInputVisible}
transparent
animationType="fade"
onRequestClose={() => setQuantityInputVisible(false)}
>
<View style={styles.overlay}>
<View style={styles.popup}>
<Text style={styles.promptText}></Text>
<TextInput
style={styles.quantityInput}
value={quantityInput}
onChangeText={setQuantityInput}
keyboardType="number-pad"
autoFocus
/>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.cancelButton1, { width: "45%" }]}
onPress={() => {
setQuantityInputVisible(false);
setEditingItem(null);
setQuantityInput("");
}}
>
<Text style={styles.cancelText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.confirmButton,
{ width: "45%", backgroundColor: "#ff5100" },
]}
onPress={handleQuantityInputConfirm}
>
<Text style={styles.confirmText}></Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
</View>
);
};
@ -1104,10 +1220,11 @@ const styles = StyleSheet.create({
alignItems: "center",
},
sizePriceBoxStepForwardInput: {
width: widthUtils(40, 30).width,
width: widthUtils(60, 40).width,
height: widthUtils(40, 30).height,
backgroundColor: "#fff",
textAlign: "center",
justifyContent: "center",
alignItems: "center",
fontSize: fontSize(14),
borderWidth: 1,
borderColor: "#f3f4f8",
@ -1325,6 +1442,23 @@ const styles = StyleSheet.create({
marginBottom: 20,
paddingHorizontal: 20,
},
quantityDisplayText: {
fontSize: fontSize(16),
fontWeight: "700",
color: "#000",
fontFamily: "Segoe UI",
},
quantityInput: {
width: "100%",
height: 40,
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 5,
paddingHorizontal: 10,
fontSize: fontSize(16),
textAlign: "center",
fontFamily: "Segoe UI",
},
});
export default ProductCard;

82
app/screens/loginList/index.tsx

@ -10,6 +10,7 @@ import {
Image,
Modal,
SafeAreaView,
Alert
} from "react-native";
import { useTranslation } from "react-i18next";
import { useNavigation } from "@react-navigation/native";
@ -27,6 +28,8 @@ import useUserStore from "../../store/user";
// statusCodes,
// } from "@react-native-google-signin/google-signin";
// import { LoginManager, AccessToken, Settings } from "react-native-fbsdk-next";
const isDevelopment = __DEV__; // 开发模式检测
// 移出条件块,始终尝试配置 Google 登录
@ -106,12 +109,10 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
// 处理谷歌登录
const handleGoogleLogin = async () => {
// try {
// if (!GoogleSignin || typeof GoogleSignin.signIn !== "function") {
// console.log("Google Sign-in模块未正确初始化或配置失败");
// return;
// }
// await GoogleSignin.hasPlayServices();
// const userInfo = await GoogleSignin.signIn();
// console.log("Google 登录成功:", userInfo);
@ -124,19 +125,16 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
// console.log("Google 登录失败:", err);
// navigation.navigate("Login");
// }
// // 这里可以处理登录成功后的逻辑
// // 比如导航到主页面或保存用户信息
// // navigation.navigate("MainTabs", { screen: "Home" });
// } catch (error: any) {
// console.log("Google 登录错误:", error);
// // 开发模式下的错误处理
// if (isDevelopment) {
// console.log("开发模式:忽略Google登录错误,但已尝试真实登录"); // 修改日志,表明已尝试真实登录
// return;
// }
// if (statusCodes && error.code === statusCodes.SIGN_IN_CANCELLED) {
// console.log("用户取消登录");
// } else if (statusCodes && error.code === statusCodes.IN_PROGRESS) {
@ -152,10 +150,80 @@ export const LoginScreen = ({ onClose, isModal }: LoginScreenProps) => {
// }
// }
};
const [userInfo, setUserInfo] = useState<any>(null);
// useEffect(() => {
// // 确保在 App 启动时初始化 SDK。这通常在您的 App.js 的顶层完成。
// // 如果您在 app.json 中配置了 Facebook App ID,这里可以省略 Settings.setAppID 和 Settings.setDisplayName
// Settings.initializeSDK();
// // 在应用程序启动时检查是否已经登录(可选)
// AccessToken.getCurrentAccessToken().then(data => {
// if (data) {
// console.log("已登录 Facebook,Token:", data.accessToken);
// // 可以尝试获取用户信息
// // fetchFacebookProfile(data.accessToken);
// }
// });
// }, []);
// 辅助函数:获取 Facebook 用户资料 (可选,需要 'public_profile' 权限)
// const fetchFacebookProfile = async (token:string) => {
// try {
// const response = await fetch(`https://graph.facebook.com/me?fields=id,name,email&access_token=${token}`);
// const profile = await response.json();
// setUserInfo(profile);
// console.log('Facebook User Info:', profile);
// } catch (error) {
// console.error('获取 Facebook 用户资料错误:', error);
// Alert.alert("获取资料失败", "无法从 Facebook 获取用户详细资料,请检查网络或权限设置。");
// }
// };
// 处理Facebook登录
const handleFacebookLogin = () => {
// 处理Facebook登录
const handleFacebookLogin = async () => {
// try {
// // 可选: 先退出登录,确保每次都是全新登录 (主要用于测试)
// // await LoginManager.logOut();
// const result = await LoginManager.logInWithPermissions([
// "public_profile",
// "email",
// ]);
// if (result.isCancelled) {
// Alert.alert("登录取消", "用户取消了 Facebook 登录。");
// return;
// }
// const data = await AccessToken.getCurrentAccessToken();
// // 确保 accessToken 存在且为字符串
// if (!data || !data.accessToken) {
// Alert.alert("登录失败", "无法获取有效的 Facebook AccessToken。");
// return;
// }
// const tokenString = data.accessToken.toString();
// console.log("Facebook Access Token:", tokenString);
// // 直接获取 Facebook 用户信息并打印
// await fetchFacebookProfile(tokenString);
// // 移除之前的 Alert, 因为 fetchFacebookProfile 内部会处理打印和可能的错误提示
// // 如果 fetchFacebookProfile 成功,信息已在控制台,如果失败,它会 Alert
// // 可以选择在这里加一个通用成功提示,表明流程已执行
// Alert.alert("操作完成", "已尝试 Facebook 登录并获取用户信息,请查看控制台输出。");
// } catch (error: any) {
// console.error("Facebook 登录或获取资料时发生错误:", error);
// let errorMessage = "发生未知错误";
// if (error && typeof error.message === 'string') {
// errorMessage = error.message;
// }
// Alert.alert("登录错误", `Facebook 操作失败:${errorMessage}`);
// }
};
// 处理Apple登录

1
app/screens/productStatus/OrderDatails.tsx

@ -821,7 +821,6 @@ export const OrderDetails = () => {
t("order.status.waiting_payment"),
t("order.status.waiting_shipment"),
t("order.status.in_transit"),
t("order.status.waiting_receipt"),
t("order.status.completed"),
]}
/>

7
app/screens/setting/CountrySetting.tsx

@ -98,6 +98,11 @@ export const CountrySetting = () => {
visibilityTime: 1000,
});
// Emit event for HomeScreen to refresh if currency or language changed
if (changeType === "currency" || changeType === "language") {
eventBus.emit("settingsChanged");
}
// 只有在用户已登录的情况下才调用服务器接口
if (user?.user_id) {
try {
@ -116,6 +121,8 @@ export const CountrySetting = () => {
// 用户未登录,只保存到本地
if (changeType === "language" && language) {
await changeLanguage(language);
// 语言变更时也需要通知HomeScreen重新加载
eventBus.emit("refreshSetting");
}
console.log('用户未登录,设置已保存到本地');
}

Loading…
Cancel
Save