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: {
|
languageOptions: {
|
||||||
globals: globals.browser,
|
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(() => {
|
useEffect(() => {
|
||||||
if (!user) navigate('/login');
|
if (!user) navigate('/login');
|
||||||
}, [user]);
|
}, [user, navigate]);
|
||||||
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Table, Button, Modal, Input, Select, message, Popconfirm, Typography, Tag, Space,
|
Table, Button, Modal, Input, Select, message, Popconfirm, Typography, Tag, Space,
|
||||||
Row, Col, Card, Statistic,
|
Row, Col, Card, Statistic,
|
||||||
@@ -35,7 +35,7 @@ export default function AccountsPage() {
|
|||||||
const canAssign = can('account:assign');
|
const canAssign = can('account:assign');
|
||||||
const canDelete = can('account:delete');
|
const canDelete = can('account:delete');
|
||||||
|
|
||||||
const loadAccounts = async () => {
|
const loadAccounts = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const params: { tag?: string } = {};
|
const params: { tag?: string } = {};
|
||||||
@@ -47,35 +47,31 @@ export default function AccountsPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [tagFilter]);
|
||||||
|
|
||||||
const loadUsers = async () => {
|
const loadUsers = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await userApi.list();
|
const data = await userApi.list();
|
||||||
setUsers(data.filter((u) => u.role === 'support'));
|
setUsers(data.filter((u) => u.role === 'support'));
|
||||||
} catch {
|
} catch {
|
||||||
// 忽略客服列表加载失败,账号列表仍可继续使用。
|
// 忽略客服列表加载失败,账号列表仍可继续使用。
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const loadTags = async () => {
|
const loadTags = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await accountApi.listTags();
|
const data = await accountApi.listTags();
|
||||||
setTags(data);
|
setTags(data);
|
||||||
} catch {
|
} catch {
|
||||||
// 忽略标签加载失败,页面会退化为无标签筛选。
|
// 忽略标签加载失败,页面会退化为无标签筛选。
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadAccounts();
|
loadAccounts();
|
||||||
if (canAssign) loadUsers();
|
if (canAssign) loadUsers();
|
||||||
loadTags();
|
loadTags();
|
||||||
}, []);
|
}, [loadAccounts, canAssign, loadUsers, loadTags]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadAccounts();
|
|
||||||
}, [tagFilter]);
|
|
||||||
|
|
||||||
const tagColorMap = useMemo(() => {
|
const tagColorMap = useMemo(() => {
|
||||||
const map: Record<string, string> = {};
|
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 { 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 { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined } from '@ant-design/icons';
|
||||||
import { cookieApi, type CookieItem } from '../api/modules';
|
import { cookieApi, type CookieItem } from '../api/modules';
|
||||||
@@ -19,7 +19,7 @@ export default function CookiePage() {
|
|||||||
const canView = can('cookie:view');
|
const canView = can('cookie:view');
|
||||||
const canExport = can('cookie:export');
|
const canExport = can('cookie:export');
|
||||||
|
|
||||||
const loadCookies = async () => {
|
const loadCookies = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const data = await cookieApi.list();
|
const data = await cookieApi.list();
|
||||||
@@ -29,11 +29,11 @@ export default function CookiePage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadCookies();
|
loadCookies();
|
||||||
}, []);
|
}, [loadCookies]);
|
||||||
|
|
||||||
const handleExport = async (format: string = 'csv') => {
|
const handleExport = async (format: string = 'csv') => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -67,14 +67,14 @@ export default function LoginTasksPage() {
|
|||||||
return map;
|
return map;
|
||||||
}, [accounts]);
|
}, [accounts]);
|
||||||
|
|
||||||
const loadAccounts = async () => {
|
const loadAccounts = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await accountApi.list();
|
const data = await accountApi.list();
|
||||||
setAccounts(data);
|
setAccounts(data);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.error(getErrorMessage(e));
|
message.error(getErrorMessage(e));
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
// 标签选择变化时,同步更新选中的账号
|
// 标签选择变化时,同步更新选中的账号
|
||||||
const handleTagChange = useCallback((tags: string[]) => {
|
const handleTagChange = useCallback((tags: string[]) => {
|
||||||
@@ -107,23 +107,23 @@ export default function LoginTasksPage() {
|
|||||||
});
|
});
|
||||||
}, [selectedTags, tagAccountMap, accounts]);
|
}, [selectedTags, tagAccountMap, accounts]);
|
||||||
|
|
||||||
const loadTasks = async () => {
|
const loadTasks = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await loginApi.listTasks(batchId || undefined);
|
const data = await loginApi.listTasks(batchId || undefined);
|
||||||
setTasks(data);
|
setTasks(data);
|
||||||
} catch {
|
} catch {
|
||||||
// 忽略轮询失败,下一次定时刷新会继续尝试。
|
// 忽略轮询失败,下一次定时刷新会继续尝试。
|
||||||
}
|
}
|
||||||
};
|
}, [batchId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Promise.all([loadAccounts(), loadTasks()]);
|
Promise.all([loadAccounts(), loadTasks()]);
|
||||||
}, []);
|
}, [loadAccounts, loadTasks]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(loadTasks, 3000);
|
const timer = setInterval(loadTasks, 3000);
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, [batchId]);
|
}, [loadTasks]);
|
||||||
|
|
||||||
// 共享的批量登录启动逻辑
|
// 共享的批量登录启动逻辑
|
||||||
const startBatch = async (accountIds: number[]) => {
|
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 { Form, Input, Switch, Button, Card, message, Row, Col } from 'antd';
|
||||||
import { proxyApi, type ProxyConfig } from '../api/modules';
|
import { proxyApi, type ProxyConfig } from '../api/modules';
|
||||||
import RealtimeLogPanel from '../components/RealtimeLogPanel';
|
import RealtimeLogPanel from '../components/RealtimeLogPanel';
|
||||||
@@ -13,7 +13,7 @@ export default function ProxyPage() {
|
|||||||
const [configLoaded, setConfigLoaded] = useState(false);
|
const [configLoaded, setConfigLoaded] = useState(false);
|
||||||
const { logs, connect: connectLogs, close: closeLogs } = useWebSocketLogs();
|
const { logs, connect: connectLogs, close: closeLogs } = useWebSocketLogs();
|
||||||
|
|
||||||
const loadConfig = async () => {
|
const loadConfig = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await proxyApi.get();
|
const data = await proxyApi.get();
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
@@ -30,14 +30,14 @@ export default function ProxyPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setConfigLoaded(true);
|
setConfigLoaded(true);
|
||||||
}
|
}
|
||||||
};
|
}, [form]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadConfig();
|
loadConfig();
|
||||||
return () => {
|
return () => {
|
||||||
closeLogs();
|
closeLogs();
|
||||||
};
|
};
|
||||||
}, []);
|
}, [loadConfig, closeLogs]);
|
||||||
|
|
||||||
const connectWs = (testId: string) => {
|
const connectWs = (testId: string) => {
|
||||||
connectLogs(`/api/proxy/ws/test/${testId}`, {
|
connectLogs(`/api/proxy/ws/test/${testId}`, {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Table, Button, Modal, Form, Input, Select, Tag, Popconfirm, message, Space,
|
Table, Button, Modal, Form, Input, Select, Tag, Popconfirm, message, Space,
|
||||||
Checkbox, Divider, Alert, Tooltip,
|
Checkbox, Divider, Alert, Tooltip,
|
||||||
@@ -54,7 +54,7 @@ export default function UsersPage() {
|
|||||||
|
|
||||||
const canAssignPerm = can('user:assign_permissions');
|
const canAssignPerm = can('user:assign_permissions');
|
||||||
|
|
||||||
const loadUsers = async () => {
|
const loadUsers = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const data = await userApi.list();
|
const data = await userApi.list();
|
||||||
@@ -64,11 +64,11 @@ export default function UsersPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUsers();
|
loadUsers();
|
||||||
}, []);
|
}, [loadUsers]);
|
||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
setEditing(null);
|
setEditing(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user