GoodsCard.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { Images } from '@/constants/images';
  2. import { GoodsItem } from '@/services/mall';
  3. import { Image } from 'expo-image';
  4. import React from 'react';
  5. import { Dimensions, ImageBackground, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
  6. const { width: SCREEN_WIDTH } = Dimensions.get('window');
  7. // 小程序: width = (screenWidth - 18) / 2, height = 504rpx = 252pt
  8. const CARD_WIDTH = (SCREEN_WIDTH - 18) / 2;
  9. const CARD_HEIGHT = 252;
  10. // 图片区域: 340rpx = 170pt, 356rpx = 178pt
  11. const IMG_BOX_WIDTH = 170;
  12. const IMG_BOX_HEIGHT = 178;
  13. interface GoodsCardProps {
  14. data: GoodsItem;
  15. onPress?: (item: GoodsItem) => void;
  16. }
  17. export function GoodsCard({ data, onPress }: GoodsCardProps) {
  18. return (
  19. <TouchableOpacity activeOpacity={0.8} onPress={() => onPress?.(data)}>
  20. <ImageBackground
  21. source={{ uri: Images.home.cellGoodsBg }}
  22. style={[styles.cell, { width: CARD_WIDTH }]}
  23. resizeMode="stretch"
  24. >
  25. <View style={[styles.content, { width: CARD_WIDTH - 10 }]}>
  26. <View style={styles.imgBox}>
  27. <Image
  28. source={typeof data.cover === 'string' ? { uri: data.cover } : data.cover}
  29. style={styles.image}
  30. contentFit="contain"
  31. />
  32. {/* 图片边框装饰 - scaled to 70% to match Vue implementation */}
  33. <ImageBackground
  34. source={{ uri: Images.home.imgBoxBorder }}
  35. style={styles.imgBoxBorder}
  36. resizeMode="contain"
  37. />
  38. </View>
  39. <View style={styles.textBox}>
  40. <Text style={styles.name} numberOfLines={1}>
  41. {data.name}
  42. </Text>
  43. <View style={styles.priceRow}>
  44. <Text style={styles.currency}>¥</Text>
  45. <Text style={styles.price}>{data.price}</Text>
  46. {data.sellType === 2 && (
  47. <View style={styles.presellTag}>
  48. <Text style={styles.presellText}>预售</Text>
  49. </View>
  50. )}
  51. </View>
  52. </View>
  53. </View>
  54. </ImageBackground>
  55. </TouchableOpacity>
  56. );
  57. }
  58. const styles = StyleSheet.create({
  59. cell: {
  60. height: CARD_HEIGHT,
  61. marginBottom: 12,
  62. justifyContent: 'center',
  63. alignItems: 'center',
  64. },
  65. content: {
  66. overflow: 'hidden',
  67. borderRadius: 8,
  68. alignItems: 'center',
  69. // Width set inline to CARD_WIDTH - 10
  70. },
  71. imgBox: {
  72. width: IMG_BOX_WIDTH, // Vue uses fixed width 340rpx (170pt)
  73. height: IMG_BOX_HEIGHT,
  74. borderRadius: 7,
  75. overflow: 'hidden',
  76. paddingHorizontal: 6, // Match Vue: padding: 0 12rpx
  77. alignSelf: 'center',
  78. justifyContent: 'center', // Ensure content centers if padding affects it
  79. alignItems: 'center',
  80. },
  81. image: {
  82. width: '100%',
  83. height: '100%',
  84. },
  85. imgBoxBorder: {
  86. position: 'absolute',
  87. width: '85%', // 70% background-size in Vue roughly maps to shrinking this, but let's try 85% first as 70% might be too small if cover is contain. Vue says bg-size 70%.
  88. height: '85%',
  89. alignSelf: 'center',
  90. top: '7.5%', // Center it vertically
  91. },
  92. textBox: {
  93. paddingHorizontal: 9,
  94. marginTop: 6,
  95. },
  96. name: {
  97. fontSize: 12,
  98. color: '#fff',
  99. },
  100. priceRow: {
  101. flexDirection: 'row',
  102. alignItems: 'baseline',
  103. marginTop: 3,
  104. },
  105. currency: {
  106. fontSize: 10,
  107. color: '#fff',
  108. },
  109. price: {
  110. fontSize: 16,
  111. fontWeight: 'bold',
  112. color: '#fff',
  113. },
  114. presellTag: {
  115. width: 64,
  116. height: 22,
  117. lineHeight: 22,
  118. borderRadius: 11,
  119. marginLeft: 8,
  120. justifyContent: 'center',
  121. alignItems: 'center',
  122. backgroundColor: 'rgba(255,255,255,0.2)',
  123. },
  124. presellText: {
  125. fontSize: 12,
  126. color: '#fff',
  127. textAlign: 'center',
  128. },
  129. });