GoodsCard.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { Colors } from "@/constants/Colors";
  2. import { GoodsItem } from "@/services/mall";
  3. import { Image } from "expo-image";
  4. import React from "react";
  5. import {
  6. Dimensions,
  7. StyleSheet,
  8. Text,
  9. TouchableOpacity,
  10. View,
  11. } from "react-native";
  12. const { width: SCREEN_WIDTH } = Dimensions.get("window");
  13. const CARD_WIDTH = (SCREEN_WIDTH - 24) / 2; // Slightly more margin
  14. const IMG_BOX_WIDTH = CARD_WIDTH - 2; // Full width minus border
  15. interface GoodsCardProps {
  16. data: GoodsItem;
  17. onPress?: (item: GoodsItem) => void;
  18. }
  19. export function GoodsCard({ data, onPress }: GoodsCardProps) {
  20. return (
  21. <TouchableOpacity activeOpacity={0.8} onPress={() => onPress?.(data)}>
  22. <View style={[styles.card, { width: CARD_WIDTH }]}>
  23. <View style={styles.imgBox}>
  24. <Image
  25. source={
  26. typeof data.cover === "string" ? { uri: data.cover } : data.cover
  27. }
  28. style={styles.image}
  29. contentFit="cover"
  30. />
  31. {/* Cyberpunk Overlay/Border */}
  32. <View style={styles.cyberOverlay} />
  33. </View>
  34. <View style={styles.textBox}>
  35. <Text style={styles.name} numberOfLines={2}>
  36. {data.name}
  37. </Text>
  38. <View style={styles.priceRow}>
  39. <Text style={styles.currency}>¥</Text>
  40. <Text style={styles.price}>{data.price}</Text>
  41. </View>
  42. {data.sellType === 2 && (
  43. <View style={styles.presellTag}>
  44. <Text style={styles.presellText}>预售</Text>
  45. </View>
  46. )}
  47. </View>
  48. {/* Deco corners */}
  49. <View style={styles.cornerTL} />
  50. <View style={styles.cornerBR} />
  51. </View>
  52. </TouchableOpacity>
  53. );
  54. }
  55. const styles = StyleSheet.create({
  56. card: {
  57. backgroundColor: Colors.darkCard,
  58. borderRadius: 4,
  59. marginBottom: 12,
  60. overflow: "hidden",
  61. borderWidth: 1,
  62. borderColor: "rgba(0, 243, 255, 0.3)", // Neon Blue low opacity
  63. paddingBottom: 8,
  64. },
  65. imgBox: {
  66. width: "100%",
  67. aspectRatio: 1,
  68. backgroundColor: "#000",
  69. position: "relative",
  70. },
  71. image: {
  72. width: "100%",
  73. height: "100%",
  74. },
  75. cyberOverlay: {
  76. ...StyleSheet.absoluteFillObject,
  77. borderWidth: 1,
  78. borderColor: "rgba(0, 243, 255, 0.1)",
  79. zIndex: 1,
  80. },
  81. textBox: {
  82. padding: 8,
  83. },
  84. name: {
  85. fontSize: 12,
  86. color: Colors.textPrimary,
  87. lineHeight: 16,
  88. height: 32, // Fixed height for 2 lines
  89. marginBottom: 4,
  90. fontWeight: "500",
  91. },
  92. priceRow: {
  93. flexDirection: "row",
  94. alignItems: "baseline",
  95. },
  96. currency: {
  97. fontSize: 10,
  98. color: Colors.neonBlue,
  99. marginRight: 2,
  100. },
  101. price: {
  102. fontSize: 16,
  103. fontWeight: "bold",
  104. color: Colors.neonBlue,
  105. textShadowColor: "rgba(0, 243, 255, 0.5)",
  106. textShadowOffset: { width: 0, height: 0 },
  107. textShadowRadius: 4,
  108. },
  109. presellTag: {
  110. position: "absolute",
  111. right: 8,
  112. bottom: 0,
  113. backgroundColor: Colors.neonPink,
  114. paddingHorizontal: 6,
  115. paddingVertical: 2,
  116. borderRadius: 2,
  117. },
  118. presellText: {
  119. fontSize: 10,
  120. color: "#fff",
  121. fontWeight: "bold",
  122. },
  123. // Corner decorations
  124. cornerTL: {
  125. position: "absolute",
  126. top: 0,
  127. left: 0,
  128. width: 6,
  129. height: 6,
  130. borderTopWidth: 2,
  131. borderLeftWidth: 2,
  132. borderColor: Colors.neonBlue,
  133. },
  134. cornerBR: {
  135. position: "absolute",
  136. bottom: 0,
  137. right: 0,
  138. width: 6,
  139. height: 6,
  140. borderBottomWidth: 2,
  141. borderRightWidth: 2,
  142. borderColor: Colors.neonBlue,
  143. },
  144. });