jiajunchen 16 uur geleden
bovenliggende
commit
6edcabae44
5 gewijzigde bestanden met toevoegingen van 803 en 10 verwijderingen
  1. 1
    0
      package.json
  2. 5
    0
      src/router/index.ts
  3. 1
    1
      src/view/dati/classOne/line.vue
  4. 719
    0
      src/view/dati/examCheck/fcbkdatistart.vue
  5. 77
    9
      src/view/dati/examCheck/index.vue

+ 1
- 0
package.json Bestand weergeven

@@ -32,6 +32,7 @@
32 32
 		"marked": "^4.3.0",
33 33
 		"pinia": "^2.1.7",
34 34
 		"pinia-plugin-persistedstate": "^3.2.1",
35
+		"qrcode": "^1.5.4",
35 36
 		"qs": "^6.11.2",
36 37
 		"vant": "^4.0.0",
37 38
 		"video.js": "^8.23.4",

+ 5
- 0
src/router/index.ts Bestand weergeven

@@ -699,6 +699,11 @@ const router = createRouter({
699 699
 			name: '人员学习',
700 700
 			component: () => import('@/view/dati/classOne/learning.vue')
701 701
 		},
702
+		{
703
+			path: '/fcbkdatistart',
704
+			name: '逢查必考跳转答题',
705
+			component: () => import('@/view/dati/examCheck/fcbkdatistart.vue')
706
+		},
702 707
 
703 708
 
704 709
 	]

+ 1
- 1
src/view/dati/classOne/line.vue Bestand weergeven

@@ -212,7 +212,7 @@ const route = useRoute();
212 212
 
213 213
 const courseId = route.query.courseId;
214 214
 const userId = route.query.userId;
215
-if (userId == '' || userId == 'undefined') userId = localStorage.getItem('userId')
215
+
216 216
 const todayStr = route.query.todayStr;
217 217
 
218 218
 const questions = ref([]);

+ 719
- 0
src/view/dati/examCheck/fcbkdatistart.vue Bestand weergeven

