You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
5.5 KiB
228 lines
5.5 KiB
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
import Taro from '@tarojs/taro';
|
|
import {
|
|
confirmWechatQRLogin,
|
|
parseQRContent,
|
|
type ConfirmLoginResult
|
|
} from '@/api/qr-login';
|
|
|
|
/**
|
|
* 扫码登录状态
|
|
*/
|
|
export enum ScanLoginState {
|
|
IDLE = 'idle', // 空闲状态
|
|
SCANNING = 'scanning', // 正在扫码
|
|
CONFIRMING = 'confirming', // 正在确认登录
|
|
SUCCESS = 'success', // 登录成功
|
|
ERROR = 'error' // 登录失败
|
|
}
|
|
|
|
/**
|
|
* 扫码登录Hook
|
|
*/
|
|
export function useQRLogin() {
|
|
const [state, setState] = useState<ScanLoginState>(ScanLoginState.IDLE);
|
|
const [error, setError] = useState<string>('');
|
|
const [result, setResult] = useState<ConfirmLoginResult | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
// 用于取消操作的引用
|
|
const cancelRef = useRef<boolean>(false);
|
|
|
|
/**
|
|
* 重置状态
|
|
*/
|
|
const reset = useCallback(() => {
|
|
setState(ScanLoginState.IDLE);
|
|
setError('');
|
|
setResult(null);
|
|
setIsLoading(false);
|
|
cancelRef.current = false;
|
|
}, []);
|
|
|
|
/**
|
|
* 开始扫码登录
|
|
*/
|
|
const startScan = useCallback(async () => {
|
|
try {
|
|
reset();
|
|
setState(ScanLoginState.SCANNING);
|
|
|
|
// 检查用户是否已登录
|
|
const userId = Taro.getStorageSync('UserId');
|
|
if (!userId) {
|
|
throw new Error('请先登录小程序');
|
|
}
|
|
|
|
// 调用扫码API
|
|
const scanResult = await new Promise<string>((resolve, reject) => {
|
|
Taro.scanCode({
|
|
onlyFromCamera: true,
|
|
scanType: ['qrCode'],
|
|
success: (res) => {
|
|
if (res.result) {
|
|
resolve(res.result);
|
|
} else {
|
|
reject(new Error('扫码结果为空'));
|
|
}
|
|
},
|
|
fail: (err) => {
|
|
reject(new Error(err.errMsg || '扫码失败'));
|
|
}
|
|
});
|
|
});
|
|
|
|
// 检查是否被取消
|
|
if (cancelRef.current) {
|
|
return;
|
|
}
|
|
|
|
// 解析二维码内容
|
|
const token = parseQRContent(scanResult);
|
|
console.log('解析二维码内容2:',token)
|
|
if (!token) {
|
|
throw new Error('无效的登录二维码');
|
|
}
|
|
|
|
// 确认登录
|
|
setState(ScanLoginState.CONFIRMING);
|
|
setIsLoading(true);
|
|
|
|
const confirmResult = await confirmWechatQRLogin(token, parseInt(userId));
|
|
console.log(confirmResult,'confirmResult>>>>')
|
|
if (cancelRef.current) {
|
|
return;
|
|
}
|
|
|
|
if (confirmResult.success) {
|
|
setState(ScanLoginState.SUCCESS);
|
|
setResult(confirmResult);
|
|
|
|
// 显示成功提示
|
|
Taro.showToast({
|
|
title: '登录确认成功',
|
|
icon: 'success',
|
|
duration: 2000
|
|
});
|
|
} else {
|
|
throw new Error(confirmResult.message || '登录确认失败');
|
|
}
|
|
|
|
} catch (err: any) {
|
|
if (!cancelRef.current) {
|
|
setState(ScanLoginState.ERROR);
|
|
const errorMessage = err.message || '扫码登录失败';
|
|
setError(errorMessage);
|
|
|
|
// 显示错误提示
|
|
Taro.showToast({
|
|
title: errorMessage,
|
|
icon: 'error',
|
|
duration: 3000
|
|
});
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [reset]);
|
|
|
|
/**
|
|
* 取消扫码登录
|
|
*/
|
|
const cancel = useCallback(() => {
|
|
cancelRef.current = true;
|
|
reset();
|
|
}, [reset]);
|
|
|
|
/**
|
|
* 处理扫码结果(用于已有扫码结果的情况)
|
|
*/
|
|
const handleScanResult = useCallback(async (qrContent: string) => {
|
|
try {
|
|
reset();
|
|
setState(ScanLoginState.CONFIRMING);
|
|
setIsLoading(true);
|
|
|
|
// 检查用户是否已登录
|
|
const userId = Taro.getStorageSync('UserId');
|
|
if (!userId) {
|
|
throw new Error('请先登录小程序');
|
|
}
|
|
|
|
// 解析二维码内容
|
|
const token = parseQRContent(qrContent);
|
|
if (!token) {
|
|
throw new Error('无效的登录二维码');
|
|
}
|
|
|
|
// 确认登录
|
|
const confirmResult = await confirmWechatQRLogin(token, parseInt(userId));
|
|
|
|
if (confirmResult.success) {
|
|
setState(ScanLoginState.SUCCESS);
|
|
setResult(confirmResult);
|
|
|
|
// 显示成功提示
|
|
Taro.showToast({
|
|
title: '登录确认成功',
|
|
icon: 'success',
|
|
duration: 2000
|
|
});
|
|
} else {
|
|
throw new Error(confirmResult.message || '登录确认失败');
|
|
}
|
|
|
|
} catch (err: any) {
|
|
setState(ScanLoginState.ERROR);
|
|
const errorMessage = err.message || '登录确认失败';
|
|
setError(errorMessage);
|
|
|
|
// 显示错误提示
|
|
Taro.showToast({
|
|
title: errorMessage,
|
|
icon: 'error',
|
|
duration: 3000
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [reset]);
|
|
|
|
/**
|
|
* 检查是否可以进行扫码登录
|
|
*/
|
|
const canScan = useCallback(() => {
|
|
const userId = Taro.getStorageSync('UserId');
|
|
const accessToken = Taro.getStorageSync('access_token');
|
|
return !!(userId && accessToken);
|
|
}, []);
|
|
|
|
// 组件卸载时取消操作
|
|
useEffect(() => {
|
|
return () => {
|
|
cancelRef.current = true;
|
|
};
|
|
}, []);
|
|
|
|
return {
|
|
// 状态
|
|
state,
|
|
error,
|
|
result,
|
|
isLoading,
|
|
|
|
// 方法
|
|
startScan,
|
|
cancel,
|
|
reset,
|
|
handleScanResult,
|
|
canScan,
|
|
|
|
// 便捷状态判断
|
|
isIdle: state === ScanLoginState.IDLE,
|
|
isScanning: state === ScanLoginState.SCANNING,
|
|
isConfirming: state === ScanLoginState.CONFIRMING,
|
|
isSuccess: state === ScanLoginState.SUCCESS,
|
|
isError: state === ScanLoginState.ERROR
|
|
};
|
|
}
|