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

课程考试增加计时功能

上级 08328d13
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -29,6 +29,13 @@
"node": ">=8.9"
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/runtime-corejs3": "^7.11.2",
"acorn": "^7.1.1",
"ali-oss": "^6.11.2",
"autoprefixer": "^9.8.6",
......@@ -49,27 +56,20 @@
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.1.1",
"html-replace-webpack-plugin": "^2.5.6",
"html-webpack-plugin": "^4.5.0",
"mini-css-extract-plugin": "^0.9.0",
"postcss-loader": "^3.0.0",
"request": "^2.88.2",
"sass-loader": "^10.0.3",
"semver": "^1.1.4",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"vconsole-webpack-plugin": "^1.5.2",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2",
"html-replace-webpack-plugin": "^2.5.6",
"request": "^2.88.2",
"semver": "^1.1.4",
"vconsole-webpack-plugin": "^1.5.2",
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-jsx": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/runtime-corejs3": "^7.11.2"
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@ckeditor/ckeditor5-build-classic": "^24.0.0",
......
......@@ -42,7 +42,7 @@
<template v-if="isRevised">
<div class="paper-check">
<p>{{ $t('viewerWork.correctionTime') }}{{ detail.checker_time }}</p>
<div class="paper-check-item">
<div class="paper-check-item" v-if="hasScore">
<b>{{ $t('viewerWork.score') }}</b>
{{ detail.score }}
</div>
......@@ -156,6 +156,10 @@ export default {
const endTime = +new Date(this.deadline)
const currentTime = new Date().getTime()
return currentTime < endTime
},
hasScore() {
// allow_score 1 显示 2隐藏
return this.detail.allow_score !== 2
}
},
methods: {
......
<template>
<container :title="exam.title" v-loading="!loaded">
<template v-slot:header-aside>
<container :title="status.title" v-loading="loading">
<template v-slot:header-aside v-if="isStartExam">
<template v-if="isCompleted">
{{ $t('viewerWork.fraction') }}{{ exam.score.total }}{{ $t('viewerWork.fractionUnit') }}
</template>
<template v-else>{{ $t('viewerWork.examTime') }}{{ status.start_time }} ~ {{ status.terminate_time }}</template>
<template v-else>
<div v-if="hasCountdown">倒计时:{{ countdownDurationText }}</div>
<div v-else>{{ $t('viewerWork.examTime') }}{{ status.start_time }} ~ {{ status.terminate_time }}</div>
</template>
</template>
<div class="exam">
<template v-if="status.examination_status === '00'">
......@@ -14,8 +17,16 @@
<div class="no-exam">{{ $t('viewerWork.examSubmitedTips') }}</div>
</template>
<template v-else>
<!-- 考试期间,未开始考试 -->
<div class="exam-welcome" v-if="!isStartExam">
<div class="inner">
<div>{{ $t('viewerWork.examTime') }}{{ status.start_time }} ~ {{ status.terminate_time }}</div>
<div v-if="hasCountdown">本场考试时间:{{ durationText }}<br />时间用尽自动交卷,请您注意答题时间。</div>
</div>
<el-button type="primary" :disabled="!isExamTime" @click="onStartExam">{{ startExamButtonText }}</el-button>
</div>
<!-- 考试试题 -->
<div class="exam-form" v-if="loaded">
<div class="exam-form" v-else>
<el-form :disabled="!canEditable">
<template v-for="items in questions">
<exam-item
......@@ -61,30 +72,21 @@ import Container from '../common/container.vue'
import ExamItem from './examItem.vue'
// api
import * as api from '../../api'
import tool from '@/tool/index'
// 章节测试题
// 课后考试
export default {
name: 'CourseExam',
components: { Container, ExamItem },
props: {
// 当前选中的章节
chapter: {
type: Object,
default() {
return {}
}
},
chapter: { type: Object, default: () => {} },
// 课程详情接口返回的数据
data: {
type: Object,
default() {
return {}
}
}
data: { type: Object, default: () => {} }
},
data() {
return {
loaded: false,
loading: false,
detail: {},
status: {},
questions: [],
......@@ -95,7 +97,9 @@ export default {
submitLoading: false,
isMultipleExams: false, // 是否可以多次考试
maxExams: 3, // 最多考试几次
examCount: this.data.exist_examination ? this.data.exist_examination.length : 0 // 试卷数量
countdownDuration: 0, // 倒计时剩余时间
countdownTimer: null,
isStartExam: false
}
},
watch: {
......@@ -104,6 +108,12 @@ export default {
handler() {
this.init()
}
},
examCount: {
immediate: true,
handler(value) {
this.isStartExam = !!value
}
}
},
computed: {
......@@ -124,6 +134,10 @@ 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
......@@ -156,7 +170,7 @@ export default {
},
// 是否显示再考一次
hasResubmit() {
if (this.examList.length >= this.maxExams) {
if (this.examCount >= this.maxExams) {
return false
}
// 判断状态是否还有未提交的试题
......@@ -166,11 +180,25 @@ export default {
}
}
return true
// return this.isSubmited && this.isExamTime && this.examCount < this.maxExams
},
// 已存在的试题列表
examList() {
return this.data.exist_examination
},
// 试卷数量
examCount() {
return this.examList ? this.examList.length : 0
},
// 是否有倒计时
hasCountdown() {
return !!this.status.duration && !this.isSubmited
},
// 倒计时文字
countdownDurationText() {
return tool.convertTime.durationToTimeString(this.countdownDuration)
},
durationText() {
return tool.convertTime.durationToTimeString2(this.status.duration)
}
},
methods: {
......@@ -180,11 +208,21 @@ export default {
// 自动获取考试状态
await this.autoCheckExamStatus()
// 获取试题
this.isStartExam && this.getExam()
},
// 开始考试
onStartExam() {
this.isStartExam = true
this.getExam()
},
// 获取考试状态
async getExamStatus() {
await api.getCourseExamStatus(this.sid, this.cid, this.pid).then(response => {
// examination_status
// 00: 考场未开放,不允许进入
// 10:考场开放,允许进入
// 20:开始答题
// 90:考试已结束
this.status = response
if (this.isSubmited || response.examination_status === '90') {
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
......@@ -200,7 +238,7 @@ export default {
},
// 获取试题
getExam() {
this.loaded = false
this.loading = true
api
.getCourseExamResult(this.sid, this.cid, this.pid, { offset: this.offset })
.then(response => {
......@@ -210,9 +248,13 @@ export default {
this.canEditable && this.autoSubmit()
// 更新菜单
this.isMultipleExams && this.$emit('update')
// 倒计时剩余时间
this.countdownDuration = this.status.duration - this.exam.duration
// 开始倒计时
this.hasCountdown && this.setCountdown()
})
.finally(() => {
this.loaded = true
this.loading = false
})
},
// 组装问题数据
......@@ -340,7 +382,9 @@ export default {
}
})
.catch(error => {
this.$message.error(error.message)
params.type && this.$message.error(error.message)
this.autoSubmitTimer && clearInterval(this.autoSubmitTimer)
this.getExam()
})
.finally(() => {
this.submitLoading = false
......@@ -357,12 +401,23 @@ export default {
},
handleNewExam() {
this.$router.push({ query: { offset: this.examCount } })
this.examCount++
},
// 清除定时器
clearTimer() {
this.autoSubmitTimer && clearInterval(this.autoSubmitTimer)
this.checkStatusTimer && clearInterval(this.checkStatusTimer)
this.countdownTimer && clearInterval(this.countdownTimer)
},
// 设置倒计时
setCountdown() {
this.countdownTimer && clearInterval(this.countdownTimer)
this.countdownTimer = setInterval(() => {
this.countdownDuration--
// if (this.countdownDuration <= 0) {
// this.getExam()
// this.clearTimer()
// }
}, 1000)
}
},
beforeMount() {
......@@ -392,9 +447,10 @@ export default {
text-align: center;
}
.exam-welcome {
padding: 40px;
display: flex;
flex-direction: column;
align-items: center;
line-height: 30px;
text-align: center;
::v-deep .el-button {
margin-top: 30px;
}
......
......@@ -28,7 +28,7 @@ export default {
fraction: '分数',
fractionUnit: '分',
examEmpty: '暂无考试',
examTime: '考试时间',
examTime: '考试有效时间',
examDeadline: '考试截止时间',
examSubmitedTips: '试卷批改中,请耐心等待',
examSubmitButtonTips: '提交之后就不能修改了哦',
......
......@@ -3,15 +3,32 @@ export default class ConvertTime {
* 工具方法 - 播放时间 转化 h:m:s
* @param {[string]} duration 时间戳
*/
durationToTimeString (duration) {
durationToTimeString(duration) {
const h = Math.floor(duration / 3600)
const m = Math.floor((duration - h * 3600) / 60)
const s = (duration - h * 3600 - m * 60) % 60
function tenify (a) {
function tenify(a) {
return a >= 10 ? a : '0' + a
}
const to = { h: tenify(h), m: tenify(m), s: tenify(s) }
const format = 'h:m:s'
return format.replace(/h|m|s/g, k => to[k]).replace(/^00:/, '')
}
durationToTimeString2(duration) {
const h = Math.floor(duration / 3600)
const m = Math.floor((duration - h * 3600) / 60)
const s = (duration - h * 3600 - m * 60) % 60
let output = ''
if (h) {
output += `${h}小时`
}
if (m) {
output += `${m}分钟`
}
if (s) {
output += `${s}秒`
}
return output
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论