优化二维码 OCR 上传体验:并发控制、状态清理、预览修复

This commit is contained in:
yml2213
2026-06-28 20:20:37 +08:00
parent b124c74a46
commit c6c6a92645
@@ -67,6 +67,7 @@ const uploadVisible = ref(false)
const uploadPendingCount = ref(0)
const uploadSaving = ref(false)
type UploadedQrImage = {
_id: string
url: string
previewUrl: string
file: File
@@ -230,6 +231,37 @@ function parseGroupNameFromOcrText(text: string) {
return candidates.find(line => line.includes('群')) || candidates.find(line => /[-]/.test(line)) || candidates[0] || ''
}
const ocrConcurrencyLimit = 4
let ocrRunningCount = 0
const ocrWaitQueue: (() => void)[] = []
function ocrAcquireSlot(): Promise<void> {
if (ocrRunningCount < ocrConcurrencyLimit) {
ocrRunningCount++
return Promise.resolve()
}
return new Promise(resolve => {
ocrWaitQueue.push(() => {
ocrRunningCount++
resolve()
})
})
}
function ocrReleaseSlot() {
ocrRunningCount = Math.max(0, ocrRunningCount - 1)
const next = ocrWaitQueue.shift()
if (next) next()
}
function ocrResetSlots() {
ocrRunningCount = 0
while (ocrWaitQueue.length > 0) {
const next = ocrWaitQueue.shift()
next?.()
}
}
async function recognizeGroupName(file: File) {
const { job_id } = await submitOCRJob(file)
const delays = [1000, 2000, 3000, 3000, 3000]
@@ -246,6 +278,15 @@ async function recognizeGroupName(file: File) {
throw new Error('OCR 识别超时')
}
async function recognizeGroupNameWithLimit(file: File) {
await ocrAcquireSlot()
try {
return await recognizeGroupName(file)
} finally {
ocrReleaseSlot()
}
}
// 上传:逐个上传图片拿 URL
async function handleFileUpload(options: { file: File }) {
if (uploadedImages.value.length + uploadPendingCount.value >= maxBatchUploadCount) {
@@ -260,6 +301,7 @@ async function handleFileUpload(options: { file: File }) {
uploadPendingCount.value += 1
try {
const item: UploadedQrImage = {
_id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
url: '',
previewUrl: URL.createObjectURL(options.file),
file: options.file,
@@ -270,7 +312,7 @@ async function handleFileUpload(options: { file: File }) {
const [uploaded, groupName] = await Promise.all([
uploadAdminFile(options.file, 'qrcode'),
recognizeGroupName(options.file).catch(error => {
recognizeGroupNameWithLimit(options.file).catch(error => {
console.warn('二维码群名 OCR 失败', error)
ElMessage.warning(readError(error, '二维码群名 OCR 失败'))
return ''
@@ -307,6 +349,8 @@ function clearUploadedImages() {
if (item.previewUrl) URL.revokeObjectURL(item.previewUrl)
})
uploadedImages.value = []
uploadPendingCount.value = 0
ocrResetSlots()
}
function handleUploadExceed() {
@@ -778,7 +822,7 @@ onMounted(reloadAll)
</template>
</el-upload>
<div v-if="uploadedImages.length" class="uploaded-list">
<div v-for="(img, idx) in uploadedImages" :key="idx" class="uploaded-item">
<div v-for="(img, idx) in uploadedImages" :key="img._id" class="uploaded-item">
<div class="uploaded-main">
<div class="uploaded-thumb">
<AuthImage
@@ -786,7 +830,7 @@ onMounted(reloadAll)
:source="img.url"
fit="cover"
:image-style="{ width: '52px', height: '52px', borderRadius: '6px' }"
:preview-src-list="imagePreviewSources()"
:preview-src-list="[img.url]"
/>
<img
v-else