实现登录态自动续期并优化扫码兼容性
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user