Vue实现锁屏功能

news2024/11/26 18:37:40

这两天刚做了个新需求,要做个系统锁屏(当然锁的是当前的系统),就类似于电脑锁屏似的。

共两种情况下锁屏,一种是无操作一定时间后自动锁屏;第二种是可以按下组合键(快捷键)主动进行锁屏。下面具体来说一下该需求的想法、思路以及实现

然后再说下想法思路吧。首先实现锁屏一般也就两种方式:

一种是去写个页面,当达到条件时要跳转到该锁屏页面,但是有一些要注意的地方, 比如:如果跳转的时候使用的$router.push,那么我是可以通过点击浏览器的回退按钮回到上一个页面的,这个要做一下限制。还有一种可能是我如果直接在浏览器上输入某个页面的路径,那么是不是也会跳转?所以我们要有个全局的变量来记录当前是不是锁屏状态,如果是锁屏状态,我们要对其进行限制。当然这只是举例的两种情况,真实做起来或许还有其他情况,反正要考虑周全,不然后面会出现你意想不到的bug

第二种就是直接去写遮罩层了,也是我当前实现锁屏的方式。这种方式相对上面来讲,会比较简单一些,也还算方便,我们只需要在达到条件后将该遮罩层显示出来就可以了,当然遮罩层的优先级肯定要设置最高的,要不然怎么覆盖掉任意当前的页面呢,也正是因为这个优先级最高,导致在这个遮罩层组件中有些$message这样的消息提示或弹窗显示不出来。这也算是一个缺陷。不过一般锁屏页面也没什么太多操作,主要是页面有个背景图,然后输入解锁密码进行解锁就可以了,所以影响并不大。还有一种需要注意,就是我现在已经锁屏页面了,我这时候打开控制台,然后是不是可以去修改这个遮罩层的样式呢?比如我给遮罩层加个display: none; 遮罩层是不是没了?答案是的,所以我们要做限制! 打开控制台的方式无非也就两种方式,一种是F12(Fn + F12),另一种是鼠标右键,然后点击检查。这样,我们锁屏出来的时候就监听键盘按下事件,阻止F12的按键,同时也阻止鼠标右键行为,这样就可以了,看上去简单粗暴哈,但也是为了实现这么个需求嘛。

下面再说一下实现的方式。

首先我把遮罩层单独写了个组件,里面也都是遮罩层的样式、逻辑。注意这是个组件,而不是个页面,哪里用到哪里引入就可以了。

然后就是遮罩层什么时候显示的问题了,既然是锁屏,那么肯定是跟登录没有关系的,也就是我们要刚一进系统就开始计时了,比如我们的需求是一分钟无操作后就要锁屏,那么我们只需要这个计时无操作的时间就可以了,什么算是操作了?什么算是无操作,鼠标点击就是操作了,鼠标双击是操作了,鼠标右键是操作了,鼠标移动是操作了,鼠标滚轮是操作了,键盘按下是操作了,基本就这些事件,也就是我们在刚进入系统时就要监听这些事件,同时也开始计时(计时就是定义变量初始值为0,然后setInterval每隔1s加1),如果有其中某个事件触发了,我们就要清除掉定时器,并将变量初始化为初始值也就是0,并重新计时,基本跟防抖差不多。然后我们无操作满足时间后会显示遮罩层,也就是进入锁屏状态,这个是,我们就要把那些事件监听移除掉,并清除掉定时器,但是注意不要将变量初始化为0,因为我们要通过这个变量和我们设置时间做对比来决定是否显示遮罩层,进入遮罩层之后就是遮罩层的操作了,解锁后呢,我们将变量的值初始化为0,隐藏遮罩层,并重新监听那些事件,重新计时就可以了。

哎哟,说太多了说太多了~    下面上代码了

我们首先在.env(development / production)的文件中定义个变量,这个变量就是无操作多长时间进入锁屏,单位 s。也就是可配置的

# 锁屏时间(s)
VUE_APP_LOCK_TIME = 300

这个也就是说无操作5分钟后进入锁屏状态 

