QuestionView.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <script setup lang="ts">
  2. import QuestionIndex from "./QuestionIndex.vue";
  3. import QuestionBody from "./QuestionBody.vue";
  4. import QuestionViewSingle from "./QuestionViewSingle.vue";
  5. import { store } from "@/store/store";
  6. import { httpApp } from "@/plugins/axiosApp";
  7. import { watch } from "vue";
  8. import { ExamQuestion } from "@/types/student-client";
  9. import SplitPane from "@/components/SplitPane.vue";
  10. import { Star, StarOutline } from "@vicons/ionicons5";
  11. let parentPaneHeight = $ref(50);
  12. watch(
  13. () => store.exam.currentQuestion?.order,
  14. async () => {
  15. store.exam.currentQuestion && (await updateQuestion());
  16. },
  17. { immediate: true }
  18. );
  19. function transferWellNumber(body: string | null) {
  20. if (body === null) return null;
  21. //将题干中的三个#替换为下划线
  22. //将题干中的两个##数字##替换为下划线
  23. return body
  24. .replace(new RegExp("###", "g"), "_______")
  25. .replace(/##(\d+)##/g, function (a, b: string) {
  26. return "__" + parseInt(b) + "__";
  27. });
  28. }
  29. type QuestionUnitContent = Pick<
  30. ExamQuestion,
  31. "answerType" | "body" | "questionOptionList" | "questionType" | "rightAnswer"
  32. >;
  33. async function updateQuestion() {
  34. const currentExamQuestion = store.exam.currentQuestion; // 避免以后执行时,this.examQuestion换掉了
  35. const examRecordDataId = store.exam.examRecordDataId;
  36. const { order, questionId } = currentExamQuestion;
  37. let qContentRes: {
  38. body: string;
  39. hasAudios: boolean;
  40. questionUnitList: QuestionUnitContent[];
  41. };
  42. try {
  43. qContentRes = (
  44. await httpApp.get(
  45. `/api/ecs_oe_student/examQuestion/getQuestionContent?questionId=${questionId}&exam_record_id=${examRecordDataId}`,
  46. { "axios-retry": { retries: 5, retryDelay: () => 3000 } }
  47. )
  48. ).data;
  49. } catch (e) {
  50. logger({
  51. cnl: ["server"],
  52. pgu: "AUTO",
  53. act: "获取试题内容失败",
  54. possibleError: e,
  55. });
  56. return;
  57. }
  58. if (!qContentRes) return;
  59. // Object.assign(currentExamQuestion, qContentRes.data);
  60. let i = 0;
  61. //判断是否为套题
  62. const isNestedQuestion = qContentRes.questionUnitList.length > 1;
  63. store.exam.examQuestionList.forEach((q) => {
  64. if (q.questionId === questionId) {
  65. q.gotQuestionContent = true;
  66. let qc = qContentRes.questionUnitList[i++];
  67. q.answerType = qc.answerType;
  68. q.parentBody = transferWellNumber(qContentRes.body);
  69. q.body = transferWellNumber(qc.body)!;
  70. q.questionOptionList = qc.questionOptionList;
  71. q.questionType = qc.questionType;
  72. q.rightAnswer = qc.rightAnswer;
  73. q.isNestedQuestion = isNestedQuestion;
  74. }
  75. });
  76. {
  77. // cache next question content
  78. if (order < store.exam.examQuestionList.length) {
  79. void httpApp.get(
  80. "/api/ecs_oe_student/examQuestion/getQuestionContent?questionId=" +
  81. store.exam.examQuestionList[order].questionId +
  82. "&exam_record_id=" +
  83. examRecordDataId,
  84. { noErrorMessage: true }
  85. );
  86. }
  87. }
  88. // 对于某些图片套题,会导致高度计算错误,需多次nextTick
  89. // 从非套题进入套题时,会根据套题的内容动态调整套题的默认高度
  90. const parentQuestion = document.getElementsByClassName("parent-question")[0];
  91. if (parentQuestion) {
  92. parentPaneHeight =
  93. 5 +
  94. (100 * parentQuestion.clientHeight) / (document.body.clientHeight - 160);
  95. if (parentPaneHeight > 60) {
  96. parentPaneHeight = 60;
  97. }
  98. }
  99. }
  100. function toggleSign() {
  101. store.updateExamQuestion({
  102. order: store.exam.currentQuestion.order,
  103. isStarred: !store.exam.currentQuestion.isStarred,
  104. });
  105. }
  106. </script>
  107. <template>
  108. <div
  109. v-if="store.exam.currentQuestion.gotQuestionContent"
  110. class="question-container"
  111. >
  112. <div class="question-header">
  113. <n-icon
  114. :component="store.exam.currentQuestion.isStarred ? Star : StarOutline"
  115. class="star"
  116. @click="toggleSign"
  117. />
  118. <question-index />
  119. </div>
  120. <div v-if="store.exam.currentQuestion.parentBody" class="tw-flex-grow">
  121. <SplitPane layout="vertical">
  122. <template #left>
  123. <div class="question-view parent-question tw-h-full">
  124. <QuestionBody
  125. :questionBody="store.exam.currentQuestion.parentBody"
  126. />
  127. </div>
  128. </template>
  129. <template #right>
  130. <QuestionViewSingle class="tw-h-full" />
  131. </template>
  132. </SplitPane>
  133. </div>
  134. <QuestionViewSingle v-else />
  135. </div>
  136. <div v-else>试题获取中...</div>
  137. </template>
  138. <style scoped>
  139. .question-container {
  140. overflow: auto;
  141. display: flex;
  142. flex-direction: column;
  143. }
  144. .question-view {
  145. padding: 20px 30px;
  146. font-size: 16px;
  147. text-align: left;
  148. overflow: auto;
  149. }
  150. .question-header {
  151. display: flex;
  152. align-items: center;
  153. }
  154. .star {
  155. font-size: 36px;
  156. color: #ffcc00;
  157. align-self: flex-start;
  158. }
  159. .star:hover {
  160. cursor: pointer;
  161. }
  162. div.hr {
  163. border-bottom: 1px dashed gray;
  164. }
  165. </style>