| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- import { Image } from 'expo-image';
- import React from 'react';
- import { ScrollView, StyleSheet, Text, View } from 'react-native';
- interface ProductItem {
- id: string;
- name: string;
- cover: string;
- level: string;
- probability: number;
- price?: number;
- quantity?: number;
- spu?: {
- id: string;
- cover: string;
- name: string;
- };
- }
- interface BoxData {
- leftQuantity: number;
- usedStat?: Record<string, { spuId: string; quantity: number }>;
- }
- interface ProductListYfsProps {
- products: ProductItem[];
- levelList?: any[];
- poolId: string;
- price: number;
- box?: BoxData | null;
- }
- // 等级配置
- const LEVEL_CONFIG: Record<string, { title: string; color: string; bgColor: string }> = {
- A: { title: '超神款', color: '#fff', bgColor: '#FF4444' },
- B: { title: '欧皇款', color: '#fff', bgColor: '#FF9900' },
- C: { title: '隐藏款', color: '#fff', bgColor: '#9966FF' },
- D: { title: '普通款', color: '#fff', bgColor: '#00CCFF' },
- };
- // 格式化概率,去除末尾的0
- const formatProbability = (val: string | number) => {
- const num = parseFloat(String(val));
- if (isNaN(num)) return '0';
- // 保留两位小数,去除末尾的0
- return num.toFixed(2).replace(/\.?0+$/, '');
- };
- export const ProductListYfs: React.FC<ProductListYfsProps> = ({ products, levelList, box }) => {
- // 按等级分组
- 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 ? `${formatProbability(item.probability)}%` : '0%';
- };
- // 获取商品剩余数量
- const getLeftNum = (item: ProductItem) => {
- const spuId = item.spu?.id || item.id;
- if (!box?.usedStat || !box.usedStat[spuId]?.quantity) {
- return item.quantity || 1;
- }
- return (item.quantity || 1) - box.usedStat[spuId].quantity;
- };
- // 获取商品概率
- const getProbability = (item: ProductItem) => {
- if (!box || box.leftQuantity <= 0) return '0';
- const leftNum = getLeftNum(item);
- return ((leftNum / box.leftQuantity) * 100).toFixed(2);
- };
- 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;
- const leftNum = getLeftNum(item);
- const prob = formatProbability(getProbability(item));
- return (
- <View key={item.id || index} style={styles.productItem}>
- {/* 商品图片 */}
- <View style={styles.productImageBox}>
- <Image
- source={{ uri: cover }}
- style={styles.productImage}
- contentFit="contain"
- />
- </View>
- {/* 等级标签 */}
- <View style={[styles.levelTag, { backgroundColor: config.bgColor }]}>
- <Text style={styles.levelTagLabel}>概率</Text>
- <Text style={styles.levelTagText}>{prob}%</Text>
- </View>
- {/* 剩余数量 */}
- <Text style={styles.leftNumText}>{leftNum}/{item.quantity || 1}</Text>
- </View>
- );
- })}
- </ScrollView>
- </View>
- );
- };
- const levelOrder = ['A', 'B', 'C', 'D'];
- return (
- <View style={styles.container}>
- {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,
- },
- 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: 90,
- marginRight: 10,
- alignItems: 'center',
- },
- productImageBox: {
- width: 90,
- height: 90,
- backgroundColor: '#fff',
- borderRadius: 4,
- overflow: 'hidden',
- },
- productImage: {
- width: 90,
- height: 90,
- },
- levelTag: {
- width: 80,
- height: 26,
- borderRadius: 2,
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- marginTop: -8,
- },
- levelTagLabel: {
- fontSize: 10,
- color: '#fff',
- marginRight: 2,
- },
- levelTagText: {
- fontSize: 12,
- color: '#fff',
- fontWeight: 'bold',
- },
- leftNumText: {
- fontSize: 10,
- color: '#999',
- marginTop: 4,
- },
- });
|