index.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <script setup>
  2. definePageMeta({
  3. layout: 'scan'
  4. })
  5. import {Html5Qrcode, Html5QrcodeSupportedFormats} from "html5-qrcode";
  6. const router = useRouter()
  7. const emit = defineEmits('oSuccess', 'onError')
  8. let scanInstance = null; // 扫码实例
  9. let results = ref(null); // 扫码结果
  10. const initScanInstance = () => {
  11. if (!scanInstance) {
  12. // reader放置扫码功能的元素ID
  13. scanInstance = new Html5Qrcode('qr-reader', {
  14. formatsToSupport: [
  15. Html5QrcodeSupportedFormats.QR_CODE,
  16. ],
  17. })
  18. }
  19. }
  20. const openQrcode = async () => {
  21. Html5Qrcode.getCameras()
  22. .then(devices => {
  23. if (devices && devices.length) {// 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
  24. initScanInstance()
  25. scanInstance.start(
  26. {facingMode: "environment"},
  27. {
  28. focusMode: 'continuous',
  29. fps: 1, // 可选,每n秒帧扫描一次
  30. qrbox: { // 扫描的UI框
  31. width: 250,
  32. height: 250
  33. },
  34. videoConstraints: {
  35. // width: 375,
  36. // height: (window.visualViewport.height - 50),
  37. aspectRatio: window.visualViewport.height / window.visualViewport.width,
  38. facingMode: "environment",
  39. }
  40. },
  41. (decodedText, decodedResult) => {
  42. showToast('识别成功')
  43. handleSuccess({decodedText, decodedResult})
  44. },
  45. (errorMessage, error) => {
  46. closeQrcode(errorMessage)
  47. }
  48. )
  49. }
  50. })
  51. .catch((err) => {
  52. // 错误信息处理仅供参考,具体情况看输出!!!
  53. let errorStr = ''
  54. if (typeof err === "string") {
  55. errorStr = err
  56. } else {
  57. if (err.name === "NotAllowedError")
  58. errorStr = "您需要授予相机访问权限"
  59. if (err.name === "NotFoundError") {
  60. errorStr = "未检测到摄像头"
  61. }
  62. if (err.name === "NotSupportedError")
  63. errorStr = "摄像头访问只支持在安全的上下文中,如https或localhost"
  64. if (err.name === "NotReadableError") errorStr = "摄像头被占用"
  65. if (err.name === "OverconstrainedError")
  66. errorStr = "安装摄像头不合适"
  67. if (err.name === "StreamApiNotSupportedError")
  68. errorStr = "此浏览器不支持流API"
  69. }
  70. showToast(errorStr)
  71. })
  72. }
  73. const scanLoadImg = (e) => {
  74. try {
  75. initScanInstance()
  76. scanInstance.scanFile(e.file, false)
  77. .then(result => {
  78. // 二维码结果
  79. console.log(result, '上传扫码成功')
  80. handleSuccess(result)
  81. })
  82. .catch(err => {
  83. console.error(err, '上传扫码失败')
  84. })
  85. } catch (e) {
  86. console.error(e, '失败')
  87. }
  88. }
  89. const getResults = (result) => {
  90. if (!result) {
  91. return {success: false, groupId: null, overTime: false}
  92. }
  93. const url = new URL(result);
  94. const urlParams = new URLSearchParams(url.search);
  95. const hasGroupId = urlParams.has('groupId');
  96. const hasTime = urlParams.has('time');
  97. console.log(url.search, hasGroupId, hasTime, 'hasTime')
  98. if (!hasGroupId || !hasTime) {
  99. return {success: false, groupId: null, overTime: false}
  100. }
  101. const givenDate = new Date(urlParams.get('time'));
  102. const currentDate = new Date();
  103. const givenDateCst = new Date(givenDate.getTime() + (8 * 60 * 60 * 1000));
  104. const currentDateCst = new Date(currentDate.getTime() + (8 * 60 * 60 * 1000));
  105. const diffInMilliseconds = currentDateCst - givenDateCst;
  106. const sevenDaysInMilliseconds = 7 * 24 * 60 * 60 * 1000;
  107. return {
  108. success: true,
  109. groupId: urlParams.get('groupId'),
  110. overTime: diffInMilliseconds > sevenDaysInMilliseconds
  111. }
  112. }
  113. const handleSuccess = (result) => {
  114. results.value = result
  115. router.replace({
  116. path: '/chat/qr-results',
  117. query: result ? getResults(result) : result
  118. })
  119. }
  120. const closeQrcode = () => {
  121. if (scanInstance && scanInstance.isScanning) scanInstance.stop()
  122. router.back()
  123. }
  124. onMounted(async () => {
  125. initScanInstance()
  126. await nextTick()
  127. await openQrcode()
  128. })
  129. onUnmounted(() => {
  130. if (scanInstance && scanInstance.isScanning) scanInstance.stop()
  131. })
  132. </script>
  133. <template>
  134. <div>
  135. <div v-if="false" class="h-200">
  136. {{ results }}
  137. </div>
  138. <div class="overlay">
  139. <div class="absolute w-full z-30 flex flex-row items-center justify-center p-12">
  140. <div class="absolute left-12 font-bold" @click="closeQrcode">
  141. <span class="iconfont icon-left text-white"></span>
  142. </div>
  143. <div class="text-base text-[#fff] font-bold">
  144. 扫描二维码
  145. </div>
  146. </div>
  147. <div class="relative qr-reader z-20" id="qr-reader"></div>
  148. <p class="absolute w-full p-12 text-center bottom-100 z-30 text-sm text-[#fff]">请将二维码对准扫码框中心</p>
  149. <div id="qr-code-file"
  150. 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">
  151. <van-uploader
  152. :preview-image="false"
  153. :after-read="scanLoadImg"
  154. accept="image/*"
  155. :multiple="false"
  156. :max-count="1"
  157. ><img src="~/assets/img/scan/pic.png" height="32" width="32" alt=""/>
  158. </van-uploader>
  159. </div>
  160. </div>
  161. </div>
  162. </template>
  163. <style scoped lang="scss">
  164. .overlay {
  165. position: fixed;
  166. bottom: 0;
  167. left: 0;
  168. z-index: 999999;
  169. width: 100%;
  170. height: 100%;
  171. background: rgba(0, 0, 0, 0.7);
  172. }
  173. </style>