zhangjie пре 3 година
родитељ
комит
3dd60d0ee5

+ 33 - 2
src/api/onlinePractice.ts

@@ -1,5 +1,9 @@
 import { httpApp } from "@/plugins/axiosApp";
-import { PracticeExam } from "@/types/student-client";
+import {
+  OnlinePracticeRecord,
+  OnlinePracticeRecordResult,
+  PracticeExam,
+} from "@/types/student-client";
 
 type ExamItem = {
   id: number;
@@ -12,7 +16,7 @@ export async function onlinePracticeExamListApi(studentId: number) {
     `/api/ecs_exam_work/exam/queryByNameLike`,
     {
       params: {
-        examTyes: "PRACTICE",
+        examTypes: "PRACTICE",
         name: "",
         studentId,
       },
@@ -30,3 +34,30 @@ export async function onlinePracticeCourseListApi(examId: number) {
     }
   );
 }
+/** 获取在线练习考试记录列表 */
+export async function onlinePracticeRecordListApi(
+  examStudentId: number | string
+) {
+  return httpApp.get<any, { data: OnlinePracticeRecord[] }>(
+    `/api/branch_ecs_oe_admin/practice/queryPracticeRecordList`,
+    {
+      params: {
+        examStudentId,
+      },
+    }
+  );
+}
+
+/** 获取在线练习考试成绩报告 */
+export async function onlinePracticeRecordDetailApi(
+  examRecordDataId: number | string
+) {
+  return httpApp.get<any, { data: OnlinePracticeRecordResult }>(
+    `/api/branch_ecs_oe_admin/practice/getPracticeDetailInfo`,
+    {
+      params: {
+        examRecordDataId,
+      },
+    }
+  );
+}

+ 5 - 0
src/features/OnlinePractice/OnlinePractice.vue

@@ -60,6 +60,11 @@ function toEnterPracticeList(course: PracticeExam) {
     params: {
       examId: course.examId,
     },
+    query: {
+      examStudentId: course.examStudentId,
+      examName: encodeURIComponent(course.examName),
+      courseName: encodeURIComponent(course.courseName),
+    },
   });
 }
 

+ 117 - 0
src/features/OnlinePractice/OnlinePracticeRecord.vue

