This commit is contained in:
imeepos
2025-10-21 10:21:45 +08:00
parent 7e3f94bae3
commit 3a99ff96d5
28 changed files with 6981 additions and 176 deletions

View File

@@ -1,27 +1,40 @@
import { StyleSheet, TouchableOpacity, View } from 'react-native';
import { Image } from 'expo-image';
import { Video, ResizeMode } from 'expo-av';
import { VideoPlayer } from '@/components/video/video-player';
import { FullscreenMediaModal } from '@/components/media/fullscreen-media-modal';
import { useThemeColor } from '@/hooks/use-theme-color';
import { Template } from '@/lib/types/template';
import { ResizeMode } from 'expo-av';
import { Image } from 'expo-image';
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';
import { Template } from '@/lib/types/template';
import { useThemeColor } from '@/hooks/use-theme-color';
import { useMemo } from 'react';
import { useRouter } from 'expo-router';
import { isVideoTemplate, canLoadMedia } from '@/utils/media-utils';
interface TemplateCardProps {
template: Template;
onPress?: (template: Template) => void;
allTemplates?: Template[];
currentIndex?: number;
onVideoChange?: (template: Template, index: number) => void;
}
export function TemplateCard({ template, onPress }: TemplateCardProps) {
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 /\.(mp4|webm|ogg|mov|avi|mkv|flv)$/i.test(template.previewUrl || '');
}, [template.previewUrl]);
return isVideoTemplate(template);
}, [template]);
const getImageHeight = () => {
if (template.aspectRatio) {
@@ -39,23 +52,36 @@ export function TemplateCard({ template, onPress }: TemplateCardProps) {
router.push(`/template/${template.id}/run`);
};
const handleCardPress = () => {
if (onPress) {
onPress(template);
} else {
// 默认行为:跳转到运行页面
handleUseTemplate();
}
const handleFullscreenMedia = () => {
setIsMediaFullscreenVisible(true);
};
const handleMediaIndexChanged = (newIndex: number) => {
setCurrentMediaIndex(newIndex);
const newTemplate = allTemplates[newIndex];
onVideoChange?.(newTemplate, newIndex);
};
const handleCardPress = () => {
// 统一处理:打开媒体全屏预览
handleFullscreenMedia();
};
// 获取当前媒体模板
const getCurrentMediaTemplate = () => {
return allTemplates[currentMediaIndex] || template;
};
const currentTemplate = getCurrentMediaTemplate();
return (
<View style={[styles.card, { backgroundColor: cardColor }]}>
<View style={styles.mediaContainer}>
<ThemedView style={[styles.mediaContent, { height: mediaHeight }]}>
{isVideo ? (
<VideoPlayer
source={{ uri: template.previewUrl }}
poster={template.coverImageUrl}
source={{ uri: currentTemplate.previewUrl }}
poster={currentTemplate.coverImageUrl}
style={[styles.videoPlayer, styles.videoCover]}
resizeMode={ResizeMode.COVER}
shouldPlay={true}
@@ -120,6 +146,15 @@ export function TemplateCard({ template, onPress }: TemplateCardProps) {
<ThemedText style={styles.useButtonText}>使</ThemedText>
</TouchableOpacity>
</View>
{/* 统一全屏媒体模态框 */}
<FullscreenMediaModal
visible={isMediaFullscreenVisible}
onClose={() => setIsMediaFullscreenVisible(false)}
currentIndex={currentMediaIndex}
templates={allTemplates}
onIndexChanged={handleMediaIndexChanged}
/>
</View>
);
}
@@ -158,6 +193,11 @@ const styles = StyleSheet.create({
width: '100%',
height: '100%',
// React Native中object-fit对应contentFit属性但在VideoPlayer中已经处理了
...Platform.select({
web: {
objectFit: 'cover',
},
}),
},
imageContainer: {
width: '100%',
@@ -181,6 +221,7 @@ const styles = StyleSheet.create({
color: 'rgba(255, 255, 255, 0.85)',
fontSize: 10,
fontWeight: '400',
lineHeight: 6
},
tagOverlay: {
position: 'absolute',
@@ -205,6 +246,7 @@ const styles = StyleSheet.create({
color: '#fff',
fontSize: 11,
fontWeight: '500',
lineHeight: 4
},
actionArea: {
padding: 12,