前端登陆业务逻辑

前端登陆业务逻辑

  1. 封装axios
  2. 封装接口请求
  3. 封装登录请求动作
  4. 保存服务端返回的token
  5. 登录鉴权
代码实现
  • 创建登录表单
<template>
  <div class="login-container">
    <el-form
      class="login-form"
      ref="loginFormRef"
      :model="loginForm"
      :rules="loginRules"
    >
      <div class="title-container">
        <h3 class="title">用户登陆</h3>
      </div>
      <!-- username  -->
      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon="user"></svg-icon>
        </span>
        <el-input
          placeholder="username"
          name="username"
          type="text"
          v-model="loginForm.username"
        ></el-input>
      </el-form-item>
      <!-- password  -->
      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon="password"></svg-icon>
        </span>
        <el-input
          placeholder="password"
          name="password"
          :type="passwordType"
          v-model="loginForm.password"
        ></el-input>
        <span class="show-pwd">
          <span class="svg-container" @click="onChangePwdType">
            <svg-icon
              :icon="passwordType === 'password' ? 'eye' : 'eye-open'"
            ></svg-icon>
          </span>
        </span>
      </el-form-item>
      <!-- 登陆按钮 -->
      <el-button
        type="primary"
        style="width: 100%; margin-bottom: 30px"
        :loading="loading"
        @click="handlerLogin"
      >
        登陆
      </el-button>
    </el-form>
  </div>
</template>
  • 数据源
// 数据源
const loginForm = ref({
  username: 'sysadmin@thingsboard.org',
  password: 'sysadmin'
})

  • 封装验证规则


// 外部封装规则
export const validatePassword = () => {
  return (rule, value, callback) => {
    if (value.length < 6) {
      callback(new Error('密码不能小于6位'))
    } else {
      callback()
    }
  }
}


// 验证规则
const loginRules = ref({
  username: [
    {
      required: true,
      trigger: 'blur',
      message: '用户名为必填项'
    }
  ],
  password: [
    {
      required: true,
      trigger: 'blur',
      validator: validatePassword()
    }
  ]
})

// 处理密码框文本显示
const passwordType = ref('password')
// template中绑定的方法直接声明即可
const onChangePwdType = () => {
  // 当passwordType的值为password的时候改为text
  // 使用ref声明的数据,在script中使用时需要加value来获取具体的值 Templates中使用的时候 不需要加value
  if (passwordType.value === 'password') {
    passwordType.value = 'text'
  } else {
    passwordType.value = 'password'
  }
}
  • 处理登录请求

// 处理登陆
const loading = ref(false)
const store = useStore()
const loginFormRef = ref(null)
const handlerLogin = () => {
  // 进行表单校验
  // console.log(loginFormRef.value)
  loginFormRef.value.validate((valid) => {
    if (!valid) return
    // 触发登陆动作
    loading.value = true
    store
      .dispatch('user/login', loginForm.value)
      .then(() => {
        loading.value = false
        // 进行登陆后处理
      })
      .catch((err) => {
        console.log(err)
        loading.value = false
      })
  })
}
  • 封装登录请求动作
import { login } from '@/api/sys'
// import md5 from 'md5'
import { setItem, getItem } from '@/utils/storage'
import { TOKEN, REFRESHTOKEN } from '@/constant'
import router from '@/router'

export default {
  namespaced: true,
  state: () => ({
    token: getItem(TOKEN) || ''
  }),
  mutations: {
    setToken(state, data) {
      state.token = data.token
      setItem(TOKEN, data.token)
      setItem(REFRESHTOKEN, data.refreshToken)
    }
  },
  /**
   * 登陆请求动作
   */
  actions: {
    login(context, userInfo) {
      const { username, password } = userInfo
      return new Promise((resolve, reject) => {
        login({
          username,
          password
          // password: md5(password)
        })
          .then((data) => {
            // console.log(data)
            this.commit('user/setToken', data)
            // 跳转
            router.push('/')
            resolve()
          })
          .catch((err) => {
            reject(err)
          })
      })
    }
  }
}

  • 创建响应拦截器
import axios from 'axios'
import { ElMessage } from 'element-plus'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
})

// 响应拦截器
service.interceptors.response.use(
  // 请求成功
  (response) => {
    const data = response.data

    // 需要判断当前请求是否成功
    if (data != null) {
      // 成功返回解析后的数据
      const message1 = '-登陆成功-'
      ElMessage.success(message1)
      return data
    } else {
      // 失败(请求成功,业务失败) 消息提示
      const message1 = '-请求成功,业务失败-'
      ElMessage.error(message1)
      return Promise.reject(new Error(message1))
    }
  },
  // 请求失败
  (error) => {
    const message2 = '-请求失败-'
    ElMessage.error(message2)
    return Promise.reject(error)
  }
)

