跳到主要内容前端实现 PC 网站微信扫码授权登录 | 极客日志JavaScriptWeChatAI大前端
前端实现 PC 网站微信扫码授权登录
PC 端微信扫码授权登录基于 Vue 3 和 Element Plus 实现,涵盖手机号验证码登录、微信直接扫码及二次绑定三种场景。核心流程涉及微信 SDK 初始化、二维码生成、回调参数处理及状态管理。通过 Pinia 存储 Token 和用户信息,结合路由守卫控制访问权限,确保登录安全与用户体验。
日志猎手4.2K 浏览 PC 端微信扫码授权登录技术实现
一、项目背景
本项目是一个基于 Vue 3 + Element Plus 的 AI 汽配查询系统,需要实现 PC 端微信扫码授权登录功能。用户可以通过手机号验证码登录,也可以通过微信扫码快速登录,实现账号与微信的绑定。
二、技术架构
2.1 技术栈
- 前端框架: Vue 3 (Composition API)
- UI 组件库: Element Plus
- 状态管理: Pinia
- 路由管理: Vue Router
- HTTP 请求: Axios (封装)
- 微信 SDK: 微信开放平台 JS SDK
2.2 核心依赖
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

三、功能流程设计
3.1 整体流程图
┌─────────────┐
│ 登录页面 │
│ /login │
└──────┬──────┘
│
├────────────┬────────────┐
│ │ │
▼ ▼ ▼
手机号登录 微信扫码 已有账号
│ │ │
▼ ▼ ▼
发送验证码 扫码授权 直接登录
│ │ │
▼ ▼ ▼
验证码登录 ───┘ ┌─────────┐
│ 首页 │
│ /home │
└─────────┘
code=506?
┌┴┐
是 │否
▼ ▼
需要绑定 登录成功
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│微信授权 │ │ 首页 │
│ /weChat │ │ /home │
└────┬────┘ └─────────┘
│
▼
扫码授权
│
▼
┌─────────────┐
│ 回调页面 │
│ /callback │
└──────┬──────┘
│
├────────────┬────────────┐
│ │ │
state=wx_bind state=wechat_login
▼ ▼
首次绑定 二次授权
│ │
┌┴┐ │
是 │否 │
▼ ▼ ▼ │
code=405 登录成功 绑定成功
│ │
▼ ▼
┌─────────┐ ┌──────────┐
│绑定手机号│ │ 首页 │
│/mobilePhone│ │ /home │
└─────────┘ └──────────┘
3.2 三种登录场景
场景 1: 手机号 + 验证码登录
- 用户输入手机号
- 点击发送验证码(60s 倒计时)
- 输入验证码并登录
- 后端返回 code=506 → 需要微信授权绑定
- 跳转到
/weChat 页面扫码绑定
场景 2: 微信直接扫码登录(已绑定)
- 登录页右侧展示微信二维码
- 用户扫码授权
- 回调到
/callback 页面,state=wx_bind
- code=200 → 登录成功,跳转首页
- code=405 → 需要绑定手机号,跳转
/mobilePhone
场景 3: 微信二次授权绑定(未绑定)
- 从手机号登录后需要绑定(code=506)
- 跳转到
/weChat 页面
- 扫码授权,state=wechat_login
- 回调到
/callback 页面
- 调用绑定接口,登录成功
四、核心代码实现
4.1 登录页面实现 (Login/index.vue)
4.1.1 微信二维码初始化
let wxLoginInstance = null;
const initWechatLogin = () => {
try {
createWxLogin();
console.log("微信授权登录初始化成功");
} catch (error) {
console.error("微信授权登录初始化失败:", error);
ElMessage.error("微信授权登录初始化失败");
}
};
const createWxLogin = () => {
try {
const customCss = `
.impowerBox {display:block!important;visibility:visible!important;...}
.impowerBox .qrcode {display:block!important;...}
.impowerBox .title {display:none!important}
.info {display:none!important}
`;
const href = 'data:text/css;base64,' + btoa(customCss);
const appid = "xxxxxxx";
const redirectUri = encodeURIComponent("https://xxxxx.com/callback");
const state = "wx_bind";
wxLoginInstance = new window.WxLogin({
self_redirect: false,
id: "wechat-login-qr",
appid: appid,
scope: "snsapi_login",
redirect_uri: redirectUri,
state: state,
href: href
});
console.log("微信二维码已生成,等待用户扫码...");
} catch (error) {
console.error("创建微信登录二维码失败:", error);
ElMessage.error("创建微信登录二维码失败");
}
};
onMounted(() => {
setTimeout(() => {
initWechatLogin();
}, 100);
});
onUnmounted(() => {
if (wxLoginInstance) {
wxLoginInstance = null;
}
const container = document.getElementById('wechat-login-qr');
if (container) {
container.innerHTML = '';
}
});
4.1.2 手机号登录逻辑
const account = ref("");
const verifyCode = ref("");
const countdown = ref(0);
const validatePhone = (phone) => {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phone);
};
const sendCode = async () => {
if (countdown.value > 0) return;
if (!account.value) {
ElMessage.warning("请输入手机号");
return;
}
if (!validatePhone(account.value)) {
ElMessage.warning("请输入正确的 11 位手机号");
return;
}
countdown.value = 60;
const timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(timer);
}
}, 1000);
const { code, msg } = await sendCodeAPI(account.value);
if (code !== 200) {
ElMessage.error(msg);
} else {
if (msg) {
verifyCode.value = msg;
ElMessage.success("验证码已自动填入");
}
}
};
const handleLogin = async () => {
if (!account.value || !validatePhone(account.value)) {
ElMessage.warning("请输入正确的手机号");
return;
}
if (!verifyCode.value) {
ElMessage.warning("请输入验证码");
return;
}
const { code, msg, data } = await loginAPI(account.value, verifyCode.value);
if (code === 506) {
loginStore.setPhone(account.value);
ElMessage.info("需要进行微信授权绑定");
router.push("/weChat");
return;
}
if (code !== 200) {
ElMessage.error(msg);
return;
}
loginStore.setToken(data.token);
loginStore.setUserInfo(data.information);
chatStore.clearChatData();
ElMessage.success("登录成功");
router.replace("/home");
};
4.2 微信授权绑定页面 (weChat/index.vue)
let wxLoginInstance = null;
const createWxBind = () => {
try {
const container = document.getElementById('wechat-bind-qr');
if (container) {
container.innerHTML = '';
}
const customCss = `...`;
const href = 'data:text/css;base64,' + btoa(customCss);
const appid = "xxxx";
const redirectUri = encodeURIComponent("https://xxx/callback");
const state = "wechat_login";
wxLoginInstance = new window.WxLogin({
self_redirect: false,
id: "wechat-bind-qr",
appid: appid,
scope: "snsapi_login",
redirect_uri: redirectUri,
state: state,
href: href
});
console.log("微信绑定二维码已生成,等待用户扫码...");
} catch (error) {
console.error("创建微信绑定二维码失败:", error);
}
};
const backToLogin = (e) => {
e.preventDefault();
e.stopPropagation();
router.push("/login");
};
4.3 回调页面处理 (callback/index.vue)
这是整个流程中最核心的部分,负责处理微信授权回调。
const loading = ref(true);
const statusText = ref("正在处理微信授权...");
onMounted(async () => {
try {
const code = route.query.code;
const state = route.query.state;
console.log("微信回调参数:", { code, state });
if (!code) {
throw new Error("未获取到授权码");
}
loginStore.setWechatLoginCode(code);
if (state === 'wx_bind') {
statusText.value = "正在检查登录状态...";
const { data, code: resultCode, msg } = await checkWechatLoginStatusAPI(code, state);
if (resultCode === 405) {
statusText.value = "需要绑定手机号,正在跳转...";
loading.value = false;
ElMessage.info("请先绑定手机号");
setTimeout(() => {
router.replace("/mobilePhone");
}, 1000);
return;
}
else {
loginStore.setToken(data.token);
loginStore.setUserInfo(data.information);
chatStore.clearChatData();
statusText.value = "微信绑定处理完成";
loading.value = false;
ElMessage.success("微信绑定成功");
setTimeout(() => {
router.replace("/home");
}, 1000);
return;
}
}
if (state === 'wechat_login') {
statusText.value = "正在进行微信二次授权...";
const phone = loginStore.phone;
if (!phone) {
throw new Error("未找到手机号信息,请重新登录");
}
const result = await bindWechatAPI(code, phone);
if (result.code === 200) {
loginStore.setToken(result.data.token);
loginStore.setUserInfo(result.data.information);
chatStore.clearChatData();
statusText.value = "微信授权成功,正在跳转...";
loading.value = false;
ElMessage.success("微信授权成功");
setTimeout(() => {
router.replace("/home");
}, 1000);
return;
} else {
throw new Error(result.msg || "微信授权失败");
}
}
} catch (error) {
console.error("微信授权处理失败:", error);
statusText.value = "授权失败";
loading.value = false;
ElMessage.error("授权处理失败,请重试");
setTimeout(() => {
router.replace("/login");
}, 2000);
}
});
4.4 API 接口封装 (apis/user.js)
import request from "../utils/request";
export const sendCodeAPI = (phone) => {
return request({
url: `/client/code/send-sms?phone=${phone}`,
method: "get",
});
};
export const loginAPI = (phoneNumber, code, registerType = 0) => {
return request({
url: `/client/customer/login-by-code`,
method: "post",
data: {
phoneNumber,
code,
registerType,
},
});
};
export const checkWechatLoginStatusAPI = (code, state) => {
return request({
url: `/client/customer/check-is-success?code=${code}&state=${state}`,
method: "get",
});
};
export const bindWechatAPI = (code, phone) => {
return request({
url: `/client/customer/check-wechat?code=${code}&phone=${phone}`,
method: "GET",
});
};
export const bindPhoneAPI = (phoneNumber, phoneCode, code) => {
return request({
url: `/client/customer/check-phone`,
method: "POST",
data: {
phoneNumber,
phoneCode,
code,
},
});
};
4.5 状态管理 (stores/modules/login.js)
import { defineStore } from "pinia";
import { ref } from "vue";
export const useLoginStore = defineStore("login", () => {
const token = ref("");
const setToken = (value) => {
token.value = value;
};
const userInfo = ref({});
const setUserInfo = (value) => {
userInfo.value = value;
};
const wechatLoginCode = ref("");
const setWechatLoginCode = (value) => {
wechatLoginCode.value = value;
};
const phone = ref("");
const setPhone = (value) => {
phone.value = value;
};
return {
token,
setToken,
userInfo,
setUserInfo,
wechatLoginCode,
setWechatLoginCode,
phone,
setPhone,
};
}, { persist: true });
4.6 路由守卫配置 (router/index.js)
import { createRouter, createWebHistory } from "vue-router";
import { useLoginStore } from "@/stores/modules/login";
const routes = [
{
path: "/login",
name: "login",
component: () => import("../views/Login/index.vue"),
},
{
path: "/home",
name: "home",
component: () => import("../views/Home/index.vue"),
meta: { requiresAuth: true },
},
{
path: "/callback",
name: "callback",
component: () => import("../views/callback/index.vue"),
},
{
path: "/weChat",
name: "weChat",
component: () => import("../views/weChat/index.vue"),
},
{
path: "/mobilePhone",
name: "mobilePhone",
component: () => import("../views/mobilePhone/index.vue"),
},
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
router.beforeEach((to, from, next) => {
const loginStore = useLoginStore();
const token = loginStore.token;
if (to.meta.requiresAuth) {
if (!token) {
console.log('用户未登录,重定向到登录页面');
next('/login');
return;
}
}
if (to.path === '/login' && token) {
console.log('用户已登录,重定向到首页');
next('/');
return;
}
next();
});
export default router;

总结
本文详细介绍了 PC 端微信扫码授权登录的完整实现方案,包括:
- 完整的业务流程: 涵盖手机号登录、微信直接登录、二次授权三种场景
- 详细的代码实现: 从初始化到回调处理的每个环节
- 丰富的技术细节: 状态管理、路由守卫、样式定制等
- 实用的优化方案: 用户体验、性能、安全性多方面考虑
- 常见问题解决: 总结实际开发中遇到的坑和解决方案
这套方案已在生产环境稳定运行,具有很好的参考价值。
相关免费在线工具
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online