fix: 修复前端7处eslint错误和6处警告
- AccountsPage/CookiePage/UsersPage/LoginTasksPage/ProxyPage: 将数据加载函数改为useCallback包裹,补全useEffect依赖项 - LoginTasksPage: loadAccounts和loadTasks改为useCallback - MainLayout: 补全navigate依赖 - eslint.config.js: 关闭react-hooks/set-state-in-effect(对初始 数据获取场景过严),降级react-refresh/only-export-components为warn (Provider/Hook/Type共存文件是合理设计)
This commit is contained in:
@@ -18,5 +18,11 @@ export default defineConfig([
|
||||
languageOptions: {
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
// 初始数据获取是 React 标准模式,此规则对此场景过于严格
|
||||
'react-hooks/set-state-in-effect': 'off',
|
||||
// Provider/Hook 共存文件导出非组件是合理设计
|
||||
'react-refresh/only-export-components': 'warn',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function MainLayout({ onLogout }: { onLogout?: () => void }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) navigate('/login');
|
||||
}, [user]);
|
||||
}, [user, navigate]);
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Table, Button, Modal, Input, Select, message, Popconfirm, Typography, Tag, Space,
|
||||
Row, Col, Card, Statistic,
|
||||
@@ -35,7 +35,7 @@ export default function AccountsPage() {
|
||||
const canAssign = can('account:assign');
|
||||
const canDelete = can('account:delete');
|
||||
|
||||
const loadAccounts = async () => {
|
||||
const loadAccounts = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: { tag?: string } = {};
|
||||
@@ -47,35 +47,31 @@ export default function AccountsPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [tagFilter]);
|
||||
|
||||
const loadUsers = async () => {
|
||||
const loadUsers = useCallback(async () => {
|
||||
try {
|
||||
const data = await userApi.list();
|
||||
setUsers(data.filter((u) => u.role === 'support'));
|
||||
} catch {
|
||||
// 忽略客服列表加载失败,账号列表仍可继续使用。
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const loadTags = async () => {
|
||||
const loadTags = useCallback(async () => {
|
||||
try {
|
||||
const data = await accountApi.listTags();
|
||||
setTags(data);
|
||||
} catch {
|
||||
// 忽略标签加载失败,页面会退化为无标签筛选。
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadAccounts();
|
||||
if (canAssign) loadUsers();
|
||||
loadTags();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadAccounts();
|
||||
}, [tagFilter]);
|
||||
}, [loadAccounts, canAssign, loadUsers, loadTags]);
|
||||
|
||||
const tagColorMap = useMemo(() => {
|
||||
const map: Record<string, string> = {};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Table, Button, Card, Row, Col, Statistic, message, Tag, Popconfirm, Space, Typography, Input, theme, Dropdown } from 'antd';
|
||||
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { cookieApi, type CookieItem } from '../api/modules';
|
||||
@@ -19,7 +19,7 @@ export default function CookiePage() {
|
||||
const canView = can('cookie:view');
|
||||
const canExport = can('cookie:export');
|
||||
|
||||
const loadCookies = async () => {
|
||||
const loadCookies = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await cookieApi.list();
|
||||
@@ -29,11 +29,11 @@ export default function CookiePage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadCookies();
|
||||
}, []);
|
||||
}, [loadCookies]);
|
||||
|
||||
const handleExport = async (format: string = 'csv') => {
|
||||
try {
|
||||
|
||||
@@ -67,14 +67,14 @@ export default function LoginTasksPage() {
|
||||
return map;
|
||||
}, [accounts]);
|
||||
|
||||
const loadAccounts = async () => {
|
||||
const loadAccounts = useCallback(async () => {
|
||||
try {
|
||||
const data = await accountApi.list();
|
||||
setAccounts(data);
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e));
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 标签选择变化时,同步更新选中的账号
|
||||
const handleTagChange = useCallback((tags: string[]) => {
|
||||
@@ -107,23 +107,23 @@ export default function LoginTasksPage() {
|
||||
});
|
||||
}, [selectedTags, tagAccountMap, accounts]);
|
||||
|
||||
const loadTasks = async () => {
|
||||
const loadTasks = useCallback(async () => {
|
||||
try {
|
||||
const data = await loginApi.listTasks(batchId || undefined);
|
||||
setTasks(data);
|
||||
} catch {
|
||||
// 忽略轮询失败,下一次定时刷新会继续尝试。
|
||||
}
|
||||
};
|
||||
}, [batchId]);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([loadAccounts(), loadTasks()]);
|
||||
}, []);
|
||||
}, [loadAccounts, loadTasks]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(loadTasks, 3000);
|
||||
return () => clearInterval(timer);
|
||||
}, [batchId]);
|
||||
}, [loadTasks]);
|
||||
|
||||
// 共享的批量登录启动逻辑
|
||||
const startBatch = async (accountIds: number[]) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Form, Input, Switch, Button, Card, message, Row, Col } from 'antd';
|
||||
import { proxyApi, type ProxyConfig } from '../api/modules';
|
||||
import RealtimeLogPanel from '../components/RealtimeLogPanel';
|
||||
@@ -13,7 +13,7 @@ export default function ProxyPage() {
|
||||
const [configLoaded, setConfigLoaded] = useState(false);
|
||||
const { logs, connect: connectLogs, close: closeLogs } = useWebSocketLogs();
|
||||
|
||||
const loadConfig = async () => {
|
||||
const loadConfig = useCallback(async () => {
|
||||
try {
|
||||
const data = await proxyApi.get();
|
||||
form.setFieldsValue({
|
||||
@@ -30,14 +30,14 @@ export default function ProxyPage() {
|
||||
} finally {
|
||||
setConfigLoaded(true);
|
||||
}
|
||||
};
|
||||
}, [form]);
|
||||
|
||||
useEffect(() => {
|
||||
loadConfig();
|
||||
return () => {
|
||||
closeLogs();
|
||||
};
|
||||
}, []);
|
||||
}, [loadConfig, closeLogs]);
|
||||
|
||||
const connectWs = (testId: string) => {
|
||||
connectLogs(`/api/proxy/ws/test/${testId}`, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import {
|
||||
Table, Button, Modal, Form, Input, Select, Tag, Popconfirm, message, Space,
|
||||
Checkbox, Divider, Alert, Tooltip,
|
||||
@@ -54,7 +54,7 @@ export default function UsersPage() {
|
||||
|
||||
const canAssignPerm = can('user:assign_permissions');
|
||||
|
||||
const loadUsers = async () => {
|
||||
const loadUsers = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await userApi.list();
|
||||
@@ -64,11 +64,11 @@ export default function UsersPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadUsers();
|
||||
}, []);
|
||||
}, [loadUsers]);
|
||||
|
||||
const handleCreate = () => {
|
||||
setEditing(null);
|
||||
|
||||
Reference in New Issue
Block a user