Arbitrate.vue 7.8 KB

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