|
@@ -0,0 +1,195 @@
|
|
|
+<script setup>
|
|
|
+import { Html5QrcodeScanner, Html5Qrcode, Html5QrcodeSupportedFormats, Html5QrcodeScanType } from 'html5-qrcode'
|
|
|
+const router = useRouter()
|
|
|
+definePageMeta({
|
|
|
+ layout: 'scan'
|
|
|
+})
|
|
|
+
|
|
|
+let scanInstance = null; // 扫码实例
|
|
|
+let results = ref(null); // 扫码结果
|
|
|
+
|
|
|
+const initScanInstance = () => {
|
|
|
+ if (!scanInstance) {
|
|
|
+ // reader放置扫码功能的元素ID
|
|
|
+ scanInstance = new Html5Qrcode('qr-reader', {
|
|
|
+ formatsToSupport: [
|
|
|
+ Html5QrcodeSupportedFormats.QR_CODE,
|
|
|
+ ],
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+const openQrcode = async () => {
|
|
|
+ Html5Qrcode.getCameras()
|
|
|
+ .then(devices => {
|
|
|
+ if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
|
|
|
+ initScanInstance()
|
|
|
+ scanInstance.start(
|
|
|
+ {facingMode: "environment"},
|
|
|
+ {
|
|
|
+ focusMode: 'continuous',
|
|
|
+ fps: 10, // 可选,每n秒帧扫描一次
|
|
|
+ qrbox: { // 扫描的UI框
|
|
|
+ width: 250,
|
|
|
+ height: 250
|
|
|
+ },
|
|
|
+ videoConstraints: {
|
|
|
+ // width: 375,
|
|
|
+ // height: (window.visualViewport.height - 50),
|
|
|
+ aspectRatio: window.visualViewport.height / window.visualViewport.width,
|
|
|
+ facingMode: "environment",
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (decodedText, decodedResult) => {
|
|
|
+ showToast('识别成功')
|
|
|
+ handleSuccess({decodedText, decodedResult})
|
|
|
+ },
|
|
|
+ (errorMessage, error) => {
|
|
|
+ handleFail(errorMessage)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ // 错误信息处理仅供参考,具体情况看输出!!!
|
|
|
+ let errorStr = ''
|
|
|
+ if (typeof err === "string") {
|
|
|
+ errorStr = err
|
|
|
+ } else {
|
|
|
+ if (err.name === "NotAllowedError")
|
|
|
+ errorStr = "您需要授予相机访问权限"
|
|
|
+ if (err.name === "NotFoundError") {
|
|
|
+ errorStr = "未检测到摄像头"
|
|
|
+ }
|
|
|
+ if (err.name === "NotSupportedError")
|
|
|
+ errorStr = "摄像头访问只支持在安全的上下文中,如https或localhost"
|
|
|
+ if (err.name === "NotReadableError") errorStr = "摄像头被占用"
|
|
|
+ if (err.name === "OverconstrainedError")
|
|
|
+ errorStr = "安装摄像头不合适"
|
|
|
+ if (err.name === "StreamApiNotSupportedError")
|
|
|
+ errorStr = "此浏览器不支持流API"
|
|
|
+ }
|
|
|
+ showToast(errorStr)
|
|
|
+ })
|
|
|
+}
|
|
|
+const scanLoadImg = (e) => {
|
|
|
+ try {
|
|
|
+ initScanInstance()
|
|
|
+ scanInstance.scanFile(e.file, false)
|
|
|
+ .then(result => {
|
|
|
+ // 二维码结果
|
|
|
+ console.log(result, '上传扫码成功')
|
|
|
+ handleSuccess(result)
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ handleFail(err)
|
|
|
+ })
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e, '失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getResults = (result) => {
|
|
|
+
|
|
|
+ if (!result) {
|
|
|
+ return {success: false, groupId: null, overTime: false}
|
|
|
+ }
|
|
|
+
|
|
|
+ const url = new URL(result);
|
|
|
+ const urlParams = new URLSearchParams(url.search);
|
|
|
+
|
|
|
+ const hasGroupId = urlParams.has('groupId');
|
|
|
+ const hasTime = urlParams.has('time');
|
|
|
+ console.log(url.search, hasGroupId, hasTime, 'hasTime')
|
|
|
+ if (!hasGroupId || !hasTime) {
|
|
|
+ return {success: false, groupId: null, overTime: false}
|
|
|
+ }
|
|
|
+
|
|
|
+ const givenDate = new Date(urlParams.get('time'));
|
|
|
+ const currentDate = new Date();
|
|
|
+ const givenDateCst = new Date(givenDate.getTime() + (8 * 60 * 60 * 1000));
|
|
|
+ const currentDateCst = new Date(currentDate.getTime() + (8 * 60 * 60 * 1000));
|
|
|
+
|
|
|
+ const diffInMilliseconds = currentDateCst - givenDateCst;
|
|
|
+ const sevenDaysInMilliseconds = 7 * 24 * 60 * 60 * 1000;
|
|
|
+
|
|
|
+ return {
|
|
|
+ success: true,
|
|
|
+ groupId: urlParams.get('groupId'),
|
|
|
+ overTime: diffInMilliseconds > sevenDaysInMilliseconds
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleSuccess = (result) => {
|
|
|
+ results.value = result
|
|
|
+ router.replace({
|
|
|
+ path: '/chat/qr-results',
|
|
|
+ query: result ? getResults(result) : result
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const handleFail = (err) => {
|
|
|
+ alert(err)
|
|
|
+/* router.replace({
|
|
|
+ path: '/chat/qr-results',
|
|
|
+ query: {success: false, groupId: null, overTime: false}
|
|
|
+ })*/
|
|
|
+}
|
|
|
+
|
|
|
+const closeQrcode = () => {
|
|
|
+ if (scanInstance && scanInstance.isScanning) scanInstance.stop()
|
|
|
+ router.back()
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await nextTick()
|
|
|
+ // initScanInstance()
|
|
|
+ await openQrcode()
|
|
|
+})
|
|
|
+onUnmounted(() => {
|
|
|
+ if (scanInstance && scanInstance.isScanning) scanInstance.stop()
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <div v-if="false" class="h-200">
|
|
|
+ {{ results }}
|
|
|
+ </div>
|
|
|
+ <div class="overlay">
|
|
|
+ <div class="absolute w-full z-30 flex flex-row items-center justify-center p-12">
|
|
|
+ <div class="absolute left-12 font-bold" @click="closeQrcode">
|
|
|
+ <span class="iconfont icon-left text-white"></span>
|
|
|
+ </div>
|
|
|
+ <div class="text-base text-[#fff] font-bold">
|
|
|
+ 扫描二维码
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="relative qr-reader z-20" id="qr-reader"></div>
|
|
|
+ <p class="absolute w-full p-12 text-center bottom-100 z-30 text-sm text-[#fff]">请将二维码对准扫码框中心</p>
|
|
|
+ <div id="qr-code-file"
|
|
|
+ class="absolute w-54 h-54 grid place-items-center bottom-40 z-30 rounded-full bg-[#000]/80 text-white left-1/2 -translate-x-1/2">
|
|
|
+ <van-uploader
|
|
|
+ :preview-image="false"
|
|
|
+ :after-read="scanLoadImg"
|
|
|
+ accept="image/*"
|
|
|
+ :multiple="false"
|
|
|
+ :max-count="1"
|
|
|
+ ><img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
|
|
|
+ </van-uploader>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.overlay {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: 999999;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(0, 0, 0, 0.7);
|
|
|
+}
|
|
|
+</style>
|