feat: 优化离线重连机制,支持通过前端实现重新登录

* feat: 优化离线重连机制,增加前端登录错误提示与二维码刷新功能

- 增加全局掉线检测弹窗
- 增强登录错误解析,支持显示 serverErrorCode 和 message
- 优化二维码登录 UI,错误时显示详细原因并提供大按钮重新获取
- 核心层解耦,通过事件抛出 KickedOffLine 通知
- 支持前端点击刷新二维码接口

* feat: 新增看门狗汪汪汪

* cp napcat-shell-loader/launcher-win.bat

* refactor: 重构重启流程,移除旧的重启逻辑,新增基于 WebUI 的重启请求处理

* fix: 刷新二维码清楚错误信息
This commit is contained in:
时瑾
2026-01-17 15:38:24 +08:00
committed by GitHub
parent 3cde32dd9f
commit 59d1ea28ab
21 changed files with 374 additions and 59 deletions

View File

@@ -52,7 +52,7 @@ const Modal: React.FC<ModalProps> = React.memo((props) => {
onNativeClose();
}}
classNames={{
backdrop: 'z-[99]',
backdrop: 'z-[99] backdrop-blur-sm',
wrapper: 'z-[99]',
}}
{...rest}

View File

@@ -1,22 +1,70 @@
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
qrcode: string;
loginError?: string;
onRefresh?: () => void;
}
const QrCodeLogin: React.FC<QrCodeLoginProps> = ({ qrcode }) => {
const QrCodeLogin: React.FC<QrCodeLoginProps> = ({ qrcode, loginError, onRefresh }) => {
return (
<div className='flex flex-col items-center'>
<div className='bg-white p-2 rounded-md w-fit mx-auto relative overflow-hidden'>
{!qrcode && (
<div className='absolute left-2 top-2 right-2 bottom-2 bg-white bg-opacity-50 backdrop-blur flex items-center justify-center'>
<Spinner color='primary' />
{loginError
? (
<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 rounded-full'>
<IoAlertCircle className='text-danger' size={64} />
</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}
>
</Button>
)}
</div>
)
: (
<>
<div className='bg-white p-2 rounded-md w-fit mx-auto relative overflow-hidden'>
{!qrcode && (
<div className='absolute left-0 top-0 right-0 bottom-0 bg-white dark:bg-zinc-900 bg-opacity-90 backdrop-blur-sm flex items-center justify-center z-10'>
<Spinner color='primary' />
</div>
)}
<QRCodeSVG size={180} value={qrcode || ' '} />
</div>
<div className='mt-5 text-center text-default-500 text-sm'>使QQ或者TIM扫描上方二维码</div>
{onRefresh && qrcode && (
<Button
className='mt-4'
variant='flat'
color='primary'
size='sm'
startContent={<IoRefresh />}
onPress={onRefresh}
>
</Button>
)}
</>
)}
<QRCodeSVG size={180} value={qrcode} />
</div>
<div className='mt-5 text-center'>使QQ或者TIM扫描上方二维码</div>
</div>
);
};