批量语音转文字改成右下角常驻

This commit is contained in:
xuncha
2026-02-06 23:09:01 +08:00
parent ca1a386146
commit fe0e2e6592
6 changed files with 428 additions and 318 deletions

View File

@@ -0,0 +1,101 @@
import React from 'react'
import { createPortal } from 'react-dom'
import { Loader2, X, CheckCircle, XCircle, AlertCircle } from 'lucide-react'
import { useBatchTranscribeStore } from '../stores/batchTranscribeStore'
import '../styles/batchTranscribe.scss'
/**
* 全局批量转写进度浮窗 + 结果弹窗
* 挂载在 App 层,切换页面时不会消失
*/
export const BatchTranscribeGlobal: React.FC = () => {
const {
isBatchTranscribing,
progress,
showToast,
showResult,
result,
setShowToast,
setShowResult
} = useBatchTranscribeStore()
return (
<>
{/* 批量转写进度浮窗(非阻塞) */}
{showToast && isBatchTranscribing && createPortal(
<div className="batch-progress-toast">
<div className="batch-progress-toast-header">
<div className="batch-progress-toast-title">
<Loader2 size={14} className="spin" />
<span></span>
</div>
<button className="batch-progress-toast-close" onClick={() => setShowToast(false)} title="最小化">
<X size={14} />
</button>
</div>
<div className="batch-progress-toast-body">
<div className="progress-text">
<span>{progress.current} / {progress.total}</span>
<span className="progress-percent">
{progress.total > 0
? Math.round((progress.current / progress.total) * 100)
: 0}%
</span>
</div>
<div className="progress-bar">
<div
className="progress-fill"
style={{
width: `${progress.total > 0
? (progress.current / progress.total) * 100
: 0}%`
}}
/>
</div>
</div>
</div>,
document.body
)}
{/* 批量转写结果对话框 */}
{showResult && createPortal(
<div className="batch-modal-overlay" onClick={() => setShowResult(false)}>
<div className="batch-modal-content batch-result-modal" onClick={(e) => e.stopPropagation()}>
<div className="batch-modal-header">
<CheckCircle size={20} />
<h3></h3>
</div>
<div className="batch-modal-body">
<div className="result-summary">
<div className="result-item success">
<CheckCircle size={18} />
<span className="label">:</span>
<span className="value">{result.success} </span>
</div>
{result.fail > 0 && (
<div className="result-item fail">
<XCircle size={18} />
<span className="label">:</span>
<span className="value">{result.fail} </span>
</div>
)}
</div>
{result.fail > 0 && (
<div className="result-tip">
<AlertCircle size={16} />
<span></span>
</div>
)}
</div>
<div className="batch-modal-footer">
<button className="btn-primary" onClick={() => setShowResult(false)}>
</button>
</div>
</div>
</div>,
document.body
)}
</>
)
}