refactor: 移除下载部分组件及样式,优化项目结构
- 删除DownloadSection组件及其相关CSS,简化代码结构 - 在历史页面中添加生成中任务的进度计算和显示功能 - 更新历史页面样式,增强用户体验和视觉效果 - 添加缩略图毛玻璃蒙版和进度指示器,提升信息传达的清晰度
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user