+
{qrGameTitle ? {qrGameTitle} : null}
{qrRoleLine}
@@ -2017,7 +2024,7 @@ export default function HuyaTasksPage() {
>
{payTask && (
-
+
{payProductName || '充值商品'}
{payChannelLabel || '支付'}{payAmountText ? ` / ${payAmountText}` : ''}{payAccountName ? ` / ${payAccountName}` : ''}
diff --git a/web/frontend/src/pages/LoginPage.tsx b/web/frontend/src/pages/LoginPage.tsx
index de0c61d..81f120d 100644
--- a/web/frontend/src/pages/LoginPage.tsx
+++ b/web/frontend/src/pages/LoginPage.tsx
@@ -1,5 +1,6 @@
import { useState } from 'react';
-import { Card, Form, Input, Button, message, Typography } from 'antd';
+import { Card, Form, Input, Button, Typography } from 'antd';
+import { message } from '../utils/antdMessage';
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import { authApi } from '../api/modules';
diff --git a/web/frontend/src/pages/LoginTasksPage.tsx b/web/frontend/src/pages/LoginTasksPage.tsx
index 9153b03..e10cb1e 100644
--- a/web/frontend/src/pages/LoginTasksPage.tsx
+++ b/web/frontend/src/pages/LoginTasksPage.tsx
@@ -1,7 +1,8 @@
import { useEffect, useState, useMemo, useCallback } from 'react';
import {
- Table, Button, Select, message, Tag, Space, InputNumber, Tooltip, Popconfirm, theme, Modal, Form,
+ Table, Button, Select, Tag, Space, InputNumber, Tooltip, Popconfirm, theme, Modal, Form,
} from 'antd';
+import { message } from '../utils/antdMessage';
import {
PlayCircleOutlined, StopOutlined, FilterOutlined, ReloadOutlined, DeleteOutlined,
SettingOutlined, SafetyCertificateOutlined,
@@ -503,43 +504,49 @@ export default function LoginTasksPage() {
label="并发数"
tooltip="同时登录的账号数。1 为顺序执行,最多 10。"
>
- setConcurrency(v || 1)}
- style={{ width: 120 }}
- addonAfter="个"
- />
+
+ setConcurrency(v || 1)}
+ style={{ width: 120 }}
+ />
+
+
- setMaxTotalTime(v ?? 0)}
- style={{ width: 120 }}
- addonAfter="秒"
- placeholder="0=不限"
- />
+
+ setMaxTotalTime(v ?? 0)}
+ style={{ width: 120 }}
+ placeholder="0=不限"
+ />
+
+
- setMaxLoginRetries(v ?? 0)}
- style={{ width: 120 }}
- addonAfter="次"
- placeholder="0=无限"
- />
+
+ setMaxLoginRetries(v ?? 0)}
+ style={{ width: 120 }}
+ placeholder="0=无限"
+ />
+
+
diff --git a/web/frontend/src/pages/ProxyPage.tsx b/web/frontend/src/pages/ProxyPage.tsx
index b909c8c..8cb0617 100644
--- a/web/frontend/src/pages/ProxyPage.tsx
+++ b/web/frontend/src/pages/ProxyPage.tsx
@@ -1,5 +1,6 @@
import { useEffect, useState, useCallback } from 'react';
-import { Form, Input, Switch, Button, Card, Select, message, Row, Col } from 'antd';
+import { Form, Input, Switch, Button, Card, Select, Row, Col } from 'antd';
+import { message } from '../utils/antdMessage';
import { proxyApi, type ProxyConfig, type PlatformInfo } from '../api/modules';
import RealtimeLogPanel from '../components/RealtimeLogPanel';
import { useWebSocketLogs } from '../hooks/useWebSocketLogs';
diff --git a/web/frontend/src/pages/UsersPage.tsx b/web/frontend/src/pages/UsersPage.tsx
index 5ff0054..2028361 100644
--- a/web/frontend/src/pages/UsersPage.tsx
+++ b/web/frontend/src/pages/UsersPage.tsx
@@ -1,8 +1,9 @@
import { useEffect, useState, useCallback } from 'react';
import {
- Table, Button, Modal, Form, Input, Select, Tag, Popconfirm, message, Space,
+ Table, Button, Modal, Form, Input, Select, Tag, Popconfirm, Space,
Checkbox, Divider, Alert, Tooltip,
} from 'antd';
+import { message } from '../utils/antdMessage';
import { PlusOutlined, EditOutlined, DeleteOutlined, SafetyOutlined } from '@ant-design/icons';
import { userApi, type UserInfo } from '../api/modules';
import { usePermissions } from '../hooks/usePermissions';
diff --git a/web/frontend/src/utils/AntdMessageBridge.tsx b/web/frontend/src/utils/AntdMessageBridge.tsx
new file mode 100644
index 0000000..d88497d
--- /dev/null
+++ b/web/frontend/src/utils/AntdMessageBridge.tsx
@@ -0,0 +1,16 @@
+import { useEffect } from 'react';
+import { App as AntdApp } from 'antd';
+import { setAntdMessageApi } from './antdMessage';
+
+export default function AntdMessageBridge() {
+ const { message } = AntdApp.useApp();
+
+ useEffect(() => {
+ setAntdMessageApi(message);
+ return () => {
+ setAntdMessageApi(null);
+ };
+ }, [message]);
+
+ return null;
+}
diff --git a/web/frontend/src/utils/antdMessage.ts b/web/frontend/src/utils/antdMessage.ts
new file mode 100644
index 0000000..e320c5c
--- /dev/null
+++ b/web/frontend/src/utils/antdMessage.ts
@@ -0,0 +1,38 @@
+import type { Key } from 'react';
+import type {
+ ArgsProps,
+ JointContent,
+ MessageInstance,
+ MessageType,
+ TypeOpen,
+} from 'antd/es/message/interface';
+
+let messageApi: MessageInstance | null = null;
+
+const noopMessage = Object.assign(() => undefined, {
+ then: (
+ onfulfilled?: ((value: boolean) => unknown) | null,
+ onrejected?: ((reason: unknown) => unknown) | null,
+ ) => Promise.resolve(true).then(onfulfilled ?? undefined, onrejected ?? undefined),
+}) as MessageType;
+
+function createTypeOpen(type: 'info' | 'success' | 'error' | 'warning' | 'loading'): TypeOpen {
+ return (content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) => {
+ if (!messageApi) return noopMessage;
+ return messageApi[type](content, duration, onClose);
+ };
+}
+
+export function setAntdMessageApi(api: MessageInstance | null) {
+ messageApi = api;
+}
+
+export const message = {
+ info: createTypeOpen('info'),
+ success: createTypeOpen('success'),
+ error: createTypeOpen('error'),
+ warning: createTypeOpen('warning'),
+ loading: createTypeOpen('loading'),
+ open: (args: ArgsProps) => messageApi?.open(args) ?? noopMessage,
+ destroy: (key?: Key) => messageApi?.destroy(key),
+} satisfies MessageInstance;