|
|
@ -52,6 +52,61 @@ type CategoryContentType = { |
|
|
|
[key: string]: SubcategoryItem[]; |
|
|
|
[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 ( |
|
|
|
|
|
|
|
<View style={[style, { overflow: "hidden" }]}> |
|
|
|
|
|
|
|
{/* Show placeholder while image is loading */} |
|
|
|
|
|
|
|
{!isLoaded && !hasError && ( |
|
|
|
|
|
|
|
<View style={[style, styles.imagePlaceholder, { position: 'absolute', zIndex: 1 }]} /> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Show error state if image failed to load */} |
|
|
|
|
|
|
|
{hasError && ( |
|
|
|
|
|
|
|
<View |
|
|
|
|
|
|
|
style={[style, styles.imagePlaceholder, { position: 'absolute', zIndex: 1 }]} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<IconComponent name="image-outline" size={24} color="#999" /> |
|
|
|
|
|
|
|
<Text style={{ fontSize: fontSize(12), color: "#999", marginTop: 4 }}> |
|
|
|
|
|
|
|
加载失败 |
|
|
|
|
|
|
|
</Text> |
|
|
|
|
|
|
|
</View> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Actual image */} |
|
|
|
|
|
|
|
<Image |
|
|
|
|
|
|
|
source={{ uri }} |
|
|
|
|
|
|
|
style={[style, { opacity: isLoaded ? 1 : 0 }]} |
|
|
|
|
|
|
|
resizeMode={resizeMode} |
|
|
|
|
|
|
|
onLoad={onLoad} |
|
|
|
|
|
|
|
onError={onError} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</View> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// 产品骨架屏组件 - 用于加载状态
|
|
|
|
// 产品骨架屏组件 - 用于加载状态
|
|
|
|
const ProductSkeleton = React.memo(() => { |
|
|
|
const ProductSkeleton = React.memo(() => { |
|
|
|
// 创建动画值
|
|
|
|
// 创建动画值
|
|
|
@ -303,19 +358,30 @@ export const HomeScreen = () => { |
|
|
|
activeOpacity={0.9} |
|
|
|
activeOpacity={0.9} |
|
|
|
style={styles.beautyProductCard1} |
|
|
|
style={styles.beautyProductCard1} |
|
|
|
> |
|
|
|
> |
|
|
|
<ImageBackground |
|
|
|
<View style={styles.beautyCardContainer1}> |
|
|
|
style={styles.beautyCardContainer1} |
|
|
|
{item.product_image_urls && item.product_image_urls.length > 0 ? ( |
|
|
|
source={{ uri: item.product_image_urls[0] }} |
|
|
|
<LazyImage |
|
|
|
> |
|
|
|
uri={item.product_image_urls[0]} |
|
|
|
|
|
|
|
style={styles.productImage} |
|
|
|
|
|
|
|
resizeMode="cover" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
) : ( |
|
|
|
|
|
|
|
<View style={[styles.productImage, styles.imagePlaceholder]}> |
|
|
|
|
|
|
|
<IconComponent name="image-outline" size={24} color="#999" /> |
|
|
|
|
|
|
|
</View> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
{userStore.user?.user_id && ( |
|
|
|
{userStore.user?.user_id && ( |
|
|
|
<TouchableOpacity style={styles.vipButton}> |
|
|
|
<View style={styles.vipButtonContainer}> |
|
|
|
<Text style={styles.vipButtonText}>VIP</Text> |
|
|
|
<TouchableOpacity style={styles.vipButton}> |
|
|
|
<Text style={styles.vipLabelBold}> |
|
|
|
<Text style={styles.vipButtonText}>VIP</Text> |
|
|
|
{userStore.user?.vip_level} |
|
|
|
<Text style={styles.vipLabelBold}> |
|
|
|
</Text> |
|
|
|
{userStore.user?.vip_level} |
|
|
|
</TouchableOpacity> |
|
|
|
</Text> |
|
|
|
|
|
|
|
</TouchableOpacity> |
|
|
|
|
|
|
|
</View> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</ImageBackground> |
|
|
|
</View> |
|
|
|
<View style={styles.beautyProductCard}> |
|
|
|
<View style={styles.beautyProductCard}> |
|
|
|
<Text |
|
|
|
<Text |
|
|
|
style={styles.beautyProductTitle} |
|
|
|
style={styles.beautyProductTitle} |
|
|
@ -921,10 +987,17 @@ const styles = StyleSheet.create({ |
|
|
|
alignItems: "flex-end", |
|
|
|
alignItems: "flex-end", |
|
|
|
justifyContent: "center", |
|
|
|
justifyContent: "center", |
|
|
|
width: "100%", |
|
|
|
width: "100%", |
|
|
|
paddingBottom: 160, |
|
|
|
height: 160, |
|
|
|
backgroundColor: "transparent", |
|
|
|
backgroundColor: "transparent", |
|
|
|
borderRadius: 10, |
|
|
|
borderRadius: 10, |
|
|
|
overflow: "hidden", |
|
|
|
overflow: "hidden", |
|
|
|
|
|
|
|
position: "relative", |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
vipButtonContainer: { |
|
|
|
|
|
|
|
position: "absolute", |
|
|
|
|
|
|
|
top: 0, |
|
|
|
|
|
|
|
right: 0, |
|
|
|
|
|
|
|
zIndex: 2, |
|
|
|
}, |
|
|
|
}, |
|
|
|
vipButton: { |
|
|
|
vipButton: { |
|
|
|
width: widthUtils(30, 66).width, |
|
|
|
width: widthUtils(30, 66).width, |
|
|
@ -1059,5 +1132,16 @@ const styles = StyleSheet.create({ |
|
|
|
position: 'absolute', |
|
|
|
position: 'absolute', |
|
|
|
top: 0, |
|
|
|
top: 0, |
|
|
|
left: 0, |
|
|
|
left: 0, |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
imagePlaceholder: { |
|
|
|
|
|
|
|
backgroundColor: '#EAEAEA', |
|
|
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
|
|
borderRadius: 8, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
productImage: { |
|
|
|
|
|
|
|
width: "100%", |
|
|
|
|
|
|
|
height: "100%", |
|
|
|
|
|
|
|
borderRadius: 10, |
|
|
|
|
|
|
|
}, |
|
|
|
}); |
|
|
|
}); |