TableFillPreview.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 } in data.option_header_list"
  20. :key="mark"
  21. :style="{
  22. width: `${width}%`,
  23. paddingLeft: isEnable(data.property.is_enable_number_column) ? '40px' : '0',
  24. }"
  25. class="header-item"
  26. >
  27. <span>{{ text }}</span>
  28. </div>
  29. </div>
  30. <div v-for="(item, i) in optionList" :key="i" class="form-content">
  31. <div
  32. v-for="({ mark, content_list }, j) in item"
  33. :key="mark"
  34. :style="{ width: `${data.option_header_list[j].width}%` }"
  35. class="form-item"
  36. >
  37. <span v-if="j === 0 && isEnable(data.property.is_enable_number_column)" class="serial-number">
  38. {{ i + 1 }}
  39. </span>
  40. <div v-for="(li, k) in content_list" :key="k" class="item-content">
  41. <span v-if="li.type === 'text'" v-html="sanitizeHTML(li.content)"></span>
  42. <template v-else-if="li.type === 'input_any'">
  43. <el-input
  44. :key="k"
  45. v-model="li.content"
  46. :disabled="disabled"
  47. :class="['fill']"
  48. placeholder="请输入"
  49. :style="[
  50. { cursor: disabled ? 'not-allowed' : 'pointer' },
  51. { width: Math.max(80, li.content.length * 16) + 'px' },
  52. ]"
  53. />
  54. </template>
  55. <template v-else-if="li.type === 'fill'">
  56. <el-input
  57. :key="k"
  58. v-model="li.content"
  59. :disabled="disabled"
  60. :class="['fill', ...computedAnswerClass(mark, li.mark)]"
  61. placeholder="请输入"
  62. :style="[
  63. { cursor: disabled ? 'not-allowed' : 'pointer' },
  64. { width: Math.max(80, li.content.length * 16) + 'px' },
  65. ]"
  66. />
  67. <span
  68. v-show="computedAnswerText(mark, li.mark).length > 0"
  69. :key="`answer-${i}-${j}-${k}`"
  70. class="right-answer-content"
  71. >
  72. {{ computedAnswerText(mark, li.mark) }}
  73. </span>
  74. </template>
  75. <template v-else-if="li.type === 'input'">
  76. <el-input
  77. :key="k"
  78. v-model="li.content"
  79. :disabled="disabled"
  80. :class="['input', ...computedAnswerClass(mark, li.mark)]"
  81. :style="[
  82. { cursor: disabled ? 'not-allowed' : 'pointer' },
  83. { width: Math.max(80, li.content.length * 16) + 'px' },
  84. ]"
  85. />
  86. <span
  87. v-show="computedAnswerText(mark, li.mark).length > 0"
  88. :key="`answer-${i}-${j}-${k}`"
  89. class="right-answer-fill"
  90. >
  91. {{ computedAnswerText(mark, li.mark) }}
  92. </span>
  93. </template>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. </template>
  101. <script>
  102. import PreviewMixin from './components/PreviewMixin';
  103. export default {
  104. name: 'TableFillPreview',
  105. mixins: [PreviewMixin],
  106. data() {
  107. return {
  108. optionList: [],
  109. };
  110. },
  111. watch: {
  112. 'data.option_list': {
  113. handler(val) {
  114. this.optionList = JSON.parse(JSON.stringify(val));
  115. },
  116. immediate: true,
  117. deep: true,
  118. },
  119. optionList: {
  120. handler(val) {
  121. val.forEach((item) => {
  122. item.forEach(({ mark, content_list }) => {
  123. let value_list = [];
  124. let answerIndex = this.answer.answer_list.findIndex((item) => item.mark === mark);
  125. content_list.forEach((li) => {
  126. if (['fill', 'input', 'input_any'].includes(li.type) && li.content.length > 0) {
  127. value_list.push({
  128. mark: li.mark,
  129. value: li.content,
  130. });
  131. }
  132. });
  133. if (answerIndex === -1 && value_list.length <= 0) return;
  134. if (answerIndex !== -1 && value_list.length <= 0) {
  135. this.answer.answer_list.splice(answerIndex, 1);
  136. return;
  137. }
  138. if (answerIndex !== -1) {
  139. this.answer.answer_list[answerIndex].value_list = value_list;
  140. return;
  141. }
  142. this.answer.answer_list.push({
  143. mark,
  144. value_list,
  145. });
  146. });
  147. });
  148. },
  149. deep: true,
  150. },
  151. isJudgingRightWrong(val) {
  152. if (!val) return;
  153. this.answer.answer_list.forEach(({ mark, value_list }) => {
  154. this.optionList
  155. .find((item) => item.find((li) => li.mark === mark))
  156. .forEach((item) => {
  157. item.content_list.forEach((li) => {
  158. if (['fill', 'input', 'input_any'].includes(li.type)) {
  159. let answer = value_list.find((item) => item.mark === li.mark);
  160. if (!answer) return;
  161. li.content = answer.value;
  162. }
  163. });
  164. });
  165. });
  166. },
  167. },
  168. methods: {
  169. /**
  170. * 计算答题对错选项字体颜色
  171. * @param {string} mark 选项标识
  172. * @param {string} liMark 选项子标识
  173. */
  174. computedAnswerClass(mark, liMark) {
  175. if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
  176. return '';
  177. }
  178. let selectOption = this.answer.answer_list
  179. .find((item) => item.mark === mark)
  180. ?.value_list.find((item) => item.mark === liMark);
  181. let answerOption = this.data.answer.answer_list
  182. .find((item) => item.mark === mark)
  183. ?.value_list.find((item) => item.mark === liMark);
  184. if (!selectOption) return '';
  185. let selectValue = selectOption.value;
  186. let answerValue = answerOption.value;
  187. let classList = [];
  188. let isRight = selectValue === answerValue;
  189. if (this.isJudgingRightWrong) {
  190. isRight ? classList.push('right') : classList.push('wrong');
  191. }
  192. if (this.isShowRightAnswer && !isRight) {
  193. classList.push('show-right-answer');
  194. }
  195. return classList;
  196. },
  197. /**
  198. * 计算正确答案
  199. * @param {string} mark 选项标识
  200. * @param {string} liMark 选项子标识
  201. */
  202. computedAnswerText(mark, liMark) {
  203. if (!this.isShowRightAnswer) return '';
  204. // mark 对应答题选项
  205. let selectOption = this.answer.answer_list
  206. .find((item) => item.mark === mark)
  207. ?.value_list.find((item) => item.mark === liMark);
  208. // mark 对应正确答案选项
  209. let answerOption = this.data.answer.answer_list
  210. .find((item) => item.mark === mark)
  211. ?.value_list.find((item) => item.mark === liMark);
  212. if (!selectOption) return '';
  213. let selectValue = selectOption.value;
  214. let answerValue = answerOption.value;
  215. let isRight = selectValue === answerValue;
  216. if (isRight) return '';
  217. return `(${answerValue})`;
  218. },
  219. },
  220. };
  221. </script>
  222. <style lang="scss" scoped>
  223. @use '@/styles/mixin.scss' as *;
  224. $table-border: 1px solid #ebebeb;
  225. .fill-form-preview {
  226. @include preview;
  227. .form-wrapper {
  228. overflow: auto;
  229. .form {
  230. &-header {
  231. display: flex;
  232. font-weight: bold;
  233. color: $main-color;
  234. background-color: #eaeffb;
  235. .header-item {
  236. display: flex;
  237. align-items: center;
  238. min-height: 40px;
  239. padding: 8px 12px;
  240. border-left: $table-border;
  241. }
  242. }
  243. .form-content:first-child {
  244. .form-item {
  245. border-top: $table-border;
  246. }
  247. }
  248. &-content {
  249. display: flex;
  250. .form-item {
  251. display: flex;
  252. align-items: center;
  253. min-height: 48px;
  254. border-bottom: $table-border;
  255. border-left: $table-border;
  256. .item-content {
  257. padding: 8px 12px;
  258. }
  259. %input,
  260. .el-input.fill {
  261. display: inline-flex;
  262. align-items: center;
  263. width: 120px;
  264. margin: 0 2px;
  265. &.right {
  266. :deep input.el-input__inner {
  267. color: $right-color;
  268. }
  269. }
  270. &.wrong {
  271. :deep input.el-input__inner {
  272. color: $error-color;
  273. }
  274. }
  275. & + .right-answer-fill {
  276. position: relative;
  277. left: -4px;
  278. display: inline-block;
  279. height: 32px;
  280. line-height: 28px;
  281. vertical-align: bottom;
  282. border-bottom: 1px solid $font-color;
  283. }
  284. & + .right-answer-content {
  285. position: relative;
  286. left: -4px;
  287. display: inline-block;
  288. height: 32px;
  289. line-height: 28px;
  290. vertical-align: bottom;
  291. }
  292. :deep input.el-input__inner {
  293. padding: 0;
  294. font-size: 16px;
  295. color: $font-color;
  296. text-align: left;
  297. background-color: #fff;
  298. border-width: 0;
  299. border-radius: 0;
  300. }
  301. }
  302. .input {
  303. @extend %input;
  304. :deep input.el-input__inner {
  305. text-align: center;
  306. border-bottom: 1px solid $font-color;
  307. }
  308. }
  309. }
  310. .form-item:last-child {
  311. border-right: $table-border;
  312. }
  313. }
  314. .serial-number {
  315. display: flex;
  316. align-items: center;
  317. height: 100%;
  318. padding: 8px 12px;
  319. border-right: $table-border;
  320. }
  321. }
  322. }
  323. }
  324. </style>