优化二维码 OCR 上传体验:并发控制、状态清理、预览修复
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user