punchQRcode.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <template>
  2. <div class="c_box">
  3. <div class="choice_box">
  4. <div class="title" style="display: flex;">
  5. <span style="min-width: fit-content;">{{ tindex + 1 + '、' }}</span>
  6. <span>{{ checkJson.title }}</span>
  7. </div>
  8. <div
  9. class="detail"
  10. v-if="checkJson.detail"
  11. v-html="checkJson.detail"
  12. style="color: #00000099;margin-top: 5px;"
  13. ></div>
  14. <div
  15. class="detail"
  16. v-if="checkJson.answer2 && typeof checkJson.answer2 == 'string'"
  17. v-html="checkJson.answer2"
  18. style="color: #000000;margin-top: 5px;"
  19. ></div>
  20. <div class="choices">
  21. <el-button type="primary" size="small" @click="sweepBtn">扫一扫</el-button>
  22. </div>
  23. </div>
  24. <!-- <sweepPage ref="sweepPageRef" @success="sweepSuccess" @error="sweepError" /> -->
  25. <!-- <sweepPage_html5_qrcode ref="sweepPage_html5_qrcodeRef" @success="sweepSuccess" @error="sweepError" /> -->
  26. <qrScanner ref="qrScannerRef" @success="sweepSuccess" />
  27. <weChatQrcode ref="weChatQrcodeRef" @success="sweepSuccess" />
  28. </div>
  29. </template>
  30. <script>
  31. // import sweepPage from '@/components/sweepPage.vue'
  32. // import sweepPage_html5_qrcode from '@/components/sweepPage_html5_qrcode.vue'
  33. import qrScanner from '@/components/qrScanner.vue'
  34. import weChatQrcode from '@/components/weChatQrcode.vue'
  35. export default {
  36. props: {
  37. tindex: {
  38. type: Number
  39. },
  40. cJson: {
  41. type: Object
  42. },
  43. checktype: {
  44. type: Number,
  45. default: 1
  46. },
  47. see: {
  48. type: Boolean,
  49. default: false
  50. }
  51. },
  52. components: {
  53. // sweepPage,
  54. // sweepPage_html5_qrcode
  55. qrScanner,
  56. weChatQrcode
  57. },
  58. data() {
  59. return {
  60. option: {
  61. 1: { name: '附件' }
  62. },
  63. userid: this.$route.query.userid,
  64. checkJson: {
  65. title: '',
  66. detail: ''
  67. }
  68. }
  69. },
  70. watch: {
  71. checkJson: {
  72. handler(newValue) {
  73. this.$emit('update:cJson', newValue)
  74. },
  75. deep: true
  76. }
  77. },
  78. methods: {
  79. depthCopy(s) {
  80. return JSON.parse(JSON.stringify(s))
  81. },
  82. sweepBtn() {
  83. if (!this.isWechatBrowser) {
  84. this.$refs.weChatQrcodeRef.open()
  85. } else {
  86. this.$refs.qrScannerRef.open()
  87. }
  88. },
  89. sweepSuccess(result) {
  90. const _valueAndTime = this.extractValueAndTime(result)
  91. if (!_valueAndTime) {
  92. if (!this.isWechatBrowser) {
  93. this.$refs.weChatQrcodeRef.close()
  94. } else {
  95. this.$refs.qrScannerRef.close()
  96. }
  97. return this.$toast.fail('扫码的格式不正确')
  98. }
  99. // 判断当前时间和扫码时间是否超过设置的时间
  100. if (_valueAndTime.time) {
  101. const currentTime = new Date().getTime() // 获取当前时间戳
  102. const checkTime = new Date(_valueAndTime.time).getTime() // 获取_valueAndTime.time的时间戳
  103. const minutes = parseInt(this.checkJson.minutes) // 获取minutes对象
  104. if (currentTime - checkTime > minutes * 60 * 1000) {
  105. // 超过指定分钟数的逻辑处理
  106. this.$toast.fail('该二维码已失效')
  107. } else {
  108. // 在限制时间内的逻辑处理
  109. this.checkJson.answer2 = _valueAndTime.value
  110. this.checkJson.codeScanningTime = _valueAndTime.time
  111. }
  112. }
  113. console.log(_valueAndTime)
  114. // this.checkJson.answer2 = result;
  115. if (!this.isWechatBrowser) {
  116. this.$refs.weChatQrcodeRef.close()
  117. } else {
  118. this.$refs.qrScannerRef.close()
  119. }
  120. },
  121. sweepError(error) {
  122. // this.$message.error(error)
  123. this.$toast.fail(error)
  124. // if (error.name === 'NotAllowedError') {
  125. // this.$toast.fail('您需要授予相机访问权限')
  126. // } else if (error.name === 'NotFoundError') {
  127. // this.$toast.fail('这个设备上没有摄像头')
  128. // } else if (error.name === 'NotSupportedError') {
  129. // this.$toast.fail('所需的安全上下文(HTTPS、本地主机)')
  130. // } else if (error.name === 'NotReadableError') {
  131. // this.$toast.fail('相机被占用')
  132. // } else if (error.name === 'OverconstrainedError') {
  133. // this.$toast.fail('安装摄像头不合适')
  134. // } else if (error.name === 'StreamApiNotSupportedError') {
  135. // this.$toast.fail('此浏览器不支持流API')
  136. // } else {
  137. // this.$toast.fail(error.name)
  138. // }
  139. },
  140. extractValueAndTime(inputStr) {
  141. try {
  142. // 1. 同时提取前缀文本和时间数字(格式:前缀 + 10位数字)
  143. const match = inputStr.match(/^(.*?)(\d{10})$/)
  144. if (!match || match[2].length !== 10) {
  145. throw new Error('格式无效:需以10位数字结尾')
  146. }
  147. const [full, value, timeStr] = match // 解构匹配结果
  148. if (!value || !timeStr) {
  149. throw new Error('未找到有效的前缀或时间编码')
  150. }
  151. // 2. 解析时间部分(格式:YYMMDDHHMM → 25 03 24 15 44)
  152. const year = `20${timeStr.substring(0, 2)}`
  153. const month = timeStr.substring(2, 4)
  154. const day = timeStr.substring(4, 6)
  155. const hours = timeStr.substring(6, 8)
  156. const minutes = timeStr.substring(8, 10)
  157. // 3. 构造 Date 对象并验证
  158. const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hours), parseInt(minutes), 0)
  159. if (isNaN(date.getTime())) {
  160. throw new Error('解析到无效的日期时间')
  161. }
  162. // 4. 格式化为目标字符串
  163. const format = num => String(num).padStart(2, '0')
  164. const formattedTime =
  165. `${date.getFullYear()}/${format(date.getMonth() + 1)}/${format(date.getDate())} ` +
  166. `${format(date.getHours())}:${format(date.getMinutes())}:00`
  167. // 5. 返回对象
  168. return { value, time: formattedTime }
  169. } catch (error) {
  170. console.error('错误:', error.message)
  171. return null
  172. }
  173. },
  174. // 检查是否在微信浏览器中
  175. isWechatBrowser() {
  176. const ua = navigator.userAgent.toLowerCase()
  177. return ua.indexOf('micromessenger') !== -1
  178. }
  179. },
  180. mounted() {
  181. this.checkJson = this.cJson ? this.depthCopy(this.cJson) : undefined
  182. }
  183. }
  184. </script>
  185. <style scoped>
  186. .c_box {
  187. width: 100%;
  188. position: relative;
  189. }
  190. /* .mask {
  191. position: absolute;
  192. height: 100%;
  193. width: 100%;
  194. z-index: 2;
  195. } */
  196. .choice_box {
  197. white-space: pre-line;
  198. }
  199. .choice_box > .title {
  200. font-weight: bold;
  201. width: 100%;
  202. word-break: break-all;
  203. }
  204. .choice_box > .choices {
  205. margin-top: 10px;
  206. }
  207. .choices > .page {
  208. margin-top: 10px;
  209. display: flex;
  210. align-items: center;
  211. }
  212. .p_page {
  213. margin: 0 10px;
  214. }
  215. .course {
  216. display: flex;
  217. align-items: center;
  218. cursor: pointer;
  219. }
  220. .course + .course {
  221. margin-top: 10px;
  222. }
  223. .course > .banner {
  224. min-width: 100px;
  225. width: 100px;
  226. height: 100px;
  227. border-radius: 5px;
  228. overflow: hidden;
  229. border: 1px solid #3896fc;
  230. box-sizing: border-box;
  231. padding: 5px;
  232. margin-right: 15px;
  233. }
  234. .course > .banner > img {
  235. width: 100%;
  236. height: 100%;
  237. object-fit: cover;
  238. }
  239. .course > .content {
  240. max-width: calc(100% - 100px - 15px);
  241. }
  242. .course > .content > .c_c {
  243. display: flex;
  244. }
  245. .course > .content > .c_c + .c_c {
  246. margin-top: 5px;
  247. }
  248. .course > .content > .c_c span:nth-child(1) {
  249. min-width: fit-content;
  250. }
  251. .course > .content > .c_c span:nth-child(2) {
  252. word-break: break-word;
  253. }
  254. </style>