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;
|
font-weight: 500;
|
||||||
letter-spacing: 0.5px;
|
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 [loading, setLoading] = useState(true);
|
||||||
const serverSdk = useServerSdk();
|
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 () => {
|
const loadRecords = async () => {
|
||||||
try {
|
try {
|
||||||
const logs = await serverSdk.getMineLogs();
|
const logs = await serverSdk.getMineLogs();
|
||||||
console.log(logs);
|
|
||||||
setRecords(logs || []);
|
setRecords(logs || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载记录失败:', error);
|
console.error('加载记录失败:', error);
|
||||||
@@ -31,6 +47,19 @@ export default function History() {
|
|||||||
loadRecords();
|
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 () => {
|
const handleRefresh = async () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
@@ -110,39 +139,52 @@ export default function History() {
|
|||||||
<Text className="empty-desc">开始创作你的第一个作品吧!{'\n'}所有的处理记录都会保存在这里</Text>
|
<Text className="empty-desc">开始创作你的第一个作品吧!{'\n'}所有的处理记录都会保存在这里</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
records.map(record => (
|
records.map(record => {
|
||||||
<View key={record.id} className="history-item" onClick={() => handleItemClick(record)}>
|
const progress = calculateProgress(record);
|
||||||
<View className="item-thumbnail">
|
const isGenerating = record.status === 'processing';
|
||||||
{record.status === 'completed' && record.outputResult ? (
|
|
||||||
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
return (
|
||||||
) : (
|
<View key={record.id} className="history-item" onClick={() => handleItemClick(record)}>
|
||||||
<View className={`thumbnail-placeholder ${record.status}`}>
|
<View className="item-thumbnail">
|
||||||
|
{record.status === 'completed' && record.outputResult ? (
|
||||||
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
||||||
</View>
|
) : (
|
||||||
)}
|
<View className={`thumbnail-placeholder ${record.status}`}>
|
||||||
</View>
|
<Image className="thumbnail-image" src={record.inputImageUrl} mode="aspectFill" />
|
||||||
|
{/* 生成中状态的缩略图毛玻璃蒙版 */}
|
||||||
<View className="item-content">
|
{isGenerating && (
|
||||||
<View className="item-header">
|
<View className="thumbnail-overlay">
|
||||||
<Text className="item-title">{record.templateName}</Text>
|
<View className="thumbnail-glassmorphism" />
|
||||||
<View className="item-right">
|
<View className="thumbnail-progress">
|
||||||
{/* 添加状态指示器 */}
|
<View className="thumbnail-progress-circle">
|
||||||
{record.status === 'generating' && (
|
<View
|
||||||
<View className="loading-indicator">
|
className="thumbnail-progress-fill"
|
||||||
<Text className="loading-dot">●</Text>
|
style={{
|
||||||
<Text className="loading-dot">●</Text>
|
background: `conic-gradient(#ff9500 ${progress * 3.6}deg, transparent 0deg)`,
|
||||||
<Text className="loading-dot">●</Text>
|
}}
|
||||||
</View>
|
/>
|
||||||
)}
|
<View className="thumbnail-progress-inner">
|
||||||
</View>
|
<Text className="thumbnail-progress-text">{Math.round(progress)}%</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="item-info">
|
<View className="item-content">
|
||||||
<Text className="item-time">{formatTime(record.createdAt)}</Text>
|
<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>
|
</View>
|
||||||
</View>
|
);
|
||||||
))
|
})
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
|
|||||||
onClose: isEnded => {
|
onClose: isEnded => {
|
||||||
if (!isEnded) {
|
if (!isEnded) {
|
||||||
showToast({
|
showToast({
|
||||||
title: '请观看完整广告才能下载',
|
title: '请观看完整广告才能保存',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
});
|
});
|
||||||
@@ -48,7 +48,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
|
|||||||
finalExtension = task?.type === 'video' ? 'mp4' : 'jpg';
|
finalExtension = task?.type === 'video' ? 'mp4' : 'jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast({ title: '下载中...', icon: 'loading', duration: 1000 * 60 });
|
showToast({ title: '保存中...', icon: 'loading', duration: 1000 * 60 });
|
||||||
const downloadRes = await downloadFile({
|
const downloadRes = await downloadFile({
|
||||||
url: mediaUrl,
|
url: mediaUrl,
|
||||||
header: {
|
header: {
|
||||||
@@ -98,7 +98,7 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
|
|||||||
} else if (err.errMsg.includes('file')) {
|
} else if (err.errMsg.includes('file')) {
|
||||||
errorMsg = '文件格式不支持或已损坏';
|
errorMsg = '文件格式不支持或已损坏';
|
||||||
} else if (err.errMsg.includes('download')) {
|
} else if (err.errMsg.includes('download')) {
|
||||||
errorMsg = '文件下载失败,请重试';
|
errorMsg = '文件保存失败,请重试';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,9 +117,9 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
|
|||||||
return '广告加载中...';
|
return '广告加载中...';
|
||||||
}
|
}
|
||||||
if (!adAvailable) {
|
if (!adAvailable) {
|
||||||
return '免费下载';
|
return '免费保存';
|
||||||
}
|
}
|
||||||
return '观看广告下载';
|
return '观看广告并保存到相册';
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTipText = () => {
|
const getTipText = () => {
|
||||||
@@ -127,9 +127,9 @@ const SuccessComponent: React.FC<SuccessComponentProps> = ({ task }) => {
|
|||||||
return '正在加载广告...';
|
return '正在加载广告...';
|
||||||
}
|
}
|
||||||
if (!adAvailable) {
|
if (!adAvailable) {
|
||||||
return '🎉 恭喜!无需观看广告即可免费下载';
|
return '🎉 恭喜!无需观看广告即可免费保存';
|
||||||
}
|
}
|
||||||
return '观看完整广告后即可下载到相册';
|
return '观看完整广告后即可保存到相册';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user