YNUtdPlatform/pages/realName/workbench/exam/examDetail.vue

751 lines
20 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 class="page">
<u-navbar class="u-navbar" :title="pageTitle" placeholder @leftClick="leftClick" leftIconColor="#fff" bgColor="#00337A" :titleStyle="{ color: '#FFF', fontSize: '32rpx' }"/>
<!-- <u-loading-page :loading="isloading"></u-loading-page> -->
<view class="exam-info">
<view style="font-weight: bold;">考试时长{{count}}</view>
<view style="font-weight: bold;"><text style="color: #06E7A3;">{{hIndex+1}}</text>/{{questionList.length}}</view>
</view>
<scroll-view class="scroll-view" scroll-x="true">
<view class="scroll-view-item" v-for="(item,index) in numList" :key="index" :class="hIndex == index ? 'active' : ''" @click="changeQuestion(index)">
{{item+1}}
</view>
</scroll-view>
<scroll-view class="content" scroll-y="true">
<view class="view-box">
<view class="title-view">
<text v-if="questionInfo.questionType==1">单选题<text style="color: #666;">(10分)</text></text>
<text v-if="questionInfo.questionType==2">多选题<text style="color: #666;">(10分)</text></text>
<text v-if="questionInfo.questionType==3">判断题<text style="color: #666;">(10分)</text></text>
</view>
<view class="exam-view">
<view style="width: 100%;height: auto;font-size: 32rpx;">题目:{{questionInfo.questionContent}}</view>
<view style="width: 100%;height: auto;margin-bottom: 20rpx;" v-if="questionInfo.questionPath">
<image :src="`${filePathHeader}${questionInfo.questionPath}`" mode="" style="width: 100%;height: 200rpx;"></image>
</view>
<view class="option-item" v-for="(oItem,oIndex) in questionInfo.optionList" :key="oIndex">
<view class="option-content">
<view style="display:flex;">
<view style="font-weight: bold;">选项{{changeNumtoLetter(oIndex)}}:</view>
<view style="margin-left: 20rpx;">{{oItem.optionContent}}</view>
</view>
<view v-if="oItem.isAnswer==1" style="margin-right:40rpx;">
<image src="../../../../static/realName/right_answer.png" mode="" style="width: 30rpx;height: 36rpx"></image>
</view>
</view>
<view class="option-img" v-if="oItem.optionPath">
<image :src="`${filePathHeader}${oItem.optionPath}`" mode="" style="width: 100%;height: 200rpx;"></image>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="submit-box">
<u-button type="primary" shape="surround" @click="up" class="submit-btn" v-if="hIndex>0" style="background:#ccc;border:0;">上一题</u-button>
<!-- <u-button type="primary" shape="surround" @click="sumbitExam" class="submit-btn" tyle="background:#5193FE" v-if="hIndex==numList.length-1" >提交</u-button> -->
<u-button type="primary" shape="surround" @click="next" class="submit-btn" tyle="background:#5193FE" v-if="hIndex<numList.length-1">下一题</u-button>
</view>
<!-- 弹窗 -->
<u-popup :show="showPopup" mode="center" @close="closePopup" :closeOnClickOverlay="false">
<view style="width:650rpx;height: 100%;position: relative;background-color: #fff;">
<view class="popup-content">
<view class="popup-header">
<view style="width: 85%;height: 80rpx;line-height: 80rpx;font-weight: bold;text-align: center;font-size: 32rpx;">
<text>考试结果</text>
<text v-if="examResult==1" style="color: #06E7A3;">(通过)</text>
<text v-else style="color: red;">(未通过)</text>
</view>
</view>
<view class="popup-view">
<view class="view-item">
<view class="label">考试得分</view>
<view >{{examScore}}</view>
</view>
<view class="view-item">
<view class="label">答对题数</view>
<view >{{trueNum}}</view>
</view>
<view class="view-item">
<view class="label">答题时长</view>
<view >{{cost}}</view>
</view>
</view>
</view>
<view class="popup-submit-box">
<u-button shape="square" class="submit-edit-1" @click="leftClick">关 闭</u-button>
<u-button type="primary" shape="square" class="submit-edit-2" @click="reExam()">重新考试</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import config from '@/config'
export default {
data() {
return {
timme: "",
count: "",
cost: "",
seconds: 7200,
costTime: 0,
pageTitle:'安全培训考试',
filePathHeader:config.realFileUrl,
examInfo:{},
trainInfo:{},
types:'',
foreignId:'',
idNumber:uni.getStorageSync('realNameUser').idNumber,
userId:uni.getStorageSync('realNameUser').userId,
examStartTime:"",
hIndex:0,
questionList:[],
questionInfo:{},
numList:[],
selectedList:[],//已选择过的题目集合
showMoreBtn:false,
showPopup:false,
trueNum:"",
examScore:"",
examResult:"",
}
},
onLoad(option) {
this.examInfo=JSON.parse(option.examInfo);
this.trainInfo=JSON.parse(option.trainInfo);
this.types=option.types;
this.foreignId=option.foreignId;
// console.log('🚀 ~ realNameUser:',this.trainInfo)
// this.examStartTime=this.timeFormat(null,'yyyy-mm-dd hh:MM:ss');
this.getExamList()
// this.seconds=Number(this.examInfo.examDuration);
// this.costTime=0;
// this.Time();
// this.count = "00:00:00"
},
onShow() {
},
methods: {
// 计算时间差 如答题时长、使用时长等,取到秒,毫秒四舍五入
jssjc() {
let kssj = new Date(this.questionInfo.examStartTime).getTime()
let jssj = new Date(this.questionInfo.examEndTime).getTime()
let sjc = jssj - kssj // 计算时间差的毫秒数
// 计算相差天数
let day = Math.floor(sjc / (24 * 60 * 60 * 1000)) // 向下取整
// 除了相差天数剩余的毫秒数
let syhm = sjc % (24 * 60 * 60 * 1000) // 取余方法
// 计算相差小时
let hour = Math.floor(syhm / (60 * 60 * 1000))
// 计算剩余时间戳
let syhm1 = syhm % (60 * 60 * 1000)
// 计算剩余分钟
let min = Math.floor(syhm1 / (60 * 1000))
// 剩余毫秒
let syhm2 = syhm1 % (60 * 1000)
// 计算相差秒
let sec = Math.round(syhm2 / 1000) // Math.round() 四舍五入取整
if (sec > 59) {
sec = 0
min ++
}
if (min > 59) {
min = 0
hour ++
}
if (hour > 23) {
hour = 0
day ++
}
hour = hour < 10 ? "0" + hour : hour;
min = min < 10 ? "0" + min : min;
sec = sec < 10 ? "0" + sec : sec;
this.count = hour + ":" + min + ":" + sec;
console.log('相差',`${day}天${hour}小时${min}分钟${sec}秒`,sjc);
},
// 倒计时
countDown() {
// let time =uni.getStorageSync("times");
let time = this.seconds;
if (time != null && time >= 0) this.seconds = parseInt(time);
let h = parseInt((this.seconds / (60 * 60)) % 24);
h = h < 10 ? "0" + h : h;
let m = parseInt((this.seconds / 60) % 60);
m = m < 10 ? "0" + m : m;
let s = parseInt(this.seconds % 60);
s = s < 10 ? "0" + s : s;
this.count = h + ":" + m + ":" + s;
},
// 计时
keepCostTime() {
// let time =uni.getStorageSync("costTime");
let time = this.costTime;
if (time != null && time >= 0) this.costTime = parseInt(time);
let h = parseInt((this.costTime / (60 * 60)) % 24);
h = h < 10 ? "0" + h : h;
let m = parseInt((this.costTime / 60) % 60);
m = m < 10 ? "0" + m : m;
let s = parseInt(this.costTime % 60);
s = s < 10 ? "0" + s : s;
this.cost = h + ":" + m + ":" + s;
},
Time() {
this.countDown();
this.timme = setInterval(() => {
this.seconds--;
this.costTime++;
// uni.setStorageSync("times", this.seconds)
// uni.setStorageSync("costTime", this.costTime)
this.countDown();
this.keepCostTime();
}, 1000);
},
// 获取试卷信息
getExamList(){
let param={
type:this.types,
completeId:this.examInfo.completeId,
examPaperId:this.examInfo.examPaperId,
}
console.log(param)
uni.request({
url: config.realExamUrl+'/exam/selectExamPagerById',
method: 'post',
data: param,
header: {
'Content-Type': 'application/json',
Authorization: uni.getStorageSync('realNameToken')
},
success: res => {
console.log(res)
res = res.data;
if(res.code==200){
this.questionList=res.data;
this.questionInfo=this.questionList[0];
this.jssjc()
this.hIndex=0;
this.numList = res.data.map((item,index)=>{
return index
})
this.selectedList=[]
}
},
fail: err => {
console.log(err)
}
})
},
// 改变题号
changeQuestion(index){
console.log(this.selectedList)
this.showMoreBtn=false
this.hIndex=index;//题号栏
this.questionInfo = this.questionList[index];
},
up(){
this.changeQuestion(this.hIndex-1)
},
next(){
this.changeQuestion(this.hIndex+1)
},
//选中选项
chosenOption(item){
console.log(this.questionInfo)
console.log(item)
if(this.questionInfo.questionType==1||this.questionInfo.questionType==3){//单选或判断直接提交·
//记录已选过题目的答案,方便回显
let obj={
"questionId": this.questionInfo.questionId,
"questionOptionId":item.questionOptionId,
"isAnswer":item.isAnswer,
}
var index = this.selectedList.findIndex(item => {return item.questionId == obj.questionId})
if (index != -1) {//当前题目有操作历史则更新
this.selectedList[index].questionOptionId=obj.questionOptionId;
this.selectedList[index].isAnswer=obj.isAnswer;
}else{//当前题目无操作历史则添加
this.selectedList.push(obj)
}
this.questionInfo.optionList.forEach(ite=>{
ite.isChecked=false;
if(ite.questionOptionId==obj.questionOptionId){
ite.isChecked=!ite.isChecked;
}
})
// this.sumbitAnswer()
}else{
console.log(this.questionInfo.optionList)
this.questionInfo.optionList.forEach(ite=>{
if(ite.questionOptionId==item.questionOptionId){
if(ite.isChecked){
ite.isChecked=!ite.isChecked;
}else{
ite.isChecked=true
}
}
})
this.showMoreBtn=true;
let obj={
"questionId": this.questionInfo.questionId,
"questionOptionId":item.questionOptionId,
"isAnswer":item.isAnswer
}
var index = this.selectedList.findIndex(item => {return item.questionId == obj.questionId})
if (index == -1) {//当前题目无操作历史则添加
this.selectedList.push(obj)
}else{//当前题目有操作历史则更新
var index2 = this.selectedList.findIndex(item => {return item.questionOptionId == obj.questionOptionId})
if (index2 == -1) {
this.selectedList.push(obj)
}else{
this.selectedList.splice(index2,1)
}
}
}
},
//提交答案接口
sumbitAnswer(){
var param={}
var index = this.selectedList.findIndex(item => {return item.questionId == this.questionInfo.questionId})
if(this.questionInfo.questionType==1||this.questionInfo.questionType==3){
param={
"questionId": this.selectedList[index].questionId,
"ownAnswer": this.selectedList[index].questionOptionId,
"isRight": this.selectedList[index].isAnswer
}
this.questionInfo.isSelect=param.ownAnswer;
}else{
if(index==-1){
uni.showToast({
title: "请选择选项!",
icon: 'none'
})
}else{
param={
"questionId": this.questionInfo.questionId,
"ownAnswer": this.selectedList[index].questionOptionId,
"isRight": "0"
}
var arr=[]
this.selectedList.forEach(item => {if(item.questionId==this.questionInfo.questionId){arr.push(item.questionOptionId)}})
param.ownAnswer=arr.join(",")
//添加属性记录选择答案便于计算已答题目未答题目
this.questionInfo.isSelect=param.ownAnswer;
var arr2=[]
this.questionInfo.optionList.forEach(item => {if(item.isAnswer==1){arr2.push(item.questionOptionId)}})
if(this.equalsArray(arr,arr2)){
param.isRight="1"
}else{
param.isRight="0"
}
}
}
console.log(param)
uni.request({
url: config.realExamUrl+'/exam/uploadExamPaper',
method: 'post',
data: param,
header: {
'Content-Type': 'application/json',
Authorization: uni.getStorageSync('realNameToken')
},
success: res => {
console.log(res)
res = res.data;
if(res.code==200){
this.next()
}else{
uni.showToast({
title: "接口异常",
icon: 'none'
})
}
},
fail: err => {
uni.showToast({
title: "接口异常",
icon: 'none'
})
console.log(err)
}
})
},
//提交试卷
sumbitExam(){
let sum=this.numList.length;//总数
let hasNum=0//已答
this.questionList.forEach(item=>{
if(item.isSelect){
hasNum=hasNum+1
}
})
let unNum=sum-hasNum;//未答
let param={
idNumber:this.idNumber,
type:this.types,
paperType:this.examInfo.paperType,
completeId:this.examInfo.completeId,
particularsId:this.examInfo.particularsId,
postId:this.trainInfo.postId,
proId:this.trainInfo.proId,
einTime:this.trainInfo.einTime,
examPaperId:this.examInfo.examPaperId,
examStartTime:this.examStartTime,
examEndTime:this.timeFormat(null,'yyyy-mm-dd hh:MM:ss'),
userId:this.userId,
}
console.log(param)
uni.showModal({
title: `已选择题目数:${hasNum},未选择题目数:${unNum},确认提交吗?`,
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
uni.request({
url: config.realExamUrl+'/exam/uploadCompleteExam',
method: 'post',
data: param,
header: {
'Content-Type': 'application/json',
Authorization: uni.getStorageSync('realNameToken')
},
success: res => {
console.log(res)
res = res.data;
if(res.code==200){
clearInterval(this.timme);
this.trueNum=res.data.trueNum;
this.examScore=res.data.examScore;
this.examResult=res.data.examResult;
this.showPopup=true;
}else{
uni.showToast({
title: "接口异常",
icon: 'none'
})
}
},
fail: err => {
uni.showToast({
title: "接口异常",
icon: 'none'
})
console.log(err)
}
})
}
}
});
},
closePopup(){
this.showPopup=false;
},
reExam(){
this.examStartTime=this.timeFormat(null,'yyyy-mm-dd hh:MM:ss');
this.getExamList()
this.seconds=Number(this.examInfo.examDuration);
this.costTime=0;
this.Time();
this.showPopup=false;
},
goStudy(item){
console.log(item)
uni.navigateTo({
url: `/pages/realName/workbench/exam/trainPdf?pdfInfo=${JSON.stringify(item)}&trainInfo=${JSON.stringify(this.trainInfo)}&types=${this.types}&foreignId=${this.foreignId}`
})
},
goExam(item){
console.log(item)
uni.navigateTo({
url: `/pages/realName/workbench/exam/examInfo?examInfo=${JSON.stringify(item)}`
})
},
// 返回
leftClick() {
console.log('返回')
uni.navigateBack({
delta: 1 // 返回
});
},
equalsArray(a, b) {
var m = new Map();
a.forEach(o => m.set(o, (m.get(o) || 0) + 1));
b.forEach(o => m.set(o, (m.get(o) || 0) - 1));
for (var value of m.values()) {
if (value !== 0) { return false }
}
return true
},
//选项下标变为字母
changeNumtoLetter(index){
let str="ABCDEFGHIJKLMGOPQISTUVWXYZ";
return str[index]
},
timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
let date
// 若传入时间为假值,则取当前时间
if (!dateTime) {
date = new Date()
}
// 若为unix秒时间戳则转为毫秒时间戳逻辑有点奇怪但不敢改以保证历史兼容
else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
date = new Date(dateTime * 1000)
}
// 若用户传入字符串格式时间戳new Date无法解析需做兼容
else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
date = new Date(Number(dateTime))
}
// 处理平台性差异在Safari/Webkit中new Date仅支持/作为分割符的字符串时间
// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
date = new Date(dateTime.replace(/-/g, '/'))
}
// 其他都认为符合 RFC 2822 规范
else {
date = new Date(dateTime)
}
const timeSource = {
'y': date.getFullYear().toString(), // 年
'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
'd': date.getDate().toString().padStart(2, '0'), // 日
'h': date.getHours().toString().padStart(2, '0'), // 时
'M': date.getMinutes().toString().padStart(2, '0'), // 分
's': date.getSeconds().toString().padStart(2, '0') // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
}
for (const key in timeSource) {
const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
if (ret) {
// 年可能只需展示两位
const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
}
}
return formatStr
},
},
onBackPress(options) {
console.log(options)
if (options.from === 'backbutton') {
// 来自手势返回
console.log('手势返回');
this.leftClick()
// 返回为 true 时,不会执行返回操作,可以自定义返回逻辑
// 返回为 false 或者不返回时,则执行默认返回操作
return true;
}
// 返回为 false 或者不返回时,则执行默认返回操作
return false;
},
destroyed() {
clearInterval(this.timme);
}
}
</script>
<style lang="scss">
/deep/ .uni-stat__select {
width:420rpx !important;
.uni-select__input-text {
width: 420rpx !important;
}
}
/deep/ .u-navbar__content__right__text{
color: #FFF;
}
.scroll-view {
white-space: nowrap;
width: 100%;
background: #FFF;
.scroll-view-item {
display: inline-block;
width: 60rpx;
height: 60rpx;
margin: 10rpx 20rpx;
line-height: 60rpx;
text-align: center;
border: 1rpx solid #39A0FD;
.activeLine{
background: #00337A;
border-radius: 10upx;
width: 160rpx;
height: 6upx;
}
}
.active {
color: #FFF;
background: #39A0FD;
font-weight: bolder;
font-size: 32rpx;
}
}
.page {
width: 100vw;
height: 100vh;
background-color: #FFF;
box-sizing: border-box;
.exam-info{
width: 94%;
height: 48rpx;
margin: 20rpx auto;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
}
.content{
width: 100%;
height: 70vh;
margin-top: 20rpx;
padding-bottom: 20rpx;
background: #FFF;
.view-box{
width: 100%;
height: auto;
margin: 20rpx auto;
background-color: #FFF;
border-radius: 10rpx;
.title-view{
font-size: 32rpx;
font-weight: 600;
margin-left: 20rpx;
}
.exam-view{
width: 94%;
margin: 10rpx auto;
height: auto;
padding-bottom: 20rpx;
.exam-item{
width: 96%;
margin: 10rpx auto;
height: auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 0;
}
.option-item{
width: 94%;
margin: 30rpx auto;
height: auto;
white-space: normal;
background: #F4F9FF;
padding: 20rpx 10rpx;
border-radius: 10rpx;
.option-content{
width: 96%;
margin: 10rpx auto;
padding: 0 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
}
.active{
background: #00337A;
color: #FFF;
}
}
}
}
.submit-box{
width: 94%;
height: auto;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
.submit-btn{
width: 45%;
}
}
.exam-button{
width: 40%;
height: 60rpx;
line-height: 60rpx;
text-align: center;
margin: 0 auto;
background-color:#F99F01;
color: #FFF;
padding:10rpx 10rpx;
border-radius: 20rpx;
}
.popup-header{
width: 100%;
height: 80rpx;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.popup-content{
width: 100%;
height: 32vh;
background: url('../../../../static/realName/exam_result_bg.png') no-repeat;
background-size: 100% 100%;
}
.popup-view{
width: 94%;
margin: 20rpx auto;
border-radius: 10rpx;
padding-top: 20rpx;
}
.view-item{
width: 90%;
margin: 0rpx auto;
padding:20rpx;
display: flex;
justify-content: space-between;
font-size: 32rpx;
font-weight: bold;
color: #FFF;
.label{
width: 160rpx;
color: #FFF;
}
}
.popup-submit-box {
width: 100%;
height: 100rpx;
position: absolute;
bottom: 0;
display: flex;
padding-bottom: 20rpx;
justify-content: space-between;
align-items: center;
background-color: #FFF;
.submit-edit-1{
width:45%;
margin: 0 auto;
height: 65rpx;
font-size: 26rpx;
background: #F5F5F5;
}
.submit-edit-2{
width: 45%;
margin: 0 auto;
height: 65rpx;
font-size: 26rpx;
background: #0052D9;
}
}
}
</style>