ProductListYfs.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { Image } from 'expo-image';
  2. import React from 'react';
  3. import { ScrollView, StyleSheet, Text, View } from 'react-native';
  4. interface ProductItem {
  5. id: string;
  6. name: string;
  7. cover: string;
  8. level: string;
  9. probability: number;
  10. price?: number;
  11. quantity?: number;
  12. spu?: {
  13. id: string;
  14. cover: string;
  15. name: string;
  16. };
  17. }
  18. interface BoxData {
  19. leftQuantity: number;
  20. usedStat?: Record<string, { spuId: string; quantity: number }>;
  21. }
  22. interface ProductListYfsProps {
  23. products: ProductItem[];
  24. levelList?: any[];
  25. poolId: string;
  26. price: number;
  27. box?: BoxData | null;
  28. }
  29. // 等级配置
  30. const LEVEL_CONFIG: Record<string, { title: string; color: string; bgColor: string }> = {
  31. A: { title: '超神款', color: '#fff', bgColor: '#FF4444' },
  32. B: { title: '欧皇款', color: '#fff', bgColor: '#FF9900' },
  33. C: { title: '隐藏款', color: '#fff', bgColor: '#9966FF' },
  34. D: { title: '普通款', color: '#fff', bgColor: '#00CCFF' },
  35. };
  36. // 格式化概率,去除末尾的0
  37. const formatProbability = (val: string | number) => {
  38. const num = parseFloat(String(val));
  39. if (isNaN(num)) return '0';
  40. // 保留两位小数,去除末尾的0
  41. return num.toFixed(2).replace(/\.?0+$/, '');
  42. };
  43. export const ProductListYfs: React.FC<ProductListYfsProps> = ({ products, levelList, box }) => {
  44. // 按等级分组
  45. const groupedProducts = products.reduce(
  46. (acc, item) => {
  47. const level = item.level || 'D';
  48. if (!acc[level]) acc[level] = [];
  49. acc[level].push(item);
  50. return acc;
  51. },
  52. {} as Record<string, ProductItem[]>
  53. );
  54. // 获取等级概率
  55. const getLevelProbability = (level: string) => {
  56. const item = levelList?.find((e) => e.level === level);
  57. return item ? `${formatProbability(item.probability)}%` : '0%';
  58. };
  59. // 获取商品剩余数量
  60. const getLeftNum = (item: ProductItem) => {
  61. const spuId = item.spu?.id || item.id;
  62. if (!box?.usedStat || !box.usedStat[spuId]?.quantity) {
  63. return item.quantity || 1;
  64. }
  65. return (item.quantity || 1) - box.usedStat[spuId].quantity;
  66. };
  67. // 获取商品概率
  68. const getProbability = (item: ProductItem) => {
  69. if (!box || box.leftQuantity <= 0) return '0';
  70. const leftNum = getLeftNum(item);
  71. return ((leftNum / box.leftQuantity) * 100).toFixed(2);
  72. };
  73. const renderLevelSection = (level: string, items: ProductItem[]) => {
  74. const config = LEVEL_CONFIG[level] || LEVEL_CONFIG['D'];
  75. return (
  76. <View key={level} style={styles.levelBox}>
  77. {/* 等级标题行 */}
  78. <View style={styles.levelTitleRow}>
  79. <Text style={[styles.levelTitle, { color: config.bgColor }]}>{config.title}</Text>
  80. <View style={styles.levelProportion}>
  81. <Text style={styles.probabilityLabel}>概率:</Text>
  82. <Text style={styles.probabilityValue}>{getLevelProbability(level)}</Text>
  83. </View>
  84. </View>
  85. {/* 商品横向滚动列表 */}
  86. <ScrollView
  87. horizontal
  88. showsHorizontalScrollIndicator={false}
  89. contentContainerStyle={styles.scrollContent}
  90. >
  91. {items.map((item, index) => {
  92. const cover = item.spu?.cover || item.cover;
  93. const leftNum = getLeftNum(item);
  94. const prob = formatProbability(getProbability(item));
  95. return (
  96. <View key={item.id || index} style={styles.productItem}>
  97. {/* 商品图片 */}
  98. <View style={styles.productImageBox}>
  99. <Image
  100. source={{ uri: cover }}
  101. style={styles.productImage}
  102. contentFit="contain"
  103. />
  104. </View>
  105. {/* 等级标签 */}
  106. <View style={[styles.levelTag, { backgroundColor: config.bgColor }]}>
  107. <Text style={styles.levelTagLabel}>概率</Text>
  108. <Text style={styles.levelTagText}>{prob}%</Text>
  109. </View>
  110. {/* 剩余数量 */}
  111. <Text style={styles.leftNumText}>{leftNum}/{item.quantity || 1}</Text>
  112. </View>
  113. );
  114. })}
  115. </ScrollView>
  116. </View>
  117. );
  118. };
  119. const levelOrder = ['A', 'B', 'C', 'D'];
  120. return (
  121. <View style={styles.container}>
  122. {levelOrder.map((level) => {
  123. const items = groupedProducts[level];
  124. if (!items || items.length === 0) return null;
  125. return renderLevelSection(level, items);
  126. })}
  127. </View>
  128. );
  129. };
  130. const styles = StyleSheet.create({
  131. container: {
  132. paddingHorizontal: 10,
  133. },
  134. levelBox: {
  135. marginBottom: 20,
  136. },
  137. levelTitleRow: {
  138. flexDirection: 'row',
  139. alignItems: 'center',
  140. justifyContent: 'center',
  141. paddingHorizontal: 14,
  142. paddingBottom: 10,
  143. position: 'relative',
  144. },
  145. levelTitle: {
  146. fontSize: 18,
  147. fontWeight: 'bold',
  148. textAlign: 'center',
  149. textShadowColor: '#000',
  150. textShadowOffset: { width: 1, height: 1 },
  151. textShadowRadius: 1,
  152. },
  153. levelProportion: {
  154. position: 'absolute',
  155. right: 0,
  156. bottom: 10,
  157. flexDirection: 'row',
  158. alignItems: 'center',
  159. },
  160. probabilityLabel: {
  161. fontSize: 12,
  162. color: '#ffc901',
  163. },
  164. probabilityValue: {
  165. fontSize: 12,
  166. color: '#fff',
  167. },
  168. scrollContent: {
  169. paddingHorizontal: 5,
  170. },
  171. productItem: {
  172. width: 90,
  173. marginRight: 10,
  174. alignItems: 'center',
  175. },
  176. productImageBox: {
  177. width: 90,
  178. height: 90,
  179. backgroundColor: '#fff',
  180. borderRadius: 4,
  181. overflow: 'hidden',
  182. },
  183. productImage: {
  184. width: 90,
  185. height: 90,
  186. },
  187. levelTag: {
  188. width: 80,
  189. height: 26,
  190. borderRadius: 2,
  191. flexDirection: 'row',
  192. justifyContent: 'center',
  193. alignItems: 'center',
  194. marginTop: -8,
  195. },
  196. levelTagLabel: {
  197. fontSize: 10,
  198. color: '#fff',
  199. marginRight: 2,
  200. },
  201. levelTagText: {
  202. fontSize: 12,
  203. color: '#fff',
  204. fontWeight: 'bold',
  205. },
  206. leftNumText: {
  207. fontSize: 10,
  208. color: '#999',
  209. marginTop: 4,
  210. },
  211. });