@@ -0,0 +1,719 @@
1
+<template>
2
+  <van-sticky>
3
+    <van-nav-bar>
4
+      <!-- <template #left>
5
+        <van-icon name="arrow-left" size="18" @click="goBack" />
6
+      </template> -->
7
+      <template #title> 答题考试 </template>
8
+      <template #right>
9
+        <!-- 提交按钮 -->
10
+        <van-button @click="checkBeforeSubmit" round color="linear-gradient(to right, #18FFFF, #304FFE)"
11
+                    style="height: 30px; width: 80px">交卷</van-button>
12
+      </template>
13
+    </van-nav-bar>
14
+  </van-sticky>
15
+  <van-overlay :show="overlayloading">
16
+    <div class="wrapper">
17
+      <van-loading color="#0094ff"> 加载中... </van-loading>
18
+    </div>
19
+  </van-overlay>
20
+
21
+  <div class="quiz-page">
22
+    <div class="question-number">
23
+      {{ activeIndex + 1 }}/{{ questions.length }}
24
+    </div>
25
+    <div v-if="questions.length > 0" class="question-content">
26
+      <!-- 题干 -->
27
+      <p class="kong">
28
+        <img
29
+          :src="getQuestionTypeImage(currentQuestion.category)"
30
+          class="question-type-img"
31
+          alt=""
32
+        />
33
+        {{ currentQuestion.stem }}
34
+      </p>
35
+
36
+
37
+      <!-- 单选题 -->
38
+      <div v-if="currentQuestion.category === '单选'">
39
+        <van-radio-group v-model="userAnswers[currentQuestion.id]">
40
+          <van-radio :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-radio>
41
+          <van-radio :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-radio>
42
+          <van-radio :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-radio>
43
+          <van-radio :name="'D'" class="kong" v-if="currentQuestion.optionD">D.
44
+            {{ currentQuestion.optionD }}</van-radio>
45
+          <van-radio :name="'E'" class="kong" v-if="currentQuestion.optionE">E.
46
+            {{ currentQuestion.optionE }}</van-radio>
47
+        </van-radio-group>
48
+      </div>
49
+
50
+      <!-- 多选题 -->
51
+      <div v-if="currentQuestion.category === '多选'">
52
+        <van-checkbox-group v-model="userAnswers[currentQuestion.id]" shape="square">
53
+          <van-checkbox :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-checkbox>
54
+          <van-checkbox :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-checkbox>
55
+          <van-checkbox :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-checkbox>
56
+          <van-checkbox :name="'D'" class="kong"
57
+                        v-if="currentQuestion.optionD">D.{{ currentQuestion.optionD }}</van-checkbox>
58
+          <van-checkbox :name="'E'" class="kong"
59
+                        v-if="currentQuestion.optionE">E.{{ currentQuestion.optionE }}</van-checkbox>
60
+        </van-checkbox-group>
61
+      </div>
62
+
63
+      <!-- 判断题 -->
64
+      <div v-if="currentQuestion.category === '判断'">
65
+        <van-radio-group v-model="userAnswers[currentQuestion.id]">
66
+          <van-radio :name="'A'" class="kong">A.正确</van-radio>
67
+          <van-radio :name="'B'" class="kong">B.错误</van-radio>
68
+        </van-radio-group>
69
+      </div>
70
+    </div>
71
+
72
+    <!-- 底部固定栏 -->
73
+    <div class="footer">
74
+      <van-button @click="prevQuestion" :disabled="activeIndex === 0"
75
+                  style="height: 40px; width: 45%">上一题</van-button>
76
+      <van-button @click="nextQuestion" :disabled="activeIndex === questions.length - 1" style="
77
+          height: 40px;
78
+          width: 45%;
79
+          background-color: var(--van-radio-checked-icon-color);
80
+          border-color: var(--van-radio-checked-icon-color);
81
+          color: #fff;
82
+        ">下一题</van-button>
83
+    </div>
84
+
85
+    <!-- 提交前确认弹窗 -->
86
+    <van-dialog v-model:show="confirmSubmitDialog" title="确认交卷" show-cancel-button @confirm="submitForm">
87
+      <p :class="{ 'van-dialog__message': true, 'text-center': true }">
88
+        <span v-if="hasUnanswered">{{ unansweredText }}</span>
89
+        <span v-else>{{ completedText }}</span>
90
+      </p>
91
+    </van-dialog>
92
+
93
+    <!-- 结果弹窗 -->
94
+    <van-popup v-model:show="showResult" position="top" style="height: 100%">
95
+      <van-sticky>
96
+        <van-nav-bar title="答题结果" />
97
+      </van-sticky>
98
+      <div style="
99
+          margin-top: 10px;
100
+          margin-left: 20px;
101
+          margin-bottom: 20px;
102
+          font-weight: bold;
103
+        ">
104
+        本次得分:{{ totalScore }}
105
+        <!--取接口成绩-->
106
+      </div>
107
+      <van-divider />
108
+      <!-- 题干 -->
109
+      <div v-for="question in questions" :key="question.id" class="question">
110
+        <p>
111
+          <span v-if="question.category === '单选'">[单选]</span>
112
+          <span v-if="question.category === '多选'">[多选]</span>
113
+          <span v-if="question.category === '判断'">[判断]</span>
114
+          {{ question.stem }}
115
+        </p>
116
+        <!-- 显示提交答案 -->
117
+        <p>
118
+					<span :style="{
119
+              color: Array.isArray(userAnswers[question.id])
120
+                ? userAnswers[question.id].sort().join('') ===
121
+                  question.answer
122
+                  ? '#007aff'
123
+                  : 'red'
124
+                : userAnswers[question.id] === question.answer
125
+                ? '#007aff'
126
+                : 'red',
127
+            }">
128
+						提交答案:{{
129
+              Array.isArray(userAnswers[question.id])
130
+                ? userAnswers[question.id].sort().join("")
131
+                : userAnswers[question.id] || "未作答"
132
+            }}
133
+					</span>
134
+        </p>
135
+        <!-- 显示正确答案 -->
136
+        <p style="color: #007aff">正确答案:{{ question.answer }}</p>
137
+        <div v-if="question.category === '单选'" class="kong">
138
+          <div>A. {{ question.optionA }}</div>
139
+          <div>B. {{ question.optionB }}</div>
140
+          <div>C. {{ question.optionC }}</div>
141
+          <div v-if="question.optionD">D. {{ question.optionD }}</div>
142
+          <div v-if="question.optionE">E. {{ question.optionE }}</div>
143
+        </div>
144
+        <div v-if="question.category === '多选'" class="kong">
145
+          <div>A. {{ question.optionA }}</div>
146
+          <div>B. {{ question.optionB }}</div>
147
+          <div>C. {{ question.optionC }}</div>
148
+          <div v-if="question.optionD">D. {{ question.optionD }}</div>
149
+          <div v-if="question.optionE">E. {{ question.optionE }}</div>
150
+        </div>
151
+        <div v-if="question.category === '判断'" class="kong">
152
+          <div>A.正确</div>
153
+          <div>B.错误</div>
154
+        </div>
155
+
156
+        <!-- AI解析按钮和内容 -->
157
+        <div style="margin: 10px 0;">
158
+          <van-button
159
+            type="primary"
160
+            size="small"
161
+            :loading="analysisLoading[question.id]"
162
+            @click="generateAIAnalysis(question,true)"
163
+          >
164
+            {{ aiAnalysis[question.id] ? '重新解析' : 'AI解析' }}
165
+          </van-button>
166
+
167
+          <!-- 显示AI解析内容 -->
168
+          <div v-if="aiAnalysis[question.id]" class="ai-analysis-content">
169
+            <div v-html="renderAnalysis(aiAnalysis[question.id])"></div>
170
+          </div>
171
+        </div>
172
+
173
+        <van-divider />
174
+      </div>
175
+      <div style="margin-top: 20px; text-align: center; margin-bottom: 20px">
176
+        <van-button class="questionBtn" type="primary" @click="confirmResult">确定</van-button>
177
+      </div>
178
+    </van-popup>
179
+  </div>
180
+</template>
181
+
182
+<script setup>
183
+import {
184
+  onMounted,
185
+  ref,
186
+  getCurrentInstance,
187
+  computed
188
+} from "vue";
189
+import {
190
+  showConfirmDialog,
191
+  showSuccessToast,
192
+  showFailToast,
193
+  showDialog
194
+} from 'vant';
195
+import {
196
+  useRouter,
197
+  useRoute
198
+} from "vue-router";
199
+import {
200
+  examResult,
201
+  formNew,
202
+  myDefaultCourse,
203
+  saveScore,
204
+  sortData,
205
+} from "@/api/dati";
206
+const {
207
+  proxy
208
+} = getCurrentInstance()
209
+
210
+const router = useRouter();
211
+const route = useRoute();
212
+
213
+
214
+// 1. 从本地存储获取当前用户的 userCode(字符串)
215
+const currentUserCode = localStorage.getItem('userCode');
216
+
217
+// 2. 包装成数组(即使为空也安全)
218
+const userCodeArray = currentUserCode ? [currentUserCode] : [];
219
+
220
+// 3. 按原来的方式生成 userDesc 字符串
221
+const userDesc = userCodeArray.join(',');
222
+const examContext = {
223
+  examId: query.examId || '',
224
+  testRole: query.testRole || '',
225
+  checkTime: query.checkTime || '',
226
+  checkName: query.checkName || '',
227
+  testType: query.testType || '',
228
+  addId: query.addId || '',
229
+  userDesc: userDesc,
230
+  courseId: query.courseId || '',
231
+  todayStr: query.todayStr || ''
232
+};
233
+const   userId = localStorage.getItem('userId');
234
+const questions = ref([]);
235
+
236
+const userAnswers = ref({});
237
+const activeIndex = ref(0);
238
+const totalScore = ref(0);
239
+const showResult = ref(false);
240
+
241
+const confirmSubmitDialog = ref(false);
242
+const hasUnanswered = ref(false);
243
+const unansweredText = "有题目未完成,是否确认交卷?";
244
+const completedText = "已完成所有题目,是否确认交卷?";
245
+const overlayloading = ref(false);
246
+// 在组件挂载时获取试卷
247
+onMounted(async () => {
248
+  overlayloading.value = true;
249
+  await saveChecUser();
250
+  await getForm();
251
+  overlayloading.value = false;
252
+});
253
+const handData={}
254
+
255
+const saveChecUser = async () => {
256
+  console.log("接到的参数:",examContext);
257
+  var url = '/sgsafe/ExamHead/saveChecUser';
258
+  var param = {
259
+    params: JSON.stringify(examContext)
260
+  };
261
+  const res = await proxy.$axios.post(url, param);
262
+    if (res.data.code == '0') {
263
+    await  handData=res.data.data
264
+    } else {
265
+      showFailToast('操作失败!' + response.data.msg)
266
+    }
267
+
268
+
269
+}
270
+//获取试卷
271
+const getForm = async () => {
272
+  console.log("headId为",handData.id);
273
+  var url = '/sgsafe/ExamLine/query'
274
+  const query = ref({
275
+    headId:   handData.id
276
+  })
277
+  var param = {
278
+    params: JSON.stringify(query.value)
279
+  }
280
+  try {
281
+    const res = await proxy.$axios.post(url, param);
282
+    if (res.data.code === 0) {
283
+      questions.value = res.data.data
284
+    } else {
285
+      console.log('操作失败!' + res.data.msg);
286
+    }
287
+  } catch (error) {
288
+    console.log('请求出错:', questions);
289
+  }
290
+};
291
+
292
+// 获取当前题目
293
+const currentQuestion = computed(() => {
294
+  return questions.value[activeIndex.value];
295
+});
296
+
297
+// 获取题目类型对应的图片路径
298
+import danxuan from '@/assets/img/dx.svg'
299
+import duoxuanImg from '@/assets/img/ksdx.svg'
300
+import panduanImg from '@/assets/img/kspd.svg'
301
+const getQuestionTypeImage = (category) => {
302
+  switch (category) {
303
+    case "单选": // 单选
304
+      return danxuan;
305
+    case "多选": // 多选
306
+      return duoxuanImg;
307
+    case "判断": // 判断
308
+      return panduanImg;
309
+    default:
310
+      return "";
311
+  }
312
+};
313
+
314
+//返回答题首页
315
+const goBack = () => {
316
+  router.push({
317
+    path: "/dailyproblem"
318
+  });
319
+};
320
+
321
+// 切换到下一题
322
+const nextQuestion = () => {
323
+  if (activeIndex.value < questions.value.length - 1) {
324
+    activeIndex.value++;
325
+  }
326
+};
327
+
328
+// 切换到上一题
329
+const prevQuestion = () => {
330
+  if (activeIndex.value > 0) {
331
+    activeIndex.value--;
332
+  }
333
+};
334
+
335
+
336
+const getUserAnswers = () => {
337
+  let useranswers = [];
338
+  questions.value.forEach((question) => {
339
+    const userAnswer = userAnswers.value[question.id]; // 获取用户的答案
340
+    let userAnswerString;
341
+    if (Array.isArray(userAnswer)) {
342
+      // 多选题,将数组转换为字符串
343
+      userAnswerString = userAnswer.sort().join(""); // 排序并转换为字符串,如 "ABC"
344
+    } else {
345
+      // 单选题,直接是字符串
346
+      userAnswerString = userAnswer || ""; // 如果未选择答案,则设为空字符串
347
+    }
348
+    // 将答案保存到 answers 数组中
349
+    useranswers.push({
350
+      id: question.id, // 题目 ID
351
+      userAnswer: userAnswerString, // 用户的答案
352
+    });
353
+  });
354
+  return useranswers;
355
+};
356
+
357
+//交卷
358
+const submitForm = async () => {
359
+  overlayloading.value = true;
360
+  try {
361
+    let answers = getUserAnswers();
362
+    //console.log('answers.value', answers)
363
+    var url = '/sgsafe/ExamLine/appSaveMyScore'
364
+    var param = {
365
+      json: JSON.stringify(
366
+        answers
367
+      )
368
+    }
369
+    try {
370
+      const res = await proxy.$axios.post(url, param);
371
+      if (res.data.code === 0) {
372
+        showSuccessToast("保存成功")
373
+      } else {
374
+        console.log('操作失败!' + res.data.msg);
375
+      }
376
+    } catch (error) {
377
+      console.log('请求出错:', questions);
378
+    }
379
+
380
+    //开始判卷
381
+    var url2 = '/sgsafe/Package/doProc'
382
+    var param2 = {
383
+      procName: 'safeplat.sxsp_grade_exam_p',
384
+      param: JSON.stringify([courseId])
385
+    }
386
+    try {
387
+      const res2 = await proxy.$axios.post(url2, param2);
388
+      if (res2.data.code === 0) {
389
+        consle.log("courseId:" + courseId + "判卷完成!")
390
+      } else {
391
+        console.log('操作失败!' + res.data.msg);
392
+      }
393
+    } catch (error) {
394
+      console.log('请求出错:', questions);
395
+    }
396
+
397
+    overlayloading.value = false;
398
+
399
+    //查询本日答题次数和分数
400
+    var url3 = '/sgsafe/DailyExam/appQueryMyScore'
401
+    const query3 = ref({
402
+      userId: userId,
403
+      examDate: todayStr,
404
+      headId: courseId,
405
+    })
406
+    var param3 = {
407
+      params: JSON.stringify(query3.value)
408
+    }
409
+    const res3 = await proxy.$axios.post(url3, param3);
410
+    if (res3.data.code === 0) {
411
+      console.log(res3.data)
412
+      if (res3.data.data.dailyExamList.length==0){
413
+        var url4='/sgsafe/ExamHead/queryByheadId'
414
+        var param4 = {
415
+          headId: courseId
416
+        }
417
+        const res4 = await proxy.$axios.post(url4, param4);
418
+        if (res4.data.code === 0) {
419
+          totalScore.value = res4.data.data.totalScore;
420
+        }
421
+        showConfirmDialog({
422
+          message: "判卷完成",
423
+          confirmButtonText: "查看本次答题结果",
424
+          cancelButtonText: "退出答题"
425
+        })
426
+          .then(() => {
427
+            showResult.value = true;
428
+          })
429
+          .catch(() => {
430
+
431
+            router.replace({
432
+              path: "/dailyproblem"
433
+            });
434
+          })
435
+      }else {
436
+        let times = res3.data.data.dailyExamList[0].examCounts;
437
+        totalScore.value = res3.data.data.headScore;
438
+        showConfirmDialog({
439
+          message: "判卷完成",
440
+          confirmButtonText: "查看本次答题结果",
441
+          cancelButtonText: times == "3" ? "退出答题" : "继续答题",
442
+        })
443
+          .then(() => {
444
+            showResult.value = true;
445
+          })
446
+          .catch(() => {
447
+            if (times == "3") {
448
+              router.replace({
449
+                path: "/dailyproblem"
450
+              });
451
+            } else {
452
+              router.push({
453
+                path: "/dailyproblem"
454
+              });
455
+            }
456
+          });
457
+      }
458
+
459
+
460
+    } else {
461
+      console.log('操作失败!' + res3.data.msg);
462
+    }
463
+
464
+  } catch (error) {
465
+    console.error("出错:", error);
466
+    showFailToast("交卷失败");
467
+  }
468
+};
469
+
470
+// 确认结果并返回
471
+const confirmResult = () => {
472
+  showResult.value = false;
473
+  // router.back();
474
+  router.back()
475
+};
476
+
477
+// 检查是否所有题目都已作答
478
+const checkBeforeSubmit = () => {
479
+  hasUnanswered.value = questions.value.some((question) => {
480
+    const userAnswer = userAnswers.value[question.id];
481
+    return (
482
+      !userAnswer || (Array.isArray(userAnswer) && userAnswer.length === 0)
483
+    );
484
+  });
485
+  confirmSubmitDialog.value = true;
486
+};
487
+
488
+// AI解析功能
489
+// AI解析相关变量
490
+const aiAnalysis = ref({}); // 存储每道题的AI解析内容
491
+const analysisLoading = ref({}); // 存储每道题的解析加载状态
492
+import { fetchHuaweiResponse } from "@/tools/deepseek.js";
493
+// 动态导入依赖
494
+let marked, DOMPurify;
495
+
496
+const initMarkdownLibs = async () => {
497
+  try {
498
+    // 尝试导入marked
499
+    const markedModule = await import('marked');
500
+    marked = markedModule.marked || markedModule.default || markedModule;
501
+
502
+    // 尝试导入DOMPurify
503
+    const dompurifyModule = await import('dompurify');
504
+    DOMPurify = dompurifyModule.default || dompurifyModule;
505
+  } catch (error) {
506
+    console.warn('Markdown libraries not available, using plain text', error);
507
+    // 如果导入失败,使用基础功能
508
+    marked = {
509
+      parse: (text) => text
510
+    };
511
+    DOMPurify = {
512
+      sanitize: (html) => html
513
+    };
514
+  }
515
+};
516
+
517
+// 在组件挂载时初始化
518
+onMounted(() => {
519
+  initMarkdownLibs();
520
+});
521
+
522
+// 生成AI解析
523
+const generateAIAnalysis = async (question, force = false) => {
524
+  // 如果该题已有解析且不是强制重新生成,直接返回
525
+  if (aiAnalysis.value[question.id] && !force) {
526
+    return;
527
+  }
528
+
529
+  // 如果是重新解析,先清空之前的内容
530
+  if (force) {
531
+    aiAnalysis.value[question.id] = '';
532
+  }
533
+
534
+  // 确保依赖已加载
535
+  if (!marked || !DOMPurify) {
536
+    await initMarkdownLibs();
537
+  }
538
+
539
+  // 设置加载状态
540
+  analysisLoading.value[question.id] = true;
541
+
542
+  try {
543
+    // 构造提示词
544
+    let prompt = `请为以下题目提供详细解析:
545
+题目类型:${question.category}题干:${question.stem}`;
546
+
547
+    // 添加选项
548
+    if (question.optionA) prompt += `\nA. ${question.optionA}`;
549
+    if (question.optionB) prompt += `\nB. ${question.optionB}`;
550
+    if (question.optionC) prompt += `\nC. ${question.optionC}`;
551
+    if (question.optionD) prompt += `\nD. ${question.optionD}`;
552
+    if (question.optionE) prompt += `\nE. ${question.optionE}`;
553
+
554
+    prompt += `\n正确答案:${question.answer}`;
555
+
556
+    // 添加用户答案(如果已作答)
557
+    const userAnswer = userAnswers.value[question.id];
558
+    if (userAnswer) {
559
+      const userAnswerString = Array.isArray(userAnswer)
560
+        ? userAnswer.sort().join("")
561
+        : userAnswer;
562
+      prompt += `\n用户答案:${userAnswerString}`;
563
+    }
564
+
565
+    prompt += `\n\n请提供以下内容:
566
+1. 正确答案的解释
567
+2. 为什么其他选项不正确(如果用户答案错误)
568
+3. 相关知识点说明`;
569
+
570
+    // 构造消息对象
571
+    const messages = [
572
+      {
573
+        role: "user",
574
+        content: prompt
575
+      }
576
+    ];
577
+
578
+    // 调用AI接口
579
+    fetchHuaweiResponse(
580
+      messages,
581
+      (content, isThinking, isEnd) => {
582
+        // 实时更新解析内容
583
+        aiAnalysis.value[question.id] = content;
584
+
585
+        // 如果是最终结果,停止加载状态
586
+        if (isEnd) {
587
+          analysisLoading.value[question.id] = false;
588
+        }
589
+      },
590
+      null
591
+    );
592
+  } catch (error) {
593
+    console.error('AI解析生成失败:', error);
594
+    analysisLoading.value[question.id] = false;
595
+    aiAnalysis.value[question.id] = '解析生成失败';
596
+  }
597
+};
598
+
599
+// 解析内容转换为HTML
600
+const renderAnalysis = (content) => {
601
+  if (!content) return '';
602
+
603
+  try {
604
+    // 确保依赖已加载
605
+    if (!marked || !DOMPurify) {
606
+      return content.replace(/\n/g, '<br>');
607
+    }
608
+
609
+    const html = marked.parse ? marked.parse(content) : marked(content);
610
+    return DOMPurify.sanitize ? DOMPurify.sanitize(html) : html;
611
+  } catch (error) {
612
+    console.error('Markdown解析错误:', error);
613
+    return content.replace(/\n/g, '<br>');
614
+  }
615
+};
616
+
617
+
618
+
619
+
620
+</script>
621
+
622
+<style scoped>
623
+.quiz-page {
624
+  padding: 20px;
625
+}
626
+
627
+.question-type-img {
628
+  width: 54px;
629
+  height: 20px;
630
+}
631
+
632
+.question {
633
+  margin-left: 20px;
634
+  margin-right: 20px;
635
+}
636
+
637
+.kong {
638
+  margin-bottom: 20px;
639
+}
640
+
641
+.footer {
642
+  position: fixed;
643
+  bottom: 0;
644
+  left: 0;
645
+  width: 100%;
646
+  background-color: #fff;
647
+  display: flex;
648
+  justify-content: space-around;
649
+  align-items: center;
650
+  margin-bottom: 10px;
651
+}
652
+
653
+.van-dialog__message {
654
+  text-align: center;
655
+}
656
+
657
+.questionBtn {
658
+  width: 40%;
659
+}
660
+
661
+/* 遮罩 */
662
+.wrapper {
663
+  display: flex;
664
+  align-items: center;
665
+  justify-content: center;
666
+  height: 100%;
667
+}
668
+
669
+.van-overlay {
670
+  z-index: 2;
671
+  background-color: rgba(0, 0, 0, 0.5);
672
+}
673
+
674
+.ai-analysis-content {
675
+  margin-top: 10px;
676
+  padding: 10px;
677
+  background-color: #f5f5f5;
678
+  border-radius: 4px;
679
+  font-size: 14px;
680
+  line-height: 1.6;
681
+}
682
+
683
+.ai-analysis-content :deep(h1),
684
+.ai-analysis-content :deep(h2),
685
+.ai-analysis-content :deep(h3) {
686
+  margin: 10px 0;
687
+  font-weight: bold;
688
+  font-size: 16px;
689
+}
690
+
691
+.ai-analysis-content :deep(p) {
692
+  margin: 8px 0;
693
+}
694
+
695
+.ai-analysis-content :deep(ul),
696
+.ai-analysis-content :deep ol {
697
+  padding-left: 20px;
698
+  margin: 8px 0;
699
+}
700
+
701
+.ai-analysis-content :deep(li) {
702
+  margin: 4px 0;
703
+}
704
+
705
+.ai-analysis-content :deep(code) {
706
+  padding: 2px 4px;
707
+  background-color: #e0e0e0;
708
+  border-radius: 3px;
709
+  font-family: monospace;
710
+}
711
+
712
+.ai-analysis-content :deep(pre) {
713
+  padding: 10px;
714
+  background-color: #e0e0e0;
715
+  border-radius: 4px;
716
+  overflow-x: auto;
717
+}
718
+
719
+</style>

