|
|
|
import React, { useEffect, useState, useCallback, useRef } from "react";
|
|
|
|
import widthUtils from "../utils/widthUtils";
|
|
|
|
import fontSize from "../utils/fontsizeUtils";
|
|
|
|
import {
|
|
|
|
View,
|
|
|
|
Text,
|
|
|
|
StyleSheet,
|
|
|
|
ScrollView,
|
|
|
|
Image,
|
|
|
|
TouchableOpacity,
|
|
|
|
SafeAreaView,
|
|
|
|
StatusBar,
|
|
|
|
ActivityIndicator,
|
|
|
|
Dimensions,
|
|
|
|
TextInput,
|
|
|
|
FlatList,
|
|
|
|
Animated,
|
|
|
|
BackHandler,
|
|
|
|
} 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,
|
|
|
|
Similars,
|
|
|
|
Sku,
|
|
|
|
ColorOptions,
|
|
|
|
} from "../services/api/productApi";
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import isSmallScreen from "../utils/isSmallScreen";
|
|
|
|
import MoreIcon from "../components/SendIcon";
|
|
|
|
import BackIcon from "../components/BackIcon";
|
|
|
|
import CardIcon from "../components/ShoppingCartIcon";
|
|
|
|
import CameraIcon from "../components/CameraIcon";
|
|
|
|
import HeartIcon from "../components/HeartIcon";
|
|
|
|
import HeartIconRed from "../components/HeartIconRed";
|
|
|
|
import WhiteCircleIcon from "../components/WhiteCircleIconIcon";
|
|
|
|
import ShoppingCartIcon from "../components/ShoppingCartIcon";
|
|
|
|
import XIconTop from "../components/XIconTop";
|
|
|
|
import XIconBottom from "../components/XIconBottom";
|
|
|
|
import RenderHtml from "react-native-render-html";
|
|
|
|
import { useWindowDimensions } from "react-native";
|
|
|
|
import { cartApi } from "../services/api/cart";
|
|
|
|
|
|
|
|
// 获取屏幕宽度
|
|
|
|
const { width: screenWidth, height: screenHeight } = 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}
|
|
|
|
>
|
|
|
|
<Image source={{ uri: color }} style={styles.colorOptionImage} />
|
|
|
|
</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.product_image_urls[0] }}
|
|
|
|
style={styles.relatedProductImage}
|
|
|
|
/>
|
|
|
|
<Text style={styles.relatedProductPrice}>$123</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 [heartStatus, setHeartStatus] = useState(true);
|
|
|
|
const [showAllSizes, setShowAllSizes] = useState(false);
|
|
|
|
const [showAllColors, setShowAllColors] = useState(false);
|
|
|
|
const [showBottomSheet, setShowBottomSheet] = useState(false);
|
|
|
|
const bottomSheetAnim = useRef(new Animated.Value(0)).current;
|
|
|
|
// 添加选择状态
|
|
|
|
const [selectedColor, setSelectedColor] = useState(0);
|
|
|
|
const [selectedSizes, setSelectedSizes] = useState<{ [key: string]: number }>(
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
const [page, setPage] = useState(1);
|
|
|
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
|
|
const [hasMoreProducts, setHasMoreProducts] = useState(true);
|
|
|
|
const [similarProducts, setSimilarProducts] = useState<Similars>([]);
|
|
|
|
const [colorOptions, setColorOptions] = useState<ColorOptions>();
|
|
|
|
const [sizeOptions, setSizeOptions] = useState<string[]>([]);
|
|
|
|
const [selectedColorIndex, setSelectedColorIndex] = useState<number>(0);
|
|
|
|
const [selectedSizeIndex, setSelectedSizeIndex] = useState<number>(0);
|
|
|
|
|
|
|
|
const [showImagePreview, setShowImagePreview] = useState(false);
|
|
|
|
const [previewImageIndex, setPreviewImageIndex] = useState(0);
|
|
|
|
|
|
|
|
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
|
|
|
|
|
|
|
const [selectedSku, setSelectedSku] = useState<any>();
|
|
|
|
const [attributes, setAttributes] = useState<any[]>([]);
|
|
|
|
const [colorSelected, setColorSelected] = useState<any>();
|
|
|
|
const [sizeSelected, setSizeSelected] = useState<any>();
|
|
|
|
const [priceSelectedSku, setPriceSelectedSku] = useState<any>();
|
|
|
|
const [skuSize, setSkuSize] = useState<any>();
|
|
|
|
const [imageUrls, setImageUrls] = useState<any[]>([]);
|
|
|
|
const { width } = useWindowDimensions(); // 获取当前屏幕的宽度
|
|
|
|
const [imageHeights, setImageHeights] = useState<{ [key: string]: number }>({});
|
|
|
|
|
|
|
|
// 获取产品详情`
|
|
|
|
useEffect(() => {
|
|
|
|
fetchProductDetails();
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (showBottomSheet) {
|
|
|
|
Animated.spring(bottomSheetAnim, {
|
|
|
|
toValue: 1,
|
|
|
|
useNativeDriver: true,
|
|
|
|
bounciness: 0,
|
|
|
|
}).start();
|
|
|
|
} else {
|
|
|
|
bottomSheetAnim.setValue(0);
|
|
|
|
}
|
|
|
|
}, [showBottomSheet]);
|
|
|
|
|
|
|
|
// 获取产品详情的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);
|
|
|
|
productApi.getSimilarProducts(route.params.offer_id).then((res) => {
|
|
|
|
setSimilarProducts(res);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (res.skus === null) return;
|
|
|
|
let result = {} as any;
|
|
|
|
// 遍历数据
|
|
|
|
res.skus.forEach((item) => {
|
|
|
|
item.attributes.forEach((attribute) => {
|
|
|
|
const { attribute_name, value } = attribute;
|
|
|
|
// 如果结果对象中没有对应的属性名,则创建一个空数组
|
|
|
|
if (!result[attribute_name]) {
|
|
|
|
result[attribute_name] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// 如果当前属性的值(value)已经存在于该组内,跳过
|
|
|
|
if (
|
|
|
|
!result[attribute_name].some(
|
|
|
|
(existingAttribute: any) => existingAttribute.value === value
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
result[attribute_name].push(attribute);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const list = [];
|
|
|
|
|
|
|
|
// Iterate over each attribute and transform the data
|
|
|
|
for (const [attributeName, attributes] of Object.entries(result)) {
|
|
|
|
const withImage: any[] = [];
|
|
|
|
const withoutImage: any[] = [];
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
attributes.forEach((attribute) => {
|
|
|
|
// Check if sku_image_url is not null or undefined
|
|
|
|
const hasImage =
|
|
|
|
attribute.sku_image_url !== null &&
|
|
|
|
attribute.sku_image_url !== undefined;
|
|
|
|
|
|
|
|
// Push the attribute to the appropriate array
|
|
|
|
if (hasImage) {
|
|
|
|
withImage.push(attribute);
|
|
|
|
} else {
|
|
|
|
withoutImage.push(attribute);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Add has_image to the list item
|
|
|
|
list.push({
|
|
|
|
attribute_name: attributeName,
|
|
|
|
has_image: withImage.length > 0, // If there are any items with images, set has_image to true
|
|
|
|
attributes: [...withImage, ...withoutImage],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (list.length == 0) {
|
|
|
|
setSkuSize(0);
|
|
|
|
} else if (list.length == 1) {
|
|
|
|
setSkuSize(1);
|
|
|
|
} else if (list.length == 2) {
|
|
|
|
setSkuSize(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
setAttributes(list);
|
|
|
|
list.forEach((item) => {
|
|
|
|
if (item.has_image) {
|
|
|
|
setColorSelected(item.attributes[0].value);
|
|
|
|
setSelectedColorIndex(0 + item.attributes[0].value);
|
|
|
|
} else {
|
|
|
|
setSizeSelected(item.attributes[0].value);
|
|
|
|
setSelectedSizeIndex(0 + item.attributes[0].value);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
const imageUrls = [];
|
|
|
|
const regex = /<img[^>]+src="([^"]+)"/g;
|
|
|
|
let match;
|
|
|
|
|
|
|
|
while ((match = regex.exec(res.description)) !== null) {
|
|
|
|
imageUrls.push(match[1]); // match[1] 是 src 属性的值
|
|
|
|
}
|
|
|
|
|
|
|
|
setImageUrls(imageUrls);
|
|
|
|
|
|
|
|
const priceSelectedSku = res.skus.find(item => item.consign_price === route.params.price)
|
|
|
|
setPriceSelectedSku(priceSelectedSku)
|
|
|
|
|
|
|
|
console.log(priceSelectedSku);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Error fetching product details:", error);
|
|
|
|
} finally {
|
|
|
|
setLoading(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 返回上一页
|
|
|
|
const goBack = useCallback(() => {
|
|
|
|
navigation.goBack();
|
|
|
|
}, [navigation]);
|
|
|
|
|
|
|
|
// 处理相关商品点击
|
|
|
|
const handleRelatedProductPress = useCallback((product: any) => {}, []);
|
|
|
|
|
|
|
|
// 获取相关商品
|
|
|
|
const fetchRelatedProducts = useCallback(
|
|
|
|
async (pageNumber = 1) => {
|
|
|
|
if (!hasMoreProducts && pageNumber > 1) return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
setLoadingMore(true);
|
|
|
|
|
|
|
|
// 模拟数据加载完毕的情况
|
|
|
|
if (pageNumber >= 3) {
|
|
|
|
setHasMoreProducts(false);
|
|
|
|
} else {
|
|
|
|
setPage(pageNumber);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error("Error fetching related products:", error);
|
|
|
|
} finally {
|
|
|
|
setLoadingMore(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[hasMoreProducts]
|
|
|
|
);
|
|
|
|
|
|
|
|
// 收藏
|
|
|
|
const changeHeartStatus = () => {
|
|
|
|
setHeartStatus(!heartStatus);
|
|
|
|
};
|
|
|
|
|
|
|
|
// 初始加载相关商品
|
|
|
|
useEffect(() => {
|
|
|
|
fetchRelatedProducts(1);
|
|
|
|
}, [fetchRelatedProducts]);
|
|
|
|
|
|
|
|
// 处理返回键
|
|
|
|
useEffect(() => {
|
|
|
|
const backAction = () => {
|
|
|
|
if (showImagePreview) {
|
|
|
|
setShowImagePreview(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const backHandler = BackHandler.addEventListener(
|
|
|
|
"hardwareBackPress",
|
|
|
|
backAction
|
|
|
|
);
|
|
|
|
|
|
|
|
return () => backHandler.remove();
|
|
|
|
}, [showImagePreview]);
|
|
|
|
// 选择颜色
|
|
|
|
const changeColor = (attribute: any, index: number) => {
|
|
|
|
setColorSelected(attribute.value);
|
|
|
|
setSelectedColorIndex(index + attribute.value);
|
|
|
|
if (skuSize === 0) return;
|
|
|
|
if (!sizeSelected) {
|
|
|
|
product?.skus.forEach((item: Sku) => {
|
|
|
|
item.attributes.forEach((item1: any) => {
|
|
|
|
if (item1.value === attribute.value) {
|
|
|
|
setPriceSelectedSku(item);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const targetValues = [attribute.value, sizeSelected];
|
|
|
|
const filteredData: Sku[] = product?.skus.filter((item) => {
|
|
|
|
const attrValues = item.attributes.map((attr) => attr.value);
|
|
|
|
return targetValues.every((target) => attrValues.includes(target));
|
|
|
|
}) as Sku[];
|
|
|
|
setPriceSelectedSku(filteredData[0]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 选择尺码
|
|
|
|
const changeSize = (size: any, index: number) => {
|
|
|
|
setSizeSelected(size.value);
|
|
|
|
setSelectedSizeIndex(index + size.value);
|
|
|
|
if (skuSize === 0) return;
|
|
|
|
if (!colorSelected) {
|
|
|
|
product?.skus.forEach((item: Sku) => {
|
|
|
|
item.attributes.forEach((item1: any) => {
|
|
|
|
if (item1.value === size.value) {
|
|
|
|
setPriceSelectedSku(item);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const targetValues = [size.value, colorSelected];
|
|
|
|
const filteredData: Sku[] = product?.skus.filter((item) => {
|
|
|
|
const attrValues = item.attributes.map((attr) => attr.value);
|
|
|
|
return targetValues.every((target) => attrValues.includes(target));
|
|
|
|
}) as Sku[];
|
|
|
|
setPriceSelectedSku(filteredData[0]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 添加到购物车
|
|
|
|
const handleAddToCart = async () => {
|
|
|
|
console.log(product?.offer_id);
|
|
|
|
console.log(priceSelectedSku);
|
|
|
|
const data = {
|
|
|
|
offer_id: product?.offer_id as number,
|
|
|
|
sku_id: priceSelectedSku?.sku_id as number,
|
|
|
|
quantity: 1,
|
|
|
|
}
|
|
|
|
const res = await cartApi(data)
|
|
|
|
console.log(res);
|
|
|
|
|
|
|
|
// const res = await cartApi.addToCart(data);
|
|
|
|
// console.log(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 添加处理图片加载的函数
|
|
|
|
const handleImageLoad = (src: string, event: any) => {
|
|
|
|
const { width: imageWidth, height: imageHeight } = event.nativeEvent.source;
|
|
|
|
const aspectRatio = imageHeight / imageWidth;
|
|
|
|
const calculatedHeight = width * aspectRatio;
|
|
|
|
setImageHeights(prev => ({
|
|
|
|
...prev,
|
|
|
|
[src]: calculatedHeight
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
return (
|
|
|
|
<SafeAreaView style={styles.container}>
|
|
|
|
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
|
|
|
|
<View style={styles.header}>
|
|
|
|
<TouchableOpacity 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 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}>
|
|
|
|
<BackIcon size={20} />
|
|
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.searchContainer}>
|
|
|
|
<TextInput
|
|
|
|
style={styles.searchInput}
|
|
|
|
placeholder="Recherche"
|
|
|
|
placeholderTextColor="#666"
|
|
|
|
/>
|
|
|
|
<TouchableOpacity style={styles.searchIconButton}>
|
|
|
|
<CameraIcon size={22} />
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
<View style={styles.headerRight}>
|
|
|
|
<TouchableOpacity style={styles.iconButton}>
|
|
|
|
<CardIcon size={24} />
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.iconButton}>
|
|
|
|
<MoreIcon size={24} />
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<ScrollView
|
|
|
|
style={styles.scrollContainer}
|
|
|
|
contentContainerStyle={styles.scrollContentContainer}
|
|
|
|
>
|
|
|
|
{/* 产品图片 */}
|
|
|
|
<View style={styles.imageContainer}>
|
|
|
|
<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 }]}>
|
|
|
|
<View style={{ position: "relative" }}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: item }}
|
|
|
|
style={{ width: "100%", height: "100%" }}
|
|
|
|
resizeMode="cover"
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
<View style={styles.pageIndicator}>
|
|
|
|
<Text style={styles.pageIndicatorText}>
|
|
|
|
{`${activeImageIndex + 1}/${
|
|
|
|
product.product_image_urls?.length || 1
|
|
|
|
}`}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.heartIconButton}
|
|
|
|
onPress={changeHeartStatus}
|
|
|
|
>
|
|
|
|
{heartStatus ? <HeartIconRed size={18} /> : <HeartIcon size={18} />}
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 价格信息 */}
|
|
|
|
<View style={styles.infoSection}>
|
|
|
|
<Text style={styles.productTitle}>
|
|
|
|
{product.subject ||
|
|
|
|
"Lanterne de nuit créative 3D cadeau d'anniversaire méduse lampe de table led..."}
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
<View style={styles.priceContainer}>
|
|
|
|
<View style={styles.mainPriceContainer}>
|
|
|
|
<Text style={styles.productPrice}>
|
|
|
|
{priceSelectedSku?.consign_price}
|
|
|
|
</Text>
|
|
|
|
<Text style={styles.currencySymbol}>FCFA</Text>
|
|
|
|
</View>
|
|
|
|
<Text style={styles.originalPrice}>
|
|
|
|
{(priceSelectedSku?.offer_price * 1.4).toFixed(2)} FCFA
|
|
|
|
</Text>
|
|
|
|
<View style={styles.discountBadge}>
|
|
|
|
<Image
|
|
|
|
source={require("../../assets/favorable.png")}
|
|
|
|
style={{ width: 28, height: 28 }}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
<View style={styles.vipBadge}>
|
|
|
|
<Image
|
|
|
|
source={require("../../assets/vip1.png")}
|
|
|
|
style={{ width: 63, height: 21 }}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={styles.salesContainer}>
|
|
|
|
<Text style={styles.salesText}>
|
|
|
|
{product.sold_out} {product.sold_out === 0 ? "" : "+"}{" "}
|
|
|
|
{t("monthlySales")}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 颜色和尺码选择 */}
|
|
|
|
{attributes.map((item, index) => (
|
|
|
|
<View
|
|
|
|
style={item.has_image ? styles.optionSection : styles.optionSection}
|
|
|
|
key={index}
|
|
|
|
>
|
|
|
|
<Text style={styles.optionTitle}>
|
|
|
|
{t(item.attribute_name)}{" "}
|
|
|
|
{item.has_image ? `: ${colorSelected}` : ""}
|
|
|
|
</Text>
|
|
|
|
{item.has_image ? (
|
|
|
|
<View style={styles.colorOptions}>
|
|
|
|
{item.attributes.map(
|
|
|
|
(attribute: any, index: number, array: any) => {
|
|
|
|
if (showAllColors || index < 6) {
|
|
|
|
return (
|
|
|
|
<ColorOption
|
|
|
|
key={`color-${index}`}
|
|
|
|
color={attribute.sku_image_url}
|
|
|
|
selected={
|
|
|
|
index + attribute.value === selectedColorIndex
|
|
|
|
}
|
|
|
|
onSelect={() => {
|
|
|
|
changeColor(attribute, index);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
} else if (index === 6) {
|
|
|
|
const remainingCount = array.length - 6;
|
|
|
|
return (
|
|
|
|
<TouchableOpacity
|
|
|
|
key="more-colors"
|
|
|
|
style={styles.moreColorsButton}
|
|
|
|
onPress={() => setShowAllColors(true)}
|
|
|
|
>
|
|
|
|
<Text style={styles.moreColorsText}>
|
|
|
|
+{remainingCount}
|
|
|
|
</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
)}
|
|
|
|
</View>
|
|
|
|
) : (
|
|
|
|
<View style={styles.sizeOptions}>
|
|
|
|
{item.attributes.map((size: any, index: number, array: any) => {
|
|
|
|
if (showAllSizes || index < 6) {
|
|
|
|
return (
|
|
|
|
<SizeOption
|
|
|
|
key={`size-${index}`}
|
|
|
|
size={size.value}
|
|
|
|
selected={index + size.value === selectedSizeIndex}
|
|
|
|
onSelect={() => {
|
|
|
|
changeSize(size, index);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
} else if (index === 6) {
|
|
|
|
const remainingCount = array.length - 6;
|
|
|
|
return (
|
|
|
|
<SizeOption
|
|
|
|
key="more-sizes"
|
|
|
|
size={`+${remainingCount}`}
|
|
|
|
selected={false}
|
|
|
|
onSelect={() => setShowAllSizes(true)}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
})}
|
|
|
|
</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}>View All</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
<FlatList
|
|
|
|
horizontal
|
|
|
|
showsHorizontalScrollIndicator={false}
|
|
|
|
data={similarProducts}
|
|
|
|
keyExtractor={(item) => `related-${item.offer_id}`}
|
|
|
|
renderItem={({ item }) => (
|
|
|
|
<RelatedProductItem
|
|
|
|
product={item}
|
|
|
|
onPress={() => handleRelatedProductPress(item)}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
contentContainerStyle={styles.relatedProductsContainer}
|
|
|
|
style={styles.relatedProductsList}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 商品详情 */}
|
|
|
|
<View style={{ width: '100%' }}>
|
|
|
|
{imageUrls.map((src, index) => (
|
|
|
|
<View key={index} style={{ width: '100%' }}>
|
|
|
|
<Image
|
|
|
|
style={{
|
|
|
|
width: '100%',
|
|
|
|
height: imageHeights[src] || 200,
|
|
|
|
backgroundColor: '#f5f5f5'
|
|
|
|
}}
|
|
|
|
source={{ uri: src }}
|
|
|
|
resizeMode="contain"
|
|
|
|
onLoad={(event) => handleImageLoad(src, event)}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
))}
|
|
|
|
</View>
|
|
|
|
</ScrollView>
|
|
|
|
|
|
|
|
{/* 底部按钮 */}
|
|
|
|
<View style={styles.fixedBottomBar}>
|
|
|
|
<TouchableOpacity style={styles.chatNowButton}>
|
|
|
|
<WhiteCircleIcon color="#fff" size={isSmallScreen ? 18 : 20} />
|
|
|
|
<Text style={styles.chatNowText}>{t("chatNow")}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.addToCartButton}
|
|
|
|
// onPress={() => setShowBottomSheet(true)}
|
|
|
|
onPress={handleAddToCart}
|
|
|
|
>
|
|
|
|
<ShoppingCartIcon color="#fff" size={isSmallScreen ? 18 : 20} />
|
|
|
|
<Text style={styles.addToCartText}>{t("addToCart")}</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 底部弹出模态框 */}
|
|
|
|
{showBottomSheet && (
|
|
|
|
<View style={styles.bottomSheetOverlay}>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.overlayTouchable}
|
|
|
|
activeOpacity={1}
|
|
|
|
onPress={() => setShowBottomSheet(false)}
|
|
|
|
/>
|
|
|
|
<Animated.View
|
|
|
|
style={[
|
|
|
|
styles.bottomSheet,
|
|
|
|
{
|
|
|
|
transform: [
|
|
|
|
{
|
|
|
|
translateY: bottomSheetAnim.interpolate({
|
|
|
|
inputRange: [0, 1],
|
|
|
|
outputRange: [500, 0],
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
>
|
|
|
|
{/* 顶部价格信息 */}
|
|
|
|
<View style={styles.priceHeader}>
|
|
|
|
<View style={styles.mainPrice}>
|
|
|
|
<View style={{ position: "relative" }}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: product.product_image_urls[0] }}
|
|
|
|
style={styles.smallProductImage}
|
|
|
|
/>
|
|
|
|
<View
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
top: 2,
|
|
|
|
right: 2,
|
|
|
|
width: 22,
|
|
|
|
height: 22,
|
|
|
|
backgroundColor: "#70615a",
|
|
|
|
opacity: 0.7,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<View
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
top: 2,
|
|
|
|
right: 2,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
width: 12,
|
|
|
|
height: 12,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<XIconTop size={12} />
|
|
|
|
</View>
|
|
|
|
<View
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
bottom: 2,
|
|
|
|
left: 2,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
width: 12,
|
|
|
|
height: 12,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<XIconBottom size={12} />
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
<View style={{ paddingLeft: 10, flex: 1 }}>
|
|
|
|
<View style={{ flexDirection: "row" }}>
|
|
|
|
<Text style={styles.priceText}>3072</Text>
|
|
|
|
<Text style={styles.currency}>FCFA</Text>
|
|
|
|
</View>
|
|
|
|
<View style={styles.shopInfo}>
|
|
|
|
<View style={{ flex: 1, alignItems: "flex-start" }}>
|
|
|
|
<View style={styles.priceRow}>
|
|
|
|
<Text style={styles.shopName}>3072</Text>
|
|
|
|
<Text style={styles.currencySamll}>FAFC</Text>
|
|
|
|
</View>
|
|
|
|
<Text>123</Text>
|
|
|
|
</View>
|
|
|
|
<View style={{ flex: 1, alignItems: "flex-start" }}>
|
|
|
|
<View style={styles.priceRow}>
|
|
|
|
<Text style={styles.shopName}>3072</Text>
|
|
|
|
<Text style={styles.currencySamll}>FAFC</Text>
|
|
|
|
</View>
|
|
|
|
<Text>123</Text>
|
|
|
|
</View>
|
|
|
|
<View style={{ flex: 1, alignItems: "flex-start" }}>
|
|
|
|
<View style={styles.priceRow}>
|
|
|
|
<Text style={styles.shopName}>3072</Text>
|
|
|
|
<Text style={styles.currencySamll}>FAFC</Text>
|
|
|
|
</View>
|
|
|
|
<Text>123</Text>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.closeButton}
|
|
|
|
onPress={() => setShowBottomSheet(false)}
|
|
|
|
>
|
|
|
|
<Text style={styles.closeButtonText}>×</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<ScrollView
|
|
|
|
style={styles.bottomSheetContent}
|
|
|
|
showsVerticalScrollIndicator={false}
|
|
|
|
>
|
|
|
|
{/* 颜色选择 */}
|
|
|
|
<View style={styles.optionSection}>
|
|
|
|
<Text style={styles.optionTitle}>
|
|
|
|
Couleur :{" "}
|
|
|
|
{["Noir", "Brun", "Beige", "Couleur", "Bleu"][selectedColor]}
|
|
|
|
</Text>
|
|
|
|
<View style={styles.colorGrid}>
|
|
|
|
{["Noir", "Brun", "Beige", "Couleur", "Bleu"].map(
|
|
|
|
(colorName, index) => {
|
|
|
|
const imageUrl =
|
|
|
|
product?.product_image_urls &&
|
|
|
|
index < product.product_image_urls.length
|
|
|
|
? product.product_image_urls[index]
|
|
|
|
: "";
|
|
|
|
return (
|
|
|
|
<TouchableOpacity
|
|
|
|
key={`color-${index}`}
|
|
|
|
style={[
|
|
|
|
styles.colorOptionBase,
|
|
|
|
index === selectedColor &&
|
|
|
|
styles.colorOptionSelectedState,
|
|
|
|
]}
|
|
|
|
onPress={() => {
|
|
|
|
setSelectedColor(index);
|
|
|
|
setSelectedSizes((prev) => {
|
|
|
|
const newSizes = { ...prev };
|
|
|
|
if (index !== selectedColor) {
|
|
|
|
Object.keys(newSizes).forEach((size) => {
|
|
|
|
newSizes[size] = 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return newSizes;
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<View style={{ position: "relative" }}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: imageUrl }}
|
|
|
|
style={styles.colorImage}
|
|
|
|
/>
|
|
|
|
<TouchableOpacity
|
|
|
|
onPress={() => {
|
|
|
|
setShowImagePreview(true);
|
|
|
|
setPreviewImageIndex(index);
|
|
|
|
}}
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
top: 2,
|
|
|
|
right: 2,
|
|
|
|
width: 22,
|
|
|
|
height: 22,
|
|
|
|
backgroundColor: "#70615a",
|
|
|
|
opacity: 0.7,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<View
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
top: 2,
|
|
|
|
right: 2,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
width: 12,
|
|
|
|
height: 12,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<XIconTop size={12} />
|
|
|
|
</View>
|
|
|
|
<View
|
|
|
|
style={{
|
|
|
|
position: "absolute",
|
|
|
|
bottom: 2,
|
|
|
|
left: 2,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
width: 12,
|
|
|
|
height: 12,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<XIconBottom size={12} />
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
<Text
|
|
|
|
style={[
|
|
|
|
styles.colorName,
|
|
|
|
index === selectedColor && { color: "#ec5e2a" },
|
|
|
|
]}
|
|
|
|
>
|
|
|
|
{colorName}
|
|
|
|
</Text>
|
|
|
|
{Object.values(selectedSizes).reduce(
|
|
|
|
(sum, current) => sum + current,
|
|
|
|
0
|
|
|
|
) > 0 &&
|
|
|
|
index === selectedColor && (
|
|
|
|
<View style={styles.colorQuantityBadge}>
|
|
|
|
<Text style={styles.colorQuantityText}>
|
|
|
|
x
|
|
|
|
{Object.values(selectedSizes).reduce(
|
|
|
|
(sum, current) => sum + current,
|
|
|
|
0
|
|
|
|
)}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
</TouchableOpacity>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
)}
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* 尺码选择 */}
|
|
|
|
<View style={styles.optionSection}>
|
|
|
|
<Text style={styles.optionTitle}>Taille</Text>
|
|
|
|
<View style={styles.sizeList}>
|
|
|
|
{[
|
|
|
|
{ size: "35", stock: "231" },
|
|
|
|
{ size: "36", stock: "62" },
|
|
|
|
{ size: "37", stock: "88" },
|
|
|
|
{ size: "38", stock: "0" },
|
|
|
|
].map((item, index) => (
|
|
|
|
<View key={`size-${index}`} style={styles.sizeItem}>
|
|
|
|
<View style={styles.sizeInfo}>
|
|
|
|
<View style={styles.sizeRow}>
|
|
|
|
{selectedSizes[item.size] > 0 && (
|
|
|
|
<Text style={styles.sizeQuantityIndicator}>
|
|
|
|
x{selectedSizes[item.size]}
|
|
|
|
</Text>
|
|
|
|
)}
|
|
|
|
<Text style={styles.sizeNumber}>{item.size}</Text>
|
|
|
|
</View>
|
|
|
|
<Text style={styles.stockInfo}>
|
|
|
|
En stock {item.stock}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
<View style={styles.quantityControl}>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.quantityButton}
|
|
|
|
onPress={() => {
|
|
|
|
setSelectedSizes((prev) => ({
|
|
|
|
...prev,
|
|
|
|
[item.size]: Math.max(
|
|
|
|
0,
|
|
|
|
(prev[item.size] || 0) - 1
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Text style={styles.quantityButtonText}>-</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<Text style={styles.quantityText}>
|
|
|
|
{selectedSizes[item.size] || 0}
|
|
|
|
</Text>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.quantityButton}
|
|
|
|
onPress={() => {
|
|
|
|
if (
|
|
|
|
Number(item.stock) >
|
|
|
|
(selectedSizes[item.size] || 0)
|
|
|
|
) {
|
|
|
|
setSelectedSizes((prev) => ({
|
|
|
|
...prev,
|
|
|
|
[item.size]: (prev[item.size] || 0) + 1,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Text style={styles.quantityButtonText}>+</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
))}
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</ScrollView>
|
|
|
|
|
|
|
|
{/* 底部总计和按钮 */}
|
|
|
|
<View style={styles.footer}>
|
|
|
|
<View style={styles.totalInfo}>
|
|
|
|
<Text style={styles.totalQuantity}>
|
|
|
|
Quantité Totale:{" "}
|
|
|
|
{Object.values(selectedSizes).reduce(
|
|
|
|
(sum, current) => sum + current,
|
|
|
|
0
|
|
|
|
)}
|
|
|
|
</Text>
|
|
|
|
<Text style={styles.totalAmount}>
|
|
|
|
Total:{" "}
|
|
|
|
{(
|
|
|
|
Object.values(selectedSizes).reduce(
|
|
|
|
(sum, current) => sum + current,
|
|
|
|
0
|
|
|
|
) * 3072
|
|
|
|
).toFixed(0)}{" "}
|
|
|
|
FCFA
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.modalAddToCartButton}
|
|
|
|
onPress={() => {
|
|
|
|
const totalQuantity = Object.values(selectedSizes).reduce(
|
|
|
|
(sum, current) => sum + current,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
if (totalQuantity > 0) {
|
|
|
|
setShowBottomSheet(false);
|
|
|
|
setShowSuccessModal(true);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Text style={styles.modalAddToCartText}>Ajouter au panier</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</Animated.View>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{/* 图片预览模态框 */}
|
|
|
|
{showImagePreview && (
|
|
|
|
<View style={styles.imagePreviewOverlay}>
|
|
|
|
<StatusBar barStyle="light-content" backgroundColor="#000" />
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.imagePreviewCloseButton}
|
|
|
|
onPress={() => setShowImagePreview(false)}
|
|
|
|
>
|
|
|
|
<Text style={styles.imagePreviewCloseText}>×</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.imagePreviewCounter}>
|
|
|
|
<Text style={styles.imagePreviewCounterText}>
|
|
|
|
{`${previewImageIndex + 1}/${
|
|
|
|
product.product_image_urls?.length || 1
|
|
|
|
}`}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
<View style={styles.colorPreviewLabel}>
|
|
|
|
<Text style={styles.colorPreviewText}>
|
|
|
|
{["Noir", "Brun", "Beige", "Couleur", "Bleu"][previewImageIndex]}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
<FlatList
|
|
|
|
data={product.product_image_urls || []}
|
|
|
|
horizontal
|
|
|
|
pagingEnabled
|
|
|
|
showsHorizontalScrollIndicator={false}
|
|
|
|
initialScrollIndex={previewImageIndex}
|
|
|
|
getItemLayout={(data, index) => ({
|
|
|
|
length: screenWidth,
|
|
|
|
offset: screenWidth * index,
|
|
|
|
index,
|
|
|
|
})}
|
|
|
|
keyExtractor={(_, index) => `preview-${index}`}
|
|
|
|
onScroll={(event) => {
|
|
|
|
const slideSize = event.nativeEvent.layoutMeasurement.width;
|
|
|
|
const index = Math.floor(
|
|
|
|
event.nativeEvent.contentOffset.x / slideSize
|
|
|
|
);
|
|
|
|
setPreviewImageIndex(index);
|
|
|
|
}}
|
|
|
|
scrollEventThrottle={16}
|
|
|
|
renderItem={({ item }) => (
|
|
|
|
<View style={styles.imagePreviewContainer}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: item }}
|
|
|
|
style={styles.imagePreviewImage}
|
|
|
|
resizeMode="contain"
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{/* 加入购物车成功弹窗 */}
|
|
|
|
{showSuccessModal && (
|
|
|
|
<View style={styles.successModalOverlay}>
|
|
|
|
<View style={styles.successModalContent}>
|
|
|
|
<View style={styles.successModalImageContainer}>
|
|
|
|
<Image
|
|
|
|
source={{ uri: product.product_image_urls[selectedColor] }}
|
|
|
|
style={styles.successModalImage}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
<Text style={styles.successModalText}>
|
|
|
|
Produit ajouté au panier !
|
|
|
|
</Text>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.successModalButton}
|
|
|
|
onPress={() => {
|
|
|
|
setShowSuccessModal(false);
|
|
|
|
navigation.navigate("Cart");
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Text style={styles.successModalButtonText}>Voir le panier</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
|
|
style={styles.successModalCloseButton}
|
|
|
|
onPress={() => setShowSuccessModal(false)}
|
|
|
|
>
|
|
|
|
<Text style={styles.successModalCloseText}>×</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
</SafeAreaView>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
},
|
|
|
|
header: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
paddingVertical: 12,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
borderBottomColor: "#eee",
|
|
|
|
},
|
|
|
|
backButton: {
|
|
|
|
padding: 5,
|
|
|
|
},
|
|
|
|
searchContainer: {
|
|
|
|
flex: 1,
|
|
|
|
height: widthUtils(50, 50).height,
|
|
|
|
backgroundColor: "#f5f5f5",
|
|
|
|
borderRadius: 20,
|
|
|
|
paddingHorizontal: 10,
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
position: "relative",
|
|
|
|
},
|
|
|
|
searchInput: {
|
|
|
|
flex: 1,
|
|
|
|
fontSize: fontSize(15),
|
|
|
|
color: "#333",
|
|
|
|
height: widthUtils(50, 50).height,
|
|
|
|
paddingVertical: 0,
|
|
|
|
paddingRight: 30,
|
|
|
|
},
|
|
|
|
searchIconButton: {
|
|
|
|
position: "absolute",
|
|
|
|
right: 8,
|
|
|
|
top: 14,
|
|
|
|
padding: 0,
|
|
|
|
zIndex: 1,
|
|
|
|
},
|
|
|
|
headerRight: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
iconButton: {
|
|
|
|
padding: 5,
|
|
|
|
marginLeft: 15,
|
|
|
|
},
|
|
|
|
imageContainer: {
|
|
|
|
height: screenHeight * 0.5,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
position: "relative",
|
|
|
|
},
|
|
|
|
priceContainer: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "flex-end",
|
|
|
|
marginBottom: 3,
|
|
|
|
},
|
|
|
|
mainPriceContainer: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "flex-start",
|
|
|
|
},
|
|
|
|
productPrice: {
|
|
|
|
fontSize: fontSize(30),
|
|
|
|
fontWeight: 700,
|
|
|
|
color: "#ff4500",
|
|
|
|
lineHeight: 30,
|
|
|
|
},
|
|
|
|
currencySymbol: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: 700,
|
|
|
|
color: "#ff4500",
|
|
|
|
marginLeft: 2,
|
|
|
|
marginTop: 3,
|
|
|
|
},
|
|
|
|
originalPrice: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
color: "#999",
|
|
|
|
textDecorationLine: "line-through",
|
|
|
|
marginLeft: 8,
|
|
|
|
marginBottom: 2,
|
|
|
|
},
|
|
|
|
discountBadge: {
|
|
|
|
paddingHorizontal: 6,
|
|
|
|
borderRadius: 3,
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
vipBadge: {
|
|
|
|
paddingHorizontal: 8,
|
|
|
|
paddingVertical: 2,
|
|
|
|
borderRadius: 3,
|
|
|
|
},
|
|
|
|
colorOptions: {
|
|
|
|
flexDirection: "row",
|
|
|
|
flexWrap: "wrap",
|
|
|
|
gap: 10,
|
|
|
|
marginTop: 5,
|
|
|
|
},
|
|
|
|
colorOption: {
|
|
|
|
width: 40,
|
|
|
|
height: 40,
|
|
|
|
borderRadius: 4,
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: "#e0e0e0",
|
|
|
|
overflow: "hidden",
|
|
|
|
},
|
|
|
|
colorOptionSelected: {
|
|
|
|
borderColor: "#ff4500",
|
|
|
|
borderWidth: 2,
|
|
|
|
},
|
|
|
|
colorOptionImage: {
|
|
|
|
width: "100%",
|
|
|
|
height: "100%",
|
|
|
|
},
|
|
|
|
sizeOptions: {
|
|
|
|
flexDirection: "row",
|
|
|
|
flexWrap: "wrap",
|
|
|
|
gap: 10,
|
|
|
|
marginTop: 5,
|
|
|
|
},
|
|
|
|
sizeOption: {
|
|
|
|
// width: 40,
|
|
|
|
// height: 30,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: "#f5f5f5",
|
|
|
|
borderWidth: 0,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingHorizontal: 0,
|
|
|
|
paddingVertical: 0,
|
|
|
|
},
|
|
|
|
sizeOptionSelected: {
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: "#ff4500",
|
|
|
|
},
|
|
|
|
sizeText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#333",
|
|
|
|
padding: 10,
|
|
|
|
},
|
|
|
|
sizeTextSelected: {
|
|
|
|
color: "#ff4500",
|
|
|
|
},
|
|
|
|
relatedProductsSection: {
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
paddingTop: 15,
|
|
|
|
paddingBottom: 15,
|
|
|
|
borderTopWidth: 10,
|
|
|
|
borderTopColor: "#f5f5f5",
|
|
|
|
},
|
|
|
|
sectionHeader: {
|
|
|
|
flexDirection: "row",
|
|
|
|
justifyContent: "space-between",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
position: "relative",
|
|
|
|
},
|
|
|
|
sectionTitle: {
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: 600,
|
|
|
|
color: "#333",
|
|
|
|
paddingBottom: 5,
|
|
|
|
position: "relative",
|
|
|
|
},
|
|
|
|
|
|
|
|
viewAllButton: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
viewAllText: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
color: "##747474",
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
},
|
|
|
|
relatedProductsContainer: {
|
|
|
|
paddingLeft: 15,
|
|
|
|
paddingRight: 5,
|
|
|
|
paddingVertical: 5,
|
|
|
|
},
|
|
|
|
relatedProductItem: {
|
|
|
|
width: widthUtils(90, 90).width,
|
|
|
|
marginRight: 10,
|
|
|
|
},
|
|
|
|
relatedProductImage: {
|
|
|
|
width: "100%",
|
|
|
|
aspectRatio: 1,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginBottom: 5,
|
|
|
|
},
|
|
|
|
relatedProductPrice: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: "bold",
|
|
|
|
color: "#ff4500",
|
|
|
|
marginTop: 2,
|
|
|
|
},
|
|
|
|
loadMoreIndicator: {
|
|
|
|
flexDirection: "row",
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
loadMoreText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#666",
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
noMoreProductsText: {
|
|
|
|
textAlign: "center",
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#999",
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
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: fontSize(14),
|
|
|
|
color: "#999",
|
|
|
|
},
|
|
|
|
bottomSpace: {
|
|
|
|
height: 60,
|
|
|
|
},
|
|
|
|
loadingContainer: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
loadingText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#666",
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
errorContainer: {
|
|
|
|
flex: 1,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
padding: 20,
|
|
|
|
},
|
|
|
|
errorText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: "bold",
|
|
|
|
color: "#333",
|
|
|
|
marginTop: 15,
|
|
|
|
marginBottom: 20,
|
|
|
|
},
|
|
|
|
headerTitle: {
|
|
|
|
flex: 1,
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "bold",
|
|
|
|
color: "#333",
|
|
|
|
textAlign: "center",
|
|
|
|
marginHorizontal: 10,
|
|
|
|
},
|
|
|
|
headerIconButton: {
|
|
|
|
padding: 5,
|
|
|
|
},
|
|
|
|
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,
|
|
|
|
paddingTop: 10,
|
|
|
|
paddingBottom: 10,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
},
|
|
|
|
optionSection: {
|
|
|
|
marginTop: 0,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
padding: 15,
|
|
|
|
borderTopColor: "#f5f5f5",
|
|
|
|
},
|
|
|
|
optionTitle: {
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: 600,
|
|
|
|
color: "#333",
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
moreColorsButton: {
|
|
|
|
width: 35,
|
|
|
|
height: 35,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: "#f5f5f5",
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
marginRight: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
moreColorsText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#333",
|
|
|
|
},
|
|
|
|
vipText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: "bold",
|
|
|
|
},
|
|
|
|
productTitle: {
|
|
|
|
fontSize: fontSize(18),
|
|
|
|
color: "#333",
|
|
|
|
lineHeight: 22,
|
|
|
|
marginBottom: 8,
|
|
|
|
fontFamily: "PingFang SC",
|
|
|
|
fontWeight: "600",
|
|
|
|
},
|
|
|
|
salesContainer: {
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
salesText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#666",
|
|
|
|
},
|
|
|
|
scrollContainer: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
scrollContentContainer: {
|
|
|
|
paddingBottom: 70,
|
|
|
|
},
|
|
|
|
imagePlaceholder: {
|
|
|
|
width: "100%",
|
|
|
|
height: "100%",
|
|
|
|
alignItems: "center",
|
|
|
|
justifyContent: "center",
|
|
|
|
},
|
|
|
|
imagePlaceholderText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#999",
|
|
|
|
},
|
|
|
|
colorCircle: {
|
|
|
|
width: 16,
|
|
|
|
height: 16,
|
|
|
|
borderRadius: 8,
|
|
|
|
marginRight: 6,
|
|
|
|
},
|
|
|
|
colorName: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
color: "#333",
|
|
|
|
textAlign: "center",
|
|
|
|
paddingTop: 4,
|
|
|
|
backgroundColor: "#f4f4f4",
|
|
|
|
width: "100%",
|
|
|
|
paddingVertical: 1,
|
|
|
|
},
|
|
|
|
discountText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: "bold",
|
|
|
|
},
|
|
|
|
pageIndicator: {
|
|
|
|
position: "absolute",
|
|
|
|
bottom: 15,
|
|
|
|
right: 20,
|
|
|
|
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
|
|
|
paddingHorizontal: 10,
|
|
|
|
paddingVertical: 5,
|
|
|
|
borderRadius: 15,
|
|
|
|
width: 50,
|
|
|
|
height: 26,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
pageIndicatorText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
fontWeight: "500",
|
|
|
|
textAlign: "center",
|
|
|
|
},
|
|
|
|
relatedProductsList: {
|
|
|
|
// 移除固定高度,让内容决定高度
|
|
|
|
},
|
|
|
|
detailsSection: {
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
padding: 15,
|
|
|
|
marginTop: 10,
|
|
|
|
borderTopWidth: 10,
|
|
|
|
borderTopColor: "#f5f5f5",
|
|
|
|
},
|
|
|
|
|
|
|
|
detailsText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#333",
|
|
|
|
},
|
|
|
|
detailsImage: {
|
|
|
|
width: "100%",
|
|
|
|
aspectRatio: 1.5,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
heartIconButton: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 20,
|
|
|
|
right: 20,
|
|
|
|
backgroundColor: "rgba(255, 255, 255, 1.0)",
|
|
|
|
borderRadius: 20,
|
|
|
|
width: 40,
|
|
|
|
height: 40,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
shadowColor: "#000",
|
|
|
|
shadowOffset: { width: 0, height: 2 },
|
|
|
|
shadowOpacity: 0.1,
|
|
|
|
shadowRadius: 3,
|
|
|
|
elevation: 2,
|
|
|
|
zIndex: 10,
|
|
|
|
},
|
|
|
|
fixedBottomBar: {
|
|
|
|
position: "absolute",
|
|
|
|
bottom: 10,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
justifyContent: "center",
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
backgroundColor: "transparent",
|
|
|
|
zIndex: 100,
|
|
|
|
height: 60,
|
|
|
|
},
|
|
|
|
chatNowButton: {
|
|
|
|
width: "40%",
|
|
|
|
height: widthUtils(50, 50).height,
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
justifyContent: "center",
|
|
|
|
paddingVertical: 0,
|
|
|
|
borderRadius: 30,
|
|
|
|
backgroundColor: "#4CAF50",
|
|
|
|
marginRight: 10,
|
|
|
|
elevation: 4,
|
|
|
|
shadowColor: "#000",
|
|
|
|
shadowOffset: { width: 0, height: 2 },
|
|
|
|
shadowOpacity: 0.2,
|
|
|
|
shadowRadius: 3,
|
|
|
|
},
|
|
|
|
chatNowText: {
|
|
|
|
fontSize: fontSize(18),
|
|
|
|
color: "#fff",
|
|
|
|
fontWeight: "bold",
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
addToCartButton: {
|
|
|
|
width: "60%",
|
|
|
|
height: isSmallScreen ? 30 : 50,
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
justifyContent: "center",
|
|
|
|
paddingVertical: 0,
|
|
|
|
borderRadius: 30,
|
|
|
|
backgroundColor: "#FF5722",
|
|
|
|
marginLeft: 10,
|
|
|
|
elevation: 4,
|
|
|
|
shadowColor: "#000",
|
|
|
|
shadowOffset: { width: 0, height: 2 },
|
|
|
|
shadowOpacity: 0.2,
|
|
|
|
shadowRadius: 3,
|
|
|
|
},
|
|
|
|
addToCartText: {
|
|
|
|
fontSize: fontSize(18),
|
|
|
|
color: "#fff",
|
|
|
|
fontWeight: "bold",
|
|
|
|
marginLeft: 8,
|
|
|
|
},
|
|
|
|
bottomSheetOverlay: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
justifyContent: "flex-end",
|
|
|
|
zIndex: 200,
|
|
|
|
},
|
|
|
|
overlayTouchable: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
|
|
},
|
|
|
|
bottomSheet: {
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
borderTopLeftRadius: 20,
|
|
|
|
borderTopRightRadius: 20,
|
|
|
|
height: "85%",
|
|
|
|
zIndex: 1,
|
|
|
|
},
|
|
|
|
priceHeader: {
|
|
|
|
flexDirection: "row",
|
|
|
|
justifyContent: "space-between",
|
|
|
|
alignItems: "flex-start",
|
|
|
|
marginBottom: 20,
|
|
|
|
paddingBottom: 15,
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
borderBottomColor: "#eee",
|
|
|
|
paddingTop: 15,
|
|
|
|
},
|
|
|
|
mainPrice: {
|
|
|
|
flexDirection: "row",
|
|
|
|
borderRadius: 10,
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
smallProductImage: {
|
|
|
|
width: 80,
|
|
|
|
height: 80,
|
|
|
|
borderRadius: 4,
|
|
|
|
marginRight: 10,
|
|
|
|
},
|
|
|
|
priceText: {
|
|
|
|
fontSize: fontSize(30),
|
|
|
|
fontWeight: "700",
|
|
|
|
color: "#ff4500",
|
|
|
|
},
|
|
|
|
currency: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
fontWeight: "700",
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
color: "#ff4500",
|
|
|
|
},
|
|
|
|
priceRanges: {
|
|
|
|
flexDirection: "row",
|
|
|
|
gap: 15,
|
|
|
|
},
|
|
|
|
priceRange: {
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
rangePrice: {
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
color: "#ff4500",
|
|
|
|
fontWeight: "500",
|
|
|
|
},
|
|
|
|
rangeQuantity: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
color: "#666",
|
|
|
|
marginTop: 2,
|
|
|
|
},
|
|
|
|
closeButton: {
|
|
|
|
position: "absolute",
|
|
|
|
right: 15,
|
|
|
|
top: 15,
|
|
|
|
padding: 0,
|
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
},
|
|
|
|
closeButtonText: {
|
|
|
|
fontSize: fontSize(24),
|
|
|
|
color: "#666",
|
|
|
|
lineHeight: 24,
|
|
|
|
textAlign: "center",
|
|
|
|
},
|
|
|
|
bottomSheetContent: {
|
|
|
|
flex: 1,
|
|
|
|
marginBottom: 15,
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
},
|
|
|
|
colorGrid: {
|
|
|
|
flexDirection: "row",
|
|
|
|
flexWrap: "wrap",
|
|
|
|
gap: 10,
|
|
|
|
},
|
|
|
|
colorOptionBase: {
|
|
|
|
width: 90,
|
|
|
|
height: 116,
|
|
|
|
borderRadius: 4,
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: "#e0e0e0",
|
|
|
|
overflow: "visible",
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
colorOptionSelectedState: {
|
|
|
|
borderColor: "#ff4500",
|
|
|
|
borderWidth: 2,
|
|
|
|
},
|
|
|
|
colorImage: {
|
|
|
|
width: "100%",
|
|
|
|
height: 90,
|
|
|
|
borderTopLeftRadius: 3,
|
|
|
|
borderTopRightRadius: 3,
|
|
|
|
},
|
|
|
|
sizeList: {
|
|
|
|
gap: 15,
|
|
|
|
},
|
|
|
|
sizeItem: {
|
|
|
|
flexDirection: "row",
|
|
|
|
justifyContent: "space-between",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingVertical: 10,
|
|
|
|
borderBottomWidth: 0,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
},
|
|
|
|
sizeInfo: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
sizeRow: {
|
|
|
|
flexDirection: "row" as const,
|
|
|
|
alignItems: "center" as const,
|
|
|
|
},
|
|
|
|
sizeNumber: {
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
color: "#333",
|
|
|
|
marginBottom: 4,
|
|
|
|
},
|
|
|
|
sizeQuantityIndicator: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
marginLeft: 4,
|
|
|
|
marginBottom: 4,
|
|
|
|
width: 30,
|
|
|
|
backgroundColor: "#ff5217",
|
|
|
|
borderRadius: 15,
|
|
|
|
textAlign: "center",
|
|
|
|
color: "#fff",
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
marginRight: 10,
|
|
|
|
},
|
|
|
|
stockInfo: {
|
|
|
|
fontSize: fontSize(12),
|
|
|
|
color: "#666",
|
|
|
|
},
|
|
|
|
quantityControl: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
gap: 1,
|
|
|
|
},
|
|
|
|
quantityButton: {
|
|
|
|
width: 24,
|
|
|
|
height: 24,
|
|
|
|
borderRadius: 4,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
borderWidth: 0,
|
|
|
|
},
|
|
|
|
quantityButtonText: {
|
|
|
|
fontSize: fontSize(18),
|
|
|
|
color: "#333",
|
|
|
|
lineHeight: 20,
|
|
|
|
textAlign: "center",
|
|
|
|
includeFontPadding: false,
|
|
|
|
},
|
|
|
|
quantityText: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#333",
|
|
|
|
minWidth: 40,
|
|
|
|
width: 40,
|
|
|
|
height: 24,
|
|
|
|
textAlign: "center",
|
|
|
|
backgroundColor: "#f4f4f4",
|
|
|
|
lineHeight: 24,
|
|
|
|
borderRadius: 4,
|
|
|
|
textAlignVertical: "center",
|
|
|
|
paddingTop: 0,
|
|
|
|
paddingBottom: 0,
|
|
|
|
},
|
|
|
|
footer: {
|
|
|
|
borderTopWidth: 1,
|
|
|
|
borderTopColor: "#eee",
|
|
|
|
paddingVertical: 15,
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
width: "100%",
|
|
|
|
},
|
|
|
|
totalInfo: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingHorizontal: 15,
|
|
|
|
},
|
|
|
|
totalQuantity: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#333",
|
|
|
|
marginRight: 15,
|
|
|
|
},
|
|
|
|
totalAmount: {
|
|
|
|
fontSize: fontSize(14),
|
|
|
|
color: "#ff4500",
|
|
|
|
fontWeight: "500",
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
modalAddToCartButton: {
|
|
|
|
backgroundColor: "#ff4500",
|
|
|
|
height: 45,
|
|
|
|
borderRadius: 22,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
marginHorizontal: 15,
|
|
|
|
marginTop: 10,
|
|
|
|
},
|
|
|
|
modalAddToCartText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "600",
|
|
|
|
},
|
|
|
|
colorQuantityBadge: {
|
|
|
|
position: "absolute",
|
|
|
|
top: -7,
|
|
|
|
left: 0,
|
|
|
|
backgroundColor: "#ff4500",
|
|
|
|
paddingHorizontal: 4,
|
|
|
|
paddingVertical: 2,
|
|
|
|
borderRadius: 4,
|
|
|
|
width: 30,
|
|
|
|
height: 20,
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
zIndex: 9999,
|
|
|
|
},
|
|
|
|
colorQuantityText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(10),
|
|
|
|
fontWeight: "500",
|
|
|
|
textAlign: "center",
|
|
|
|
},
|
|
|
|
shopInfo: {
|
|
|
|
marginTop: 10,
|
|
|
|
backgroundColor: "#f3f4f8",
|
|
|
|
flexDirection: "row",
|
|
|
|
justifyContent: "space-between",
|
|
|
|
paddingVertical: 5,
|
|
|
|
paddingHorizontal: 10,
|
|
|
|
borderRadius: 5,
|
|
|
|
},
|
|
|
|
shopName: {
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "700",
|
|
|
|
color: "#373737",
|
|
|
|
},
|
|
|
|
currencySamll: {
|
|
|
|
fontFamily: "Segoe UI",
|
|
|
|
fontSize: fontSize(11),
|
|
|
|
fontWeight: "700",
|
|
|
|
color: "#373737",
|
|
|
|
marginLeft: 4,
|
|
|
|
},
|
|
|
|
priceRow: {
|
|
|
|
flexDirection: "row",
|
|
|
|
alignItems: "flex-start",
|
|
|
|
},
|
|
|
|
imagePreviewOverlay: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
backgroundColor: "#000",
|
|
|
|
zIndex: 1000,
|
|
|
|
},
|
|
|
|
imagePreviewCloseButton: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 40,
|
|
|
|
right: 20,
|
|
|
|
zIndex: 1001,
|
|
|
|
},
|
|
|
|
imagePreviewCloseText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(28),
|
|
|
|
fontWeight: "300",
|
|
|
|
},
|
|
|
|
imagePreviewCounter: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 40,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
alignItems: "center",
|
|
|
|
zIndex: 1001,
|
|
|
|
},
|
|
|
|
imagePreviewCounterText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "400",
|
|
|
|
},
|
|
|
|
imagePreviewContainer: {
|
|
|
|
width: screenWidth,
|
|
|
|
height: "100%",
|
|
|
|
justifyContent: "center",
|
|
|
|
},
|
|
|
|
imagePreviewImage: {
|
|
|
|
width: "100%",
|
|
|
|
height: "100%",
|
|
|
|
},
|
|
|
|
colorPreviewLabel: {
|
|
|
|
position: "absolute",
|
|
|
|
bottom: 40,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
alignItems: "center",
|
|
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
|
|
paddingVertical: 10,
|
|
|
|
},
|
|
|
|
colorPreviewText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "400",
|
|
|
|
textAlign: "center",
|
|
|
|
},
|
|
|
|
successModalOverlay: {
|
|
|
|
position: "absolute",
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
bottom: 0,
|
|
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
|
|
justifyContent: "center",
|
|
|
|
alignItems: "center",
|
|
|
|
zIndex: 2000,
|
|
|
|
},
|
|
|
|
successModalContent: {
|
|
|
|
backgroundColor: "#fff",
|
|
|
|
borderRadius: 10,
|
|
|
|
width: "90%",
|
|
|
|
alignItems: "center",
|
|
|
|
paddingVertical: 20,
|
|
|
|
position: "relative",
|
|
|
|
},
|
|
|
|
successModalImageContainer: {
|
|
|
|
width: 100,
|
|
|
|
height: 100,
|
|
|
|
marginBottom: 15,
|
|
|
|
},
|
|
|
|
successModalImage: {
|
|
|
|
width: "100%",
|
|
|
|
height: "100%",
|
|
|
|
borderRadius: 5,
|
|
|
|
},
|
|
|
|
successModalText: {
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
color: "#333",
|
|
|
|
marginBottom: 20,
|
|
|
|
},
|
|
|
|
successModalButton: {
|
|
|
|
backgroundColor: "#ff4500",
|
|
|
|
paddingVertical: 12,
|
|
|
|
paddingHorizontal: 30,
|
|
|
|
borderRadius: 25,
|
|
|
|
},
|
|
|
|
successModalButtonText: {
|
|
|
|
color: "#fff",
|
|
|
|
fontSize: fontSize(16),
|
|
|
|
fontWeight: "600",
|
|
|
|
},
|
|
|
|
successModalCloseButton: {
|
|
|
|
position: "absolute",
|
|
|
|
right: 10,
|
|
|
|
top: 10,
|
|
|
|
padding: 5,
|
|
|
|
},
|
|
|
|
successModalCloseText: {
|
|
|
|
fontSize: fontSize(24),
|
|
|
|
color: "#666",
|
|
|
|
},
|
|
|
|
});
|