vue3+vite+ts+vue3-qr-reader实现移动端h5+pc端调起摄像头核销二维码
1、首先我们看示例图:
h5:
pc:
2、我们开始做第一步就是装依赖:yarn add vue3-qr-reader 或者 npm install vue3-qr-reader
我记得装完后还需要装一个 yarn add -D sass
3、封装一个组件公用:组件位置你们自己定 我写在了components/QrScanner/index
这里面我用到了 element-plus 的icon 想用的可以安装对应依赖 https://s-test.belle.cn/zh-CN/
<template> <div> <QrStream :style="scannerStyle" @decode="handleDecode" @init="handleInit" @error="handleError" > <template v-if="showCloseButton"> <div class="qr-scanner-container"> <!-- <div class="close-view dis-center" @click="closeScanner">X</div> --> <el-icon @click="closeScanner" class="close-view dis-center"><CircleClose /></el-icon> <div class="qr-scanner"> <div class="box"> <div class="line"></div> <div class="angle"></div> </div> </div> </div> </template> </QrStream> </div> </template> <script setup> import { ref, defineExpose } from 'vue' import { QrStream } from 'vue3-qr-reader' import { CircleClose } from '@element-plus/icons-vue' const emit = defineEmits(['decode','close']) const scannerStyle = { position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh', zIndex: 9999, } const showCloseButton = ref(false) const qrcodeData = ref(null) const handleDecode = (data) => { console.log('Decoded data:', data) emit('decode', data) qrcodeData.value = data } const closeScanner = () => { emit('close') } const handleInit = async (promise) => { try { const { capabilities } = await promise console.log('Camera capabilities:', capabilities) showCloseButton.value = true } catch (error) { handleError(error) } } const handleError = (error) => { const errorMessages = { NotAllowedError: '您需要授予相机访问权限', NotFoundError: '这个设备上没有摄像头', NotSupportedError: '所需的安全上下文(HTTPS、本地主机)', NotReadableError: '相机被占用', OverconstrainedError: '安装摄像头不合适', StreamApiNotSupportedError: '此浏览器不支持流API', InsecureContextError: '仅允许在安全上下文中访问摄像机。使用HTTPS或本地主机,而不是HTTP。', } const message = errorMessages[error.name] || 'ERROR: 摄像头错误' message.error(message) console.error(message) } defineExpose({ qrcodeData }) </script> <style scoped lang="scss"> .qr-scanner-container { position: relative; width: 100%; height: 100%; .close-view { position: absolute; top: 20px; right: 20px; font-size: 40px; color: #FFFFFF; z-index: 1000000; } } .qr-scanner { background-image: linear-gradient( 0deg, transparent 24%, rgba(32, 255, 77, 0.1) 25%, rgba(32, 255, 77, 0.1) 26%, transparent 27%, transparent 74%, rgba(32, 255, 77, 0.1) 75%, rgba(32, 255, 77, 0.1) 76%, transparent 77%, transparent ), linear-gradient( 90deg, transparent 24%, rgba(32, 255, 77, 0.1) 25%, rgba(32, 255, 77, 0.1) 26%, transparent 27%, transparent 74%, rgba(32, 255, 77, 0.1) 75%, rgba(32, 255, 77, 0.1) 76%, transparent 77%, transparent ); background-size: 3rem 3rem; background-position: -1rem -1rem; width: 100%; /* height: 100%; */ height: 100vh; position: relative; ">#1110; /* */ } .qr-scanner .box { width: 213px; height: 213px; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); overflow: hidden; border: 0.1rem solid rgba(0, 255, 51, 0.2); /* background: url('http://resource.beige.world/imgs/gongconghao.png') no-repeat center center; */ } .qr-scanner .line { height: calc(100% - 2px); width: 100%; background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #00ff33 211%); border-bottom: 3px solid #00ff33; transform: translateY(-100%); animation: radar-beam 2s infinite alternate; animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99); animation-delay: 1.4s; } .qr-scanner .box:after, .qr-scanner .box:before, .qr-scanner .angle:after, .qr-scanner .angle:before { content: ''; display: block; position: absolute; width: 3vw; height: 3vw; border: 0.2rem solid transparent; } .qr-scanner .box:after, .qr-scanner .box:before { top: 0; border-top-color: #00ff33; } .qr-scanner .angle:after, .qr-scanner .angle:before { bottom: 0; border-bottom-color: #00ff33; } .qr-scanner .box:before, .qr-scanner .angle:before { left: 0; border-left-color: #00ff33; } .qr-scanner .box:after, .qr-scanner .angle:after { right: 0; border-right-color: #00ff33; } @keyframes radar-beam { 0% { transform: translateY(-100%); } 100% { transform: translateY(0); } } </style>
4、显示的页面里使用: <template> <QrScanner v-if="showQrCodeReader" @decode="onDecodeHandler" @close="qrReaderClose"></QrScanner> </template> <script setup> import QrScanner from "@/components/QrScanner/index.vue" import { ref, reactive, onMounted, onUnmounted, nextTick, getCurrentInstance, watchEffect } from "vue"; const showQrCodeReader = ref(false) const paymentData = reactive({ orderId: '', authCode: '', drawer: false }) const qrcodeDetail = ref({}) /* data 的值为下列地址为扫描二维码后得到的数据 http://localhost:8000?content=p6hi1rQzu%2B956WKJ%2BomXQVxuvddBBgPdVxKvDXBiIoO9HhE%2FLdR0pyikGRrAYjMGbcraGZjravDGcAjYdDSu9Q%3D%3D 使用getContentFromUrl() 获取content的值 */ const onDecodeHandler = (data) => { paymentData.authCode = getContentFromUrl(data) showQrCodeReader.value = false } const qrReaderClose = () => { showQrCodeReader.value = false } const getContentFromUrl = (url) => { const urlObj = new URL(url); const content = urlObj.searchParams.get('content'); return content; } </script>
努力到无能为力,拼搏到感动自己。
欢迎大家在下方多多评论。