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.IDLE); const [error, setError] = useState(''); const [result, setResult] = useState(null); const [isLoading, setIsLoading] = useState(false); // 用于取消操作的引用 const cancelRef = useRef(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((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 }; }