fill-question.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <template>
  2. <!-- 填空题 -->
  3. <view class="fill-area" v-model="questionData">
  4. <view class="question_title">
  5. <text class="question_number">
  6. {{ questionNumberEndIsBracket(questionData.property.question_number) }}
  7. </text>
  8. <text class="question_stem" v-html="sanitizeHTML(questionData.stem)"
  9. :ref="'richText-1-1'+questionData.question_id"
  10. @longpress="previewByRichTextImg(-1,-1,questionData.question_id)"></text>
  11. </view>
  12. <view class="description"
  13. v-if="isEnable(questionData.property.is_enable_description)&&questionData.description.length > 0"
  14. v-html="sanitizeHTML(questionData.description)" :ref="'richText-2-2'+questionData.question_id"
  15. @longpress="previewByRichTextImg(-2,-2,questionData.question_id)">
  16. </view>
  17. <view class="word-select-fill"
  18. v-if="isEnable(questionData.property.is_enable_word_select_fill)&&questionData.word_select_fill.length > 0">
  19. <text v-for="(text, i) in wordSelectFillList" :key="i" class="word-select-fill-item"
  20. :style="{ color: text_color_list[i] }" @click="selectedDescription(text,i)">
  21. {{ text }}
  22. </text>
  23. </view>
  24. <view class="model-essay-area" v-if="questionChanged">
  25. <view class="model-essay" v-for="(item,i) in questionData.model_essay" :key="i">
  26. <template v-for="(li, j) in item">
  27. <text v-if="li.type === 'text'" :key="j" v-html="sanitizeHTML(li.content)"></text>
  28. <input v-if="li.type === 'input'" :key="j" v-model="li.content" @blur="saveUserAnswer(li.mark)"
  29. :style="{ width: Math.max(160, li.content.length * 24 * 2) + 'rpx' }"
  30. :class="[...computedAnswerClass(li.mark)]"
  31. :disabled="answer_control[questionData.question_id].isReadOnly" />
  32. <input v-show="computedAnswerText(li.mark).length > 0" :key="`answer-${j}`" class="right-answer"
  33. :value="computedAnswerText(li.mark)" :disabled="true"
  34. :style="{ width: Math.max(160, computedAnswerText(li.mark).length * 24 * 2) + 'rpx' }" />
  35. </text>
  36. </template>
  37. </view>
  38. </view>
  39. <view class="reference" v-if="isViewReference&&answer_control[questionData.question_id].isViewRightAnswer">
  40. <text class="reference-title">参考答案</text>
  41. <text class="reference-answer">{{questionData.reference_answer}}</text>
  42. </view>
  43. <view class="reference" v-if="isViewAnalysis&&answer_control[questionData.question_id].isViewRightAnswer">
  44. <text class="reference-title">解析</text>
  45. <text class="reference-answer" v-html="sanitizeHTML(questionData.analysis)"
  46. :ref="'richText-3-3'+questionData.question_id"
  47. @longpress="previewByRichTextImg(-3,-3,questionData.question_id)">
  48. </text>
  49. </view>
  50. </view>
  51. </template>
  52. <script>
  53. import {
  54. questionData,
  55. sanitizeHTML,
  56. isEnable,
  57. answer_control,
  58. addTone,
  59. } from '@/pages/answer_question/common/data/common.js';
  60. import AnswerControlMixin from '@/pages/answer_question/common/data/AnswerControlMixin.js';
  61. export default {
  62. name: "fill-question",
  63. mixins: [AnswerControlMixin],
  64. props: {
  65. questionData: questionData
  66. },
  67. data() {
  68. return {
  69. sanitizeHTML,
  70. isEnable,
  71. answer_control,
  72. addTone,
  73. select_text_mark: '',
  74. text_color_list: [],
  75. };
  76. },
  77. created() {},
  78. watch: {
  79. 'questionData.question_id': {
  80. handler(val) {
  81. this.setUserAnswer();
  82. },
  83. immediate: true,
  84. deep: true
  85. },
  86. questionData: {
  87. handler(val) {
  88. this.$forceUpdate();
  89. },
  90. immediate: true,
  91. deep: true
  92. },
  93. },
  94. computed: {
  95. wordSelectFillList() {
  96. return this.questionData.word_select_fill.split(/\s+/);
  97. },
  98. questionChanged: function() {
  99. this.setUserAnswer();
  100. return true;
  101. },
  102. isViewReference: function() {
  103. //非选词填空,有参考答案
  104. return !isEnable(this.questionData.property.is_enable_word_select_fill);
  105. },
  106. isViewAnalysis: function() {
  107. return isEnable(this.questionData.property.is_enable_analysis);
  108. }
  109. },
  110. methods: {
  111. selectedDescription(text, index) {
  112. if (!this.select_text_mark) return;
  113. var _inputArr = this.questionData.model_essay.map(p => {
  114. return p.find(x => x.mark == this.select_text_mark)
  115. });
  116. if (!_inputArr || _inputArr.length == 0) return;
  117. _inputArr.find(p => p).content = text;
  118. this.text_color_list.forEach((color, m) => {
  119. this.$set(this.text_color_list, m, '#000000');
  120. });
  121. this.$set(this.text_color_list, index, '#306eff');
  122. this.saveUserAnswer(this.select_text_mark);
  123. },
  124. //填写答案后保存答案
  125. saveUserAnswer(mark) {
  126. var that = this;
  127. that.select_text_mark = mark;
  128. var questionId = that.questionData.question_id;
  129. this.questionData.model_essay.forEach(p => {
  130. p.filter(m => m.type === 'input').forEach(x => {
  131. if (!x.mark) return false;
  132. that.handleTone(mark, x.content);
  133. var index = that.questionData.user_answer[questionId].answer_list.findIndex(n => n.mark == x.mark)
  134. if (index > -1)
  135. that.questionData.user_answer[questionId].answer_list.splice(index, 1);
  136. that.questionData.user_answer[questionId].answer_list.push({
  137. mark: x.mark,
  138. value: x.content
  139. })
  140. })
  141. })
  142. this.questionData.user_answer[questionId].isEdit = true;
  143. var ansed = this.questionData.user_answer[questionId].answer_list;
  144. this.questionData.user_answer[questionId].is_fill_answer = ansed.filter(p => p.value).length > 0;
  145. this.questionData.user_answer[questionId].content = JSON.stringify(ansed);
  146. // this.questionData.user_answer[questionId].answer_list = ansed;
  147. this.$set(this.questionData.user_answer[questionId], 'answer_list', ansed);
  148. this.$emit("setSubAnswer", this.questionData.user_answer[questionId], questionId);
  149. },
  150. //加声调
  151. handleTone(mark, value) {
  152. if (!/^[a-zA-Z0-9\s]+$/.test(value)) return;
  153. var _inputArr = this.questionData.model_essay.map(p => {
  154. return p.find(x => x.mark == this.select_text_mark)
  155. });
  156. if (!_inputArr || _inputArr.length == 0) return;
  157. _inputArr.find(p => p).content = value
  158. .trim()
  159. .split(/\s+/)
  160. .map((item) => {
  161. return this.handleToneValue(item);
  162. })
  163. .map((item) =>
  164. item.map(({
  165. number,
  166. con
  167. }) => (number && con ? addTone(Number(number), con) : number || con || '')),
  168. )
  169. .filter((item) => item.length > 0)
  170. .join(' ');
  171. },
  172. handleToneValue(valItem) {
  173. let numList = [];
  174. if (/[A-Za-zü]+\d/g.test(valItem)) {
  175. valItem.split('').forEach((item, i) => {
  176. if (/\d/.test(item)) {
  177. let numIndex = numList.length === 0 ? 0 : numList[numList.length - 1].index;
  178. let con = valItem.substring(numIndex, i).replace(/\d/g, '');
  179. numList.push({
  180. number: item,
  181. con,
  182. });
  183. }
  184. });
  185. } else {
  186. numList = [];
  187. }
  188. return numList.length === 0 ? [{
  189. con: valItem
  190. }] : numList;
  191. },
  192. setUserAnswer: function() {
  193. var that = this;
  194. var callback = function() {
  195. var userAnswer = [];
  196. var questionId = that.questionData.question_id;
  197. var _ua = that.questionData.user_answer[questionId];
  198. if (_ua && _ua.answer_list && _ua.answer_list.length > 0)
  199. userAnswer = _ua.answer_list;
  200. if (userAnswer.length == 0) return;
  201. that.questionData.model_essay.forEach(p => {
  202. p.forEach(x => {
  203. if (!x.mark) return false;
  204. var en = userAnswer.find(y => y.mark == x.mark)
  205. if (!en) return false;
  206. x.content = en.value;
  207. })
  208. })
  209. }
  210. if (that.questionData.isSubSub) {
  211. callback();
  212. } else {
  213. this.$emit("getUserAnswer", this.questionData.question_id, callback);
  214. }
  215. },
  216. computedAnswerClass(mark) {
  217. var question_id = this.questionData.question_id;
  218. var cur = this.commonComputedAnswerControl(question_id);
  219. if ((!cur.isJudgeAnswer && !cur.isViewRightAnswer) || this.isViewReference) {
  220. return '';
  221. }
  222. let classList = [];
  223. let selectOption = this.questionData.user_answer[question_id].answer_list.find((item) => item.mark ===
  224. mark); // 是否已选中的选项
  225. let answerOption = this.questionData.answer.answer_list.find((item) => item.mark === mark); // 是否正确的选项
  226. if (!selectOption) return '';
  227. let selectValue = selectOption.value;
  228. let answerValue = answerOption.value;
  229. let type = answerOption.type;
  230. let isRight =
  231. (type === 'only_one' && selectValue === answerValue) ||
  232. (type === 'any_one' && answerValue.split('/').includes(selectValue));
  233. if (cur.isJudgeAnswer) {
  234. isRight ? classList.push('right') : classList.push('wrong');
  235. }
  236. if (cur.isViewRightAnswer && !isRight) {
  237. classList.push('show-right-answer');
  238. }
  239. return classList;
  240. },
  241. computedAnswerText(mark) {
  242. if (!mark) return '';
  243. var question_id = this.questionData.question_id;
  244. var cur = this.commonComputedAnswerControl(question_id);
  245. if (!cur.isViewRightAnswer || this.isViewReference) return '';
  246. let selectOption = this.questionData.user_answer[question_id].answer_list.find((item) => item.mark ===
  247. mark); // 是否已选中的选项
  248. let answerOption = this.questionData.answer.answer_list.find((item) => item.mark === mark); // 是否正确的选项
  249. if (!selectOption) {
  250. return `(${answerOption.value})`;
  251. };
  252. let selectValue = selectOption.value;
  253. let answerValue = answerOption.value;
  254. let type = answerOption.type;
  255. let isRight =
  256. (type === 'only_one' && selectValue === answerValue) ||
  257. (type === 'any_one' && answerValue.split('/').includes(selectValue));
  258. if (isRight) return '';
  259. return `(${answerValue})`;
  260. },
  261. }
  262. }
  263. </script>
  264. <style lang="scss" scoped>
  265. .fill-area {
  266. .word-select-fill {
  267. display: flex;
  268. column-gap: 16rpx;
  269. row-gap: 16rpx;
  270. .word-select-fill-item {
  271. padding: 12rpx 16rpx;
  272. background-color: #E9EDF7;
  273. font-size: 32rpx;
  274. border-radius: 8rpx;
  275. }
  276. }
  277. .model-essay-area {
  278. margin: 32rpx 0;
  279. line-height: 64rpx;
  280. font-size: $font-size-serial;
  281. color: #1D2129;
  282. .model-essay {
  283. /deep/ uni-input {
  284. display: inline-block;
  285. border-bottom: 1px solid #1D2129;
  286. text-align: center;
  287. margin: 0 8rpx;
  288. margin-top: -8rpx;
  289. height: 72rpx;
  290. vertical-align: middle;
  291. font-size: $font-size-serial;
  292. &.right {
  293. color: $right-color;
  294. }
  295. &.wrong {
  296. color: $error-color;
  297. }
  298. }
  299. .right-answer {
  300. margin-left: -8rpx;
  301. }
  302. }
  303. }
  304. .reference {
  305. margin: 32rpx 0;
  306. background-color: $uni-bg-color-grey;
  307. padding: 24rpx;
  308. font-size: 28rpx;
  309. .reference-title {
  310. display: block;
  311. line-height: 64rpx;
  312. color: #4E5969;
  313. }
  314. .reference-answer {
  315. color: #1D2129;
  316. line-height: 48rpx;
  317. }
  318. }
  319. }
  320. </style>