|
|
@@ -207,13 +207,19 @@
|
|
|
<v-snackbar v-model="submitting" absolute centered color="info">
|
|
|
<span v-if="examTimesUp">评测结束</span> 提交试卷中...
|
|
|
</v-snackbar>
|
|
|
- <v-snackbar v-model="examAvailableDialog" centered absolute color="error">
|
|
|
+ <v-snackbar v-model="examNotAvailable" centered absolute color="error">
|
|
|
评测不可用
|
|
|
</v-snackbar>
|
|
|
+ <v-snackbar v-model="reenter" centered absolute color="error">
|
|
|
+ 已经提交试卷,考试结束
|
|
|
+ </v-snackbar>
|
|
|
<v-snackbar v-model="submittedError" absolute>
|
|
|
提交失败,请重试
|
|
|
</v-snackbar>
|
|
|
<v-snackbar v-model="endTimeChanged" absolute>评测已延长</v-snackbar>
|
|
|
+ <v-snackbar v-model="resetLast" absolute color="info" top
|
|
|
+ >已恢复到上次答题状态</v-snackbar
|
|
|
+ >
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -224,20 +230,22 @@ import ChoicePane from '@/views/exam/components/ChoicePane.vue'
|
|
|
import CountDown from '@/views/exam/components/CountDown.vue'
|
|
|
import BlankPane from '@/views/exam/components/BlankPane.vue'
|
|
|
import {
|
|
|
+ CodeQuestionSerializer,
|
|
|
ExamQuestionSerializer,
|
|
|
ExamSerializer,
|
|
|
- CodeQuestionSerializer,
|
|
|
+ ID,
|
|
|
+ LocalDateTime,
|
|
|
QuestionSerializer,
|
|
|
QuestionType,
|
|
|
- Record,
|
|
|
- LocalDateTime,
|
|
|
- ID
|
|
|
+ Record
|
|
|
} from '@/api/types'
|
|
|
-import { getExamById, getExamQuestions, getExamCodeQuestions } from '@/api/exam'
|
|
|
+import { getExamById, getExamCodeQuestions, getExamQuestions } from '@/api/exam'
|
|
|
import { getQuestionStem } from '@/api/question'
|
|
|
import { objectiveJudge } from '@/api/judge'
|
|
|
import dayjs from 'dayjs'
|
|
|
import { TIME_FORMAT } from '@/utils/common'
|
|
|
+import { mapGetters, mapMutations } from 'vuex'
|
|
|
+import { getExamSubmitRecords } from '@/api/record'
|
|
|
|
|
|
export default Vue.extend({
|
|
|
name: 'ExamPane',
|
|
|
@@ -260,11 +268,11 @@ export default Vue.extend({
|
|
|
currentQuestion: (null as unknown) as QuestionSerializer,
|
|
|
currentQuestionIndex: -1,
|
|
|
objectiveAnswers: {} as Map<ID, Record>,
|
|
|
- questionIds: [] as Array<string>,
|
|
|
+ questionIds: [] as Array<ID>,
|
|
|
questionStartTime: dayjs().format(TIME_FORMAT) as LocalDateTime,
|
|
|
examId: 0 as ID,
|
|
|
codeQuestions: [] as Array<CodeQuestionSerializer>,
|
|
|
- examAvailableDialog: false,
|
|
|
+ examNotAvailable: false,
|
|
|
submitting: false,
|
|
|
finished: false,
|
|
|
answered: false,
|
|
|
@@ -274,7 +282,9 @@ export default Vue.extend({
|
|
|
objectiveQuestionNum: 0,
|
|
|
codeQuestionNum: 0,
|
|
|
codeQuestionsReady: false,
|
|
|
- endTimeChanged: false
|
|
|
+ endTimeChanged: false,
|
|
|
+ resetLast: false,
|
|
|
+ reenter: false
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -286,6 +296,8 @@ export default Vue.extend({
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
+ ...mapGetters(['getExamAnswers', 'getExamId']),
|
|
|
+ ...mapMutations(['setExamAnswers', 'setExamId']),
|
|
|
questionType(): QuestionType {
|
|
|
return this.currentQuestion.type
|
|
|
},
|
|
|
@@ -304,41 +316,35 @@ export default Vue.extend({
|
|
|
this.examTimesUp = true
|
|
|
this.submitAnswer()
|
|
|
},
|
|
|
- // saveSubmittedCode(code: string, lang: string) {
|
|
|
- // const changedCodeQuestion = this.codeQuestions[this.codeIndex]
|
|
|
- // const changedCodeTuple =
|
|
|
- // changedCodeQuestion.lastCommitCodes?.find((val) => {
|
|
|
- // return val.lang === lang
|
|
|
- // }) ?? {}
|
|
|
- // changedCodeTuple.code = code
|
|
|
- // },
|
|
|
saveObjectiveAnswer(answer: string) {
|
|
|
let questionEndTime = dayjs().format(TIME_FORMAT)
|
|
|
+
|
|
|
this.objectiveAnswers.set(this.questionIds[this.currentQuestionIndex], {
|
|
|
answer,
|
|
|
startAt: this.questionStartTime,
|
|
|
endAt: questionEndTime
|
|
|
})
|
|
|
+
|
|
|
+ this.setExamAnswers(Object.fromEntries(this.objectiveAnswers))
|
|
|
+ },
|
|
|
+ // 清空本地存储
|
|
|
+ clearLocalStorage() {
|
|
|
+ this.setExamId('')
|
|
|
+ this.setExamAnswers({})
|
|
|
},
|
|
|
leaveExam() {
|
|
|
+ // 离开考试暂时不清除本地存储
|
|
|
this.exitDialog = false
|
|
|
- this.$router.push('/')
|
|
|
+ this.$router.push({ name: 'Home' })
|
|
|
},
|
|
|
submitAnswer() {
|
|
|
this.submitDialog = false
|
|
|
this.submitting = true
|
|
|
- console.log(this.objectiveAnswers)
|
|
|
- console.log(this.questionIds)
|
|
|
-
|
|
|
- let mapObj = Object.create(null)
|
|
|
- for (let [k, v] of this.objectiveAnswers) {
|
|
|
- if (v.answer) {
|
|
|
- mapObj[k] = v
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- objectiveJudge(this.examId, mapObj)
|
|
|
+ objectiveJudge(this.examId, Object.fromEntries(this.objectiveAnswers))
|
|
|
.then(() => {
|
|
|
+ // 交卷后清除本地存储
|
|
|
+ this.clearLocalStorage()
|
|
|
setTimeout(() => {
|
|
|
this.$router.push({
|
|
|
name: 'result',
|
|
|
@@ -369,13 +375,7 @@ export default Vue.extend({
|
|
|
}
|
|
|
|
|
|
if (this.currentQuestionIndex < this.totalQuestionNum - 1) {
|
|
|
- if (
|
|
|
- !this.objectiveAnswers.get(
|
|
|
- this.questionIds[this.currentQuestionIndex]
|
|
|
- )
|
|
|
- ) {
|
|
|
- //TODO 是否需要提示未作答?
|
|
|
- }
|
|
|
+ //TODO 是否需要提示未作答?
|
|
|
this.questionStartTime = dayjs().format(TIME_FORMAT)
|
|
|
|
|
|
this.currentQuestionIndex++
|
|
|
@@ -388,16 +388,26 @@ export default Vue.extend({
|
|
|
fetchData() {
|
|
|
this.examId = this.$route.params.examId
|
|
|
|
|
|
+ // 提交过客观题,说明已经交卷
|
|
|
+ getExamSubmitRecords(this.examId).then(({ data }) => {
|
|
|
+ if (data.result.find((val) => !val.message)) {
|
|
|
+ this.reenter = true
|
|
|
+ setTimeout(() => {
|
|
|
+ this.leaveExam()
|
|
|
+ }, 1000)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
getExamById(this.examId).then(({ data }) => {
|
|
|
this.examInfo = data.result
|
|
|
this.endTime = dayjs(this.examInfo.endTime, TIME_FORMAT).valueOf()
|
|
|
this.currentTime = dayjs().valueOf()
|
|
|
const startTime = dayjs(this.examInfo.startTime, TIME_FORMAT).valueOf()
|
|
|
-
|
|
|
+ // 检查时间
|
|
|
if (this.currentTime >= this.endTime || this.currentTime < startTime) {
|
|
|
- this.examAvailableDialog = true
|
|
|
+ this.examNotAvailable = true
|
|
|
setTimeout(() => {
|
|
|
- this.$router.push({ name: 'Home' })
|
|
|
+ this.leaveExam()
|
|
|
}, 1000)
|
|
|
}
|
|
|
getExamQuestions(this.examId).then(({ data }) => {
|
|
|
@@ -424,12 +434,36 @@ export default Vue.extend({
|
|
|
})
|
|
|
this.codeQuestionsReady = true
|
|
|
|
|
|
- this.currentQuestionIndex = 0
|
|
|
- this.fetchQuestionStem(0)
|
|
|
+ if (this.getExamId() != this.examId) {
|
|
|
+ // 新参加的考试,将 vuex 状态清空,不存在同时参加多场考试的情况
|
|
|
+ this.clearLocalStorage()
|
|
|
+ this.setExamId(this.examId)
|
|
|
+ this.objectiveAnswers = new Map()
|
|
|
+ this.currentQuestionIndex = 0
|
|
|
+ this.fetchQuestionStem(0)
|
|
|
+ } else {
|
|
|
+ // 中途重进考试,可以基于本地存储恢复现场
|
|
|
+ this.objectiveAnswers = new Map(
|
|
|
+ Object.entries(this.getExamAnswers())
|
|
|
+ )
|
|
|
+
|
|
|
+ getExamSubmitRecords(this.examId).then(({ data }) => {
|
|
|
+ if (data.result.find((val) => val.message)) {
|
|
|
+ // 提交过代码题,表示已经做完客观题
|
|
|
+ this.currentQuestionIndex = this.codeQuestionStartIndex
|
|
|
+ } else {
|
|
|
+ if (this.objectiveAnswers.size === 0) {
|
|
|
+ this.currentQuestionIndex = 0
|
|
|
+ } else {
|
|
|
+ this.currentQuestionIndex = this.objectiveAnswers.size - 1
|
|
|
+ }
|
|
|
+ this.fetchQuestionStem(this.currentQuestionIndex)
|
|
|
+ }
|
|
|
+ this.resetLast = true
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|
|
|
})
|
|
|
-
|
|
|
- this.objectiveAnswers = new Map<ID, Record>()
|
|
|
})
|
|
|
})
|
|
|
}
|