欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

虚拟考试系统的实践(一)

最编程 2024-04-20 19:04:30
...

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

考试系统主要包括教师端-老师出题和学生端-学生做题这两大主要功能,这是我开发的第一个vue项目,由于做项目时候是小白一个,写了很多low的代码,现在项目结束了,总结一下,温故而知新。 由于项目比较大,分几个章节来讲,今天先介绍一下学生端-学生做题的页面

QQ图片20211118152240.png

如图,学生做题页面是传统的上下左右结构。header,left和right,下面我们主要讲做题的核心代码吧。包括了8种题型,分别是:qtype:1 单选题 2判断题 3多选题 4填空题 5连线题 6排序题 7问答题 8阅读理解(阅读理解比较特殊,7种类型题目的汇总)

这个界面主要实现了,学生进入做题页面,获取当前试卷的信息,渲染页面,开始考试倒计时,渲染左侧题型,默认加载第一种题型下的试题。学生选择其他题型,加载对应题型,加载过的题型不会重复加载,监控学生做题,左侧题目的序号变成已做,做题进度更新。学生点击左侧题的序号,加载滚动到对于题目。切屏超过3次自动提交试卷,切屏给与警告。提交试卷后,客观题自动批阅,主观题老师批阅。待成绩发放学生进来可以看到题目的正确与否,以及分数。本文先从学生做题界面开始介绍。

1.单选和判断用 el-radio

<div class="option">
    <el-radio-group v-model="item.answerd" @change="chooseOptionFinal(itemP,item.indexNew,item,index,item.id)">
        <el-radio :label="item2.id" :key="item2.id" v-for="item2 in item.options"><span class="spanFlex" v-html="item2.char+'  '+item2.opName"></span></el-radio>
    </el-radio-group>
</div>

2.多选用el-checkbox

<div class="option">
    <el-checkbox-group v-model="item.answerd" @change="chooseOptionFinal(itemP,item.indexNew,item,index,item.id)">
        <el-checkbox :label="item2.id" :key="item2.id" v-for="item2 in item.options"><span class="spanFlex" v-html="item2.char+'  '+item2.opName"></span></el-checkbox>
    </el-checkbox-group>
</div>

3.填空(老师出题时插入的input,做题时直接在input输入值)

<div class="stem" v-if="item1.sameScore" :ques-id="item.id">
    <div v-html="item.stem"></div>
</div>

4.right部分代码

<el-main class="mainAll">
    <el-collapse accordion v-model="chooseTab">
        <el-collapse-item :class="`collapse${itemP.qtype}`" :name="itemP.qtype" v-for="(itemP,index) in questionTypes" :key="index">
            <template slot="title">
                <span class="fs16">{{itemP.qtype | typeChinese}}</span>
                <i v-if="itemP.sameScore">(共{{itemP.qnum}}题,每题{{itemP.sameScore}}分,合计{{itemP.qscore}}分)</i>
                <i v-else>(共{{itemP.qnum}}题,合计{{itemP.qscore}}分)</i>
            </template>
            <template v-if="itemP.qtype !== 8">
                <div :id="`${item.id}`" :class="`subject subject${itemP.qtype}`" v-for="(item,index) in itemP.questions" :key="index">
                    <template v-if="itemP.qtype === 4"><!--填空题-->
                        <div class="stem" v-else  :ques-id="item.id">
                            <div v-html="index+1+'、'"></div>
                            <div v-html="item.stem"></div>
                            <div v-html="'(' + (item.quesScore || 0) + '分)'"></div>
                        </div>
                    </template>
                    <template v-else>
                        <div class="stem" v-if="itemP.sameScore" v-html="index+1+'、'+item.stem" :ques-id="item.id"></div>
                        <div class="stem" v-else v-html="index+1+'、'+item.stem + '(' + (item.quesScore || 0) + '分)'" :ques-id="item.id"></div>
                    </template>
                    <template v-if="itemP.qtype === 1"><!--单选题-->
                        <div class="option">
                            <el-radio-group v-model="item.answerd" @change="chooseOptionFinal(itemP,index,item)">
                                <el-radio :label="item2.id" :key="item2.id" v-for="item2 in item.options">
                                    <span class="spanFlex" v-html="item2.char+'  '+item2.opName"></span>
                                </el-radio>
                            </el-radio-group>
                        </div>
                    </template>
                    <template v-if="itemP.qtype === 2 || itemP.qtype === 9"><!--多选题、不定项选择题-->
                        <div class="option">
                            <el-checkbox-group v-model="item.answerd" @change="chooseOptionFinal(itemP,index,item)">
                                <el-checkbox :label="item2.id" :key="item2.id" v-for="item2 in item.options">
                                    <span class="spanFlex" v-html="item2.char+'  '+item2.opName"></span>
                                </el-checkbox>
                            </el-checkbox-group>
                        </div>
                    </template>
                    <template v-if="itemP.qtype === 3"><!--判断题-->
                        <div class="option">
                            <el-radio-group v-model="item.answerd" @change="chooseOptionFinal(itemP,index,item)">
                                <el-radio :label="item2.id" :key="item2.id" v-for="item2 in item.options">
                                    <span class="spanFlex" v-html="item2.opName"></span>
                                </el-radio>
                            </el-radio-group>
                        </div>
                    </template>
                </div>
            </template>
        </el-collapse-item>
    </el-collapse>
