ReadPreview.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="read-preview">
  4. <div class="stem">
  5. <span class="question-number">{{ data.property.question_number }}.</span>
  6. <span v-html="sanitizeHTML(data.stem)"></span>
  7. </div>
  8. <div
  9. v-if="isEnable(data.property.is_enable_description)"
  10. class="description"
  11. v-html="sanitizeHTML(data.description)"
  12. ></div>
  13. <div class="container">
  14. <div class="articel" v-html="sanitizeHTML(data.article)"></div>
  15. <div class="question-list">
  16. <template v-if="isAnswer">
  17. <component
  18. :is="previewComponents[item.type]"
  19. v-for="(item, i) in question_list"
  20. :key="i"
  21. ref="preview"
  22. class="preview"
  23. :data="item"
  24. @change="changeAnswer(i, item.type, $event)"
  25. />
  26. </template>
  27. <template v-else>
  28. <component
  29. :is="previewComponents[item.type]"
  30. v-for="(item, i) in childPreviewData"
  31. :key="i"
  32. class="preview"
  33. :data="item"
  34. @change="changeAnswer(i, item.type, $event)"
  35. />
  36. </template>
  37. </div>
  38. </div>
  39. </div>
  40. </template>
  41. <script>
  42. import PreviewMixin from './components/PreviewMixin';
  43. import SelectPreview from '@/views/exercise_questions/preview/SelectPreview.vue';
  44. import JudgePreview from '@/views/exercise_questions/preview/JudgePreview.vue';
  45. import MatchingPreview from '@/views/exercise_questions/preview/MatchingPreview.vue';
  46. import FillPreview from '../preview/FillPreview.vue';
  47. import ShortAnswerPreview from './ShortAnswerPreview.vue';
  48. import { GetQuestionInfo } from '@/api/exercise';
  49. export default {
  50. name: 'ReadPreview',
  51. mixins: [PreviewMixin],
  52. props: {
  53. childPreviewData: {
  54. type: Array,
  55. default: () => [],
  56. },
  57. },
  58. data() {
  59. return {
  60. previewComponents: {
  61. select: SelectPreview,
  62. judge: JudgePreview,
  63. matching: MatchingPreview,
  64. fill: FillPreview,
  65. short_answer: ShortAnswerPreview,
  66. },
  67. question_list: [],
  68. };
  69. },
  70. computed: {
  71. // 是否答题
  72. isAnswer() {
  73. return this.childPreviewData.length === 0 && this.childPreviewData.length !== this.data.question_list;
  74. },
  75. },
  76. watch: {
  77. isAnswer: {
  78. handler(val) {
  79. if (val) {
  80. const promises = this.data.question_list.map(({ id }) => {
  81. return GetQuestionInfo({ question_id: id });
  82. });
  83. Promise.all(promises).then((res) => {
  84. this.question_list = res.map(({ question }) => {
  85. let content = JSON.parse(question.content);
  86. if (!question.content) return { answer_list: [] };
  87. content.id = question.id;
  88. return content;
  89. });
  90. });
  91. }
  92. },
  93. immediate: true,
  94. },
  95. },
  96. created() {
  97. this.$set(
  98. this.answer,
  99. 'question_list',
  100. this.data.question_list.map((item) => {
  101. return { id: item.id, type: item.type, answer_list: [] };
  102. }),
  103. );
  104. },
  105. methods: {
  106. changeAnswer(i, type, { answer_list }) {
  107. if (this.disabled) return;
  108. this.answer.question_list[i].answer_list = answer_list;
  109. this.answer.question_list[i].type = type;
  110. },
  111. /**
  112. * 显示答案
  113. * @param {Boolean} isJudgingRightWrong 是否判断对错
  114. * @param {Boolean} isShowRightAnswer 是否显示正确答案
  115. * @param {Object} userAnswer 用户答案
  116. * @param {Boolean} disabled 是否禁用
  117. */
  118. showAnswer(isJudgingRightWrong, isShowRightAnswer, userAnswer, disabled) {
  119. this.isJudgingRightWrong = isJudgingRightWrong;
  120. this.isShowRightAnswer = isShowRightAnswer;
  121. if (userAnswer) this.answer = userAnswer;
  122. if (this.question_list.length === this.answer.question_list.length) {
  123. return this.fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled);
  124. }
  125. this.$watch('question_list', (val) => {
  126. if (val.length !== this.answer.question_list.length) return;
  127. this.fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled);
  128. });
  129. },
  130. fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled) {
  131. this.answer.question_list.forEach(({ id, answer_list }) => {
  132. const index = this.question_list.findIndex((item) => item.id === id);
  133. if (index !== -1) {
  134. this.$refs.preview[index].showAnswer(isJudgingRightWrong, isShowRightAnswer, { answer_list }, disabled);
  135. }
  136. });
  137. },
  138. },
  139. };
  140. </script>
  141. <style lang="scss" scoped>
  142. @use '@/styles/mixin.scss' as *;
  143. .read-preview {
  144. @include preview;
  145. .container {
  146. display: flex;
  147. flex-direction: column;
  148. column-gap: 24px;
  149. .articel {
  150. padding: 24px 40px;
  151. color: #2f3742;
  152. background-color: $content-color;
  153. border-radius: 16px;
  154. }
  155. .question-list {
  156. display: flex;
  157. flex: 1;
  158. flex-direction: column;
  159. row-gap: 24px;
  160. .preview {
  161. min-height: 0;
  162. padding: 0;
  163. margin: 0;
  164. }
  165. }
  166. }
  167. }
  168. </style>