mx-question-mixin.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // author: hht 22.1.17
  2. // 代码结构来自mx-app mx-question-content,
  3. // web可以利用slot来实现很多抽象结构,所以这边的写法稍有调整
  4. import consts from '@/common/mx-const'
  5. import { formatDuration } from '@/utils/index'
  6. import eventMixin from './mx-question-event-mixin'
  7. export default {
  8. mixins: [eventMixin],
  9. props: {
  10. options: {
  11. type: Object,
  12. default: _ => ({
  13. question: {},
  14. allowAnswer: false,
  15. allowScore: false,
  16. examineeType: '',
  17. paperOptions: null
  18. })
  19. },
  20. deep: {
  21. // for question display override
  22. type: Number,
  23. default: 0
  24. }
  25. },
  26. data() {
  27. return {
  28. optionCodes: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'],
  29. useMathJax: false // for override
  30. }
  31. },
  32. watch: {
  33. 'question.questionId': {
  34. immediate: true,
  35. handler: function(newVal) {
  36. // for math jax auto update
  37. if (!this.useMathJax) return
  38. // console.log('watch question changed by id - call math jax format', this)
  39. this.$nextTick(_ => this.mxGlobal.MathQueue())
  40. }
  41. }
  42. },
  43. computed: {
  44. question() {
  45. return this.options.question
  46. },
  47. allowAnswer() {
  48. return this.options.allowAnswer
  49. },
  50. allowScore() {
  51. return this.options.allowScore
  52. },
  53. examineeType() {
  54. // examineeType.evaluation as default.
  55. return this.options.examineeType || consts.enum.paper.examineeType.evaluation
  56. },
  57. userWrong() {
  58. return this.sysAnswer && this.userAnswer != this.sysAnswer
  59. },
  60. userAnswer() {
  61. return this.question?.answer || ''
  62. },
  63. sysAnswer() {
  64. return this.question?.answers?.first() || ''
  65. },
  66. userAnswerConvert() {
  67. // 转成便于计算的数组
  68. return this.userAnswer && this.userAnswer.split(',') || []
  69. },
  70. sysAnswerConvert() {
  71. // 转成便于计算的数组
  72. return this.sysAnswer && this.sysAnswer.split(',') || []
  73. },
  74. useReadonlyMode() {
  75. return !this.allowAnswer && !this.allowScore
  76. },
  77. questionWithSeq() {
  78. const {seq, type, title} = this.question
  79. let prefix = seq ? seq + '、' : ''
  80. if (type) prefix += '[' + type + ']'
  81. if (title?.startsWith('<div')||title?.startsWith('<p')) {
  82. let tagBegin = /<(div|p)[^>]*>(\s|(&nbsp;))*/.exec(title)?.first()
  83. if (tagBegin) {
  84. const titleRest = title.substring(tagBegin.length)
  85. tagBegin = tagBegin.replaceAll('&nbsp;', '').replaceAll(' ', '')
  86. return tagBegin + prefix + titleRest
  87. }
  88. }
  89. return prefix + title
  90. },
  91. optionsVisible() {
  92. return this.question.options && this.question.options.length
  93. },
  94. optionsWithCode() {
  95. return this.question.options.map((opt, idx) => ({
  96. code: this.optionCodes[idx],
  97. option: this.optionCodes[idx] + '、' + opt
  98. }))
  99. },
  100. enableRememberAnswer() {
  101. const rememberTypes = [4,5,6,7]
  102. return rememberTypes.some(id => id == this.question.typeId)
  103. },
  104. useMultipleStyle() {
  105. // 子题都算主观题, 学生手写作答
  106. return this.deep == 0 && this.question.typeId == 3
  107. },
  108. useRadioStyle() {
  109. // 子题都算主观题, 学生手写作答
  110. const radioTypes = [1,6,7]
  111. return this.deep == 0 && radioTypes.some(id => id == this.question.typeId)
  112. },
  113. useViewOfLifeValuesPair() {
  114. // 人生价值观测评,此类题有两个选项,要求两个选项的得分之和为3
  115. return this.deep == 0 && this.question.typeId == 4
  116. },
  117. useViewOfLifeValuesQuadruplet() {
  118. // 人生价值观测评,此类题有四个选项,要求四个选项分别得3,2,1,0四种得分
  119. return this.deep == 0 && this.question.typeId == 5
  120. },
  121. useAnswerStyle() {
  122. // 主观题需要手写作答
  123. return this.allowAnswer &&
  124. !this.useRadioStyle && !this.useMultipleStyle &&
  125. !this.useViewOfLifeValuesPair && !this.useViewOfLifeValuesQuadruplet
  126. },
  127. useParseStyle() {
  128. // 答案/解析/评分
  129. return !this.allowAnswer
  130. },
  131. useBoardTextStyle() {
  132. return this.question._boardMode == consts.enum.paper.boardMode.text
  133. },
  134. useBoardImageStyle() {
  135. return this.question._boardMode == consts.enum.paper.boardMode.image
  136. },
  137. durationDisplay() {
  138. return formatDuration(this.question._duration, true)
  139. },
  140. useBoardParseStyle() {
  141. return this.question._parseMode == consts.enum.paper.parseMode.parse
  142. },
  143. useBoardScoreStyle() {
  144. return this.question._parseMode == consts.enum.paper.parseMode.score
  145. }
  146. },
  147. methods: {
  148. // question options display
  149. isWrong(code) {
  150. // 展示错误回答,标红
  151. if (!this.userWrong) return false
  152. return this.userAnswerConvert.includes(code) && !this.sysAnswerConvert.includes(code)
  153. },
  154. isHighlight(code) {
  155. // 显示正确答案,标绿
  156. // if (!this.userWrong) return false
  157. return this.sysAnswerConvert.includes(code)
  158. },
  159. // question board style
  160. setBoardTextStyle() {
  161. this.question._boardMode = consts.enum.paper.boardMode.text
  162. },
  163. setBoardImageStyle() {
  164. this.question._boardMode = consts.enum.paper.boardMode.image
  165. },
  166. setParseDefaultStyle() {
  167. this.question._parseMode = consts.enum.paper.parseMode.parse
  168. },
  169. setParseScoreStyle() {
  170. this.question._parseMode = consts.enum.paper.parseMode.score
  171. },
  172. setLocalDurationUp() {
  173. this.question._duration = this.question._duration + 1
  174. }
  175. }
  176. }