代碼
paperConfig:{
type:3,
title: '閱卷',
visible: true,
paperData:{
//試卷ID
paperId:'1',
//試卷名稱
paperName: '測試試卷',
//考生ID
examineId:'1000',
//考生名稱
examineName: '張三',
//分數
score: 80,
//考試時長
examDuration: 90,
//交卷時間
submissionTime: '2019-11-25 16:30:26',
//題目集合
list:[
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:2, no:1, subject:'以下屬於南方電網員工職業操守中明文規定的有()',totalScore:6,
answers:[
{no:'A',answer:'熱愛祖國、熱愛南網、熱愛崗位'},
{no:'B',answer:'遵紀守法、忠於職守、令行禁止'},
{no:'C',answer:'客戶至上、誠實守信、優質服務'}
],examineAnswer:['A','B'],correctAnswer:['A','B','C'],
answerAnalysis:'答案解析.......',isHook:2,score:0,
},
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:1, no:1, subject:'在生產管理信息系統中,下列操作步驟能正確將工單推進流程的是( )',totalScore:1,
answers:[
{no:'A',answer:'在工具欄中點擊“workflow”標籤'},
{no:'B',answer:'在缺陷單界面中點擊“推進流程”按鈕'},
{no:'C',answer:'在缺陷單界面中點擊“提交”按鈕'}
],examineAnswer:'A',correctAnswer:'B',
answerAnalysis:'答案解析.......',isHook:2,score:0,
},
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:1, no:2, subject:'在營銷系統中查詢客戶有無欠費、餘額及抄表數據接待客戶時應做到哪些最基本的禮儀?',totalScore:5,
answers:[
{no:'A',answer:'起身、微笑、示坐、問候客戶'},
{no:'B',answer:'坐着,問候客戶'},
{no:'C',answer:'請問需要辦理什麼業務'}
],examineAnswer:'A',correctAnswer:'A',
answerAnalysis:'答案解析.......',isHook:1,score:5,
},
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:3, no:1, subject:'記錄一次與人有效溝通的案例',totalScore:10,
answers:[],examineAnswer:'對',correctAnswer:'對',
answerAnalysis:'答案解析.......',isHook:1,score:10,
},
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:4, no:1, subject:'打招呼的方式一般有()()()()',totalScore:10,
answers:[],examineAnswer:'寒暄式',correctAnswer:['寒暄式','問候式','致意式','致禮式'],
answerAnalysis:'答案解析.......',isHook:1,score:10,
},
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type:5, no:1, subject:'請簡單說一下你對禮儀的認識與理解',totalScore:10,
answers:[],examineAnswer:'寒暄式',correctAnswer:'',
answerAnalysis:'答案解析.......',isHook:1,score:10,
}
]
}
}
控件
<template>
<el-dialog
:title="title"
:visible.sync="visible"
:width="this.width+'px'"
:close-on-click-modal="false"
:close-on-press-escape="false"
@opened="initPage"
>
<div class="paper-main" :style="'height:'+this.height+'px;'">
<div class="paper-header">
<el-form label-position="top" label-width="100px" :model="tempDataSource" style="padding-top:0px; ">
<el-row>
<el-col :span="4" :offset="1">
<el-form-item label="試卷">
{{dataSource.paperName}}
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="考生">
{{dataSource.examineName}}
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="分數" v-if="this.type===2 || this.type===3">
{{dataSource.score}}
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="考試時長">
{{dataSource.examDuration}}分
</el-form-item>
</el-col>
<el-col :span="4" v-if="this.type===1">
<el-form-item label="倒計時間">
<span class="downTime">{{hour? hourString+':'+minuteString+':'+secondString : minuteString+':'+secondString}}</span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="交卷時間" v-if="this.type===2 || this.type===3">
{{dataSource.submissionTime}}
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div ref="paperLeft" class="paper-left">
<div class="paper-title">
<h1><i class="el-icon-s-grid"></i>答題卡</h1>
</div>
<el-collapse v-model="answerCardActiveName">
<el-collapse-item v-for="item in convertDatas" :name="item.code" >
<template slot="title">
<h2>{{item.name}}</h2><spn>共{{item.count}}題</spn>
</template>
<el-button class="answer-button" circle size="small" v-for="index of item.count" :id="'answer'+item.code+index" @click.native="jump(item.code+index)">{{index}}</el-button>
</el-collapse-item>
</el-collapse>
</div>
<div ref="paperContent" class="paper-content">
<div class="subject" v-for="item in convertDatas">
<div class="subject-title" >
<h2>{{item.name}}</h2><spn>(共 {{item.count}} 題,合計 {{item.totalScore}} 分)</spn>
</div>
<el-card class="box-card" v-for="(sub,index) in item.childs" :id="item.code+(index+1)">
<div slot="header" class="clearfix">
<el-tag effect="dark"> {{sub.no}} </el-tag>
<span>{{sub.type===4?'':sub.subject}}</span>
<span>({{sub.totalScore}}分)</span>
<div v-if="type===2 || type===3" style="float: right; padding: 3px 0">
<el-radio-group v-model="sub.isHook">
<el-radio-button :disabled="disabledRead" :label="1" @change.native="isHookButtionCheck(sub)"><i class="el-icon-check"/></el-radio-button>
<el-radio-button :disabled="disabledRead" :label="2" @change.native="isHookButtionCheck(sub)"><i class="el-icon-close"/></el-radio-button>
</el-radio-group>
<div v-if="sub.type===1 ||sub.type===2||sub.type===3" style="display: inline;">
<el-input :disabled="true" v-model="sub.score" style="width:50px" ></el-input><span>分</span>
</div>
<div v-else style="display: inline;">
<el-input :disabled="disabledRead" v-model="sub.score" style="width:50px" ></el-input><span>分</span>
</div>
</div>
</div>
<el-radio-group v-if="sub.type===1" v-model="sub.examineAnswer">
<el-radio :disabled="disabledAnswer" v-for="o in sub.answers" :label="o.no" class="answer-radio" @change="answerButtionCheck($event,item,sub)">{{o.no}}.{{o.answer}}</el-radio>
</el-radio-group>
<el-checkbox-group v-if="sub.type===2" v-model="sub.examineAnswer">
<el-checkbox :disabled="disabledAnswer" v-for="o in sub.answers" :label="o.no" class="answer-checkbox" @change="answerButtionCheck($event,item,sub)">{{o.no}}.{{o.answer}}</el-checkbox>
</el-checkbox-group>
<el-radio-group v-if="sub.type===3" v-model="sub.examineAnswer">
<el-radio :disabled="disabledAnswer" label="對" class="answer-radio" @change="answerButtionCheck($event,item,sub)">對</el-radio>
<el-radio :disabled="disabledAnswer" label="錯" class="answer-radio" @change="answerButtionCheck($event,item,sub)">錯</el-radio>
</el-radio-group>
<div v-if="sub.type===4">
<template v-for="(subject,subindex) in sub.subjectList">
<span v-if="subject" style="margin-right: 5px;">{{subject}}</span>
<el-input v-else :disabled="disabledAnswer" v-model="sub.newExamineAnswer[subindex]" style="width: 150px;margin-right: 5px;" @blur="answerButtionCheck($event,item,sub)"> </el-input>
</template>
</div>
<el-input :disabled="disabledAnswer" v-if="sub.type===5" type="textarea" :rows="10" v-model="sub.examineAnswer" resize="none" maxlength="2000" @blur="answerButtionCheck($event,item,sub)"> </el-input>
<div v-if="type!==1" class="subject-remark">
<!--<div class="item">-->
<!--<span class="title">考生答案:</span>-->
<!--<span>{{converAnswerStr(sub.examineAnswer)}}</span>-->
<!--</div>-->
<div class="item">
<span class="title">正確答案:</span>
<span>{{converAnswerStr(sub.correctAnswer)}}</span>
</div>
<div class="item">
<span class="title">答案解析:</span>
<span>{{sub.answerAnalysis}}</span>
</div>
</div>
</el-card>
</div>
</div>
</div>
<span slot="footer" v-if="type===1 || type===2">
<el-button v-if="type===1" type="success" @click.native="btnClick('handPaper')">交卷</el-button>
<el-button v-if="type===2" type="success" @click.native="btnClick('readPaper')">閱卷</el-button>
<el-button v-if="type===2" type="success" @click.native="btnClick('readPaperUpper')">上一個</el-button>
<el-button v-if="type===2" type="success" @click.native="btnClick('readPaperNext')">下一個</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'examinationPaper',
props: {
// 標題
title: {
type: String
},
// 是否顯示
visible: {
type: Boolean
},
//試卷類型 1 試卷-考試 2 試卷-閱卷 3 試卷-查看
type: {
type: Number,
default: 1
},
//數據源
dataSource: {
type: Object,
default: () => {
return {
//試卷名稱
paperName: '',
//考生名稱
examineName: '',
//分數
score: null,
//考試時長(分鐘)
examDuration: null,
//交卷時間
submissionTime: '',
//擴展數據
extData:{},
//題目集合
list: [
{
//題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
type: null,
//題號
no: null,
//題目
subject: '',
//題目總分
totalScore: null,
//答案集合
answers: [
{
//答案序號
no: '',
//答案
answer: ''
}
],
//考生答案
examineAnswer: null,//1.單選題 '' 2.多選題 ['1','2'] 3.判斷題 '' 4.填空題 ['1','2'] 5.簡答題 ''
//正確答案
correctAnswer: null,//1.單選題 '' 2.多選題 ['1','2'] 3.判斷題 '' 4.填空題 ['1','2'] 5.簡答題 ''
//答案解析
answerAnalysis: '',
//是否對錯 1.對 2.錯
isHook: null,
//得分
score: null
}
]
}
}
},
},
data() {
return {
//倒計小時
hour: '',
//倒計分鐘
minute: '',
//倒計秒
second: '',
//計時器
promiseTimer: '',
//數據源
tempDataSource: {},
//答題卡激活項
answerCardActiveName: [],
//組裝後數據集
convertDatas: [],
//禁止答題
disabledAnswer: false,
//禁止閱卷
disabledRead: false,
//寬度
width:0,
//高度
height:0,
}
},
watch: {
dataSource(newValue, oldValue) {
Object.assign(this.tempDataSource, newValue)
this.convertData()
}
},
created() {
Object.assign(this.tempDataSource, this.dataSource)
if(!this.tempDataSource.list)this.tempDataSource.list=[]
this.convertData()
if (this.type === 2) {
this.disabledAnswer = true
}
if (this.type === 3) {
this.disabledAnswer = true
this.disabledRead = true
}
},
computed: {
hourString() {
return this.hour < 10 ? '0' + this.hour : '' + this.hour
},
minuteString() {
return this.minute < 10 ? '0' + this.minute : '' + this.minute
},
secondString() {
return this.second < 10 ? '0' + this.second : '' + this.second
}
},
mounted() {
let vue =this;
vue.getClientWidth();
vue. getClientHeight();
window.addEventListener('resize', function() {
vue.getClientWidth();
vue. getClientHeight();
})
},
methods: {
/**界面寬度 */
getClientWidth() {
this.width= window.document.body.clientWidth - 40
},
/**界面高度 */
getClientHeight() {
this.height= (window.document.body.clientHeight - 160)
},
/**
* 頁面加載
*/
initPage() {
if (this.type === 1) {
let remainTime = this.dataSource.examDuration * 60
if (remainTime > 0) {
this.hour = Math.floor((remainTime / 3600) % 24)
this.minute = Math.floor((remainTime / 60) % 60)
this.second = Math.floor(remainTime % 60)
this.countDowm()
}
}
if (this.type === 2 || this.type === 3) {
this.convertDatas.forEach(t => {
t.childs.forEach(c => {
this.answerButtionCheck(c.examineAnswer, t, c)
})
})
}
},
/**
* 按鈕點擊事件
*/
btnClick(type) {
console.log(this.tempDataSource)
debugger
this.tempDataSource.list.forEach(t=>{
if(t.type===4)
{
t.subjectList = t.subject.split('{}')
t.examineAnswer=[]
t.newExamineAnswer.forEach(e=>{
if(e)
{
t.examineAnswer.push(e)
}
})
// delete t.newExamineAnswer
// delete t.subjectList
}
})
switch (type) {
//交卷
case 'handPaper':
this.$emit('PaperHand', this.tempDataSource)
break
//閱卷
case 'readPaper':
this.$emit('paperRead', this.tempDataSource)
break
//閱卷 上一個
case 'readPaperUpper':
this.$emit('paperReadUpper')
break
//閱卷 下一個
case 'readPaperNext':
this.$emit('paperReadNext')
break
}
},
/**
* 錨點定位
*/
jump(postion) {
let jump = this.$refs.paperContent.querySelectorAll('#' + postion)
// 獲取需要滾動的距離
let total = jump[0].offsetTop
//實現form錨點定位
this.$refs.paperContent.scrollTop = jump[0].offsetTop
},
/**
*對錯選擇
*/
isHookButtionCheck(val) {
if (val.type === 1 || val.type === 2 || val.type === 3) {
if (val.isHook === 1) {
val.score = val.totalScore
}
if (val.isHook === 2) {
val.score = 0
}
}
},
/**
*答題卡選中
*/
answerButtionCheck(value, parent, child) {
let answerId = 'answer' + parent.code + child.no
let but = this.$refs.paperLeft.querySelectorAll('#' + answerId)
if (but.length > 0) {
if (but[0].className.indexOf('answer-button-check') > -1) {
if(child.examineAnswer instanceof Array)
{
let t=false;
child.examineAnswer.forEach(e=>{
if(e){t=true}
})
if(!t)
{
but[0].classList.remove('answer-button-check')
}
}
else{
if (!child.examineAnswer) {
but[0].classList.remove('answer-button-check')
}
}
} else {
if(child.examineAnswer instanceof Array)
{
let t=false;
child.examineAnswer.forEach(e=>{
if(e){t=true}
})
if(t)
{
but[0].classList.add('answer-button-check')
}
}
else{
if (child.examineAnswer) {
but[0].classList.add('answer-button-check')
}
}
}
}
},
/**
* 轉換答案
*/
converAnswerStr(answer) {
if (answer instanceof Array) {
return answer.join(' ')
}
return answer
},
/**
* 轉換數據
*/
convertData() {
let sorted = this.groupBy(this.tempDataSource.list, function(item) {
return [item.type]
})
this.convertDatas = []
this.answerCardActiveName = []
this.orderBy(sorted, 'key', 'asc')
sorted.forEach(item => {
let totalScore = 0
item.value.forEach(t => {
totalScore += t.totalScore
})
switch (item.key) {
case '[1]':
this.convertDatas.push({
name: '單選題',
code: 'Single',
count: item.value.length,
totalScore: totalScore,
childs: item.value
})
this.answerCardActiveName.push('Single')
break
case '[2]':
this.convertDatas.push({
name: '多選題',
code: 'Multiple',
count: item.value.length,
totalScore: totalScore,
childs: item.value
})
this.answerCardActiveName.push('Multiple')
break
case '[3]':
this.convertDatas.push({
name: '判斷題',
code: 'Judgment',
count: item.value.length,
totalScore: totalScore,
childs: item.value
})
this.answerCardActiveName.push('Judgment')
break
case '[4]':
item.value.forEach(t=>{
t.subjectList = t.subject.replace('{}','∷{}∷').split('∷')
t.newExamineAnswer=[]
let index=0;
t.subjectList.forEach(e=>{
if(e)
{
t.newExamineAnswer.push("")
}
else{
t.newExamineAnswer.push(t.examineAnswer[index])
index++
}
});
})
this.convertDatas.push({
name: '填空題',
code: 'Blank',
count: item.value.length,
totalScore: totalScore,
childs: item.value,
})
this.answerCardActiveName.push('Blank')
break
case '[5]':
this.convertDatas.push({
name: '簡答題',
code: 'Answer',
count: item.value.length,
totalScore: totalScore,
childs: item.value
})
this.answerCardActiveName.push('Answer')
break
}
})
},
/**
* 排序
* @param {} datas 數組
* @param {} col 列
* @param {} type 類型 desc,asc
* @returns {}
*/
orderBy(datas, col, type) {
let m
for (let i = 0; i < datas.length; i++) {
for (let k = 0; k < datas.length; k++) {
if (type === 'asc') {
if (datas[i][col] < datas[k][col]) {
m = datas[k]
datas[k] = datas[i]
datas[i] = m
}
} else if (type === 'desc') {
if (datas[i][col] > datas[k][col]) {
m = datas[k]
datas[k] = datas[i]
datas[i] = m
}
}
}
}
return datas
},
/**
* 分組
* @param array 數據集
* @param f 函數
* let sorted = groupBy(list, function(item){ return [item.name];});
*/
groupBy(array, f) {
const groups = {}
const keyValues = []
array.forEach(function(o) {
const group = JSON.stringify(f(o))
groups[group] = groups[group] || []
groups[group].push(o)
})
Object.keys(groups).map(function(group) {
return keyValues.push({ key: group, value: groups[group] })
})
return keyValues
},
/**
* 倒計時
*/
countDowm() {
let self = this
clearInterval(this.promiseTimer)
this.promiseTimer = setInterval(function() {
if (self.hour === 0 && self.minute === 0 && self.second === 0) {
self.disabledAnswer = true
}
if (self.hour === 0) {
if (self.minute !== 0 && self.second === 0) {
self.second = 59
self.minute -= 1
} else if (self.minute === 0 && self.second === 0) {
self.second = 0
self.$emit('countDowmEnd', true)
clearInterval(self.promiseTimer)
} else {
self.second -= 1
}
} else {
if (self.minute !== 0 && self.second === 0) {
self.second = 59
self.minute -= 1
} else if (self.minute === 0 && self.second === 0) {
self.hour -= 1
self.minute = 59
self.second = 59
} else {
self.second -= 1
}
}
}, 1000)
}
}
}
</script>
<style scoped>
.paper-main {
overflow: hidden
}
.paper-header {
width: 100%;
height: 60px;
background-color: #f7f7f7;
z-index: 1000;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .1);
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .1);
}
.paper-left {
position: absolute;
padding: 10px;
top: 115px;
bottom: 50px;
width: 300px;
overflow-x: hidden;
overflow-y: auto;
border: 1px solid #e4e4e4;
border-top: none;
}
.paper-content {
position: absolute;
left: 305px;
top: 115px;
right: 10px;
bottom: 50px;
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
padding: 10px;
border: 1px solid #e4e4e4;
border-top: none;
}
.paper-title {
padding-left: 10px;
width: 100%;
height: 45px;
line-height: 45px;
background: #f7f7f7;
}
.paper-title h1 {
font-size: 1.2em;
margin: 0;
}
.downTime {
color: rgb(230, 93, 110);
font-size: 16px;
font-weight: bold;
}
.answer-button {
padding: 0px;
color: #0a0a0a;
background-color: #ffffff;
border-color: #e4e4e4;
margin-left: 10px;
width: 30px;
height: 30px;
}
.answer-button:hover {
background: #ecf1ef;
border-color: #e4e4e4;
color: #0a0a0a;
}
.answer-button-check {
background: #13ce66;
border-color: #30B08F;
}
.answer-radio {
display: list-item;
margin: 5px 0px;
}
.answer-checkbox {
display: list-item;
margin: 5px 0px;
}
.subject-title {
padding-left: 10px;
width: 100%;
height: 45px;
line-height: 45px;
background: #f7f7f7;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
-webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
}
.subject-title h2 {
font-size: 16px;
display: inline-block;
}
.subject-title span {
font-size: 16px;
display: inline-block;
}
.subject-remark {
margin-top: 10px;
background: #f7f7f7;
}
.subject-remark .item {
display: block;
padding: 5px;
}
.subject-remark .title {
font-weight: bold;
}
.el-radio >>> .el-radio__input.is-checked .el-radio__inner {
background-color: #13ce66;
border-color: #13ce66;
}
.el-radio-button >>> .el-radio-button__inner {
padding: 10px;
}
.el-collapse-item h2 {
width: 150px;
font-size: 14px;
display: inline-block;
}
.el-form--label-top >>> .el-form-item__label {
float: none;
display: inline-block;
text-align: left;
padding: 0px;
}
.el-card {
margin: 10px;
}
.el-card >>> .el-card__header {
background-color: #ffffff;
padding: 0px 10px;
line-height: 35px;
font-size: 16px;
}
.el-card >>> .el-card__body {
padding: 5px 20px;
}
</style>