</el-main>

5.left部分

<el-aside width="260px" class="asideLeft">
    <div class="aside_div">
        <div class="oneItem">
            <div class="fl">
                <div class="fs14">试题总数</div>
                <div><i class="fs28">{{answerData.questionNum}}</i><i class="fs12 co333"></i></div>
            </div>
            <div class="fr">
                <div class="fs14">试题总分</div>
                <div><i class="fs28">{{answerData.paperScore}}</i><i class="fs12 co333"></i></div>
            </div>
        </div>
        <div class="twoItem pd20">
            <p class="fs14">做题进度</p>
            <el-progress :percentage="answerPro" :format="format"></el-progress>
            <div>
                <p class="fs14">剩余时间</p>
                <div class="timeItem">
                    <div class="blackItem"></div>
                    <div class="blackItem"></div>
                    <span>{{hour? hourString:'00'}}</span>
                </div>
                :
                <div class="timeItem">
                    <div class="blackItem"></div>
                    <div class="blackItem"></div>
                    <span>{{minute?minuteString:'00'}}</span>
                </div>
                :
                <div class="timeItem">
                    <div class="blackItem"></div>
                    <div class="blackItem"></div>
                    <span>{{second?secondString:'00'}}</span>
                </div>
            </div>
        </div>
        <div :class="['testItem',chooseTab === item.qtype?'active':'']"
             v-for="item in questionTypes" :key="item.qtype" @click="chooseTestTab(item.qtype)">
            <div class="itemInner">
                <div class="fs16">{{item.qtype | typeChinese}}</div>
                <div class="fs12">共 {{item.qnum}} 题,<span v-if="item.sameScore && item.sameScore.toString().length>0">每题 {{item.sameScore}} 分,</span> 合计 {{item.qscore}} 分</div>
                <template v-if="item.qtype===8">
                    <div v-for="item1 in item.radioList" :key="item1.index" style="margin-bottom: 10px">
                        <div class="fs12">{{item1.name}}  (共 {{item1.num}} 题,合计 {{item1.score}} 分)</div>
                        <el-radio :id="`radio${item2.qid}`" @change="questionClick(item2.qid,item2)" :label="item2.label" border v-for="item2 in item1.radioList1" :key="item2.label" :class="[item2.flag===3?'redBorder':'',item2.flag===1?'greenBorder':'',item2.flag===2?'yellowBorder':'',item2.flag===4?'blueBorder':'',item2.clickFlag?'clickRect':'']"></el-radio>
                    </div>
                </template>
                <template v-else>
                    <el-radio :id="`radio${item2.qid}`" @change="questionClick(item2.qid,item2)" :label="item2.label" border v-for="item2 in item.radioList" :key="item2.label" :class="[item2.flag===3?'redBorder':'',item2.flag===1?'greenBorder':'',item2.flag===2?'yellowBorder':'',item2.flag===4?'blueBorder':'',item2.clickFlag?'clickRect':'']"></el-radio>
                </template>
            </div>
        </div>
        <div class="bottomItem pd20">
            <i></i>未答题 <i></i>已答题
        </div>
    </div>
