提交 41be0d08 authored 作者: 王鹏飞's avatar 王鹏飞

课程考试支持多次考试

上级 a979c5a3
......@@ -211,15 +211,30 @@ export default class CourseAction extends BaseACTION {
type: 102
})
if (cur.course_examination) {
let courseExamChildren = []
if (data.exist_examination.length > 1) {
courseExamChildren = data.exist_examination.map((item, index) => {
const map = ['一', '二', '三']
return {
name: `第${map[index]}次考试`,
id: 'course_exam',
offset: index,
sid: sid,
cid: cid,
examId: cur.course_examination,
type: 101
}
})
}
json.tabs1ChapterList.course.push({
title: '课程考试',
isUp: true,
chapters: [],
chapters: courseExamChildren,
id: 'course_exam',
sid: sid,
cid: cid,
examId: cur.course_examination,
type: 102
type: 101
})
}
/* 课程考核 考核标准文案读取 */
......
<template>
<container :title="detail.title" v-loading="loading">
<template v-slot:header-aside v-if="isExamComplete">分数:{{exam.score.total}}</template>
<container :title="exam.title" v-loading="!loaded">
<template v-slot:header-aside>
<template v-if="isCompleted">分数:{{ exam.score.total }}</template>
<template v-else>考试时间:{{ status.start_time }} ~ {{ status.terminate_time }}</template>
</template>
<div class="exam">
<template v-if="status.examination_status === '00'">
<div class="no-exam">暂无考试</div>
</template>
<template v-else-if="isSubmited && !isExamComplete">
<template v-else-if="isSubmited && !isCompleted && !isMultipleExams">
<div class="no-exam">试卷批改中,请耐心等待</div>
</template>
<template v-else>
<!-- 考试期间,未开始考试 -->
<div class="exam-welcome" v-if="!isStartExam">
<div>考试时间:{{status.start_time}} ~ {{status.terminate_time}}</div>
<el-button
type="primary"
:disabled="!isExamTime"
@click="onStartExam"
>{{startExamButtonText}}</el-button>
</div>
<!-- 考试试题 -->
<div class="exam-form" v-if="isStartExam">
<el-form :disabled="isSubmited">
<div class="exam-form" v-if="loaded">
<el-form :disabled="!canEditable">
<template v-for="items in questions">
<exam-item
v-for="(item, index) in items"
......@@ -28,16 +22,27 @@
:type="item.type"
:data="item"
:value="item.formModel"
:disabled="isSubmited"
:disabled="!canEditable"
:showResult="isCompleted"
:key="item.id"
></exam-item>
</template>
<div class="exam-buttons">
</el-form>
<div class="exam-buttons">
<!-- 允许多次提交 -->
<template v-if="isMultipleExams">
<el-button type="primary" @click="handlePrev" v-if="hasPrev">上一套试卷</el-button>
<el-button type="primary" @click="handleNext" v-if="hasNext">下一套试卷</el-button>
<el-button type="primary" @click="handleNewExam" v-if="hasResubmit">再考一次</el-button>
</template>
<template v-if="!(isSubmited && isMultipleExams)">
<el-tooltip effect="dark" content="提交之后就不能修改了哦" placement="right">
<el-button type="primary" :loading="submitLoading" @click="onSubmit">{{submitText}}</el-button>
<el-button type="primary" :disabled="!canEditable" :loading="submitLoading" @click="onSubmit">{{
submitText
}}</el-button>
</el-tooltip>
</div>
</el-form>
</template>
</div>
</div>
</template>
</div>
......@@ -75,16 +80,26 @@ export default {
},
data() {
return {
loading: false,
loaded: false,
detail: {},
status: {},
questions: [],
messageInstance: null,
exam: {},
isStartExam: false, // 是否开始考试
autoSubmitTimer: null, // 自动提交定时器
checkStatusTimer: null, // 考试状态定时器
submitLoading: false
submitLoading: false,
isMultipleExams: false, // 是否可以多次考试
maxExams: 3, // 最多考试几次
examCount: this.data.exist_examination.length || 0 // 试卷数量
}
},
watch: {
offset: {
immediate: true,
handler() {
this.init()
}
}
},
computed: {
......@@ -105,46 +120,95 @@ export default {
// 大于开始时间,小于结束时间
return this.status.examination_status === '20'
},
// 考试按钮
startExamButtonText() {
return this.status.examination_status === '90' ? '考试结束' : '开始考试'
// 是否提交
isSubmited() {
return this.exam.type === 1 || this.exam.type === 2
},
// 考试完成
isExamComplete() {
isCompleted() {
// 考试完成,批改完成并且公布成绩
return this.exam.is_published === 1 && this.exam.type === 2
},
// 是否提交
isSubmited() {
return this.exam.type === 1 || this.exam.type === 2
// 可以编辑
canEditable() {
return !this.isSubmited && this.isExamTime
},
// 提交按钮文本
submitText() {
return this.isSubmited ? '已提交' : '提交'
},
// 试卷页码
offset() {
const { query } = this.$route
return parseInt(query.offset) || 0
},
// 是否显示上一套试题
hasPrev() {
return !!this.offset
},
// 是否显示下一套试题
hasNext() {
return this.offset < this.examCount - 1
},
// 是否显示再考一次
hasResubmit() {
if (this.examList.length >= this.maxExams) {
return false
}
// 判断状态是否还有未提交的试题
for (const exam of this.examList) {
if (!['1', '2'].includes(exam.status)) {
return false
}
}
return true
// return this.isSubmited && this.isExamTime && this.examCount < this.maxExams
},
// 已存在的试题列表
examList() {
return this.data.exist_examination
}
},
methods: {
// 开始考试
onStartExam() {
this.isStartExam = true
// 自动提交答题
this.autoSubmit()
// 初始化
init() {
this.clearTimer()
// 自动获取考试状态
this.autoCheckExamStatus()
// 获取试题
this.getExam()
},
// 获取考试状态
getExamStatus() {
api.getCourseExamStatus(this.sid, this.cid, this.pid).then(response => {
this.status = response
if (this.isSubmited || response.examination_status === '90') {
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
}
})
},
// 自动获取考试状态
autoCheckExamStatus() {
// 获取试题状态
this.getExamStatus()
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
this.checkStatusTimer = setInterval(this.getExamStatus, 5000)
},
// 获取试题
getDetail(callback) {
this.loading = true
getExam() {
this.loaded = false
api
.getCourseExam(this.sid, this.cid)
.getCourseExamResult(this.sid, this.cid, this.pid, { offset: this.offset })
.then(response => {
this.detail = Array.isArray(response) ? null : response
// 设置问题列表数据
this.questions = this.detail
? this.genQuestions(this.detail.examination)
: []
callback && callback()
this.exam = response
this.questions = this.genQuestions(response.sheet)
// 自动提交
this.canEditable && this.autoSubmit()
// 更新菜单
this.isMultipleExams && this.$emit('update')
})
.finally(() => {
this.loading = false
this.loaded = true
})
},
// 组装问题数据
......@@ -176,9 +240,7 @@ export default {
type: 3,
formModel: {
id: item.id,
user_answer: item.user_answer
? Base64.decode(item.user_answer.replace(/ /gi, '+'))
: '',
user_answer: item.user_answer ? Base64.decode(item.user_answer.replace(/ /gi, '+')) : '',
attachments: item.attachments || []
}
}
......@@ -187,39 +249,7 @@ export default {
return [...radioList, ...checkboxList, ...shortAnswerList]
})
},
// 获取考试状态
getExamStatus() {
api.getCourseExamStatus(this.sid, this.cid, this.pid).then(response => {
this.status = response
if (this.isSubmited || response.examination_status === '90') {
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
}
})
},
// 自动获取考试状态
autoCheckExamStatus() {
// 获取试题状态
this.getExamStatus()
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
this.checkStatusTimer = setInterval(() => {
this.getExamStatus()
}, 3000)
},
// 获取考试结果
getExamResult() {
api.getCourseExamResult(this.sid, this.cid, this.pid).then(response => {
// 设置问题列表数据
if (response.code !== 8001) {
this.isStartExam = true
this.exam = response
this.questions = this.genQuestions(response.sheet)
// 自动提交
if (this.isStartExam && !this.isSubmited && !this.isExamComplete) {
this.autoSubmit()
}
}
})
},
// 提交校验
checkSubmit() {
for (let i = 0; i < this.questions.length; i++) {
......@@ -289,6 +319,7 @@ export default {
// 请求提交接口
handleSubmitRequest(params) {
this.submitLoading = true
params.offset = this.offset
api
.submitCourseExam(this.sid, this.cid, this.pid, params)
.then(response => {
......@@ -299,7 +330,7 @@ export default {
if (response.code === 200) {
this.$message.success('考试答卷提交成功')
this.autoSubmitTimer && clearInterval(this.autoSubmitTimer)
this.getExamResult()
this.getExam()
} else {
this.$message.error(response.data.error)
}
......@@ -310,20 +341,34 @@ export default {
.finally(() => {
this.submitLoading = false
})
},
// 上一套试卷
handlePrev() {
const offset = this.offset - 1
this.$router.push({ query: { offset } })
},
handleNext() {
const offset = this.offset + 1
this.$router.push({ query: { offset } })
},
handleNewExam() {
this.$router.push({ query: { offset: this.examCount } })
this.examCount++
},
// 清除定时器
clearTimer() {
this.autoSubmitTimer && clearInterval(this.autoSubmitTimer)
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
}
},
beforeMount() {
// 获取试题
this.getDetail(() => {
// 获取考试结果
this.getExamResult()
// 自动获取考试状态
this.autoCheckExamStatus()
})
// // 自动获取考试状态
// this.autoCheckExamStatus()
// // 获取试题
// this.getExam()
},
destroyed() {
this.autoSubmitTimer && clearInterval(this.autoSubmitTimer)
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
this.clearTimer()
}
}
</script>
......@@ -333,7 +378,7 @@ export default {
padding: 40px 0;
text-align: center;
.el-button {
width: 240px;
min-width: 160px;
margin: 40px auto;
}
}
......
<template>
<div class="q-item">
<div class="q-item-hd">
<div class="q-item-num">{{index + 1}}.</div>
<div class="q-item-num">{{ index + 1 }}.</div>
<div class="q-item-title" v-html="data.content"></div>
<div class="q-item-aside">
<template v-if="typeText">({{typeText}})</template>
<template v-if="data.hasOwnProperty('score')">({{data.score}}分)</template>
<template v-if="typeText">({{ typeText }})</template>
<template v-if="data.hasOwnProperty('score')">({{ data.score }}分)</template>
</div>
</div>
<div class="q-item-bd">
<!-- 单选 -->
<el-radio-group v-model="currentValue.user_answer" v-if="type === 1">
<div class="q-option-item" v-for="item in currentOptions" :key="item.id">
<el-radio :class="genClass(item)" :label="item.id">{{item.abc_option}}</el-radio>
<el-radio :class="genClass(item)" :label="item.id">{{ item.abc_option }}</el-radio>
</div>
</el-radio-group>
<!-- 多选 -->
<el-checkbox-group v-model="currentValue.user_answer" v-if="type === 2">
<div class="q-option-item" v-for="item in currentOptions" :key="item.id">
<el-checkbox :class="genClass(item)" :label="item.id">{{item.abc_option}}</el-checkbox>
<el-checkbox :class="genClass(item)" :label="item.id">{{ item.abc_option }}</el-checkbox>
</div>
</el-checkbox-group>
<!-- 简答题 -->
......@@ -27,39 +27,34 @@
<v-upload :disabled="disabled" v-model="currentValue.attachments">请上传对应的文件附件:</v-upload>
</template>
</div>
<div class="q-item-ft" v-if="disabled">
<div class="q-item-ft" v-if="disabled && showResult">
<template v-if="type === 3">
<p v-if="data.check_comment">
<span>评语:</span>
<span>{{data.check_comment}}</span>
<span>{{ data.check_comment }}</span>
</p>
</template>
<template v-else>
<div class="result">
<p>
<span>学生答案:</span>
<span :class="isCorrect ? 'is-success' : 'is-error'">{{submitAnswerText}}</span>
<span :class="isCorrect ? 'is-success' : 'is-error'">{{ submitAnswerText }}</span>
</p>
<p>
<span>正确答案:</span>
<span>{{correctAnswerText}}</span>
<span>{{ correctAnswerText }}</span>
</p>
</div>
</template>
<p v-if="data.hasOwnProperty('get_score')">
<span>评分:</span>
<span>{{data.get_score}}分</span>
<span>{{ data.get_score }}分</span>
</p>
<div class="analyze" v-if="data.analysis">
<span>解析:</span>
<div class="analyze-main">
<span style="color:blue;cursor:pointer;" @click="showAnalyze = !showAnalyze">查看解析</span>
<div
v-html="data.analysis"
v-if="data.analysis"
v-show="showAnalyze"
class="analyze-content"
></div>
<span style="color: blue; cursor: pointer" @click="showAnalyze = !showAnalyze">查看解析</span>
<div v-html="data.analysis" v-if="data.analysis" v-show="showAnalyze" class="analyze-content"></div>
</div>
</div>
</div>
......@@ -94,7 +89,8 @@ export default {
}
},
// 是否禁用,提交过的是禁用状态
disabled: { type: Boolean, default: false }
disabled: { type: Boolean, default: false },
showResult: { type: Boolean, default: true }
},
data() {
return {
......@@ -135,16 +131,12 @@ export default {
item.abc_option = `${this.A_Z[index]}. ${item.option}`
// 提交时的选中状态
const value = this.value.user_answer || ''
item.selected = Array.isArray(value)
? value.includes(item.id)
: value === item.id
item.selected = Array.isArray(value) ? value.includes(item.id) : value === item.id
// 处理正确的选中状态
const hasChecked = Object.prototype.hasOwnProperty.call(item, 'checked')
const rightAnswer = this.data.right_answer || ''
if (!hasChecked && rightAnswer) {
item.checked = Array.isArray(rightAnswer)
? rightAnswer.includes(item.id)
: rightAnswer === item.id
item.checked = Array.isArray(rightAnswer) ? rightAnswer.includes(item.id) : rightAnswer === item.id
}
return item
})
......@@ -179,7 +171,7 @@ export default {
methods: {
// 生成class
genClass(item) {
if (!this.disabled) {
if (!this.disabled || !this.showResult) {
return null
}
return {
......
<template>
<component
:is="currentCompoent"
:chapter="chapter"
:data="data"
v-bind="$attrs"
v-on="$listeners"
v-if="chapter"
/>
<component :is="currentCompoent" :chapter="chapter" :data="data" v-bind="$attrs" v-on="$listeners" v-if="chapter" />
</template>
<script>
......
......@@ -35,6 +35,7 @@
:key="pid"
@pptupdate="handlePPTupdate"
@change-ppt="handleChangePPT(...arguments, false)"
@update="getCourse"
/>
</div>
</div>
......
......@@ -542,7 +542,7 @@ export default {
window.open(data.live.record_url || data.live.join_url)
return
}
this.$router.push({ name: 'viewerCourseChapter', params: { sid, cid, id: data.id } })
this.$router.push({ name: 'viewerCourseChapter', params: { sid, cid, id: data.id }, query: { offset: data.offset } })
},
/**
* 开始学习或继续学习 - 跳转到对应音视频播放页
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论