MarkBoardKeyBoard.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div v-if="store.currentTask" class="mark-board-track-container">
  3. <div>
  4. <h1 class="tw-text-3xl tw-text-center" @click="toggleKeyMouse">
  5. 键盘给分
  6. </h1>
  7. </div>
  8. <div>
  9. <h1 class="tw-text-3xl tw-text-center">
  10. 总分:{{ store.currentMarkResult?.markerScore || 0 }}
  11. </h1>
  12. </div>
  13. <div v-if="store.currentTask && store.currentTask.questionList">
  14. <template
  15. v-for="(question, index) in store.currentTask?.questionList"
  16. :key="index"
  17. >
  18. <div
  19. @click="chooseQuestion(question)"
  20. class="question tw-rounded tw-p-1 tw-mb-2"
  21. :class="isCurrentQuestion(question) && 'current-question'"
  22. >
  23. <div class="tw-flex tw-justify-between">
  24. <div>
  25. <div>
  26. {{ question.title }} {{ question.mainNumber }}-{{
  27. question.subNumber
  28. }}
  29. </div>
  30. <div class="tw-text-center tw-text-3xl">
  31. {{ question.score }}
  32. </div>
  33. </div>
  34. <div>
  35. <div class="tw-text-center">
  36. 间隔{{ question.intervalScore }}分
  37. </div>
  38. <div class="tw-text-3xl">
  39. {{ question.minScore }} ~ {{ question.maxScore }}
  40. </div>
  41. </div>
  42. </div>
  43. </div>
  44. </template>
  45. </div>
  46. </div>
  47. </template>
  48. <script lang="ts">
  49. import { Question } from "@/types";
  50. import { isNumber } from "lodash";
  51. import { computed, defineComponent, onMounted, onUnmounted, watch } from "vue";
  52. import { store } from "./store";
  53. import { keyMouse } from "./use/keyboardAndMouse";
  54. import { autoChooseFirstQuestion } from "./use/autoChooseFirstQuestion";
  55. export default defineComponent({
  56. name: "MarkBoardKeyBoard",
  57. emits: ["submit"],
  58. setup(props, { emit }) {
  59. const { toggleKeyMouse } = keyMouse();
  60. const { chooseQuestion } = autoChooseFirstQuestion();
  61. const questionScoreSteps = computed(() => {
  62. const question = store.currentQuestion;
  63. if (!question) return [];
  64. const steps = [];
  65. for (
  66. let i = 0;
  67. i <= question.maxScore - (question.score || 0);
  68. i += question.intervalScore
  69. ) {
  70. steps.push(i);
  71. }
  72. if (
  73. (question.maxScore - (question.score || 0)) % question.intervalScore !==
  74. 0
  75. ) {
  76. steps.push(question.maxScore - (question.score || 0));
  77. }
  78. return steps;
  79. });
  80. function isCurrentQuestion(question: Question) {
  81. return (
  82. store.currentQuestion?.mainNumber === question.mainNumber &&
  83. store.currentQuestion?.subNumber === question.subNumber
  84. );
  85. }
  86. // 当题目改变时,重置当前分数
  87. watch(
  88. () => store.currentQuestion,
  89. () => {
  90. store.currentScore = undefined;
  91. }
  92. );
  93. let keyPressTimestamp = 0;
  94. let keys: string[] = [];
  95. function numberKeyListener(event: KeyboardEvent) {
  96. // console.log(event);
  97. if (!store.currentQuestion || !store.currentTask) return;
  98. // 处理Enter跳下一题或submit
  99. if (event.key === "Enter") {
  100. if (!isNumber(store.currentQuestion.score)) {
  101. // 当前题赋分不通过,Enter无效
  102. return;
  103. }
  104. const idx = store.currentTask?.questionList.findIndex(
  105. (q) =>
  106. q.mainNumber === store.currentQuestion?.mainNumber &&
  107. q.subNumber === store.currentQuestion.subNumber
  108. );
  109. if (idx + 1 === store.currentTask?.questionList.length) {
  110. submit();
  111. } else {
  112. chooseQuestion(store.currentTask.questionList[idx + 1]);
  113. }
  114. keys = [];
  115. return;
  116. }
  117. // TODO: 确认数字按键的间隔
  118. if (event.timeStamp - keyPressTimestamp > 1.5 * 1000) {
  119. keys = [];
  120. }
  121. keyPressTimestamp = event.timeStamp;
  122. keys.push(event.key);
  123. if (isNaN(parseFloat(keys.join("")))) {
  124. keys = [];
  125. }
  126. if (event.key === "Escape") {
  127. keys = [];
  128. }
  129. const score = parseFloat(keys.join(""));
  130. if (isNumber(score) && questionScoreSteps.value.includes(score)) {
  131. store.currentQuestion.score = score;
  132. }
  133. }
  134. onMounted(() => {
  135. document.addEventListener("keydown", numberKeyListener);
  136. });
  137. onUnmounted(() => {
  138. document.removeEventListener("keydown", numberKeyListener);
  139. });
  140. function submit() {
  141. const errors: any = [];
  142. store.currentTask?.questionList.forEach((question, index) => {
  143. if (!isNumber(question.score)) {
  144. errors.push({ question, index, error: "没有赋分不能提交" });
  145. }
  146. });
  147. if (errors.length === 0) {
  148. emit("submit");
  149. } else {
  150. console.log(errors);
  151. }
  152. }
  153. return {
  154. store,
  155. toggleKeyMouse,
  156. isCurrentQuestion,
  157. chooseQuestion,
  158. questionScoreSteps,
  159. submit,
  160. };
  161. },
  162. });
  163. </script>
  164. <style scoped>
  165. .mark-board-track-container {
  166. max-width: 250px;
  167. min-width: 250px;
  168. border-left: 1px solid grey;
  169. padding-left: 6px;
  170. padding-right: 6px;
  171. }
  172. .question {
  173. min-width: 80px;
  174. border: 1px solid grey;
  175. }
  176. .current-question {
  177. border: 1px solid yellowgreen;
  178. background-color: lightblue;
  179. }
  180. .single-score {
  181. width: 30px;
  182. height: 30px;
  183. display: grid;
  184. place-content: center;
  185. border: 1px solid black;
  186. border-radius: 5px;
  187. }
  188. .current-score {
  189. border: 1px solid yellowgreen;
  190. background-color: lightblue;
  191. }
  192. </style>