|
@@ -0,0 +1,253 @@
|
|
|
+<template>
|
|
|
+ <div class="c_box">
|
|
|
+ <div class="choice_box">
|
|
|
+ <div class="title" style="display: flex;">
|
|
|
+ <span style="min-width: fit-content;">{{ tindex + 1 + '、' }}</span>
|
|
|
+ <span>{{ checkJson.title }}</span>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="detail"
|
|
|
+ v-if="checkJson.detail"
|
|
|
+ v-html="checkJson.detail"
|
|
|
+ style="color: #00000099;margin-top: 5px;"
|
|
|
+ ></div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="detail"
|
|
|
+ v-if="checkJson.answer2 && typeof checkJson.answer2=='string'"
|
|
|
+ v-html="checkJson.answer2"
|
|
|
+ style="color: #000000;margin-top: 5px;"
|
|
|
+ ></div>
|
|
|
+
|
|
|
+ <div class="choices">
|
|
|
+ <el-button type="primary" size="small" @click="sweepBtn">扫一扫</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <sweepPage ref="sweepPageRef" @success="sweepSuccess" @error="sweepError"/>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import sweepPage from '@/components/sweepPage.vue';
|
|
|
+export default {
|
|
|
+ props: {
|
|
|
+ tindex: {
|
|
|
+ type: Number
|
|
|
+ },
|
|
|
+ cJson: {
|
|
|
+ type: Object
|
|
|
+ },
|
|
|
+ checktype: {
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ see: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ components:{
|
|
|
+ sweepPage
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ option: {
|
|
|
+ 1: { name: '附件' }
|
|
|
+ },
|
|
|
+ userid: this.$route.query.userid,
|
|
|
+ checkJson: {
|
|
|
+ title:"",
|
|
|
+ detail:""
|
|
|
+ },
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ checkJson: {
|
|
|
+ handler(newValue) {
|
|
|
+ this.$emit('update:cJson', newValue)
|
|
|
+ },
|
|
|
+ deep: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ depthCopy(s) {
|
|
|
+ return JSON.parse(JSON.stringify(s))
|
|
|
+ },
|
|
|
+ sweepBtn(){
|
|
|
+ this.$refs.sweepPageRef.open();
|
|
|
+ },
|
|
|
+ sweepSuccess(result){
|
|
|
+ let _valueAndTime = this.extractValueAndTime(result);
|
|
|
+
|
|
|
+ if(!_valueAndTime)return this.$toast.fail("扫码的格式不正确");
|
|
|
+
|
|
|
+ //判断当前时间和扫码时间是否超过设置的时间
|
|
|
+ if(_valueAndTime.time){
|
|
|
+ let currentTime = new Date().getTime(); // 获取当前时间戳
|
|
|
+ let checkTime = new Date(_valueAndTime.time).getTime(); // 获取_valueAndTime.time的时间戳
|
|
|
+ let minutes = parseInt(this.checkJson.minutes); // 获取minutes对象
|
|
|
+
|
|
|
+ if ((currentTime - checkTime) > minutes * 60 * 1000) {
|
|
|
+ // 超过指定分钟数的逻辑处理
|
|
|
+ this.$toast.fail("该二维码已失效")
|
|
|
+ } else {
|
|
|
+ // 在限制时间内的逻辑处理
|
|
|
+ this.checkJson.answer2 = _valueAndTime.value;
|
|
|
+ this.checkJson.codeScanningTime = _valueAndTime.time;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log(_valueAndTime)
|
|
|
+ // this.checkJson.answer2 = result;
|
|
|
+ this.$refs.sweepPageRef.close();
|
|
|
+ },
|
|
|
+ sweepError(error){
|
|
|
+ if (error.name === 'NotAllowedError') {
|
|
|
+ this.$toast.fail('您需要授予相机访问权限')
|
|
|
+ } else if (error.name === 'NotFoundError') {
|
|
|
+ this.$toast.fail('这个设备上没有摄像头')
|
|
|
+ } else if (error.name === 'NotSupportedError') {
|
|
|
+ this.$toast.fail('所需的安全上下文(HTTPS、本地主机)')
|
|
|
+ } else if (error.name === 'NotReadableError') {
|
|
|
+ this.$toast.fail('相机被占用')
|
|
|
+ } else if (error.name === 'OverconstrainedError') {
|
|
|
+ this.$toast.fail('安装摄像头不合适')
|
|
|
+ } else if (error.name === 'StreamApiNotSupportedError') {
|
|
|
+ this.$toast.fail('此浏览器不支持流API')
|
|
|
+ }else{
|
|
|
+ this.$toast.fail(error.name)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ extractValueAndTime(inputStr) {
|
|
|
+ try {
|
|
|
+ // 1. 同时提取前缀文本和时间数字(格式:前缀 + 10位数字)
|
|
|
+ const match = inputStr.match(/^(.*?)(\d{10})$/);
|
|
|
+ if (!match || match[2].length !== 10) {
|
|
|
+ throw new Error("格式无效:需以10位数字结尾");
|
|
|
+ }
|
|
|
+
|
|
|
+ const [full, value, timeStr] = match; // 解构匹配结果
|
|
|
+ if (!value || !timeStr) {
|
|
|
+ throw new Error("未找到有效的前缀或时间编码");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 解析时间部分(格式:YYMMDDHHMM → 25 03 24 15 44)
|
|
|
+ const year = `20${timeStr.substring(0, 2)}`;
|
|
|
+ const month = timeStr.substring(2, 4);
|
|
|
+ const day = timeStr.substring(4, 6);
|
|
|
+ const hours = timeStr.substring(6, 8);
|
|
|
+ const minutes = timeStr.substring(8, 10);
|
|
|
+
|
|
|
+ // 3. 构造 Date 对象并验证
|
|
|
+ const date = new Date(
|
|
|
+ parseInt(year),
|
|
|
+ parseInt(month) - 1,
|
|
|
+ parseInt(day),
|
|
|
+ parseInt(hours),
|
|
|
+ parseInt(minutes),
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ if (isNaN(date.getTime())) {
|
|
|
+ throw new Error("解析到无效的日期时间");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 格式化为目标字符串
|
|
|
+ const format = num => String(num).padStart(2, "0");
|
|
|
+ const formattedTime =
|
|
|
+ `${date.getFullYear()}/${format(date.getMonth() + 1)}/${format(
|
|
|
+ date.getDate()
|
|
|
+ )} ` + `${format(date.getHours())}:${format(date.getMinutes())}:00`;
|
|
|
+
|
|
|
+ // 5. 返回对象
|
|
|
+ return { value, time: formattedTime };
|
|
|
+ } catch (error) {
|
|
|
+ console.error("错误:", error.message);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.checkJson = this.cJson ? this.depthCopy(this.cJson) : undefined
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.c_box {
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+/* .mask {
|
|
|
+ position: absolute;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ z-index: 2;
|
|
|
+} */
|
|
|
+
|
|
|
+.choice_box {
|
|
|
+ white-space: pre-line;
|
|
|
+}
|
|
|
+
|
|
|
+.choice_box > .title {
|
|
|
+ font-weight: bold;
|
|
|
+ width: 100%;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
+
|
|
|
+.choice_box > .choices {
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.choices > .page {
|
|
|
+ margin-top: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.p_page {
|
|
|
+ margin: 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.course {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.course + .course {
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.course > .banner {
|
|
|
+ min-width: 100px;
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ border-radius: 5px;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1px solid #3896fc;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 5px;
|
|
|
+ margin-right: 15px;
|
|
|
+}
|
|
|
+.course > .banner > img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+.course > .content {
|
|
|
+ max-width: calc(100% - 100px - 15px);
|
|
|
+}
|
|
|
+.course > .content > .c_c {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.course > .content > .c_c + .c_c {
|
|
|
+ margin-top: 5px;
|
|
|
+}
|
|
|
+.course > .content > .c_c span:nth-child(1) {
|
|
|
+ min-width: fit-content;
|
|
|
+}
|
|
|
+.course > .content > .c_c span:nth-child(2) {
|
|
|
+ word-break: break-word;
|
|
|
+}
|
|
|
+</style>
|