MultipleQuestionView.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <template>
  2. <div
  3. v-if="isSyncState && examQuestion.questionType === 'MULTIPLE_CHOICE'"
  4. class="question-view"
  5. >
  6. <question-body
  7. :questionBody="question.body"
  8. :examQuestion="examQuestion"
  9. ></question-body>
  10. <div class="ops">
  11. <div class="stu-answer"> {{oldIndexToABCD}}</div>
  12. <div class="score">({{examQuestion.questionScore}}分)</div>
  13. </div>
  14. <div
  15. v-for="(option, index) in newQuestionOptions"
  16. :key="index"
  17. class="option"
  18. @click="toggleAnswer(option.oldIndex)"
  19. >
  20. <div
  21. :class="studentAnswer.includes(option.oldIndex) && 'row-selected'"
  22. style="display: flex;"
  23. >
  24. <input
  25. type="checkbox"
  26. name="question"
  27. value="option.oldIndex"
  28. :checked="studentAnswer && studentAnswer.includes(option.oldIndex)"
  29. style="margin-top: 4px"
  30. />
  31. <span style="padding: 0 10px;">{{optionName[index]}}: </span>
  32. <div
  33. class="question-options"
  34. v-if="option.value"
  35. >
  36. <question-body
  37. :questionBody="option.value.body"
  38. :examQuestion="examQuestion"
  39. ></question-body>
  40. </div>
  41. </div>
  42. </div>
  43. <div class="reset">
  44. <i-button
  45. type="warning"
  46. size="large"
  47. @click="() => answerQuestion(null)"
  48. >重置答案</i-button>
  49. <span v-if="examShouldShowAnswer()">
  50. &nbsp;&nbsp;&nbsp;<i-button
  51. type="info"
  52. size="large"
  53. @click="showAnswer"
  54. >显示答案</i-button>
  55. </span>
  56. <div v-if="examShouldShowAnswer() && isShowAnswer">
  57. 正确答案:<div>{{rightAnswerTransform}}</div>
  58. </div>
  59. </div>
  60. </div>
  61. </template>
  62. <script>
  63. import QuestionBody from "./QuestionBody";
  64. import { createNamespacedHelpers } from "vuex";
  65. const { mapMutations, mapGetters } = createNamespacedHelpers(
  66. "examingHomeModule"
  67. );
  68. const optionName = "ABCDEF".split("");
  69. export default {
  70. name: "MultipleQuestionView",
  71. data() {
  72. return {
  73. questionBody: this.question.body,
  74. optionName,
  75. studentAnswer: this.examQuestion.studentAnswer || "",
  76. isShowAnswer: false
  77. };
  78. },
  79. props: {
  80. question: Object,
  81. examQuestion: Object
  82. },
  83. created: function() {
  84. window.addEventListener("keyup", this.keyup);
  85. },
  86. beforeDestroy() {
  87. window.removeEventListener("keyup", this.keyup);
  88. },
  89. beforeUpdate() {
  90. this.answerQuestion(this.studentAnswer);
  91. },
  92. methods: {
  93. ...mapMutations(["updateExamQuestion"]),
  94. ...mapGetters(["examShouldShowAnswer"]),
  95. keyup(e) {
  96. if (
  97. ["BODY", "A", "BUTTON", "DIV"].includes(
  98. document.activeElement.tagName
  99. ) ||
  100. (["INPUT"].includes(document.activeElement.tagName) &&
  101. ["checkbox", "radio"].includes(document.activeElement.type))
  102. ) {
  103. if (
  104. ["KeyA", "KeyB", "KeyC", "KeyD", "KeyE", "KeyF", "KeyG"]
  105. .slice(0, this.question.questionOptionList.length)
  106. .includes(e.code)
  107. ) {
  108. window._hmt.push(["_trackEvent", "正在考试页面", "快捷键", "ABCDE"]);
  109. const selectedOldIndex =
  110. "" +
  111. this.newQuestionOptions.find(v => v.name == e.code[3]).oldIndex;
  112. if (this.studentAnswer.includes(selectedOldIndex)) {
  113. this.studentAnswer = this.studentAnswer.replace(
  114. selectedOldIndex,
  115. ""
  116. );
  117. } else {
  118. this.studentAnswer = this.studentAnswer
  119. .concat(selectedOldIndex)
  120. .split("")
  121. .sort()
  122. .join("");
  123. }
  124. }
  125. }
  126. },
  127. toggleAnswer: function(selectedOldIndex) {
  128. if (this.studentAnswer.includes(selectedOldIndex)) {
  129. this.studentAnswer = this.studentAnswer.replace(selectedOldIndex, "");
  130. } else {
  131. this.studentAnswer = this.studentAnswer
  132. .concat(selectedOldIndex)
  133. .split("")
  134. .sort()
  135. .join("");
  136. }
  137. },
  138. async answerQuestion(studentAnswer) {
  139. if (this.isSyncState === false) {
  140. console.log("更新答案时,问题与答案的号码不一致。");
  141. return;
  142. }
  143. let realAnswer = studentAnswer;
  144. if (studentAnswer === "") {
  145. realAnswer = null;
  146. }
  147. if (realAnswer !== this.examQuestion.studentAnswer) {
  148. this.updateExamQuestion({
  149. order: this.examQuestion.order,
  150. studentAnswer: realAnswer
  151. });
  152. }
  153. },
  154. showAnswer() {
  155. this.isShowAnswer = !this.isShowAnswer;
  156. }
  157. },
  158. watch: {
  159. examQuestion: function() {
  160. this.studentAnswer = this.examQuestion.studentAnswer || "";
  161. }
  162. },
  163. computed: {
  164. isSyncState() {
  165. return this.examQuestion.order == this.$route.params.order;
  166. },
  167. newQuestionOptions() {
  168. return this.question.questionOptionList.map((v, i) => {
  169. return {
  170. value: this.question.questionOptionList[
  171. this.examQuestion.optionPermutation[i]
  172. ],
  173. oldIndex: "" + this.examQuestion.optionPermutation[i],
  174. name: optionName[i]
  175. };
  176. });
  177. },
  178. oldIndexToABCD() {
  179. return (
  180. this.examQuestion.studentAnswer &&
  181. this.newQuestionOptions
  182. .filter(v => this.examQuestion.studentAnswer.includes(v.oldIndex))
  183. .map(v => v.name)
  184. .join("")
  185. );
  186. },
  187. rightAnswerTransform() {
  188. return (
  189. this.question.rightAnswer &&
  190. this.newQuestionOptions
  191. .filter(v => this.question.rightAnswer.includes(v.oldIndex))
  192. .map(v => v.name)
  193. .join("")
  194. );
  195. }
  196. },
  197. components: {
  198. QuestionBody
  199. }
  200. };
  201. </script>
  202. <style scoped>
  203. .question-view {
  204. display: grid;
  205. grid-row-gap: 10px;
  206. }
  207. .question-body {
  208. font-size: 18px;
  209. margin-bottom: 10px;
  210. }
  211. .ops {
  212. display: flex;
  213. align-items: flex-end;
  214. }
  215. .stu-answer {
  216. width: 100px;
  217. border-bottom: 1px solid black;
  218. text-align: center;
  219. height: 20px;
  220. }
  221. .option {
  222. display: flex;
  223. cursor: pointer;
  224. }
  225. .option:hover {
  226. background-color: aliceblue;
  227. }
  228. .row-selected {
  229. background-color: aliceblue;
  230. width: 100%;
  231. }
  232. .question-options {
  233. text-align: left;
  234. }
  235. .question-options > .question-body {
  236. text-align: left;
  237. font-size: 16px !important;
  238. }
  239. </style>