SelectPreview.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <!-- eslint-disable vue/no-v-html -->
  2. <template>
  3. <div class="select-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. <!-- 选项细分 -->
  16. <div v-if="isEnable(data.property.is_option_subdivision)" class="option-subdivision">
  17. <ul v-for="(item, i) in data.option_list" :key="item.mark" class="option-subdivision-list">
  18. <span class="serial-number" :style="{ fontSize: data.property.option_question_number_font_size }">
  19. {{
  20. isEnable(isEnableManualModify)
  21. ? item.custom_number
  22. ? `${item.custom_number}.`
  23. : ''
  24. : computeOptionMethods[data.option_number_show_mode](i)
  25. }}
  26. </span>
  27. <li
  28. v-for="{ content, mark } in item.data_list"
  29. :key="mark"
  30. :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
  31. :class="['option-item', { active: isAnswer(mark, item.mark) }, ...computedAnswerClass(mark, item.mark)]"
  32. @click="selectAnswer(mark, item.mark)"
  33. >
  34. <span v-if="data.property.select_type === selectTypeList[0].value" class="selectionbox"></span>
  35. <span v-else class="checkbox">
  36. <SvgIcon icon-class="check-mark" width="10" height="7" />
  37. </span>
  38. <span class="content rich-text" v-html="sanitizeHTML(content)"></span>
  39. </li>
  40. </ul>
  41. </div>
  42. <ul v-else class="option-list">
  43. <li
  44. v-for="({ content, mark, custom_number }, i) in data.option_list"
  45. :key="mark"
  46. :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
  47. :class="['option-item', { active: isAnswer(mark) }, ...computedAnswerClass(mark)]"
  48. @click="selectAnswer(mark)"
  49. >
  50. {{ selectTypeList[0].type }}
  51. <span v-if="data.property.select_type === selectTypeList[0].value" class="selectionbox"></span>
  52. <span v-else class="checkbox">
  53. <SvgIcon icon-class="check-mark" width="10" height="7" />
  54. </span>
  55. <span class="serial-number" :style="{ fontSize: data.property.option_question_number_font_size }">
  56. {{
  57. isEnable(isEnableManualModify)
  58. ? custom_number
  59. ? `${custom_number}.`
  60. : ''
  61. : computeOptionMethods[data.option_number_show_mode](i)
  62. }}
  63. </span>
  64. <span class="content rich-text" v-html="sanitizeHTML(content)"></span>
  65. </li>
  66. </ul>
  67. <div v-if="isEnable(data.property.is_enable_analysis) && isShowRightAnswer" class="analysis">
  68. <span class="analysis-title">解析</span>
  69. <div class="analysis-content" v-html="sanitizeHTML(data.analysis)"></div>
  70. </div>
  71. </div>
  72. </template>
  73. <script>
  74. import { computeOptionMethods, isEnable, selectTypeList } from '@/views/exercise_questions/data/common';
  75. import PreviewMixin from './components/PreviewMixin';
  76. export default {
  77. name: 'SelectPreview',
  78. mixins: [PreviewMixin],
  79. props: {
  80. isEnableManualModify: {
  81. type: String,
  82. default: 'false',
  83. },
  84. },
  85. data() {
  86. return {
  87. selectTypeList,
  88. computeOptionMethods,
  89. };
  90. },
  91. computed: {
  92. isSubdivision() {
  93. return isEnable(this.data.property.is_option_subdivision);
  94. },
  95. },
  96. methods: {
  97. /**
  98. * 判断是否为选中答案
  99. * @param {string} mark 标记
  100. * @param {string} parent_mark 父级标记
  101. */
  102. isAnswer(mark, parent_mark) {
  103. if (isEnable(this.data.property.is_option_subdivision)) {
  104. return this.answer.answer_list.find((item) => item.mark === parent_mark)?.value_list?.includes(mark);
  105. }
  106. return this.answer.answer_list.includes(mark);
  107. },
  108. /**
  109. * 选择答案
  110. * @param {string} mark 标记
  111. * @param {string} parent_mark 父级标记
  112. */
  113. selectAnswer(mark, parent_mark) {
  114. if (this.disabled) return;
  115. let answer_list = this.answer.answer_list;
  116. let index = this.isSubdivision
  117. ? answer_list.findIndex((item) => item.mark === parent_mark)
  118. : answer_list.indexOf(mark);
  119. if (this.data.property.select_type === selectTypeList[0].value) {
  120. if (this.isSubdivision) {
  121. if (index === -1) {
  122. answer_list.push({ mark: parent_mark, value_list: [mark] });
  123. } else {
  124. this.$set(this.answer.answer_list[index], 'value_list', [mark]);
  125. }
  126. } else {
  127. this.$set(this.answer, 'answer_list', [mark]);
  128. }
  129. }
  130. if (this.data.property.select_type === selectTypeList[1].value) {
  131. if (this.isSubdivision) {
  132. if (index === -1) {
  133. answer_list.push({ mark: parent_mark, value_list: [mark] });
  134. } else {
  135. let mIndex = answer_list[index].value_list.findIndex((item) => item === mark);
  136. if (mIndex === -1) {
  137. answer_list[index].value_list.push(mark);
  138. } else {
  139. answer_list[index].value_list.splice(mIndex, 1);
  140. }
  141. }
  142. return;
  143. }
  144. if (index === -1) {
  145. answer_list.push(mark);
  146. } else {
  147. answer_list.splice(index, 1);
  148. }
  149. }
  150. },
  151. /**
  152. * 计算答题对错选项class样式
  153. * @param {string} mark 选项标识
  154. * @param {string} parent_mark 选项父级标识
  155. */
  156. computedAnswerClass(mark, parent_mark) {
  157. if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
  158. return [];
  159. }
  160. let answer_list = this.answer.answer_list;
  161. let isHas = this.isSubdivision
  162. ? answer_list.find((item) => item.mark === parent_mark)?.value_list?.includes(mark)
  163. : answer_list.includes(mark); // 是否已选中的选项
  164. let isRight = this.isSubdivision
  165. ? this.data.answer.answer_list.find((item) => item.mark === parent_mark)?.value_list?.includes(mark)
  166. : this.data.answer.answer_list.includes(mark); // 是否是正确答案
  167. if (!isHas && this.isShowRightAnswer) {
  168. return isRight ? ['answer-right'] : [];
  169. }
  170. // 判断是否是正确答案
  171. if (this.isJudgingRightWrong) {
  172. return isRight ? ['right'] : ['wrong'];
  173. }
  174. return [];
  175. },
  176. },
  177. };
  178. </script>
  179. <style lang="scss" scoped>
  180. @use '@/styles/mixin.scss' as *;
  181. .select-preview {
  182. @include preview;
  183. %option-list,
  184. .option-list {
  185. display: flex;
  186. flex-direction: column;
  187. row-gap: 16px;
  188. .option-item {
  189. display: flex;
  190. column-gap: 8px;
  191. align-items: center;
  192. padding: 12px 24px;
  193. color: #706f78;
  194. cursor: pointer;
  195. background-color: $content-color;
  196. border-radius: 40px;
  197. .selectionbox {
  198. width: 14px;
  199. min-width: 14px;
  200. height: 14px;
  201. min-height: 14px;
  202. margin-right: 8px;
  203. border: 2px solid #dcdbdd;
  204. border-radius: 50%;
  205. }
  206. .checkbox {
  207. display: flex;
  208. align-items: center;
  209. justify-content: center;
  210. width: 14px;
  211. height: 14px;
  212. margin-right: 8px;
  213. cursor: pointer;
  214. background-color: #fff;
  215. border: 1px solid #dcdfe6;
  216. border-radius: 2px;
  217. .svg-icon {
  218. display: none;
  219. }
  220. }
  221. .serial-number {
  222. font-size: 16pt;
  223. }
  224. .content {
  225. flex: 1;
  226. }
  227. &.answer-right {
  228. background-color: #e8f7f2;
  229. &::after {
  230. font-size: 14px;
  231. color: $right-color;
  232. content: '正确答案';
  233. }
  234. }
  235. &.active {
  236. color: #34343a;
  237. background-color: $main-active-color;
  238. .selectionbox {
  239. border-color: $light-main-color;
  240. border-width: 4px;
  241. }
  242. .checkbox {
  243. color: #fff;
  244. background-color: $light-main-color;
  245. .svg-icon {
  246. display: block;
  247. }
  248. }
  249. &.right {
  250. background-color: $content-color;
  251. border: 1px solid $right-color;
  252. &::after {
  253. font-size: 14px;
  254. color: $right-color;
  255. content: '已选';
  256. }
  257. }
  258. &.wrong {
  259. background-color: $content-color;
  260. box-shadow: 0 0 0 1px $error-color;
  261. &::after {
  262. font-size: 14px;
  263. color: #a09fa6;
  264. content: '已选';
  265. }
  266. }
  267. }
  268. }
  269. }
  270. .option-subdivision {
  271. display: flex;
  272. flex-direction: column;
  273. row-gap: 24px;
  274. &-list {
  275. @extend %option-list;
  276. .serial-number {
  277. font-size: 16pt;
  278. }
  279. flex-direction: row;
  280. column-gap: 24px;
  281. align-items: center;
  282. .option-item {
  283. flex: 1;
  284. }
  285. :deep p {
  286. margin: 0;
  287. }
  288. }
  289. }
  290. }
  291. </style>