export default service

设置登录鉴权

import { createRouter, createWebHashHistory } from 'vue-router'

/**
 * 公开路由表
 */
const publicRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index')
  },
  {
    path: '/',
    component: () => import('@/layout/index')
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes: publicRoutes
})

export default router

  • 前置导航守卫
import router from '@/router'
import store from '@/store'

// 白名单
const whileList = ['/login']

/**
 * 路由前置守卫
 * @param {*} to  要到那里去
 * @param {*} from  你从哪里来
 * @param {*} next  是否要去
 */
router.beforeEach((to, from, next) => {
  // 用户以登陆,不允许进入login
  // 用户未登陆,只允许进入login
  if (store.getters.token) {
    // 用户以登陆,不允许进入login
    if (to.path === '/login') {
      next('/')
    } else {
      next()
    }
  } else {
    // 用户未登陆,只允许进入login
    if (whileList.indexOf(to.path) > -1) {
      next()
    } else {
      next('/login')
    }
  }
})

### 前端扫码登录使用 `setInterval` 的最佳实践 在前端实现扫码登录的过程中,可以采用 `setInterval` 来定期向服务器发起 HTTP 请求以检测用户的扫码状态。以下是基于已有引用内容以及行业标准的最佳实践。 #### 1. 轮询时间间隔的选择 轮询的时间间隔是一个重要的参数,它直接影响到系统的性能和用户体验。如果时间间隔太短,则会对服务器造成较大的负载;而时间间隔过长则可能导致用户感知延迟较大[^1]。 一般建议将时间间隔设置为 **2~5 秒** 左右,在此范围内可以根据具体业务需求调整。对于实时性要求较低的应用(如天气更新、在线聊天等),可以选择更长的间隔以减少服务器压力。 #### 2. 定义轮询逻辑 以下是一段完整的代码示例,展示了如何通过 `setInterval` 实现扫码登录的功能: ```javascript // 初始化定时器变量 let timer; function startPolling() { // 开始轮询 timer = setInterval(async () => { try { const response = await axios.post('/api/check-login-status', { qrCodeId: 'unique-id' }); if (response.data.code === 'SCAN_SUCCESS') { // 扫码成功后的跳转或其他操作 handleLoginSuccess(); } else if (response.data.code === 'QR_EXPIRED') { // 处理二维码过期的情况 handleQrExpired(); } } catch (error) { console.error('轮询过程中发生错误:', error); } }, 5000); // 设置时间为 5 秒 } function stopPolling() { // 清除定时器并释放资源 if (timer) { clearInterval(timer); timer = null; } } function handleLoginSuccess() { stopPolling(); // 停止轮询 window.location.href = '/home'; // 页面跳转至首页 } function handleQrExpired() { stopPolling(); // 停止轮询 refreshQrcode(); // 刷新二维码 } ``` 上述代码中定义了两个主要函数:`startPolling()` 和 `stopPolling()`。前者负责启动轮询过程,后者用于停止轮询并清理相关资源。当接收到特定响应时(例如扫码成功或二维码过期),应立即调用相应的回调函数来执行后续动作。 #### 3. 错误处理与超时机制 为了提高系统的健壮性和稳定性,还需要考虑以下几个方面: - 添加网络异常捕获逻辑,确保即使在网络不稳定的情况下也不会中断整个程序运行。 - 设定最大重试次数或者总等待时间限制,避免无限循环带来的潜在风险[^2]。 可以通过引入计数器的方式控制尝试次数上限: ```javascript let retryCount = 0; const MAX_RETRIES = 10; // 最大重试次数 async function pollStatus() { while (retryCount < MAX_RETRIES && !isCancelled()) { try { const response = await axios.post('/api/check-login-status'); processResponse(response); break; // 成功退出循环 } catch (e) { retryCount++; await delay(5000); // 每次失败后稍作停顿再继续下一轮 } } if (retryCount >= MAX_RETRIES) { handleError("达到最大重试次数"); } } ``` 这里增加了对每次请求之间加入适当延时(`delay`)的操作,从而进一步减轻服务端的压力[^4]。 --- ### 总结 综上所述,合理配置 `setInterval` 时间间隔、完善错误恢复策略以及适时终止不必要的连接都是构建高效可靠的扫码登录系统不可或缺的部分。这些措施不仅能够提升用户体验还能有效保护后台基础设施免受过度访问的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值