+ 77
- 9
src/view/dati/examCheck/index.vue Bestand weergeven

@@ -55,7 +55,19 @@
55 55
       </template>
56 56
       <div style="padding: 30px;">确定要删除该项目吗?</div>
57 57
     </van-dialog>
58
-
58
+    <!-- 二维码弹窗 -->
59
+    <van-dialog
60
+      v-model:show="qrDialogVisible"
61
+      title="扫码添加人员"
62
+      show-cancel-button
63
+      close-on-popstate
64
+      @cancel="qrDialogVisible = false"
65
+    >
66
+      <div class="qr-dialog-content">
67
+        <img v-if="qrDataUrl" :src="qrDataUrl" alt="二维码" class="qr-code" />
68
+        <p class="qr-tip">请使用手机扫码 App 扫描上方二维码</p>
69
+      </div>
70
+    </van-dialog>
59 71
   </div>
60 72
 </template>
61 73
 
@@ -65,6 +77,15 @@ import { Dialog, showDialog, showSuccessToast, showToast, Toast } from 'vant';
65 77
 
66 78
 const { proxy } = getCurrentInstance();
67 79
 
80
+//生成二维码
81
+import QRCode from 'qrcode';
82
+
83
+// 新增状态
84
+const qrDialogVisible = ref(false);
85
+const qrDataUrl = ref('');
86
+const currentExamItem = ref(null); // 用于临时保存当前 item
87
+
88
+
68 89
 const onClickLeft = () => {
69 90
   history.back();
70 91
 };
