|
|
|
@ -781,6 +781,11 @@ export const HomeScreen = () => {
|
|
|
|
|
Alert.alert("已重置", "现在您可以使用相机功能了"); |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
// 优化轮播图切换回调
|
|
|
|
|
const handleCarouselSnap = useCallback((index: number) => { |
|
|
|
|
setActiveIndex(index); |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
const renderItem = ({ item, index }: { item: Product; index: number }) => { |
|
|
|
|
if (index >= products.length && index < products.length + loadingPlaceholders) { |
|
|
|
|
return <ProductSkeleton />; |
|
|
|
@ -788,10 +793,11 @@ export const HomeScreen = () => {
|
|
|
|
|
return renderProductItem({ item }); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const renderHeader = () => ( |
|
|
|
|
const renderHeader = useCallback(() => ( |
|
|
|
|
<> |
|
|
|
|
<View style={styles.swiperContainer}> |
|
|
|
|
<Carousel |
|
|
|
|
key="carousel-header" |
|
|
|
|
width={screenWidth} |
|
|
|
|
data={data} |
|
|
|
|
height={widthUtils(286, 286).height} |
|
|
|
@ -799,6 +805,7 @@ export const HomeScreen = () => {
|
|
|
|
|
parallaxScrollingScale: 0, |
|
|
|
|
parallaxScrollingOffset: 0, |
|
|
|
|
}} |
|
|
|
|
onSnapToItem={handleCarouselSnap} |
|
|
|
|
renderItem={({ item }) => ( |
|
|
|
|
<TouchableOpacity |
|
|
|
|
onPress={() => navigation.navigate(item.add)} |
|
|
|
@ -822,6 +829,17 @@ export const HomeScreen = () => {
|
|
|
|
|
</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} |
|
|
|
@ -943,7 +961,7 @@ export const HomeScreen = () => {
|
|
|
|
|
</View> |
|
|
|
|
) : null} |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
), [activeIndex, selectedHorizontalCategory]); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<SafeAreaView style={styles.safeArea}> |
|
|
|
@ -956,30 +974,8 @@ export const HomeScreen = () => {
|
|
|
|
|
{renderSkeletonGrid()} |
|
|
|
|
</View> |
|
|
|
|
) : ( |
|
|
|
|
<FlatList |
|
|
|
|
ref={flatListRef} |
|
|
|
|
data={[...products, ...Array(loadingPlaceholders).fill(null)]} |
|
|
|
|
numColumns={2} |
|
|
|
|
<ScrollView |
|
|
|
|
showsVerticalScrollIndicator={false} |
|
|
|
|
columnWrapperStyle={styles.productCardGroup} |
|
|
|
|
renderItem={renderItem} |
|
|
|
|
keyExtractor={(item, index) => |
|
|
|
|
(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 ? ( |
|
|
|
|
<View style={{ padding: 10, alignItems: 'center' }}> |
|
|
|
|
<Text>没有更多数据了</Text> |
|
|
|
|
</View> |
|
|
|
|
) : null |
|
|
|
|
)} |
|
|
|
|
refreshControl={ |
|
|
|
|
<RefreshControl |
|
|
|
|
refreshing={refreshing} |
|
|
|
@ -989,7 +985,236 @@ 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> |
|
|
|
|
))} |
|
|
|
|
</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.productCardGroup}> |
|
|
|
|
{/* 左侧商品 */} |
|
|
|
|
{leftItem ? ( |
|
|
|
|
i >= products.length ? ( |
|
|
|
|
<ProductSkeleton /> |
|
|
|
|
) : ( |
|
|
|
|
renderProductItem({ item: leftItem }) |
|
|
|
|
) |
|
|
|
|
) : null} |
|
|
|
|
|
|
|
|
|
{/* 右侧商品 */} |
|
|
|
|
{rightItem ? ( |
|
|
|
|
i + 1 >= products.length ? ( |
|
|
|
|
<ProductSkeleton /> |
|
|
|
|
) : ( |
|
|
|
|
renderProductItem({ item: rightItem }) |
|
|
|
|
) |
|
|
|
|
) : ( |
|
|
|
|
<View style={{ width: "48%" }} /> |
|
|
|
|
)} |
|
|
|
|
</View> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return rows; |
|
|
|
|
})()} |
|
|
|
|
</View> |
|
|
|
|
|
|
|
|
|
{/* 底部提示 */} |
|
|
|
|
{!hasMore && !loadingPlaceholders && ( |
|
|
|
|
<View style={{ padding: 10, alignItems: 'center' }}> |
|
|
|
|
<Text>没有更多数据了</Text> |
|
|
|
|
</View> |
|
|
|
|
)} |
|
|
|
|
</View> |
|
|
|
|
</ScrollView> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
<Modal |
|
|
|
@ -1385,6 +1610,7 @@ const styles = StyleSheet.create<StylesType>({
|
|
|
|
|
paddingTop: 0, |
|
|
|
|
}, |
|
|
|
|
productCardGroup: { |
|
|
|
|
flexDirection: "row", |
|
|
|
|
justifyContent: "space-between", |
|
|
|
|
marginBottom: 15, |
|
|
|
|
paddingHorizontal: 15, |
|
|
|
|