MarkBoardTrack.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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">
  9. 总分:{{ store.currentMarkResult?.markerScore || 0 }}
  10. </h1>
  11. </div>
  12. <div>
  13. <div class="tw-text-2xl tw-text-center" @click="submit">提交</div>
  14. </div>
  15. <div
  16. v-if="store.currentTask && store.currentTask.questionList"
  17. class="tw-flex tw-gap-1 tw-flex-wrap tw-justify-between"
  18. >
  19. <template
  20. v-for="(question, index) in store.currentTask?.questionList"
  21. :key="index"
  22. >
  23. <div
  24. @click="chooseQuestion(question)"
  25. class="question tw-rounded tw-p-1"
  26. :class="isCurrentQuestion(question) && 'current-question'"
  27. >
  28. <div>
  29. {{ question.title }} {{ question.mainNumber }}-{{
  30. question.subNumber
  31. }}
  32. </div>
  33. <div class="tw-text-center">
  34. {{ question.score || 0 }}
  35. </div>
  36. </div>
  37. </template>
  38. </div>
  39. <div class="tw-flex tw-gap-1 tw-flex-wrap tw-mt-5">
  40. <div
  41. v-for="(s, i) in questionScoreSteps"
  42. :key="i"
  43. @click="chooseScore(s)"
  44. class="single-score"
  45. :class="isCurrentScore(s) && 'current-score'"
  46. >
  47. {{ s }}
  48. </div>
  49. </div>
  50. <div class="tw-flex tw-justify-between tw-mt-4">
  51. <div @click="clearLatestMarkOfCurrentQuetion">回退</div>
  52. <div @click="clearAllMarksOfCurrentQuetion">清除本题</div>
  53. </div>
  54. </div>
  55. </template>
  56. <script lang="ts">
  57. import { Question } from "@/types";
  58. import { isNumber } from "lodash";
  59. import { computed, defineComponent, onMounted, onUnmounted, watch } from "vue";
  60. import { store } from "./store";
  61. import { autoChooseFirstQuestion } from "./use/autoChooseFirstQuestion";
  62. import { message } from "ant-design-vue";
  63. export default defineComponent({
  64. name: "MarkBoardTrack",
  65. emits: ["submit"],
  66. setup(props, { emit }) {
  67. const { chooseQuestion } = autoChooseFirstQuestion();
  68. const questionScoreSteps = computed(() => {
  69. const question = store.currentQuestion;
  70. if (!question) return [];
  71. const remainScore = question.maxScore - (question.score || 0);
  72. const steps = [];
  73. for (
  74. let i = 0;
  75. i <= remainScore;
  76. i = (i * 10 + question.intervalScore * 10) / 10
  77. ) {
  78. steps.push(i);
  79. }
  80. if ((remainScore * 10) % (question.intervalScore * 10) !== 0) {
  81. steps.push(remainScore);
  82. }
  83. return steps;
  84. });
  85. function isCurrentQuestion(question: Question) {
  86. return (
  87. store.currentQuestion?.mainNumber === question.mainNumber &&
  88. store.currentQuestion?.subNumber === question.subNumber
  89. );
  90. }
  91. watch(
  92. () => store.currentQuestion,
  93. () => {
  94. store.currentScore = undefined;
  95. }
  96. );
  97. function isCurrentScore(score: number) {
  98. return store.currentScore === score;
  99. }
  100. function chooseScore(score: number) {
  101. store.currentScore = score;
  102. }
  103. let keyPressTimestamp = 0;
  104. let keys: string[] = [];
  105. function numberKeyListener(event: KeyboardEvent) {
  106. // console.log(event);
  107. if (!store.currentQuestion) return;
  108. function indexOfCurrentQuestion() {
  109. return store.currentTask?.questionList.findIndex(
  110. (q) =>
  111. q.mainNumber === store.currentQuestion?.mainNumber &&
  112. q.subNumber === store.currentQuestion.subNumber
  113. );
  114. }
  115. // tab 循环答题列表
  116. if (event.key === "Tab") {
  117. const idx = indexOfCurrentQuestion() as number;
  118. if (idx >= 0 && store.currentTask) {
  119. const len = store.currentTask.questionList.length;
  120. chooseQuestion(store.currentTask.questionList[(idx + 1) % len]);
  121. event.preventDefault();
  122. }
  123. return;
  124. }
  125. if (event.timeStamp - keyPressTimestamp > 1.5 * 1000) {
  126. keys = [];
  127. }
  128. keyPressTimestamp = event.timeStamp;
  129. keys.push(event.key);
  130. if (isNaN(parseFloat(keys.join("")))) {
  131. keys = [];
  132. }
  133. if (event.key === "Escape") {
  134. keys = [];
  135. }
  136. const score = parseFloat(keys.join(""));
  137. if (isNumber(score) && questionScoreSteps.value.includes(score)) {
  138. chooseScore(score);
  139. }
  140. }
  141. onMounted(() => {
  142. document.addEventListener("keydown", numberKeyListener);
  143. });
  144. onUnmounted(() => {
  145. document.removeEventListener("keydown", numberKeyListener);
  146. });
  147. function clearLatestMarkOfCurrentQuetion() {
  148. if (!store.currentMarkResult || !store.currentQuestion) return;
  149. const ts = store.currentMarkResult?.trackList.filter(
  150. (q) =>
  151. q.mainNumber === store.currentQuestion?.mainNumber &&
  152. q.subNumber === store.currentQuestion?.subNumber
  153. );
  154. if (ts.length === 0) return;
  155. const maxNumber = Math.max(...ts.map((q) => q.number));
  156. const idx = store.currentMarkResult.trackList.findIndex(
  157. (q) =>
  158. q.mainNumber === store.currentQuestion?.mainNumber &&
  159. q.subNumber === store.currentQuestion?.subNumber &&
  160. q.number === maxNumber
  161. );
  162. store.removeScoreTracks = store.currentMarkResult.trackList.splice(
  163. idx,
  164. 1
  165. );
  166. }
  167. function clearAllMarksOfCurrentQuetion() {
  168. if (!store.currentMarkResult || !store.currentQuestion) return;
  169. store.removeScoreTracks = store.currentMarkResult?.trackList.filter(
  170. (q) =>
  171. q.mainNumber === store.currentQuestion?.mainNumber &&
  172. q.subNumber === store.currentQuestion?.subNumber
  173. );
  174. store.currentMarkResult.trackList = store.currentMarkResult?.trackList.filter(
  175. (q) =>
  176. !(
  177. q.mainNumber === store.currentQuestion?.mainNumber &&
  178. q.subNumber === store.currentQuestion?.subNumber
  179. )
  180. );
  181. }
  182. function submit() {
  183. const errors: any = [];
  184. store.currentTask?.questionList.forEach((question, index) => {
  185. if (!isNumber(question.score)) {
  186. errors.push({ question, index, error: "没有赋分不能提交" });
  187. }
  188. });
  189. if (errors.length === 0) {
  190. emit("submit");
  191. } else {
  192. console.log(errors);
  193. message.error({
  194. content: errors
  195. .map((e: any) => `${e.index + 1}、${e.error}`)
  196. .join("\n"),
  197. duration: 10,
  198. });
  199. }
  200. }
  201. return {
  202. store,
  203. isCurrentQuestion,
  204. chooseQuestion,
  205. isCurrentScore,
  206. chooseScore,
  207. questionScoreSteps,
  208. clearLatestMarkOfCurrentQuetion,
  209. clearAllMarksOfCurrentQuetion,
  210. submit,
  211. };
  212. },
  213. });
  214. </script>
  215. <style scoped>
  216. .mark-board-track-container {
  217. max-width: 250px;
  218. min-width: 250px;
  219. border-left: 1px solid grey;
  220. padding-left: 6px;
  221. padding-right: 6px;
  222. }
  223. .question {
  224. min-width: 100px;
  225. border: 1px solid grey;
  226. }
  227. .current-question {
  228. border: 1px solid yellowgreen;
  229. background-color: lightblue;
  230. }
  231. .single-score {
  232. width: 30px;
  233. height: 30px;
  234. display: grid;
  235. place-content: center;
  236. border: 1px solid black;
  237. border-radius: 5px;
  238. }
  239. .current-score {
  240. border: 1px solid yellowgreen;
  241. background-color: lightblue;
  242. }
  243. </style>