You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
449 lines
12 KiB
449 lines
12 KiB
8 months ago
|
<template>
|
||
|
<div class="index">
|
||
|
<div class="top">
|
||
|
<div class="item">
|
||
|
|
||
|
|
||
|
<div class="count">
|
||
|
阶段剩余时间
|
||
|
<span>{{ day }}</span>天
|
||
|
<span>{{ hour }}</span>小时
|
||
|
<span>{{ minutes }}</span>分
|
||
|
<span>{{ seconds }}</span>秒
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div class="item">
|
||
|
<div class="count m-r-30">
|
||
|
用时
|
||
|
<span>{{ day }}</span>天
|
||
|
<span>{{ hour }}</span>小时
|
||
|
<span>{{ minutes }}</span>分
|
||
|
<span>{{ seconds }}</span>秒
|
||
|
</div>
|
||
|
|
||
|
<el-button class="submit" @click="submit">提交</el-button>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div class="wrap">
|
||
|
<div class="left">
|
||
|
<h6 class="title">答题卡</h6>
|
||
|
<div class="progress">
|
||
|
<p class="fs-14">答题进度</p>
|
||
|
<el-progress class="m-t-5 m-b-5" :percentage="50"></el-progress>
|
||
|
<p>共{{ form.questionCount }}题,满分{{ form.score }}分</p>
|
||
|
</div>
|
||
|
<div v-if="form.paperOutline" class="type-wrap">
|
||
|
<div v-for="(item, i) in form.paperOutline" :key="i" class="type">
|
||
|
<h6 class="stem">{{ arabicToChinese(i + 1) }}、{{ item.questionTypeName }}(本题共{{ item.questionNum }}小题,共{{
|
||
|
item.targetScore }}分)</h6>
|
||
|
<ul class="serials">
|
||
|
<li v-for="(ques, j) in item.examQuestions" :key="j">{{ j + 1 }}</li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
</div>
|
||
|
<ul class="status-filter">
|
||
|
<li>未作答</li>
|
||
|
<li>已作答</li>
|
||
|
<li>部分已作答</li>
|
||
|
<li>已标记</li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
|
||
|
<ul v-if="form.paperOutline" class="ques-wrap">
|
||
|
<li v-for="(item, i) in form.paperOutline" :key="i">
|
||
|
<div class="outline">
|
||
|
{{ arabicToChinese(i + 1) }}、{{ item.questionTypeName }}(本题共{{ item.questionNum }}小题,共{{
|
||
|
item.targetScore }}分)
|
||
|
<img :class="['shrink', { active: item.shrink }]" src="@/assets/img/shrink.svg" alt=""
|
||
|
@click="item.shrink = !item.shrink">
|
||
|
</div>
|
||
|
<div :class="['ques', { hide: item.shrink }]">
|
||
|
<div v-for="(ques, j) in item.examQuestions" :key="j" class="item">
|
||
|
<div class="stem-wrap">
|
||
|
<span class="label">{{ j + 1 }} / {{ item.questionNum }}</span>
|
||
|
<span class="label">{{ item.questionTypeName }}</span>
|
||
|
<div :id="'stem' + ques.questionVersionId" v-html="getQuesStem(ques)"></div>
|
||
|
<p v-if="item.questionType !== 'fill_blank'">({{ ques.score }}分)</p>
|
||
|
</div>
|
||
|
|
||
|
<!-- 单选、多选、判断的选项 -->
|
||
|
<template
|
||
|
v-if="item.questionType !== 'fill_blank' && item.questionType !== 'essay' && ques.questionAnswerVersionsList">
|
||
|
<div v-for="(opt, j) in ques.questionAnswerVersionsList" :key="j" class="opt">
|
||
|
<el-checkbox v-if="item.questionType === 'multiple_choice'" v-model="item.answerIsCorrect"
|
||
|
:true-label="1"></el-checkbox>
|
||
|
<el-radio v-else v-model="item.answerIsCorrect" :true-label="1" :label="1"
|
||
|
@change="correctChange(i)"></el-radio>
|
||
|
|
||
|
<span>{{ numToLetter(j) }}. </span>
|
||
|
<div class="text" v-html="opt.optionText"></div>
|
||
|
</div>
|
||
|
</template>
|
||
|
<!-- 简答题需要展示题干文件及富文本 -->
|
||
|
<template v-if="item.questionType === 'essay'">
|
||
|
<div v-if="ques.stemAttachment" class="m-b-20">
|
||
|
<el-link class="m-r-10" type="primary">{{ ques.stemAttachment }}</el-link>
|
||
|
<el-button type="primary" size="mini" round @click="download(ques.stemAttachment)">下载</el-button>
|
||
|
</div>
|
||
|
<UeditorPlus ref="answerAnalysis" @ready="answerAnalysisReady" v-model="form.answerAnalysis" />
|
||
|
<Upload v-if="ques.allowAttachment" class="m-t-20"
|
||
|
accept=".csv,.xlsx,.xls,.docx,.doc,.pdf,.jpg,.png,.zip,.rar,.7z" :max-size="50"
|
||
|
:file-list="uploadList" :on-remove="handleRemove" @onSuccess="uploadSuccess">
|
||
|
<div slot="trigger">
|
||
|
<el-button type="primary">上传附件</el-button>
|
||
|
</div>
|
||
|
<template slot="tip">
|
||
|
<p>上传说明内容,支持.csv;.xlsx; .docx; .pdf; .jpg; .zip 等常见文件格式;上传文件大小不能超过50MB</p>
|
||
|
</template>
|
||
|
</Upload>
|
||
|
</template>
|
||
|
</div>
|
||
|
</div>
|
||
|
</li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import Util from '@/libs/util'
|
||
|
import Setting from "@/setting"
|
||
|
import QuesConst from '@/const/ques'
|
||
|
import TestPaperConst from '@/const/testPaper'
|
||
|
import Oss from '@/components/upload/upload.js'
|
||
|
import Upload from '@/components/upload'
|
||
|
import UeditorPlus from '@/components/ueditorPlus'
|
||
|
export default {
|
||
|
components: {
|
||
|
Upload, UeditorPlus
|
||
|
},
|
||
|
data () {
|
||
|
return {
|
||
|
routes: [],
|
||
|
questionTypes: QuesConst.questionTypes,
|
||
|
numToLetter: Util.numToLetter,
|
||
|
arabicToChinese: Util.arabicToChinese,
|
||
|
headers: {
|
||
|
token: Util.local.get(Setting.tokenKey)
|
||
|
},
|
||
|
token: Util.local.get(Setting.tokenKey),
|
||
|
// id: +this.$route.query.id,
|
||
|
id: 43,
|
||
|
counterTimer: null, // 获取setInterval对象值
|
||
|
day: 0, // 天数
|
||
|
seconds: 0, // 秒数
|
||
|
minutes: 0, // 分钟数
|
||
|
hour: 0, // 小时数
|
||
|
form: {},
|
||
|
uploadList: [],
|
||
|
};
|
||
|
},
|
||
|
mounted () {
|
||
|
this.$once('hook:beforeDestroy', function () {
|
||
|
clearInterval(this.timer)
|
||
|
this.timerList.forEach(n => {
|
||
|
clearTimeout(n)
|
||
|
})
|
||
|
this.timerList = []
|
||
|
})
|
||
|
this.getDetail()
|
||
|
|
||
|
// this.initOss()
|
||
|
},
|
||
|
methods: {
|
||
|
// 获取试卷详情
|
||
|
async getDetail () {
|
||
|
try {
|
||
|
const { id } = this
|
||
|
if (id) {
|
||
|
const res = await this.$get(this.api.examPaperDetails, {
|
||
|
id
|
||
|
})
|
||
|
const r = res.examPaper
|
||
|
const paper = r.paperOutline
|
||
|
const types = this.questionTypes
|
||
|
paper.map(e => {
|
||
|
e.questionTypeName = types.find(n => n.id === e.questionType).name
|
||
|
e.shrink = false
|
||
|
e.examQuestions.map(n => {
|
||
|
Object.assign(n, n.question)
|
||
|
})
|
||
|
|
||
|
})
|
||
|
this.form = r
|
||
|
}
|
||
|
} catch (e) { }
|
||
|
},
|
||
|
// 处理题干显示
|
||
|
getQuesStem (ques) {
|
||
|
let { stem } = ques
|
||
|
if (ques.questionType === 'fill_blank') { // 填空题
|
||
|
let { jsonText } = ques
|
||
|
if (jsonText) jsonText = JSON.parse(jsonText)
|
||
|
|
||
|
const regex = /<span class="gapfilling-span" data-id="(.*?)">______<\/span>/g
|
||
|
let match
|
||
|
let index = 0 // 用于跟踪索引
|
||
|
let result = stem
|
||
|
|
||
|
while ((match = regex.exec(stem)) !== null) {
|
||
|
const newInput = `<input type="text" class="fill-input">`
|
||
|
result = result.replace(match[0], newInput)
|
||
|
index++
|
||
|
}
|
||
|
return result
|
||
|
} else {
|
||
|
return stem
|
||
|
}
|
||
|
},
|
||
|
// 下载附件
|
||
|
download (url) {
|
||
|
Util.downloadFile(url, url)
|
||
|
},
|
||
|
handleRemove () {
|
||
|
Oss.del(this.form.stemAttachment)
|
||
|
this.form.stemAttachment = ''
|
||
|
},
|
||
|
uploadSuccess (file) {
|
||
|
this.form.stemAttachment = file.url
|
||
|
},
|
||
|
// 解析富文本加载完毕回调
|
||
|
answerAnalysisReady (editor) {
|
||
|
this.answerAnalysis && editor.setContent(this.answerAnalysis)
|
||
|
},
|
||
|
// 提交
|
||
|
async submit () {
|
||
|
let msg = '此操作将视为结束答题,确认要提交吗?'
|
||
|
// if (!isVscode) msg = '实验报告未填写,实验成绩为零,是否确认提交?'
|
||
|
try {
|
||
|
await this.$confirm(msg, '提示', {
|
||
|
confirmButtonText: '确定',
|
||
|
cancelButtonText: '取消',
|
||
|
type: 'warning',
|
||
|
closeOnClickModal: false
|
||
|
})
|
||
|
} catch (e) { }
|
||
|
},
|
||
|
}
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
.top {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
align-items: center;
|
||
|
padding: 10px;
|
||
|
color: #fff;
|
||
|
background-color: #5786fc;
|
||
|
|
||
|
.item {
|
||
|
display: inline-flex;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
.submit {
|
||
|
width: 106px;
|
||
|
font-size: 15px;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/deep/.wrap {
|
||
|
display: flex;
|
||
|
padding: 15px;
|
||
|
|
||
|
.left {
|
||
|
width: 300px;
|
||
|
margin-right: 15px;
|
||
|
background-color: #fff;
|
||
|
|
||
|
.title {
|
||
|
padding: 10px 0;
|
||
|
font-size: 16px;
|
||
|
text-align: center;
|
||
|
color: #333;
|
||
|
}
|
||
|
|
||
|
.progress {
|
||
|
padding: 10px;
|
||
|
color: #5a5a5a;
|
||
|
background-color: #d4e9ff;
|
||
|
}
|
||
|
|
||
|
.type-wrap {
|
||
|
max-height: calc(100vh - 358px);
|
||
|
padding: 10px;
|
||
|
overflow: auto;
|
||
|
|
||
|
.type {
|
||
|
margin-bottom: 20px;
|
||
|
}
|
||
|
|
||
|
.stem {
|
||
|
font-size: 15px;
|
||
|
color: #333;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.serials {
|
||
|
display: flex;
|
||
|
flex-wrap: wrap;
|
||
|
margin-top: 5px;
|
||
|
|
||
|
li {
|
||
|
width: 35px;
|
||
|
margin: 7px 9px;
|
||
|
font-size: 13px;
|
||
|
text-align: center;
|
||
|
line-height: 35px;
|
||
|
color: #fff;
|
||
|
background-color: #66b2ff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.status-filter {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
padding: 10px;
|
||
|
border-top: 1px solid #e5e5e5;
|
||
|
|
||
|
li {
|
||
|
display: inline-flex;
|
||
|
align-items: center;
|
||
|
font-size: 12px;
|
||
|
color: #333;
|
||
|
cursor: pointer;
|
||
|
|
||
|
&:before {
|
||
|
content: '';
|
||
|
width: 13px;
|
||
|
height: 13px;
|
||
|
margin-right: 5px;
|
||
|
border: 1px solid #ccc;
|
||
|
}
|
||
|
|
||
|
&:nth-child(2):before {
|
||
|
background-color: #56aaff;
|
||
|
border: 0;
|
||
|
}
|
||
|
|
||
|
&:nth-child(3):before {
|
||
|
border-color: #56aaff;
|
||
|
}
|
||
|
|
||
|
&:last-child:before {
|
||
|
// background-color: #54b6ff;
|
||
|
// border: 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.ques-wrap {
|
||
|
width: calc(100% - 315px);
|
||
|
height: calc(100vh - 192px);
|
||
|
background-color: #fff;
|
||
|
overflow: auto;
|
||
|
|
||
|
&>li {
|
||
|
margin-bottom: 15px;
|
||
|
}
|
||
|
|
||
|
.outline {
|
||
|
display: flex;
|
||
|
justify-content: space-between;
|
||
|
align-items: center;
|
||
|
padding: 10px 15px;
|
||
|
font-size: 15px;
|
||
|
font-weight: 600;
|
||
|
color: #333;
|
||
|
background-color: #e8f0ff;
|
||
|
}
|
||
|
|
||
|
.shrink {
|
||
|
cursor: pointer;
|
||
|
transition: .5s;
|
||
|
|
||
|
&.active {
|
||
|
transform: rotate(180deg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.ques {
|
||
|
max-height: none;
|
||
|
padding: 15px;
|
||
|
margin-bottom: 15px;
|
||
|
transition: all 1.5s;
|
||
|
overflow: hidden;
|
||
|
|
||
|
&.hide {
|
||
|
max-height: 10px;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.item:not(:last-child) {
|
||
|
padding-bottom: 15px;
|
||
|
margin-bottom: 15px;
|
||
|
border-bottom: 1px dashed #e1e1e1;
|
||
|
}
|
||
|
|
||
|
.stem-wrap {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
margin-bottom: 10px;
|
||
|
}
|
||
|
|
||
|
.label {
|
||
|
padding: 3px 5px;
|
||
|
margin-right: 10px;
|
||
|
font-size: 12px;
|
||
|
line-height: 1;
|
||
|
color: $main-color;
|
||
|
border: 1px solid;
|
||
|
border-radius: 2px;
|
||
|
}
|
||
|
|
||
|
.fill-input {
|
||
|
width: 100px;
|
||
|
height: 28px;
|
||
|
padding: 0 15px;
|
||
|
margin: 0 10px;
|
||
|
font-size: 13px;
|
||
|
line-height: 28px;
|
||
|
color: #606266;
|
||
|
border: 1px solid #DCDEE0;
|
||
|
border-radius: 2px;
|
||
|
|
||
|
&:focus {
|
||
|
outline: none;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.opt {
|
||
|
display: flex;
|
||
|
flex-wrap: wrap;
|
||
|
align-items: baseline;
|
||
|
padding-left: 10px;
|
||
|
margin-bottom: 5px;
|
||
|
font-size: 14px;
|
||
|
color: #707070;
|
||
|
line-height: 1.6;
|
||
|
|
||
|
.el-radio,
|
||
|
.el-checkbox {
|
||
|
margin-right: 15px;
|
||
|
}
|
||
|
|
||
|
.el-radio__label {
|
||
|
display: none;
|
||
|
}
|
||
|
|
||
|
.text {
|
||
|
max-width: calc(100% - 48px);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|