FillPreview.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="select-preview" :style="getAreaStyle()">
  4. <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
  5. <div class="main" :style="getMainStyle()">
  6. <AudioFill :file-id="data.audio_file_id" />
  7. <div class="fill-wrapper">
  8. <p v-for="(item, i) in data.model_essay" :key="i">
  9. <template v-for="(li, j) in item">
  10. <span v-if="li.type === 'text'" :key="j" v-html="sanitizeHTML(li.content)"></span>
  11. <template v-if="li.type === 'input'">
  12. <el-input
  13. :key="j"
  14. v-model="li.content"
  15. :disabled="disabled"
  16. :class="[data.property.fill_font, ...computedAnswerClass(li.mark)]"
  17. :style="[{ width: Math.max(80, li.content.length * 21.3) + 'px' }]"
  18. />
  19. <span v-show="computedAnswerText(li.mark).length > 0" :key="`answer-${j}`" class="right-answer">
  20. {{ computedAnswerText(li.mark) }}
  21. </span>
  22. </template>
  23. </template>
  24. </p>
  25. </div>
  26. <SoundRecord
  27. v-if="isEnable(data.property.is_enable_voice_answer)"
  28. ref="record"
  29. type="normal"
  30. class="record-box"
  31. :answer-record-list="data.record_list"
  32. :task-model="isJudgingRightWrong ? 'ANSWER' : ''"
  33. @handleWav="handleWav"
  34. />
  35. </div>
  36. </div>
  37. </template>
  38. <script>
  39. import { getFillData, fillFontList, arrangeTypeList, audioPositionList } from '@/views/book/courseware/data/fill';
  40. import PreviewMixin from '../common/PreviewMixin';
  41. import AudioFill from './components/AudioFillPlay.vue';
  42. import SoundRecord from '../../common/SoundRecord.vue';
  43. export default {
  44. name: 'FillPreview',
  45. components: {
  46. AudioFill,
  47. SoundRecord,
  48. },
  49. mixins: [PreviewMixin],
  50. data() {
  51. return {
  52. data: getFillData(),
  53. };
  54. },
  55. computed: {
  56. fontFamily() {
  57. return fillFontList.find(({ value }) => this.data.property.fill_font === value).font;
  58. },
  59. },
  60. watch: {
  61. 'data.model_essay': {
  62. handler(list) {
  63. this.answer.answer_list = list
  64. .map((item) => {
  65. return item
  66. .map(({ type, content, mark }) => {
  67. if (type === 'input') {
  68. return {
  69. value: content,
  70. mark,
  71. };
  72. }
  73. })
  74. .filter((item) => item);
  75. })
  76. .flat();
  77. },
  78. deep: true,
  79. },
  80. isJudgingRightWrong(val) {
  81. if (!val) return;
  82. this.answer.answer_list.forEach(({ mark, value }) => {
  83. this.data.model_essay.forEach((item) => {
  84. item.forEach((li) => {
  85. if (li.mark === mark) {
  86. li.content = value;
  87. }
  88. });
  89. });
  90. });
  91. this.handleWav(this.answer.record_list);
  92. },
  93. 'data.record_list'(val) {
  94. this.answer.record_list = val;
  95. },
  96. },
  97. created() {
  98. this.answer.answer_list = this.data.model_essay
  99. .map((item) => {
  100. return item
  101. .map(({ type, content, mark }) => {
  102. if (type === 'input') {
  103. return {
  104. value: content,
  105. mark,
  106. };
  107. }
  108. })
  109. .filter((item) => item);
  110. })
  111. .flat();
  112. },
  113. methods: {
  114. handleWav(data) {
  115. this.data.record_list = data;
  116. },
  117. getMainStyle() {
  118. const isRow = this.data.property.arrange_type === arrangeTypeList[0].value;
  119. const isFront = this.data.property.audio_position === audioPositionList[0].value;
  120. const isEnableVoice = this.data.property.is_enable_voice_answer === 'true';
  121. let _list = [
  122. { name: 'audio', value: '24px' },
  123. { name: 'fill', value: '1fr' },
  124. ];
  125. if (!isFront) {
  126. _list = _list.reverse();
  127. }
  128. let grid = isRow
  129. ? `"${_list[0].name} ${_list[1].name}${isEnableVoice ? ' record' : ''}" auto / ${_list[0].value} ${_list[1].value}${isEnableVoice ? ' 160px' : ''}`
  130. : `"${_list[0].name}" ${_list[0].value} "${_list[1].name}" ${_list[1].value}${isEnableVoice ? `" record" 32px ` : ''} / 1fr`;
  131. let style = {
  132. 'grid-auto-flow': isRow ? 'column' : 'row',
  133. 'column-gap': isRow ? '16px' : undefined,
  134. 'row-gap': isRow ? undefined : '8px',
  135. grid,
  136. };
  137. return style;
  138. },
  139. /**
  140. * 计算答题对错选项字体颜色
  141. * @param {string} mark 选项标识
  142. */
  143. computedAnswerClass(mark) {
  144. if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
  145. return '';
  146. }
  147. let selectOption = this.answer.answer_list.find((item) => item.mark === mark);
  148. let answerOption = this.data.answer.answer_list.find((item) => item.mark === mark);
  149. if (!selectOption) return '';
  150. let selectValue = selectOption.value;
  151. let answerValue = answerOption.value;
  152. let answerType = answerOption.type;
  153. let classList = [];
  154. let isRight =
  155. answerType === 'only_one' ? selectValue === answerValue : answerValue.split('/').includes(selectValue);
  156. if (this.isJudgingRightWrong) {
  157. isRight ? classList.push('right') : classList.push('wrong');
  158. }
  159. if (this.isShowRightAnswer && !isRight) {
  160. classList.push('show-right-answer');
  161. }
  162. return classList;
  163. },
  164. /**
  165. * 计算正确答案文本
  166. * @param {string} mark 选项标识
  167. */
  168. computedAnswerText(mark) {
  169. if (!this.isShowRightAnswer) return '';
  170. let selectOption = this.answer.answer_list.find((item) => item.mark === mark);
  171. let answerOption = this.data.answer.answer_list.find((item) => item.mark === mark);
  172. if (!selectOption) return '';
  173. let selectValue = selectOption.value;
  174. let answerValue = answerOption.value;
  175. let isRight = selectValue === answerValue;
  176. if (isRight) return '';
  177. return `(${answerValue})`;
  178. },
  179. },
  180. };
  181. </script>
  182. <style lang="scss" scoped>
  183. @use '@/styles/mixin.scss' as *;
  184. .select-preview {
  185. @include preview-base;
  186. .main {
  187. display: grid;
  188. align-items: center;
  189. }
  190. .fill-wrapper {
  191. grid-area: fill;
  192. font-size: 16pt;
  193. p {
  194. margin: 0;
  195. }
  196. .el-input {
  197. display: inline-flex;
  198. align-items: center;
  199. width: 120px;
  200. margin: 0 2px;
  201. &.pinyin :deep input.el-input__inner {
  202. font-family: 'PINYIN-B', sans-serif;
  203. }
  204. &.chinese :deep input.el-input__inner {
  205. font-family: 'arial', sans-serif;
  206. }
  207. &.english :deep input.el-input__inner {
  208. font-family: 'arial', sans-serif;
  209. }
  210. &.right {
  211. :deep input.el-input__inner {
  212. color: $right-color;
  213. }
  214. }
  215. &.wrong {
  216. :deep input.el-input__inner {
  217. color: $error-color;
  218. }
  219. }
  220. & + .right-answer {
  221. position: relative;
  222. left: -4px;
  223. display: inline-block;
  224. height: 32px;
  225. line-height: 28px;
  226. vertical-align: bottom;
  227. border-bottom: 1px solid $font-color;
  228. }
  229. :deep input.el-input__inner {
  230. padding: 0;
  231. font-size: 16pt;
  232. color: $font-color;
  233. text-align: center;
  234. background-color: #fff;
  235. border-width: 0;
  236. border-bottom: 1px solid $font-color;
  237. border-radius: 0;
  238. }
  239. }
  240. }
  241. .record-box {
  242. padding: 6px 12px;
  243. background-color: $fill-color;
  244. :deep .record-time {
  245. width: 100px;
  246. }
  247. }
  248. }
  249. </style>