import { Button } from '@heroui/button'; import { CardBody, CardHeader } from '@heroui/card'; import { Image } from '@heroui/image'; import { Tab, Tabs } from '@heroui/tabs'; import { useEffect, useRef, useState } from 'react'; import { toast } from 'react-hot-toast'; import { useNavigate } from 'react-router-dom'; import CryptoJS from 'crypto-js'; import { MdSettings } from 'react-icons/md'; import logo from '@/assets/images/logo.png'; import GUIDManager from '@/components/guid_manager'; import Modal from '@/components/modal'; import HoverEffectCard from '@/components/effect_card'; import { title } from '@/components/primitives'; import PasswordLogin from '@/components/password_login'; import QrCodeLogin from '@/components/qr_code_login'; import QuickLogin from '@/components/quick_login'; import type { QQItem } from '@/components/quick_login'; import { ThemeSwitch } from '@/components/theme-switch'; import QQManager from '@/controllers/qq_manager'; import useDialog from '@/hooks/use-dialog'; import PureLayout from '@/layouts/pure'; import { motion } from 'motion/react'; const parseLoginError = (errorStr: string) => { if (errorStr.startsWith('登录失败: ')) { const jsonPart = errorStr.substring('登录失败: '.length); try { const parsed = JSON.parse(jsonPart); if (Array.isArray(parsed) && parsed[1]) { const info = parsed[1]; const codeStr = info.serverErrorCode ? ` (错误码: ${info.serverErrorCode})` : ''; return `${info.message || errorStr}${codeStr}`; } } catch (e) { // 忽略解析错误 } } return errorStr; }; export default function QQLoginPage () { const navigate = useNavigate(); const dialog = useDialog(); const [uinValue, setUinValue] = useState(''); const [isLoading, setIsLoading] = useState(false); const [qrcode, setQrcode] = useState(''); const [loginError, setLoginError] = useState(''); const lastErrorRef = useRef(''); const [qqList, setQQList] = useState<(QQItem | LoginListItem)[]>([]); const [refresh, setRefresh] = useState(false); const [activeTab, setActiveTab] = useState('shortcut'); const firstLoad = useRef(true); const onSubmit = async () => { if (!uinValue) { toast.error('请选择快捷登录的QQ'); return; } setIsLoading(true); try { await QQManager.setQuickLogin(uinValue); } catch (error) { const msg = (error as Error).message; toast.error(`快速登录QQ失败: ${msg}`); } finally { setTimeout(() => { setIsLoading(false); }, 1000); } }; const onPasswordSubmit = async (uin: string, password: string) => { setIsLoading(true); try { // 计算密码的MD5值 const passwordMd5 = CryptoJS.MD5(password).toString(); await QQManager.passwordLogin(uin, passwordMd5); toast.success('密码登录请求已发送'); } catch (error) { const msg = (error as Error).message; toast.error(`密码登录失败: ${msg}`); } finally { setIsLoading(false); } }; const onUpdateQrCode = async () => { if (firstLoad.current) setIsLoading(true); try { const data = await QQManager.checkQQLoginStatusWithQrcode(); if (firstLoad.current) { setIsLoading(false); firstLoad.current = false; } if (data.isLogin) { toast.success('QQ登录成功'); navigate('/', { replace: true }); } else { setQrcode(data.qrcodeurl); if (data.loginError && data.loginError !== lastErrorRef.current) { lastErrorRef.current = data.loginError; setLoginError(data.loginError); const friendlyMsg = parseLoginError(data.loginError); // 仅在扫码登录 Tab 下才弹窗,或者错误不是"二维码已过期" // 如果是 "二维码已过期",且不在 qrcode tab,则不弹窗 const isQrCodeExpired = friendlyMsg.includes('二维码') && (friendlyMsg.includes('过期') || friendlyMsg.includes('失效')); if (!isQrCodeExpired || activeTab === 'qrcode') { dialog.alert({ title: '登录失败', content: friendlyMsg, confirmText: '确定', }); } } else if (!data.loginError) { lastErrorRef.current = ''; setLoginError(''); } } } catch (error) { const msg = (error as Error).message; toast.error(`获取二维码失败: ${msg}`); } }; const onUpdateQQList = async () => { setRefresh(true); try { const data = await QQManager.getQQQuickLoginListNew(); setQQList(data); } catch (_error) { try { const data = await QQManager.getQQQuickLoginList(); const qqList = data.map((item) => ({ uin: item, })); setQQList(qqList); } catch (_error) { const msg = (_error as Error).message; toast.error(`获取QQ列表失败: ${msg}`); } } finally { setRefresh(false); } }; const handleSelectionChange: React.ChangeEventHandler = ( e ) => { setUinValue(e.target.value); }; const onRefreshQRCode = async () => { try { lastErrorRef.current = ''; setLoginError(''); await QQManager.refreshQRCode(); toast.success('已发送刷新请求'); } catch (error) { const msg = (error as Error).message; toast.error(`刷新二维码失败: ${msg}`); } }; const [showGUIDManager, setShowGUIDManager] = useState(false); useEffect(() => { const timer = setInterval(() => { onUpdateQrCode(); }, 3000); onUpdateQrCode(); onUpdateQQList(); return () => clearInterval(timer); }, []); return ( <> QQ登录 - NapCat WebUI
logo
Web  Login 
key !== null && setActiveTab(key.toString())} >
{showGUIDManager && ( } size='lg' hideFooter onClose={() => setShowGUIDManager(false)} /> )} ); }