import React, { useEffect, useState } from 'react' import { createPortal } from 'react-dom' import { Loader2, X, CheckCircle, XCircle, AlertCircle, Clock } from 'lucide-react' import { useBatchTranscribeStore } from '../stores/batchTranscribeStore' import '../styles/batchTranscribe.scss' /** * 全局批量转写进度浮窗 + 结果弹窗 * 挂载在 App 层,切换页面时不会消失 */ export const BatchTranscribeGlobal: React.FC = () => { const { isBatchTranscribing, progress, showToast, showResult, result, sessionName, startTime, setShowToast, setShowResult } = useBatchTranscribeStore() const [eta, setEta] = useState('') // 计算剩余时间 useEffect(() => { if (!isBatchTranscribing || !startTime || progress.current === 0) { setEta('') return } const timer = setInterval(() => { const now = Date.now() const elapsed = now - startTime const rate = progress.current / elapsed // ms per item const remainingItems = progress.total - progress.current if (remainingItems <= 0) { setEta('') return } const remainingTimeMs = remainingItems / rate const remainingSeconds = Math.ceil(remainingTimeMs / 1000) if (remainingSeconds < 60) { setEta(`${remainingSeconds}秒`) } else { const minutes = Math.floor(remainingSeconds / 60) const seconds = remainingSeconds % 60 setEta(`${minutes}分${seconds}秒`) } }, 1000) return () => clearInterval(timer) }, [isBatchTranscribing, startTime, progress.current, progress.total]) return ( <> {/* 批量转写进度浮窗(非阻塞) */} {showToast && isBatchTranscribing && createPortal(
批量转写中{sessionName ? `(${sessionName})` : ''}
{progress.current} / {progress.total} {progress.total > 0 ? Math.round((progress.current / progress.total) * 100) : 0}%
{eta && (
剩余 {eta}
)}
0 ? (progress.current / progress.total) * 100 : 0}%` }} />
, document.body )} {/* 批量转写结果对话框 */} {showResult && createPortal(
setShowResult(false)}>
e.stopPropagation()}>

转写完成

成功: {result.success} 条
{result.fail > 0 && (
失败: {result.fail} 条
)}
{result.fail > 0 && (
部分语音转写失败,可能是语音文件损坏或网络问题
)}
, document.body )} ) }