</el-aside>

6.js核心代码

data() {
    return {
        questionTypes: [],// 题库
        answerCurrent: 0,// 做题数
        answerPro: 0,// 做题进度
        hour: '',// 时
        minute: '',// 分
        second: '',// 秒
    }
}

watch:{
// 点击题型,展开对应题型,请求后台,获取题库,记录状态。加载过的不重复加载
    chooseTab(newVal) {
        this.questionTypes.forEach(item => {
            if (item.qtype == newVal && item.questions.length===0) { // 没有加载该类型
                this.getPaperInfos(); // 根据题型获取题库
            }
        });
    },
    // 题库
    questionTypes: {
        handler() {
            this.answerCurrent = 0;  // 已做试题总数
            this.answerPro = 0;// 做题进度
            this.questionTypes.forEach(item => {
                item.radioList.forEach(itemRadio => {
                    if (commonJs.isNullVal(itemRadio.flag) && itemRadio.flag != 0) {
                        this.answerCurrent++;  // 已做试题总数
                        this.answerPro++;  // 做题进度
                    }
                    if (itemRadio.radioList1 && itemRadio.radioList1.length > 0) {
                        itemRadio.radioList1.forEach(itemRadio2 => {
                            if (itemRadio2.flag != 0) {
                                this.answerCurrent++;  // 已做试题总数
                                this.answerPro++;  // 做题进度
                            }
                        })
                    }
                })
            })
        },
        deep: true,
    },
},
mounted() {
    this.getPaperInfos();// 获取题库
},
methods:{
    // 获取题库
    getPaperInfos() {
        // 请求后台接口,处理一些逻辑。
        // 后台返回的考试时长
        if (this.promiseTimer > 0) {// 分钟
            this.hour = Math.floor((this.promiseTimer / 3600) % 24);
            this.minute = Math.floor((this.promiseTimer / 60) % 60);
            this.second = Math.floor(this.promiseTimer % 60);
            this.countDown();/ 倒计时
        }
    },
    //进度条
    format() {
        if (this.answerTotal) {
            if(this.answerCurrent > this.answerTotal){
                this.answerCurrent = this.answerTotal;
            }
            this.answerPro = (this.answerCurrent / this.answerTotal) * 100;
            return this.answerCurrent + "/" + this.answerTotal;
        } else {
            this.answerPro = 0;
            return 0;
        }
    },
    // 考试倒计时
    countDown() {
        let self = this;
        clearInterval(this.timeInterval);
        //返回值是数字,从1开始
        this.timeInterval = setInterval(function () {
            self.costTime += 1;
            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;
                    clearInterval(self.timeInterval);
                    self.saveOptionAll(3,'stop');
                } 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);
    },
    // 题型的选择
    chooseTestTab(key) {
        this.chooseTab = key;
        this.questionTypes.forEach((item,i)=> {
            if(item.qtype !== 8){
                item.radioList.forEach((itemRadio,r)=> {
                    this.$set(itemRadio, 'clickFlag',false);
                })
            }else{
                item.radioList.forEach((itemRadio,r)=> {
                    itemRadio.radioList1.forEach((itemRadio1,r1)=> {
                        this.$set(itemRadio1, 'clickFlag',false);
                    })
                })
            }
        })
    },
}

以上是学生端-学生做题界面的第一部分。记录一下,温故而知新!

推荐阅读