import { ThemedView } from '@/components/themed-view'; import { StatusBar } from 'expo-status-bar'; import React, { useState, useEffect } from 'react'; import { ActivityIndicator, Image, ScrollView, StyleSheet, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { getTemplateGenerations, TemplateGeneration } from '@/lib/api/template-generations'; interface GroupedData { [date: string]: TemplateGeneration[]; } const groupByDate = (data: TemplateGeneration[]): GroupedData => { const groups: GroupedData = {}; data.forEach(item => { const date = new Date(item.createdAt); const dateKey = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'short' }); if (!groups[dateKey]) { groups[dateKey] = []; } groups[dateKey].push(item); }); return groups; }; const formatDateLabel = (dateStr: string): string => { const date = new Date(dateStr); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); const dateOnly = new Date(date.toDateString()); const todayOnly = new Date(today.toDateString()); const yesterdayOnly = new Date(yesterday.toDateString()); if (dateOnly.getTime() === todayOnly.getTime()) { return 'Today'; } else if (dateOnly.getTime() === yesterdayOnly.getTime()) { return 'Yesterday'; } else { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', weekday: 'short' }); } }; const distributeToColumns = (items: TemplateGeneration[]) => { const leftColumn: TemplateGeneration[] = []; const rightColumn: TemplateGeneration[] = []; let leftHeight = 0; let rightHeight = 0; items.forEach(item => { const height = item.type === 'VIDEO' ? 280 : 240; if (leftHeight <= rightHeight) { leftColumn.push(item); leftHeight += height + 16; } else { rightColumn.push(item); rightHeight += height + 16; } }); return { leftColumn, rightColumn }; }; const getImageHeight = (type: string): number => { switch (type) { case 'VIDEO': return 280; case 'IMAGE': return 240; default: return 200; } }; const getStatusColor = (status: string): string => { switch (status) { case 'completed': return '#4CAF50'; case 'processing': case 'pending': return '#FFA726'; case 'failed': return '#EF5350'; default: return '#9E9E9E'; } }; const getStatusText = (status: string): string => { switch (status) { case 'completed': return '已完成'; case 'processing': case 'pending': return '处理中'; case 'failed': return '失败'; default: return status; } }; export default function HistoryScreen() { const [groupedData, setGroupedData] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { setLoading(true); const response = await getTemplateGenerations({ limit: 100 }); if (response.success && response.data) { const grouped = groupByDate(response.data.generations); setGroupedData(grouped); } else { setError('获取数据失败'); } } catch (err) { setError(err instanceof Error ? err.message : '获取数据失败'); } finally { setLoading(false); } }; fetchData(); }, []); if (loading) { return ( 加载中... ); } if (error) { return ( {error} ); } return ( Content Generation {Object.keys(groupedData).length === 0 ? ( 暂无生成记录 ) : ( Object.entries(groupedData).map(([date, items]) => { const { leftColumn, rightColumn } = distributeToColumns(items); return ( {date} {leftColumn.map(item => ( {item.resultUrl && item.resultUrl.length > 0 ? ( ) : ( {item.type === 'VIDEO' ? '🎬' : item.type === 'IMAGE' ? '🖼️' : '📝'} )} {getStatusText(item.status)} {item.template?.title || '未知模板'} ))} {rightColumn.map(item => ( {item.resultUrl && item.resultUrl.length > 0 ? ( ) : ( {item.type === 'VIDEO' ? '🎬' : item.type === 'IMAGE' ? '🖼️' : '📝'} )} {getStatusText(item.status)} {item.template?.title || '未知模板'} ))} ); }) )} ); } const styles = StyleSheet.create({ canvas: { flex: 1, }, safeArea: { flex: 1, }, scrollContent: { paddingHorizontal: 24, paddingTop: 24, paddingBottom: 48, }, heading: { fontSize: 24, fontWeight: '600', color: '#FFFFFF', textAlign: 'center', letterSpacing: 0.4, }, dateline: { marginTop: 16, marginBottom: 16, fontSize: 16, fontWeight: '500', color: '#EDEDED', }, gallery: { flexDirection: 'row', marginBottom: 24, }, leadingLane: { flex: 1, paddingRight: 12, }, trailingLane: { flex: 1, paddingLeft: 12, }, frame: { width: '100%', borderRadius: 28, marginBottom: 16, overflow: 'hidden', }, image: { width: '100%', borderRadius: 28, }, placeholderImage: { backgroundColor: '#2A2A2A', justifyContent: 'center', alignItems: 'center', }, placeholderText: { fontSize: 48, }, overlay: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: 12, backgroundColor: 'rgba(0, 0, 0, 0.6)', borderBottomLeftRadius: 28, borderBottomRightRadius: 28, }, statusBadge: { flexDirection: 'row', alignItems: 'center', marginBottom: 4, }, statusDot: { width: 8, height: 8, borderRadius: 4, marginRight: 6, }, statusText: { fontSize: 12, color: '#FFFFFF', fontWeight: '500', }, templateName: { fontSize: 13, color: '#FFFFFF', fontWeight: '600', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, loadingText: { marginTop: 16, fontSize: 16, color: '#FFFFFF', }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, }, errorText: { fontSize: 16, color: '#EF5350', textAlign: 'center', }, emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 48, }, emptyText: { fontSize: 16, color: '#9E9E9E', textAlign: 'center', }, dateGroup: { marginBottom: 24, }, });