2025-11-06 15:06:07 +08:00
< template >
< div class = "app-box" >
2025-11-10 15:27:34 +08:00
< image src = "/packageB/static/images/train/bj.jpg" class = "bjImg" mode = "" > < / image >
2025-11-06 15:06:07 +08:00
< div class = "con-box" >
< div class = "header" >
2025-11-07 17:30:16 +08:00
< div style = "font-weight: 600;font-size: 32rpx;" > { { rows . name } } < / div >
< div class = "headerCon" >
< div > 考试时长 : { { rows . timeLimit } } 分钟 < / div >
< div > { { formattedTime } } < / div >
< div class = "headBtn" @click ="exit()" > 退出 < / div >
< / div >
2025-11-06 15:06:07 +08:00
< / div >
< div class = "problemCard" >
< div v-for = "(item,index) in problemData" :key="index" >
< template v-if = "questionIndex==(index+1)" >
< div class = "problemTitle" >
< span class = "titleType" v-if = "item.type=='single'" > 单选题 < / span >
< span class = "titleType" v-if = "item.type=='multiple'" > 多选题 < / span >
< span class = "titleType" v-if = "item.type=='judge'" > 判断题 < / span >
< span > { { item . content } } < / span >
< / div >
< div class = "options" v-if = "item.type=='single'" >
2025-11-07 17:30:16 +08:00
< div class = "opt" @click ="selected(i,index)" :class = "item.choice!==''&&i==item.choice?'active':''" v-for = "(val,i) in parseOptions(item.trainChooses)" >
2025-11-06 15:06:07 +08:00
< div class = "optLab" > { { indexToLetter ( i ) } } < / div >
< span > { { val } } < / span >
< / div >
< / div >
< div class = "options" v-if = "item.type=='multiple'" >
2025-11-07 17:30:16 +08:00
< div class = "opt" @click ="selected2(i,index)" :class = "judgment(i,index)?'active':''" v-for = "(val,i) in parseOptions(item.trainChooses)" >
2025-11-06 15:06:07 +08:00
< div class = "optLab" > { { indexToLetter ( i ) } } < / div >
< span > { { val } } < / span >
< / div >
< / div >
< div class = "options" v-if = "item.type=='judge'" >
2025-11-08 11:26:35 +08:00
< div class = "opt" @click ="selected3('正确',index)" :class = "item.choice=='正确'?'active':''" >
2025-11-06 15:06:07 +08:00
< span > 正确 < / span >
< / div >
2025-11-08 11:26:35 +08:00
< div class = "opt" @click ="selected3('错误',index)" :class = "item.choice=='错误'?'active':''" >
2025-11-06 15:06:07 +08:00
< span > 错误 < / span >
< / div >
< / div >
2025-11-07 17:30:16 +08:00
2025-11-06 15:06:07 +08:00
< div class = "problemBtns" >
2025-11-07 17:30:16 +08:00
< div :class = "(problemData[questionIndex-1].type=='multiple'?problemData[questionIndex-1].choice.length==0:problemData[questionIndex-1].choice==='')?'events':''" @click ="submit()" > 提交答案 < / div >
2025-11-06 15:06:07 +08:00
< / div >
< / template >
< / div >
< / div >
< div class = "footer" >
< div class = "footerLeft" >
< div class = "zuo" :class = "questionIndex==1?'events':''" @click ="questionIndex-=1" > < / div >
< div class = "you" :class = "questionIndex==problemData.length?'events':''" @click ="questionIndex+=1" > < / div >
2025-11-07 17:30:16 +08:00
2025-11-06 15:06:07 +08:00
< / div >
2025-11-07 17:30:16 +08:00
< div class = "footerBtn" @click ="complete()" > 完成练习 < / div >
2025-11-06 15:06:07 +08:00
< div class = "footerLeft" >
< div @click ="dialogVisible=true" >
< div > < span style = "color: #1CADF5;" > { { questionIndex } } < / span > / { { problemData . length } } < / div >
< div > 题号 < / div >
< / div >
< / div >
< / div >
< / div >
< div class = "cards" v-if = "dialogVisible" >
< div class = "cardCon" >
< div class = "cardHead" >
< div > 题号 < / div >
< div style = "font-size: 40rpx;" @click ="clones()" > × < / div >
< / div >
< div class = "questionNums" >
< div class = "questions" :class = "item.whether=='正确'?'questionCorrect':item.whether=='错误'?'questionError':questionIndex==(index+1)?'questionsActive':''" @click ="switchs(index)" v-for = "(item,index) in problemList" :key="index" > {{ index + 1 }} < / div >
< / div >
< / div >
< / div >
< / div >
< / template >
< script setup >
import { reactive , inject , watch , ref , onMounted , onBeforeUnmount , computed } from 'vue' ;
import { onLoad , onShow } from '@dcloudio/uni-app' ;
const { $api , urls , navTo , navBack , vacanciesTo , formatTotal , config } = inject ( 'globalFunction' ) ;
import useUserStore from '@/stores/useUserStore' ;
import useDictStore from '@/stores/useDictStore' ;
const userInfo = ref ( { } ) ;
2025-11-07 17:30:16 +08:00
const rows = ref ( { } ) ;
2025-11-06 15:06:07 +08:00
const Authorization = ref ( '' ) ;
const radio = ref ( '' ) ;
const radio2 = ref ( '' ) ;
const checkList = ref ( [ ] ) ;
const questionIndex = ref ( 1 ) ;
const correctIndex = ref ( 0 ) ;
const errorsIndex = ref ( 0 ) ;
const elapsedTime = ref ( 0 ) ;
const judgWhether = ref ( '' ) ;
const isRunning = ref ( false ) ;
const dialogVisible = ref ( false ) ;
const problemData = ref ( [ ] ) ;
const problemList = ref ( [ ] ) ;
let timer = null ;
const formattedTime = computed ( ( ) => {
const minutes = Math . floor ( elapsedTime . value / 60 )
const seconds = elapsedTime . value % 60
return ` ${ padTime ( minutes ) } : ${ padTime ( seconds ) } `
} ) ;
watch ( questionIndex , ( newVal , oldVal ) => {
radio . value = ""
radio2 . value = ""
checkList . value = [ ]
judgWhether . value = ""
} ) ;
// watch(problemData, (newVal, oldVal) => {
// problemList.value=[];
// newVal.forEach((item,i)=>{
// problemList.value.push({index:i+1,whether:""})
// })
// });
onLoad ( ( options ) => {
2025-11-07 17:30:16 +08:00
rows . value = options
2025-11-06 15:06:07 +08:00
Authorization . value = uni . getStorageSync ( 'Padmin-Token' ) || ''
getHeart ( ) ;
} ) ;
onShow ( ( ) => {
} )
onBeforeUnmount ( ( ) => {
if ( timer ) {
clearInterval ( timer ) ; // 清除定时器
timer = null ; // 防止内存泄漏,确保下次不会再误用已清除的定时器
}
} ) ;
function getHeart ( ) {
const raw = Authorization . value ;
const token = typeof raw === "string" ? raw . trim ( ) : "" ;
const headers = token ? { 'Authorization' : raw . startsWith ( "Bearer " ) ? raw : ` Bearer ${ token } ` } : { }
$api . myRequest ( "/dashboard/auth/heart" , { } , "POST" , 10100 , headers ) . then ( ( resData ) => {
if ( resData . code == 200 ) {
getUserInfo ( ) ;
} else {
navTo ( '/packageB/login' )
}
} ) ;
} ;
function getUserInfo ( ) {
let header = {
'Authorization' : Authorization . value
}
$api . myRequest ( '/system/user/login/user/info' , { } , 'get' , 10100 , header ) . then ( ( resData ) => {
userInfo . value = resData . info || { } ;
// userId.value=resData.info.userId
queryData ( )
} ) ;
} ;
function queryData ( ) {
problemData . value = [ ]
let header = {
'Authorization' : Authorization . value ,
'Content-Type' : "application/x-www-form-urlencoded"
}
2025-11-07 17:30:16 +08:00
if ( rows . value . types == 1 ) {
$api . myRequest ( '/train/public/trainExamDash/getExamTopicNoAnswer' , {
userId : userInfo . value . userId ,
examPaperId : rows . value . examPaperId
} , 'post' , 9100 , header ) . then ( ( resData ) => {
2025-11-08 11:26:35 +08:00
if ( resData && resData . code == 200 ) {
elapsedTime . value = ( rows . value . timeLimit * 60 )
resData . data . forEach ( ( item , i ) => {
if ( item . type == 'multiple' ) {
item . choice = [ ]
} else {
item . choice = ""
}
problemData . value . push ( item )
problemList . value . push ( { index : i + 1 , whether : "" } )
} )
start ( )
}
2025-11-07 17:30:16 +08:00
} ) ;
} else if ( rows . value . types == 2 ) {
$api . myRequest ( '/train/public/trainExamDash/continueExam' , {
userId : userInfo . value . userId ,
examPaperId : rows . value . examPaperId
} , 'post' , 9100 , header ) . then ( ( resData ) => {
if ( resData && resData . code == 200 ) {
elapsedTime . value = ( rows . value . timeLimit * 60 )
resData . data . forEach ( ( item , i ) => {
if ( item . type == 'multiple' ) {
if ( item . choosed ) {
item . choice = item . choosed . split ( "," ) ;
item . submitAnswers = true
} else {
item . choice = [ ]
2025-11-08 11:26:35 +08:00
item . submitAnswers = false
2025-11-07 17:30:16 +08:00
}
} else {
if ( item . choosed ) {
item . choice = item . choosed ;
item . submitAnswers = true
} else {
item . choice = ""
2025-11-08 14:28:24 +08:00
item . submitAnswers = false
2025-11-07 17:30:16 +08:00
}
}
problemData . value . push ( item )
problemList . value . push ( { index : i + 1 , whether : "" } )
} )
start ( )
}
} ) ;
}
2025-11-06 15:06:07 +08:00
}
function collect ( is ) {
let header = {
'Authorization' : Authorization . value ,
'Content-Type' : "application/x-www-form-urlencoded"
}
$api . myRequest ( '/train/public/questionUser/addOrUpdateCollect' , {
userId : userInfo . value . userId ,
questionId : problemData . value [ questionIndex . value - 1 ] . questionId ,
collect : is
} , 'post' , 9100 , header ) . then ( ( resData ) => {
problemData . value [ questionIndex . value - 1 ] . isCollect = is
} ) ;
} ;
2025-11-07 17:30:16 +08:00
//提交答案
function submit ( ) {
let indexs = questionIndex . value - 1
let parm = {
examPaperId : rows . value . examPaperId ,
questionId : problemData . value [ indexs ] . questionId ,
userId : userInfo . value . userId ,
answer : problemData . value [ indexs ] . type == 'multiple' ? problemData . value [ indexs ] . choice . join ( "," ) : problemData . value [ indexs ] . choice ,
examId : problemData . value [ indexs ] . examId
}
2025-11-06 15:06:07 +08:00
let header = {
'Authorization' : Authorization . value ,
2025-11-08 11:26:35 +08:00
'Content-Type' : "application/x-www-form-urlencoded"
2025-11-06 15:06:07 +08:00
}
2025-11-07 17:30:16 +08:00
$api . myRequest ( '/train/public/trainExamDash/submitAnswer' , parm , 'post' , 9100 , header ) . then ( ( resData ) => {
if ( resData && resData . code == 200 ) {
problemData . value [ indexs ] . submitAnswers = true
if ( problemData . value . length == questionIndex . value ) {
2025-11-08 11:26:35 +08:00
$api . msg ( '已经是最后一题了,请仔细检查后交卷!' )
2025-11-07 17:30:16 +08:00
} else {
questionIndex . value += 1
}
}
2025-11-06 15:06:07 +08:00
} ) ;
} ;
2025-11-07 17:30:16 +08:00
//完成练习
function complete ( ) {
let arr = [ ]
problemData . value . forEach ( ( item , index ) => {
if ( ! item . submitAnswers ) {
arr . push ( index + 1 )
}
} )
wx . showModal ( {
title : '提示' ,
content : ( arr . length > 0 ? '第' + arr . join ( "," ) + '题还未作答,' : '请仔细检查试卷 , ' ) + '确认是否交卷?' ,
success ( res ) {
if ( res . confirm ) {
onpaper ( )
} else if ( res . cancel ) {
console . log ( '用户点击取消' )
}
}
} )
2025-11-08 11:26:35 +08:00
2025-11-07 17:30:16 +08:00
} ;
function onpaper ( ) {
2025-11-06 15:06:07 +08:00
let indexs = questionIndex . value - 1
let parm = {
2025-11-07 17:30:16 +08:00
examPaperId : rows . value . examPaperId ,
2025-11-06 15:06:07 +08:00
userId : userInfo . value . userId ,
2025-11-07 17:30:16 +08:00
examId : problemData . value [ indexs ] . examId ,
useTime : ( rows . value . timeLimit * 60 ) - elapsedTime . value ,
2025-11-06 15:06:07 +08:00
}
let header = {
'Authorization' : Authorization . value ,
2025-11-08 11:26:35 +08:00
'Content-Type' : "application/x-www-form-urlencoded"
2025-11-06 15:06:07 +08:00
}
2025-11-07 17:30:16 +08:00
$api . myRequest ( '/train/public/trainExamDash/submitExam' , parm , 'post' , 9100 , header ) . then ( ( resData ) => {
2025-11-06 15:06:07 +08:00
if ( resData && resData . code == 200 ) {
2025-11-07 17:30:16 +08:00
$api . msg ( '提交成功!' )
navBack ( )
2025-11-06 15:06:07 +08:00
}
} ) ;
} ;
2025-11-07 17:30:16 +08:00
function selected ( i , index ) {
problemData . value [ index ] . choice = i
problemData . value = JSON . parse ( JSON . stringify ( problemData . value ) )
2025-11-06 15:06:07 +08:00
} ;
//多选
2025-11-07 17:30:16 +08:00
function selected2 ( i , indexs ) {
let arr = problemData . value [ indexs ] . choice . join ( "," )
2025-11-06 15:06:07 +08:00
if ( arr . indexOf ( i ) !== - 1 ) {
2025-11-07 17:30:16 +08:00
const index = problemData . value [ indexs ] . choice . indexOf ( i ) ;
2025-11-06 15:06:07 +08:00
if ( index !== - 1 ) {
2025-11-07 17:30:16 +08:00
problemData . value [ indexs ] . choice . splice ( index , 1 ) ;
2025-11-06 15:06:07 +08:00
}
} else {
2025-11-07 17:30:16 +08:00
problemData . value [ indexs ] . choice . push ( i )
2025-11-06 15:06:07 +08:00
}
2025-11-07 17:30:16 +08:00
problemData . value = JSON . parse ( JSON . stringify ( problemData . value ) )
2025-11-06 15:06:07 +08:00
} ;
2025-11-07 17:30:16 +08:00
function judgment ( i , indexs ) {
let arr = problemData . value [ indexs ] . choice . join ( "," )
2025-11-06 15:06:07 +08:00
if ( arr . indexOf ( i ) !== - 1 ) {
return true
} else {
return false
}
} ;
2025-11-07 17:30:16 +08:00
function selected3 ( i , index ) {
// radio2.value=i
problemData . value [ index ] . choice = i
problemData . value = JSON . parse ( JSON . stringify ( problemData . value ) )
2025-11-06 15:06:07 +08:00
} ;
// 解析选项
function parseOptions ( options ) {
if ( ! options ) return [ ] ;
// 假设options是字符串格式, 以分号分隔
if ( typeof options === 'string' ) {
return options . split ( ',' ) . filter ( opt => opt . trim ( ) ) ;
}
// 如果是数组,直接返回
if ( Array . isArray ( options ) ) {
return options ;
}
return [ ] ;
} ;
function indexToLetter ( index ) {
// 将索引转换为对应的字母
return String . fromCharCode ( 65 + index ) ;
} ;
function padTime ( time ) {
return time < 10 ? ` 0 ${ time } ` : time
} ;
function start ( ) {
if ( isRunning . value ) return
isRunning . value = true
timer = setInterval ( ( ) => {
2025-11-07 17:30:16 +08:00
elapsedTime . value -= 1
if ( elapsedTime . value == 0 ) {
// this.$message({
// message: '考试时间已结束,系统将自动交卷',
// type: 'warning'
// });
onpaper ( )
}
2025-11-06 15:06:07 +08:00
} , 1000 )
} ;
function clones ( ) {
dialogVisible . value = false
} ;
function switchs ( i ) {
questionIndex . value = ( i + 1 )
dialogVisible . value = false
} ;
function exit ( ) {
2025-11-08 11:26:35 +08:00
wx . showModal ( {
title : '提示' ,
content : '直接退出是不计分的, 确定要退出吗?' ,
success ( res ) {
if ( res . confirm ) {
navBack ( )
} else if ( res . cancel ) {
console . log ( '用户点击取消' )
}
}
} )
2025-11-06 15:06:07 +08:00
}
< / script >
< style lang = "stylus" scoped >
. app - box {
width : 100 % ;
height : 100 vh ;
position : relative ;
. bjImg {
position : absolute ;
width : 100 % ;
height : 100 % ;
}
. con - box {
position : absolute ;
width : 100 % ;
height : 100 % ;
left : 0 ;
top : 0 ;
z - index : 10 ;
padding : 20 rpx 28 rpx ;
box - sizing : border - box ;
. header {
2025-11-07 17:30:16 +08:00
height : 110 rpx ;
padding : 10 rpx 10 rpx 0 ;
2025-11-06 15:06:07 +08:00
background : linear - gradient ( 0 deg , # 4285 EC 0 % , # 0 BBAFB 100 % ) ;
color : # fff ;
font - size : 26 rpx ;
border - radius : 10 rpx
2025-11-07 17:30:16 +08:00
. headerCon {
display : flex ;
align - items : center ;
justify - content : space - between ;
. headBtn {
background : # 499 FFF ;
border - radius : 4 px ;
width : 100 rpx ;
text - align : center ;
height : 50 rpx ;
line - height : 50 rpx ;
}
2025-11-06 15:06:07 +08:00
}
2025-11-07 17:30:16 +08:00
2025-11-06 15:06:07 +08:00
}
. problemCard {
margin - top : 30 rpx ;
. problemTitle {
font - size : 30 rpx ;
. titleType {
display : inline - block ;
background - color : # 499 FFF ;
border - radius : 10 rpx 10 rpx 10 rpx 0 ;
padding : 8 rpx 12 rpx ;
color : # fff ;
font - size : 26 rpx ;
margin - right : 20 rpx ;
}
}
. options {
margin - top : 30 rpx ;
. opt {
height : 60 rpx ;
/* background-color: #F8F9FA; */
border - radius : 5 px ;
margin - bottom : 15 px ;
display : flex ;
align - items : center ;
padding - left : 30 rpx ;
box - sizing : border - box ;
color : # 808080 ;
font - size : 30 rpx ;
. optLab {
width : 40 rpx ;
height : 40 rpx ;
text - align : center ;
line - height : 40 rpx ;
border - radius : 50 % ;
background - color : # 8 C8C8C ;
color : # fff ;
font - weight : 600 ;
font - size : 32 rpx ;
margin - right : 20 rpx ;
}
}
. active {
background : linear - gradient ( 90 deg , # 25 A9F5 0 % , # B1DBFF 100 % ) ;
color : # fff ! important ;
font - weight : bold ;
}
. active > view {
background - color : # fff ! important ;
color : # 25 A9F5 ! important ;
}
}
. analysis {
margin - top : 30 rpx ;
background - color : # fff ;
border - radius : 6 px ;
margin - bottom : 15 rpx ;
border : 1 px solid # 10 A8FF ;
padding : 20 rpx ;
box - sizing : border - box ;
. analysisHead {
display : flex ;
align - items : center ;
justify - content : space - between ;
font - size : 32 rpx ;
font - family : Microsoft YaHei ;
font - weight : bold ;
letter - spacing : 2 px ;
}
. correct {
color : # 67 C23A ;
}
. errors {
color : # F06A6A ;
}
. analysisCon {
margin - top : 30 rpx ;
. parse1 {
font - size : 30 rpx ;
font - family : Microsoft YaHei ;
font - weight : bold ;
}
. parse2 {
font - size : 26 rpx ;
color : # 333 ;
margin - top : 10 px ;
}
}
}
. problemBtns {
display : flex
align - items : center
justify - content : center
view {
width : 140 rpx
height : 50 rpx
text - align : center
line - height : 50 rpx
background - color : # 10 A8FF
color : # fff
font - size : 28 rpx
border - radius : 5 rpx
margin - right : 10 rpx ;
}
}
}
. footer {
width : 100 % ;
height : 120 rpx ;
border - top : 1 px solid # ddd
position : fixed
bottom : 0
left : 0
display : flex
align - items : center
justify - content : space - between
. footerLeft {
display : flex
align - items : center
font - size : 30 rpx ;
text - align : center
. zuo {
width : 26 rpx ;
height : 26 rpx ;
border - top : 2 px solid # 666
border - left : 2 px solid # 666
transform : rotate ( - 45 deg ) ; /* 鼠标悬停时旋转360度 */
margin - left : 60 rpx ;
}
. you {
width : 26 rpx ;
height : 26 rpx ;
border - right : 2 px solid # 666
border - bottom : 2 px solid # 666
transform : rotate ( - 45 deg ) ; /* 鼠标悬停时旋转360度 */
// margin-left: 30rpx;
margin - right : 50 rpx ;
}
. icons {
width : 30 rpx ;
height : 30 rpx ;
color : # fff ;
text - align : center ;
line - height : 30 rpx ;
border - radius : 50 %
}
. texts {
font - size : 24 rpx ;
}
}
. footerLeft > view {
margin - right : 40 rpx ;
}
. footerBtn {
width : 140 rpx
height : 50 rpx
text - align : center
line - height : 50 rpx
background - color : # 67 C23A
color : # fff
font - size : 28 rpx
border - radius : 5 rpx
}
}
}
. cards {
position : fixed ;
left : 0 ;
top : 0 ;
width : 100 % ;
height : 100 vh ;
background - color : rgba ( 0 , 0 , 0 , 0.5 ) ;
z - index : 1000 ;
padding : 100 rpx 50 rpx ;
box - sizing : border - box ;
. cardCon {
height : 100 % ;
background - color : # fff ;
padding : 20 rpx ;
box - sizing : border - box ;
. cardHead {
display : flex ;
align - items : center ;
justify - content : space - between ;
font - size : 30 rpx ;
font - weight : 600 ;
}
. questionNums {
width : 100 % ;
display : flex ;
flex - wrap : wrap ;
}
. questions {
width : 60 rpx ;
height : 60 rpx ;
text - align : center ;
line - height : 60 rpx ;
border - radius : 8 rpx ;
border : 2 px solid # E0E0E0 ;
font - size : 28 rpx ;
margin - right : 15 px ;
cursor : pointer ;
}
. questionsActive {
background - color : # F0F9FF ;
border : 2 px solid # 409 EFF ;
}
. questionCorrect {
background - color : # F0F9FF ;
border : 2 px solid # 64 BC38 ;
color : # 6 BC441 ;
}
. questionError {
background - color : # FFF1F0 ;
border : 2 px solid # F56C6C ;
color : # F36B6B ;
}
}
}
}
. events {
pointer - events : none ; /* 这会禁用所有指针事件 */
opacity : 0.5 ; /* 可选:改变透明度以视觉上表示不可点击 */
cursor : not - allowed ; /* 可选:改变鼠标光标样式 */
}
< / style >