diff --git a/chuan-next/src/components/webrtc/WebRTCFileReceive.tsx b/chuan-next/src/components/webrtc/WebRTCFileReceive.tsx index 945245e..1024312 100644 --- a/chuan-next/src/components/webrtc/WebRTCFileReceive.tsx +++ b/chuan-next/src/components/webrtc/WebRTCFileReceive.tsx @@ -1,11 +1,12 @@ "use client"; -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; -import { Download, FileText, Image, Video, Music, Archive } from 'lucide-react'; +import { Download, FileText, Image, Video, Music, Archive, Clock, Zap } from 'lucide-react'; import { useToast } from '@/components/ui/toast-simple'; import { ConnectionStatus } from '@/components/ConnectionStatus'; +import { TransferProgressTracker, formatTransferSpeed, formatTime } from '@/lib/transfer-utils'; interface FileInfo { id: string; @@ -14,6 +15,8 @@ interface FileInfo { type: string; status: 'ready' | 'downloading' | 'completed'; progress: number; + transferSpeed?: number; // bytes per second + startTime?: number; // 传输开始时间 } const getFileIcon = (mimeType: string) => { @@ -60,6 +63,9 @@ export function WebRTCFileReceive({ const [pickupCode, setPickupCode] = useState(''); const [isValidating, setIsValidating] = useState(false); const { showToast } = useToast(); + + // 用于跟踪传输进度的trackers + const transferTrackers = useRef>(new Map()); // 使用传入的取件码或本地状态的取件码 const displayPickupCode = propPickupCode || pickupCode; @@ -242,16 +248,44 @@ export function WebRTCFileReceive({ const isDownloading = file.status === 'downloading'; const isCompleted = file.status === 'completed'; const hasDownloadedFile = downloadedFiles?.has(file.id); - const currentProgress = file.progress; console.log('文件状态:', { fileName: file.name, status: file.status, progress: file.progress, - isDownloading, - currentProgress + isDownloading }); + // 计算传输进度信息 + let transferInfo = null; + let currentProgress = 0; // 使用稳定的进度值 + + if (isDownloading && file) { + const fileKey = `${file.name}-${file.size}`; + let tracker = transferTrackers.current.get(fileKey); + + // 如果tracker不存在,创建一个新的 + if (!tracker) { + tracker = new TransferProgressTracker(file.size); + transferTrackers.current.set(fileKey, tracker); + } + + // 更新传输进度 + const transferredBytes = (file.progress / 100) * file.size; + const progressInfo = tracker.update(transferredBytes); + transferInfo = progressInfo; + currentProgress = progressInfo.percentage; // 使用稳定的百分比 + } else { + // 如果不在传输中,使用原始进度值 + currentProgress = file.progress; + } + + // 清理已完成的tracker + if (file.status === 'completed') { + const fileKey = `${file.name}-${file.size}`; + transferTrackers.current.delete(fileKey); + } + return (
@@ -266,7 +300,27 @@ export function WebRTCFileReceive({

✅ 传输完成,点击保存

)} {isDownloading && ( -

⏳ 传输中...{currentProgress.toFixed(1)}%

+
+ {/* 传输速度和剩余时间信息 */} + {transferInfo && ( +
+
+ + {transferInfo.speed.displaySpeed} + + {transferInfo.speed.unit} + +
+ {transferInfo.remainingTime.seconds < Infinity && ( +
+ + 剩余 + {transferInfo.remainingTime.display} +
+ )} +
+ )} +
)}
@@ -289,7 +343,9 @@ export function WebRTCFileReceive({ {(isDownloading || isCompleted) && currentProgress > 0 && (
- {hasDownloadedFile ? '传输完成' : '正在传输...'} + + {hasDownloadedFile ? '传输完成' : '正在传输...'} + {currentProgress.toFixed(1)}%
diff --git a/chuan-next/src/components/webrtc/WebRTCFileUpload.tsx b/chuan-next/src/components/webrtc/WebRTCFileUpload.tsx index 01ddb0b..04d47d4 100644 --- a/chuan-next/src/components/webrtc/WebRTCFileUpload.tsx +++ b/chuan-next/src/components/webrtc/WebRTCFileUpload.tsx @@ -1,10 +1,11 @@ "use client"; -import React, { useState, useRef, useCallback } from 'react'; +import React, { useState, useRef, useCallback, useEffect } from 'react'; import { Button } from '@/components/ui/button'; -import { Upload, FileText, Image, Video, Music, Archive, X } from 'lucide-react'; +import { Upload, FileText, Image, Video, Music, Archive, X, Clock, Zap } from 'lucide-react'; import RoomInfoDisplay from '@/components/RoomInfoDisplay'; import { ConnectionStatus } from '@/components/ConnectionStatus'; +import { TransferProgressTracker, formatTransferSpeed, formatTime } from '@/lib/transfer-utils'; interface FileInfo { @@ -14,6 +15,8 @@ interface FileInfo { type: string; status: 'ready' | 'downloading' | 'completed'; progress: number; + transferSpeed?: number; // bytes per second + startTime?: number; // 传输开始时间 } const getFileIcon = (mimeType: string) => { @@ -65,6 +68,9 @@ export function WebRTCFileUpload({ }: WebRTCFileUploadProps) { const [isDragOver, setIsDragOver] = useState(false); const fileInputRef = useRef(null); + + // 用于跟踪传输进度的trackers + const transferTrackers = useRef>(new Map()); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); @@ -197,9 +203,38 @@ export function WebRTCFileUpload({ // 查找对应的文件信息(包含状态和进度) const fileInfo = fileList.find(f => f.name === file.name && f.size === file.size); const isTransferringThisFile = fileInfo?.status === 'downloading'; - const currentProgress = fileInfo?.progress || 0; const fileStatus = fileInfo?.status || 'ready'; + // 计算传输进度信息 + let transferInfo = null; + let currentProgress = 0; // 使用稳定的进度值 + + if (isTransferringThisFile && fileInfo) { + const fileKey = `${file.name}-${file.size}`; + let tracker = transferTrackers.current.get(fileKey); + + // 如果tracker不存在,创建一个新的 + if (!tracker) { + tracker = new TransferProgressTracker(file.size); + transferTrackers.current.set(fileKey, tracker); + } + + // 更新传输进度 + const transferredBytes = (fileInfo.progress / 100) * file.size; + const progressInfo = tracker.update(transferredBytes); + transferInfo = progressInfo; + currentProgress = progressInfo.percentage; // 使用稳定的百分比 + } else { + // 如果不在传输中,使用原始进度值 + currentProgress = fileInfo?.progress || 0; + } + + // 清理已完成的tracker + if (fileStatus === 'completed') { + const fileKey = `${file.name}-${file.size}`; + transferTrackers.current.delete(fileKey); + } + return (
)}
+ + {/* 传输速度和剩余时间信息 */} + {transferInfo && ( +
+
+ + {transferInfo.speed.displaySpeed} + + {transferInfo.speed.unit} + +
+ {transferInfo.remainingTime.seconds < Infinity && ( +
+ + 剩余 + {transferInfo.remainingTime.display} +
+ )} +
+ )}