@@ -124,14 +145,39 @@ const handAdd =  () => {
124 145
     } });
125 146
 
126 147
 };
127
-const goaddPeo = (item) => {
128
-  router.push({
129
-    path: '/addPeo',
130
-    query: {
131
-      data: JSON.stringify(item)
132
-    }
133
-  })
134
-}
148
+import.meta.env.VITE_BASE_API
149
+const goaddPeo = async (item) => {
150
+  currentExamItem.value = item;
151
+
152
+  // ✅ 构建跳转 URL(指向你的答题页面)
153
+  const baseUrl = window.location.origin + '/fcbkdatistart';
154
+
155
+  const url = new URL(baseUrl);
156
+  url.searchParams.set('examId', item.id);           // 考试ID
157
+  url.searchParams.set('testRole', item.testRole); //规则id
158
+  url.searchParams.set('testRole', item.checkTime); //考试时间
159
+  url.searchParams.set('checkName', item.checkName || '');
160
+  url.searchParams.set('testType', item.testType || '');//考试类型
161
+  // 如果还需要 userId,也可以加:
162
+   url.searchParams.set('addId', localStorage.getItem('userId') || '');
163
+
164
+  const qrContent = url.toString(); // ← 这就是二维码内容!
165
+
166
+  try {
167
+    qrDataUrl.value = await QRCode.toDataURL(qrContent, {
168
+      width: 240,
169
+      margin: 2,
170
+      color: {
171
+        dark: '#000000',
172
+        light: '#ffffff'
173
+      }
174
+    });
175
+    qrDialogVisible.value = true;
176
+  } catch (err) {
177
+    console.error('二维码生成失败:', err);
178
+    Toast('二维码生成失败,请重试');
179
+  }
180
+};
135 181
 
136 182
 const edits = (row) => {
137 183
   kz.value = true;
@@ -845,4 +891,26 @@ const closeSwipe = (idx) => {
845 891
   background-size: auto 100%;
846 892
   background-repeat: no-repeat;
847 893
 }
894
+.qr-dialog-content {
895
+  display: flex;
896
+  flex-direction: column;
897
+  align-items: center;
898
+  padding: 20px 0;
899
+}
900
+
901
+.qr-code {
902
+  width: 240px;
903
+  height: 240px;
904
+  background: #fff;
905
+  padding: 8px;
906
+  border-radius: 8px;
907
+  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
908
+}
909
+
910
+.qr-tip {
911
+  margin-top: 12px;
912
+  font-size: 13px;
913
+  color: #666;
914
+  text-align: center;
915
+}
848 916
 </style>

Laden…
Annuleren
Opslaan