今天分享一个考试防止切屏作弊的功能
需求
效果如动图:
需求描述:不难看出,这是只要考试页面不在当前页面的都会被看做是切屏,当超过设置的考试切屏允许的最大次数时(在考试管理里面会有这个功能),就会自动交卷停止作答。
关键点1:在vue生命周期mounted钩子里面,添加一个监听页面可见或不可见的事件visibilitychange
关键点2:为visibilitychange事件绑定一个函数,在此函数中获取页面元素的状态document.visibilityState
关键点3:调用后台接口,获取在考试管理中是否设置了开启可切屏功能,以及可切屏的最大次数和还剩余多少次的数据(还剩多少次切屏这个是调用接口获取的,每切屏一次调用一次接口,后台老哥去处理一次,其实完全也可以前端来做)
HTML结构
<el-dialog title="提示" :visible.sync="tipsFlag" width="480px" class="commonDialog multi clickLight" center :close-on-click-modal="false">
<div class="dialogTipsbox" v-if="tips===1">你还有试题未作答,确认要交卷?</div>
<div class="dialogTipsbox" v-if="tips===2">
最多只能切屏{{switchPage.switchPageTimes}}次,你还可切换{{switchPage.remaTimes}}次,
<br />
超过{{switchPage.switchPageTimes}}次将强行交卷!
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="tipsFlag = false" v-if="tips===1">取 消</el-button>
<el-button type="primary" @click="onConfirmTip" v-if="tips===1">确 定</el-button>
<el-button type="primary" @click="onConfirmTip" v-if="tips===2">我知道了</el-button>
</span>
</el-dialog>
tipsFlag变量用来控制提示的显示和隐藏,tips变量用来区分提示语,tips类型 1:未做完提醒 2:切屏提醒
功能代码
mounted() {
// 监听滚动
window.addEventListener("scroll", this.handleScroll);
// 监听浏览器窗口变化
window.addEventListener("resize", this.getLfetDistance);
// 监听页面可见性
window.addEventListener("visibilitychange", this.pageHidden);
this.$nextTick(function () {
let body = document.querySelector("body");
body.style.overflow = "auto";
this.flexLeft = (body.offsetWidth - 1200) / 2;
});
},
window.addEventListener(“visibilitychange”, this.pageHidden)这个就是添加监听页面显示与隐藏的事件,当页面的内容变得可见或被隐藏时,会触发 visibilitychange (能见度更改)事件。
//切换页面检测
//isReduce 0扣次数 1不扣次数 router 判断是否为路由转跳
//事件默认参数
pageHidden(e = null, isReduce = 0, router = false) {
return new Promise((resolve, reject) => {
if (document.visibilityState === "hidden" || router) {
this.axios({
method: "post",
url: "/knowledge/exam/saveSwitchPageCount",
data: {
pkExam: this.testData.pkExam,
pkPaper: this.testData.pkPaper,
startTime: this.testData.startTime,
pkCurExam: this.testData.pkCurExam,
isFirst: isReduce,
endTime: this.testData.endTime
}
}).then(res => {
let data = res.data;
if (data.code == "0") {
this.switchPage = data.data;
//remaTimes 可切片次数大于0
if (
this.switchPage.remaTimes >= 0 &&
!this.isStop &&
(this.switchPage.remaTimes != this.switchPage.switchPageTimes ||
(this.switchPage.remaTimes != 0 &&
this.switchPage.switchPageTimes != 0)) &&
this.switchPage.switchPageTimes != 1000
) {
this.tipsFlag = true;
this.tips = 2;
} else if (this.switchPage.remaTimes < 0 && !this.isStop) {
this.submitTest();
}
resolve();
} else {
reject();
}
});
}
});
},
document.visibilityState(只读属性), 返回document的可见性, 即当前可见元素的上下文环境. 由此可以知道当前文档(即为页面)是在背后, 或是不可见的隐藏的标签页,或者(正在)预渲染.有三个值(为字符串类型):
1、visible: 此时页面内容至少是部分可见, 即此页面在前景标签页中,并且窗口没有最小化。
2、hidden: 此时页面对用户不可见, 即文档处于背景标签页或者窗口处于最小化状态,或者操作系统正处于 ‘锁屏状态’ 。
3、prerender: 页面此时正在渲染中, 因此是不可见的 (considered hidden for purposes of document.hidden).,文档只能从此状态开始,永远不能从其他值变为此状态。
submitTest() {
this.loading = true;
this.axios({
method: "post",
url: "/knowledge/exam/submitPaper",
data: {
pkExam: this.pkExam,
pkPaper: this.testData.pkPaper,
startTime: this.testData.startTime,
endTime: this.testData.endTime,
pkCurExam: this.testData.pkCurExam
}
}).then(res => {
let data = res.data;
this.loading = false;
if (data.code == "0") {
this.isStop = true;
this.tipsFlag = false;
this.testResult = data.data;
clearInterval(this.countdownTime);
} else {
this.MixerrorMes(data.message);
}
});
}
这是提交试卷的方法,在切屏次数超过设置次数后会强行调用此方法去获取考试结果。
destroyed() {
window.removeEventListener("visibilitychange", this.pageHidden);
window.removeEventListener("scroll", this.handleScroll);
window.removeEventListener("resize", this.getLfetDistance);
clearInterval(this.countdownTime); // 计时器
}
当然了最后还要移除这些添加的事件和计时器