|
|
|
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
|
|
|
import {
|
|
|
|
View,
|
|
|
|
Text,
|
|
|
|
StyleSheet,
|
|
|
|
ScrollView,
|
|
|
|
Image,
|
|
|
|
TouchableOpacity,
|
|
|
|
SafeAreaView,
|
|
|
|
StatusBar,
|
|
|
|
ActivityIndicator,
|
|
|
|
Dimensions,
|
|
|
|
FlatList
|
|
|
|
} from 'react-native';
|
|
|
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
|
|
import { useNavigation, useRoute } from '@react-navigation/native';
|
|
|
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|
|
|
import { RouteProp } from '@react-navigation/native';
|
|
|
|
import { productApi, ProductDetailParams } from '../services/api/productApi';
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
|
|
|
// 获取屏幕宽度
|
|
|
|
const { width: screenWidth } = Dimensions.get('window');
|
|
|
|
|
|
|
|
// 图标组件 - 使用React.memo优化渲染
|
|
|
|
const IconComponent = React.memo(({ name, size, color }: { name: string; size: number; color: string }) => {
|
|
|
|
const Icon = Ionicons as any;
|
|
|
|
return <Icon name={name} size={size} color={color} />;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 路由参数类型
|
|
|
|
type ProductDetailRouteParams = {
|
|
|
|
offer_id: string;
|
|
|
|
searchKeyword?: string;
|
|
|
|
price: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
// 颜色选项组件
|
|
|
|
const ColorOption = React.memo(({ color, selected, onSelect }: { color: string; selected: boolean; onSelect: () => void }) => (
|
|
|
|
<TouchableOpacity
|
|
|
|
style={[styles.colorOption, selected && styles.colorOptionSelected]}
|
|
|
|
onPress={onSelect}
|
|
|
|
>
|
|
|
|
<View style={[styles.colorCircle, { backgroundColor: color }]} />
|
|
|
|
<Text style={styles.colorName}>{color}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
));
|
|
|
|
|
|
|
|
// 尺寸选项组件
|
|
|
|
const SizeOption = React.memo(({ size, selected, onSelect }: { size: string; selected: boolean; onSelect: () => void }) => (
|
|
|
|
<TouchableOpacity
|
|
|
|
style={[styles.sizeOption, selected && styles.sizeOptionSelected]}
|
|
|
|
onPress={onSelect}
|
|
|
|
>
|
|
|
|
<Text style={[styles.sizeText, selected && styles.sizeTextSelected]}>{size}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
));
|
|
|
|
|
|
|
|
// 相关商品组件
|
|
|
|
const RelatedProductItem = React.memo(({ product, onPress }: { product: any; onPress: () => void }) => (
|
|
|
|
<TouchableOpacity style={styles.relatedProductItem} onPress={onPress}>
|
|
|
|
<Image source={{ uri: product.image }} style={styles.relatedProductImage} />
|
|
|
|
<Text style={styles.relatedProductPrice}>${product.price}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
));
|
|
|
|
|
|
|
|
export const ProductDetailScreen = () => {
|
|
|
|
const { t } = useTranslation();
|
|
|
|
const navigation = useNavigation<NativeStackNavigationProp<any>>();
|
|
|
|
const route = useRoute<RouteProp<Record<string, ProductDetailRouteParams>, string>>();
|
|
|
|
const [product, setProduct] = useState<ProductDetailParams | null>(null);
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const [activeImageIndex, setActiveImageIndex] = useState(0);
|
|
|
|
const [selectedColor, setSelectedColor] = useState('Black');
|
|
|
|
const [selectedSize, setSelectedSize] = useState('M');
|
|
|
|
const [relatedProducts, setRelatedProducts] = useState([
|
|
|
|
{ id: 1, name: 'Related Product 1', price: 19.99, image: 'https://via.placeholder.com/150' },
|
|
|
|
{ id: 2, name: 'Related Product 2', price: 24.99, image: 'https://via.placeholder.com/150' },
|
|
|
|
{ id: 3, name: 'Related Product 3', price: 14.99, image: 'https://via.placeholder.com/150' },
|
|
|
|
{ id: 4, name: 'Related Product 4', price: 34.99, image: 'https://via.placeholder.com/150' },
|
|
|
|
]);
|
|
|
|
const [page, setPage] = useState(1);
|
|
|
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
|
|
const [hasMoreProducts, setHasMoreProducts] = useState(true);
|
|
|
|
|
|
|
|
// 颜色和尺寸选项
|
|
|
|
const colorOptions = ['Black', 'White', 'Blue', 'Red'];
|
|
|
|
const sizeOptions = ['S', 'M', 'L', 'XL', 'XXL'];
|
|
|
|
|
|
|
|
// 获取产品详情
|
|
|
|
useEffect(() => {
|
|
|
|
fetchProductDetails();
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
// 获取产品详情的API调用
|
|
|
|
const fetchProductDetails = async () => {
|
|
|
|
if (!route.params?.offer_id) return;
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
|
|
const res = await productApi.getProductDetail(route.params.offer_id);
|
|
|
|
res.price = route.params.price;
|
|
|
|
setProduct(res);
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error fetching product details:', error);
|
|
|
|
} finally {
|
|
|
|
setLoading(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 返回上一页
|
|
|
|
const goBack = useCallback(() => {
|
|
|
|
navigation.goBack();
|
|
|
|
}, [navigation]);
|
|
|
|
|
|
|
|
// 处理相关商品点击
|
|
|
|
const handleRelatedProductPress = useCallback((product: any) => {
|
|
|
|
// 这里可以导航到对应的商品详情页
|
|
|
|
console.log('Navigate to related product:', product.id);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
// 处理添加到购物车
|
|
|
|
const handleAddToCart = useCallback(() => {
|
|
|
|
// 添加到购物车的逻辑
|
|
|
|
console.log('Add to cart with color:', selectedColor, 'and size:', selectedSize);
|
|
|
|
}, [selectedColor, selectedSize]);
|
|
|
|
|
|
|
|
// 处理立即购买
|
|
|
|
const handleBuyNow = useCallback(() => {
|
|
|
|
// 立即购买的逻辑
|
|
|
|
console.log('Buy now with color:', selectedColor, 'and size:', selectedSize);
|
|
|
|
}, [selectedColor, selectedSize]);
|
|
|
|
|
|
|
|
// 获取相关商品
|
|
|
|
const fetchRelatedProducts = useCallback(async (pageNumber = 1) => {
|
|
|
|
if (!hasMoreProducts && pageNumber > 1) return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
setLoadingMore(true);
|
|
|
|
|
|
|
|
// 模拟网络请求延迟
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
|
|
|
|
// 模拟新数据
|
|
|
|
const newProducts = Array(4).fill(0).map((_, index) => ({
|
|
|
|
id: pageNumber * 10 + index,
|
|
|
|
name: `Related Product ${pageNumber * 10 + index}`,
|
|
|
|
price: Number((Math.random() * 30 + 10).toFixed(2)),
|
|
|
|
image: 'https://via.placeholder.com/150'
|
|
|
|
}));
|
|
|
|
|
|
|
|
if (pageNumber === 1) {
|
|
|
|
setRelatedProducts(newProducts);
|
|
|
|
} else {
|
|
|
|
setRelatedProducts(prev => [...prev, ...newProducts]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 模拟数据加载完毕的情况
|
|
|
|
if (pageNumber >= 3) {
|
|
|
|
setHasMoreProducts(false);
|
|
|
|
} else {
|
|
|
|
setPage(pageNumber);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error fetching related products:', error);
|
|
|
|
} finally {
|
|
|
|
setLoadingMore(false);
|
|
|
|
}
|
|
|
|
}, [hasMoreProducts]);
|
|
|
|
|
|
|
|
// 初始加载相关商品
|
|
|
|
useEffect(() => {
|
|
|
|
fetchRelatedProducts(1);
|
|
|
|
}, [fetchRelatedProducts]);
|
|
|
|
|
|
|
|
// 处理滚动到底部加载更多
|
|
|
|
const handleLoadMore = useCallback(() => {
|
|
|
|
if (!loadingMore && hasMoreProducts) {
|
|
|
|
fetchRelatedProducts(page + 1);
|
|
|
|
}
|
|
|
|
}, [fetchRelatedProducts, loadingMore, hasMoreProducts, page]);
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
return (
|
|
|
|
<SafeAreaView style={styles.container}>
|
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
|
|
|
<View style={styles.header}>
|
|
|
|
<TouchableOpacity style={styles.backButton} onPress={goBack}>
|
|
|
|
<IconComponent name="arrow-back" size={24} color="#333" />
|
|
|
|
</TouchableOpacity>
|
|
|
|
<Text style={styles.headerTitle}>{t('productDetail')}</Text>
|
|
|
|
<View style={styles.headerRight}>
|
|
|
|
<IconComponent name="ellipsis-vertical" size={20} color="#333" />
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={styles.loadingContainer}>
|
|
|
|
<ActivityIndicator size="large" color="#ff6600" />
|
|
|
|
<Text style={styles.loadingText}>{t('loadingProductInfo')}</Text>
|
|
|
|
</View>
|
|
|
|
</SafeAreaView>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!product) {
|
|
|
|
return (
|
|
|
|
<SafeAreaView style={styles.container}>
|
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
|
|
|
<View style={styles.header}>
|
|
|
|
<TouchableOpacity style={styles.backButton} onPress={goBack}>
|
|
|
|
<IconComponent name="arrow-back" size={24} color="#333" />
|
|
|
|
</TouchableOpacity>
|
|
|
|
<Text style={styles.headerTitle}>{t('productDetail')}</Text>
|
|
|
|
<View style={styles.headerRight}>
|
|
|
|
<IconComponent name="ellipsis-vertical" size={20} color="#333" />
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={styles.errorContainer}>
|
|
|
|
<IconComponent name="alert-circle-outline" size={48} color="#ff6600" />
|
|
|
|
<Text style={styles.errorText}>{t('productNotAvailable')}</Text>
|
|
|
|
</View>
|
|
|
|
</SafeAreaView>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<SafeAreaView style={styles.container}>
|
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
|
|
|
|
|
|
|
{/* 头部导航 */}
|
|
|
|
<View style={styles.header}>
|
|
|
|
<TouchableOpacity style={styles.backButton} onPress={goBack}>
|
|
|
|
<IconComponent name="arrow-back" size={24} color="#333" />
|
|
|
|
</TouchableOpacity>
|
|
|
|
<Text style={styles.headerTitle} numberOfLines={1}>
|
|
|
|
{t('productDetail')}
|
|
|
|
</Text>
|
|
|
|
<View style={styles.headerRight}>
|
|
|
|
<TouchableOpacity style={styles.headerIconButton}>
|
|
|
|
<IconComponent name="ellipsis-vertical" size={20} color="#333" />
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<ScrollView
|
|
|
|
style={styles.scrollContainer}
|
|
|
|
showsVerticalScrollIndicator={false}
|
|
|
|
onScroll={({ nativeEvent }) => {
|
|
|
|
const { layoutMeasurement, contentOffset, contentSize } = nativeEvent;
|
|
|
|
const paddingToBottom = 20;
|
|
|
|
if (layoutMeasurement.height + contentOffset.y >=
|
|
|
|
contentSize.height - paddingToBottom) {
|
|
|
|
handleLoadMore();
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
scrollEventThrottle={400}
|
|
|
|
>
|
|
|
|
{/* 产品图片轮播 */}
|
|
|
|
<View style={styles.imageContainer}>
|
|
|
|
{product.product_image_urls && product.product_image_urls.length > 0 ? (
|
|
|
|
<>
|
|
|
|
<FlatList
|
|
|
|
data={product.product_image_urls}
|
|
|
|
horizontal
|
|
|
|
pagingEnabled
|
|
|
|
showsHorizontalScrollIndicator={false}
|
|
|
|
keyExtractor={(_, index) => `image-${index}`}
|
|
|
|
onScroll={(event) => {
|
|
|
|
const slideSize = event.nativeEvent.layoutMeasurement.width;
|
|
|
|
const index = Math.floor(event.nativeEvent.contentOffset.x / slideSize);
|
|
|
|
setActiveImageIndex(index);
|
|
|
|
}}
|
|
|
|
scrollEventThrottle={16}
|
|
|
|
renderItem={({ item }) => (
|
|
|
|
<View style={[styles.imageContainer, { width: screenWidth }]}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: item }}
|
|
|
|
style={{ width: '100%', height: '100%' }}
|
|
|
|
resizeMode="contain"
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
{/* 指示器 */}
|
|
|
|
{product.product_image_urls.length > 1 && (
|
|
|
|
<View style={styles.paginationContainer}>
|
|
|
|
{product.product_image_urls.map((_, index) => (
|
|
|
|
<View
|
|
|
|
key={`dot-${index}`}
|
|
|
|
style={[
|
|
|
|
styles.paginationDot,
|
|
|
|
index === activeImageIndex ? styles.paginationDotActive : {}
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<View style={styles.imagePlaceholder}>
|
|
|
|
<Text style={styles.imagePlaceholderText}>{t('productDetail')}</Text>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 产品基本信息 */}
|
|
|
|
<View style={styles.infoSection}>
|
|
|
|
<View style={styles.priceContainer}>
|
|
|
|
<Text style={styles.productPrice}>
|
|
|
|
${product.price?.toFixed(2) || '29.99'}
|
|
|
|
</Text>
|
|
|
|
<Text style={styles.originalPrice}>${(product.price * 1.4).toFixed(2) || '49.99'}</Text>
|
|
|
|
<View style={styles.discountBadge}>
|
|
|
|
<Text style={styles.discountText}>-40%</Text>
|
|
|
|
</View>
|
|
|
|
<View style={styles.vipBadge}>
|
|
|
|
<Text style={styles.vipText}>VIP</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<Text style={styles.productTitle}>{product.subject || 'Product Name'}</Text>
|
|
|
|
<Text style={styles.productSales}>{t('monthlySales')} {product.sold_out || 2458}</Text>
|
|
|
|
|
|
|
|
{/* 颜色选择 */}
|
|
|
|
<View style={styles.optionSection}>
|
|
|
|
<Text style={styles.optionTitle}>{t('color')}</Text>
|
|
|
|
<View style={styles.colorOptions}>
|
|
|
|
{colorOptions.map((color, index) => (
|
|
|
|
<ColorOption
|
|
|
|
key={`color-${index}`}
|
|
|
|
color={color}
|
|
|
|
selected={selectedColor === color}
|
|
|
|
onSelect={() => setSelectedColor(color)}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
<TouchableOpacity style={styles.moreColorsButton}>
|
|
|
|
<Text style={styles.moreColorsText}>+2</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 尺寸选择 */}
|
|
|
|
<View style={styles.optionSection}>
|
|
|
|
<Text style={styles.optionTitle}>{t('size')}</Text>
|
|
|
|
<View style={styles.sizeOptions}>
|
|
|
|
{sizeOptions.map((size, index) => (
|
|
|
|
<SizeOption
|
|
|
|
key={`size-${index}`}
|
|
|
|
size={size}
|
|
|
|
selected={selectedSize === size}
|
|
|
|
onSelect={() => setSelectedSize(size)}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 相关商品 */}
|
|
|
|
<View style={styles.relatedProductsSection}>
|
|
|
|
<View style={styles.sectionHeader}>
|
|
|
|
<Text style={styles.sectionTitle}>{t('moreFromStore')}</Text>
|
|
|
|
<TouchableOpacity style={styles.viewAllButton}>
|
|
|
|
<Text style={styles.viewAllText}>{t('viewAll')}</Text>
|
|
|
|
<IconComponent name="chevron-forward" size={16} color="#0066ff" />
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<View style={styles.relatedProductsContainer}>
|
|
|
|
{relatedProducts.map((product, index) => (
|
|
|
|
<RelatedProductItem
|
|
|
|
key={`related-${product.id}`}
|
|
|
|
product={product}
|
|
|
|
onPress={() => handleRelatedProductPress(product)}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{loadingMore && (
|
|
|
|
<View style={styles.loadMoreIndicator}>
|
|
|
|
<ActivityIndicator size="small" color="#ff6600" />
|
|
|
|
<Text style={styles.loadMoreText}>{t('loadingMoreProducts')}</Text>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{!hasMoreProducts && relatedProducts.length > 0 && (
|
|
|
|
<Text style={styles.noMoreProductsText}>{t('noMoreProducts')}</Text>
|
|
|
|
)}
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 商品详情 */}
|
|
|
|
<View style={styles.detailsSection}>
|
|
|
|
<Text style={styles.sectionTitle}>{t('productDetails')}</Text>
|
|
|
|
<View style={styles.productDetails}>
|
|
|
|
<View style={styles.productDetailItem}>
|
|
|
|
<View style={styles.detailPlaceholder}>
|
|
|
|
<Text style={styles.detailPlaceholderText}>{t('productDetails')} 1</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={styles.productDetailItem}>
|
|
|
|
<View style={styles.detailPlaceholder}>
|
|
|
|
<Text style={styles.detailPlaceholderText}>{t('productDetails')} 2</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={styles.productDetailItem}>
|
|
|
|
<View style={styles.detailPlaceholder}>
|
|
|
|
<Text style={styles.detailPlaceholderText}>{t('productDetails')} 3</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 底部空间,确保内容不被底部操作栏遮挡 */}
|
|
|
|
<View style={styles.bottomSpace} />
|
|
|
|
</ScrollView>
|
|
|
|
|
|
|
|
{/* 底部操作栏 */}
|
|
|
|
<View style={styles.bottomBar}>
|
|
|
|
<TouchableOpacity style={styles.chatButton}>
|
|
|
|
<IconComponent name="chatbubble-outline" size={22} color="#666" />
|
|
|
|
<Text style={styles.chatButtonText}>{t('customerService')}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.addToCartButton} onPress={handleAddToCart}>
|
|
|
|
<IconComponent name="cart-outline" size={20} color="#fff" />
|
|
|
|
<Text style={styles.addToCartText}>{t('addToCart')}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.buyNowButton} onPress={handleBuyNow}>
|
|
|
|
<Text style={styles.buyNowText}>{t('buyNow')}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</SafeAreaView>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
backgroundColor: '#f5f5f5',
|
|
|
|
},
|
|
|
|
header: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
paddingVertical: 10,
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
borderBottomColor: '#f0f0f0',
|
|
|
|
},
|
|
|
|
backButton: {
|
|
|
|
padding: 5,
|
|
|
|
},
|
|
|
|
headerTitle: {
|
|
|
|
flex: 1,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#333',
|
|
|
|
textAlign: 'center',
|
|
|
|
marginHorizontal: 10,
|
|
|
|
},
|
|
|
|
headerRight: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
headerIconButton: {
|
|
|
|
padding: 5,
|
|
|
|
},
|
|
|
|
scrollContainer: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
imageContainer: {
|
|
|
|
height: 300,
|
|
|
|
backgroundColor: '#f9f9f9',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
position: 'relative',
|
|
|
|
},
|
|
|
|
imagePlaceholder: {
|
|
|
|
width: '100%',
|
|
|
|
height: '100%',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
},
|
|
|
|
imagePlaceholderText: {
|
|
|
|
fontSize: 16,
|
|
|
|
color: '#999',
|
|
|
|
},
|
|
|
|
paginationContainer: {
|
|
|
|
position: 'absolute',
|
|
|
|
bottom: 15,
|
|
|
|
flexDirection: 'row',
|
|
|
|
width: '100%',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
paginationDot: {
|
|
|
|
width: 8,
|
|
|
|
height: 8,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: 'rgba(255, 255, 255, 0.4)',
|
|
|
|
marginHorizontal: 4,
|
|
|
|
},
|
|
|
|
paginationDotActive: {
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
width: 10,
|
|
|
|
height: 10,
|
|
|
|
borderRadius: 5,
|
|
|
|
},
|
|
|
|
infoSection: {
|
|
|
|
padding: 15,
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
priceContainer: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
alignItems: 'center',
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
productPrice: {
|
|
|
|
fontSize: 24,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#ff6600',
|
|
|
|
},
|
|
|
|
originalPrice: {
|
|
|
|
fontSize: 16,
|
|
|
|
color: '#999',
|
|
|
|
textDecorationLine: 'line-through',
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
discountBadge: {
|
|
|
|
backgroundColor: '#ff6600',
|
|
|
|
paddingHorizontal: 6,
|
|
|
|
paddingVertical: 2,
|
|
|
|
borderRadius: 3,
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
discountText: {
|
|
|
|
color: '#fff',
|
|
|
|
fontSize: 12,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
},
|
|
|
|
vipBadge: {
|
|
|
|
backgroundColor: '#f1c40f',
|
|
|
|
paddingHorizontal: 6,
|
|
|
|
paddingVertical: 2,
|
|
|
|
borderRadius: 3,
|
|
|
|
marginLeft: 6,
|
|
|
|
},
|
|
|
|
vipText: {
|
|
|
|
color: '#fff',
|
|
|
|
fontSize: 12,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
},
|
|
|
|
productTitle: {
|
|
|
|
fontSize: 16,
|
|
|
|
color: '#333',
|
|
|
|
marginBottom: 8,
|
|
|
|
},
|
|
|
|
productSales: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#999',
|
|
|
|
marginBottom: 15,
|
|
|
|
},
|
|
|
|
optionSection: {
|
|
|
|
marginTop: 15,
|
|
|
|
},
|
|
|
|
optionTitle: {
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#333',
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
colorOptions: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
},
|
|
|
|
colorOption: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
alignItems: 'center',
|
|
|
|
paddingHorizontal: 10,
|
|
|
|
paddingVertical: 6,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginRight: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: '#e0e0e0',
|
|
|
|
},
|
|
|
|
colorOptionSelected: {
|
|
|
|
borderColor: '#ff6600',
|
|
|
|
backgroundColor: '#fff8f5',
|
|
|
|
},
|
|
|
|
colorCircle: {
|
|
|
|
width: 16,
|
|
|
|
height: 16,
|
|
|
|
borderRadius: 8,
|
|
|
|
marginRight: 6,
|
|
|
|
},
|
|
|
|
colorName: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#333',
|
|
|
|
},
|
|
|
|
moreColorsButton: {
|
|
|
|
width: 35,
|
|
|
|
height: 35,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: '#f5f5f5',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
marginRight: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
moreColorsText: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#333',
|
|
|
|
},
|
|
|
|
sizeOptions: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
},
|
|
|
|
sizeOption: {
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
paddingVertical: 6,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginRight: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: '#e0e0e0',
|
|
|
|
},
|
|
|
|
sizeOptionSelected: {
|
|
|
|
borderColor: '#ff6600',
|
|
|
|
backgroundColor: '#fff8f5',
|
|
|
|
},
|
|
|
|
sizeText: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#333',
|
|
|
|
},
|
|
|
|
sizeTextSelected: {
|
|
|
|
color: '#ff6600',
|
|
|
|
fontWeight: 'bold',
|
|
|
|
},
|
|
|
|
relatedProductsSection: {
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
padding: 15,
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
sectionHeader: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
alignItems: 'center',
|
|
|
|
marginBottom: 15,
|
|
|
|
},
|
|
|
|
sectionTitle: {
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#333',
|
|
|
|
},
|
|
|
|
viewAllButton: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
viewAllText: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#0066ff',
|
|
|
|
},
|
|
|
|
relatedProductsContainer: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
relatedProductItem: {
|
|
|
|
width: screenWidth / 2 - 25,
|
|
|
|
marginBottom: 15,
|
|
|
|
},
|
|
|
|
relatedProductImage: {
|
|
|
|
width: '100%',
|
|
|
|
height: 120,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginBottom: 5,
|
|
|
|
},
|
|
|
|
relatedProductPrice: {
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#ff6600',
|
|
|
|
},
|
|
|
|
loadMoreIndicator: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
loadMoreText: {
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#666',
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
noMoreProductsText: {
|
|
|
|
textAlign: 'center',
|
|
|
|
fontSize: 12,
|
|
|
|
color: '#999',
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
detailsSection: {
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
padding: 15,
|
|
|
|
},
|
|
|
|
productDetails: {
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
productDetailItem: {
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
productDetailImage: {
|
|
|
|
width: '100%',
|
|
|
|
height: 200,
|
|
|
|
borderRadius: 4,
|
|
|
|
},
|
|
|
|
detailPlaceholder: {
|
|
|
|
width: '100%',
|
|
|
|
height: 200,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: '#f0f0f0',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
detailPlaceholderText: {
|
|
|
|
fontSize: 16,
|
|
|
|
color: '#999',
|
|
|
|
},
|
|
|
|
bottomSpace: {
|
|
|
|
height: 60,
|
|
|
|
},
|
|
|
|
bottomBar: {
|
|
|
|
position: 'absolute',
|
|
|
|
bottom: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
flexDirection: 'row',
|
|
|
|
height: 60,
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
borderTopWidth: 1,
|
|
|
|
borderTopColor: '#f0f0f0',
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
chatButton: {
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
marginRight: 15,
|
|
|
|
},
|
|
|
|
chatButtonText: {
|
|
|
|
fontSize: 10,
|
|
|
|
color: '#666',
|
|
|
|
marginTop: 2,
|
|
|
|
},
|
|
|
|
addToCartButton: {
|
|
|
|
flex: 1,
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
backgroundColor: '#ff9500',
|
|
|
|
borderRadius: 25,
|
|
|
|
marginRight: 10,
|
|
|
|
},
|
|
|
|
addToCartText: {
|
|
|
|
color: '#fff',
|
|
|
|
fontWeight: 'bold',
|
|
|
|
marginLeft: 5,
|
|
|
|
},
|
|
|
|
buyNowButton: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
backgroundColor: '#ff6600',
|
|
|
|
borderRadius: 25,
|
|
|
|
},
|
|
|
|
buyNowText: {
|
|
|
|
color: '#fff',
|
|
|
|
fontWeight: 'bold',
|
|
|
|
},
|
|
|
|
loadingContainer: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
},
|
|
|
|
loadingText: {
|
|
|
|
fontSize: 16,
|
|
|
|
color: '#666',
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
errorContainer: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: 'center',
|
|
|
|
alignItems: 'center',
|
|
|
|
padding: 20,
|
|
|
|
},
|
|
|
|
errorText: {
|
|
|
|
fontSize: 18,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#333',
|
|
|
|
marginTop: 15,
|
|
|
|
marginBottom: 20,
|
|
|
|
},
|
|
|
|
});
|