Files
learning-system-mobile/pages/study/exam.vue
2025-07-15 15:06:14 +08:00

472 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!--考试页面用户在此页面一个试题一个试题的回答-->
<view>
<u-toast ref="messager"></u-toast>
<page-title :showBack="false">课程考试</page-title>
<view>
<view style="display: flex;justify-content: space-between;padding: 20upx;">
<view style="padding-top: 10upx;color: #757575; ">{{curIndex+1}} / {{total}}</view>
<view style="color: #000000;">
<text style="font-size: 40upx;" :class="{'redText':timerValue<5}">{{timerValue}}</text> 分钟
</view>
</view>
<!--试题内容-->
<view class="qitem">
<view class="qitem-info">
<view> [{{getTypeName(curItem.type)}}]{{curItem.content}}</view>
<view class="qimg" v-if="curItem.images"><img class="qimg-fit" :src="imageBaseUrl+curItem.images"/></view>
</view>
<view v-for="(opt,optIdx) in curItem.options" :key="optIdx">
<view class="qitem-opts">
<view class="qitem-opt" :class="{check:opt.checked}" @click="chooseOption(opt)">
{{toLetter(optIdx+1)}}.{{opt.content}}
<u-icon v-if="opt.checked" name="checkbox-mark" color="#00aa00"></u-icon>
</view>
<view v-if="opt.images" class="qimg">
<img class="qimg-fit" :src="imageBaseUrl+opt.images"/>
</view>
</view>
</view>
</view>
</view>
<view style="height: 50px;"></view>
<view v-if="!stop" class="bottom-btns">
<u-button type="success" text="提交" class="sub" :disabled="!thisTrue" @click="present"></u-button>
<u-button type="info" text="上一题" @click="prevSub" class="next" v-if="curIndex>0"></u-button>
<u-button type="primary" text="下一题" @click="nextSub" class="next" v-if="curIndex<(total-1)"></u-button>
</view>
<u-modal :show="stop"
@confirm="confirmStop"
@cancel="cancelStop"
ref="stopModal"
content="时间已到,是否提交答卷? 否:不提交答卷返回课程页面,是:提交答卷并返回课程页面"
confirmText="是"
:showCancelButton="true"
cancelText="否"
:asyncClose="true">
</u-modal>
<u-modal :show="scoreShow"
@confirm="confirmFinish"
title="您本次得分">
<view class="slot-content">
<view style="text-align: center;font-size: 60rpx;">{{lastScore}}</view>
</view>
</u-modal>
</view>
</template>
<script>
import config from '@/config/index.js'
import apiCourseStudy from '@/api/modules/courseStudy.js'
import apiCourse from '@/api/modules/course.js';
import apiExamPaper from '@/api/modules/paper.js';
import {formatDate,getQuestionType,correctJudgment,numberToLetter} from '@/utils/tools.js';
export default {
data() {
return {
thisTrue: true,
studyId:'',
examId:'',
ctype:10,
startTime:null,
toLetter:numberToLetter,
getTypeName:getQuestionType,
imageBaseUrl:config.fileUrl,
info:{},
stop:false,
paper:[],
total:0,
curIndex:0,
curItem:{},
timer:null,
scoreShow:false,
lastScore:0,
timerValue:0,
noAnswers:[] //有未答完的试题
}
},
onLoad(options){
this.studyId=options.studyId;
this.examId=options.ccid;
this.ctype=options.ctype;
if(this.examId){
this.loadExamInfo();
}
// this.debouncedPresent = this.debounce(this.present, 500);
// this.confirmStop = this.debounce(this.submitTest,500)
},
methods: {
changeTimer(){
if(this.timerValue==0){
window.clearInterval(this.timer);
//系统自动提交
this.stop=true;
}else{
this.timerValue--;
}
},
loadExamInfo(){//加载考试信息
this.curIndex=0;
apiCourse.getExam(this.examId).then(res=>{
if(res.status==200){
this.info=res.result;
this.loadStudyItemId();
if(this.info.paperType==2){
// apiExamPaper.getPaperContent(this.info.paperId).then(rs=>{
apiExamPaper.getPcPaperContent(this.info.id).then(rs=>{
if(rs.status==200){
//console.log(rs.result)
let paperItems=JSON.parse(rs.result);
//console.log(this.examPaper.json,'this.examPaper.json');
let qitems=this.convertToItems(paperItems);
this.paper ={items:qitems};
this.total=qitems.length;
this.curItem=qitems[this.curIndex];
this.startTime=new Date();//记录开始时间
this.timerValue=this.info.testDuration;
this.timer=setInterval(this.changeTimer,60000);
}else{
this.$message.error('加载试卷内容失败,请与管理员联系,试卷是否已删除');
}
})
}else{
let paper= JSON.parse(this.info.paperContent);
paper.items.forEach(item=>{
//console.log(item);
if(item.type==101){
item.userAnswer='';
}else if(item.type==102){
item.userAnswer=[];
}else{
item.userAnswer=''
}
item.options.forEach(opt=>{
opt.checked=false;
})
})
this.total=paper.items.length;
this.paper =paper;
//console.log(this.paper);
this.curItem=paper.items[this.curIndex];
this.startTime=new Date();//记录开始时间
this.timerValue=res.result.testDuration;
this.timer=setInterval(this.changeTimer,60000);
}
}else if(res.status==404){
//没有找到考试信息
}else{
//this.$message.error(res.message);
}
})
},
//独立考试的试题转化为课内考试的试题
convertToItems(questions){
let qitems=[];
questions.forEach(item=>{
let q={
id:item.id,
type:item.type==1? 101:item.type==2? 102:103,
score:item.defaultScore,
checked:false,
userAnswer:'',
optShow:true,
content:item.title,
images:item.images,
options:[]
}
if(item.type==3){
q.options.push({
id:item.id+'1',
images:'',
content:"正确",
answer:item.answer=='true'? true:false,
checked:false
});
q.options.push({
id:item.id+'2',
images:'',
content:"错误",
answer:item.answer=='true'? false:true,
checked:false
})
}else{
item.optionList.forEach(opt=>{
q.options.push({
id:opt.id,
images:opt.images,
content:opt.content,
answer:opt.isAnswer,
checked:false
})
});
}
if(q.type==102){
q.userAnswer=[];
}
qitems.push(q);
});
console.log(qitems,'qitems')
return qitems;
},
loadStudyItemId(){
//获取studyItemId;
apiCourseStudy.getStudyContentItem(this.studyId,this.info.contentId).then(rs=>{
if(rs.status==200){
this.studyItemId=rs.result.id;
}
})
},
chooseOption(opt){
if(this.curItem.type==101 || this.curItem.type==103){
this.curItem.options.forEach(op=>{
op.checked=false;
})
opt.checked=true;
}else{
if(opt.checked){
opt.checked=false;
}else{
opt.checked=true;
}
}
},
confirmStop(){
this.submitTest()
},
cancelStop(){
//应该返回课程面页
this.confirmFinish();
},
prevSub(){
if(this.curIndex==0){
return;
}
this.curIndex--;
if(this.curIndex>-1){
this.curItem=this.paper.items[this.curIndex];
}
},
nextSub() {
if(this.curIndex>=(this.total-1)){
return;
}
this.curIndex++;
this.curItem=this.paper.items[this.curIndex];
},
countTest(){ //计算课程的分数
//console.log(this.paper.items);
let totalScore=0;
this.paper.items.forEach(item => {
item.score=parseInt(item.score);
totalScore+=item.score;//加到总分中
if(item.type != 102){
item.userAnswer='';
item.options.forEach(opt => {
if(opt.checked){
item.userAnswer=opt.id;
}
});
}else{
item.userAnswer=[];
item.options.forEach(opt => {
if(opt.checked){
item.userAnswer.push(opt.id);
}
});
}
});
let scoreNum = 0;//用户得分
let noAnswers=[];//是否都已答
this.paper.items.forEach((item,idx) => {
if(item.type != 102){
if(item.userAnswer==''){
noAnswers.push(idx+1);
}
item.options.forEach(it => {
if(it.answer && item.userAnswer == it.id) {
scoreNum+=item.score
}
})
}else{
if(item.userAnswer.length==0){
noAnswers.push(idx+1);
}
let allRight = true;
item.options.forEach(it =>{
if(it.answer){ //正确答案
if(item.userAnswer.indexOf(it.id)==-1){
allRight=false;
}
}else{
if(item.userAnswer.indexOf(it.id)>-1){
allRight=false;
}
}
});
if(allRight){
scoreNum+=item.score;
}
}
})
this.noAnswers=noAnswers;
if(scoreNum === null)scoreNum=0;
this.lastScore=scoreNum;
//转化成百分制显示
if(this.info.percentScore){
this.lastScore=parseInt(scoreNum*100/totalScore);
}
return this.lastScore;
},
submitTest(){
let now=new Date();
let postData={
studyId:this.studyId,//
studyItemId:this.studyItemId,//此值没有的,需要单独的查询了来
courseId:this.info.courseId,
contentId:this.info.contentId,
testId:this.info.id,
testName:''+this.info.testName,//应该是课程的名称 + 内容的名称
testDuration:0,
arrange:this.info.arrange,
passLine:this.info.passLine,
randomMode:this.info.randomMode,
score:this.lastScore,//分数需要计算,在检查是否填写完整性时就可以计算出分数
paperJson:JSON.stringify(this.paper),//原来是对象,这里要也要对象
startTime:formatDate(this.startTime),//此时间需要格式化,格式化时间可以放在util中
//endTime:formatDate(now),
}
//计划考试的时长
var dateDiff = now.getTime() - this.startTime.getTime();//时间差的毫秒数
var minutes=Math.floor(dateDiff/(1000))//计算相差秒数分钟记录的太大经常为0
postData.testDuration=minutes;
this.thisTrue = false
apiCourseStudy.saveExam(postData).then(res=>{
this.thisTrue = true
if(res.status == 200) {
this.studyItemId=res.result.studyItemId;
this.scoreShow=true;
} else {
this.$refs.messager.show({message:res.message,type:'error'});
}
})
},
debounce(func, delay) {
let timerId;
return function (...args) {
if (timerId) clearTimeout(timerId);
timerId = setTimeout(() => {
func.apply(this, args);
timerId = null;
}, delay);
};
},
present(){
let $this=this;
let score=this.countTest();
//console.log(score)
//console.log(this.noAnswers)
if(this.noAnswers.length>0){
uni.showModal({
title: '友情提示',
content: '还有未答试题,您确定要提交吗?',
success(res) {
if (res.confirm) {
$this.submitTest()
}
}
});
}else{
this.submitTest()
}
},
confirmFinish(){
//这里应该是返回课程的页面
uni.redirectTo({
url:'/pages/study/courseStudy?id='+this.info.courseId + '&contentId='+this.examId
})
// if(this.ctype==10){
// uni.redirectTo({
// url:'/pages/resource/microDetail?id='+this.info.courseId + '&exam=1'
// })
// }else if(this.ctype==20){
// uni.redirectTo({
// url:'/pages/study/onlineCourse?id='+this.info.courseId
// })
// }
}
}
}
</script>
<style lang="scss" scoped>
.redText{
color:#ff0000;
}
.footer {
position: fixed;
bottom: 0px;
width: 100%;
height: 80upx;
line-height: 80upx;
border-top: 1px solid #d9d9d9;
background-color: #FFFFFF;
}
.column{
color: #838383;
font-size: 40upx;
padding: 10px 20px;
}
.qitem {
padding: 10px 20px;
.qitem-info {
font-size: 1.2em;
padding: 10px 0px;
}
.qitem-opts {
padding: 5px 0px;
.qitem-opt {
display: flex;
margin-bottom: 10px;
padding: 20px 15px;
background-color: #FFFFFF;
border-radius: 6px;
}
.check{
color:#00aa00;
}
}
}
.bottom-btns{
padding: 10px;
position: fixed;
width: 100%;
bottom: 0px;
display: flex;
justify-content: space-around;
}
.qimg{
//padding-left: 30px;
width:100%;
text-align: left;
.qimg-fit{
width:100%;
object-fit:scale-down
}
}
</style>