TableFillPreview.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="fill-form-preview">
  4. <div class="stem">
  5. <span class="question-number" :style="{ fontSize: data.property.stem_question_number_font_size }">
  6. {{ questionNumberEndIsBracket(data.property.question_number) }}
  7. </span>
  8. <span v-html="sanitizeHTML(data.stem)"></span>
  9. </div>
  10. <div
  11. v-if="isEnable(data.property.is_enable_description)"
  12. class="description rich-text"
  13. v-html="sanitizeHTML(data.description)"
  14. ></div>
  15. <div class="form-wrapper">
  16. <div class="form" :style="{ width: `${data.property.form_width}px` }">
  17. <div v-if="isEnable(data.property.is_enable_table_header)" class="form-header">
  18. <div
  19. v-for="({ text, mark, width }, i) in data.option_header_list"
  20. :key="mark"
  21. :style="{
  22. borderLeft: isEnable(data.property.is_enable_number_column) || i > 0 ? '1px solid #e0e0e0' : 'none',
  23. width:
  24. isEnable(data.property.is_enable_number_column) && i === 0 ? `calc(${width}% - 39.5px)` : `${width}%`,
  25. marginLeft: isEnable(data.property.is_enable_number_column) && i === 0 ? '39.5px' : '0',
  26. }"
  27. class="header-item"
  28. >
  29. <span>{{ text }}</span>
  30. </div>
  31. </div>
  32. <div v-for="(item, i) in optionList" :key="i" class="form-content">
  33. <div
  34. v-for="(li, j) in item"
  35. :key="li.mark"
  36. :style="{ width: `${data.option_header_list[j].width}%` }"
  37. class="form-item"
  38. >
  39. <span v-if="j === 0 && isEnable(data.property.is_enable_number_column)" class="serial-number">
  40. {{ i + 1 }}
  41. </span>
  42. <div class="item-content" :style="{ paddingBottom: li.type === 'input' ? '0' : '8px' }">
  43. <span v-if="li.type === 'text'">{{ li.text }}</span>
  44. <template v-else-if="li.type === 'input'">
  45. <el-input
  46. v-model="li.text"
  47. :disabled="disabled"
  48. type="textarea"
  49. class="fill"
  50. resize="none"
  51. placeholder="请输入"
  52. :style="[{ cursor: disabled ? 'not-allowed' : 'pointer' }]"
  53. @blur="handleTone(li.text, i, j)"
  54. />
  55. </template>
  56. </div>
  57. </div>
  58. </div>
  59. </div>
  60. </div>
  61. <div v-if="isEnable(data.property.is_enable_reference_answer) && isShowRightAnswer" class="reference-box">
  62. <h5 class="reference-title">参考答案</h5>
  63. <div class="form-wrapper">
  64. <div class="form" :style="{ width: `${data.property.form_width}px`, backgroundColor: '#fff' }">
  65. <div v-if="isEnable(data.property.is_enable_table_header)" class="form-header">
  66. <div
  67. v-for="({ text, mark, width }, i) in data.reference_answer_option_header_list"
  68. :key="mark"
  69. :style="{
  70. borderLeft: isEnable(data.property.is_enable_number_column) || i > 0 ? '1px solid #e0e0e0' : 'none',
  71. width:
  72. isEnable(data.property.is_enable_number_column) && i === 0 ? `calc(${width}% - 39.5px)` : `${width}%`,
  73. marginLeft: isEnable(data.property.is_enable_number_column) && i === 0 ? '39.5px' : '0',
  74. }"
  75. class="header-item"
  76. >
  77. <span>{{ text }}</span>
  78. </div>
  79. </div>
  80. <div v-for="(item, i) in data.reference_answer_option_list" :key="i" class="form-content">
  81. <div
  82. v-for="(li, j) in item"
  83. :key="li.mark"
  84. :style="{ width: `${data.reference_answer_option_header_list[j].width}%` }"
  85. class="form-item"
  86. >
  87. <span v-if="j === 0 && isEnable(data.property.is_enable_number_column)" class="serial-number">
  88. {{ i + 1 }}
  89. </span>
  90. <div class="item-content">
  91. <span>{{ li.text }}</span>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. <div v-if="isEnable(data.property.is_enable_analysis) && isShowRightAnswer" class="analysis">
  99. <span class="analysis-title">解析</span>
  100. <div class="analysis-content" v-html="sanitizeHTML(data.analysis)"></div>
  101. </div>
  102. </div>
  103. </template>
  104. <script>
  105. import PreviewMixin from './components/PreviewMixin';
  106. import { addTone, handleToneValue } from '@/views/exercise_questions/data/common';
  107. export default {
  108. name: 'TableFillPreview',
  109. mixins: [PreviewMixin],
  110. data() {
  111. return {
  112. optionList: [],
  113. };
  114. },
  115. watch: {
  116. 'data.option_list': {
  117. handler(val) {
  118. if (!val) return;
  119. this.optionList = JSON.parse(JSON.stringify(val));
  120. },
  121. immediate: true,
  122. deep: true,
  123. },
  124. optionList: {
  125. handler(val) {
  126. if (!val) return;
  127. this.optionList.forEach((item) => {
  128. item.forEach((li) => {
  129. if (['input'].includes(li.type)) {
  130. const findIndex = this.answer.answer_list.findIndex(({ mark }) => mark === li.mark);
  131. if (findIndex === -1 && li.text.length <= 0) return;
  132. if (findIndex !== -1 && li.text.length <= 0) {
  133. this.answer.answer_list.splice(findIndex, 1);
  134. return;
  135. }
  136. if (findIndex === -1) {
  137. this.answer.answer_list.push({
  138. mark: li.mark,
  139. value: li.text,
  140. });
  141. return;
  142. }
  143. if (findIndex !== -1) {
  144. this.answer.answer_list[findIndex].value = li.text;
  145. }
  146. }
  147. });
  148. });
  149. },
  150. immediate: true,
  151. deep: true,
  152. },
  153. isJudgingRightWrong(val) {
  154. if (!val) return;
  155. this.answer.answer_list.forEach(({ mark, value }) => {
  156. const parentIndex = this.optionList.findIndex((item) => item.find((li) => li.mark === mark));
  157. if (parentIndex === -1) return;
  158. const childIndex = this.optionList[parentIndex].findIndex((item) => item.mark === mark);
  159. this.optionList[parentIndex][childIndex].text = value;
  160. });
  161. },
  162. },
  163. methods: {
  164. handleTone(value, i, j) {
  165. this.optionList[i][j].text = value
  166. .trim()
  167. .split(/\s+/)
  168. .map((item) => {
  169. return handleToneValue(item);
  170. })
  171. .map((item) =>
  172. item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
  173. )
  174. .filter((item) => item.length > 0)
  175. .join(' ');
  176. },
  177. },
  178. };
  179. </script>
  180. <style lang="scss" scoped>
  181. @use '@/styles/mixin.scss' as *;
  182. $table-border: 1px solid #e0e0e0;
  183. .fill-form-preview {
  184. @include preview;
  185. .form-wrapper {
  186. overflow: auto;
  187. .form {
  188. border: $table-border;
  189. &-header {
  190. display: flex;
  191. font-weight: bold;
  192. color: $main-color;
  193. background-color: #eaeffb;
  194. .header-item {
  195. min-height: 40px;
  196. padding: 8px 12px;
  197. font-size: 16px;
  198. text-align: center;
  199. }
  200. }
  201. &-header + .form-content {
  202. border-top: $table-border;
  203. }
  204. &-content:not(:last-child) {
  205. border-bottom: $table-border;
  206. }
  207. &-content {
  208. display: flex;
  209. .form-item:not(:first-child) {
  210. border-left: $table-border;
  211. }
  212. .form-item {
  213. display: flex;
  214. align-items: center;
  215. min-height: 48px;
  216. overflow: auto;
  217. .item-content {
  218. flex: 1;
  219. padding: 8px 12px;
  220. }
  221. .el-textarea.fill {
  222. display: inline-flex;
  223. align-items: center;
  224. margin: 0 2px;
  225. :deep .el-textarea__inner {
  226. padding: 0;
  227. padding-top: 14px;
  228. font-size: 16px;
  229. color: $font-color;
  230. text-align: left;
  231. background-color: #fff;
  232. border-width: 0;
  233. border-radius: 0;
  234. }
  235. }
  236. }
  237. }
  238. .serial-number {
  239. display: flex;
  240. align-items: center;
  241. width: 40px;
  242. min-width: 40px;
  243. max-width: 40px;
  244. height: 100%;
  245. padding: 8px 12px;
  246. border-right: $table-border;
  247. }
  248. }
  249. }
  250. }
  251. </style>