CouponModal.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import React from "react";
  2. import {
  3. Alert,
  4. Modal,
  5. ScrollView,
  6. StyleSheet,
  7. Text,
  8. TouchableOpacity,
  9. View,
  10. } from "react-native";
  11. import { Colors } from "@/constants/Colors";
  12. import { batchReceiveCoupon, CouponItem } from "@/services/activity";
  13. interface Props {
  14. visible: boolean;
  15. coupons: CouponItem[];
  16. onClose: () => void;
  17. onSuccess: () => void;
  18. }
  19. export function CouponModal({ visible, coupons, onClose, onSuccess }: Props) {
  20. const handleReceive = async () => {
  21. try {
  22. const ids = coupons.map((c) => c.id);
  23. const ok = await batchReceiveCoupon(ids);
  24. if (ok) {
  25. Alert.alert("领取成功", "优惠券已发放到您的账户");
  26. onSuccess();
  27. }
  28. onClose();
  29. } catch (e) {
  30. console.error("领取优惠券失败:", e);
  31. onClose();
  32. }
  33. };
  34. const formatTime = (val: string) => (val ? val.slice(0, 10) : "");
  35. if (!coupons || coupons.length === 0) return null;
  36. return (
  37. <Modal visible={visible} transparent animationType="fade" onRequestClose={onClose}>
  38. <View style={styles.overlay}>
  39. <View style={styles.dialog}>
  40. {/* 标题 */}
  41. <View style={styles.titleRow}>
  42. <Text style={styles.title}>新人大礼包</Text>
  43. <TouchableOpacity onPress={onClose} style={styles.closeBtn}>
  44. <Text style={styles.closeBtnText}>✕</Text>
  45. </TouchableOpacity>
  46. </View>
  47. {/* 优惠券列表 */}
  48. <ScrollView style={styles.scrollArea} showsVerticalScrollIndicator={false}>
  49. {coupons.map((item, index) => (
  50. <View key={item.id || index} style={styles.couponCard}>
  51. <View style={styles.couponLeft}>
  52. <Text style={styles.couponLabel}>福利优惠劵</Text>
  53. </View>
  54. <View style={styles.couponCenter}>
  55. <Text style={styles.couponAmount}>
  56. ¥ <Text style={styles.couponAmountNum}>{item.amount}</Text>
  57. </Text>
  58. </View>
  59. <View style={styles.couponRight}>
  60. <Text style={styles.couponCondition}>
  61. {item.fullAmount > 0 ? `满${item.fullAmount}可用` : "无门槛"}
  62. </Text>
  63. <Text style={styles.couponExpiry}>
  64. {formatTime(item.endTime)}过期
  65. </Text>
  66. </View>
  67. </View>
  68. ))}
  69. </ScrollView>
  70. {/* 一键领取按钮 */}
  71. <TouchableOpacity style={styles.receiveBtn} onPress={handleReceive} activeOpacity={0.8}>
  72. <Text style={styles.receiveBtnText}>一键领取</Text>
  73. </TouchableOpacity>
  74. </View>
  75. </View>
  76. </Modal>
  77. );
  78. }
  79. const styles = StyleSheet.create({
  80. overlay: {
  81. flex: 1,
  82. backgroundColor: "rgba(0,0,0,0.6)",
  83. justifyContent: "center",
  84. alignItems: "center",
  85. },
  86. dialog: {
  87. width: "85%",
  88. maxHeight: "70%",
  89. backgroundColor: Colors.darkCard,
  90. borderRadius: 16,
  91. borderWidth: 1,
  92. borderColor: "rgba(0, 243, 255, 0.4)",
  93. overflow: "hidden",
  94. },
  95. titleRow: {
  96. flexDirection: "row",
  97. justifyContent: "center",
  98. alignItems: "center",
  99. paddingVertical: 16,
  100. paddingHorizontal: 16,
  101. position: "relative",
  102. },
  103. title: {
  104. fontSize: 22,
  105. fontWeight: "bold",
  106. color: "#fff",
  107. textShadowColor: Colors.neonBlue,
  108. textShadowRadius: 8,
  109. },
  110. closeBtn: {
  111. position: "absolute",
  112. right: 16,
  113. top: 12,
  114. width: 28,
  115. height: 28,
  116. borderRadius: 14,
  117. backgroundColor: "rgba(255,255,255,0.1)",
  118. justifyContent: "center",
  119. alignItems: "center",
  120. },
  121. closeBtnText: {
  122. color: "#fff",
  123. fontSize: 14,
  124. },
  125. scrollArea: {
  126. maxHeight: 250,
  127. paddingHorizontal: 16,
  128. },
  129. couponCard: {
  130. flexDirection: "row",
  131. alignItems: "center",
  132. backgroundColor: "rgba(255,255,255,0.05)",
  133. borderRadius: 10,
  134. marginBottom: 10,
  135. padding: 14,
  136. borderWidth: 1,
  137. borderColor: "rgba(0, 243, 255, 0.15)",
  138. },
  139. couponLeft: {
  140. marginRight: 12,
  141. },
  142. couponLabel: {
  143. color: Colors.neonBlue,
  144. fontSize: 11,
  145. fontWeight: "bold",
  146. writingDirection: "ltr",
  147. },
  148. couponCenter: {
  149. marginRight: 12,
  150. },
  151. couponAmount: {
  152. color: "#fff",
  153. fontSize: 14,
  154. fontWeight: "bold",
  155. },
  156. couponAmountNum: {
  157. fontSize: 32,
  158. fontWeight: "800",
  159. color: "#fff",
  160. },
  161. couponRight: {
  162. flex: 1,
  163. },
  164. couponCondition: {
  165. color: "#fff",
  166. fontSize: 13,
  167. fontWeight: "500",
  168. },
  169. couponExpiry: {
  170. color: Colors.textTertiary,
  171. fontSize: 11,
  172. marginTop: 3,
  173. },
  174. receiveBtn: {
  175. marginHorizontal: 16,
  176. marginVertical: 16,
  177. backgroundColor: Colors.neonBlue,
  178. borderRadius: 24,
  179. paddingVertical: 14,
  180. alignItems: "center",
  181. },
  182. receiveBtnText: {
  183. color: "#000",
  184. fontSize: 16,
  185. fontWeight: "bold",
  186. },
  187. });