Judge.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <ModuleBase ref="base" :type="data.type">
  3. <template #content>
  4. <ul class="option-list">
  5. <li v-for="(item, i) in data.option_list" :key="i" class="option-item">
  6. <span class="serial-number">{{ computedOptionNumber(i) }}.</span>
  7. <div class="option-content">
  8. <RichText
  9. ref="richText"
  10. v-model="item.content"
  11. :font-size="data?.unified_attrib?.font_size"
  12. :font-family="data?.unified_attrib?.font"
  13. placeholder="输入内容"
  14. :inline="true"
  15. />
  16. </div>
  17. <div
  18. v-for="option_type in incertitudeList"
  19. :key="option_type"
  20. :class="[
  21. 'option-type-item',
  22. {
  23. active: data.answer.answer_list.find((li) => li.mark === item.mark && li.option_type === option_type),
  24. },
  25. ]"
  26. @click="selectOptionAnswer(option_type, item.mark)"
  27. >
  28. <SvgIcon v-if="option_type === option_type_list[0].value" icon-class="check-mark" width="10" height="7" />
  29. <SvgIcon v-if="option_type === option_type_list[1].value" icon-class="cross" size="8" />
  30. <SvgIcon v-if="option_type === option_type_list[2].value" icon-class="circle" size="10" />
  31. </div>
  32. <span v-if="data.option_list.length > 1" class="delete" @click="deleteOption(i)">
  33. <SvgIcon icon-class="delete-2" width="12" height="12" />
  34. </span>
  35. </li>
  36. </ul>
  37. <div class="add">
  38. <SvgIcon icon-class="add-circle" width="14" height="14" />
  39. <span class="add-button" @click="addOption">增加选项</span>
  40. </div>
  41. <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">拼音效果</el-divider>
  42. <PinyinText
  43. v-for="(item, i) in data.option_list"
  44. v-show="isEnable(data.property.view_pinyin)"
  45. :key="i"
  46. ref="PinyinText"
  47. :paragraph-list="item.paragraph_list"
  48. :pinyin-position="data.property.pinyin_position"
  49. @fillCorrectPinyin="fillCorrectPinyin($event, i)"
  50. />
  51. <AnswerAnalysisList
  52. v-if="data.answer_list?.length > 0 || data.analysis_list?.length > 0"
  53. :answer-list="data.answer_list"
  54. :analysis-list="data.analysis_list"
  55. :unified-attrib="data.unified_attrib"
  56. @updateAnswerAnalysisFileList="updateAnswerAnalysisFileList"
  57. @deleteAnswerAnalysis="deleteAnswerAnalysis"
  58. />
  59. </template>
  60. </ModuleBase>
  61. </template>
  62. <script>
  63. import ModuleMixin from '../../common/ModuleMixin';
  64. import PinyinText from '@/components/PinyinText.vue';
  65. import { getJudgeData, getOption, option_type_list, isEnable } from '@/views/book/courseware/data/judge';
  66. import { serialNumberTypeList, computeOptionMethods } from '@/views/book/courseware/data/common';
  67. export default {
  68. name: 'JudgePage',
  69. components: {
  70. PinyinText,
  71. },
  72. mixins: [ModuleMixin],
  73. data() {
  74. return {
  75. data: getJudgeData(),
  76. option_type_list,
  77. isEnable,
  78. };
  79. },
  80. computed: {
  81. incertitudeList() {
  82. let _option_type_list = this.data.property.option_type_list;
  83. if (isEnable(this.data.property.is_view_incertitude)) {
  84. return _option_type_list;
  85. }
  86. // 返回不包含第三个元素的新数组
  87. return [..._option_type_list.slice(0, 2), ..._option_type_list.slice(3)];
  88. },
  89. },
  90. watch: {
  91. 'data.option_list': 'handleMindMap',
  92. 'data.property': {
  93. handler({ view_pinyin }) {
  94. if (!this.isEnable(view_pinyin)) {
  95. this.data.option_list.forEach((item) => {
  96. item.paragraph_list = [];
  97. item.paragraph_list_parameter = {
  98. text: '',
  99. pinyin_proofread_word_list: [],
  100. };
  101. });
  102. return;
  103. }
  104. if (this.data.option_list.length > 0 && this.data.option_list[0].paragraph_list.length > 0) return;
  105. this.data.option_list.forEach((item, i) => {
  106. const text = item.content.replace(/<[^>]+>/g, '');
  107. if (!text) return;
  108. item.paragraph_list_parameter.text = text;
  109. this.createParsedTextInfoPinyin(text, i);
  110. });
  111. },
  112. deep: true,
  113. },
  114. },
  115. methods: {
  116. computedOptionNumber(number) {
  117. let type = serialNumberTypeList.find((item) => item.value === this.data.property.option_serial_type)?.value;
  118. if (!type) return number + 1;
  119. return computeOptionMethods[type](number);
  120. },
  121. /**
  122. * 选择选项答案
  123. * @param {String} option_type 选项类型
  124. * @param {String} mark 选项标记
  125. */
  126. selectOptionAnswer(option_type, mark) {
  127. const index = this.data.answer.answer_list.findIndex((item) => item.mark === mark);
  128. if (index === -1) {
  129. this.data.answer.answer_list.push({ option_type, mark });
  130. } else {
  131. this.data.answer.answer_list[index].option_type = option_type;
  132. }
  133. },
  134. /**
  135. * @description 添加选项
  136. */
  137. addOption() {
  138. this.data.option_list.push(getOption());
  139. },
  140. /**
  141. * @description 删除选项
  142. * @param {number} index 选项索引
  143. */
  144. deleteOption(index) {
  145. this.data.option_list.splice(index, 1);
  146. },
  147. handleMindMap() {
  148. const optionCount = this.data.option_list.length;
  149. this.data.mind_map.node_list = [
  150. {
  151. name: `${optionCount}选项判断组件`,
  152. },
  153. ];
  154. },
  155. },
  156. };
  157. </script>
  158. <style lang="scss" scoped>
  159. .option-list {
  160. display: flex;
  161. flex-direction: column;
  162. row-gap: 8px;
  163. .option-item {
  164. display: flex;
  165. column-gap: 8px;
  166. align-items: center;
  167. min-height: 36px;
  168. .serial-number {
  169. width: 40px;
  170. height: 36px;
  171. padding: 4px 8px;
  172. color: $text-color;
  173. background-color: $fill-color;
  174. border-radius: 2px;
  175. }
  176. .option-content {
  177. display: flex;
  178. flex: 1;
  179. column-gap: 6px;
  180. align-items: center;
  181. padding: 0 12px;
  182. overflow: hidden;
  183. background-color: $fill-color;
  184. }
  185. .option-type {
  186. display: flex;
  187. column-gap: 8px;
  188. align-items: center;
  189. height: 32px;
  190. padding: 8px 16px;
  191. background-color: $fill-color;
  192. &-item {
  193. display: flex;
  194. align-items: center;
  195. justify-content: center;
  196. width: 16px;
  197. height: 16px;
  198. color: $font-light-color;
  199. cursor: pointer;
  200. background-color: #d7d7d7;
  201. border-radius: 2px;
  202. &.active {
  203. color: #fff;
  204. background-color: $main-color;
  205. }
  206. }
  207. }
  208. .delete {
  209. margin-left: 8px;
  210. cursor: pointer;
  211. }
  212. }
  213. }
  214. .add {
  215. display: flex;
  216. column-gap: 6px;
  217. align-items: center;
  218. justify-content: center;
  219. margin-top: 12px;
  220. .svg-icon {
  221. cursor: pointer;
  222. }
  223. .add-button {
  224. font-size: 14px;
  225. color: $main-color;
  226. cursor: pointer;
  227. }
  228. }
  229. </style>