然后我们需要在Vuex中保存一个计时时间lockTime,也就是当这个时间大于等于我们设置的那个时间就进入锁屏,即 lockTime >= parseInt(process.env.VUE_APP_LOCK_TIME)

我这里分了个模块 common (src/store/modules/common.js)

export default {
  namespaced: true,
  state: {
    // 无操作计时时间
    lockTime: 0
  },
  mutations: {
    updatelockTime(state, lockTime) {
      state.lockTime = lockTime
    }
  }
}

然后是遮罩层(锁屏)什么时候进行显示的逻辑: 

我这里是封装在了mixins中,这是为了可以把锁屏的逻辑单独抽离出来,逻辑更清晰一些,后面如果说我不想在整个系统锁屏了,我只要在某些页面中,也就是只在系统的A页面、B页面中才会有锁屏,其他都不需要锁屏,那我们直接将逻辑混入到组件就可以了。

// 引入锁屏遮罩层组件
import Lock from '@/components/lock'

export default {
  data() {
    return {
      timer: null
    }
  },
  components: { Lock },
  computed: {
    isLock() {
      return this.lockTime >= parseInt(process.env.VUE_APP_LOCK_TIME)
    },
    lockTime: {
      get() {
        return this.$store.state.common.lockTime
      },
      set(lockTime) {
        this.$store.commit('common/updatelockTime', lockTime)
      }
    }
  },
  watch: {
    isLock() {
      if (this.isLock) {
        this.removeHandle()
      } else {
        this.init()
      }
    }
  },
  mounted() {
    this.init()
  },
  destroyed() {
    this.removeHandle()
  },
  methods: {
    init() {
      this.reckonByTime()
      document.addEventListener('click', this.clickHandle)
      document.addEventListener('dblclick', this.dblclickHandle)
      document.addEventListener('contextmenu', this.contextmenuHandle)
      document.addEventListener('mousemove', this.mousemoveHandle)
      document.addEventListener('mousewheel', this.mousewheelHandle)
      document.addEventListener('keydown', this.keydownHandle)
    },
    // 移除事件监听、定时器等
    removeHandle() {
      if (this.timer) {
        clearInterval(this.timer)
        this.timer = null
      }
      document.removeEventListener('click', this.clickHandle)
      document.removeEventListener('dblclick', this.dblclickHandle)
      document.removeEventListener('contextmenu', this.contextmenuHandle)
      document.removeEventListener('mousemove', this.mousemoveHandle)
      document.removeEventListener('mousewheel', this.mousewheelHandle)
      document.removeEventListener('keydown', this.keydownHandle)
    },
    // 无操作计时
    reckonByTime() {
      if (this.timer) {
        this.lockTime = 0
        clearInterval(this.timer)
        this.timer = null
      }
      this.timer = setInterval(() => {
        this.lockTime += 1
      }, 1000)
    },
    // 鼠标点击
    clickHandle() {
      this.reckonByTime()
    },
    // 鼠标双击
    dblclickHandle() {
      this.reckonByTime()
    },
    // 鼠标右键
    contextmenuHandle() {
      this.reckonByTime()
    },
    // 鼠标移动
    mousemoveHandle() {
      this.reckonByTime()
    },
    // 鼠标滚轮
    mousewheelHandle() {
      this.reckonByTime()
    },
    // 键盘按下
    keydownHandle(event) {
      const { altKey, ctrlKey, keyCode } = event
      if (altKey && ctrlKey && keyCode == 76) {
        // Ctrl + Alt + L 快捷键直接锁屏
        this.lockTime = parseInt(process.env.VUE_APP_LOCK_TIME)
      } else {
        this.reckonByTime()
      }
    }
  }
}

然后是锁屏组件(遮罩层组件) 

@/components/lock/index.vue 

