时里院子市集
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

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
};
}