vue 試卷控件 界面模式

考試界面
在這裏插入圖片描述
閱卷界面
在這裏插入圖片描述
初始化

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>
  <div class="paper-main">
    <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.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>
          <el-input :disabled="disabledAnswer"  v-if="sub.type===4" type="textarea" :rows="2"  v-model="sub.examineAnswer" resize="none" maxlength="150" @blur="answerButtionCheck($event,item,sub)"> </el-input>
          <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 class="paper-footer">
      <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>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'examinationPaper',
    props: {
      //試卷類型 1 考試 2 閱卷 3 查看
      type: {
        type: Number,
        default: 1
      },
      //數據源
      dataSource: {
        type: Object,
        default: () => {
          return {
            //試卷ID
            paperId: '',
            //試卷名稱
            paperName: '',
            //考生ID
            examineId: '',
            //考生名稱
            examineName: '',
            //分數
            score: null,
            //考試時長(分鐘)
            examDuration: null,
            //交卷時間
            submissionTime: '',
            //題目集合
            list: [
              {
                //題目類型 1.單選題 2.多選題 3.判斷題 4.填空題 5.簡答題
                type: null,
                //題號
                no: null,
                //題目
                subject: '',
                //題目總分
                totalScore: null,
                //答案集合
                answers: [
                  {
                    //答案序號
                    no: '',
                    //答案
                    answer: ''
                  }
                ],
                //考生答案
                examineAnswer: null,
                //正確答案
                correctAnswer: null,
                //答案解析
                answerAnalysis: '',
                //是否對錯  1.對 2.錯
                isHook: null,
                //得分
                score: null
              }
            ]
          }
        }
      }
    },
    data() {
      return {
        //倒計小時
        hour: '',
        //倒計分鐘
        minute: '',
        //倒計秒
        second: '',
        //計時器
        promiseTimer: '',
        //數據源
        tempDataSource: {},
        //答題卡激活項
        answerCardActiveName: [],
        //組裝後數據集
        convertDatas: [],
        //禁止答題
        disabledAnswer:false,
        //禁止閱卷
        disabledRead:false,

      }
    },
    watch: {
      dataSource(newValue, oldValue) {
        Object.assign(this.tempDataSource, newValue)
        this.convertData()
      }
    },
    created() {

      Object.assign(this.tempDataSource, this.dataSource)
      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 () {
      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);
          });
        });
      }

    },
    methods: {
      /**
       * 按鈕點擊事件
       */
      btnClick(type){
        console.log(this.tempDataSource);
        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){
        console.log(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 && child.examineAnswer.length==0){
                but[0].classList.remove("answer-button-check");
              }
           }
           else{
             if (child.examineAnswer && child.examineAnswer.length > 0) {
               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]":
              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
          }

        })
        console.log(this.convertDatas)
      },
      /**
       * 排序
       * @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 {
    margin: 10px;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    overflow: hidden
  }

  .paper-header {
    width: 100%;
    height: 60px;
    background-color: #f7f7f7;
    position: absolute;
    top: 0;
    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;
    left: 0;
    top: 60px;
    bottom: 0;
    width: 300px;
    overflow-x: hidden;
    overflow-y: auto;
    border: 1px solid #e4e4e4;
    border-top: none;
  }

  .paper-content {
    position: absolute;
    left: 305px;
    top: 60px;
    right: 0px;
    bottom: 45px;
    overflow-x: hidden;
    overflow-y: auto;
    box-sizing: border-box;
    padding: 10px;
    border: 1px solid #e4e4e4;
    border-top: none;
  }

  .paper-footer {
    position: absolute;
    padding: 5px 10px;
    left: 305px;
    right: 0;
    bottom: 0px;
    height: 45px;
    overflow: hidden;
    box-sizing: border-box;
    background-color: #f7f7f7;
    box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
    -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
    text-align: center;
  }

  .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{
    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>

發佈了11 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章