@@ -0,0 +1,117 @@
+<script setup lang="ts">
+import { onlinePracticeRecordListApi } from "@/api/onlinePractice";
+import { OnlinePracticeRecord } from "@/types/student-client";
+import moment from "moment";
+import { onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+
+const route = useRoute();
+const router = useRouter();
+const examName = decodeURIComponent(route.query.examName as string);
+const courseName = decodeURIComponent(route.query.courseName as string);
+let maxAccuracy: string = $ref("0");
+let aveAccuracy: string = $ref("0");
+let recordList = $ref<OnlinePracticeRecord[]>([]);
+async function getRecordList() {
+  if (!route.query.examStudentId) return;
+  const res = await onlinePracticeRecordListApi(
+    route.query.examStudentId as string
+  );
+  recordList = res.data || [];
+  recordList.reverse();
+}
+function getRecordInfo() {
+  if (!recordList.length) return;
+  maxAccuracy = Math.max(...recordList.map((v) => v.objectiveAccuracy)).toFixed(
+    2
+  );
+
+  aveAccuracy = (
+    recordList.map((v) => v.objectiveAccuracy).reduce((a, b) => a + b) /
+    recordList.length
+  ).toFixed(2);
+}
+function formatTime(ms: number) {
+  return ms ? moment.utc(ms).format("HH:mm:ss") : "";
+}
+
+function toPracticeRecordDetail(record: OnlinePracticeRecord) {
+  void router.push({
+    name: "OnlinePracticeRecordDetail",
+    params: {
+      examId: route.params.examId,
+      examRecordDataId: record.id,
+    },
+  });
+}
+
+onMounted(async () => {
+  await getRecordList();
+  getRecordInfo();
+});
+</script>
+
+<template>
+  <div class="record-head">
+    <p>批次:{{ examName }}</p>
+    <p>科目:{{ courseName }}</p>
+    <p>平均正确率:{{ aveAccuracy }}%</p>
+    <p>最高正确率:{{ maxAccuracy }}%</p>
+  </div>
+
+  <n-table class="n-table-text-center" :singleLine="false">
+    <colgroup>
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+      <col width="120px" />
+    </colgroup>
+    <thead>
+      <tr>
+        <th>练习编号</th>
+        <th>开始日期</th>
+        <th>结束日期</th>
+        <th>练习时长</th>
+        <th>总题量</th>
+        <th>正确</th>
+        <th>错误</th>
+        <th>未答</th>
+        <th>正确率</th>
+        <th>操作</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr v-for="record in recordList" :key="record.id">
+        <td>{{ record.id }}</td>
+        <td>{{ record.startTime }}</td>
+        <td>{{ record.endTime }}</td>
+        <td>{{ formatTime(record.usedExamTime) }}</td>
+        <td>{{ record.totalQuestionCount }}</td>
+        <td>{{ record.succQuestionNum }}</td>
+        <td>{{ record.failQuestionNum }}</td>
+        <td>{{ record.notAnsweredCount }}</td>
+        <td>{{ record.objectiveAccuracy }}%</td>
+        <td>
+          <n-button type="success" block @click="toPracticeRecordDetail(record)"
+            >查看成绩报告</n-button
+          >
+        </td>
+      </tr>
+    </tbody>
+  </n-table>
+</template>
+
+<style scoped>
+.record-head {
+  display: flex;
+  margin-bottom: 20px;
+  align-items: baseline;
+  justify-content: space-between;
+}
+</style>

+ 124 - 0
src/features/OnlinePractice/OnlinePracticeRecordDetail.vue

@@ -0,0 +1,124 @@
+<script setup lang="ts">
+import { onlinePracticeRecordDetailApi } from "@/api/onlinePractice";
+import { OnlinePracticeRecordResult } from "@/types/student-client";
+import { onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+
+const route = useRoute();
+const router = useRouter();
+
+const defaultExamRecordResult: OnlinePracticeRecordResult = {
+  courseName: "",
+  objectiveAccuracy: "",
+  paperStructInfos: [],
+};
+let examRecordResult: OnlinePracticeRecordResult = $ref(
+  defaultExamRecordResult
+);
+async function getExamRecordResult() {
+  if (!route.params.examRecordDataId) return;
+  const res = await onlinePracticeRecordDetailApi(
+    route.params.examRecordDataId as string
+  );
+  examRecordResult = res.data || defaultExamRecordResult;
+}
+
+function toOpenPaper() {
+  // TODO:
+  $message.info("等待展开试卷...");
+}
+function goBack() {
+  router.back();
+}
+
+onMounted(() => {
+  void getExamRecordResult();
+});
+</script>
+
+<template>
+  <h2 class="record-detail-title">
+    练习情况概率(科目:{{ examRecordResult.courseName }})
+  </h2>
+  <p class="record-detail-info">
+    答题正确率: <span>{{ examRecordResult.objectiveAccuracy }}%</span>
+  </p>
+
+  <n-table class="n-table-text-center" :singleLine="false">
+    <colgroup>
+      <col />
+      <col />
+      <col />
+      <col />
+      <col />
+    </colgroup>
+    <thead>
+      <tr>
+        <th>题目分类</th>
+        <th>题量</th>
+        <th>正确</th>
+        <th>错误</th>
+        <th>未答</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr
+        v-for="record in examRecordResult.paperStructInfos"
+        :key="record.index"
+      >
+        <td>{{ record.title }}</td>
+        <td>{{ record.questionCount }}</td>
+        <td>{{ record.succQuestionNum }}</td>
+        <td>{{ record.failQuestionNum }}</td>
+        <td>{{ record.notAnsweredCount }}</td>
+      </tr>
+      <tr>
+        <td colspan="5">
+          <div class="record-detail-desc">
+            <h6>报告提示:</h6>
+            <p>
+              1、若本练习卷中包含部分主观题型,则报告统计数据仅供参考,因为部分题型需考生根据参考答案判断正确和错误,报告仅对可统计的题型进行统计。
+            </p>
+            <p>2、若单题存在多处作答,只要有一处出错,该题就判为错题。</p>
+          </div>
+        </td>
+      </tr>
+    </tbody>
+  </n-table>
+  <div class="record-detail-action">
+    <n-grid :xGap="20" :cols="2">
+      <n-gi>
+        <n-button type="success" block @click="toOpenPaper">展开试卷</n-button>
+      </n-gi>
+      <n-gi>
+        <n-button type="success" block @click="goBack">返回</n-button>
+      </n-gi>
+    </n-grid>
+  </div>
+</template>
+
+<style scoped>
+.record-detail-title {
+  font-size: 18px;
+  line-height: 2;
+  text-align: center;
+  font-weight: bold;
+}
+.record-detail-info {
+  color: var(--app-color-info);
+  margin-bottom: 20px;
+}
+.record-detail-desc {
+  text-align: left;
+  padding: 5px;
+  min-height: 80px;
+  color: var(--app-color-info);
+}
+.record-detail-desc h6 {
+  font-weight: bold;
+  margin-bottom: 5px;
+}
+.record-detail-action {
+  margin-top: 20px;
+}
+</style>

+ 3 - 1
src/types/student-client.d.ts

@@ -229,6 +229,8 @@ export type PracticeExam = ExamCycle & {
   examName: string;
   /** 考试类型 */
   examType: ExamType;
+  /** 考生id,用户的每场考试都有一个考生id */
+  examStudentId: number;
   /** 课程名称 */
   courseName: string;
   /** 课程code */
@@ -265,7 +267,7 @@ export type OnlinePracticeRecord = {
   /** 考试结束时间 */
   endTime: string;
   /** 练习时长 类型待确认??? */
-  usedExamTime: any;
+  usedExamTime: number;
   /** 总题量 */
   totalQuestionCount: number;
   /** 正确的数量  */