Arbitrate.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div class="my-container">
  3. <mark-header />
  4. <div class="tw-flex tw-gap-1">
  5. <mark-history
  6. v-if="!isSingleStudent"
  7. :subjectCode="subjectCode"
  8. :groupNumber="groupNumber"
  9. :get-history="getArbitrateHistory"
  10. />
  11. <ArbitrateMarkList />
  12. <mark-body @error="renderError" />
  13. <mark-board-key-board
  14. v-if="showMarkBoardKeyBoard"
  15. @submit="saveTaskToServer(false)"
  16. @unselectiveSubmit="saveTaskToServer(true)"
  17. />
  18. <mark-board-mouse
  19. v-if="showMarkBoardMouse"
  20. @submit="saveTaskToServer(false)"
  21. @unselectiveSubmit="saveTaskToServer(true)"
  22. />
  23. </div>
  24. </div>
  25. <AnswerModal />
  26. <PaperModal />
  27. <MinimapModal />
  28. </template>
  29. <script setup lang="ts">
  30. import { computed, onMounted, watch } from "vue";
  31. // 要共用UI就要共用store
  32. import { store } from "@/store/store";
  33. import MarkHeader from "./MarkHeader.vue";
  34. import { useRoute } from "vue-router";
  35. import MarkBody from "./MarkBody.vue";
  36. import MarkBoardKeyBoard from "@/features/mark/MarkBoardKeyBoard.vue";
  37. import MarkBoardMouse from "@/features/mark/MarkBoardMouse.vue";
  38. import MinimapModal from "@/features/mark/MinimapModal.vue";
  39. import MarkHistory from "@/features/mark/MarkHistory.vue";
  40. import { message } from "ant-design-vue";
  41. import {
  42. clearArbitrateTask,
  43. getArbitrateSetting,
  44. getArbitrateTaskStatus,
  45. getOneOfArbitrateTask,
  46. getSingleArbitrateTask,
  47. saveArbitrateTask,
  48. } from "@/api/arbitratePage";
  49. import ArbitrateMarkList from "./ArbitrateMarkList.vue";
  50. import type { Setting, Task } from "@/types";
  51. import { isNumber } from "lodash";
  52. import AnswerModal from "../mark/AnswerModal.vue";
  53. import PaperModal from "../mark/PaperModal.vue";
  54. import { getPaper } from "@/api/jsonMark";
  55. import { getArbitrateHistory } from "@/api/arbitratePage";
  56. import EventBus from "@/plugins/eventBus";
  57. const route = useRoute();
  58. let isSingleStudent = !!route.query.historyId;
  59. const {
  60. subjectCode,
  61. groupNumber,
  62. historyId: libraryId,
  63. } = route.query as {
  64. subjectCode: string;
  65. groupNumber: string;
  66. historyId: string;
  67. };
  68. async function updateClearTask() {
  69. await clearArbitrateTask(libraryId, subjectCode);
  70. }
  71. async function updateSetting() {
  72. const settingRes = await getArbitrateSetting(
  73. libraryId,
  74. subjectCode,
  75. groupNumber
  76. );
  77. store.setting.examType = settingRes.data.examType;
  78. store.setting.fileServer = settingRes.data.fileServer;
  79. store.setting.subject = settingRes.data.subject;
  80. store.setting.userName = settingRes.data.userName;
  81. store.setting.selective = settingRes.data.selective;
  82. store.setting.uiSetting = {
  83. "answer.paper.scale": 1,
  84. "score.board.collapse": false,
  85. "normal.mode": "keyboard",
  86. } as Setting["uiSetting"];
  87. store.setting.splitConfig = settingRes.data.splitConfig;
  88. if (store.setting.subject?.answerUrl) {
  89. store.setting.subject.answerUrl =
  90. store.setting.fileServer + store.setting.subject?.answerUrl;
  91. }
  92. if (store.setting.subject?.paperUrl) {
  93. store.setting.subject.paperUrl =
  94. store.setting.fileServer + store.setting.subject?.paperUrl;
  95. if (!store.isScanImage) {
  96. await getPaper(store);
  97. }
  98. }
  99. }
  100. async function updateStatus() {
  101. const res = await getArbitrateTaskStatus(subjectCode, groupNumber);
  102. if (res.data.valid) store.status = res.data;
  103. }
  104. async function updateTask() {
  105. // const mkey = "fetch_task_key";
  106. message.info({ content: "获取任务中...", duration: 1.5 });
  107. let res;
  108. if (isSingleStudent) {
  109. res = await getSingleStuTask();
  110. } else {
  111. res = await getOneOfStuTask();
  112. }
  113. // message.success({ content: "获取成功", key: mkey });
  114. if (res.data.libraryId) {
  115. let rawTask = res.data as Task;
  116. rawTask.sliceUrls = rawTask.sliceUrls?.map(
  117. (s) => store.setting.fileServer + s
  118. );
  119. rawTask.sheetUrls = rawTask.sheetUrls?.map(
  120. (s) => store.setting.fileServer + s
  121. );
  122. rawTask.jsonUrl = store.setting.fileServer + rawTask.jsonUrl;
  123. store.currentTask = res.data;
  124. // if (store.currentTask)
  125. // store.setting.subject = store.currentTask.subject;
  126. } else {
  127. store.message = res.data.message;
  128. }
  129. }
  130. // async function reloadAndfetchTask() {
  131. // // 需要清除缓存,才能取到之前锁定的任务
  132. // await updateClearTask();
  133. // // await updateSetting();
  134. // await fetchTask();
  135. // }
  136. watch(
  137. () => store.historyOpen,
  138. async () => {
  139. if (!store.historyOpen) {
  140. await updateClearTask();
  141. // await updateSetting();
  142. await fetchTask();
  143. }
  144. }
  145. );
  146. async function fetchTask() {
  147. !isSingleStudent && (await updateStatus());
  148. await updateTask();
  149. }
  150. const showMarkBoardKeyBoard = computed(() => {
  151. return store.setting.uiSetting["normal.mode"] === "keyboard";
  152. });
  153. const showMarkBoardMouse = computed(() => {
  154. return store.setting.uiSetting["normal.mode"] === "mouse";
  155. });
  156. onMounted(async () => {
  157. await updateClearTask();
  158. await updateSetting();
  159. await fetchTask(); // mark-header 会调用 (watchEffect)
  160. });
  161. watch(
  162. () => store.currentTask,
  163. () => {
  164. // 重置当前选择的quesiton和score
  165. store.currentQuestion = undefined;
  166. store.currentScore = undefined;
  167. }
  168. );
  169. // FIXME: 更新分数,在评卷界面不需要
  170. watch(
  171. () => store.currentTask,
  172. () => {
  173. const markResult = store.currentTask?.markResult;
  174. if (markResult && store.currentTask) {
  175. const scoreList = store.currentTask.questionList.map((q) => q.score);
  176. markResult.scoreList = [...(scoreList as number[])];
  177. markResult.markerScore =
  178. (markResult.scoreList.filter((s) => isNumber(s)) as number[]).reduce(
  179. (acc, v) => (acc += Math.round(v * 100)),
  180. 0
  181. ) / 100;
  182. }
  183. },
  184. { deep: true }
  185. );
  186. async function getSingleStuTask() {
  187. return getSingleArbitrateTask(libraryId);
  188. }
  189. async function getOneOfStuTask() {
  190. return getOneOfArbitrateTask(subjectCode, groupNumber);
  191. }
  192. const saveTaskToServer = async (unselective: boolean) => {
  193. if (!store.currentTask) return;
  194. console.log("save inspect task to server");
  195. const mkey = "save_task_key";
  196. message.loading({ content: "保存评卷任务...", key: mkey });
  197. let res;
  198. if (unselective) {
  199. res = (await saveArbitrateTask(
  200. store.currentTask.libraryId + "",
  201. store.currentTask.studentId + "",
  202. -1,
  203. [],
  204. true
  205. )) as any;
  206. } else {
  207. res = (await saveArbitrateTask(
  208. store.currentTask.libraryId + "",
  209. store.currentTask.studentId + "",
  210. store.currentTask.markResult.markerScore as number,
  211. store.currentTask.markResult.scoreList as Array<number>,
  212. false
  213. )) as any;
  214. }
  215. if (res.data.success && store.currentTask) {
  216. message.success({ content: "仲裁成功", key: mkey, duration: 2 });
  217. if (!store.historyOpen) {
  218. store.currentTask = undefined;
  219. if (!isSingleStudent) fetchTask();
  220. } else {
  221. EventBus.emit("should-reload-history");
  222. }
  223. } else if (res.data.message) {
  224. console.log(res.data.message);
  225. message.error({ content: res.data.message, key: mkey, duration: 10 });
  226. } else if (!store.currentTask) {
  227. message.warn({ content: "暂无新任务", key: mkey, duration: 10 });
  228. }
  229. };
  230. const renderError = () => {
  231. store.currentTask = undefined;
  232. store.message = "加载失败,请重新加载。";
  233. };
  234. </script>
  235. <style scoped>
  236. .my-container {
  237. width: 100%;
  238. overflow: clip;
  239. }
  240. </style>