diff --git a/src/pages/ChatPage.scss b/src/pages/ChatPage.scss index ea8c403..953341b 100644 --- a/src/pages/ChatPage.scss +++ b/src/pages/ChatPage.scss @@ -3331,9 +3331,12 @@ // 批量转写模态框基础样式(共享样式在 styles/batchTranscribe.scss) // 批量转写确认对话框 -.batch-confirm-modal { +.batch-modal-content.batch-confirm-modal { width: 480px; max-width: 90vw; + max-height: none; + overflow: visible; + overflow-y: visible; .batch-modal-header { display: flex; @@ -3470,6 +3473,74 @@ font-weight: 600; color: var(--primary-color); } + + .batch-concurrency-field { + position: relative; + + .batch-concurrency-trigger { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border-radius: 9999px; + border: 1px solid var(--border-color); + background: var(--bg-primary); + color: var(--text-primary); + font-size: 13px; + cursor: pointer; + + &:hover { + border-color: var(--text-tertiary); + } + + &.open { + border-color: var(--primary); + } + + svg { + color: var(--text-tertiary); + transition: transform 0.2s; + } + + &.open svg { + transform: rotate(180deg); + } + } + + .batch-concurrency-dropdown { + position: absolute; + top: calc(100% + 6px); + right: 0; + min-width: 180px; + background: color-mix(in srgb, var(--bg-primary) 90%, var(--bg-secondary)); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 6px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); + z-index: 100; + } + + .batch-concurrency-option { + width: 100%; + text-align: left; + padding: 8px 12px; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text-primary); + font-size: 13px; + cursor: pointer; + + &:hover { + background: var(--bg-tertiary); + } + + &.active { + color: var(--primary); + font-weight: 500; + } + } + } } } @@ -3527,7 +3598,7 @@ &.btn-primary, &.batch-transcribe-start-btn { background: var(--primary-color); - color: white; + color: #000; &:hover { opacity: 0.9; diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index e79506e..bf85c5c 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -345,6 +345,8 @@ function ChatPage(_props: ChatPageProps) { const [batchImageMessages, setBatchImageMessages] = useState(null) const [batchImageDates, setBatchImageDates] = useState([]) const [batchImageSelectedDates, setBatchImageSelectedDates] = useState>(new Set()) + const [batchDecryptConcurrency, setBatchDecryptConcurrency] = useState(6) + const [showConcurrencyDropdown, setShowConcurrencyDropdown] = useState(false) // 批量删除相关状态 const [isDeleting, setIsDeleting] = useState(false) @@ -1662,29 +1664,44 @@ function ChatPage(_props: ChatPageProps) { let successCount = 0 let failCount = 0 - for (let i = 0; i < images.length; i++) { - const img = images[i] + let completed = 0 + const concurrency = batchDecryptConcurrency + + const decryptOne = async (img: typeof images[0]) => { try { const r = await window.electronAPI.image.decrypt({ sessionId: session.username, imageMd5: img.imageMd5, imageDatName: img.imageDatName, - force: false + force: true }) if (r?.success) successCount++ else failCount++ } catch { failCount++ } - - updateDecryptProgress(i + 1, images.length) - if (i % 5 === 0) { - await new Promise(resolve => setTimeout(resolve, 0)) - } + completed++ + updateDecryptProgress(completed, images.length) } + // 并发池:同时跑 concurrency 个任务 + const pool: Promise[] = [] + for (const img of images) { + const p = decryptOne(img) + pool.push(p) + if (pool.length >= concurrency) { + await Promise.race(pool) + // 移除已完成的 + for (let j = pool.length - 1; j >= 0; j--) { + const settled = await Promise.race([pool[j].then(() => true), Promise.resolve(false)]) + if (settled) pool.splice(j, 1) + } + } + } + await Promise.all(pool) + finishDecrypt(successCount, failCount) - }, [batchImageMessages, batchImageSelectedDates, currentSessionId, finishDecrypt, sessions, startDecrypt, updateDecryptProgress]) + }, [batchImageMessages, batchImageSelectedDates, batchDecryptConcurrency, currentSessionId, finishDecrypt, sessions, startDecrypt, updateDecryptProgress]) const batchImageCountByDate = useMemo(() => { const map = new Map() @@ -2623,6 +2640,39 @@ function ChatPage(_props: ChatPageProps) { 已选: {batchImageSelectedDates.size} 天,共 {batchImageSelectedCount} 张图片 +
+ 并发数: +
+ + {showConcurrencyDropdown && ( +
+ {[ + { value: 1, label: '1(最慢,最稳)' }, + { value: 3, label: '3' }, + { value: 6, label: '6(推荐)' }, + { value: 10, label: '10' }, + { value: 20, label: '20(最快,可能卡顿)' }, + ].map(opt => ( + + ))} +
+ )} +
+