今天分享一个考试防止切屏作弊的功能

需求

效果如动图:在这里插入图片描述
需求描述:不难看出,这是只要考试页面不在当前页面的都会被看做是切屏,当超过设置的考试切屏允许的最大次数时(在考试管理里面会有这个功能),就会自动交卷停止作答。

关键点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); // 计时器
  }

当然了最后还要移除这些添加的事件和计时器

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