BoxPopup.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { Image } from 'expo-image';
  2. import React, { forwardRef, useImperativeHandle, useState } from 'react';
  3. import { Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
  4. import { Images } from '@/constants/images';
  5. interface BoxItem {
  6. boxNumber: number;
  7. leftQuantity: number;
  8. quantity: number;
  9. isCompleted: boolean;
  10. }
  11. interface BoxPopupProps {
  12. onSelect: (item: BoxItem) => void;
  13. }
  14. export interface BoxPopupRef {
  15. open: (list: BoxItem[]) => void;
  16. close: () => void;
  17. }
  18. export const BoxPopup = forwardRef<BoxPopupRef, BoxPopupProps>(({ onSelect }, ref) => {
  19. const [visible, setVisible] = useState(false);
  20. const [list, setList] = useState<BoxItem[]>([]);
  21. useImperativeHandle(ref, () => ({
  22. open: (data: BoxItem[]) => {
  23. setList(data);
  24. setVisible(true);
  25. },
  26. close: () => setVisible(false),
  27. }));
  28. const handleSelect = (item: BoxItem) => {
  29. onSelect(item);
  30. setVisible(false);
  31. };
  32. return (
  33. <Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
  34. <View style={styles.overlay}>
  35. <View style={styles.container}>
  36. {/* 标题区域 */}
  37. <View style={styles.titleSection}>
  38. <View style={styles.titleBg}>
  39. <Text style={styles.titleText}>选择盒子</Text>
  40. </View>
  41. <TouchableOpacity style={styles.closeBtn} onPress={() => setVisible(false)}>
  42. <Text style={styles.closeBtnText}>×</Text>
  43. </TouchableOpacity>
  44. </View>
  45. {/* 盒子列表 */}
  46. <View style={styles.content}>
  47. <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
  48. <View style={styles.grid}>
  49. {list.map((item, index) => (
  50. <TouchableOpacity key={index} style={styles.item} onPress={() => handleSelect(item)}>
  51. <View style={styles.imgBox}>
  52. <Image
  53. source={{ uri: item.isCompleted ? Images.box.detail.packagingBox2 : Images.box.detail.packagingBox1 }}
  54. style={styles.boxImg}
  55. contentFit="contain"
  56. />
  57. </View>
  58. <View style={styles.numBadge}>
  59. <Text style={styles.numText}>{item.boxNumber}箱</Text>
  60. </View>
  61. <Text style={styles.remaining}>
  62. {item.leftQuantity}/{item.quantity}
  63. </Text>
  64. </TouchableOpacity>
  65. ))}
  66. </View>
  67. </ScrollView>
  68. </View>
  69. </View>
  70. </View>
  71. </Modal>
  72. );
  73. });
  74. const styles = StyleSheet.create({
  75. overlay: {
  76. flex: 1,
  77. backgroundColor: 'rgba(0,0,0,0.5)',
  78. justifyContent: 'flex-end',
  79. },
  80. container: {
  81. backgroundColor: '#ffc901',
  82. borderTopLeftRadius: 10,
  83. borderTopRightRadius: 10,
  84. maxHeight: '80%',
  85. },
  86. titleSection: {
  87. position: 'relative',
  88. alignItems: 'center',
  89. paddingVertical: 15,
  90. },
  91. titleBg: {
  92. backgroundColor: '#ff8c16',
  93. paddingHorizontal: 40,
  94. paddingVertical: 10,
  95. borderRadius: 20,
  96. },
  97. titleText: {
  98. color: '#fff',
  99. fontSize: 16,
  100. fontWeight: 'bold',
  101. },
  102. closeBtn: {
  103. position: 'absolute',
  104. right: 15,
  105. top: 10,
  106. width: 30,
  107. height: 30,
  108. justifyContent: 'center',
  109. alignItems: 'center',
  110. },
  111. closeBtnText: {
  112. fontSize: 24,
  113. color: '#333',
  114. fontWeight: 'bold',
  115. },
  116. content: {
  117. backgroundColor: '#fff',
  118. marginHorizontal: 10,
  119. marginBottom: 20,
  120. borderRadius: 13,
  121. borderWidth: 1,
  122. borderColor: '#000',
  123. maxHeight: 470,
  124. },
  125. scrollView: {
  126. padding: 10,
  127. },
  128. grid: {
  129. flexDirection: 'row',
  130. flexWrap: 'wrap',
  131. },
  132. item: {
  133. width: '25%',
  134. alignItems: 'center',
  135. marginBottom: 15,
  136. },
  137. imgBox: {
  138. width: 54,
  139. height: 54,
  140. },
  141. boxImg: {
  142. width: '100%',
  143. height: '100%',
  144. },
  145. numBadge: {
  146. backgroundColor: '#959595',
  147. borderRadius: 15,
  148. paddingHorizontal: 10,
  149. paddingVertical: 2,
  150. marginTop: 5,
  151. },
  152. numText: {
  153. color: '#fff',
  154. fontSize: 12,
  155. fontWeight: '500',
  156. },
  157. remaining: {
  158. fontSize: 12,
  159. color: '#999',
  160. fontWeight: 'bold',
  161. marginTop: 3,
  162. },
  163. });