ProductSwiper.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import { Image } from 'expo-image';
  2. import React, { useState } from 'react';
  3. import {
  4. Dimensions,
  5. FlatList,
  6. ImageBackground,
  7. StyleSheet,
  8. Text,
  9. TouchableOpacity,
  10. View,
  11. } from 'react-native';
  12. import { Images } from '@/constants/images';
  13. const { width } = Dimensions.get('window');
  14. interface Product {
  15. cover: string;
  16. name: string;
  17. level: string;
  18. quantity?: number;
  19. probability?: string;
  20. }
  21. interface ProductSwiperProps {
  22. products: Product[];
  23. box?: any; // Add box prop
  24. onShowSwipe?: (index: number) => void;
  25. }
  26. export default function ProductSwiper({ products, box, onShowSwipe }: ProductSwiperProps) {
  27. const [current, setCurrent] = useState(0);
  28. const getLeftNum = (item: any) => {
  29. const usedStat = box?.usedStat || box?.used_stat;
  30. if (!box || !usedStat) return item.quantity;
  31. const spuId = String(item.spu?.id || item.spu_id || item.id); // Try to match ID
  32. const itemId = String(item.id);
  33. let used: any = null;
  34. if (Array.isArray(usedStat)) {
  35. used = usedStat.find((u: any) => {
  36. const uId = String(u.spuId || u.spu_id || u.id);
  37. return uId === spuId || uId === itemId;
  38. });
  39. } else {
  40. used = usedStat[spuId] || usedStat[itemId];
  41. }
  42. if (used) {
  43. return item.quantity - (used.quantity || 0);
  44. }
  45. return item.quantity;
  46. };
  47. const getProbability = (item: Product) => {
  48. if (!box || !box.leftQuantity) return item.probability || '0%';
  49. const left = getLeftNum(item);
  50. const prob = (left / box.leftQuantity * 100).toFixed(4);
  51. return parseFloat(prob) === 0 ? '0%' : `${prob}%`;
  52. };
  53. if (!products || products.length === 0) return null;
  54. const renderItem = ({ item, index }: { item: Product; index: number }) => (
  55. <TouchableOpacity
  56. activeOpacity={0.9}
  57. onPress={() => onShowSwipe?.(index)}
  58. style={styles.itemContainer}
  59. >
  60. <View style={styles.imageBox}>
  61. <Image source={{ uri: item.cover }} style={styles.image} contentFit="contain" />
  62. </View>
  63. {/* Details Button/Tag */}
  64. <ImageBackground
  65. source={{ uri: Images.box.detail.detailsBut }}
  66. style={styles.detailsTag}
  67. resizeMode="contain"
  68. >
  69. <View style={styles.detailsContent}>
  70. <Text style={styles.levelText}>
  71. {item.level === 'A' ? '超神款' :
  72. item.level === 'B' ? '欧皇款' :
  73. item.level === 'C' ? '隐藏款' : '普通款'}
  74. </Text>
  75. <Text style={styles.probText}>{getProbability(item)}</Text>
  76. </View>
  77. </ImageBackground>
  78. {/* Name Tag */}
  79. <ImageBackground
  80. source={{ uri: Images.box.detail.nameBg }}
  81. style={styles.nameTag}
  82. resizeMode="stretch"
  83. >
  84. <Text style={styles.nameText} numberOfLines={1}>{item.name}</Text>
  85. </ImageBackground>
  86. </TouchableOpacity>
  87. );
  88. return (
  89. <View style={styles.container}>
  90. <FlatList
  91. data={products}
  92. renderItem={renderItem}
  93. horizontal
  94. pagingEnabled
  95. showsHorizontalScrollIndicator={false}
  96. keyExtractor={(_, index) => index.toString()}
  97. onMomentumScrollEnd={(e) => {
  98. const contentOffset = e.nativeEvent.contentOffset;
  99. const viewSize = e.nativeEvent.layoutMeasurement;
  100. const pageNum = Math.floor(contentOffset.x / viewSize.width);
  101. setCurrent(pageNum);
  102. }}
  103. />
  104. {/* Left/Right Arrows if needed */}
  105. {current > 0 && (
  106. <View style={[styles.arrow, styles.leftArrow]}>
  107. <Image source={{ uri: Images.box.detail.left }} style={styles.arrowImg} contentFit="contain" />
  108. </View>
  109. )}
  110. {current < products.length - 1 && (
  111. <View style={[styles.arrow, styles.rightArrow]}>
  112. <Image source={{ uri: Images.box.detail.right }} style={styles.arrowImg} contentFit="contain" />
  113. </View>
  114. )}
  115. </View>
  116. );
  117. }
  118. const styles = StyleSheet.create({
  119. container: {
  120. width: '100%',
  121. height: 380, // Adjusted height
  122. alignItems: 'center',
  123. marginTop: 20,
  124. },
  125. itemContainer: {
  126. width: width, // Full width for paging
  127. height: '100%',
  128. justifyContent: 'center',
  129. alignItems: 'center',
  130. },
  131. imageBox: {
  132. width: '60%',
  133. height: '60%',
  134. justifyContent: 'center',
  135. alignItems: 'center',
  136. },
  137. image: {
  138. width: '100%',
  139. height: '100%',
  140. },
  141. detailsTag: {
  142. position: 'absolute',
  143. right: 40,
  144. bottom: 80,
  145. width: 100,
  146. height: 60,
  147. justifyContent: 'center',
  148. alignItems: 'center',
  149. },
  150. detailsContent: {
  151. alignItems: 'center',
  152. justifyContent: 'center',
  153. marginTop: 5,
  154. },
  155. levelText: {
  156. color: '#fff',
  157. fontSize: 12,
  158. fontWeight: 'bold',
  159. textShadowColor: '#000',
  160. textShadowOffset: { width: 1, height: 1 },
  161. textShadowRadius: 1,
  162. },
  163. probText: {
  164. color: '#fff',
  165. fontSize: 10,
  166. fontWeight: 'bold',
  167. textShadowColor: '#000',
  168. textShadowOffset: { width: 1, height: 1 },
  169. textShadowRadius: 1,
  170. },
  171. nameTag: {
  172. position: 'absolute',
  173. left: 40,
  174. top: 40,
  175. width: 40,
  176. height: 120,
  177. justifyContent: 'center',
  178. alignItems: 'center',
  179. paddingVertical: 10,
  180. },
  181. nameText: {
  182. color: '#000',
  183. fontSize: 12,
  184. fontWeight: 'bold',
  185. width: 12,
  186. textAlign: 'center',
  187. },
  188. arrow: {
  189. position: 'absolute',
  190. top: '50%',
  191. marginTop: -20,
  192. width: 40,
  193. height: 40,
  194. zIndex: 10,
  195. },
  196. leftArrow: {
  197. left: 10,
  198. },
  199. rightArrow: {
  200. right: 10,
  201. },
  202. arrowImg: {
  203. width: '100%',
  204. height: '100%',
  205. }
  206. });