diff --git a/src/screens/trade/TradeDetailScreen.tsx b/src/screens/trade/TradeDetailScreen.tsx index f2f29ba..77fd5b4 100644 --- a/src/screens/trade/TradeDetailScreen.tsx +++ b/src/screens/trade/TradeDetailScreen.tsx @@ -1,22 +1,23 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { View, ScrollView, Image, StyleSheet, Pressable, Alert, Modal } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { useAppColors, spacing, borderRadius, fontSizes, shadows, type AppColors } from '../../theme'; -import { Text, Loading, AppBackButton } from '../../components/common'; +import { Text, Loading, AppBackButton, ImageGrid, ImageGallery } from '../../components/common'; import { tradeService } from '../../services/trade/tradeService'; import { messageService } from '../../services/message/messageService'; import { useIsAuthenticated, useCurrentUser } from '../../stores/auth'; import type { TradeItemDetailDTO } from '../../types/trade'; import { TRADE_CONDITION_MAP } from '../../types/trade'; import { hrefChat, hrefEditTrade } from '../../navigation/hrefs'; +import { useResponsive, useResponsiveValue, useResponsiveSpacing } from '../../hooks/useResponsive'; function createStyles(colors: AppColors) { return StyleSheet.create({ container: { flex: 1, - backgroundColor: colors.background.paper, + backgroundColor: colors.background.default, }, header: { flexDirection: 'row', @@ -39,17 +40,6 @@ function createStyles(colors: AppColors) { headerRight: { width: 40, }, - imageContainer: { - width: '100%', - aspectRatio: 1, - backgroundColor: colors.background.paper, - position: 'relative', - }, - image: { - width: '100%', - height: '100%', - resizeMode: 'contain', - }, // 降价标签 discountBadge: { position: 'absolute', @@ -130,7 +120,7 @@ function createStyles(colors: AppColors) { flexDirection: 'row', alignItems: 'flex-start', gap: spacing.sm, - marginBottom: spacing.md, + marginBottom: spacing.lg, }, conditionBadge: { paddingHorizontal: 8, @@ -189,10 +179,9 @@ function createStyles(colors: AppColors) { color: '#FF7875', }, title: { - fontSize: fontSizes['3xl'], - fontWeight: '600', + fontWeight: '800', color: colors.text.primary, - lineHeight: 34, + letterSpacing: -0.3, flex: 1, }, // 商品属性行 @@ -217,12 +206,11 @@ function createStyles(colors: AppColors) { color: colors.text.primary, fontWeight: '500', }, - // 描述 + // 描述 - 与帖子正文同步 contentText: { - fontSize: fontSizes.lg, color: colors.text.primary, - lineHeight: 28, - marginBottom: spacing.md, + letterSpacing: 0.2, + marginBottom: spacing.lg, }, // 底部操作栏 bottomBar: { @@ -362,12 +350,23 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { const [item, setItem] = useState(null); const [loading, setLoading] = useState(true); - const [currentImageIndex, setCurrentImageIndex] = useState(0); const [manageModalVisible, setManageModalVisible] = useState(false); + const [showImageModal, setShowImageModal] = useState(false); + const [selectedImageIndex, setSelectedImageIndex] = useState(0); const isOwner = item?.user_id === currentUser?.id; + // 响应式 hook(与帖子同步) + const { + width, + isMobile, + isTablet, + isDesktop, + isWideScreen, + } = useResponsive(); + const responsivePadding = useResponsiveSpacing({ xs: 12, sm: 14, md: 16, lg: 20, xl: 24, '2xl': 28, '3xl': 32, '4xl': 40 }); + const responsiveGap = useResponsiveSpacing({ xs: 8, sm: 10, md: 12, lg: 16, xl: 20, '2xl': 24, '3xl': 28, '4xl': 32 }); const fetchItem = useCallback(async () => { try { @@ -456,6 +455,28 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { } }, [isAuth, item?.author?.id, router]); + // 响应式图片间距和圆角(与帖子同步) + const imageGap = isDesktop ? 12 : isTablet ? 10 : 8; + const imageBorderRadius = isDesktop ? borderRadius.xl : borderRadius.lg; + + const images = item?.images || []; + + // 转换图片格式给 ImageGrid 使用 + const tradeImages = useMemo(() => { + return images.map(img => ({ + id: img.id, + url: img.url || img.preview_url_large || img.preview_url || '', + thumbnail_url: img.preview_url || img.preview_url_large || img.url || '', + width: img.width, + height: img.height, + })); + }, [images]); + + const handleImagePress = useCallback((allImages: any[], index: number) => { + setSelectedImageIndex(index); + setShowImageModal(true); + }, []); + if (loading) { return ; } @@ -470,9 +491,6 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { ); } - const images = item.images || []; - const currentImage = images[currentImageIndex]; - return ( @@ -484,7 +502,7 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { - + {/* 卖家信息头部 */} {item.author && ( @@ -522,34 +540,56 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { )} - {/* 标题 */} + {/* 标题 - 与帖子同步 */} - {item.title} + + {item.title} + - {/* 商品描述 */} + {/* 商品描述 - 与帖子正文同步 */} {item.content && ( - {item.content} + + {item.content} + )} - {/* 图片放在文字下方 */} - {images.length > 0 && ( - - {images.map((img, idx) => ( - - - - ))} + {/* 图片 - 使用 ImageGrid 与帖子同步 */} + {tradeImages.length > 0 && ( + + )} {/* 内容区右下角属性文字(仅出售商品显示) */} {item.trade_type === 'sell' && ( - + 品牌:{item.brand || '其他'} @@ -617,6 +657,18 @@ export function TradeDetailScreen({ tradeId }: TradeDetailScreenProps) { + + {/* 图片预览 */} + ({ + id: img.id || img.url || String(Math.random()), + url: img.url || '', + }))} + initialIndex={selectedImageIndex} + onClose={() => setShowImageModal(false)} + enableSave + /> ); } \ No newline at end of file