فهرست منبع

feat: 扫码功能

qinyuyue 2 ماه پیش
والد
کامیت
163fe64791
6فایلهای تغییر یافته به همراه230 افزوده شده و 228 حذف شده
  1. 0 127
      src/components/ScanCode/index.vue
  2. 101 0
      src/composables/useScanCode.js
  3. 11 0
      src/layouts/scan.vue
  4. 2 5
      src/pages/profile/index.vue
  5. 92 96
      src/pages/scan/index.vue
  6. 24 0
      src/pages/scan/scan-result/index.vue

+ 0 - 127
src/components/ScanCode/index.vue

@@ -1,127 +0,0 @@
-<script setup>
-import {Html5Qrcode, Html5QrcodeSupportedFormats} from "html5-qrcode";
-
-const emit = defineEmits('oSuccess', 'onError')
-const slots = useSlots();
-// 判断是否有 default 插槽
-const hasDefaultSlot = computed(() => !!slots.default);
-
-let isShow = ref(false)
-let results = ref(null)
-
-let scanInstance = null
-const openQrcode = async () => {
-  Html5Qrcode.getCameras()
-      .then(devices => {
-        alert(10)
-        if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
-          if(!scanInstance) {
-            // reader  放置扫码功能的元素ID
-            scanInstance = new Html5Qrcode('qr-reader', {
-              formatsToSupport: [
-                Html5QrcodeSupportedFormats.QR_CODE,
-              ],
-            })
-          }
-
-          isShow.value = true
-          scanInstance.start(
-              {facingMode: "environment"},
-              {
-                focusMode: 'continuous',
-                fps: 1, // 可选,每n秒帧扫描一次
-                qrbox: { // 扫描的UI框
-                  width: 250,
-                  height: 250
-                },
-                videoConstraints: {
-                  width: 375,
-                  aspectRatio: 16 / 9,
-                  facingMode: "environment",
-                }
-              },
-              (decodedText, decodedResult) => {
-                showToast('识别成功')
-                // 扫描结果
-                results.value = {
-                  decodedText,
-                  decodedResult
-                }
-                isShow.value = false
-                scanInstance.stop()
-                emit('oSuccess', {
-                  decodedText,
-                  decodedResult
-                })
-              },
-              (errorMessage, error) => {
-                emit('onError', {
-                  errorMessage,
-                  error
-                })
-              }
-          )
-        }
-      })
-      .catch((err) => {
-        isShow.value = false
-        // 错误信息处理仅供参考,具体情况看输出!!!
-        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 closeQrcode = () => {
-  isShow.value = false
-  if (scanInstance) scanInstance.stop()
-}
-
-onUnmounted(() => {
-  if (scanInstance) scanInstance.stop()
-})
-</script>
-
-<template>
-  <div>
-    <template v-if="hasDefaultSlot">
-      <slot></slot>
-    </template>
-    <template v-else>
-      <van-button @click="openQrcode" size="mini">扫一扫</van-button>
-    </template>
-    <div v-if="false" class="h-200">
-      {{ results }}
-    </div>
-    <div class="overlay" v-show="isShow">
-      <div class="absolute left-20 top-20 bg-[red] z-30" @click="closeQrcode">返回</div>
-      <div class="absolute qr-reader z-20" id="qr-reader"></div>
-    </div>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  z-index: 999999;
-  width: 100%;
-  height: 100%;
-  background: rgba(0, 0, 0, 0.5);
-}
-</style>

+ 101 - 0
src/composables/useScanCode.js

@@ -0,0 +1,101 @@
+import {Html5Qrcode, Html5QrcodeScanner, Html5QrcodeScanType, Html5QrcodeSupportedFormats} from "html5-qrcode";
+
+export const useScanCode = (elementId) => {
+    const router = useRouter()
+
+    let scanInstance = null; // 扫码实例
+    let results = ref(null); // 扫码结果
+
+    const initScanInstance = () => {
+        if(!elementId) throw error('请传入放置扫码功能的元素ID')
+        if (!scanInstance) {
+            // reader放置扫码功能的元素ID
+            scanInstance = new Html5Qrcode(elementId, {
+                formatsToSupport: [
+                    Html5QrcodeSupportedFormats.QR_CODE,
+                ],
+            })
+        }
+    }
+    const openQrcode = async () => {
+        Html5Qrcode.getCameras()
+            .then(devices => {
+                if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
+                    initScanInstance()
+
+                    // isShow.value = true
+                    scanInstance.start(
+                        {facingMode: "environment"},
+                        {
+                            focusMode: 'continuous',
+                            fps: 1, // 可选,每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('识别成功')
+                            // 扫描结果
+                            results.value = {
+                                decodedText,
+                                decodedResult
+                            }
+                            // scanInstance.stop()
+                        },
+                        (errorMessage, error) => {
+                            closeQrcode(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)
+                closeQrcode()
+            })
+    }
+
+    const closeQrcode = () => {
+        // if (scanInstance) scanInstance.stop()
+        console.log(router, 'router')
+        router.back()
+    }
+
+
+    onMounted(() => {
+        initScanInstance()
+        openQrcode()
+    })
+    onUnmounted(() => {
+        if (scanInstance) scanInstance.stop()
+    })
+
+    return {
+        results,
+        closeQrcode
+    }
+}

+ 11 - 0
src/layouts/scan.vue

@@ -0,0 +1,11 @@
+<template>
+  <div class="w-full h-full">
+    <slot />
+  </div>
+</template>
+<script setup lang="ts">
+
+</script>
+<style scoped lang="scss">
+
+</style>

+ 2 - 5
src/pages/profile/index.vue

@@ -2,10 +2,8 @@
   <div class="profile-page bg-[#f8f8f8]">
     <div
         class="pt-66 pb-14 px-26 relative w-full bg-[url('~/assets/img/profile/profile_banner.png')] bg-no-repeat bg-cover">
-      <NuxtLink to="/chat/sweep" class="absolute top-17 right-14">
-        <ScanCode>
-          <img src="~/assets/img/profile/pofile_qr.png" height="33" width="33" alt="扫一扫"/>
-        </ScanCode>
+      <NuxtLink to="/scan" class="absolute top-17 right-14">
+        <img src="~/assets/img/profile/pofile_qr.png" height="33" width="33" alt="扫一扫"/>
       </NuxtLink>
       <div class="w-335 left-20 top-66 right-20 ">
         <NuxtLink to="/profile/userInfo">
@@ -79,7 +77,6 @@ import profile_colection from "~/assets/img/profile/profile_colection.png";
 import profile_car_order from "~/assets/img/profile/profile_car_order.png";
 import profile_visa_order from "~/assets/img/profile/profile_visa_order.png";
 import profile_my_comment from "~/assets/img/profile/profile_my_comment.png";
-// import {formatNumber} from "~/utils";
 
 const userInfoStore = useUserInfoStore();
 const {userInfo} = storeToRefs(userInfoStore);

+ 92 - 96
src/pages/scan/index.vue

@@ -1,29 +1,32 @@
 <script setup>
-import {Html5Qrcode, Html5QrcodeScanner, Html5QrcodeScanType, Html5QrcodeSupportedFormats} from "html5-qrcode";
+definePageMeta({
+  layout: 'scan'
+})
+
+import {Html5Qrcode, Html5QrcodeSupportedFormats} from "html5-qrcode";
+
+const router = useRouter()
 
 const emit = defineEmits('oSuccess', 'onError')
-const slots = useSlots();
-// 判断是否有 default 插槽
-const hasDefaultSlot = computed(() => !!slots.default);
 
-let isShow = ref(false)
-let results = ref(null)
+let scanInstance = null; // 扫码实例
+let results = ref(null); // 扫码结果
 
-let scanInstance = 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) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
-          if (!scanInstance) {
-            // reader  放置扫码功能的元素ID
-            scanInstance = new Html5Qrcode('qr-reader', {
-              formatsToSupport: [
-                Html5QrcodeSupportedFormats.QR_CODE,
-              ],
-            })
-          }
-
-          isShow.value = true
+          initScanInstance()
           scanInstance.start(
               {facingMode: "environment"},
               {
@@ -42,29 +45,15 @@ const openQrcode = async () => {
               },
               (decodedText, decodedResult) => {
                 showToast('识别成功')
-                // 扫描结果
-                results.value = {
-                  decodedText,
-                  decodedResult
-                }
-                isShow.value = false
-                scanInstance.stop()
-                emit('oSuccess', {
-                  decodedText,
-                  decodedResult
-                })
+                handleSuccess({decodedText, decodedResult})
               },
               (errorMessage, error) => {
-                emit('onError', {
-                  errorMessage,
-                  error
-                })
+                closeQrcode(errorMessage)
               }
           )
         }
       })
       .catch((err) => {
-        isShow.value = false
         // 错误信息处理仅供参考,具体情况看输出!!!
         let errorStr = ''
         if (typeof err === "string") {
@@ -86,68 +75,84 @@ const openQrcode = async () => {
         showToast(errorStr)
       })
 }
-const closeQrcode = () => {
-  isShow.value = false
-  if (scanInstance) scanInstance.stop()
+const scanLoadImg = (e) => {
+  try {
+    initScanInstance()
+    scanInstance.scanFile(e.file, false)
+        .then(result => {
+          // 二维码结果
+          console.log(result, '上传扫码成功')
+          handleSuccess(result)
+        })
+        .catch(err => {
+          console.error(err, '上传扫码失败')
+        })
+  } catch (e) {
+    console.error(e, '失败')
+  }
 }
 
+const getResults = (result) => {
 
-let fileList = ref([])
-const uploadImg = () => {
-  try {
-    window.qrcode.callback = (result) => {
-      showToast(result);
-    };
-    let file = state.fileList[0].file;
-    let reader = new FileReader();
-    reader.onload = (function () {
-      return function (e) {
-        window.qrcode.decode(e.target.result);
-      };
-    })(file);
-    reader.readAsDataURL(file);
-  } catch (error) {
-    console.log(error);
-    showToast({
-      message: error+"识别失败!",
-      duration: 2000,
-    });
+  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 selectFile = ()=> {
-  let odeScanner = new Html5QrcodeScanner(
-      "qr-code-file", { fps: 1 , qrbox: { // 扫描的UI框
-          width: 250,
-          height: 250
-        },});
-  odeScanner.render((e) => {
-    console.log(e)
-  });
-  console.log(odeScanner, 'odeScanner')
+const handleSuccess = (result) => {
+  results.value = result
+  router.replace({
+    path: '/scan/scan-result',
+    query: result ? getResults(result) : result
+  })
 }
 
+const closeQrcode = () => {
+  if (scanInstance && scanInstance.isScanning) scanInstance.stop()
+  router.back()
+}
 
-onMounted(() => {
-  selectFile()
+onMounted(async () => {
+  initScanInstance()
+  await nextTick()
+  await openQrcode()
 })
 onUnmounted(() => {
-  if (scanInstance) scanInstance.stop()
+  if (scanInstance && scanInstance.isScanning) scanInstance.stop()
 })
+
 </script>
 
 <template>
   <div>
-    <template v-if="hasDefaultSlot">
-      <slot></slot>
-    </template>
-    <template v-else>
-      <van-button @click="openQrcode" size="mini">扫一扫</van-button>
-    </template>
     <div v-if="false" class="h-200">
       {{ results }}
     </div>
-    <div class="overlay" v-show="isShow">
+    <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>
@@ -158,23 +163,18 @@ onUnmounted(() => {
       </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">
-        <img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
+      <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 id="qr-code-file" class="w-54 h-54 grid place-items-center bottom-40 z-30 rounded-full bg-[#000]/80 text-white">
-      <img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
-    </div>
-    <van-uploader
-        v-model="fileList"
-        :preview-image="false"
-        :after-read="uploadImg"
-        accept="image/*"
-        :multiple="false"
-        :max-count="1"
-    >
-      <van-icon name="photo-o"
-      /></van-uploader>
   </div>
 </template>
 
@@ -185,11 +185,7 @@ onUnmounted(() => {
   left: 0;
   z-index: 999999;
   width: 100%;
-  height: calc(100% - 50px);
+  height: 100%;
   background: rgba(0, 0, 0, 0.7);
-
-  .scan-instruction {
-
-  }
 }
 </style>

+ 24 - 0
src/pages/scan/scan-result/index.vue

@@ -0,0 +1,24 @@
+<script setup>
+definePageMeta({
+  layout: 'scan'
+})
+const router = useRouter()
+const route = useRoute()
+console.log(route, 'route')
+</script>
+
+<template>
+<div>
+  <van-nav-bar
+      title="扫码结果"
+      left-arrow
+      @click-left="router.back()"
+  />
+
+  {{route.query}}
+</div>
+</template>
+
+<style scoped lang="scss">
+
+</style>