Files
SnowCatQQ/packages/snowcat-webui-frontend/src/components/qr_code_login.tsx
T
天天的鸟蛋蛋 b9655a5ab1 SnowCat 雪猫底层全源码:NapCat 4.18.28 底座 + SnowLuma 能力移植 + V8 通道适配 9.9.37
26 包 monorepo(TS 全源码):
- snowcat-core: NT 内核 API 封装 + listener(含 9.9.37 六回调适配)+ Packet 发包
- snowcat-onebot: OneBot v11 全动作 + MCP Streamable HTTP 适配器 + 稳定线
- snowcat-webui-frontend/backend: 雪猫控制台(React+HeroUI)
- snowcat-shell: 单文件 bundle 出口(vite inlineDynamicImports)
- snowcat-native: napi2native / MoeHoo / ffmpeg / pty 运行时
- 56 项 SnowLuma 独门动作移植(qzone 全家桶/群相册/装扮/闪照/收藏)
- 工具件: 解包器/启动器/便携制作器/doctor

GitHub 关联已全量指向 DandanLLab/SnowCatQQ
2026-11-12 06:27:36 +08:00

103 lines
4.2 KiB
TypeScript

import { Button } from '@heroui/button';
import { Spinner } from '@heroui/spinner';
import { QRCodeSVG } from 'qrcode.react';
import { IoAlertCircle, IoRefresh } from 'react-icons/io5';
interface QrCodeLoginProps {
qrcode: string;
loginError?: string;
onRefresh?: () => void;
isRefreshing?: boolean;
loginPhase?: string;
isRecoveringLoginService?: boolean;
}
const QrCodeLogin: React.FC<QrCodeLoginProps> = ({ qrcode, loginError, onRefresh, isRefreshing = false, loginPhase, isRecoveringLoginService = false }) => {
const transitionMessage = loginPhase === 'qrcode_scanned'
? '二维码已扫描,等待手机确认…'
: loginPhase === 'initializing'
? 'QQ 登录成功,正在启动 SnowCat…'
: loginPhase === 'reconnecting' || isRecoveringLoginService
? '正在重新连接 QQ 登录服务…'
: undefined;
return (
<div className='flex flex-col items-center'>
{(isRefreshing || transitionMessage) && (
<div className='fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-md'>
<div className='flex flex-col items-center gap-3 rounded-3xl bg-white/90 dark:bg-zinc-900/90 backdrop-blur-2xl border border-white/70 dark:border-white/10 px-10 py-8 shadow-2xl shadow-primary-900/20'>
<Spinner color='primary' size='lg' />
<div className='text-base font-semibold'>{transitionMessage || '正在刷新二维码…'}</div>
<div className='text-xs text-default-500'>
{transitionMessage ? '请不要关闭此页面,最长可能需要 3 分钟' : '正在等待新的二维码,最长 10 秒'}
</div>
</div>
</div>
)}
{loginError && !qrcode && !isRecoveringLoginService
? (
<div className='flex flex-col items-center py-4'>
<div className='w-full flex justify-center mb-6'>
<div className='p-4 bg-danger-50 dark:bg-danger-900/20 rounded-2xl'>
<IoAlertCircle className='text-danger' size={56} />
</div>
</div>
<div className='text-center space-y-2 px-4'>
<div className='text-xl font-bold text-danger'>登录失败</div>
<div className='text-default-600 text-sm leading-relaxed max-w-[300px]'>
{loginError}
</div>
</div>
{onRefresh && (
<Button
className='mt-8 min-w-[160px]'
variant='solid'
color='primary'
size='lg'
startContent={<IoRefresh />}
onPress={onRefresh}
isLoading={isRefreshing}
isDisabled={isRefreshing}
>
重新获取二维码
</Button>
)}
</div>
)
: (
<>
{loginError && (
<div className='mb-4 w-full max-w-[360px] rounded-lg bg-warning-50 px-4 py-3 text-center text-sm text-warning-700 dark:bg-warning-50/20 dark:text-warning-300'>
{loginError}
</div>
)}
<div className='relative mx-auto w-fit rounded-2xl bg-white p-3 shadow-lg shadow-primary-900/10 ring-1 ring-black/5 overflow-hidden'>
{!qrcode && (
<div className='absolute inset-0 bg-white/90 dark:bg-zinc-900/90 backdrop-blur-sm flex items-center justify-center z-10'>
<Spinner color='primary' />
</div>
)}
<QRCodeSVG key={qrcode} size={176} value={qrcode || ' '} />
</div>
<div className='mt-5 text-center text-default-500 text-xs'>请使用 QQ 或 TIM 扫描上方二维码</div>
{onRefresh && qrcode && (
<Button
className='mt-4'
variant='flat'
color='primary'
size='sm'
startContent={<IoRefresh />}
onPress={onRefresh}
isLoading={isRefreshing}
isDisabled={isRefreshing}
>
刷新二维码
</Button>
)}
</>
)}
</div>
);
};
export default QrCodeLogin;