|
|
@@ -83,10 +83,10 @@
|
|
|
<div class="btns">
|
|
|
<van-row gutter="10">
|
|
|
<van-col span="12">
|
|
|
- <van-button :disabled="curQuNo <= 0" class="w100" @click="handlePre">上一题</van-button>
|
|
|
+ <van-button :disabled="isPreDisabled" :loading="isSubmitting" class="w100" @click="handlePre">上一题</van-button>
|
|
|
</van-col>
|
|
|
<van-col span="12">
|
|
|
- <van-button :disabled="curQuNo == sortQuList.length - 1" class="w100" @click="handleNext">下一题</van-button>
|
|
|
+ <van-button :disabled="isNextDisabled" :loading="isSubmitting" class="w100" @click="handleNext">下一题</van-button>
|
|
|
</van-col>
|
|
|
</van-row>
|
|
|
<!-- <van-row gutter="10" class="mt10">
|
|
|
@@ -95,7 +95,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<van-action-bar placeholder>
|
|
|
- <van-action-bar-button type="primary" text="交卷" @click="handleSubmitAllAnswer(false)" />
|
|
|
+ <van-action-bar-button type="primary" text="交卷" :disabled="isSubmitDisabled" :loading="isSubmitting" @click="handleSubmitAllAnswer(false)" />
|
|
|
</van-action-bar>
|
|
|
</template>
|
|
|
|
|
|
@@ -129,18 +129,68 @@
|
|
|
const checkboxRefs = ref([])
|
|
|
const debounceTimer = ref(0)
|
|
|
const isClearingAnswer = ref(false)
|
|
|
- const toggle = (index) => {
|
|
|
- checkboxRefs.value[index].toggle()
|
|
|
+ const hasUnsavedChanges = ref(false)
|
|
|
+ const activeRequestCount = ref(0)
|
|
|
+ const isSubmitting = computed(() => activeRequestCount.value > 0)
|
|
|
+
|
|
|
+ // 检查题目是否已经回答过
|
|
|
+ const isQuestionAnswered = (qu: any) => {
|
|
|
+ if (!qu) return false
|
|
|
+ const quType = String(qu.quType)
|
|
|
+ if (quType === '1' || quType === '3') {
|
|
|
+ return !!(qu.isCorrect && String(qu.isCorrect).trim() !== '')
|
|
|
+ } else if (quType === '2') {
|
|
|
+ return Array.isArray(qu.isCorrect) && qu.isCorrect.filter((x: any) => x && String(x).trim() !== '').length > 0
|
|
|
+ } else if (quType === '4') {
|
|
|
+ return Array.isArray(qu.quContent) && qu.quContent.some((item: any) => item.content && String(item.content).trim() !== '')
|
|
|
+ } else if (quType === '5') {
|
|
|
+ return !!(qu.answer && String(qu.answer).trim() !== '')
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 上一题按钮禁用条件:第一题,或者正在提交中,或者有未保存的改动
|
|
|
+ const isPreDisabled = computed(() => {
|
|
|
+ return curQuNo.value <= 0 || isSubmitting.value || hasUnsavedChanges.value
|
|
|
+ })
|
|
|
+
|
|
|
+ // 下一题按钮禁用条件:最后一题,或者正在提交中,或者有未保存改动,或者当前题目尚未选择/保存答案
|
|
|
+ const isNextDisabled = computed(() => {
|
|
|
+ if (curQuNo.value >= sortQuList.value.length - 1) return true
|
|
|
+ if (isSubmitting.value) return true
|
|
|
+ if (hasUnsavedChanges.value) return true
|
|
|
+ if (!isQuestionAnswered(currentNode.value)) return true
|
|
|
+ return false
|
|
|
+ })
|
|
|
+
|
|
|
+ // 交卷按钮禁用条件:必须到最后一个题目,且不能在提交中/有未保存改动/未完成最后一题
|
|
|
+ const isSubmitDisabled = computed(() => {
|
|
|
+ if (sortQuList.value.length === 0) return true
|
|
|
+ if (curQuNo.value !== sortQuList.value.length - 1) return true
|
|
|
+ if (isSubmitting.value) return true
|
|
|
+ if (hasUnsavedChanges.value) return true
|
|
|
+ if (!isQuestionAnswered(currentNode.value)) return true
|
|
|
+ return false
|
|
|
+ })
|
|
|
+
|
|
|
+ const toggle = (index: number) => {
|
|
|
+ if (checkboxRefs.value && checkboxRefs.value[index]) {
|
|
|
+ (checkboxRefs.value[index] as any).toggle()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 处理答案变化,立即提交到后端
|
|
|
const handleAnswerChange = () => {
|
|
|
if (isClearingAnswer.value) return
|
|
|
- // 使用防抖,避免频繁调用接口
|
|
|
+ hasUnsavedChanges.value = true
|
|
|
+ const nodeToSubmit = currentNode.value
|
|
|
clearTimeout(debounceTimer.value)
|
|
|
+
|
|
|
+ const quType = String(nodeToSubmit.quType)
|
|
|
+ const delay = (quType === '4' || quType === '5') ? 500 : 100
|
|
|
debounceTimer.value = setTimeout(() => {
|
|
|
- submitAnswer()
|
|
|
- }, 500)
|
|
|
+ submitAnswer(nodeToSubmit)
|
|
|
+ }, delay) as any
|
|
|
}
|
|
|
// 答题失败时清理当前题已输入答案
|
|
|
const clearQuestionAnswer = (question: any) => {
|
|
|
@@ -199,6 +249,7 @@
|
|
|
quTypeVal: quTypeList[item.quType],
|
|
|
quScore: item.quScore,
|
|
|
answer: item.quType == 5 ? '' : [],
|
|
|
+ isCorrect: item.quType == 2 ? [] : '',
|
|
|
quContent: item.quContent
|
|
|
})
|
|
|
})
|
|
|
@@ -231,8 +282,9 @@
|
|
|
val.isCorrect = item.answer
|
|
|
} else {
|
|
|
// 多选 判断
|
|
|
- val.answer = item.answer.split(' ')
|
|
|
- val.isCorrect = item.answer.split(' ')
|
|
|
+ let validAnswers = item.answer ? item.answer.split(' ').filter((x: any) => x && x.trim() !== '') : []
|
|
|
+ val.answer = validAnswers
|
|
|
+ val.isCorrect = validAnswers
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
@@ -279,8 +331,8 @@
|
|
|
const onClickRight = () => {
|
|
|
router.go(-1)
|
|
|
}
|
|
|
- const submitAnswer = async () => {
|
|
|
- let curAnswer = currentNode.value
|
|
|
+ const submitAnswer = async (node = currentNode.value) => {
|
|
|
+ let curAnswer = node
|
|
|
let params = {
|
|
|
epId: epId,
|
|
|
answerId: answerId,
|
|
|
@@ -292,40 +344,54 @@
|
|
|
// 填空题
|
|
|
params['fillAnswer'] = curAnswer.quContent.map((item: any) => ({
|
|
|
name: setFillNo(item.name),
|
|
|
- answer: item.content
|
|
|
+ answer: item.content || ''
|
|
|
}))
|
|
|
} else if (curAnswer.quType == '5') {
|
|
|
// 问答题
|
|
|
- params['answer'] = curAnswer.answer
|
|
|
+ params['answer'] = curAnswer.answer || ''
|
|
|
} else if (curAnswer.quType == '1' || curAnswer.quType == '3') {
|
|
|
// 单选
|
|
|
params['answer'] = curAnswer.isCorrect || ''
|
|
|
} else {
|
|
|
// 多选
|
|
|
- params['answer'] = curAnswer.isCorrect.join(' ')
|
|
|
+ params['answer'] = Array.isArray(curAnswer.isCorrect) ? curAnswer.isCorrect.filter((x: any) => x && String(x).trim() !== '').join(' ') : ''
|
|
|
}
|
|
|
- if (params.answer.length || params.fillAnswer.length) {
|
|
|
- const [err]: ToResponse = await to(trainingApi.submitDocAmswer(params))
|
|
|
- if (err) {
|
|
|
- clearQuestionAnswer(curAnswer)
|
|
|
- }
|
|
|
+
|
|
|
+ activeRequestCount.value++
|
|
|
+ const [err]: ToResponse = await to(trainingApi.submitDocAmswer(params))
|
|
|
+ activeRequestCount.value--
|
|
|
+ if (err) {
|
|
|
+ clearQuestionAnswer(curAnswer)
|
|
|
+ return false
|
|
|
}
|
|
|
+ hasUnsavedChanges.value = false
|
|
|
+ return true
|
|
|
}
|
|
|
const handlePre = async () => {
|
|
|
- // 不再重复提交答案,因为选择答案时已经实时提交了
|
|
|
+ if (isPreDisabled.value) return
|
|
|
if (curQuNo.value >= 1) {
|
|
|
curQuNo.value--
|
|
|
+ checkboxRefs.value = []
|
|
|
currentNode.value = sortQuList.value[curQuNo.value]
|
|
|
}
|
|
|
}
|
|
|
const handleNext = async () => {
|
|
|
- // 不再重复提交答案,因为选择答案时已经实时提交了
|
|
|
- if (curQuNo.value < sortQuList.value.length) {
|
|
|
+ if (isNextDisabled.value) return
|
|
|
+ if (curQuNo.value < sortQuList.value.length - 1) {
|
|
|
curQuNo.value++
|
|
|
+ checkboxRefs.value = []
|
|
|
currentNode.value = sortQuList.value[curQuNo.value]
|
|
|
}
|
|
|
}
|
|
|
const handleSubmitAllAnswer = async (auto: any) => {
|
|
|
+ if (!auto && isSubmitDisabled.value) return
|
|
|
+
|
|
|
+ if (hasUnsavedChanges.value) {
|
|
|
+ clearTimeout(debounceTimer.value)
|
|
|
+ const success = await submitAnswer(currentNode.value)
|
|
|
+ if (!success && !auto) return // 手动交卷如果当前题目提交失败,阻断
|
|
|
+ }
|
|
|
+
|
|
|
let params = {
|
|
|
answerId: answerId,
|
|
|
auto: true
|