MarkBoardInspect.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <template>
  2. <div
  3. v-if="store.currentTask"
  4. :style="{ display: store.MarkBoardTrackCollapse ? 'none' : 'block' }"
  5. class="mark-board-track-container"
  6. >
  7. <div>
  8. <h1 class="tw-text-3xl tw-text-center">试卷总分:{{ markerScore }}</h1>
  9. </div>
  10. <div v-if="groups">
  11. <template v-for="(groupNumber, index) in groups" :key="index">
  12. <div class="tw-mb-4">
  13. <div
  14. class="
  15. tw-flex tw-justify-between tw-place-items-center
  16. hover:tw-bg-gray-200
  17. "
  18. style="border-bottom: 1px solid grey"
  19. @mouseover="addFocusTrack(groupNumber, undefined, undefined)"
  20. @mouseleave="removeFocusTrack"
  21. >
  22. 分组 {{ groupNumber }}
  23. <input
  24. class="tw-my-auto"
  25. title="打回"
  26. type="checkbox"
  27. @click="groupClicked(groupNumber)"
  28. :checked="groupChecked(groupNumber)"
  29. />
  30. </div>
  31. <div v-if="questions">
  32. <template v-for="(question, index) in questions" :key="index">
  33. <div
  34. v-if="question.groupNumber === groupNumber"
  35. class="
  36. question
  37. tw-flex tw-place-items-center tw-mb-1 tw-ml-2
  38. hover:tw-bg-gray-200
  39. "
  40. @mouseover="
  41. addFocusTrack(
  42. undefined,
  43. question.mainNumber,
  44. question.subNumber
  45. )
  46. "
  47. @mouseleave="removeFocusTrack"
  48. >
  49. <span class="tw-flex-1">
  50. {{ question.title }} {{ question.mainNumber }}-{{
  51. question.subNumber
  52. }}
  53. </span>
  54. <span class="tw-flex-1 tw-text-center">
  55. {{ question.score || 0 }}
  56. </span>
  57. <input
  58. title="打回"
  59. type="checkbox"
  60. @change="questionCheckChanged(question)"
  61. :checked="questionChecked(question)"
  62. />
  63. </div>
  64. </template>
  65. </div>
  66. </div>
  67. </template>
  68. </div>
  69. <div class="tw-flex tw-justify-center">
  70. <qm-button
  71. type="primary"
  72. v-if="
  73. store.currentTask.inspectTime && store.currentTask.inspectTime > 0
  74. "
  75. @click="reject"
  76. >
  77. 打回
  78. </qm-button>
  79. <qm-button
  80. v-else-if="checkedQuestions.length === 0"
  81. @click="inspect"
  82. type="primary"
  83. >
  84. 复核
  85. </qm-button>
  86. <qm-button v-else @click="reject" type="primary">打回</qm-button>
  87. </div>
  88. </div>
  89. </template>
  90. <script setup lang="ts">
  91. import type { Question } from "@/types";
  92. import { message } from "ant-design-vue";
  93. import { computed, reactive, watch } from "vue";
  94. import { store } from "../store";
  95. const emit = defineEmits(["inspect", "reject"]);
  96. let checkedQuestions = reactive([] as Array<Question>);
  97. watch(
  98. () => store.currentTask,
  99. () => {
  100. checkedQuestions.splice(0);
  101. }
  102. );
  103. const groups = computed(() => {
  104. const gs = store.currentTask?.questionList.map((q) => q.groupNumber);
  105. return [...new Set(gs)].sort((a, b) => a - b);
  106. });
  107. const questions = computed(() => {
  108. const qs = store.currentTask?.questionList;
  109. return qs;
  110. });
  111. const markerScore = computed(
  112. () =>
  113. (questions.value
  114. ?.map((q) => Math.round((q.score || 0) * 100))
  115. .reduce((acc, s) => acc + s) || 0) / 100
  116. );
  117. function addToCheckedQuestion(question: Question) {
  118. checkedQuestions.push(question);
  119. }
  120. function removeCheckedQuestion(question: Question) {
  121. const idx = checkedQuestions.indexOf(question);
  122. checkedQuestions.splice(idx, 1);
  123. }
  124. function groupChecked(groupNumber: number) {
  125. return (
  126. checkedQuestions.filter((q) => q.groupNumber === groupNumber).length ===
  127. questions.value?.filter((q) => q.groupNumber === groupNumber).length
  128. );
  129. }
  130. function questionChecked(question: Question) {
  131. return checkedQuestions.includes(question);
  132. }
  133. function questionCheckChanged(question: Question) {
  134. const checked = questionChecked(question);
  135. if (checked) {
  136. removeCheckedQuestion(question);
  137. } else {
  138. addToCheckedQuestion(question);
  139. }
  140. }
  141. function groupClicked(groupNumber: number) {
  142. if (groupChecked(groupNumber)) {
  143. checkedQuestions
  144. .filter((q) => q.groupNumber === groupNumber)
  145. .forEach((q) => {
  146. const idx = checkedQuestions.indexOf(q);
  147. checkedQuestions.splice(idx, 1);
  148. });
  149. } else {
  150. questions.value
  151. ?.filter((q) => q.groupNumber === groupNumber)
  152. .forEach((q) => {
  153. if (!questionChecked(q)) checkedQuestions.push(q);
  154. });
  155. }
  156. }
  157. function addFocusTrack(
  158. groupNumber: number | undefined,
  159. mainNumber: number | undefined,
  160. subNumber: string | undefined
  161. ) {
  162. store.focusTracks.splice(0);
  163. if (groupNumber) {
  164. questions.value
  165. ?.filter((q) => q.groupNumber === groupNumber)
  166. ?.map((q) => q.trackList)
  167. .reduce((acc, ts) => acc.concat(ts))
  168. .forEach((t) => {
  169. store.focusTracks.push(t);
  170. });
  171. } else {
  172. questions.value
  173. ?.map((q) => q.trackList)
  174. .reduce((acc, ts) => acc.concat(ts))
  175. .filter((t) => {
  176. if (mainNumber) {
  177. return t.mainNumber === mainNumber && t.subNumber === subNumber;
  178. } else {
  179. return false;
  180. }
  181. })
  182. .forEach((t) => {
  183. store.focusTracks.push(t);
  184. });
  185. }
  186. // console.log(store.focusTracks);
  187. }
  188. function removeFocusTrack() {
  189. store.focusTracks.splice(0);
  190. }
  191. function reject() {
  192. if (checkedQuestions.length === 0) {
  193. message.warn({ content: "请先选择试题。" });
  194. return;
  195. }
  196. emit("reject", checkedQuestions);
  197. }
  198. function inspect() {
  199. emit("inspect");
  200. }
  201. </script>
  202. <style scoped>
  203. .mark-board-track-container {
  204. max-width: 250px;
  205. min-width: 250px;
  206. border-left: 1px solid grey;
  207. padding-left: 6px;
  208. padding-right: 6px;
  209. max-height: calc(100vh - 41px);
  210. overflow: auto;
  211. }
  212. .question {
  213. min-width: 100px;
  214. border-bottom: 1px dotted grey;
  215. }
  216. </style>