diff --git a/src/frontend/src/constants/routes/routes.jsx b/src/frontend/src/constants/routes/routes.jsx
index c479820..e4ea434 100644
--- a/src/frontend/src/constants/routes/routes.jsx
+++ b/src/frontend/src/constants/routes/routes.jsx
@@ -1,6 +1,7 @@
import { Route } from 'react-router-dom';
import Welcome from '@/pages/Welcome';
import Dashboard from '@/pages/Dashboard';
+import ScanPage from '@/pages/ScanPage';
/**
* 路由
@@ -9,6 +10,7 @@ import Dashboard from '@/pages/Dashboard';
const routeList = [
{ path: '/', element: },
{ path: '/dashboard', element: },
+ { path: '/dashboard/scan', element: },
];
const buildRoutes = () =>
diff --git a/src/frontend/src/libs/system/Notification.jsx b/src/frontend/src/libs/system/Notification.jsx
index 463c32b..701f35f 100644
--- a/src/frontend/src/libs/system/Notification.jsx
+++ b/src/frontend/src/libs/system/Notification.jsx
@@ -48,6 +48,7 @@ export const NotificationProvider = ({ children }) => {
const MotionBox = motion(Box);
+ // TODO 弹窗颜色问题及重新渲染问题
return (
{children}
diff --git a/src/frontend/src/pages/Configuration.jsx b/src/frontend/src/pages/Configuration.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/frontend/src/pages/ScanPage.jsx b/src/frontend/src/pages/ScanPage.jsx
new file mode 100644
index 0000000..508493b
--- /dev/null
+++ b/src/frontend/src/pages/ScanPage.jsx
@@ -0,0 +1,164 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Box,
+ VStack,
+ Heading,
+ Input,
+ Button,
+ Text,
+ Spinner,
+ Badge,
+ HStack,
+} from '@chakra-ui/react';
+import { Table } from '@chakra-ui/react';
+import DocumentTitle from '@/components/system/pages/DocumentTitle';
+import PageContainer from '@/components/system/PageContainer';
+import DashboardBackground from '@/components/system/pages/DashboardBackground';
+import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
+import { api } from '@/services/api';
+import { useNotification } from '@/libs/system/Notification';
+
+/**
+ * 网络扫描页面
+ * @returns {JSX.Element}
+ * @constructor
+ */
+const ScanPage = () => {
+ const [subnet, setSubnet] = useState('');
+ const [devices, setDevices] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [localIp, setLocalIp] = useState('');
+ const notify = useNotification();
+
+ useEffect(() => {
+ const fetchLocalInfo = async () => {
+ try {
+ const res = await api.test();
+ if (res.message) {
+ setLocalIp(res.local_ip || '172.17.99.208');
+ if (!subnet) {
+ const ipParts = (res.local_ip || '172.17.99.208').split('.');
+ setSubnet(`${ipParts[0]}.${ipParts[1]}.${ipParts[2]}.0/24`);
+ }
+ }
+ } catch (err) {
+ setLocalIp('未知');
+ notify.error({ title: '获取后端信息失败' });
+ }
+ };
+ fetchLocalInfo();
+ }, [subnet, notify]);
+
+ const handleScan = async () => {
+ setLoading(true);
+ try {
+ const res = await api.scan(subnet);
+ setDevices(res.devices || []);
+ } catch (err) {
+ notify.error({ title: '扫描网络失败' });
+ }
+ setLoading(false);
+ };
+
+ const handleLastScan = async () => {
+ setLoading(true);
+ try {
+ const res = await api.listDevices();
+ setDevices(res.devices || []);
+ } catch (err) {
+ notify.error({ title: '获取上次扫描记录失败' });
+ }
+ setLoading(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+ {'网络扫描'}
+
+
+
+ {'后端服务器IP: '}
+ {localIp}
+
+
+
+ setSubnet(e.target.value)}
+ width={'300px'}
+ bg={'whiteAlpha.200'}
+ />
+
+
+
+
+ {loading && (
+
+
+ {'正在加载,请稍候...'}
+
+ )}
+
+ {!loading && devices.length > 0 && (
+
+
+
+ {'IP 地址'}
+ {'MAC 地址'}
+ {'开放端口'}
+
+
+
+ {devices.map((d) => (
+
+ {d.ip}
+ {d.mac}
+ {d.ports.join(', ')}
+
+ ))}
+
+
+ )}
+
+ {!loading && devices.length === 0 && (
+ {'暂无扫描结果,请执行扫描。'}
+ )}
+
+
+
+
+
+ );
+};
+
+export default ScanPage;