|
@@ -0,0 +1,211 @@
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import {
|
|
|
|
+ onlinePracticeCourseListApi,
|
|
|
|
+ onlinePracticeExamListApi,
|
|
|
|
+} from "@/api/onlinePractice";
|
|
|
|
+import { store } from "@/store/store";
|
|
|
|
+import { PracticeExam } from "@/types/student-client";
|
|
|
|
+import moment from "moment";
|
|
|
|
+import { SelectOption } from "naive-ui";
|
|
|
|
+import { onMounted } from "vue";
|
|
|
|
+import { useRoute, useRouter } from "vue-router";
|
|
|
|
+
|
|
|
|
+const route = useRoute();
|
|
|
|
+const router = useRouter();
|
|
|
|
+
|
|
|
|
+// exam-list
|
|
|
|
+let examId: number = $ref();
|
|
|
|
+let examList = $ref<SelectOption[]>([]);
|
|
|
|
+async function getExamList() {
|
|
|
|
+ const userId = store.user.id;
|
|
|
|
+ const res = await onlinePracticeExamListApi(userId);
|
|
|
|
+ let exams = res.data || [];
|
|
|
|
+ examList = exams.map((item) => {
|
|
|
|
+ return {
|
|
|
|
+ label: item.name,
|
|
|
|
+ value: item.id,
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// course-list
|
|
|
|
+let nowTime = $ref(Date.now() + store.sysTime.difference);
|
|
|
|
+let enterButtonClicked = $ref(false);
|
|
|
|
+let courseList = $ref<PracticeExam[]>([]);
|
|
|
|
+async function getCourseList() {
|
|
|
|
+ const res = await onlinePracticeCourseListApi(examId);
|
|
|
|
+ courseList = res.data || [];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function checkExamInProgress() {
|
|
|
|
+ return Promise.resolve(false);
|
|
|
|
+}
|
|
|
|
+async function toEnterPractice(course: PracticeExam) {
|
|
|
|
+ if (enterButtonClicked) return;
|
|
|
|
+ const examInProgress = await checkExamInProgress();
|
|
|
|
+ if (examInProgress) {
|
|
|
|
+ // TODO:
|
|
|
|
+ } else {
|
|
|
|
+ void router.push({
|
|
|
|
+ name: "OnlineExamOverview",
|
|
|
|
+ params: {
|
|
|
|
+ examId: course.examId,
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+function toEnterPracticeList(course: PracticeExam) {
|
|
|
|
+ void router.push({
|
|
|
|
+ name: "OnlinePracticeRecord",
|
|
|
|
+ params: {
|
|
|
|
+ examId: course.examId,
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// transfer
|
|
|
|
+function weekDayNameTransfer(week: number): string {
|
|
|
|
+ const weekdayNames: Record<number, string> = {
|
|
|
|
+ 1: "一",
|
|
|
|
+ 2: "二",
|
|
|
|
+ 3: "三",
|
|
|
|
+ 4: "四",
|
|
|
|
+ 5: "五",
|
|
|
|
+ 6: "六",
|
|
|
|
+ 7: "日",
|
|
|
|
+ };
|
|
|
|
+ return weekdayNames[week] ? `周${weekdayNames[week]}` : "";
|
|
|
|
+}
|
|
|
|
+function courseInBetween(course: PracticeExam) {
|
|
|
|
+ return moment(nowTime).isBetween(
|
|
|
|
+ moment(course.startTime),
|
|
|
|
+ moment(course.endTime)
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
+function courseInCycle(course: PracticeExam) {
|
|
|
|
+ if (!course.examCycleEnabled) return true;
|
|
|
|
+ const weekday = moment(nowTime).isoWeekday();
|
|
|
|
+ if (!course.examCycleWeek.includes(weekday)) return false;
|
|
|
|
+ const HHmm = moment(nowTime).format("HH:mm");
|
|
|
|
+ return course.examCycleTimeRange.some((range) =>
|
|
|
|
+ range.timeRange.some((v) => HHmm >= v[0] && HHmm <= v[1])
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
+function courseIsDisabled(course: PracticeExam) {
|
|
|
|
+ return (
|
|
|
|
+ !courseInBetween(course) ||
|
|
|
|
+ course.allowExamCount < 1 ||
|
|
|
|
+ enterButtonClicked ||
|
|
|
|
+ !courseInCycle(course)
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
+function courseDiableReason(course: PracticeExam) {
|
|
|
|
+ if (!courseInBetween(course)) {
|
|
|
|
+ return "当前时间不在练习开放时间范围";
|
|
|
|
+ } else if (course.allowExamCount < 1) {
|
|
|
|
+ return "无剩余练习次数";
|
|
|
|
+ } else if (enterButtonClicked) {
|
|
|
|
+ return "请稍后点击";
|
|
|
|
+ } else if (!courseInCycle(course)) {
|
|
|
|
+ return "不在练习时间周期内";
|
|
|
|
+ } else {
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// init
|
|
|
|
+async function initData() {
|
|
|
|
+ await getExamList();
|
|
|
|
+ if (route.query.examId) {
|
|
|
|
+ examId = Number(route.query.examId);
|
|
|
|
+ } else {
|
|
|
|
+ examId = examList[0] && (examList[0].value as number);
|
|
|
|
+ }
|
|
|
|
+ void getCourseList();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ void initData();
|
|
|
|
+});
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <div class="qm-mb-20">
|
|
|
|
+ <span>选择考试批次:</span>
|
|
|
|
+ <n-select
|
|
|
|
+ v-model:value="examId"
|
|
|
|
+ class="qm-select"
|
|
|
|
+ :options="examList"
|
|
|
|
+ ></n-select>
|
|
|
|
+ </div>
|
|
|
|
+ <n-table class="n-table-text-center" :singleLine="false">
|
|
|
|
+ <colgroup>
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col />
|
|
|
|
+ <col width="200px" />
|
|
|
|
+ </colgroup>
|
|
|
|
+ <thead>
|
|
|
|
+ <tr>
|
|
|
|
+ <th>课程</th>
|
|
|
|
+ <th>考试进入时间</th>
|
|
|
|
+ <th>考试时间周期</th>
|
|
|
|
+ <th>练习次数</th>
|
|
|
|
+ <th>最近正确率</th>
|
|
|
|
+ <th>平均正确率</th>
|
|
|
|
+ <th>最高正确率</th>
|
|
|
|
+ <th>操作</th>
|
|
|
|
+ </tr>
|
|
|
|
+ </thead>
|
|
|
|
+ <tbody>
|
|
|
|
+ <tr v-for="course in courseList" :key="course.courseCode">
|
|
|
|
+ <td>{{ course.courseName }}</td>
|
|
|
|
+ <td>
|
|
|
|
+ {{ course.startTime }} <br />
|
|
|
|
+ ~ <br />
|
|
|
|
+ {{ course.endTime }}
|
|
|
|
+ </td>
|
|
|
|
+ <td>
|
|
|
|
+ <div v-if="course.examCycleEnabled">
|
|
|
|
+ <p>
|
|
|
|
+ <span
|
|
|
|
+ v-for="(week, index) in course.examCycleWeek"
|
|
|
|
+ :key="index"
|
|
|
|
+ >{{ weekDayNameTransfer(week) }}</span
|
|
|
|
+ >
|
|
|
|
+ </p>
|
|
|
|
+ <p>
|
|
|
|
+ <span
|
|
|
|
+ v-for="(rang, rindex) in course.examCycleTimeRange"
|
|
|
|
+ :key="rindex"
|
|
|
|
+ >
|
|
|
|
+ {{ rang.timeRange[0] }}~{{ rang.timeRange[1] }}
|
|
|
|
+ </span>
|
|
|
|
+ </p>
|
|
|
|
+ </div>
|
|
|
|
+ </td>
|
|
|
|
+ <td>{{ course.practiceCount }}</td>
|
|
|
|
+ <td>{{ course.recentObjectiveAccuracy }}%</td>
|
|
|
|
+ <td>{{ course.aveObjectiveAccuracy }}%</td>
|
|
|
|
+ <td>{{ course.maxObjectiveAccuracy }}%</td>
|
|
|
|
+ <td>
|
|
|
|
+ <n-button
|
|
|
|
+ type="success"
|
|
|
|
+ block
|
|
|
|
+ :disabled="courseIsDisabled(course)"
|
|
|
|
+ :title="courseDiableReason(course)"
|
|
|
|
+ @click="toEnterPractice(course)"
|
|
|
|
+ >进入练习</n-button
|
|
|
|
+ >
|
|
|
|
+ <n-button type="success" block @click="toEnterPracticeList(course)"
|
|
|
|
+ >查看详情</n-button
|
|
|
|
+ >
|
|
|
|
+ </td>
|
|
|
|
+ </tr>
|
|
|
|
+ </tbody>
|
|
|
|
+ </n-table>
|
|
|
|
+</template>
|