ReadPreview.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 rich-text"
  11. v-html="sanitizeHTML(data.description)"
  12. ></div>
  13. <div class="container">
  14. <div class="articel rich-text" 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. /**
  107. * 改变答案
  108. * @param {number} i 序号
  109. * @param {string} type 类型
  110. * @param {object} param2
  111. * @param {array} param2.answer_list 答案列表
  112. */
  113. changeAnswer(i, type, { answer_list }) {
  114. if (this.disabled) return;
  115. this.answer.question_list[i].answer_list = answer_list;
  116. this.answer.question_list[i].type = type;
  117. },
  118. /**
  119. * 显示答案
  120. * @param {boolean} isJudgingRightWrong 是否判断对错
  121. * @param {boolean} isShowRightAnswer 是否显示正确答案
  122. * @param {Object} userAnswer 用户答案
  123. * @param {boolean} disabled 是否禁用
  124. */
  125. showAnswer(isJudgingRightWrong, isShowRightAnswer, userAnswer, disabled) {
  126. this.isJudgingRightWrong = isJudgingRightWrong;
  127. this.isShowRightAnswer = isShowRightAnswer;
  128. if (userAnswer) this.answer = userAnswer;
  129. if (this.question_list.length === this.answer.question_list.length) {
  130. return this.fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled);
  131. }
  132. this.$watch('question_list', (val) => {
  133. if (val.length !== this.answer.question_list.length) return;
  134. this.fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled);
  135. });
  136. },
  137. /**
  138. * 填充答案
  139. * @param {boolean} isJudgingRightWrong 是否判断对错
  140. * @param {boolean} isShowRightAnswer 是否显示正确答案
  141. * @param {boolean} disabled 是否禁用
  142. */
  143. fillAnswer(isJudgingRightWrong, isShowRightAnswer, disabled) {
  144. this.answer.question_list.forEach(({ id, answer_list }) => {
  145. const index = this.question_list.findIndex((item) => item.id === id);
  146. if (index !== -1) {
  147. this.$refs.preview[index].showAnswer(isJudgingRightWrong, isShowRightAnswer, { answer_list }, disabled);
  148. }
  149. });
  150. },
  151. },
  152. };
  153. </script>
  154. <style lang="scss" scoped>
  155. @use '@/styles/mixin.scss' as *;
  156. .read-preview {
  157. @include preview;
  158. .container {
  159. display: flex;
  160. flex-direction: column;
  161. column-gap: 24px;
  162. .articel {
  163. padding: 24px 40px;
  164. color: #2f3742;
  165. background-color: $content-color;
  166. border-radius: 16px;
  167. }
  168. .question-list {
  169. display: flex;
  170. flex: 1;
  171. flex-direction: column;
  172. row-gap: 24px;
  173. .preview {
  174. min-height: 0;
  175. padding: 0;
  176. margin: 0;
  177. }
  178. }
  179. }
  180. }
  181. </style>