注:需用谷歌瀏覽器才能調用權限
1. 引入依賴:npm install @ericblade/quagga2
<template><el-button color="#188ae2" @click="handleScan" class="scan-btn" :disabled="isInitializing || isScanning">{{t('btn.scan') }}</el-button><!-- 條形碼掃描器的占位符 --><div id="interactive" ref="interactiveRef" class="viewport" style="display: none;"></div></template><script setup>import { ref, onBeforeUnmount } from 'vue'
import Quagga from '@ericblade/quagga2'; // 引用
const interactiveRef = ref < HTMLElement | null > (null)
const isInitializing = ref(false)
const isScanning = ref(false)
const lastScanTime = ref(0)
let onDetectedHandler = null// 統一停止/清理
const stopScan = () => {try { Quagga.stop() } catch { }const el = interactiveRef.value || (document.querySelector('#interactive') || null)if (el) el.style.display = 'none'if (onDetectedHandler) {Quagga.offDetected(onDetectedHandler)onDetectedHandler = null}isScanning.value = falseisInitializing.value = false
}const handleScan = async () => {// 防抖:1s 內不重復觸發const now = Date.now()if (now - lastScanTime.value < 1000) returnlastScanTime.value = now// ? 修復:不要寫成 if (!isMobile.value); 末尾多分號if (!isMobile.value) {toast('不是手機端登錄', 'warning')return}// 避免重復初始化/啟動if (isInitializing.value || isScanning.value) {toast('正在打開攝像頭…', 'info')return}const container = interactiveRef.value || (document.querySelector('#interactive') | null)if (!container) {toast('找不到掃碼容器 #interactive', 'error')return}// 顯示并給尺寸(沒有尺寸很難識別)container.style.display = 'block'container.style.width = '100%'container.style.height = '60vh'isInitializing.value = truetry {await new Promise((resolve, reject) => {Quagga.init({inputStream: {name: 'Live',type: 'LiveStream',target: container,constraints: {facingMode: 'environment',width: { min: 640 },height: { min: 480 },},},decoder: { readers: ['code_128_reader', 'ean_reader'] },locate: true,},(err) => (err ? reject(err) : resolve()),)})Quagga.start()isScanning.value = truetoast('攝像頭已啟動', 'success')} catch (e) {console.error('攝像頭/初始化失敗', e)toast('無法啟動攝像頭:請使用 HTTPS 并允許相機權限', 'error')stopScan()return} finally {isInitializing.value = false}// 識別一次即關閉(做一次性處理)anyonDetectedHandler = (data) => {if (!isScanning.value) returnconst code = data?.codeResult?.codeif (code) {trackingNumber.value = code // ? 把結果寫回輸入框toast('已識別:' + code, 'success')// todo 在這里添加掃描后需調用的邏輯stopScan()}}Quagga.onDetected(onDetectedHandler)
}// 清理資源
onBeforeUnmount(() => {stopScan()
});</script>