import { FullscreenMediaModal } from '@/components/media/fullscreen-media-modal'; import { VideoPlayer } from '@/components/video/video-player'; import { useThemeColor } from '@/hooks/use-theme-color'; import { Template } from '@/lib/types/template'; import { isVideoTemplate } from '@/utils/media-utils'; import { Image } from 'expo-image'; // ResizeMode 兼容映射 const ResizeMode = { CONTAIN: 'contain' as const, COVER: 'cover' as const, STRETCH: 'fill' as const, }; import { useRouter } from 'expo-router'; import { useMemo, useState } from 'react'; import { Platform, StyleSheet, TouchableOpacity, View } from 'react-native'; import { ThemedText } from '../themed-text'; import { ThemedView } from '../themed-view'; interface TemplateCardProps { template: Template; onPress?: (template: Template) => void; allTemplates?: Template[]; currentIndex?: number; onVideoChange?: (template: Template, index: number) => void; } export function TemplateCard({ template, onPress, allTemplates = [], currentIndex = 0, onVideoChange }: TemplateCardProps) { const router = useRouter(); const cardColor = useThemeColor({}, 'card'); const imagePlaceholderColor = useThemeColor({}, 'imagePlaceholder'); const [isMediaFullscreenVisible, setIsMediaFullscreenVisible] = useState(false); const [currentMediaIndex, setCurrentMediaIndex] = useState(currentIndex); const isVideo = useMemo(() => { return isVideoTemplate(template); }, [template]); const getImageHeight = () => { if (template.aspectRatio) { const [width, height] = template.aspectRatio.split(':').map(Number); if (width && height) { return width > height ? 280 : 360; } } return 280; }; const mediaHeight = getImageHeight(); const handleUseTemplate = () => { router.push(`/template/${template.id}/run`); }; const handleFullscreenMedia = () => { setIsMediaFullscreenVisible(true); }; const handleMediaIndexChanged = (newIndex: number) => { setCurrentMediaIndex(newIndex); const newTemplate = allTemplates[newIndex]; onVideoChange?.(newTemplate, newIndex); }; const handleCardPress = () => { if (onPress) { onPress(template); } else { handleFullscreenMedia(); } }; // 获取当前媒体模板 const getCurrentMediaTemplate = () => { return allTemplates[currentMediaIndex] || template; }; const currentTemplate = getCurrentMediaTemplate(); return ( {isVideo ? ( ) : ( )} {template.category && ( {template.category.name} )} {template.tags.length > 0 && ( {template.tags.slice(0, 2).map((tag) => ( {tag.name} ))} )} {/* 操作按钮区域 */} {template.title} 立即使用 {/* 统一全屏媒体模态框 */} setIsMediaFullscreenVisible(false)} currentIndex={currentMediaIndex} templates={allTemplates} onIndexChanged={handleMediaIndexChanged} /> ); } const styles = StyleSheet.create({ card: { flex: 1, marginBottom: 14, marginHorizontal: 5, borderRadius: 16, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.08, shadowRadius: 24, elevation: 4, }, mediaContainer: { width: '100%', }, mediaContent: { position: 'relative', width: '100%', minHeight: 280, }, image: { width: '100%', height: '100%', }, videoPlayer: { width: '100%', height: '100%', }, videoCover: { overflow: 'hidden', width: '100%', height: '100%', // React Native中object-fit对应contentFit属性,但在VideoPlayer中已经处理了 ...Platform.select({ web: { objectFit: 'cover', }, }), }, imageContainer: { width: '100%', height: '100%', }, watermark: { position: 'absolute', top: 12, left: 14, zIndex: 10, }, categoryBadge: { backgroundColor: 'rgba(0, 0, 0, 0.15)', paddingHorizontal: 10, paddingVertical: 6, borderRadius: 100, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.2)', }, categoryText: { color: 'rgba(255, 255, 255, 0.85)', fontSize: 10, fontWeight: '400', lineHeight: 6 }, tagOverlay: { position: 'absolute', bottom: 16, left: 24, right: 24, zIndex: 10, flexDirection: 'row', gap: 8, justifyContent: 'center', }, tagBadge: { backgroundColor: 'rgba(0, 0, 0, 0.3)', paddingHorizontal: 12, paddingVertical: 8, borderRadius: 100, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.3)', backdropFilter: 'blur(5px)', }, tagText: { color: '#fff', fontSize: 11, fontWeight: '500', lineHeight: 4 }, actionArea: { padding: 12, backgroundColor: 'rgba(0, 0, 0, 0.02)', }, templateTitle: { fontSize: 14, fontWeight: '600', marginBottom: 8, textAlign: 'center', }, useButton: { backgroundColor: '#4ECDC4', borderRadius: 8, paddingVertical: 10, alignItems: 'center', shadowColor: '#4ECDC4', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 8, elevation: 3, }, useButtonText: { fontSize: 14, fontWeight: '600', color: '#fff', }, });