| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- import { Image } from "expo-image";
- import { useRouter } from "expo-router";
- import React from "react";
- import {
- ImageBackground,
- ScrollView,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- } from "react-native";
- import { Images } from "@/constants/images";
- interface ProductItem {
- id: string;
- name: string;
- cover: string;
- level: string;
- probability: number;
- price?: number;
- quantity?: number;
- spu?: {
- id: string;
- cover: string;
- name: string;
- };
- }
- interface ProductListProps {
- products: ProductItem[];
- levelList?: any[];
- poolId: string;
- price: number;
- }
- // 等级配置 - 对应小程序的 LEVEL_MAP
- const LEVEL_CONFIG: Record<
- string,
- { title: string; color: string; bgColor: string; productItem: string }
- > = {
- A: {
- title: "超神款",
- color: "#fff",
- bgColor: "#FF4444",
- productItem: Images.box.detail.productItemA,
- },
- B: {
- title: "欧皇款",
- color: "#fff",
- bgColor: "#FF9900",
- productItem: Images.box.detail.productItemB,
- },
- C: {
- title: "隐藏款",
- color: "#fff",
- bgColor: "#9966FF",
- productItem: Images.box.detail.productItemC,
- },
- D: {
- title: "普通款",
- color: "#fff",
- bgColor: "#00CCFF",
- productItem: Images.box.detail.productItemD,
- },
- };
- const ignoreRatio0 = (val: number) => {
- // Revert to original logic: Do NOT multiply. Assume API sends WYSIWYG value (e.g. 99.05 or 0.001)
- // Just format string to remove trailing zeros.
- let str = String(val);
- // Match original logic: strip trailing zeros if decimal
- if (str.indexOf(".") > -1) {
- str = str.replace(/0+$/, "").replace(/\.$/, "");
- }
- return str;
- };
- export const ProductList: React.FC<ProductListProps> = ({
- products,
- levelList,
- poolId,
- }) => {
- const router = useRouter();
- // 按等级分组
- const groupedProducts = products.reduce(
- (acc, item) => {
- const level = item.level || "D";
- if (!acc[level]) acc[level] = [];
- acc[level].push(item);
- return acc;
- },
- {} as Record<string, ProductItem[]>,
- );
- // 计算各等级概率
- const getLevelProbability = (level: string) => {
- const item = levelList?.find((e) => e.level === level);
- return item ? `${item.probability}%` : "0%";
- };
- // 点击产品跳转到详情页
- const handleProductPress = (item: ProductItem) => {
- // Look up by object reference to handle duplicate IDs correctly
- const index = products.indexOf(item);
- router.push({
- pathname: "/treasure-hunt/swipe" as any,
- params: { poolId, index: index >= 0 ? index : 0 },
- });
- };
- const renderLevelSection = (level: string, items: ProductItem[]) => {
- const config = LEVEL_CONFIG[level] || LEVEL_CONFIG["D"];
- return (
- <View key={level} style={styles.levelBox}>
- {/* 等级标题行 */}
- <View style={styles.levelTitleRow}>
- <Text style={[styles.levelTitle, { color: config.bgColor }]}>
- {config.title}
- </Text>
- <View style={styles.levelProportion}>
- <Text style={styles.probabilityLabel}>概率:</Text>
- <Text style={styles.probabilityValue}>
- {getLevelProbability(level)}
- </Text>
- </View>
- </View>
- {/* 商品横向滚动列表 */}
- <ScrollView
- horizontal
- showsHorizontalScrollIndicator={false}
- contentContainerStyle={styles.scrollContent}
- >
- {items.map((item, index) => {
- const cover = item.spu?.cover || item.cover;
- return (
- <TouchableOpacity
- key={item.id || index}
- onPress={() => handleProductPress(item)}
- activeOpacity={0.8}
- >
- <ImageBackground
- source={{ uri: Images.box.detail.levelBoxBg }}
- style={styles.productItem}
- resizeMode="stretch"
- >
- {/* 商品图片 */}
- <Image
- source={{ uri: cover }}
- style={styles.productImage}
- contentFit="contain"
- />
- {/* 概率标签背景 */}
- <ImageBackground
- source={{ uri: config.productItem }}
- style={styles.levelTagBg}
- resizeMode="stretch"
- >
- <View style={styles.levelTagContent}>
- <Text style={styles.levelTagLabel}>概率:</Text>
- <Text style={styles.levelTagText}>
- {ignoreRatio0(item.probability)}%
- </Text>
- </View>
- </ImageBackground>
- </ImageBackground>
- </TouchableOpacity>
- );
- })}
- </ScrollView>
- </View>
- );
- };
- const levelOrder = ["A", "B", "C", "D"];
- return (
- <View style={styles.container}>
- {/* 标题 */}
- <View style={styles.titleBox}>
- <Image
- source={{ uri: Images.box.detail.productTitle }}
- style={styles.titleImg}
- contentFit="contain"
- />
- </View>
- {levelOrder.map((level) => {
- const items = groupedProducts[level];
- if (!items || items.length === 0) return null;
- return renderLevelSection(level, items);
- })}
- </View>
- );
- };
- const styles = StyleSheet.create({
- container: {
- paddingHorizontal: 10,
- marginTop: -40,
- },
- titleBox: {
- alignItems: "center",
- marginBottom: 15,
- },
- titleImg: {
- width: 121,
- height: 29,
- },
- levelBox: {
- marginBottom: 20,
- },
- levelTitleRow: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "center",
- paddingHorizontal: 14,
- paddingBottom: 10,
- position: "relative",
- },
- levelTitle: {
- fontSize: 18,
- fontWeight: "bold",
- textAlign: "center",
- textShadowColor: "#000",
- textShadowOffset: { width: 1, height: 1 },
- textShadowRadius: 1,
- },
- levelProportion: {
- position: "absolute",
- right: 0,
- bottom: 10,
- flexDirection: "row",
- alignItems: "center",
- },
- probabilityLabel: {
- fontSize: 12,
- color: "#ffc901",
- },
- probabilityValue: {
- fontSize: 12,
- color: "#fff",
- },
- scrollContent: {
- paddingHorizontal: 5,
- },
- productItem: {
- width: 88,
- height: 110,
- marginRight: 10,
- alignItems: "center",
- },
- productImage: {
- width: 88,
- height: 90,
- },
- levelTagBg: {
- width: 88,
- height: 53,
- marginTop: -18,
- justifyContent: "center",
- paddingTop: 12,
- },
- levelTagContent: {
- flexDirection: "row",
- justifyContent: "center",
- alignItems: "center",
- },
- levelTagLabel: {
- fontSize: 8,
- color: "#fff",
- },
- levelTagText: {
- fontSize: 9,
- color: "#fff",
- fontWeight: "bold",
- },
- });
|