ProductList.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { Image } from 'expo-image';
  2. import React from 'react';
  3. import { ImageBackground, StyleSheet, Text, View } from 'react-native';
  4. import { Images } from '@/constants/images';
  5. interface ProductItem {
  6. id: string;
  7. name: string;
  8. cover: string;
  9. level: string;
  10. probability: number;
  11. price?: number;
  12. quantity?: number;
  13. }
  14. interface ProductListProps {
  15. products: ProductItem[];
  16. levelList?: any[];
  17. poolId: string;
  18. price: number;
  19. }
  20. const LEVEL_CONFIG: Record<string, { title: string; bg: string }> = {
  21. A: { title: '超神款', bg: Images.box.detail.productItemA },
  22. B: { title: '欧皇款', bg: Images.box.detail.productItemB },
  23. C: { title: '隐藏款', bg: Images.box.detail.productItemC },
  24. D: { title: '普通款', bg: Images.box.detail.productItemD },
  25. };
  26. const ignoreRatio0 = (val: number) => {
  27. const str = String(val);
  28. const match = str.match(/^(\d+\.?\d*?)0*$/);
  29. return match ? match[1].replace(/\.$/, '') : str;
  30. };
  31. export const ProductList: React.FC<ProductListProps> = ({ products, levelList }) => {
  32. // 按等级分组
  33. const groupedProducts = products.reduce(
  34. (acc, item) => {
  35. const level = item.level || 'D';
  36. if (!acc[level]) acc[level] = [];
  37. acc[level].push(item);
  38. return acc;
  39. },
  40. {} as Record<string, ProductItem[]>
  41. );
  42. // 计算各等级概率
  43. const levelProbabilities =
  44. levelList?.reduce(
  45. (acc, item) => {
  46. acc[item.level] = item.probability;
  47. return acc;
  48. },
  49. {} as Record<string, number>
  50. ) || {};
  51. const renderLevelSection = (level: string, items: ProductItem[]) => {
  52. const config = LEVEL_CONFIG[level] || { title: level, bg: Images.box.detail.productItemD };
  53. const probability = levelProbabilities[level] || items.reduce((sum, i) => sum + (i.probability || 0), 0);
  54. return (
  55. <View key={level} style={styles.levelSection}>
  56. {/* 等级标题 */}
  57. <ImageBackground source={{ uri: Images.box.detail.levelBoxBg }} style={styles.levelHeader} resizeMode="stretch">
  58. <Text style={styles.levelTitle}>{config.title}</Text>
  59. <Text style={styles.levelProbability}>概率: {ignoreRatio0(probability * 100)}%</Text>
  60. </ImageBackground>
  61. {/* 商品网格 */}
  62. <View style={styles.productGrid}>
  63. {items.map((item, index) => (
  64. <View key={item.id || index} style={styles.productItem}>
  65. <ImageBackground source={{ uri: config.bg }} style={styles.productItemBg} resizeMode="stretch">
  66. <View style={styles.productImageBox}>
  67. <Image source={{ uri: item.cover }} style={styles.productImage} contentFit="cover" />
  68. </View>
  69. <Text style={styles.productName} numberOfLines={2}>
  70. {item.name}
  71. </Text>
  72. <Text style={styles.productProbability}>概率: {ignoreRatio0((item.probability || 0) * 100)}%</Text>
  73. </ImageBackground>
  74. </View>
  75. ))}
  76. </View>
  77. </View>
  78. );
  79. };
  80. const levelOrder = ['A', 'B', 'C', 'D'];
  81. return (
  82. <View style={styles.container}>
  83. {/* 标题 */}
  84. <View style={styles.titleBox}>
  85. <Image source={{ uri: Images.box.detail.productTitle }} style={styles.titleImg} contentFit="contain" />
  86. </View>
  87. {levelOrder.map((level) => {
  88. const items = groupedProducts[level];
  89. if (!items || items.length === 0) return null;
  90. return renderLevelSection(level, items);
  91. })}
  92. </View>
  93. );
  94. };
  95. const styles = StyleSheet.create({
  96. container: {
  97. paddingHorizontal: 10,
  98. marginTop: -40,
  99. },
  100. titleBox: {
  101. alignItems: 'center',
  102. marginBottom: 10,
  103. },
  104. titleImg: {
  105. width: 121,
  106. height: 29,
  107. },
  108. levelSection: {
  109. marginBottom: 15,
  110. },
  111. levelHeader: {
  112. height: 40,
  113. flexDirection: 'row',
  114. alignItems: 'center',
  115. justifyContent: 'space-between',
  116. paddingHorizontal: 15,
  117. marginBottom: 10,
  118. },
  119. levelTitle: {
  120. color: '#fff',
  121. fontSize: 16,
  122. fontWeight: 'bold',
  123. },
  124. levelProbability: {
  125. color: 'rgba(255,255,255,0.8)',
  126. fontSize: 12,
  127. },
  128. productGrid: {
  129. flexDirection: 'row',
  130. flexWrap: 'wrap',
  131. marginHorizontal: -5,
  132. },
  133. productItem: {
  134. width: '33.33%',
  135. paddingHorizontal: 5,
  136. marginBottom: 10,
  137. },
  138. productItemBg: {
  139. width: '100%',
  140. aspectRatio: 0.75,
  141. padding: 8,
  142. alignItems: 'center',
  143. },
  144. productImageBox: {
  145. width: '100%',
  146. aspectRatio: 1,
  147. borderRadius: 5,
  148. overflow: 'hidden',
  149. backgroundColor: 'rgba(255,255,255,0.1)',
  150. },
  151. productImage: {
  152. width: '100%',
  153. height: '100%',
  154. },
  155. productName: {
  156. color: '#fff',
  157. fontSize: 10,
  158. marginTop: 5,
  159. textAlign: 'center',
  160. height: 26,
  161. },
  162. productProbability: {
  163. color: '#FBC400',
  164. fontSize: 9,
  165. marginTop: 2,
  166. },
  167. });