优化登录弹窗

This commit is contained in:
yml2213
2026-06-25 08:54:54 +08:00
parent 0b4fe1daa2
commit 4681136484
2 changed files with 89 additions and 16 deletions
+2 -2
View File
@@ -124,8 +124,8 @@ class AccountOut(BaseModel):
# ---- 登录任务 ---- # ---- 登录任务 ----
class LoginBatchRequest(BaseModel): class LoginBatchRequest(BaseModel):
account_ids: list[int] account_ids: list[int]
max_login_retries: int = 0 # 登录整体重试次数,0=无限重试直到成功 max_login_retries: int = 0 # 登录整体重试次数,0=无限重试直到成功(后端兜底 20 次)
max_total_time: float = 0 # 单账号登录总时长上限,0=不限制 max_total_time: float = 300 # 单账号登录总时长上限(秒)0=不限制
concurrency: int = 3 # 并发数,1-10 concurrency: int = 3 # 并发数,1-10
+87 -14
View File
@@ -1,8 +1,8 @@
import { useEffect, useState, useMemo, useCallback } from 'react'; import { useEffect, useState, useMemo, useCallback } from 'react';
import { import {
Table, Button, Select, message, Tag, Space, InputNumber, Tooltip, Popconfirm, theme, Table, Button, Select, message, Tag, Space, InputNumber, Tooltip, Popconfirm, theme, Modal, Form,
} from 'antd'; } from 'antd';
import { PlayCircleOutlined, StopOutlined, FilterOutlined, ThunderboltOutlined, ReloadOutlined, DeleteOutlined } from '@ant-design/icons'; import { PlayCircleOutlined, StopOutlined, FilterOutlined, ReloadOutlined, DeleteOutlined, SettingOutlined } from '@ant-design/icons';
import { accountApi, loginApi, type AccountItem, type LoginTaskItem } from '../api/modules'; import { accountApi, loginApi, type AccountItem, type LoginTaskItem } from '../api/modules';
import RealtimeLogPanel from '../components/RealtimeLogPanel'; import RealtimeLogPanel from '../components/RealtimeLogPanel';
import { usePermissions } from '../hooks/usePermissions'; import { usePermissions } from '../hooks/usePermissions';
@@ -43,9 +43,20 @@ export default function LoginTasksPage() {
const v = localStorage.getItem('login_concurrency'); const v = localStorage.getItem('login_concurrency');
return v ? Math.max(1, Math.min(10, Number(v) || 3)) : 3; return v ? Math.max(1, Math.min(10, Number(v) || 3)) : 3;
}); });
const [maxTotalTime, setMaxTotalTime] = useState(() => {
const v = localStorage.getItem('login_max_total_time');
return v !== null ? Math.max(0, Number(v) || 0) : 300;
});
const [maxLoginRetries, setMaxLoginRetries] = useState(() => {
const v = localStorage.getItem('login_max_login_retries');
return v !== null ? Math.max(0, Number(v) || 0) : 0;
});
const [settingsOpen, setSettingsOpen] = useState(false);
// 值变化时自动持久化 // 值变化时自动持久化
useEffect(() => { localStorage.setItem('login_concurrency', String(concurrency)); }, [concurrency]); useEffect(() => { localStorage.setItem('login_concurrency', String(concurrency)); }, [concurrency]);
useEffect(() => { localStorage.setItem('login_max_total_time', String(maxTotalTime)); }, [maxTotalTime]);
useEffect(() => { localStorage.setItem('login_max_login_retries', String(maxLoginRetries)); }, [maxLoginRetries]);
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs(); const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs();
const { can } = usePermissions(); const { can } = usePermissions();
@@ -143,6 +154,8 @@ export default function LoginTasksPage() {
const result = await loginApi.createBatch({ const result = await loginApi.createBatch({
account_ids: accountIds, account_ids: accountIds,
concurrency, concurrency,
max_login_retries: maxLoginRetries,
max_total_time: maxTotalTime,
}); });
setBatchId(result.batch_id); setBatchId(result.batch_id);
message.success(`已创建登录任务,共 ${result.count} 个账号`); message.success(`已创建登录任务,共 ${result.count} 个账号`);
@@ -329,18 +342,12 @@ export default function LoginTasksPage() {
{accounts.filter((a) => selectedTags.includes((a.tag || '').trim())).length} {accounts.filter((a) => selectedTags.includes((a.tag || '').trim())).length}
</span> </span>
)} )}
<Tooltip title="同时登录的账号数,1为顺序执行"> <Tooltip title={`登录设置(并发 ${concurrency},超时 ${maxTotalTime || '∞'}s,重试 ${maxLoginRetries || '∞'}`}>
<Space size={4}> <Button
<ThunderboltOutlined style={{ color: token.colorTextSecondary }} /> icon={<SettingOutlined />}
<InputNumber onClick={() => setSettingsOpen(true)}
min={1} size="small"
max={10} />
value={concurrency}
onChange={(v) => setConcurrency(v || 1)}
style={{ width: 50 }}
size="small"
/>
</Space>
</Tooltip> </Tooltip>
<Button <Button
type="primary" type="primary"
@@ -416,6 +423,72 @@ export default function LoginTasksPage() {
spinWhenEmpty spinWhenEmpty
style={{ marginTop: 4 }} style={{ marginTop: 4 }}
/> />
<Modal
title="登录设置"
open={settingsOpen}
onCancel={() => setSettingsOpen(false)}
footer={[
<Button
key="reset"
onClick={() => {
setConcurrency(3);
setMaxTotalTime(300);
setMaxLoginRetries(0);
}}
>
</Button>,
<Button key="ok" type="primary" onClick={() => setSettingsOpen(false)}>
</Button>,
]}
>
<Form layout="vertical" style={{ marginTop: 8 }}>
<Form.Item
label="并发数"
tooltip="同时登录的账号数。1 为顺序执行,最多 10。"
>
<InputNumber
min={1}
max={10}
value={concurrency}
onChange={(v) => setConcurrency(v || 1)}
style={{ width: 120 }}
addonAfter="个"
/>
</Form.Item>
<Form.Item
label="单账号超时"
tooltip="单个账号登录的总时长上限,超时则放弃。0 表示不限。"
>
<InputNumber
min={0}
max={3600}
step={30}
value={maxTotalTime}
onChange={(v) => setMaxTotalTime(v ?? 0)}
style={{ width: 120 }}
addonAfter="秒"
placeholder="0=不限"
/>
</Form.Item>
<Form.Item
label="登录整体重试次数"
tooltip="单个账号最多重试多少轮(每轮换新代理)。0 表示无限重试直到成功或超时。"
>
<InputNumber
min={0}
max={50}
value={maxLoginRetries}
onChange={(v) => setMaxLoginRetries(v ?? 0)}
style={{ width: 120 }}
addonAfter="次"
placeholder="0=无限"
/>
</Form.Item>
</Form>
</Modal>
</div> </div>
); );
} }