/** * 任务-3D原图详情组件 */ 'use client'; import { useState, useEffect, useRef } from 'react'; import { parseDownloadError } from '@/lib/download-errors'; interface Asset { id: string; assetType: string; fileUrl: string; fileName: string; fileSize: number; sortOrder: number; } interface ThreeDOriginalDetailProps { material: { id: string; taskNo?: string; productModel: string; categoryName: string; sceneName: string; materialType?: string; taskStatus?: string; taskFetchedAt?: string; assets: Asset[]; }; canDownloadSource: boolean; onDownload: (type: string, assetId?: string) => Promise; onBack?: () => void; showToast?: (message: string, options?: { variant?: 'success' | 'error' }) => void; } export default function ThreeDOriginalDetail({ material, canDownloadSource, onDownload, onBack, showToast, }: ThreeDOriginalDetailProps) { const [currentImageIndex, setCurrentImageIndex] = useState(0); const [downloading, setDownloading] = useState(false); const [downloadType, setDownloadType] = useState(''); const isTaskOther = material.materialType === '任务-其他'; // 轮播相关状态 const [autoPlayEnabled, setAutoPlayEnabled] = useState(true); // 按类型分组资产 // ⚠️ 注意:preview是S3存储的压缩预览图(小文件),用于网页展示 // zip是本地存储的原图压缩包(大文件),用于下载 const mainImages = material.assets.filter((a) => a.assetType === 'preview').sort((a, b) => a.sortOrder - b.sortOrder); const zipFile = material.assets.find((a) => a.assetType === 'zip'); // 轮播逻辑 const autoPlayTimerRef = useRef(null); useEffect(() => { // 清理之前的定时器 if (autoPlayTimerRef.current) { clearInterval(autoPlayTimerRef.current); } // 只在有多张原图时启用轮播 if (autoPlayEnabled && mainImages.length > 1) { autoPlayTimerRef.current = setInterval(() => { setCurrentImageIndex((prev) => (prev + 1) % mainImages.length); }, 5000); // 5秒轮播 } return () => { if (autoPlayTimerRef.current) { clearInterval(autoPlayTimerRef.current); } }; }, [currentImageIndex, autoPlayEnabled, mainImages.length]); // 格式化文件大小 const formatBytes = (bytes: number) => { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; }; return (
{/* 警告:已被外部系统获取 */} {material.taskStatus === 'fetched' && material.taskFetchedAt && (

注意:此素材已被外部系统获取

获取时间:{new Date(material.taskFetchedAt).toLocaleString('zh-CN')}

编辑或删除此素材可能影响外部系统的使用

)}
{/* 左侧:原图展示 */}
{/* 原图预览区 */} {mainImages.length > 0 && (

{isTaskOther ? '预览图' : '原图'} ({currentImageIndex + 1}/{mainImages.length})

{mainImages.length > 1 && ( )}
{`原图
)} {/* 原图小卡片展示区 */} {mainImages.length > 0 && (

所有原图 ({mainImages.length})

{isTaskOther && (

任务-其他只展示上传时提交的预览图

)}
{mainImages.map((img, index) => ( ))}
)}
{/* 右侧:基本信息 + 下载按钮 */}
{/* 基本信息卡片 */}

素材详情

任务号

{material.taskNo}

{!isTaskOther && (

SPU型号

{material.productModel}

)} {!isTaskOther && (

店铺

{material.categoryName}

)} {!isTaskOther && material.sceneName && (

场景

{material.sceneName}

)}
{/* ZIP 文件信息和下载按钮 - 使用容器实现sticky,保持相对位置 */}
{/* ZIP 源文件 */} {zipFile && (

源文件

{zipFile.fileName}

{formatBytes(zipFile.fileSize)}

{canDownloadSource ? ( ) : ( 无权限 )}
)} {/* 返回上一页按钮 */} {onBack && ( )}
); }