<template>
  <div class="lock">
    <div>
      <img src="../../assets/img/lock/lock-icon.png" alt="">
      <el-input class="lockPasswdInp" v-show="!passwdError" ref="lockPasswdInp" size="small" show-password placeholder="密码" v-model="passwd">
        <template slot="append">
          <el-button @click="unLock()" style="background: transparent;">
            <i class="el-icon-right"></i>
          </el-button>
        </template>
      </el-input>
      <div class="passwdError-container" v-show="passwdError">
        <div class="passwdError">密码不正确。请再试一次。</div>
        <div class="confirm-btn" @click="reset()">确认</div>
      </div>
    </div>
    <!-- <img @click="logout" class="logout" src="../../assets/img/system/logout.png"> -->
  </div>
</template>
<script>
/**
 * 注意:由于遮罩层优先级太高 类似于$message之类的消息提示等会显示不出来
 */
export default {
  data() {
    return {
      passwd: '',
      passwdError: false
    }
  },
  computed: {
    lockPassword: () => process.env.VUE_APP_LOCK_PASSWORD || '123456'
  },
  mounted() {
    this.reset()
    document.addEventListener('keydown', this.keyDownHandle)
    document.addEventListener('contextmenu', this.contextmenuHandle)
  },
  destroyed() {
    document.removeEventListener('keydown', this.keyDownHandle)
    document.removeEventListener('contextmenu', this.contextmenuHandle)
  },
  methods:{
    // 重置初始化
    reset() {
      this.passwdError = false
      this.passwd = ''
      this.$nextTick(() => {
        this.$refs.lockPasswdInp.focus()
      })
    },
    // 解锁
    unLock() {
      if(this.passwd != this.lockPassword) {
        return this.passwdError = true
      }
      this.$store.commit('common/updatelockTime', 0)
    },
    // 监听鼠标按下事件,阻止 F12 事件
    keyDownHandle(event) {
      if(event.keyCode == 13) {
        if(this.passwdError) {
          this.reset()
        } else {
          this.unLock()
        }
      }
      return (event.keyCode !== 123 ) || (event.returnValue = false)
    },
    // 阻止鼠标右键事件
    contextmenuHandle(event) {
      return event.returnValue = false;
    },
    // // 退出登录
    // logout() {
    //   this.$store.commit('common/updatelockTime', 0)
    //   this.$router.replace('/login')
    //   /**
    //    * 走接口 清楚本地缓存等
    //    * ...
    //    */
    // }
  }
}
</script>
<style scoped>
.lock {
  width: 100%;
  height: 100vh;
  background: #ccc;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999999;
  display: flex;
  justify-content: center;
  align-items: center;
  background-image: url('../../assets/img/lock/lock-bg.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.lock > div {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.lockPasswdInp {
  margin-top: 8px;
}

/deep/ .el-input__inner {
  background-color: transparent !important;
  border: 1px solid #0076c8 !important;
  color: #fff;
}
/deep/ .el-input-group__append {
  background-color: rgba(6, 14, 22, .5);
  border: 1px solid #0076c8;
  border-left-color: transparent;
}

/deep/ .el-input-group__append:hover {
  background-color: rgba(6, 14, 22, .6);
  cursor: pointer;
}

.passwdError-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.passwdError {
  width: 260px;
  text-align: center;
  color: #fff;
  font-size: 13px;
  margin: 10px 0;
}

.confirm-btn {
  width: 70px;
  height: 28px;
  text-align: center;
  line-height: 28px;
  color: #fff;
  font-size: 13px;
  background-color: rgba(6, 14, 22, .5);
  border: 1px solid #0076c8;
  border-radius: 3px;
  cursor: pointer;
}

.confirm-btn:hover {
  background-color: rgba(6, 14, 22, .8);
}

/* .logout {
  position: fixed;
  bottom: 20px;
  right: 20px;
  height: 40px;
  cursor: pointer;
} */
</style>

最后App.vue中混入上面的逻辑lock.js,然后引入lock.vue组件并使用 

因为我的需求是整个系统都要有锁屏的功能,所以我将其写在App.vue中 

<template>
  <div id="app">
    <router-view />
    <Lock v-if="isLock" />
  </div>
</template>
<script>
import lockMixin from "@/mixins/lock";
export default {
  mixins: [lockMixin]
}
</script>
<style lang="scss">
* {
  margin: 0;
  padding: 0;
}

#app {
  width: 100vw;
  height: 100vh;
  overflow-y: hidden;
}
</style>

