useStepPaperInjection.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import {ref, watch, computed, nextTick} from 'vue'
  2. import {createEventHook, injectLocal, provideLocal} from "@vueuse/core";
  3. import {useProvidePaperService} from "@/components/mx-paper/usePaperInjection";
  4. import {numberToChinese} from "@/utils";
  5. import {useProvideQuestionOptionFormatter} from "@/components/mx-question-content/useQuestionOptionInjection";
  6. import {useProvideMathJaxSwitch} from "@/components/mx-question-content/useMathJaxSwitchInjection";
  7. import {toast} from "@/uni_modules/uv-ui-tools/libs/function";
  8. import {empty, func} from "@/uni_modules/uv-ui-tools/libs/function/test";
  9. import mxConst from "@/common/mxConst";
  10. import {fnPlaceholder} from "@/utils/uni-helper";
  11. const key = Symbol('STEP_PAPER_SERVICE')
  12. export const useProvideStepPaper = (name, answerFormatter = (v) => v) => {
  13. const title = ref(name)
  14. const welcomeShow = ref(true)
  15. const resultShow = ref(false)
  16. const step = ref(0)
  17. const steps = ref([])
  18. const stepQuestionContainer = ref(new Map())
  19. const answerData = ref(new Map())
  20. const beginEvent = createEventHook()
  21. const stepStartEvent = createEventHook()
  22. const finishEvent = createEventHook()
  23. const currentStep = computed(() => steps.value[step.value] || {})
  24. const currentStepQuestions = computed(() => stepQuestionContainer.value.get(currentStep.value.id))
  25. const paperData = computed(() => ({
  26. name: name,
  27. allowAnswer: true,
  28. allowScore: false,
  29. questions: currentStepQuestions.value
  30. }))
  31. const overridePaperService = {
  32. commitQuestion: async (commits) => {
  33. // step paper只涉及本地提交
  34. // 没有更改doChunk,业务上还是可以依靠它来驱动
  35. commits.forEach(item => {
  36. answerData.value.set(item.questionId, item.answer)
  37. })
  38. },
  39. commitPaper: async () => {
  40. // step paper表示往前走一步
  41. // 此进是强制走步,此时将触发finish
  42. step.value += 1
  43. }
  44. }
  45. // 只重写了step需要影响的部分
  46. const paperService = useProvidePaperService(paperData, overridePaperService)
  47. const optionFormatter = (options) => options.map(o => ({code: o.id, option: o.label}))
  48. const optionClass = (q) => mxConst.question.isRadio(q.typeId) ? 'pt-10' : ''
  49. useProvideQuestionOptionFormatter(optionFormatter, optionClass)
  50. useProvideMathJaxSwitch(true) // 明确此类测评没有包含任何公式转换
  51. const hasNextStep = computed(() => step.value < steps.value.length - 1)
  52. const hasPrevStep = computed(() => step.value > 0)
  53. const tryGoNextStep = () => {
  54. if (!hasNextStep.value) return toast('已经是最后一部分了')
  55. step.value += 1
  56. paperService.index.value = 0
  57. }
  58. const tryGoPrevStep = () => {
  59. if (!hasPrevStep.value) return toast('已经是第一题了')
  60. step.value -= 1
  61. nextTick().then(() => {
  62. // 需要等待题加载完毕,再重置题的位置
  63. paperService.index.value = paperService.questions.value.length - 1
  64. })
  65. }
  66. watch([welcomeShow, resultShow], async () => {
  67. if (!welcomeShow.value && !resultShow.value) {
  68. await beginEvent.trigger()
  69. }
  70. })
  71. watch(currentStep, async (step) => {
  72. if (!empty(step)) {
  73. await stepStartEvent.trigger(step.id)
  74. }
  75. })
  76. watch(() => step.value >= steps.value.length, async (complete) => {
  77. if (complete) {
  78. // 转成API要求的标准格式
  79. const details = []
  80. for (const [key, value] of answerData.value) {
  81. details.push({
  82. questionId: key,
  83. answer: answerFormatter(value)
  84. })
  85. }
  86. await finishEvent.trigger({details})
  87. resultShow.value = true
  88. }
  89. })
  90. const options = {
  91. ...paperService,
  92. title,
  93. welcomeShow,
  94. resultShow,
  95. step,
  96. steps,
  97. currentStep,
  98. stepQuestionContainer,
  99. hasPrevStep,
  100. hasNextStep,
  101. tryGoPrevStep,
  102. tryGoNextStep,
  103. paperData,
  104. answerData,
  105. onBegin: beginEvent.on,
  106. onStepStart: stepStartEvent.on,
  107. onFinish: finishEvent.on
  108. }
  109. provideLocal(key, options)
  110. return options
  111. }
  112. export const useInjectStepPaper = () => {
  113. return injectLocal(key)
  114. }
  115. export const stepFormatter = (steps) => {
  116. steps.forEach((s, i) => s.name = numberToChinese(i + 1))
  117. return steps
  118. }
  119. export const questionsFormatter = (questions, typeId, options = null) => {
  120. questions.forEach((q, i) => {
  121. const op = func(options) ? options(q) : options
  122. q.questionId = q.questionId || q.id
  123. q.typeId = typeId
  124. q.seq = i + 1
  125. q.options = op || q.options
  126. })
  127. return questions
  128. }