refactor: 移除下载部分组件及样式,优化项目结构

- 删除DownloadSection组件及其相关CSS,简化代码结构
- 在历史页面中添加生成中任务的进度计算和显示功能
- 更新历史页面样式,增强用户体验和视觉效果
- 添加缩略图毛玻璃蒙版和进度指示器,提升信息传达的清晰度
This commit is contained in:
iHeyTang
2025-09-11 15:24:35 +08:00
parent e0b0c6e533
commit 64df42678f
5 changed files with 167 additions and 171 deletions

View File

@@ -1,74 +0,0 @@
.download-section {
width: 100%;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 0 40px;
}
.download-btn {
width: 80%;
height: 100px;
background: linear-gradient(45deg, #ff6b6b, #ee5a24);
color: white;
border: none;
border-radius: 50px;
font-size: 32px;
font-weight: bold;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.2s;
}
.download-btn:active {
opacity: 0.8;
}
.download-btn.disabled {
background: #ccc;
color: #666;
cursor: not-allowed;
}
.download-btn.disabled:active {
opacity: 1;
}
.download-btn.watched {
background: linear-gradient(45deg, #52c41a, #73d13d);
}
.download-btn.watched:active {
opacity: 0.8;
}
.regenerate-btn {
width: 80%;
height: 100px;
background: linear-gradient(45deg, #52c41a, #73d13d);
color: white;
border: none;
border-radius: 50px;
font-size: 32px;
font-weight: bold;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.2s;
}
.regenerate-btn:active {
opacity: 0.8;
}
.download-tip {
font-size: 24px;
opacity: 0.8;
}

View File

@@ -1,61 +0,0 @@
import { View, Text } from '@tarojs/components';
import './index.css';
interface DownloadSectionProps {
onDownload: () => void;
onRegenerate?: () => void;
onImageToVideo?: () => void;
loading: boolean;
hasWatchedAd?: boolean;
adAvailable?: boolean;
showImageToVideo?: boolean;
imageToVideoLoading?: boolean;
}
const DownloadSection: React.FC<DownloadSectionProps> = ({
onDownload,
onRegenerate,
onImageToVideo,
loading,
adAvailable = true,
showImageToVideo = false,
imageToVideoLoading = false,
}) => {
const getButtonText = () => {
if (loading) {
return '广告加载中...';
}
if (!adAvailable) {
return '📱 免费下载';
}
return '🎬 看广告下载';
};
const getTipText = () => {
if (!adAvailable) {
return '🎁 已为您跳过广告,可以直接免费下载';
}
return '观看完整广告即可免费下载所有图片';
};
return (
<View className="download-section">
<View className={`download-btn ${loading ? 'disabled' : ''} ${!adAvailable ? 'watched' : ''}`} onClick={loading ? undefined : onDownload}>
<Text>{getButtonText()}</Text>
</View>
{onRegenerate && (
<View className="regenerate-btn" onClick={onRegenerate}>
<Text>🎨 </Text>
</View>
)}
{showImageToVideo && onImageToVideo && (
<View className={`regenerate-btn ${imageToVideoLoading ? 'disabled' : ''}`} onClick={imageToVideoLoading ? undefined : onImageToVideo}>
<Text>{imageToVideoLoading ? '🎬 生成中...' : '🎬 图片生视频'}</Text>
</View>
)}
<Text className="download-tip">{getTipText()}</Text>
</View>
);
};
export default DownloadSection;

View File

@@ -274,3 +274,92 @@
font-weight: 500;
letter-spacing: 0.5px;
}
/* 缩略图毛玻璃蒙版 */
.thumbnail-overlay {
position: absolute;
inset: 0;
border-radius: 12px;
overflow: hidden;
z-index: 10;
pointer-events: none;
}
.thumbnail-glassmorphism {
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgb(255 255 255 / 80%) 0%, rgb(255 255 255 / 70%) 50%, rgb(255 255 255 / 85%) 100%);
backdrop-filter: blur(6px);
}
.thumbnail-progress {
position: relative;
z-index: 2;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.thumbnail-progress-circle {
width: 100px;
height: 100px;
border-radius: 50%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
animation: pulse 2s ease-in-out infinite;
}
.thumbnail-progress-fill {
position: absolute;
inset: 0;
border-radius: 50%;
background: conic-gradient(#ff9500 0deg, transparent 0deg);
transition: all 0.3s ease;
mask: radial-gradient(circle at center, transparent 42px, black 45px);
}
.thumbnail-progress-inner {
width: 90px;
height: 90px;
border-radius: 50%;
background: transparent;
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 2;
}
.thumbnail-progress-text {
font-size: 28px;
color: #ff9500;
text-shadow: 0 1px 2px rgb(0 0 0 / 20%);
}
/* 动画效果 */
@keyframes pulse {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.8;
}
}
@keyframes shimmer {
0% {
background-position: -200px 0;
}
100% {
background-position: calc(200px + 100%) 0;
}
}

View File

@@ -10,10 +10,26 @@ export default function History() {
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();
console.log(logs);
setRecords(logs || []);
} catch (error) {
console.error('加载记录失败:', error);
@@ -31,6 +47,19 @@ export default function History() {
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);
@@ -110,7 +139,11 @@ export default function History() {
<Text className="empty-desc">{'\n'}</Text>
</View>
) : (
records.map(record => (
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 ? (
@@ -118,6 +151,25 @@ export default function History() {
) : (
<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>
@@ -125,24 +177,14 @@ export default function History() {
<View className="item-content">
<View className="item-header">
<Text className="item-title">{record.templateName}</Text>
<View className="item-right">
{/* 添加状态指示器 */}
{record.status === 'generating' && (
<View className="loading-indicator">
<Text className="loading-dot"></Text>
<Text className="loading-dot"></Text>
<Text className="loading-dot"></Text>
</View>
)}
</View>
</View>
<View className="item-info">
<Text className="item-time">{formatTime(record.createdAt)}</Text>
</View>
</View>
</View>
))
);
})
)}
</View>
</ScrollView>

View File

@@ -18,7 +18,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
onClose: isEnded => {
if (!isEnded) {
showToast({
title: '请观看完整广告才能下载',
title: '请观看完整广告才能保存',
icon: 'none',
duration: 2000,
});
@@ -48,7 +48,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
finalExtension = task?.type === 'video' ? 'mp4' : 'jpg';
}
showToast({ title: '下载中...', icon: 'loading', duration: 1000 * 60 });
showToast({ title: '保存中...', icon: 'loading', duration: 1000 * 60 });
const downloadRes = await downloadFile({
url: mediaUrl,
header: {
@@ -98,7 +98,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
} else if (err.errMsg.includes('file')) {
errorMsg = '文件格式不支持或已损坏';
} else if (err.errMsg.includes('download')) {
errorMsg = '文件下载失败,请重试';
errorMsg = '文件保存失败,请重试';
}
}
}
@@ -117,9 +117,9 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
return '广告加载中...';
}
if (!adAvailable) {
return '免费下载';
return '免费保存';
}
return '观看广告下载';
return '观看广告并保存到相册';
};
const getTipText = () => {
@@ -127,9 +127,9 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
return '正在加载广告...';
}
if (!adAvailable) {
return '🎉 恭喜!无需观看广告即可免费下载';
return '🎉 恭喜!无需观看广告即可免费保存';
}
return '观看完整广告后即可下载到相册';
return '观看完整广告后即可保存到相册';
};
return (