最好再给大家上个锁屏遮罩层的样式效果吧,我这个当然是在无操作5分钟才显示出来的哈 

  

好了,这篇就这么多^v^ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/621249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用ThreadLocal+AOP切面编程实现RPC日志

前言 最近在稍微学了一下ThreadLocal以及它的使用场景&#xff0c;同时也学了一下AOP相关的内容&#xff0c;刚好做个很常见的RPC日志来练练手。 主要思想是通过切面来拦截所有的请求&#xff0c;在请求进入切面的时候&#xff0c;可以用ThreadLocal来存储当前请求的线程专属的…

微信小程序基础使用

微信小程序的基本使用 微信小程序文件类型 微信小程序主要提供了 4 种文件类型&#xff1a; 类型名称作用是否必须存在.wxml用于页面的布局结构&#xff0c;相当于网页中 .html 文件是.wxss用于页面的样式&#xff0c;相当于网页中的 .css 文件否.js用于页面的逻辑是.json用…

springboot期末随笔,随缘随缘啊哈哈

一、使用ImportResource加载XML配置文件示例&#xff1a; 创建一个XML配置文件&#xff0c;命名为person.xml&#xff0c;在文件中定义一个Person类的bean&#xff1a; <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.…

腾讯云轻量应用服务器是什么?和CVM云服务器有什么区别?

腾讯云轻量应用服务器是什么&#xff1f;腾讯云轻量服务器和云服务器有什么区别&#xff1f;为什么轻量应用服务器费用更低&#xff1f;是因为轻量服务器CPU内存性能比云服务器CVM性能差吗&#xff1f;轻量应用服务器适合中小企业或个人开发者搭建企业官网、博客论坛、微信小程…

(浙大陈越版)数据结构 第三章 树(中) 二叉搜索树和平衡二叉树

目录 4.1.1 二叉搜索树及查找 什么是二叉搜索树 定义 二叉搜索树特殊函数集&#xff1a; 查找操作&#xff1a;Find 算法思想 代码实现 补&#xff1a;查找最大和最小元素 4.1.2 二叉搜索树的插入 插入操作&#xff1a;Insert 算法思想 代码实现 例题 4.1.3 二叉…

JavaSE_day49-50(XML,DTD约束,schema约束,解析XML,DOM实现解析XML文件)

注意&#xff1a;学习下面内容需要参考那个文档w3cschool&#xff08;学习Java资源&#xff09;&#xff0c;Gitee上传&#xff01; 1 Xml格式&#xff1a; <?xml version"1.0" encoding"UTF-8"?><!-- 这是注释--><Students><stude…

【MySQL数据库 | 第十三篇】多表查询

前言&#xff1a; 多表查询是指在一个SQL语句中使用多个表进行数据查询和操作。多表查询可以对数据表之间的关系进行查询&#xff0c;例如可以通过连接多个表来获取更完整的数据信息。关于单表查询我们也介绍过&#xff0c;已经整理成文章发布&#xff1a;【MySQL数据库 | 第九…

vue2模板渲染更新详细流程

vue2模板渲染更新详细流程 此文章基于vue2.6.10版本进行解析&#xff0c;在看文章最好结合源码一起看能帮助更快的理解。 在vue中会将.vue文件或者template属性解析成一个render函数&#xff0c;在渲染&#xff08;调用$mount方法&#xff09;的时候通过执行这个render函数生…

#systemverilog# 关于流操作符>>和<<引发的思考

前言 对于流操作符&#xff0c;相比大家都不陌生&#xff0c;在实际项目中运用特别广泛。今天我们通过几个小例子&#xff0c;来回顾和深层认识一下该操作符。 概念 流操作符&#xff08;bit-stream&#xff09;&#xff0c;表示方式为{>>{}} 和 {<<{}}。前者会…

spring源码 IoC 之xml配置bean注册解析的 BeanDefinitions

