- 删除DownloadSection组件及其相关CSS,简化代码结构 - 在历史页面中添加生成中任务的进度计算和显示功能 - 更新历史页面样式,增强用户体验和视觉效果 - 添加缩略图毛玻璃蒙版和进度指示器,提升信息传达的清晰度
194 lines
6.8 KiB
TypeScript
194 lines
6.8 KiB
TypeScript
import { Image, ScrollView, Text, View } from '@tarojs/components';
|
||
import Taro, { navigateTo } from '@tarojs/taro';
|
||
import { useEffect, useState } from 'react';
|
||
import { useServerSdk } from '../../hooks/index';
|
||
import './index.css';
|
||
|
||
export default function History() {
|
||
const [records, setRecords] = useState<any[]>([]);
|
||
const [refreshing, setRefreshing] = useState(false);
|
||
const [loading, setLoading] = useState(true);
|
||
const serverSdk = useServerSdk();
|
||
|
||
// 计算生成中任务的进度
|
||
const calculateProgress = (record: any) => {
|
||
if (!record?.createdAt || record.status !== 'processing') return 0;
|
||
|
||
const createdAt = new Date(record.createdAt).getTime();
|
||
const now = Date.now();
|
||
const elapsedMinutes = (now - createdAt) / (1000 * 60);
|
||
|
||
// 根据任务类型确定预计时间
|
||
const isVideo = record.taskType === 'video' || record.outputType === 'video' || record.type === 'video';
|
||
const estimatedTime = isVideo ? 2.5 : 1; // 视频2.5分钟,图片1分钟
|
||
|
||
// 计算进度百分比,最大95%
|
||
const calculatedProgress = Math.min((elapsedMinutes / estimatedTime) * 100, 95);
|
||
return Math.max(calculatedProgress, 0);
|
||
};
|
||
|
||
const loadRecords = async () => {
|
||
try {
|
||
const logs = await serverSdk.getMineLogs();
|
||
setRecords(logs || []);
|
||
} catch (error) {
|
||
console.error('加载记录失败:', error);
|
||
Taro.showToast({
|
||
title: '加载记录失败',
|
||
icon: 'error',
|
||
duration: 2000,
|
||
});
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
loadRecords();
|
||
}, []);
|
||
|
||
// 定时更新生成中任务的进度
|
||
useEffect(() => {
|
||
const hasGeneratingTasks = records.some(record => record.status === 'processing');
|
||
if (!hasGeneratingTasks) return;
|
||
|
||
const interval = setInterval(() => {
|
||
// 触发重新渲染以更新进度
|
||
setRecords(prevRecords => [...prevRecords]);
|
||
}, 1000);
|
||
|
||
return () => clearInterval(interval);
|
||
}, [records]);
|
||
|
||
// 下拉刷新
|
||
const handleRefresh = async () => {
|
||
setRefreshing(true);
|
||
try {
|
||
await loadRecords();
|
||
Taro.showToast({
|
||
title: '刷新成功',
|
||
icon: 'success',
|
||
duration: 1500,
|
||
});
|
||
} catch (error) {
|
||
console.error('刷新失败:', error);
|
||
Taro.showToast({
|
||
title: '刷新失败',
|
||
icon: 'error',
|
||
duration: 1500,
|
||
});
|
||
} finally {
|
||
setRefreshing(false);
|
||
}
|
||
};
|
||
|
||
// 点击历史记录项
|
||
const handleItemClick = (record: any) => {
|
||
if (record.status === 'completed' && record.outputUrl) {
|
||
// 跳转到结果页面查看
|
||
navigateTo({
|
||
url: `/pages/result/index?taskId=${record.executionId}`,
|
||
});
|
||
} else if (record.status === 'failed') {
|
||
// 显示错误信息
|
||
Taro.showModal({
|
||
title: '处理失败',
|
||
content: record.errorMessage || '图片处理失败,请重试',
|
||
showCancel: false,
|
||
});
|
||
} else if (record.status === 'processing') {
|
||
// 跳转到生成页面查看进度
|
||
navigateTo({
|
||
url: `/pages/result/index?taskId=${record.executionId}`,
|
||
});
|
||
}
|
||
};
|
||
|
||
const formatTime = (timeStr: string) => {
|
||
const date = new Date(timeStr);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
};
|
||
|
||
return (
|
||
<View className="history">
|
||
<ScrollView
|
||
className="history-list"
|
||
scrollY
|
||
refresherEnabled
|
||
refresherTriggered={refreshing}
|
||
onRefresherRefresh={handleRefresh}
|
||
refresherBackground="transparent"
|
||
refresherDefaultStyle="white"
|
||
>
|
||
<View className="history-grid">
|
||
{loading ? (
|
||
<View className="loading-state">
|
||
<Text className="loading-text">加载中...</Text>
|
||
</View>
|
||
) : records.length === 0 ? (
|
||
<View className="empty-state">
|
||
<Text className="empty-icon">✨</Text>
|
||
<Text className="empty-text">暂无历史记录</Text>
|
||
<Text className="empty-desc">开始创作你的第一个作品吧!{'\n'}所有的处理记录都会保存在这里</Text>
|
||
</View>
|
||
) : (
|
||
records.map(record => {
|
||
const progress = calculateProgress(record);
|
||
const isGenerating = record.status === 'processing';
|
||
|
||
return (
|
||
<View key={record.id} className="history-item" onClick={() => handleItemClick(record)}>
|
||
<View className="item-thumbnail">
|
||
{record.status === 'completed' && record.outputResult ? (
|
||
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
||
) : (
|
||
<View className={`thumbnail-placeholder ${record.status}`}>
|
||
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
||
{/* 生成中状态的缩略图毛玻璃蒙版 */}
|
||
{isGenerating && (
|
||
<View className="thumbnail-overlay">
|
||
<View className="thumbnail-glassmorphism" />
|
||
<View className="thumbnail-progress">
|
||
<View className="thumbnail-progress-circle">
|
||
<View
|
||
className="thumbnail-progress-fill"
|
||
style={{
|
||
background: `conic-gradient(#ff9500 ${progress * 3.6}deg, transparent 0deg)`,
|
||
}}
|
||
/>
|
||
<View className="thumbnail-progress-inner">
|
||
<Text className="thumbnail-progress-text">{Math.round(progress)}%</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
)}
|
||
</View>
|
||
|
||
<View className="item-content">
|
||
<View className="item-header">
|
||
<Text className="item-title">{record.templateName}</Text>
|
||
</View>
|
||
<View className="item-info">
|
||
<Text className="item-time">{formatTime(record.createdAt)}</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
);
|
||
})
|
||
)}
|
||
</View>
|
||
</ScrollView>
|
||
</View>
|
||
);
|
||
}
|