实现登录态自动续期并优化扫码兼容性

This commit is contained in:
andy
2026-07-11 15:53:41 +08:00
parent 4870b6761e
commit ab4da9850b
14 changed files with 514 additions and 32 deletions

View File

@@ -4,6 +4,11 @@ import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import { QrCode, X } from 'lucide-vue-next'
import jsQR from 'jsqr'
import {
cameraConstraintCandidates,
getCameraAccessErrorMessage,
shouldTryNextCameraConstraint
} from '@/utils/cameraAccess'
import { parseWriteOffCode, type WriteOffCodePayload } from '@/utils/writeOffCode'
const router = useRouter()
@@ -18,6 +23,7 @@ let canvas: HTMLCanvasElement | null = null
let canvasContext: CanvasRenderingContext2D | null = null
let frameId = 0
let lastDecodeAt = 0
let lastScanHintAt = 0
const getBarcodeDetector = () => {
return (window as unknown as {
@@ -42,6 +48,7 @@ const stopScan = () => {
}
detector = null
lastDecodeAt = 0
lastScanHintAt = 0
}
const goConfirm = (payload: WriteOffCodePayload) => {
@@ -87,6 +94,11 @@ const detectWithJsQr = (video: HTMLVideoElement) => {
return jsQR(imageData.data, width, height, { inversionAttempts: 'dontInvert' })?.data || ''
}
const detectQrValue = async (video: HTMLVideoElement) => {
const detectorValue = detector ? await detectWithBarcodeDetector(video).catch(() => '') : ''
return detectorValue || detectWithJsQr(video)
}
const detectLoop = async () => {
if (!scanning.value || !videoRef.value) return
const video = videoRef.value
@@ -95,14 +107,18 @@ const detectLoop = async () => {
const now = Date.now()
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA && now - lastDecodeAt > 120) {
lastDecodeAt = now
const value = detector ? await detectWithBarcodeDetector(video) : detectWithJsQr(video)
const value = await detectQrValue(video)
if (value) {
handleCodeValue(value)
return
}
if (!scanError.value && now - lastScanHintAt > 3000) {
lastScanHintAt = now
scanError.value = '暂未识别到二维码,请将二维码完整放入取景框'
}
}
} catch {
scanError.value = '扫码识别失败,请调整距离或光线后重试'
scanError.value = '扫码识别失败,请调整距离或切换角度后重试'
}
if (scanning.value) {
@@ -110,6 +126,19 @@ const detectLoop = async () => {
}
}
const openCameraStream = async () => {
let lastError: unknown = null
for (const constraints of cameraConstraintCandidates) {
try {
return await navigator.mediaDevices.getUserMedia(constraints)
} catch (error) {
lastError = error
if (!shouldTryNextCameraConstraint(error)) break
}
}
throw lastError
}
const startScan = async () => {
if (scanning.value || loading.value) return
const BarcodeDetector = getBarcodeDetector()
@@ -123,12 +152,7 @@ const startScan = async () => {
scanError.value = ''
try {
detector = BarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : null
stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
facingMode: { ideal: 'environment' }
}
})
stream = await openCameraStream()
scanning.value = true
await nextTick()
if (!videoRef.value) return
@@ -137,9 +161,7 @@ const startScan = async () => {
frameId = window.requestAnimationFrame(detectLoop)
} catch (error) {
stopScan()
scanError.value = error instanceof Error && error.name === 'NotAllowedError'
? '未获得摄像头权限,请允许后重试'
: '无法打开摄像头,请检查浏览器权限'
scanError.value = getCameraAccessErrorMessage(error)
showToast(scanError.value)
} finally {
loading.value = false