320 lines
8.1 KiB
TypeScript
320 lines
8.1 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { StyleSheet, ScrollView, TouchableOpacity, RefreshControl, Alert } from 'react-native';
|
|
|
|
import { ThemedView } from '@/components/themed-view';
|
|
import { ThemedText } from '@/components/themed-text';
|
|
import { useThemeColor } from '@/hooks/use-theme-color';
|
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
import { GenerationRecord } from '@/lib/auth/types';
|
|
|
|
interface GenerationRecordsProps {
|
|
userId?: string;
|
|
}
|
|
|
|
const mockRecords: GenerationRecord[] = [
|
|
{
|
|
id: '1',
|
|
userId: 'user1',
|
|
type: 'image',
|
|
title: '夏日风景插画',
|
|
description: '生成了一张充满夏日气息的风景插画',
|
|
thumbnail: undefined,
|
|
mediaUrl: undefined,
|
|
status: 'completed',
|
|
cost: 0.5,
|
|
createdAt: new Date('2024-01-15T10:30:00'),
|
|
updatedAt: new Date('2024-01-15T10:35:00'),
|
|
},
|
|
{
|
|
id: '2',
|
|
userId: 'user1',
|
|
type: 'video',
|
|
title: '产品介绍视频',
|
|
description: '为新产品制作了一个介绍视频',
|
|
thumbnail: undefined,
|
|
mediaUrl: undefined,
|
|
status: 'pending',
|
|
cost: 2.0,
|
|
createdAt: new Date('2024-01-14T15:20:00'),
|
|
updatedAt: new Date('2024-01-14T15:20:00'),
|
|
},
|
|
];
|
|
|
|
export function GenerationRecords({ userId }: GenerationRecordsProps) {
|
|
const [records, setRecords] = useState<GenerationRecord[]>(mockRecords);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
|
|
const tintColor = useThemeColor({}, 'tint');
|
|
|
|
const getTypeIcon = (type: string) => {
|
|
switch (type) {
|
|
case 'image':
|
|
return 'photo';
|
|
case 'video':
|
|
return 'video';
|
|
case 'text':
|
|
return 'doc.text';
|
|
default:
|
|
return 'doc';
|
|
}
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'completed':
|
|
return '#34C759';
|
|
case 'pending':
|
|
return '#FF9500';
|
|
case 'failed':
|
|
return '#FF3B30';
|
|
default:
|
|
return '#999';
|
|
}
|
|
};
|
|
|
|
const getStatusText = (status: string) => {
|
|
switch (status) {
|
|
case 'completed':
|
|
return '已完成';
|
|
case 'pending':
|
|
return '生成中';
|
|
case 'failed':
|
|
return '失败';
|
|
default:
|
|
return '未知';
|
|
}
|
|
};
|
|
|
|
const handleRefresh = async () => {
|
|
setRefreshing(true);
|
|
try {
|
|
// TODO: 实现刷新逻辑
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
} catch (error) {
|
|
console.error('刷新失败:', error);
|
|
} finally {
|
|
setRefreshing(false);
|
|
}
|
|
};
|
|
|
|
const handleDelete = (recordId: string) => {
|
|
Alert.alert(
|
|
'删除记录',
|
|
'确定要删除这条生成记录吗?此操作无法撤销。',
|
|
[
|
|
{ text: '取消', style: 'cancel' },
|
|
{
|
|
text: '删除',
|
|
style: 'destructive',
|
|
onPress: () => {
|
|
setRecords(prev => prev.filter(record => record.id !== recordId));
|
|
}
|
|
}
|
|
]
|
|
);
|
|
};
|
|
|
|
const handlePress = (record: GenerationRecord) => {
|
|
if (record.status === 'completed') {
|
|
// TODO: 跳转到详情页面
|
|
}
|
|
};
|
|
|
|
if (records.length === 0) {
|
|
return (
|
|
<ThemedView style={styles.container}>
|
|
<ThemedView style={styles.header}>
|
|
<IconSymbol name="clock" size={20} color={tintColor} />
|
|
<ThemedText style={styles.title}>生成记录</ThemedText>
|
|
</ThemedView>
|
|
<ThemedView style={styles.emptyContainer}>
|
|
<IconSymbol name="doc.text" size={48} color="#ccc" />
|
|
<ThemedText style={styles.emptyText}>暂无生成记录</ThemedText>
|
|
<ThemedText style={styles.emptySubText}>开始创建你的第一个作品吧</ThemedText>
|
|
</ThemedView>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ThemedView style={styles.container}>
|
|
<ThemedView style={styles.header}>
|
|
<IconSymbol name="clock" size={20} color={tintColor} />
|
|
<ThemedText style={styles.title}>生成记录</ThemedText>
|
|
<ThemedText style={styles.count}>({records.length})</ThemedText>
|
|
</ThemedView>
|
|
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
refreshControl={
|
|
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
|
}
|
|
>
|
|
{records.map((record) => (
|
|
<TouchableOpacity
|
|
key={record.id}
|
|
style={styles.recordItem}
|
|
onPress={() => handlePress(record)}
|
|
activeOpacity={0.7}
|
|
>
|
|
<ThemedView style={styles.recordLeft}>
|
|
<ThemedView style={styles.typeIcon}>
|
|
<IconSymbol
|
|
name={getTypeIcon(record.type)}
|
|
size={20}
|
|
color={tintColor}
|
|
/>
|
|
</ThemedView>
|
|
<ThemedView style={styles.recordContent}>
|
|
<ThemedText style={styles.recordTitle}>{record.title}</ThemedText>
|
|
{record.description && (
|
|
<ThemedText style={styles.recordDescription} numberOfLines={2}>
|
|
{record.description}
|
|
</ThemedText>
|
|
)}
|
|
<ThemedView style={styles.recordMeta}>
|
|
<ThemedText style={styles.recordTime}>
|
|
{new Date(record.createdAt).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
})}
|
|
</ThemedText>
|
|
<ThemedText style={styles.recordCost}>¥{record.cost}</ThemedText>
|
|
</ThemedView>
|
|
</ThemedView>
|
|
</ThemedView>
|
|
|
|
<ThemedView style={styles.recordRight}>
|
|
<ThemedView
|
|
style={[
|
|
styles.statusBadge,
|
|
{ backgroundColor: getStatusColor(record.status) + '20' }
|
|
]}
|
|
>
|
|
<ThemedText
|
|
style={[
|
|
styles.statusText,
|
|
{ color: getStatusColor(record.status) }
|
|
]}
|
|
>
|
|
{getStatusText(record.status)}
|
|
</ThemedText>
|
|
</ThemedView>
|
|
<TouchableOpacity
|
|
style={styles.deleteButton}
|
|
onPress={() => handleDelete(record.id)}
|
|
>
|
|
<IconSymbol name="trash" size={16} color="#FF3B30" />
|
|
</TouchableOpacity>
|
|
</ThemedView>
|
|
</TouchableOpacity>
|
|
))}
|
|
</ScrollView>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
marginBottom: 16,
|
|
},
|
|
title: {
|
|
fontSize: 18,
|
|
fontWeight: '600',
|
|
},
|
|
count: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
},
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
recordItem: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#f0f0f0',
|
|
},
|
|
recordLeft: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
gap: 12,
|
|
},
|
|
typeIcon: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 8,
|
|
backgroundColor: '#f0f0f0',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
recordContent: {
|
|
flex: 1,
|
|
},
|
|
recordTitle: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
marginBottom: 4,
|
|
},
|
|
recordDescription: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
marginBottom: 4,
|
|
},
|
|
recordMeta: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
recordTime: {
|
|
fontSize: 12,
|
|
color: '#999',
|
|
},
|
|
recordCost: {
|
|
fontSize: 12,
|
|
color: '#666',
|
|
fontWeight: '500',
|
|
},
|
|
recordRight: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
},
|
|
statusBadge: {
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 4,
|
|
borderRadius: 12,
|
|
},
|
|
statusText: {
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
},
|
|
deleteButton: {
|
|
padding: 8,
|
|
},
|
|
emptyContainer: {
|
|
alignItems: 'center',
|
|
paddingVertical: 40,
|
|
},
|
|
emptyText: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
marginTop: 16,
|
|
marginBottom: 8,
|
|
},
|
|
emptySubText: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
},
|
|
});
|