Vue實(shí)現(xiàn)鎖屏功能的示例代碼
這兩天剛做了個(gè)新需求,要做個(gè)系統(tǒng)鎖屏(當(dāng)然鎖的是當(dāng)前的系統(tǒng)),就類似于電腦鎖屏似的。
共兩種情況下鎖屏,一種是無(wú)操作一定時(shí)間后自動(dòng)鎖屏;第二種是可以按下組合鍵(快捷鍵)主動(dòng)進(jìn)行鎖屏。下面具體來(lái)說(shuō)一下該需求的想法、思路以及實(shí)現(xiàn)
然后再說(shuō)下想法思路吧。首先實(shí)現(xiàn)鎖屏一般也就兩種方式:
一種是去寫(xiě)個(gè)頁(yè)面,當(dāng)達(dá)到條件時(shí)要跳轉(zhuǎn)到該鎖屏頁(yè)面,但是有一些要注意的地方, 比如:如果跳轉(zhuǎn)的時(shí)候使用的$router.push,那么我是可以通過(guò)點(diǎn)擊瀏覽器的回退按鈕回到上一個(gè)頁(yè)面的,這個(gè)要做一下限制。還有一種可能是我如果直接在瀏覽器上輸入某個(gè)頁(yè)面的路徑,那么是不是也會(huì)跳轉(zhuǎn)?所以我們要有個(gè)全局的變量來(lái)記錄當(dāng)前是不是鎖屏狀態(tài),如果是鎖屏狀態(tài),我們要對(duì)其進(jìn)行限制。當(dāng)然這只是舉例的兩種情況,真實(shí)做起來(lái)或許還有其他情況,反正要考慮周全,不然后面會(huì)出現(xiàn)你意想不到的bug
第二種就是直接去寫(xiě)遮罩層了,也是我當(dāng)前實(shí)現(xiàn)鎖屏的方式。這種方式相對(duì)上面來(lái)講,會(huì)比較簡(jiǎn)單一些,也還算方便,我們只需要在達(dá)到條件后將該遮罩層顯示出來(lái)就可以了,當(dāng)然遮罩層的優(yōu)先級(jí)肯定要設(shè)置最高的,要不然怎么覆蓋掉任意當(dāng)前的頁(yè)面呢,也正是因?yàn)檫@個(gè)優(yōu)先級(jí)最高,導(dǎo)致在這個(gè)遮罩層組件中有些$message這樣的消息提示或彈窗顯示不出來(lái)。這也算是一個(gè)缺陷。不過(guò)一般鎖屏頁(yè)面也沒(méi)什么太多操作,主要是頁(yè)面有個(gè)背景圖,然后輸入解鎖密碼進(jìn)行解鎖就可以了,所以影響并不大。還有一種需要注意,就是我現(xiàn)在已經(jīng)鎖屏頁(yè)面了,我這時(shí)候打開(kāi)控制臺(tái),然后是不是可以去修改這個(gè)遮罩層的樣式呢?比如我給遮罩層加個(gè)display: none; 遮罩層是不是沒(méi)了?答案是的,所以我們要做限制! 打開(kāi)控制臺(tái)的方式無(wú)非也就兩種方式,一種是F12(Fn + F12),另一種是鼠標(biāo)右鍵,然后點(diǎn)擊檢查。這樣,我們鎖屏出來(lái)的時(shí)候就監(jiān)聽(tīng)鍵盤(pán)按下事件,阻止F12的按鍵,同時(shí)也阻止鼠標(biāo)右鍵行為,這樣就可以了,看上去簡(jiǎn)單粗暴哈,但也是為了實(shí)現(xiàn)這么個(gè)需求嘛。
下面再說(shuō)一下實(shí)現(xiàn)的方式。
首先我把遮罩層單獨(dú)寫(xiě)了個(gè)組件,里面也都是遮罩層的樣式、邏輯。注意這是個(gè)組件,而不是個(gè)頁(yè)面,哪里用到哪里引入就可以了。
然后就是遮罩層什么時(shí)候顯示的問(wèn)題了,既然是鎖屏,那么肯定是跟登錄沒(méi)有關(guān)系的,也就是我們要?jiǎng)傄贿M(jìn)系統(tǒng)就開(kāi)始計(jì)時(shí)了,比如我們的需求是一分鐘無(wú)操作后就要鎖屏,那么我們只需要這個(gè)計(jì)時(shí)無(wú)操作的時(shí)間就可以了,什么算是操作了?什么算是無(wú)操作,鼠標(biāo)點(diǎn)擊就是操作了,鼠標(biāo)雙擊是操作了,鼠標(biāo)右鍵是操作了,鼠標(biāo)移動(dòng)是操作了,鼠標(biāo)滾輪是操作了,鍵盤(pán)按下是操作了,基本就這些事件,也就是我們?cè)趧傔M(jìn)入系統(tǒng)時(shí)就要監(jiān)聽(tīng)這些事件,同時(shí)也開(kāi)始計(jì)時(shí)(計(jì)時(shí)就是定義變量初始值為0,然后setInterval每隔1s加1),如果有其中某個(gè)事件觸發(fā)了,我們就要清除掉定時(shí)器,并將變量初始化為初始值也就是0,并重新計(jì)時(shí),基本跟防抖差不多。然后我們無(wú)操作滿足時(shí)間后會(huì)顯示遮罩層,也就是進(jìn)入鎖屏狀態(tài),這個(gè)是,我們就要把那些事件監(jiān)聽(tīng)移除掉,并清除掉定時(shí)器,但是注意不要將變量初始化為0,因?yàn)槲覀円ㄟ^(guò)這個(gè)變量和我們?cè)O(shè)置時(shí)間做對(duì)比來(lái)決定是否顯示遮罩層,進(jìn)入遮罩層之后就是遮罩層的操作了,解鎖后呢,我們將變量的值初始化為0,隱藏遮罩層,并重新監(jiān)聽(tīng)那些事件,重新計(jì)時(shí)就可以了。
哎喲,說(shuō)太多了說(shuō)太多了~ 下面上代碼了
我們首先在.env(development / production)的文件中定義個(gè)變量,這個(gè)變量就是無(wú)操作多長(zhǎng)時(shí)間進(jìn)入鎖屏,單位 s。也就是可配置的
# 鎖屏?xí)r間(s)
VUE_APP_LOCK_TIME = 300
這個(gè)也就是說(shuō)無(wú)操作5分鐘后進(jìn)入鎖屏狀態(tài)
然后我們需要在Vuex中保存一個(gè)計(jì)時(shí)時(shí)間lockTime,也就是當(dāng)這個(gè)時(shí)間大于等于我們?cè)O(shè)置的那個(gè)時(shí)間就進(jìn)入鎖屏,即 lockTime >= parseInt(process.env.VUE_APP_LOCK_TIME)
我這里分了個(gè)模塊 common (src/store/modules/common.js)
export default { namespaced: true, state: { // 無(wú)操作計(jì)時(shí)時(shí)間 lockTime: 0 }, mutations: { updatelockTime(state, lockTime) { state.lockTime = lockTime } } }
然后是遮罩層(鎖屏)什么時(shí)候進(jìn)行顯示的邏輯:
我這里是封裝在了mixins中,這是為了可以把鎖屏的邏輯單獨(dú)抽離出來(lái),邏輯更清晰一些,后面如果說(shuō)我不想在整個(gè)系統(tǒng)鎖屏了,我只要在某些頁(yè)面中,也就是只在系統(tǒng)的A頁(yè)面、B頁(yè)面中才會(huì)有鎖屏,其他都不需要鎖屏,那我們直接將邏輯混入到組件就可以了。
// 引入鎖屏遮罩層組件 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) }, // 移除事件監(jiān)聽(tīng)、定時(shí)器等 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) }, // 無(wú)操作計(jì)時(shí) reckonByTime() { if (this.timer) { this.lockTime = 0 clearInterval(this.timer) this.timer = null } this.timer = setInterval(() => { this.lockTime += 1 }, 1000) }, // 鼠標(biāo)點(diǎn)擊 clickHandle() { this.reckonByTime() }, // 鼠標(biāo)雙擊 dblclickHandle() { this.reckonByTime() }, // 鼠標(biāo)右鍵 contextmenuHandle() { this.reckonByTime() }, // 鼠標(biāo)移動(dòng) mousemoveHandle() { this.reckonByTime() }, // 鼠標(biāo)滾輪 mousewheelHandle() { this.reckonByTime() }, // 鍵盤(pán)按下 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">密碼不正確。請(qǐng)?jiān)僭囈淮巍?lt;/div> <div class="confirm-btn" @click="reset()">確認(rèn)</div> </div> </div> <!-- <img @click="logout" class="logout" src="../../assets/img/system/logout.png"> --> </div> </template> <script> /** * 注意:由于遮罩層優(yōu)先級(jí)太高 類似于$message之類的消息提示等會(huì)顯示不出來(lái) */ 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) }, // 監(jiān)聽(tīng)鼠標(biāo)按下事件,阻止 F12 事件 keyDownHandle(event) { if(event.keyCode == 13) { if(this.passwdError) { this.reset() } else { this.unLock() } } return (event.keyCode !== 123 ) || (event.returnValue = false) }, // 阻止鼠標(biāo)右鍵事件 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組件并使用
因?yàn)槲业男枨笫钦麄€(gè)系統(tǒng)都要有鎖屏的功能,所以我將其寫(xiě)在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>
最好再給大家上個(gè)鎖屏遮罩層的樣式效果吧,我這個(gè)當(dāng)然是在無(wú)操作5分鐘才顯示出來(lái)的哈
以上就是Vue實(shí)現(xiàn)鎖屏功能的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Vue鎖屏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在 Vue-CLI 中引入 simple-mock實(shí)現(xiàn)簡(jiǎn)易的 API Mock 接口數(shù)據(jù)模擬
本文以 Vue-CLI 為例介紹引入 simple-mock 實(shí)現(xiàn)前端開(kāi)發(fā)數(shù)據(jù)模擬的步驟。感興趣的朋友跟隨小編一起看看吧2018-11-11關(guān)于antd中select搜索框改變搜索值的問(wèn)題
這篇文章主要介紹了關(guān)于antd中select搜索框改變搜索值的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04基于vue中解決v-for使用報(bào)紅并出現(xiàn)警告的問(wèn)題
下面小編就為大家分享一篇基于vue中解決v-for使用報(bào)紅并出現(xiàn)警告的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Vue中.vue文件比main.js先執(zhí)行的問(wèn)題及解決
這篇文章主要介紹了Vue中.vue文件比main.js先執(zhí)行的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12vuejs router history 配置到iis的方法
今天小編就為大家分享一篇vuejs router history 配置到iis的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue項(xiàng)目啟動(dòng)時(shí)無(wú)法識(shí)別es6的擴(kuò)展語(yǔ)法的解決
啟動(dòng)項(xiàng)目時(shí)遇到ES6的拓展運(yùn)算符報(bào)錯(cuò)可以通過(guò)切換到淘寶鏡像,以及在項(xiàng)目根目錄下新增.babelrc和postcss.config.js文件來(lái)解決,這些操作有助于正確配置項(xiàng)目環(huán)境,從而避免報(bào)錯(cuò),并保證項(xiàng)目的順利運(yùn)行,希望這些經(jīng)驗(yàn)?zāi)軌驇椭接龅较嗤瑔?wèn)題的開(kāi)發(fā)者2024-10-10