需求:vue3项目中,在登录页面,账号密码输入错误,后台返回剩余次数lockRemain,锁定时间秒数lockSeconds。前端根据返回数据,锁定登录及倒计时等信息展示。

思路

1. 当登录错误,后台返回剩余次数不为0时,页面展示账号密码错误,剩余几次机会。

2. 当剩余次数lockRemain为0以及有锁定时间秒数,使用localStorage存储,锁定时间秒数lockSeconds,以及解锁时间lockEndTime。lockEndTime为当前时间加上锁定秒数。 后续根据当前时间是否超过lockEndTime,判断是否需要锁定。如若锁定则进行倒计时。

localStorage知识点:

需要注意的点是,虽然localStorage可以在同一浏览器的不同标签页之间共享数据,但是,当我们在一个标签页中修改localStorage时,其他标签页不会自动收到更新通知。

1)当我们打开一个登录标签页,此页面登录多次错误锁定,我们存储在localStorage的信息,在重新打开一个新的登录页时,这个时候是能共享的。

2)但是当我们同时打开了两个登录标签页。在第一个标签页中,多次输入错误导致账户被锁定。此时,第一个标签页会更新localStorage中的信息并开始倒计时。但是,第二个标签页并不知道这个变化,它仍然显示为未锁定状态,仍然可以尝试登录。此时,我们需要监听`storage`事件,window.addEventListener('storage',   )确保多个标签页之间的状态同步。 

3)虽然浏览器确实提供了 storage 事件机制,但它仅适用于同源的不同标签页之间。对于当前标签页内的 LocalStorage 变化,却没有直接的方法来实现实时监听。此时通过手动创建并分发 StorageEvent, 使用 window.dispatchEvent(new StorageEvent('storage', { }))方式来实现。

 实现代码如下:封装一个useLockTimer.js 

import { ref, onMounted, onBeforeUnmount, computed } from 'vue'

export function useLockTimer() {
  const isLocked = ref(false) // 是否锁定
  const lockSeconds = ref(0) // 锁定秒数
  const lockRemain = ref(0) // 剩余次数

  let timer = null

  // 检查锁定状态
  const checkLockStatus = () => {
    const lockData = localStorage.getItem('loginLock')
    if (lockData) {
      const { lockEndTime } = JSON.parse(lockData)
      const currentTime = Date.now()
      
      if (lockEndTime > currentTime) {
        // 仍在锁定期内
        const secondsLeft = Math.ceil((lockEndTime - currentTime) / 1000)
        startCountdown(secondsLeft)
      } else {
        // 锁定已过期
        localStorage.removeItem('loginLock')
        isLocked.value = false
      }
    } else {
      isLocked.value = false
    }
  }

  // 开始倒计时
  const startCountdown = (seconds) => {
    isLocked.value = true
    lockSeconds.value = seconds
    
    clearInterval(timer)
    
    timer = setInterval(() => {
      lockSeconds.value -= 1
      
      if (lockSeconds.value <= 0) {
        clearLockTimer()
        localStorage.removeItem('loginLock')
      }
    }, 1000)
  }

  // 设置锁定计时器
  const setLockTimer = (lockSeconds) => {
    const lockEndTime = Date.now() + lockSeconds * 1000
    const lockData = JSON.stringify({
      lockEndTime,
      lockSeconds
    })
    
    localStorage.setItem('loginLock', lockData)
    
    // 手动触发 StorageEvent,同步分发事件,实现同一页签下的监听
    window.dispatchEvent(new StorageEvent('storage', {
      key: 'loginLock',
      newValue: lockData
    }))
    
    startCountdown(lockSeconds)
  }

  // 清除计时器
  const clearLockTimer = () => {
    clearInterval(timer)
    timer = null
    isLocked.value = false
    lockSeconds.value = 0
  }

  // 初始化检查
  onMounted(() => {
    checkLockStatus()
    // 监听存储数据变化
    window.addEventListener('storage', handleStorageChange)
  })
  
  // 清理
  onBeforeUnmount(() => {
    clearLockTimer()
    window.removeEventListener('storage', handleStorageChange)
  })

  // 处理跨标签页状态同步
  const handleStorageChange = (event) => {
    if(event.key === 'loginLock') {
      checkLockStatus()
    }
  }
  
  // 错误显示
  const errorMessage = computed(() => {
    if(lockRemain.value) {
      return `用户名密码错误,剩余${lockRemain.value}次机会`
    } else if(lockSeconds.value) {
      return `系统已锁定,请等待${lockSeconds.value}秒`
    } else {
      return ''
    }
  })

  return {
    isLocked,
    lockSeconds,
    lockRemain,
    checkLockStatus,
    setLockTimer,
    clearLockTimer,
    errorMessage
  }
}

然后在登录页面中引用该方法,如下:

<script setup>
 import { useLockTimer } from '@/hooks/useLockTimer'
 // 密码登录错误锁定功能
 const { isLocked, lockSeconds, lockRemain, setLockTimer, errorMessage } = useLockTimer()

  // 点击登录按钮事件
const login = () => {
    
 // 请求后台接口返回中处理锁定功能

  lockRemain.value = data.lockRemain
  lockSeconds.value = data.lockSeconds
  // 如果剩余次数为0,且有锁定时间,则锁定
  if (lockRemain.value === 0 && lockSeconds.value) {
      setLockTimer(lockSeconds.value)
   }

}
</script>

页面展示如下:

<p v-html="errorMessage"></p>
<el-button :disabled="isLocked" type="primary" @click="login">登录</el-button>

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