OfflineExamList.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <script setup lang="ts">
  2. import { OfflineExam } from "@/types/student-client";
  3. import {
  4. exportOfflinePaperApi,
  5. getOfflineCourseApi,
  6. startOfflineExamApi,
  7. } from "@/api/offlineExam";
  8. import { CloudDownload } from "@vicons/ionicons5";
  9. import { onMounted } from "vue";
  10. import { store } from "@/store/store";
  11. import {
  12. downloadBlobByApi,
  13. downloadByBlob,
  14. toBlobByUrl,
  15. } from "@/utils/download";
  16. import OfflineExamUploadModal from "./OfflineExamUploadModal.vue";
  17. interface OfflineExamUploadModalInst {
  18. open: () => void;
  19. }
  20. const OfflineExamUploadModalRef = $ref<OfflineExamUploadModalInst | null>(null);
  21. const timeDifference = store.sysTime.difference;
  22. let loading = $ref(false);
  23. // 科目列表
  24. let courseList = $ref<OfflineExam[]>();
  25. let curCourse: OfflineExam = $ref();
  26. async function getCourseList() {
  27. const res = await getOfflineCourseApi();
  28. courseList = res.data || [];
  29. }
  30. function checkCourseTime(time: string) {
  31. return new Date(time).getTime() - Date.now() > timeDifference;
  32. }
  33. // 下载离线文件
  34. async function toDownloadOfflineFile(url: string, name: string) {
  35. logger({
  36. cnl: ["local", "server"],
  37. pgn: "离线考试",
  38. act: "下载作答",
  39. });
  40. if ([".png", ".jpg", ".jpeg"].some((v) => name.endsWith(v))) {
  41. const blob = await toBlobByUrl(url);
  42. downloadByBlob(blob, name);
  43. } else {
  44. window.location.href = url;
  45. }
  46. }
  47. async function toEnterExam(course: OfflineExam) {
  48. logger({
  49. cnl: ["local", "server"],
  50. pgn: "离线考试",
  51. act: "抽取试卷",
  52. ext: {
  53. examStudentId: course.examStudentId,
  54. },
  55. });
  56. loading = true;
  57. const res = await startOfflineExamApi(course.examStudentId).catch(
  58. () => false
  59. );
  60. loading = false;
  61. if (!res) return;
  62. void getCourseList();
  63. }
  64. function toViewPaper(course: OfflineExam) {
  65. logger({
  66. cnl: ["local", "server"],
  67. pgn: "离线考试",
  68. act: "查看试卷",
  69. });
  70. const user = {
  71. loginName: course.examStudentId,
  72. backUrl: window.document.location.href,
  73. isOnlineExam: true,
  74. };
  75. window.name = JSON.stringify(user);
  76. /** 此地址为后台管理系统中的页面, 后台管理系统与学生端页面同域*/
  77. window.location.href = `/admin/preview_paper/${course.paperId}?isback=true`;
  78. }
  79. let downloading = $ref(false);
  80. async function toDownloadPaper(course: OfflineExam) {
  81. if (downloading) return;
  82. logger({
  83. cnl: ["local", "server"],
  84. pgn: "离线考试",
  85. act: "下载试卷",
  86. });
  87. downloading = true;
  88. await downloadBlobByApi(() => {
  89. return exportOfflinePaperApi(course.paperId);
  90. }).catch(() => false);
  91. downloading = false;
  92. // 由于无法知晓用户是否取消保存,所以此时无法做相应的提示
  93. // if (result) {
  94. // $message.success("下载成功!");
  95. // } else {
  96. // $message.error("下载失败,请重新尝试!");
  97. // }
  98. }
  99. function toUploadPaper(course: OfflineExam) {
  100. logger({
  101. cnl: ["local", "server"],
  102. pgn: "离线考试",
  103. act: "上传答案",
  104. });
  105. curCourse = course;
  106. OfflineExamUploadModalRef?.open();
  107. }
  108. onMounted(() => {
  109. void getCourseList();
  110. });
  111. </script>
  112. <template>
  113. <div class="off-exam-courses">
  114. <n-table class="n-table-text-center" :singleLine="false">
  115. <colgroup>
  116. <col />
  117. <col />
  118. <col width="200px" />
  119. <col />
  120. <col width="200px" />
  121. </colgroup>
  122. <thead>
  123. <tr>
  124. <th>课程</th>
  125. <th>专业</th>
  126. <th>考试进入时间</th>
  127. <th>状态</th>
  128. <th>操作</th>
  129. </tr>
  130. </thead>
  131. <tbody>
  132. <tr v-for="(course, index) in courseList" :key="index">
  133. <td>{{ course.courseName }}</td>
  134. <td>{{ course.specialtyName }}</td>
  135. <td>
  136. <p>{{ course.startTime }}</p>
  137. <p>~</p>
  138. <p>{{ course.endTime }}</p>
  139. </td>
  140. <td>
  141. <div v-if="course.offlineFiles">
  142. <n-button
  143. v-for="(file, findex) in course.offlineFiles"
  144. :key="findex"
  145. :title="file.originalFileName"
  146. text
  147. block
  148. @click="
  149. toDownloadOfflineFile(
  150. file.offlineFileUrl,
  151. file.originalFileName
  152. )
  153. "
  154. >
  155. <template #icon>
  156. <n-icon :component="CloudDownload" :size="16"></n-icon>
  157. </template>
  158. 下载作答
  159. </n-button>
  160. </div>
  161. <div v-else>未上传</div>
  162. </td>
  163. <td>
  164. <div v-if="course.paperId">
  165. <n-button type="success" block @click="toViewPaper(course)"
  166. >查看试卷</n-button
  167. >
  168. <n-button
  169. type="success"
  170. :loading="downloading"
  171. :disabled="downloading"
  172. block
  173. @click="toDownloadPaper(course)"
  174. >下载试卷</n-button
  175. >
  176. <n-button type="success" block @click="toUploadPaper(course)"
  177. >上传答案</n-button
  178. >
  179. </div>
  180. <div v-else>
  181. <div v-if="checkCourseTime(course.startTime)">未开始</div>
  182. <div v-else-if="!checkCourseTime(course.endTime)">已结束</div>
  183. <div v-else>
  184. <n-button
  185. type="success"
  186. :loading="loading"
  187. @click="toEnterExam(course)"
  188. >
  189. 抽取试卷
  190. </n-button>
  191. </div>
  192. </div>
  193. </td>
  194. </tr>
  195. </tbody>
  196. </n-table>
  197. </div>
  198. <OfflineExamUploadModal
  199. ref="OfflineExamUploadModalRef"
  200. :course="curCourse"
  201. @modified="getCourseList"
  202. />
  203. </template>