概述 IoC 容器的初始化过程分为三步骤&#xff1a;Resource 定位、BeanDefinition 的载入和解析&#xff0c;BeanDefinition 注册 1、Resource 定位。我们一般用外部资源来描述 Bean 对象&#xff0c;所以在初始化 IoC 容器的第一步就是需要定位这个外部资源 2、BeanDefinition…

opencv4 傅里叶变换

傅里叶变换 ① 高频&#xff1a;变化剧烈的灰度分量&#xff0c;例如边界礁石。 ② 低频&#xff1a;变化缓慢的灰度分量&#xff0c;例如一片大海。 ③ 高通滤波器&#xff1a;只保留高频&#xff0c;会使得图像细节增强。高频边界锐化了&#xff0c;增强了&#xff0c;细节…

【群智能算法改进】一种改进的算术优化算法 改进算术优化算法 改进AOA[2]【Matlab代码#38】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始AOA2. 改进后的MAOA算法2.1. Kent映射种群初始化2.2 复合摆线法优化MOA参数2.3 种群top20%精英变异和Cauchy变异组合2.3.1 麻雀精英变异2.3.2 柯西变异 3. 部分代码展示4. 仿真结果展示5. 资源获取说明 【获取资…

高性能软件负载OpenResty整合Reids集群配置

目录 1 OpenResty整合Reids集群配置1.1 下载安装lua_resty_redis1.1.1 连接Redis集群封装1.1.2 配置lua脚本路径1.1.3 测试脚本 1.2 请求参数封装1.2.1 测试脚本 1.3 抓取模板内容封装1.3.1 下载安装lua-resty-http1.3.2 测试脚本 1.4 模版渲染配置1.4.1 下载安装lua-resty-tem…

基于RK3399/RK3588 H.265/HEVC的低延迟视频传输系统设计与实现

近年来&#xff0c;随着短视频直播的兴起&#xff0c;视频传输设备在生活中的应用越发普及。人们对图像 清晰度、帧率、码率等技术指标的要求不断提高&#xff0c;视频帧所包含的数据量也在急速增加。在 有限的网络带宽下&#xff0c;传统的视频采集设备面临压缩率不足、帧率…

Ui自动化测试如何上传文件

前言 实施UI自动化测试的时候&#xff0c;经常会遇见上传文件的操作&#xff0c;那么对于上传文件你知道几种方法呢&#xff1f;今天我们就总结一下几种常用的上传文件的方法&#xff0c;并分析一下每个方法的优点和缺点以及哪种方法效率&#xff0c;稳定性更高 被测HTML代码…

python基础知识(九):函数

目录 1. 函数的定义2. 传递实参2.1 位置实参2.2 关键字实参2.3 默认值2.4 传递任意数量的实参2.5 结合使用位置实参和任意数量实参 3. 导入模块3.1 导入特定的函数3.2 使用 as 给函数指定别名3.3 使用 as 给模块指定别名3.4 导入模块中的所有函数 1. 函数的定义 函数的定义形式…

Wijmo 5.20231.888 JavaScript UI Crack

Wijmo使用更快、更灵活的 JavaScript UI 组件构建更好的应用程序 使用 Wijmo&#xff0c;利用我们引人注目的 UI 组件库&#xff0c;将更多时间花在应用程序的核心功能上。要求零依赖&#xff0c;Wijmo sports弹性网格&#xff0c;业内最好的 JavaScript 数据网格&#xff0c;提…

基于深度学习的高精度动物检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度动物检测识别系统可用于日常生活中或野外来检测与定位动物目标&#xff08;狼、鹿、猪、兔和浣熊&#xff09;&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的动物&#xff08;狼、鹿、猪、兔和浣熊&#xff09;目标检测识…

前端基础面试题(HTML,CSS,JS)

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 html语义化的理解 代码结构: 使页面在没有css的情况下,也能够呈现出好的内容结构 有利于SE…

AI实战营:MMPreTrain代码实现

环境 环境安装 pip install openmim mim install mmengine mim install mmcv mim install mmpretrain # 安装多模态模型 pip install "mmpretrain[multimodal]" 验证环境 In [1]: import mmengineIn [2]: mmengine.__version__ Out[2]: 0.7.3In [3]: import …