bw-expo-app/components/user/generation-records.tsx

317 lines
8.0 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: 跳转到详情页面
console.log('查看记录详情:', record.id);
}
};
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()}
</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',
},
});