| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- import { LEVEL_MAP } from '@/constants/config';
- import { Images } from '@/constants/images';
- import { Image, ImageBackground } from 'expo-image';
- import React, { useMemo } from 'react';
- import { Dimensions, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
- const { width } = Dimensions.get('window');
- interface ProductListYfsProps {
- products: any[];
- poolId: string;
- box: any;
- onProductClick?: (product: any) => void;
- }
- export default function ProductListYfs({ products = [], poolId, box, onProductClick }: ProductListYfsProps) {
-
- const levels = useMemo(() => {
- console.log('ProductListYfs products:', products ? products.length : 'null');
- const grouped: Record<string, any[]> = { A: [], B: [], C: [], D: [] };
- products.forEach(p => {
- console.log('Processing item level:', p.level);
- if (grouped[p.level]) {
- grouped[p.level].push(p);
- } else {
- console.log('Unknown level:', p.level);
- }
- });
- const result = [
- { level: 'A', list: grouped.A, ...LEVEL_MAP.A },
- { level: 'B', list: grouped.B, ...LEVEL_MAP.B },
- { level: 'C', list: grouped.C, ...LEVEL_MAP.C },
- { level: 'D', list: grouped.D, ...LEVEL_MAP.D },
- ].filter(g => g.list && g.list.length > 0);
- console.log('ProductListYfs levels:', result.length);
- return result;
- }, [products]);
- const getLeftNum = (item: any) => {
- if (!box || !box.usedStat || !box.usedStat[item.spu.id]) {
- return item.quantity;
- }
- return item.quantity - (box.usedStat[item.spu.id].quantity || 0);
- };
- const getProbability = (item: any) => {
- if (!box || !box.leftQuantity) return '0%';
- const left = getLeftNum(item);
- const prob = (left / box.leftQuantity * 100).toFixed(4);
- return parseFloat(prob) === 0 ? '0%' : `${prob}%`;
- };
- const getLevelProbability = (level: string) => {
- if (!box || !box.leftQuantity) return '0%';
- let sumLeft = 0;
- products.filter(p => p.level === level).forEach(p => {
- sumLeft += getLeftNum(p);
- });
- const prob = (sumLeft / box.leftQuantity * 100).toFixed(4);
- return parseFloat(prob) === 0 ? '0%' : `${prob}%`;
- };
- if (!products || products.length === 0) return null;
- return (
- <View style={styles.container}>
- {levels.map((levelItem) => (
- <View key={levelItem.level} style={styles.levelGroup}>
- {/* Header with Title and Probability */}
- <View style={styles.levelHeader}>
- <Text style={[styles.levelTitle, { color: levelItem.color }]}>{levelItem.title}</Text>
- <Text style={styles.levelProb}>{getLevelProbability(levelItem.level)}</Text>
-
- {/* The legacy code used borders and backgrounds for titles, but for now text color + shadow mimics the look well enough or we can add ImageBackground if needed.
- Legacy: :style="{ color: LEVEL_MAP[levelItem.level].color }" and centered.
- */}
- </View>
-
- {/* List Container - Legacy uses levelBoxBg for EACH item container?
- Legacy:
- <view class="levelListScroll"> ... <scroll-view ...>
- <view ... class="item" :style="{ backgroundImage: 'url(' + ossurl.box.detail.levelBoxBg + ')' }">
- */}
- <View style={styles.listWrapper}>
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.scrollContent}>
- {levelItem.list.map((item, index) => (
- <TouchableOpacity
- key={index}
- style={styles.itemContainer}
- onPress={() => onProductClick && onProductClick(item)}
- >
- {/* Item Background (levelBoxBg) */}
- <ImageBackground
- source={{ uri: Images.box.detail.levelBoxBg }}
- style={styles.itemBg}
- resizeMode="stretch"
- >
- <Image source={{ uri: item.spu.cover }} style={styles.itemImage} contentFit="contain" />
-
- {/* Info Box (productItemA/B/C/D) */}
- <ImageBackground
- source={{ uri: levelItem.productItem }}
- style={styles.textBox}
- resizeMode="stretch"
- >
- <View style={styles.probTag}>
- <Text style={[styles.probLabel, { color: levelItem.color }]}>概率:</Text>
- <Text style={styles.probValue}>{getProbability(item)}</Text>
- </View>
- <View style={[styles.countTag, { backgroundColor: levelItem.color }]}>
- <Text style={styles.countText}>{getLeftNum(item)}/{item.quantity}</Text>
- </View>
- </ImageBackground>
- </ImageBackground>
- </TouchableOpacity>
- ))}
- </ScrollView>
- </View>
- </View>
- ))}
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- paddingHorizontal: 10,
- paddingVertical: 10,
- },
- levelGroup: {
- marginBottom: 5,
- },
- levelHeader: {
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- marginBottom: 5,
- position: 'relative',
- height: 40,
- },
- levelTitle: {
- fontSize: 18,
- fontWeight: 'bold',
- textShadowColor: '#000',
- textShadowOffset: { width: 1, height: 1 },
- textShadowRadius: 1,
- },
- levelProb: {
- position: 'absolute',
- right: 15,
- bottom: 5,
- color: '#fff',
- fontSize: 12,
- fontFamily: 'System', // Prevent potential font missing issues
- },
- listWrapper: {
- paddingBottom: 10,
- },
- scrollContent: {
- paddingRight: 10,
- },
- itemContainer: {
- width: 88, // 175rpx / 2
- height: 110, // 220rpx / 2
- marginRight: 8,
- },
- itemBg: {
- width: '100%',
- height: '100%',
- position: 'relative',
- },
- itemImage: {
- width: 88,
- height: 90,
- position: 'absolute',
- top: 0,
- left: 0,
- zIndex: 1,
- },
- textBox: {
- width: '100%',
- height: 53, // 106rpx / 2
- position: 'absolute',
- bottom: 0, // Adjusted from top: -30rpx legacy logic which was weird, simplified for RN
- zIndex: 2,
- justifyContent: 'flex-start',
- paddingTop: 18, // Push content down below the curve
- alignItems: 'center',
- },
- probTag: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- probLabel: {
- fontSize: 8,
- marginRight: 2,
- },
- probValue: {
- fontSize: 8,
- fontWeight: 'bold',
- color: '#333'
- },
- countTag: {
- position: 'absolute',
- top: 0,
- right: 0,
- paddingHorizontal: 4,
- borderRadius: 2,
- zIndex: 3,
- },
- countText: {
- color: '#fff',
- fontSize: 9,
- }
- });
|