在Vue.js登录页调用二维码接口,首先通过axios或fetch向后端接口发送请求,获取二维码数据(如base64编码或图片URL),使用async/await处理异步请求,结合ref/reactive管理二维码状态,将返回数据绑定到img标签的src属性渲染,需处理请求异常,并可根据需求设置定时器刷新二维码(如过期后重新获取),确保登录流程顺畅,核心步骤包括接口请求、数据渲染、异常处理及定时更新,实现二维码登录功能。
Vue.js登录页实现二维码登录:接口调用与集成指南
在现代化Web应用开发中,二维码登录凭借其便捷性和安全性优势,已成为用户认证的主流方式之一,本文将系统性地介绍如何在Vue.js项目中集成二维码登录功能,涵盖后端接口对接、二维码生成与展示、状态轮询机制以及完整的用户交互流程,为开发者提供一套可落地的技术实现方案。
需求分析与流程梳理
实现二维码登录功能需要遵循以下核心流程:
-
前端请求二维码:用户访问登录页面时,前端向后端接口发起请求,生成唯一的二维码标识。
-
二维码展示:前端接收后端返回的二维码数据(URL或base64图片格式),并将其渲染到页面指定区域。
-
状态轮询机制:前端设置定时器,定期向后端查询二维码当前状态(包括:未扫描、已扫描、已确认登录、已过期等)。
-
登录结果处理:根据轮询结果实时更新UI状态,显示相应提示信息,并在登录成功后执行页面跳转或业务逻辑。
技术准备
前端依赖配置
本项目基于以下技术栈构建:
- Vue.js 3:采用Composition API进行组件开发(Vue 2项目可类似实现)
- Axios:处理HTTP请求,支持请求/响应拦截器
- 二维码生成库:推荐使用
qrcode(生成base64图片)或vue-qrcode(Vue专用组件)
安装依赖命令:
npm install axios qrcode # 或使用vue-qrcode组件化方案 npm install vue-qrcode@2
后端接口规范
假设后端已提供以下标准化接口(具体字段需与后端团队协商确定):
生成二维码接口
请求:POST /api/login/qrcode
// 请求参数(可选)
{
"deviceInfo": "web_chrome_2024",
"platform": "pc"
}
响应:
{
"code": 0,
"message": "success",
"data": {
"token": "qr_2024052012345678", // 二维码唯一标识
"qrCodeUrl": "https://api.example.com/qr?token=xxx", // 二维码图片URL
"qrCodeBase64": "data:image/png;base64,iVBORw0KGgoAAAANS...", // 或直接返回base64
"expireTime": 300, // 有效时间(秒)
"createTime": 1716234567890 // 创建时间戳
}
}
查询二维码状态接口
请求:GET /api/login/qrcode/status
// 请求参数
{
"token": "qr_2024052012345678"
}
响应:
{
"code": 0,
"message": "success",
"data": {
"status": 1, // 1:未扫描 2:已扫描 3:已确认登录 4:已过期 5:已取消
"userInfo": { // status=3时返回
"userId": "1001",
"username": "张三",
"avatar": "https://example.com/avatar.jpg",
"token": "user_jwt_token_2024"
},
"scanTime": 1716234578900, // 扫描时间戳
"confirmTime": 1716234580000 // 确认登录时间戳
}
}
前端实现步骤
创建登录页组件
在src/views/Login.vue中实现完整的登录页面布局:
<template>
<div class="login-container">
<div class="login-box">
<h2>二维码登录</h2>
<!-- 二维码展示区域 -->
<div v-if="qrCodeUrl" class="qr-code-wrapper">
<img
:src="qrCodeUrl"
alt="二维码"
class="qr-code"
@error="handleQrError"
/>
<p class="status-text">{{ statusText }}</p>
<!-- 扫码动画效果 -->
<div v-if="status === 2" class="scan-animation">
<div class="scan-line"></div>
</div>
</div>
<!-- 加载状态 -->
<div v-else class="loading">
<div class="loading-spinner"></div>
<p>{{ loadingText }}</p>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button
v-if="showRefreshBtn"
@click="getQrCode"
class="refresh-btn"
>
重新生成
</button>
<button
v-if="status === 4"
@click="resetQrCode"
class="reset-btn"
>
刷新二维码
</button>
</div>
<!-- 其他登录方式 -->
<div class="other-login-methods">
<p>其他登录方式</p>
<div class="social-login">
<!-- 社交媒体登录按钮 -->
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import axios from 'axios'
import QRCode from 'qrcode'
// 状态定义
const qrCodeUrl = ref('') // 二维码图片URL
const qrToken = ref('') // 二维码唯一标识
const status = ref(1) // 1:未扫描 2:已扫描 3:已确认 4:已过期 5:已取消
const loadingText = ref('正在生成二维码...')
const showRefreshBtn = ref(false)
const timer = ref(null) // 轮询定时器
const isPolling = ref(false) // 轮询状态标识
// 根据状态显示对应的文本
const statusText = computed(() => {
const statusMap = {
1: '请使用手机扫描二维码',
2: '扫码成功,请在手机上确认登录',
3: '登录成功,正在跳转...',
4: '二维码已过期,请重新生成',
5: '登录已取消'
}
return statusMap[status.value] || ''
})
// 生成二维码
const getQrCode = async () => {
try {
loadingText.value = '正在生成二维码...'
showRefreshBtn.value = false
status.value = 1
const response = await axios.post('/api/login/qrcode', {
deviceInfo: navigator.userAgent,
platform: 'web'
})
if (response.data.code === 0) {
const { token, qrCodeUrl, qrCodeBase64 } = response.data.data
qrToken.value = token
// 优先使用base64,否则使用URL
if (qrCodeBase64) {
qrCodeUrl.value = qrCodeBase64
} else {
qrCodeUrl.value = qrCodeUrl
}
// 开始轮询状态
startPolling()
} else {
throw new Error(response.data.message || '生成二维码失败')
}
} catch (error) {
console.error('生成二维码失败:', error)
loadingText.value = '生成失败,请重试'
showRefreshBtn.value = true
}
}
// 轮询二维码状态
const pollQrStatus = async () => {
if (!qrToken.value || isPolling.value) return
try {
isPolling.value = true
const response = await axios.get('/api/login/qrcode/status', {
params: { token: qrToken.value }
})
if (response.data.code === 0) {
const newStatus = response.data.data.status
// 状态变化处理
if (newStatus !== status.value) {
status.value = newStatus
// 登录成功处理
if (new