import React, { useCallback, useState, useRef, useEffect, useMemo } from "react"; import { View, Text, StyleSheet, TouchableOpacity, FlatList, InteractionManager, Image, ScrollView, Modal, RefreshControl, Dimensions, Animated, Platform, StatusBar, SafeAreaView, ViewStyle, TextStyle, ImageStyle, Linking, Alert, } from "react-native"; import { productApi, ProductParams, type Product, } from "../services/api/productApi"; import Carousel from "react-native-reanimated-carousel"; import Ionicons from "@expo/vector-icons/Ionicons"; import { useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { useTranslation } from "react-i18next"; import widthUtils from "../utils/widthUtils"; import DownArrowIcon from "../components/DownArrowIcon"; import { LinearGradient } from "expo-linear-gradient"; import fontSize from "../utils/fontsizeUtils"; import CloseIcon from "../components/CloseIcon"; import CheckmarkIcon from "../components/CheckmarkIcon"; import { getSubjectTransLanguage } from "../utils/languageUtils"; import useUserStore from "../store/user"; import * as ImagePicker from "expo-image-picker"; import * as FileSystem from "expo-file-system"; import { useGlobalStore } from "../store/useGlobalStore"; import { getCurrentLanguage } from '../i18n'; // 为图标定义类型 type IconProps = { name: string; size: number; color: string; }; // 图标组件辅助函数 - 使用React.memo优化渲染 const IconComponent = React.memo(({ name, size, color }: IconProps) => { const Icon = Ionicons as any; return ; }); type SubcategoryItem = { id: string | number; title: string; icon: string; }; type CategoryContentType = { [key: string]: SubcategoryItem[]; }; // 懒加载图片组件 - 改进版本 const LazyImage = React.memo( ({ uri, style, resizeMode, }: { uri: string; style: any; resizeMode: any; }) => { const [isLoaded, setIsLoaded] = useState(false); const [hasError, setHasError] = useState(false); const onLoad = useCallback(() => { setIsLoaded(true); }, []); const onError = useCallback(() => { setHasError(true); setIsLoaded(true); // Also mark as loaded on error to remove placeholder }, []); return ( {/* Show placeholder while image is loading */} {!isLoaded && !hasError && ( )} {/* Show error state if image failed to load */} {hasError && ( 加载失败 )} {/* Actual image */} ); } ); // 产品骨架屏组件 - 用于加载状态 const ProductSkeleton = React.memo(() => { // 创建动画值 const shimmerAnim = useRef(new Animated.Value(0)).current; // 设置动画效果 useEffect(() => { const shimmerAnimation = Animated.loop( Animated.timing(shimmerAnim, { toValue: 1, duration: 1500, useNativeDriver: true, }) ); shimmerAnimation.start(); return () => { shimmerAnimation.stop(); }; }, []); // 定义动画插值 const shimmerTranslate = shimmerAnim.interpolate({ inputRange: [0, 1], outputRange: [-200, 200], }); return ( ); }); // Define the styles type to fix TypeScript errors type StylesType = { safeArea: ViewStyle; safeAreaContent: ViewStyle; container: ViewStyle; swpImg: ImageStyle; searchOverlay: ViewStyle; searchBar: ViewStyle; searchPlaceholder: TextStyle; cameraButton: ViewStyle; bannerContainer: ViewStyle; leftContainer: ViewStyle; leftTopItem: ViewStyle; leftBottomItem: ViewStyle; rightContainer: ViewStyle; bannerIcon: ImageStyle; bigbannerIcon: ImageStyle; category: ViewStyle; categoryScrollContainer: ViewStyle; categoryScroll: ViewStyle; categoryItem: ViewStyle; categoryItemActive: ViewStyle; categoryText: TextStyle; categoryTextActive: TextStyle; swiperContainer: ViewStyle; swiper: ViewStyle; dot: ViewStyle; activeDot: ViewStyle; slide: ViewStyle; slideImage: ImageStyle; fadeGradient: ViewStyle; categoryArrowContainer: ViewStyle; modalOverlay: ViewStyle; modalContent: ViewStyle; modalHeader: ViewStyle; modalTitleContainer: ViewStyle; modalTitle: TextStyle; closeButton: ViewStyle; closeButtonText: TextStyle; modalScrollView: ViewStyle; categoryModalItem: ViewStyle; categoryModalText: TextStyle; selectedCategoryText: TextStyle; subcategoryContainer: ViewStyle; subcategoryScroll: ViewStyle; subcategoryContent: ViewStyle; subcategoryItem: ViewStyle; subcategoryImagePlaceholder: ViewStyle; subcategoryText: TextStyle; productContainer: ViewStyle; productCardList: ViewStyle; productCardGroup: ViewStyle; beautyProductCard1: ViewStyle; beautyCardContainer1: ViewStyle; vipButtonContainer: ViewStyle; vipButton: ViewStyle; vipButtonText: TextStyle; vipLabelBold: TextStyle; beautyProductCard: ViewStyle; beautyProductTitle: TextStyle; beautyProductInfoRow: ViewStyle; flexRowCentered: ViewStyle; priceContainer: ViewStyle; highlightedText: TextStyle; highlightedText1: TextStyle; priceContainer1: ViewStyle; priceLabel1: TextStyle; beautySalesInfo: TextStyle; indicatorContainer: ViewStyle; indicator: ViewStyle; activeIndicator: ViewStyle; inactiveIndicator: ViewStyle; skeletonContainer: ViewStyle; skeletonImage: ViewStyle; skeletonTitle: ViewStyle; skeletonPrice: ViewStyle; skeletonSales: ViewStyle; shimmer: ViewStyle; imagePlaceholder: ViewStyle; productImage: ImageStyle; imagePickerOverlay: ViewStyle; imagePickerContent: ViewStyle; imagePickerOption: ViewStyle; imagePickerText: TextStyle; imagePickerDivider: ViewStyle; imagePickerCancelButton: ViewStyle; imagePickerCancelText: TextStyle; }; export const HomeScreen = () => { const [activeIndex, setActiveIndex] = useState(0); const screenWidth = Dimensions.get("window").width; const navigation = useNavigation>(); 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(null); const horizontalScrollRef = useRef(null); const data = [ { 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 [galleryUsed, setGalleryUsed] = useState(false); const [hotTerms, setHotTerms] = useState([]); const [isLoadingHotTerms, setIsLoadingHotTerms] = useState(false); // 直接在组件中实现分页加载逻辑 const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [hasMore, setHasMore] = useState(true); const [refreshing, setRefreshing] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [loadingPlaceholders, setLoadingPlaceholders] = useState(0); const [totalItems, setTotalItems] = useState(0); const [params, setParams] = useState({ keyword: "pen", // 初始关键词,将在获取热门关键词后更新 sort_order: "desc", sort_by: "default", language: getCurrentLanguage(), page: 1, page_size: 10, ...(userStore.user?.user_id ? { user_id: userStore.user.user_id } : {}), }); // 获取热门关键词并初始化产品列表 useEffect(() => { const initApp = async () => { 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 })); // 获取第一页数据 await fetchInitialProducts(randomKeyword); } else { // 如果没有热门关键词,使用默认关键词"pen" await fetchInitialProducts("pen"); } } catch (error) { console.error("初始化失败:", error); // 出错时使用默认关键词 await fetchInitialProducts("pen"); } }; initApp(); }, []); // 获取随机关键词 const getRandomKeyword = useCallback(() => { if (hotTerms.length === 0) return "pen"; const randomIndex = Math.floor(Math.random() * hotTerms.length); const keyword = hotTerms[randomIndex]; console.log("获取随机关键词:", keyword); return keyword; }, [hotTerms]); // 获取初始产品数据(第一页及额外的三页) const fetchInitialProducts = useCallback(async (keyword: string) => { setLoading(true); try { // 第一页请求参数 const initialParams = { ...params, keyword, page: 1, page_size: 10 }; // 获取第一页数据 const firstPageRes = await productApi.getSearchProducts(initialParams); setProducts(firstPageRes.products); setTotalItems(firstPageRes.total || 0); if (hotTerms.length > 0) { // 存储已使用的关键词,避免重复 const usedKeywords = new Set([keyword]); // 创建获取唯一关键词的函数 const getUniqueKeyword = () => { // 如果热门关键词数量不足,或者已经用完所有关键词,返回随机关键词 if (hotTerms.length <= usedKeywords.size || hotTerms.length <= 1) { return hotTerms[Math.floor(Math.random() * hotTerms.length)]; } // 尝试获取未使用过的关键词 let attempts = 0; while (attempts < 10) { // 最多尝试10次 const randomIndex = Math.floor(Math.random() * hotTerms.length); const candidateKeyword = hotTerms[randomIndex]; if (!usedKeywords.has(candidateKeyword)) { usedKeywords.add(candidateKeyword); return candidateKeyword; } attempts++; } // 如果无法找到唯一关键词,返回随机关键词 return hotTerms[Math.floor(Math.random() * hotTerms.length)]; }; // 使用不同关键词加载额外的3页数据 const remainingRequests = Array.from({ length: 3 }, async (_, index) => { // 获取唯一的随机关键词 const pageKeyword = getUniqueKeyword(); const pageParams = { ...params, keyword: pageKeyword, page: index + 2, page_size: 10 }; return productApi.getSearchProducts(pageParams); }); // 并行获取额外数据 const additionalResults = await Promise.all(remainingRequests); const additionalProducts = additionalResults.flatMap(result => result.products); // 合并所有产品 setProducts(prev => [...prev, ...additionalProducts]); setCurrentPage(4); setHasMore(firstPageRes.products.length + additionalProducts.length < (firstPageRes.total || 0)); } else { // 如果没有热门关键词,只使用第一页数据 setCurrentPage(1); setHasMore(firstPageRes.products.length < (firstPageRes.total || 0)); } } catch (error) { console.error("获取产品数据失败:", error); } finally { setLoading(false); } }, [params, hotTerms]); // 加载更多产品 const handleLoadMore = useCallback(() => { if (!hasMore || loadingMore || hotTerms.length === 0) return; setLoadingMore(true); setLoadingPlaceholders(10); // 使用新的随机关键词 const newKeyword = getRandomKeyword(); // 准备请求参数 const loadMoreParams = { ...params, keyword: newKeyword, page: currentPage + 1, page_size: 10 }; // 获取下一页数据 productApi.getSearchProducts(loadMoreParams) .then(res => { setProducts(prev => [...prev, ...res.products]); setCurrentPage(prev => prev + 1); setHasMore((products.length + res.products.length) < (res.total || 0)); }) .catch(error => { console.error("加载更多失败:", error); }) .finally(() => { setLoadingMore(false); setLoadingPlaceholders(0); }); }, [hasMore, loadingMore, hotTerms, getRandomKeyword, params, currentPage, products.length]); // 刷新产品列表 const handleRefresh = useCallback(async () => { if (hotTerms.length === 0) return; setRefreshing(true); try { // 使用新的随机关键词 const refreshKeyword = getRandomKeyword(); console.log("刷新,使用关键词:", refreshKeyword); // 重新获取初始数据 await fetchInitialProducts(refreshKeyword); } catch (error) { console.error("刷新失败:", error); } finally { setRefreshing(false); } }, [hotTerms, getRandomKeyword, fetchInitialProducts]); const handleProductPress = useCallback( (item: Product) => { InteractionManager.runAfterInteractions(() => { navigation.navigate("ProductDetail", { offer_id: item.offer_id, searchKeyword: params.keyword, price: item.min_price, }); }); }, [navigation] ); const categories = [ "Tous", "Bijoux", "Maison et Cuisine", "Vêtements femme", "Grandes tailles femme", "Chaussures femme", "Pyjamas et Sous-vête-ments", "Accessoires beauté", "Soins cheveux", "Hygiène et Soins pour le corps", "Maquillage", ]; const defaultSubcategories: SubcategoryItem[] = [ { id: 1, title: "Jewelry", icon: "diamond-outline" }, { id: 2, title: "Earrings", icon: "ear-outline" }, { id: 3, title: "Bracelet", icon: "watch-outline" }, { id: 4, title: "Jewelry Sets", icon: "gift-outline" }, { id: 5, title: "Earrings", icon: "ear-outline" }, { id: 6, title: "Bracelet", icon: "watch-outline" }, ]; const categoryContent: CategoryContentType = { Tous: [], Bijoux: defaultSubcategories, "Maison et Cuisine": defaultSubcategories, "Vêtements femme": defaultSubcategories, "Grandes tailles femme": defaultSubcategories, "Chaussures femme": defaultSubcategories, "Pyjamas et Sous-vête-ments": defaultSubcategories, "Accessoires beauté": defaultSubcategories, "Soins cheveux": defaultSubcategories, "Hygiène et Soins pour le corps": defaultSubcategories, Maquillage: defaultSubcategories, }; useEffect(() => { if (!categoryContent[selectedHorizontalCategory]) { setSelectedHorizontalCategory("Tous"); } }, [selectedHorizontalCategory]); const navigateToSearch = useCallback(() => { InteractionManager.runAfterInteractions(() => { navigation.navigate("Search"); }); }, [navigation]); const navigateToShippingDetails = useCallback(() => { InteractionManager.runAfterInteractions(() => { navigation.navigate("ShippingDetailsSection"); }); }, [navigation]); const navigateToInquiry = useCallback(() => { InteractionManager.runAfterInteractions(() => { navigation.navigate("InquiryScreen"); }); }, [navigation]); const scrollToCategory = (category: string) => { const categoryIndex = categories.findIndex((c) => c === category); if (categoryIndex !== -1 && horizontalScrollRef.current) { const firstFourKeys = Object.keys(categoryContent).slice(0, categoryIndex - 1); let str = ""; firstFourKeys.forEach((key) => { str += key; }); horizontalScrollRef.current.scrollTo({ x: str.length * fontSize(16) + (categoryIndex - 1 + 17), animated: true, }); } }; const renderProductItem = ({ item }: { item: Product }) => ( handleProductPress(item)} activeOpacity={0.9} style={styles.beautyProductCard1} > {item.product_image_urls && item.product_image_urls.length > 0 ? ( ) : ( )} {userStore.user?.user_id && ( VIP {userStore.user?.vip_level} )} {getSubjectTransLanguage(item) || item.subject_trans} {userStore.user?.user_id && ( {item.original_min_price || "0"} {item.currency || "FCFA"} )} {item.min_price || "0"} {item.currency || "FCFA"} {item.sold_out || "0"}+ {t("homePage.sales")} ); const renderSkeletonGrid = useCallback(() => { const skeletonArray = Array(8).fill(null); return ( } keyExtractor={(_, index) => `skeleton-${index}`} numColumns={2} columnWrapperStyle={styles.productCardGroup} scrollEnabled={false} contentContainerStyle={{ paddingBottom: 15 }} /> ); }, []); const cleanupImagePickerCache = async () => { try { if (Platform.OS === 'web') { console.log('Cache cleanup skipped on web platform'); setGalleryUsed(false); return; } const cacheDir = `${FileSystem.cacheDirectory}ImagePicker`; await FileSystem.deleteAsync(cacheDir, { idempotent: true }); console.log("已清理ImagePicker缓存"); setGalleryUsed(false); } catch (error) { console.log("清理缓存错误", error); setGalleryUsed(false); } }; const handleChooseFromGallery = useCallback(async () => { setShowImagePickerModal(false); setTimeout(async () => { try { const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (permissionResult.status !== "granted") { console.log("相册权限被拒绝"); return; } const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.canceled && result.assets && result.assets.length > 0) { await cleanupImagePickerCache(); navigation.navigate("ImageSearchResultScreen", { image: result.assets[0].uri, type: 1, }); } } catch (error) { console.error("相册错误:", error); await cleanupImagePickerCache(); } }, 500); }, []); const handleTakePhoto = useCallback(async () => { setShowImagePickerModal(false); setTimeout(async () => { try { const permissionResult = await ImagePicker.requestCameraPermissionsAsync(); if (permissionResult.status !== "granted") { console.log("相机权限被拒绝"); return; } const result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.canceled && result.assets && result.assets.length > 0) { await cleanupImagePickerCache(); navigation.navigate("ImageSearchResultScreen", { image: result.assets[0].uri, type: 1, }); } } catch (error) { console.error("相机错误:", error); await cleanupImagePickerCache(); } }, 500); }, []); const resetAppState = useCallback(() => { setGalleryUsed(false); cleanupImagePickerCache(); Alert.alert("已重置", "现在您可以使用相机功能了"); }, []); const renderItem = ({ item, index }: { item: Product; index: number }) => { if (index >= products.length && index < products.length + loadingPlaceholders) { return ; } return renderProductItem({ item }); }; const renderHeader = () => ( <> ( navigation.navigate(item.add)} key={item.imgUrl} style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#f2f2f2", borderRadius: 0, overflow: "hidden", }} > )} /> {t("homePage.searchPlaceholder")} setShowImagePickerModal(true)} > navigation.navigate("TikTokScreen")} > {categories.map((category, index) => ( setSelectedHorizontalCategory(category)} > {t(`homePage.${category.toLowerCase()}`)} ))} setShowCategoryModal(true)}> {selectedHorizontalCategory && categoryContent[selectedHorizontalCategory] && categoryContent[selectedHorizontalCategory].length > 0 ? ( {categoryContent[selectedHorizontalCategory].map((item) => ( { // Handle subcategory selection }} > {item.title} ))} ) : null} ); return ( {loading ? ( {renderHeader()} {renderSkeletonGrid()} ) : ( (item?.offer_id ? `${item.offer_id}-${currentPage}-${index}` : `placeholder-${currentPage}-${index}`) } contentContainerStyle={{ paddingBottom: 15, backgroundColor: "transparent", }} ListHeaderComponent={renderHeader} onEndReached={handleLoadMore} onEndReachedThreshold={3} ListFooterComponent={() => ( !hasMore && !loadingPlaceholders ? ( 没有更多数据了 ) : null )} refreshControl={ } /> )} setShowCategoryModal(false)} > 推荐 setShowCategoryModal(false)} > {categories.map((category, index) => ( { setSelectedCategory(category); setSelectedHorizontalCategory(category); setShowCategoryModal(false); scrollToCategory(category); }} > {category} {selectedCategory === category && ( )} ))} setShowImagePickerModal(false)} > setShowImagePickerModal(false)} > {!galleryUsed ? ( {t("homePage.takePhoto")} ) : ( 重置相机功能 )} {t("homePage.chooseFromGallery")} setShowImagePickerModal(false)} > {t("homePage.cancel")} ); }; const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: "#fff", }, safeAreaContent: { flex: 1, paddingTop: Platform.OS === "android" ? 0 : 0, }, container: { flex: 1, backgroundColor: "#fff", }, swpImg: { width: "100%", height: 180, }, searchOverlay: { position: "absolute", top: widthUtils(20, 20).height, left: 15, right: 15, zIndex: 10, }, searchBar: { flexDirection: "row", alignItems: "center", backgroundColor: "rgba(255, 255, 255, 0.9)", borderRadius: 20, paddingHorizontal: 15, height: 40, shadowColor: "#000", shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 3, elevation: 3, }, searchPlaceholder: { flex: 1, marginLeft: 8, fontSize: fontSize(16), }, cameraButton: { padding: 5, }, bannerContainer: { flexDirection: "row", marginTop: 20, paddingHorizontal: 15, width: "100%", }, leftContainer: { marginRight: "4%", }, leftTopItem: { height: widthUtils(90, 200).height, width: widthUtils(90, 200).width, borderRadius: 8, padding: 0, marginBottom: 10, overflow: "hidden", }, leftBottomItem: { height: widthUtils(90, 200).height, width: widthUtils(90, 200).width, borderRadius: 8, padding: 0, overflow: "hidden", }, rightContainer: { width: widthUtils(190, 180).width, height: widthUtils(190, 180).height, borderRadius: 8, padding: 0, overflow: "hidden", flex: 1, }, bannerIcon: { width: "100%", height: "100%", objectFit: "contain", }, bigbannerIcon: { width: "100%", height: "100%", objectFit: "contain", }, category: { width: "100%", paddingVertical: 10, flexDirection: "row", alignItems: "center", marginTop: 10, backgroundColor: "#fff", }, categoryScrollContainer: { flex: 1, position: "relative", overflow: "hidden", paddingRight: 55, }, categoryScroll: { paddingHorizontal: 15, }, categoryItem: { paddingHorizontal: 12, paddingVertical: 8, marginRight: 5, }, categoryItemActive: { borderBottomWidth: 2, borderBottomColor: "#000", }, categoryText: { fontSize: fontSize(16), color: "#747474", fontFamily: "Alexandria", fontWeight: "400", }, categoryTextActive: { color: "#000", fontWeight: "500", }, swiperContainer: { width: "100%", height: widthUtils(286, 430).height, paddingHorizontal: 0, }, swiper: { width: "100%", }, dot: { backgroundColor: "rgba(255,255,255,0.5)", width: 8, height: 8, borderRadius: 4, marginLeft: 3, marginRight: 3, }, activeDot: { backgroundColor: "#fff", width: 20, height: 8, borderRadius: 4, marginLeft: 3, marginRight: 3, }, slide: { flex: 1, justifyContent: "center", alignItems: "center", width: "100%", }, slideImage: { width: "100%", height: "100%", }, fadeGradient: { position: "absolute", right: 40, top: 0, bottom: 0, width: 40, zIndex: 1, backgroundColor: "transparent", }, categoryArrowContainer: { position: "absolute", right: 0, paddingRight: 15, height: "100%", justifyContent: "center", alignItems: "center", zIndex: 2, backgroundColor: "#fff", }, modalOverlay: { flex: 1, backgroundColor: "rgba(0, 0, 0, 0.5)", justifyContent: "flex-end", }, modalContent: { backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20, maxHeight: "80%", }, modalHeader: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", padding: 16, borderBottomWidth: 1, borderBottomColor: "#f0f0f0", }, modalTitleContainer: { flexDirection: "row", alignItems: "center", }, modalTitle: { fontSize: fontSize(16), fontWeight: "600", marginLeft: 8, }, closeButton: { padding: 8, }, closeButtonText: { fontSize: fontSize(24), color: "#000", fontWeight: "300", }, modalScrollView: { padding: 16, }, categoryModalItem: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingVertical: 16, }, categoryModalText: { fontSize: fontSize(16), color: "#666", fontFamily: "Segoe UI", fontWeight: "700", }, selectedCategoryText: { color: "#000", fontWeight: "500", }, subcategoryContainer: { height: 100, // Fixed height for the subcategory container marginTop: 15, }, subcategoryScroll: { flex: 1, }, subcategoryContent: { paddingHorizontal: 15, }, subcategoryItem: { alignItems: "center", marginRight: 15, width: 60, }, subcategoryImagePlaceholder: { width: 60, height: 60, borderRadius: 30, backgroundColor: "#f5f5f5", justifyContent: "center", alignItems: "center", marginBottom: 8, }, subcategoryText: { fontSize: fontSize(12), color: "#333", textAlign: "center", fontFamily: "Alexandria", }, productContainer: { flex: 1, backgroundColor: "#fff", }, productCardList: { padding: 15, paddingTop: 0, }, productCardGroup: { justifyContent: "space-between", marginBottom: 15, paddingHorizontal: 15, }, beautyProductCard1: { width: "48%", }, beautyCardContainer1: { flexDirection: "column", alignItems: "flex-end", justifyContent: "center", width: "100%", height: 160, backgroundColor: "transparent", borderRadius: 10, overflow: "hidden", position: "relative", }, vipButtonContainer: { position: "absolute", top: 0, right: 0, zIndex: 2, }, vipButton: { width: widthUtils(30, 60).width, height: widthUtils(30, 60).height, justifyContent: "center", alignItems: "center", backgroundColor: "#3b3b3b", borderRadius: 10, flexDirection: "row", }, vipButtonText: { fontStyle: "italic", fontWeight: "900", fontSize: fontSize(18), color: "#f1c355", }, vipLabelBold: { fontStyle: "italic", fontWeight: "900", fontSize: fontSize(18), color: "#f1c355", }, beautyProductCard: { marginTop: 9, }, beautyProductTitle: { fontSize: fontSize(14), fontWeight: "600", color: "black", lineHeight: 18, }, beautyProductInfoRow: { flexDirection: "row", alignItems: "center", }, flexRowCentered: {}, priceContainer: { flexDirection: "row", }, highlightedText: { fontWeight: "700", fontSize: fontSize(24), color: "#ff5100", marginLeft: 2, }, highlightedText1: { fontWeight: "700", fontSize: fontSize(14), color: "#ff5100", }, priceContainer1: {}, priceLabel1: { fontSize: fontSize(12), fontWeight: "600", color: "#9a9a9a", textDecorationLine: "line-through", }, beautySalesInfo: { marginTop: 6.75, fontSize: fontSize(14), fontWeight: "600", color: "#7c7c7c", }, indicatorContainer: { flexDirection: "row", justifyContent: "center", alignItems: "center", position: "absolute", bottom: 10, left: 0, right: 0, }, indicator: { marginHorizontal: 4, borderRadius: 3, }, activeIndicator: { width: 14, height: 6, backgroundColor: "#fff", }, inactiveIndicator: { width: 6, height: 6, backgroundColor: "rgba(255, 255, 255, 0.5)", }, // 骨架屏样式 skeletonContainer: { paddingHorizontal: 0, paddingTop: 0, }, skeletonImage: { width: "100%", paddingBottom: "100%", borderRadius: 10, backgroundColor: "#e1e1e1", overflow: "hidden", position: "relative", }, skeletonTitle: { height: 16, borderRadius: 4, marginTop: 8, marginBottom: 4, width: "100%", backgroundColor: "#e1e1e1", overflow: "hidden", position: "relative", }, skeletonPrice: { height: 24, width: 80, borderRadius: 4, marginTop: 8, backgroundColor: "#e1e1e1", overflow: "hidden", position: "relative", }, skeletonSales: { height: 14, width: "40%", borderRadius: 4, marginTop: 8, backgroundColor: "#e1e1e1", overflow: "hidden", position: "relative", }, shimmer: { width: "30%", height: "100%", backgroundColor: "rgba(255, 255, 255, 0.3)", position: "absolute", top: 0, left: 0, }, imagePlaceholder: { backgroundColor: "#EAEAEA", justifyContent: "center", alignItems: "center", borderRadius: 8, }, productImage: { width: "100%", height: "100%", borderRadius: 10, }, // Image Picker Modal Styles imagePickerOverlay: { flex: 1, backgroundColor: "rgba(0, 0, 0, 0.5)", justifyContent: "flex-end", }, imagePickerContent: { backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20, paddingTop: 20, }, imagePickerOption: { flexDirection: "row", alignItems: "center", paddingVertical: 16, paddingHorizontal: 20, }, imagePickerText: { fontSize: fontSize(16), marginLeft: 12, color: "#333", }, imagePickerDivider: { height: 1, backgroundColor: "#f0f0f0", marginHorizontal: 20, }, imagePickerCancelButton: { alignItems: "center", paddingVertical: 16, marginTop: 8, borderTopWidth: 1, borderTopColor: "#f0f0f0", }, imagePickerCancelText: { fontSize: fontSize(16), color: "#999", }, });