Merge remote-tracking branch 'origin/ai-hub' into ai-hub
This commit is contained in:
@@ -1,6 +1,21 @@
|
||||
import { get } from '@/utils/request';
|
||||
import { get, post } from '@/utils/request';
|
||||
|
||||
// 获取用户信息
|
||||
export function getUserInfo() {
|
||||
return get<any>('/ai-chat/account').json();
|
||||
}
|
||||
|
||||
// 获取二维码 LoginOrRegister 登录注册, Bind 绑定
|
||||
export function getQrCode(data: any) {
|
||||
return post<any>('/fuwuhao/qrcode', data).json();
|
||||
}
|
||||
|
||||
// 扫码轮询
|
||||
// 0=Wait, 1=Login, 2=Register, 3=Bind, 10=Expired
|
||||
export function getQrCodeResult(data: any) {
|
||||
return get<any>('/fuwuhao/qrcode/result', data).json();
|
||||
}
|
||||
// 注册微信授权
|
||||
export function getWechatAuth(data: any) {
|
||||
return post<any>('/fuwuhao/register', data).json();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { Check, Picture as IconPicture, Refresh } from '@element-plus/icons-vue';
|
||||
import { useCountdown } from '@vueuse/core';
|
||||
import { useQRCode } from '@vueuse/integrations/useQRCode';
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
|
||||
import { getQrCode, getQrCodeResult, getWechatAuth } from '@/api';
|
||||
|
||||
// 响应式状态
|
||||
const urlText = shallowRef('');
|
||||
const qrCodeUrl = useQRCode(urlText);
|
||||
// const urlText = shallowRef('');
|
||||
const qrCodeUrl = ref('');
|
||||
const isExpired = ref(false);
|
||||
const isScanned = ref(false); // 新增:是否已扫码
|
||||
const isConfirming = ref(false); // 新增:是否进入确认登录阶段
|
||||
const confirmCountdownSeconds = shallowRef(180); // 确认登录倒计时(3分钟)
|
||||
const isScanned = ref(false);
|
||||
const isConfirming = ref(false);
|
||||
const confirmCountdownSeconds = shallowRef(180);
|
||||
const sceneStr = ref(''); // 场景值,用于标识二维码
|
||||
|
||||
// 二维码倒计时实例
|
||||
const { start: qrStart, stop: qrStop } = useCountdown(shallowRef(60), {
|
||||
const { start: qrStart, stop: qrStop } = useCountdown(shallowRef(600), {
|
||||
interval: 1000,
|
||||
onComplete: () => {
|
||||
isExpired.value = true;
|
||||
stopPolling(); // 二维码过期时停止轮询
|
||||
stopPolling();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -27,47 +28,204 @@ const { start: confirmStart, stop: confirmStop } = useCountdown(confirmCountdown
|
||||
onComplete: () => {
|
||||
isExpired.value = true;
|
||||
isConfirming.value = false;
|
||||
stopPolling(); // 确认倒计时结束时停止轮询
|
||||
stopPolling();
|
||||
},
|
||||
});
|
||||
|
||||
// 轮询相关
|
||||
let scanPolling: number | null = null;
|
||||
let confirmPolling: number | null = null;
|
||||
let statusPolling: number | null = null;
|
||||
|
||||
// 模拟后端接口 这里返回新的二维码地址
|
||||
async function fetchNewQRCode() {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return `https://login-api.com/qr/${Date.now()}`;
|
||||
}
|
||||
// 模拟后端接口 这里返回是否已扫码
|
||||
async function checkScanStatus() {
|
||||
// 模拟扫码状态接口(实际应调用后端接口)
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return Math.random() > 0.3; // 30%概率未扫码,70%概率已扫码
|
||||
// 获取登录二维码图片和二维码标识
|
||||
async function fetchQRCodeInfo() {
|
||||
try {
|
||||
const param = {
|
||||
sceneType: 'LoginOrRegister',
|
||||
};
|
||||
const response = await getQrCode(param);
|
||||
console.log('response---', response);
|
||||
// {
|
||||
// "code": 200,
|
||||
// "data": {
|
||||
// "qrCodeUrl": "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQG17zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySnR6NzBJcGxhTC0xQmR4TXhFY1UAAgT1XrBoAwRYAgAA",
|
||||
// "scene": "8baf623f79dc452880c6832415deb26a"
|
||||
// },
|
||||
// "msg": "success"
|
||||
// }
|
||||
if (response && response.data.qrCodeUrl && response.data.scene) {
|
||||
qrCodeUrl.value = response.data.qrCodeUrl;
|
||||
sceneStr.value = response.data.scene;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取二维码失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟后端接口 这里返回扫码后是否已确认
|
||||
async function checkConfirmStatus() {
|
||||
// 模拟确认登录接口(实际应调用后端接口)
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
return Math.random() > 0.5; // 50%概率已确认
|
||||
// 轮询二维码状态
|
||||
async function checkQRCodeStatus() {
|
||||
if (!sceneStr.value)
|
||||
return;
|
||||
|
||||
try {
|
||||
console.log('startStatusPolling---');
|
||||
const param = {
|
||||
scene: sceneStr.value,
|
||||
};
|
||||
const response = await getQrCodeResult(param);
|
||||
console.log('response.data.sceneResult---', response.data);
|
||||
console.log('response.data.sceneResult---', response.data.sceneResult);
|
||||
switch (response.data.sceneResult) {
|
||||
case 'Wait': // Wait
|
||||
// 继续等待
|
||||
break;
|
||||
case 'Login': // Login
|
||||
// 登录成功
|
||||
handleLoginSuccess(response.data.token, response.refreshToken);
|
||||
break;
|
||||
case 'Register': // Register
|
||||
// 需要注册
|
||||
handleRegister();
|
||||
break;
|
||||
case 'Bind': // Bind
|
||||
// 需要绑定
|
||||
handleBind(response.data.token);
|
||||
break;
|
||||
case 'Expired': // Expired
|
||||
// 二维码过期
|
||||
isExpired.value = true;
|
||||
stopPolling();
|
||||
break;
|
||||
default:
|
||||
console.warn('未知状态:', response.data.sceneResult);
|
||||
}
|
||||
|
||||
// 更新UI状态
|
||||
updateUIStatus(response.data.sceneResult);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('检查二维码状态失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟登录逻辑 如果在客户端已确认,则会调用这个方法进行登录
|
||||
async function mockLogin() {
|
||||
// 模拟登录成功逻辑
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
// 处理登录成功
|
||||
function handleLoginSuccess(token: string, refreshToken: string) {
|
||||
// 停止轮询
|
||||
stopPolling();
|
||||
|
||||
// 存储token
|
||||
localStorage.setItem('access_token', token);
|
||||
localStorage.setItem('refresh_token', refreshToken || '');
|
||||
|
||||
// 提示用户
|
||||
ElMessage.success('登录成功');
|
||||
|
||||
// 刷新页面或跳转到首页
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 处理注册授权
|
||||
function handleRegister() {
|
||||
console.log('需要注册授权');
|
||||
ElMessage.info('请在微信授权');
|
||||
// const appId = 'wx373eb3ecd65bdac4';
|
||||
// const redirectUri = encodeURIComponent('http://localhost:17001/wechat/callback');
|
||||
// const state = 'register'; // 用于防止CSRF,可根据业务生成随机字符串
|
||||
|
||||
// scope 可以选 snsapi_base(静默,只拿 openid) 或 snsapi_userinfo(需要用户确认,可拿昵称头像)
|
||||
// const scope = 'snsapi_userinfo';
|
||||
|
||||
// 拼接授权链接
|
||||
// const wechatAuthUrl
|
||||
// = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}`
|
||||
// + `&redirect_uri=${redirectUri}`
|
||||
// + `&response_type=code&scope=${scope}&state=${state}#wechat_redirect`;
|
||||
// console.log('wechatAuthUrl---', wechatAuthUrl);
|
||||
// 打开授权页(推荐用 location.href 替换,直接跳转)
|
||||
// window.location.href = wechatAuthUrl;
|
||||
}
|
||||
|
||||
// 处理绑定
|
||||
function handleBind(token: string) {
|
||||
// 处理账号绑定逻辑
|
||||
console.log('需要绑定,临时token:', token);
|
||||
ElMessage.info('请绑定您的账号');
|
||||
}
|
||||
|
||||
// 完成注册
|
||||
async function completeRegistration() {
|
||||
try {
|
||||
|
||||
// 调用后端API完成注册
|
||||
// const response = await completeRegister({ token });
|
||||
// ElMessage.success('注册成功,正在登录...');
|
||||
// 模拟登录成功
|
||||
// handleLoginSuccess();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('注册失败:', error);
|
||||
ElMessage.error('注册失败,请重试');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新UI状态
|
||||
function updateUIStatus(status: string) {
|
||||
switch (status) {
|
||||
case 'Wait': // Wait
|
||||
isScanned.value = false;
|
||||
isConfirming.value = false;
|
||||
break;
|
||||
case 'Login': // Login - 已扫码并确认
|
||||
case 'Register': // Register - 已扫码并确认
|
||||
case 'Bind': // Bind - 已扫码并确认
|
||||
isScanned.value = true;
|
||||
isConfirming.value = false;
|
||||
break;
|
||||
case 'Expired': // Expired
|
||||
isExpired.value = true;
|
||||
isScanned.value = false;
|
||||
isConfirming.value = false;
|
||||
break;
|
||||
default:
|
||||
// 其他状态认为是已扫码但未确认
|
||||
isScanned.value = true;
|
||||
isConfirming.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 在微信授权页面获取到微信code后发给服务端
|
||||
async function handleWechatAuth(code: string) {
|
||||
try {
|
||||
const param = {
|
||||
code,
|
||||
};
|
||||
const response = await getWechatAuth(param);
|
||||
|
||||
if (response.success) {
|
||||
// 授权成功,可以获取用户信息或完成登录
|
||||
console.log('微信授权成功', response);
|
||||
}
|
||||
else {
|
||||
console.error('微信授权失败:', response.message);
|
||||
ElMessage.error(`微信授权失败: ${response.message}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('处理微信授权失败:', error);
|
||||
ElMessage.error('处理微信授权时出错');
|
||||
}
|
||||
}
|
||||
|
||||
/** 停止所有轮询 */
|
||||
function stopPolling() {
|
||||
if (scanPolling)
|
||||
clearInterval(scanPolling);
|
||||
if (confirmPolling)
|
||||
clearInterval(confirmPolling);
|
||||
scanPolling = null;
|
||||
confirmPolling = null;
|
||||
if (statusPolling) {
|
||||
clearInterval(statusPolling);
|
||||
statusPolling = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新二维码 */
|
||||
@@ -76,48 +234,40 @@ async function handleRefresh() {
|
||||
isScanned.value = false;
|
||||
isConfirming.value = false;
|
||||
stopPolling();
|
||||
qrStart(shallowRef(60));
|
||||
const newUrl = await fetchNewQRCode();
|
||||
urlText.value = newUrl;
|
||||
|
||||
const success = await fetchQRCodeInfo();
|
||||
if (success) {
|
||||
qrStart(shallowRef(600));
|
||||
startStatusPolling();
|
||||
}
|
||||
else {
|
||||
ElMessage.error('刷新二维码失败');
|
||||
}
|
||||
}
|
||||
|
||||
/** 启动扫码状态轮询 */
|
||||
function startScanPolling() {
|
||||
scanPolling = setInterval(async () => {
|
||||
if (!isExpired.value && !isScanned.value) {
|
||||
const scanned = await checkScanStatus();
|
||||
if (scanned) {
|
||||
isScanned.value = true;
|
||||
isConfirming.value = true;
|
||||
confirmStart(confirmCountdownSeconds); // 启动确认倒计时
|
||||
startConfirmPolling(); // 开始确认登录轮询
|
||||
stopPolling(); // 停止扫码轮询
|
||||
}
|
||||
}
|
||||
}, 2000); // 每2秒轮询一次
|
||||
}
|
||||
/** 启动状态轮询 */
|
||||
function startStatusPolling() {
|
||||
console.log('1111----');
|
||||
stopPolling(); // 先停止之前的轮询
|
||||
|
||||
/** 启动确认登录轮询 */
|
||||
function startConfirmPolling() {
|
||||
confirmPolling = setInterval(async () => {
|
||||
if (isConfirming.value && !isExpired.value) {
|
||||
const confirmed = await checkConfirmStatus();
|
||||
if (confirmed) {
|
||||
stopPolling();
|
||||
confirmStop();
|
||||
await mockLogin();
|
||||
handleRefresh(); // 登录成功后刷新二维码
|
||||
}
|
||||
statusPolling = setInterval(async () => {
|
||||
if (!isExpired.value) {
|
||||
await checkQRCodeStatus();
|
||||
}
|
||||
}, 2000); // 每2秒轮询一次
|
||||
}
|
||||
|
||||
/** 组件初始化 */
|
||||
onMounted(async () => {
|
||||
const initialUrl = await fetchNewQRCode();
|
||||
urlText.value = initialUrl;
|
||||
qrStart();
|
||||
startScanPolling(); // 初始启动扫码轮询
|
||||
const success = await fetchQRCodeInfo();
|
||||
console.log('qrCodeUrl---', success);
|
||||
if (success) {
|
||||
qrStart();
|
||||
startStatusPolling();
|
||||
}
|
||||
else {
|
||||
ElMessage.error('初始化二维码失败');
|
||||
}
|
||||
});
|
||||
|
||||
/** 组件卸载清理 */
|
||||
@@ -126,12 +276,24 @@ onBeforeUnmount(() => {
|
||||
confirmStop();
|
||||
stopPolling();
|
||||
});
|
||||
|
||||
// 监听URL参数中的微信code(适用于微信授权回调)
|
||||
onMounted(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const code = urlParams.get('code');
|
||||
const state = urlParams.get('state');
|
||||
|
||||
if (code) {
|
||||
// 处理微信授权回调
|
||||
handleWechatAuth(code);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="qr-wrapper">
|
||||
<div class="tip">
|
||||
请使用手机扫码登录
|
||||
请使用手机微信扫码登录
|
||||
</div>
|
||||
|
||||
<div class="qr-img-wrapper">
|
||||
@@ -164,12 +326,20 @@ onBeforeUnmount(() => {
|
||||
已扫码
|
||||
</p>
|
||||
|
||||
<p class="scanned-text">
|
||||
<p v-if="isConfirming" class="scanned-text">
|
||||
请在手机端确认登录
|
||||
</p>
|
||||
|
||||
<p v-else class="scanned-text">
|
||||
处理中...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-text">
|
||||
扫码后请在微信中确认登录
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -256,5 +426,9 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.help-text {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -284,7 +284,7 @@ function openContact() {
|
||||
</div>
|
||||
<div class="right-section">
|
||||
<!-- 隐藏二维码登录 -->
|
||||
<div v-if="false" class="mode-toggle" @click.stop="toggleLoginMode">
|
||||
<div class="mode-toggle" @click.stop="toggleLoginMode">
|
||||
<SvgIcon v-if="!isQrMode" name="erweimadenglu" />
|
||||
<SvgIcon v-else name="zhanghaodenglu" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user