do.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <template>
  2. <div class="app-contain">
  3. <a-row class="do-exam-title">
  4. <a-col :span="24">
  5. <span v-for="item in answer.answerItems" :key="item.itemOrder">
  6. <a-tag
  7. :color="questionCompleted(item.completed)"
  8. class="do-exam-title-tag"
  9. @click="goAnchor(`#question-${item.itemOrder}`)"
  10. >
  11. {{ item.itemOrder }}
  12. </a-tag>
  13. </span>
  14. <span class="do-exam-time">
  15. <label>剩余时间:</label>
  16. <label>{{ formatSeconds(remainTime) }}</label>
  17. </span>
  18. </a-col>
  19. </a-row>
  20. <a-row class="do-exam-title-hidden">
  21. <a-col :span="24">
  22. <span v-for="item in answer.answerItems" :key="item.itemOrder">
  23. <a-tag class="do-exam-title-tag">{{ item.itemOrder }}</a-tag>
  24. </span>
  25. <span class="do-exam-time">
  26. <label>剩余时间:</label>
  27. </span>
  28. </a-col>
  29. </a-row>
  30. <a-layout class="app-item-contain">
  31. <a-layout-header class="align-center">
  32. <h1>{{ form.name }}</h1>
  33. <div>
  34. <span class="question-title-padding">试卷总分:{{ form.score }}</span>
  35. <span class="question-title-padding">考试时间:{{ form.suggestTime }}分钟</span>
  36. </div>
  37. </a-layout-header>
  38. <a-layout-content>
  39. <a-form :model="form" ref="formRef" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }" :loading="formLoading">
  40. <template v-for="(titleItem, index) in form.titleItems" :key="index">
  41. <h3 class="question-title">{{ titleItem.name }}</h3>
  42. <a-card class="exampaper-item-box" v-if="titleItem.questionItems && titleItem.questionItems.length">
  43. <a-form-item
  44. v-for="questionItem in titleItem.questionItems"
  45. :key="questionItem.itemOrder"
  46. :label="`${questionItem.itemOrder}.`"
  47. class="exam-question-item"
  48. :id="`question-${questionItem.itemOrder}`"
  49. :label-col="{ span: 0 }"
  50. :wrapper-col="{ span: 24 }"
  51. :colon="false"
  52. >
  53. <QuestionEdit
  54. :qType="questionItem.questionType"
  55. :question="questionItem"
  56. :answer="answer.answerItems[questionItem.itemOrder - 1]"
  57. @update:answerCompleted="answer.answerItems[questionItem.itemOrder - 1].completed = true"
  58. @update:answerContent="answer.answerItems[questionItem.itemOrder - 1].content = $event"
  59. @update:answerContentArray="answer.answerItems[questionItem.itemOrder - 1].contentArray = $event"
  60. />
  61. </a-form-item>
  62. </a-card>
  63. </template>
  64. <a-row class="do-align-center">
  65. <a-button type="primary" @click="submitForm">提交</a-button>
  66. <a-button style="margin-left: 12px">取消</a-button>
  67. </a-row>
  68. </a-form>
  69. </a-layout-content>
  70. </a-layout>
  71. </div>
  72. </template>
  73. <script setup>
  74. import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
  75. import { useRoute, useRouter } from 'vue-router'
  76. import { useExamStore } from '@/store/exam'
  77. import { formatSeconds } from '@/utils/exam'
  78. import QuestionEdit from '../components/QuestionEdit.vue'
  79. import examPaperApi from '@/api/student/examPaper'
  80. import examPaperAnswerApi from '@/api/student/examPaperAnswer'
  81. import { Modal } from 'ant-design-vue'
  82. import '../../style.less'
  83. const route = useRoute()
  84. const router = useRouter()
  85. const examStore = useExamStore()
  86. const form = reactive({})
  87. const formLoading = ref(false)
  88. const answer = reactive({
  89. questionId: null,
  90. doTime: 0,
  91. answerItems: []
  92. })
  93. const timer = ref(null)
  94. const remainTime = ref(0)
  95. const formRef = ref()
  96. function questionCompleted(completed) {
  97. // doCompletedTag: [{ key: false, value: 'info' }, { key: true, value: 'success' }]
  98. return examStore.enumFormat(examStore.exam.question.answer.doCompletedTag, completed)
  99. }
  100. function goAnchor(selector) {
  101. const el = document.querySelector(selector)
  102. if (el) {
  103. el.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' })
  104. }
  105. }
  106. function initAnswer() {
  107. answer.id = form.id
  108. answer.answerItems = []
  109. const titleItemArray = form.titleItems || []
  110. for (const tItem of titleItemArray) {
  111. const questionArray = tItem.questionItems || []
  112. for (const question of questionArray) {
  113. answer.answerItems.push({
  114. questionId: question.id,
  115. content: null,
  116. contentArray: [],
  117. completed: false,
  118. itemOrder: question.itemOrder
  119. })
  120. }
  121. }
  122. }
  123. function timeReduce() {
  124. timer.value = setInterval(() => {
  125. if (remainTime.value <= 0) {
  126. submitForm()
  127. } else {
  128. answer.doTime++
  129. remainTime.value--
  130. }
  131. }, 1000)
  132. }
  133. function submitForm() {
  134. clearInterval(timer.value)
  135. formLoading.value = true
  136. examPaperAnswerApi
  137. .answerSubmit(answer)
  138. .then((re) => {
  139. Modal.success({
  140. title: '考试结果',
  141. content: `试卷得分:${re}分`,
  142. okText: '返回考试记录',
  143. onOk: () => {
  144. router.push('/student/record/')
  145. }
  146. })
  147. formLoading.value = false
  148. })
  149. .catch(() => {
  150. formLoading.value = false
  151. })
  152. }
  153. onMounted(() => {
  154. const id = route.query.id
  155. if (id && parseInt(id) !== 0) {
  156. formLoading.value = true
  157. examPaperApi.select(id).then((re) => {
  158. Object.assign(form, re)
  159. remainTime.value = re.suggestTime * 60
  160. initAnswer()
  161. timeReduce()
  162. formLoading.value = false
  163. })
  164. }
  165. })
  166. onBeforeUnmount(() => {
  167. clearInterval(timer.value)
  168. })
  169. </script>