介绍
条码扫描器,又称为条码阅读器、条码扫描枪、条形码扫描器、条形码扫描枪及条形码阅读器。它是用于读取条码所包含信息的阅读设备,利用光学原理,把条形码的内容解码后通过数据线或者无线的方式传输到电脑或者别的设备。广泛应用于超市、物流快递、图书馆等扫描商品、单据的条码
插件安装命令
npm install @zxing/library
代码部分
<template>
<div class="code-reader-content">
<div class="page">
<video ref="video" autoplay id="video" height="200"></video>
<p v-if="videoInputDevicesArray.length === 0">{{ textContent }}</p>
</div>
<div class="scan-box">
<div class="frame upper-left"></div>
<div class="frame upper-right"></div>
<div class="frame lower-right"></div>
<div class="frame lower-left"></div>
<div class="pointer-box">
<div class="pointer"></div>
</div>
<div v-show="tipShow" class="tip">{{ tipMsg }}</div>
<div class="btn-switch" @click="toggle"></div>
<button @click="handleScanComplete" class="btn-wc">扫描完成</button>
</div>
</div>
</template>
功能部分
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { BrowserMultiFormatReader } from '@zxing/library';
import { useRouter } from 'vue-router'
const router = useRouter()
import { defineEmits } from 'vue';
const emits = defineEmits(['scan-complete']);
const handleScanComplete = (val1) => {
console.log('扫描完成按钮被点击了');
emits('scan-complete', true, val1);
};
const codeReader = ref(null);
const tipMsg = ref('正在尝试识别....');
const tipShow = ref(true);
const textContent = ref(undefined);
const videoInputDevicesArray = ref([]);
const deviceId = ref('');
const isEswitch = ref(false);
let timer = null;
// 开启扫描
const openScan = async () => {
codeReader.value = await new BrowserMultiFormatReader();
codeReader.value
.getVideoInputDevices()
.then(async (videoInputDevices) => {
tipShow.value = true;
tipMsg.value = '正在尝试识别....';
videoInputDevicesArray.value = videoInputDevices;
if (videoInputDevicesArray.value.length > 1) {
deviceId.value = videoInputDevicesArray.value[1].deviceId;
} else {
deviceId.value = videoInputDevicesArray.value[0].deviceId;
}
decodeFromInputVideoFunc();
console.log('codeReader', codeReader.value);
console.log('----------------');
console.log('deviceId', deviceId.value);
console.log('-------------------');
console.log('videoInputDevicesArray', videoInputDevicesArray.value);
})
.catch(() => {
tipShow.value = false;
});
};
// 解码部分
const decodeFromInputVideoFunc = () => {
if (videoInputDevicesArray.value.length === 0) {
textContent.value = '初始化摄像头失败';
document.getElementById('video').style.display = 'none';
return;
}
codeReader.value.reset();
codeReader.value.decodeFromInputVideoDeviceContinuously(
deviceId.value,
'video',
(result) => {
tipMsg.value = '正在扫描';
if (result) {
tipMsg.value = '扫描成功!';
setTimeout(() => {
tipMsg.value = '解析完成';
handleScanComplete(result.text)
}, 1000); // 延迟1秒钟
console.log('扫描成功123', result);
console.log('result123', result.text);
// tipShow.value = false;
// window && window.getResultEvent(result);
// window?.parent?.Gikam?.toast('扫码成功');
// codeReader.value.reset();
// codeReader.value.stopContinuousDecode();
// cid.value = result.text
// // 发送POST请求给服务器
// fetch('http://localhost:2303/mtg/parse-product', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({ barcode: result.text })
// })
// .then(response => response.json())
// .then(data => {
// // 从服务器获取到产品信息
// console.log('产品信息:', data);
// // 进行其他操作...
// })
// .catch(error => {
// console.error('请求出错:', error);
// });
// router.push('/scanbook')
}
}
);
};
const cutover = () => {
if (
videoInputDevicesArray.value &&
videoInputDevicesArray.value.length > 1
) {
if (deviceId.value === videoInputDevicesArray.value[0].deviceId) {
deviceId.value = videoInputDevicesArray.value[1].deviceId;
} else {
deviceId.value = videoInputDevicesArray.value[0].deviceId;
}
}
codeReader.value.stopStreams();
};
// 切换摄像头
const toggle = async () => {
codeReader.value.stopStreams();
timer = setTimeout(() => {
timer = null;
}, 2000);
if (timer) {
await codeReader.value.tryPlayVideo('video');
cutover();
decodeFromInputVideoFunc();
}
};
onMounted(() => {
openScan();
});
onBeforeUnmount(() => {
codeReader.value.stopContinuousDecode();
codeReader.value.reset();
});
</script>
样式部分
<style lang="less" scoped>
.code-reader-content {
.page {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 200px;
#video {
// height: 100%;
width: 100%;
object-fit: fill;
}
}
.scan-box {
position: absolute;
left: 50%;
top: 58%;
transform: translate(-50%, -90%);
height: 20%;
width: 80%;
.frame {
position: absolute;
width: 15px;
height: 15px;
border: 3px solid transparent;
}
.upper-left {
top: 0;
left: 0;
border-left-color: rgba(66, 133, 244, 1);
border-top-color: rgba(66, 133, 244, 1);
}
.upper-right {
top: 0;
right: 0;
border-right-color: rgba(66, 133, 244, 1);
border-top-color: rgba(66, 133, 244, 1);
}
.lower-right {
bottom: 0;
right: 0;
border-bottom-color: rgba(66, 133, 244, 1);
border-right-color: rgba(66, 133, 244, 1);
}
.lower-left {
bottom: 0;
left: 0;
border-left-color: rgba(66, 133, 244, 1);
border-bottom-color: rgba(66, 133, 244, 1);
}
.pointer-box {
position: absolute;
top: 0;
left: 0;
width: 98%;
height: 100%;
overflow: hidden;
.pointer {
height: 3px;
background-image: linear-gradient(to right,
transparent 0%,
rgba(66, 133, 244, 1) 50%,
transparent 100%);
transform: translateY(-3px);
animation: move 2s linear infinite;
}
@keyframes move {
0% {
transform: translateY(-3px);
}
100% {
transform: translateY(calc(20vh - 3px));
}
}
}
.tip {
position: absolute;
left: 50%;
top: 122%;
transform: translate(-50%, 0);
white-space: nowrap;
color: rgb(176, 209, 28);
font-size: 16px;
}
.btn-switch {
position: absolute;
left: 50%;
top: 140%;
width: 20px;
height: 20px;
transform: translate(-50%, 0);
background-color: red;
}
.btn-wc {
position: absolute;
left: 50%;
top: 160%;
transform: translate(-50%, 0);
}
}
}
</style>
效果图预览
扫描中... 扫描完