FillQuestion.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <QuestionBase>
  3. <template #content>
  4. <div class="stem">
  5. <RichText v-model="data.stem" :font-size="18" placeholder="输入题干" />
  6. <el-input
  7. v-if="isEnable(data.property.is_enable_description)"
  8. v-model="data.description"
  9. rows="3"
  10. resize="none"
  11. type="textarea"
  12. placeholder="输入填空内容"
  13. />
  14. </div>
  15. <div class="content">
  16. <RichText
  17. ref="modelEssay"
  18. v-model="data.article"
  19. :is-fill="true"
  20. :toolbar="false"
  21. :wordlimit-num="false"
  22. placeholder="输入文段"
  23. />
  24. <el-button @click="identifyText">识别</el-button>
  25. <div v-if="data.answer.answer_list.length > 0" class="correct-answer">
  26. <div class="subtitle">正确答案</div>
  27. <el-input
  28. v-for="(item, i) in data.answer.answer_list.filter(({ type }) => type === 'any_one')"
  29. :key="item.mark"
  30. v-model="item.value"
  31. @blur="handleTone(item.value, i)"
  32. >
  33. <span slot="prefix">{{ i + 1 }}.</span>
  34. </el-input>
  35. </div>
  36. </div>
  37. </template>
  38. <template #property>
  39. <el-form :model="data.property">
  40. <el-form-item label="题号">
  41. <el-input v-model="data.property.question_number" />
  42. </el-form-item>
  43. <el-form-item label-width="45px">
  44. <el-radio
  45. v-for="{ value, label } in questionNumberTypeList"
  46. :key="value"
  47. v-model="data.other.question_number_type"
  48. :label="value"
  49. >
  50. {{ label }}
  51. </el-radio>
  52. </el-form-item>
  53. <el-form-item label="描述">
  54. <el-radio
  55. v-for="{ value, label } in switchOption"
  56. :key="value"
  57. v-model="data.property.is_enable_description"
  58. :label="value"
  59. >
  60. {{ label }}
  61. </el-radio>
  62. </el-form-item>
  63. <el-form-item label="分值">
  64. <el-radio
  65. v-for="{ value, label } in scoreTypeList"
  66. :key="value"
  67. v-model="data.property.score_type"
  68. :label="value"
  69. >
  70. {{ label }}
  71. </el-radio>
  72. </el-form-item>
  73. <el-form-item label-width="45px">
  74. <el-input-number
  75. v-model="data.property.score"
  76. :min="0"
  77. :step="data.property.score_type === scoreTypeList[0].value ? 1 : 0.1"
  78. />
  79. </el-form-item>
  80. </el-form>
  81. </template>
  82. </QuestionBase>
  83. </template>
  84. <script>
  85. import QuestionMixin from '../common/QuestionMixin.js';
  86. import { getRandomNumber } from '@/utils';
  87. import { addTone } from '@/views/exercise_questions/data/common';
  88. import { fillData, handleToneValue } from '@/views/exercise_questions/data/fill';
  89. export default {
  90. name: 'FillQuestion',
  91. mixins: [QuestionMixin],
  92. data() {
  93. return {
  94. data: JSON.parse(JSON.stringify(fillData)),
  95. };
  96. },
  97. methods: {
  98. // 识别文本
  99. identifyText() {
  100. this.data.model_essay = [];
  101. this.data.answer.answer_list = [];
  102. this.data.article
  103. .split(/<p .*?>(.*?)<\/p>/gi)
  104. .filter((item) => item)
  105. .forEach((item) => {
  106. if (item.charCodeAt() === 10) return;
  107. let str = item
  108. // 去除所有的 font-size 样式
  109. .replace(/font-size:\s*\d+(\.\d+)?px;/gi, '')
  110. // 匹配 class 名为 rich-fill 的 span 标签和三个以上的_,并将它们组成数组
  111. .replace(/<span class="rich-fill".*?>(.*?)<\/span>|([_]{3,})/gi, '###$1$2###');
  112. this.data.model_essay.push(this.splitRichText(str));
  113. });
  114. },
  115. // 分割富文本
  116. splitRichText(str) {
  117. let _str = str;
  118. let start = 0;
  119. let index = 0;
  120. let arr = [];
  121. let matchNum = 0;
  122. while (index !== -1) {
  123. index = _str.indexOf('###', start);
  124. if (index === -1) break;
  125. matchNum += 1;
  126. arr.push({ content: _str.slice(start, index), type: 'text' });
  127. if (matchNum % 2 === 0 && arr.length > 0) {
  128. arr[arr.length - 1].type = 'input';
  129. let mark = getRandomNumber();
  130. arr[arr.length - 1].mark = mark;
  131. let content = arr[arr.length - 1].content;
  132. // 设置答案数组
  133. let isUnderline = /^_{3,}$/.test(content);
  134. this.data.answer.answer_list.push({
  135. value: isUnderline ? '' : content,
  136. mark,
  137. type: isUnderline ? 'any_one' : 'only_one',
  138. });
  139. // 将 content 设置为空,为预览准备
  140. arr[arr.length - 1].content = '';
  141. }
  142. start = index + 3;
  143. }
  144. let last = _str.slice(start);
  145. if (last) {
  146. arr.push({ content: last, type: 'text' });
  147. }
  148. return arr;
  149. },
  150. handleTone(value, i) {
  151. if (!/^[a-zA-Z0-9\s]+$/.test(value)) return;
  152. this.data.answer.answer_list[i].value = value
  153. .trim()
  154. .split(/\s+/)
  155. .map((item) => {
  156. return handleToneValue(item);
  157. })
  158. .map((item) =>
  159. item.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
  160. )
  161. .filter((item) => item.length > 0)
  162. .join(' ');
  163. },
  164. },
  165. };
  166. </script>
  167. <style lang="scss" scoped>
  168. .content {
  169. position: relative;
  170. .el-button {
  171. margin-top: 8px;
  172. }
  173. .correct-answer {
  174. .subtitle {
  175. margin: 8px 0;
  176. font-size: 14px;
  177. color: #4e5969;
  178. }
  179. .el-input {
  180. width: 180px;
  181. :deep &__prefix {
  182. display: flex;
  183. align-items: center;
  184. color: $text-color;
  185. }
  186. + .el-input {
  187. margin-left: 8px;
  188. }
  189. }
  190. }
  191. }
  192. </style>