201 lines
4.7 KiB
TypeScript
201 lines
4.7 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
StyleProp,
|
|
ViewStyle,
|
|
} from 'react-native';
|
|
import { ThemedText } from '@/components/themed-text';
|
|
import { ThemedView } from '@/components/themed-view';
|
|
import { Colors, Spacing, BorderRadius, FontSize, Shadow } from '@/constants/theme';
|
|
|
|
type HistoryStatus = 'running' | 'completed' | 'failed';
|
|
|
|
interface HistoryCardProps {
|
|
templateName: string;
|
|
createdAt: string;
|
|
preview?: string;
|
|
status: HistoryStatus;
|
|
onView?: () => void;
|
|
onDelete?: () => void;
|
|
style?: StyleProp<ViewStyle>;
|
|
}
|
|
|
|
export function HistoryCard({
|
|
templateName,
|
|
createdAt,
|
|
preview,
|
|
status,
|
|
onView,
|
|
onDelete,
|
|
style,
|
|
}: HistoryCardProps) {
|
|
const formatTimeAgo = (dateString: string) => {
|
|
const now = new Date();
|
|
const past = new Date(dateString);
|
|
const diffInMinutes = Math.floor((now.getTime() - past.getTime()) / 60000);
|
|
|
|
if (diffInMinutes < 1) return 'Just now';
|
|
if (diffInMinutes < 60) return `${diffInMinutes} minutes ago`;
|
|
|
|
const diffInHours = Math.floor(diffInMinutes / 60);
|
|
if (diffInHours < 24) return `${diffInHours} hours ago`;
|
|
|
|
const diffInDays = Math.floor(diffInHours / 24);
|
|
if (diffInDays < 30) return `${diffInDays} days ago`;
|
|
|
|
return past.toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
});
|
|
};
|
|
|
|
const getStatusConfig = (status: HistoryStatus) => {
|
|
switch (status) {
|
|
case 'running':
|
|
return {
|
|
color: Colors.brand.secondary,
|
|
text: '进行中',
|
|
bgColor: '#E8F8F7',
|
|
};
|
|
case 'completed':
|
|
return {
|
|
color: Colors.brand.success,
|
|
text: '已完成',
|
|
bgColor: '#E8F5E9',
|
|
};
|
|
case 'failed':
|
|
return {
|
|
color: Colors.brand.danger,
|
|
text: '失败',
|
|
bgColor: '#FFEBEB',
|
|
};
|
|
}
|
|
};
|
|
|
|
const statusConfig = getStatusConfig(status);
|
|
|
|
return (
|
|
<ThemedView style={[styles.container, style]}>
|
|
<View style={styles.header}>
|
|
<Text style={styles.templateName}>{templateName}</Text>
|
|
<Text style={styles.timeText}>{formatTimeAgo(createdAt)}</Text>
|
|
</View>
|
|
|
|
<View style={styles.content}>
|
|
{preview ? (
|
|
<Text style={styles.previewText} numberOfLines={2}>
|
|
{preview}
|
|
</Text>
|
|
) : (
|
|
<View
|
|
style={[
|
|
styles.statusBadge,
|
|
{ backgroundColor: statusConfig.bgColor },
|
|
]}
|
|
>
|
|
<Text style={[styles.statusText, { color: statusConfig.color }]}>
|
|
{statusConfig.text}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
<View style={styles.actions}>
|
|
<TouchableOpacity
|
|
style={styles.viewButton}
|
|
onPress={onView}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={styles.viewButtonText}>查看详情</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={styles.deleteButton}
|
|
onPress={onDelete}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={styles.deleteButtonText}>删除</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</ThemedView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: Colors.background.secondary,
|
|
borderRadius: BorderRadius.md,
|
|
padding: Spacing.md,
|
|
marginVertical: Spacing.sm,
|
|
marginHorizontal: Spacing.md,
|
|
...Shadow.small,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'flex-start',
|
|
marginBottom: Spacing.md,
|
|
},
|
|
templateName: {
|
|
fontSize: FontSize.md,
|
|
fontWeight: '600',
|
|
color: Colors.text.primary,
|
|
flex: 1,
|
|
marginRight: Spacing.sm,
|
|
},
|
|
timeText: {
|
|
fontSize: FontSize.xs,
|
|
color: Colors.text.tertiary,
|
|
textAlign: 'right',
|
|
},
|
|
content: {
|
|
marginBottom: Spacing.md,
|
|
},
|
|
previewText: {
|
|
fontSize: FontSize.sm,
|
|
color: Colors.text.secondary,
|
|
lineHeight: 20,
|
|
},
|
|
statusBadge: {
|
|
paddingHorizontal: Spacing.md,
|
|
paddingVertical: 6,
|
|
borderRadius: BorderRadius.full,
|
|
alignSelf: 'flex-start',
|
|
},
|
|
statusText: {
|
|
fontSize: FontSize.sm,
|
|
fontWeight: '500',
|
|
},
|
|
actions: {
|
|
flexDirection: 'row',
|
|
gap: Spacing.sm,
|
|
},
|
|
viewButton: {
|
|
flex: 1,
|
|
paddingVertical: 10,
|
|
paddingHorizontal: Spacing.md,
|
|
borderRadius: BorderRadius.sm,
|
|
borderWidth: 1,
|
|
borderColor: Colors.brand.primary,
|
|
alignItems: 'center',
|
|
},
|
|
viewButtonText: {
|
|
color: Colors.brand.primary,
|
|
fontSize: FontSize.sm,
|
|
fontWeight: '500',
|
|
},
|
|
deleteButton: {
|
|
paddingVertical: 10,
|
|
paddingHorizontal: Spacing.md,
|
|
alignItems: 'center',
|
|
},
|
|
deleteButtonText: {
|
|
color: Colors.brand.danger,
|
|
fontSize: FontSize.sm,
|
|
fontWeight: '500',
|
|
},
|
|
});
|