Vue realizes the lock screen function

I just made a new request in the past two days. I want to make a system lock screen (of course, the current system is locked), which is similar to a computer lock screen.

There are two situations to lock the screen, one is to automatically lock the screen after a certain period of time without operation; the second is to press the combination key (shortcut key) to lock the screen actively. Let's talk about the idea, idea and realization of this requirement in detail.

Then talk about your thoughts. First of all, there are generally two ways to realize the lock screen:

One is to write a page and jump to the lock screen page when the conditions are met, but there are some points to pay attention to, for example: if $router.push is used when jumping, then I can click to browse The back button of the browser returns to the previous page, this needs to be restricted. Another possibility is that if I directly enter the path of a certain page on the browser, will it also jump? So we need a global variable to record whether the screen is currently locked, and if it is locked, we need to restrict it. Of course, these are just two examples. There may be other situations in practice. Anyway, you must think carefully, otherwise there will be unexpected bugs later.

The second is to directly write the mask layer, which is also the way I currently implement the lock screen. Compared with the above method, this method is simpler and more convenient. We only need to display the mask layer after meeting the conditions. Of course, the priority of the mask layer must be set to the highest. Otherwise, how to overwrite any current page? It is precisely because this priority is the highest that some message prompts or pop-up windows such as $message cannot be displayed in this mask layer component. This is also a defect. However, there are not many operations on the general lock screen page. The main thing is that the page has a background image, and then enter the unlock password to unlock it, so the impact is not great. There is another thing to note, that is, I have locked the screen now. If I open the console at this time, can I modify the style of the mask layer? For example, if I add display: none; to the mask layer, is the mask layer gone? The answer is yes, so we have to limit! There are two ways to open the console, one is F12 (Fn + F12), the other is the right mouse button, and then click Check. In this way, when we lock the screen, we will monitor the keyboard press event, prevent the F12 button, and also prevent the right button of the mouse.

Let's talk about the way to achieve it.

First of all, I wrote a separate component for the mask layer, which also contains the style and logic of the mask layer. Note that this is a component, not a page, and it can be imported wherever it is used.

Then there is the question of when the mask layer will be displayed. Since it is a lock screen, it must have nothing to do with login, that is, we have to start timing as soon as we enter the system. To lock the screen, then we only need to count the time of no operation. What is an operation? What is no operation, mouse click is operation, mouse double-click is operation, mouse right button is operation, mouse movement is operation, mouse wheel is operation, keyboard pressing is operation, basically these events, that is, we When we first enter the system, we need to listen to these events and start timing at the same time (timing is to define the initial value of the variable as 0, and then setInterval increases by 1 every 1s). If one of the events is triggered, we need to clear the timer. , and initialize the variable to the initial value, which is 0, and restart the timing, which is basically the same as anti-shake. Then we will display the mask layer after we have no operation to meet the time, that is, enter the lock screen state. This is, we have to remove those event listeners and clear the timer, but be careful not to initialize the variable to 0, because We need to compare this variable with our setting time to decide whether to display the mask layer. After entering the mask layer, it is the operation of the mask layer. After unlocking, we initialize the value of the variable to 0 to hide the mask layer. And re-listen to those events, re-timing on it.

Oops, I said too much, I said too much~ Here is the code

We first define a variable in the .env (development / production) file. This variable is how long it takes to enter the lock screen without any operation, and the unit is s. configurable

# 锁屏时间(s)
VUE_APP_LOCK_TIME = 300

This means that after 5 minutes of no operation, it will enter the lock screen state. 

Then we need to save a timing time lockTime in Vuex, that is, when this time is greater than or equal to the time we set, we will enter the lock screen, that is, lockTime >= parseInt(process.env.VUE_APP_LOCK_TIME)

I have divided a module common here (src/store/modules/common.js)

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

Then there is the logic of when the mask layer (lock screen) is displayed: 

I have encapsulated it in mixins here. This is to separate the logic of the lock screen, and the logic is clearer. Later, if I don’t want to lock the screen in the entire system, I only need to be on certain pages, that is, Only in the system's A page and B page will there be a lock screen, and the other does not need a lock screen, then we can directly mix the logic into the component.

// 引入锁屏遮罩层组件
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()
      }
    }
  }
}

Then there is the lock screen component (the mask layer component) 

@/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>

Finally, the above logic lock.js is mixed into App.vue, and then the lock.vue component is introduced and used 

Because my requirement is that the entire system must have the function of locking the screen, so I write it in 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>

It’s better to give everyone a style effect of the lock screen mask layer. Of course, this is displayed after 5 minutes of no operation. 

  

Well, that’s all for this article ^v^ 

Guess you like

Origin blog.csdn.net/m0_51431448/article/details/131095704