detail.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import { Image } from "expo-image";
  2. import { useLocalSearchParams, useRouter } from "expo-router";
  3. import React, { useCallback, useEffect, useRef, useState } from "react";
  4. import {
  5. ActivityIndicator,
  6. Alert,
  7. ImageBackground,
  8. StatusBar,
  9. StyleSheet,
  10. Text,
  11. TouchableOpacity,
  12. View,
  13. } from "react-native";
  14. import { useSafeAreaInsets } from "react-native-safe-area-context";
  15. import { Images } from "@/constants/images";
  16. import { convertApply, convertPreview, getLuckDetail } from "@/services/award";
  17. const LEVEL_MAP: Record<string, { title: string; color: string }> = {
  18. A: { title: "超神", color: "#FF3B30" },
  19. B: { title: "欧皇", color: "#FF9500" },
  20. C: { title: "隐藏", color: "#AF52DE" },
  21. D: { title: "普通", color: "#8E8E93" },
  22. };
  23. const FROM_TYPE_MAP: Record<string, string> = {
  24. MALL: "商城",
  25. DISTRIBUTION: "分销",
  26. LUCK: "奖池",
  27. LUCK_PICKUP: "商品提货",
  28. LUCK_EXCHANGE: "商品兑换",
  29. SUBSTITUTE: "商品置换",
  30. TRANSFER: "商品转赠",
  31. LUCK_ROOM: "福利房",
  32. LUCK_WHEEL: "魔天轮",
  33. DOLL_MACHINE: "扭蛋",
  34. RECHARGE: "充值",
  35. OFFICIAL: "官方",
  36. ACTIVITY: "活动",
  37. LUCK_ACTIVITY: "奖池活动",
  38. REDEEM_CODE_ACTIVITY: "兑换码活动",
  39. };
  40. export default function StoreDetailScreen() {
  41. const params = useLocalSearchParams<{ id: string }>();
  42. const id = params?.id;
  43. const router = useRouter();
  44. const insets = useSafeAreaInsets();
  45. const [loading, setLoading] = useState(true);
  46. const [item, setItem] = useState<any>(null);
  47. const [converting, setConverting] = useState(false);
  48. const [error, setError] = useState<string | null>(null);
  49. console.log('[仓库详情] 页面加载, params:', JSON.stringify(params), 'id:', id);
  50. const loadData = useCallback(async () => {
  51. console.log('[仓库详情] loadData 开始, id:', id);
  52. if (!id) {
  53. setError('未获取到商品ID');
  54. setLoading(false);
  55. return;
  56. }
  57. setLoading(true);
  58. setError(null);
  59. try {
  60. const data = await getLuckDetail(id);
  61. console.log('[仓库详情] getLuckDetail 返回:', data ? '有数据' : 'null');
  62. setItem(data);
  63. if (!data) {
  64. setError('商品数据为空');
  65. }
  66. } catch (e: any) {
  67. console.error("[仓库详情] 加载失败:", e);
  68. setError(`加载失败: ${e?.message || '未知错误'}`);
  69. }
  70. setLoading(false);
  71. }, [id]);
  72. useEffect(() => {
  73. loadData();
  74. }, [loadData]);
  75. const handleConvert = async () => {
  76. if (!item || item.magicAmount <= 0) {
  77. Alert.alert("提示", "不可兑换");
  78. return;
  79. }
  80. setConverting(true);
  81. try {
  82. const preview = await convertPreview([item.id]);
  83. if (preview?.data) {
  84. Alert.alert(
  85. "确认兑换",
  86. `将兑换 ${item.spu?.name} 获得 ${preview.data?.totalMagicAmount || item.magicAmount} 果实`,
  87. [
  88. { text: "取消", style: "cancel" },
  89. {
  90. text: "确定",
  91. onPress: async () => {
  92. const success = await convertApply([item.id]);
  93. if (success) {
  94. Alert.alert("提示", "兑换成功", [
  95. { text: "确定", onPress: () => router.back() },
  96. ]);
  97. }
  98. },
  99. },
  100. ],
  101. );
  102. }
  103. } catch (e) {
  104. Alert.alert("提示", "兑换失败");
  105. }
  106. setConverting(false);
  107. };
  108. if (loading) {
  109. return (
  110. <View style={styles.loadingBox}>
  111. <ActivityIndicator size="large" color="#fff" />
  112. </View>
  113. );
  114. }
  115. if (!item) {
  116. return (
  117. <View style={styles.loadingBox}>
  118. <Text style={{ color: "#999", fontSize: 16 }}>{error || '商品不存在'}</Text>
  119. <TouchableOpacity onPress={() => router.back()} style={{ marginTop: 20 }}>
  120. <Text style={{ color: "#fff", fontSize: 14 }}>返回</Text>
  121. </TouchableOpacity>
  122. </View>
  123. );
  124. }
  125. const levelInfo = LEVEL_MAP[item.level] || { title: "其他", color: "#999" };
  126. return (
  127. <View style={styles.container}>
  128. <StatusBar barStyle="light-content" />
  129. <ImageBackground
  130. source={{ uri: Images.mine.kaixinMineBg }}
  131. style={styles.background}
  132. resizeMode="cover"
  133. >
  134. {/* 顶部导航 */}
  135. <View style={[styles.header, { paddingTop: insets.top }]}>
  136. <TouchableOpacity
  137. style={styles.backBtn}
  138. onPress={() => router.back()}
  139. >
  140. <Text style={styles.backText}>←</Text>
  141. </TouchableOpacity>
  142. <Text style={styles.title}>仓库</Text>
  143. <View style={styles.placeholder} />
  144. </View>
  145. {/* 商品信息 */}
  146. <View style={styles.content}>
  147. <View style={styles.detailCard}>
  148. <Image
  149. source={{ uri: item.spu?.cover }}
  150. style={styles.coverImage}
  151. contentFit="contain"
  152. />
  153. <Text style={[styles.levelTag, { color: levelInfo.color }]}>
  154. {levelInfo.title}
  155. </Text>
  156. <Text style={styles.goodsName}>{item.spu?.name}</Text>
  157. <View style={styles.sourceBox}>
  158. <Text style={styles.sourceText}>
  159. 从{FROM_TYPE_MAP[item.fromRelationType] || "其他"}获得
  160. </Text>
  161. </View>
  162. </View>
  163. </View>
  164. {/* 兑换按钮 - 已关闭 */}
  165. </ImageBackground>
  166. </View>
  167. );
  168. }
  169. const styles = StyleSheet.create({
  170. container: { flex: 1, backgroundColor: "#1a1a2e" },
  171. background: { flex: 1 },
  172. loadingBox: {
  173. flex: 1,
  174. backgroundColor: "#1a1a2e",
  175. justifyContent: "center",
  176. alignItems: "center",
  177. },
  178. header: {
  179. flexDirection: "row",
  180. alignItems: "center",
  181. justifyContent: "space-between",
  182. paddingHorizontal: 15,
  183. paddingBottom: 10,
  184. },
  185. backBtn: {
  186. width: 40,
  187. height: 40,
  188. justifyContent: "center",
  189. alignItems: "center",
  190. },
  191. backText: { color: "#fff", fontSize: 20 },
  192. title: { color: "#fff", fontSize: 16, fontWeight: "bold" },
  193. placeholder: { width: 40 },
  194. content: { flex: 1, paddingHorizontal: 20, paddingTop: 20 },
  195. detailCard: {
  196. backgroundColor: "rgba(255,255,255,0.95)",
  197. borderRadius: 16,
  198. padding: 24,
  199. alignItems: "center",
  200. },
  201. coverImage: {
  202. width: 180,
  203. height: 180,
  204. borderRadius: 10,
  205. marginBottom: 16,
  206. },
  207. levelTag: {
  208. fontSize: 16,
  209. fontWeight: "bold",
  210. marginBottom: 8,
  211. textShadowColor: "#000",
  212. textShadowOffset: { width: 1, height: 1 },
  213. textShadowRadius: 0,
  214. },
  215. goodsName: {
  216. fontSize: 16,
  217. color: "#333",
  218. fontWeight: "bold",
  219. textAlign: "center",
  220. marginBottom: 12,
  221. lineHeight: 22,
  222. },
  223. sourceBox: {
  224. backgroundColor: "rgba(0,0,0,0.05)",
  225. borderRadius: 20,
  226. paddingHorizontal: 16,
  227. paddingVertical: 6,
  228. },
  229. sourceText: { fontSize: 13, color: "#666" },
  230. bottomBar: {
  231. paddingHorizontal: 30,
  232. paddingTop: 10,
  233. alignItems: "center",
  234. },
  235. convertBtn: {
  236. width: "80%",
  237. height: 60,
  238. },
  239. convertBtnBg: {
  240. width: "100%",
  241. height: "100%",
  242. justifyContent: "center",
  243. alignItems: "center",
  244. },
  245. convertBtnText: {
  246. color: "#000",
  247. fontSize: 16,
  248. fontWeight: "bold",
  249. },
  250. convertBtnSub: {
  251. color: "#735200",
  252. fontSize: 11,
  253. },
  254. });