fix:问卷整改

1、增加查看已答题数据时,显示总分和每题得分,以及每题正确答案
2、增加倒计时、说明
This commit is contained in:
2026-10-30 16:49:50 +08:00
parent 35877f4b5c
commit 6c6caa2558
@@ -41,6 +41,30 @@ layout("/layouts/platform.html"){
box-sizing: border-box;
}
.qsv-online-answer-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-right: 16px;
}
.qsv-online-quiz-timer {
flex-shrink: 0;
color: #e6a23c;
font-weight: 600;
}
.qsv-online-description {
line-height: 1.8;
overflow-wrap: break-word;
}
.qsv-online-description img {
max-width: 100%;
height: auto;
}
.qsv-online-preview-title {
margin: 0;
font-size: 18px;
@@ -90,6 +114,33 @@ layout("/layouts/platform.html"){
min-width: 72px;
}
.qsv-online-result-score {
color: #409eff;
font-size: 28px;
font-weight: 700;
}
.qsv-online-result-tags {
margin-bottom: 12px;
}
.qsv-online-result-option {
color: #303133;
}
.qsv-online-result-option.is-correct {
color: #67c23a;
}
.qsv-online-subject-result {
margin-top: 12px;
color: #f56c6c;
}
.qsv-online-subject-result.is-correct {
color: #67c23a;
}
.qsv-online-option-fill {
width: 520px;
max-width: 100%;
@@ -495,8 +546,22 @@ layout("/layouts/platform.html"){
size="1145px"
custom-class="qsv-online-drawer"
append-to-body
@closed="stopVoteCountdown">
@close="stopAnswerTimers">
<div slot="title" class="qsv-online-answer-header">
<span>{{answerDialog.title}}</span>
<span v-if="answerDialog.activity.category === 'QUIZ' && answerDialog.activity.timeLimit > 0 && !answerDialog.readonly" class="qsv-online-quiz-timer">
<i class="el-icon-time"></i> 剩余时间:{{quizRemainingText}}
</span>
</div>
<div class="qsv-online-preview-body" v-loading="answerDialog.loading">
<div v-if="answerDialog.activity.description" class="qsv-online-subject">
<div class="qsv-online-subject-title">说明</div>
<div class="qsv-online-description" v-html="answerDialog.activity.description"></div>
</div>
<div v-if="showQuizResult" class="qsv-online-subject">
<div class="qsv-online-subject-title">{{answerDialog.activity.title}}</div>
<div class="qsv-online-result-score">总分:{{quizTotalScore}} 分</div>
</div>
<div v-if="isVoteActivity" class="qsv-online-vote">
<div class="qsv-online-vote-header">
<h2 class="qsv-online-vote-title">{{answerDialog.activity.title}}</h2>
@@ -573,6 +638,11 @@ layout("/layouts/platform.html"){
</div>
<el-empty v-if="!isVoteActivity && visibleSubjects.length === 0 && !answerDialog.loading" description="暂无题目"></el-empty>
<div v-if="!isVoteActivity" v-for="(subject, index) in visibleSubjects" :key="subject.id" class="qsv-online-subject">
<div v-if="showQuizResult" class="qsv-online-result-tags">
<el-tag v-if="subject.type === 'radio'" size="small">单选</el-tag>
<el-tag v-else-if="subject.type === 'checkbox'" size="small">多选</el-tag>
<el-tag v-if="subject.score !== undefined && subject.score !== null" size="small">{{subject.score}}分</el-tag>
</div>
<div class="qsv-online-subject-title">
<span v-if="answerDialog.showIndex" class="qsv-online-subject-index">{{index + 1}}.</span>
<span>{{subject.title}}</span>
@@ -585,7 +655,7 @@ layout("/layouts/platform.html"){
:value="getRadioValue(subject)"
@input="setRadioValue(subject, $event)">
<template v-for="option in subject.options">
<el-radio :key="option.id" :label="option.id">{{option.text}}</el-radio>
<el-radio :key="option.id" :label="option.id"><span :class="{ 'qsv-online-result-option': showQuizResult, 'is-correct': showQuizResult && option.isCorrect }">{{option.text}}<span v-if="showQuizResult && option.isCorrect">(正确答案)</span></span></el-radio>
<el-input
v-if="isOptionFillVisible(subject, option)"
:key="option.id + '-fill'"
@@ -604,7 +674,7 @@ layout("/layouts/platform.html"){
:disabled="answerDialog.readonly"
@change="onCheckboxChange(subject)">
<template v-for="option in subject.options">
<el-checkbox :key="option.id" :label="option.id">{{option.text}}</el-checkbox>
<el-checkbox :key="option.id" :label="option.id"><span :class="{ 'qsv-online-result-option': showQuizResult, 'is-correct': showQuizResult && option.isCorrect }">{{option.text}}<span v-if="showQuizResult && option.isCorrect">(正确答案)</span></span></el-checkbox>
<el-input
v-if="isOptionFillVisible(subject, option)"
:key="option.id + '-fill'"
@@ -623,6 +693,10 @@ layout("/layouts/platform.html"){
:readonly="answerDialog.readonly"
v-model="subject.userFillContent"
:placeholder="'请输入' + subject.title"></el-input>
<div v-if="showQuizResult && getSubjectResult(subject)" class="qsv-online-subject-result" :class="{ 'is-correct': getSubjectResult(subject).isCorrect }">
<i :class="getSubjectResult(subject).isCorrect ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
{{getSubjectResult(subject).isCorrect ? '回答正确' : '回答错误'}},得分:{{getSubjectResult(subject).score || 0}} 分
</div>
</div>
</div>
<div class="qsv-online-footer">
@@ -709,6 +783,10 @@ layout("/layouts/platform.html"){
minutes: 0,
seconds: 0
},
quizTimer: null,
quizStartedAt: 0,
quizElapsedSeconds: 0,
quizExpired: false,
voteTimer: null,
voteRankVisible: false,
voteDetailVisible: false,
@@ -717,6 +795,21 @@ layout("/layouts/platform.html"){
}
},
computed: {
// 仅已完成的计分答题显示成绩和正确答案,普通调查与投票保持原有展示。
showQuizResult() {
return this.answerDialog.activity.category === "QUIZ" && !!this.answerDialog.answerRecord.isFinish
},
quizRemainingSeconds() {
return Math.max(0, Number(this.answerDialog.activity.timeLimit || 0) * 60 - this.answerDialog.quizElapsedSeconds)
},
quizRemainingText() {
const seconds = this.quizRemainingSeconds
return Math.floor(seconds / 60) + '分' + String(seconds % 60).padStart(2, '0') + '秒'
},
quizTotalScore() {
const answers = this.answerDialog.answerRecord.extJson || {}
return Object.keys(answers).reduce((total, id) => total + Number(answers[id].score || 0), 0)
},
visibleSubjects() {
return this.answerDialog.subjects.filter((subject) => {
return this.isSubjectVisible(subject)
@@ -862,6 +955,10 @@ layout("/layouts/platform.html"){
this.$message.warning(unavailableMessage)
return
}
this.stopAnswerTimers()
this.answerDialog.quizStartedAt = 0
this.answerDialog.quizElapsedSeconds = 0
this.answerDialog.quizExpired = false
this.answerDialog.visible = true
this.answerDialog.loading = true
this.answerDialog.readonly = row.actionType === "view"
@@ -908,6 +1005,7 @@ layout("/layouts/platform.html"){
this.answerDialog.answerRecord = res.data || {}
this.answerDialog.readonly = !!this.answerDialog.answerRecord.isFinish
this.initAnswer()
this.startQuizTimer()
}
})
},
@@ -928,6 +1026,10 @@ layout("/layouts/platform.html"){
this.clearHiddenSubjectAnswers()
}
},
getSubjectResult(subject) {
const answers = this.answerDialog.answerRecord.extJson || {}
return answers[subject.id]
},
getRadioValue(subject) {
return subject.userSelectOptionIds && subject.userSelectOptionIds.length > 0
? subject.userSelectOptionIds[0]
@@ -985,6 +1087,43 @@ layout("/layouts/platform.html"){
}
})
},
// 使用实际经过时间计时,避免切换浏览器标签后定时器节流导致倒计时变慢。
startQuizTimer() {
this.stopQuizTimer()
if (!this.answerDialog.visible || this.answerDialog.readonly || this.answerDialog.activity.category !== "QUIZ") return
this.answerDialog.quizStartedAt = Date.now()
this.answerDialog.quizElapsedSeconds = 0
this.answerDialog.quizExpired = false
this.answerDialog.quizTimer = setInterval(() => this.updateQuizTimer(), 1000)
},
updateQuizTimer() {
if (!this.answerDialog.quizStartedAt || this.answerDialog.readonly || !this.answerDialog.visible) return
this.answerDialog.quizElapsedSeconds = Math.floor((Date.now() - this.answerDialog.quizStartedAt) / 1000)
if (Number(this.answerDialog.activity.timeLimit) > 0 && this.quizRemainingSeconds === 0
&& !this.answerDialog.quizExpired && !this.answerDialog.submitLoading) {
this.answerDialog.quizExpired = true
this.stopQuizTimer()
// 与手机端一致:超时提示重新答题,不自动提交未完成的答卷。
this.$alert("答题时间到,请重新进入答题", "提示", {
confirmButtonText: "确定",
showClose: false,
closeOnClickModal: false,
closeOnPressEscape: false
}).then(() => {
this.answerDialog.visible = false
})
}
},
stopQuizTimer() {
if (this.answerDialog.quizTimer) {
clearInterval(this.answerDialog.quizTimer)
this.answerDialog.quizTimer = null
}
},
stopAnswerTimers() {
this.stopQuizTimer()
this.stopVoteCountdown()
},
startVoteCountdown() {
this.stopVoteCountdown()
this.updateVoteCountdown()
@@ -1196,6 +1335,8 @@ layout("/layouts/platform.html"){
return true
},
submitAnswer() {
this.updateQuizTimer()
if (this.answerDialog.readonly || this.answerDialog.quizExpired || this.answerDialog.submitLoading) return
if (!this.validateAnswer()) {
return
}
@@ -1204,11 +1345,14 @@ layout("/layouts/platform.html"){
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.updateQuizTimer()
if (!this.answerDialog.visible || this.answerDialog.readonly || this.answerDialog.quizExpired || this.answerDialog.submitLoading) return
this.answerDialog.submitLoading = true
$.post("/platform/qsv/online/submitAnswer", {
answer: JSON.stringify({
activityId: this.answerDialog.activity.id,
answerRecordId: this.answerDialog.answerRecordId,
answerTime: this.answerDialog.quizElapsedSeconds,
subjects: this.submitSubjects.map((subject) => {
return {
id: subject.id,
@@ -1221,7 +1365,14 @@ layout("/layouts/platform.html"){
}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.answerDialog.visible = false
if (this.answerDialog.activity.category === "QUIZ") {
// 提交后读取服务端判题结果,直接展示成绩并防止重复提交。
this.answerDialog.readonly = true
this.stopQuizTimer()
this.loadAnswerRecord()
} else {
this.answerDialog.visible = false
}
this.pageData()
} else {
this.$message.error(res.msg)
@@ -1237,7 +1388,7 @@ layout("/layouts/platform.html"){
this.openAnswerFromUrl()
},
beforeDestroy() {
this.stopVoteCountdown()
this.stopAnswerTimers()
}
})
</script>