SingleQuestionView.vue 5.7 KB

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