mirror of
https://github.com/Jerryplusy/AI-powered-switches.git
synced 2025-07-03 20:59:19 +00:00
需修改
This commit is contained in:
parent
ae8786f297
commit
921b17e3f5
@ -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: <Welcome /> },
|
||||
{ path: '/dashboard', element: <Dashboard /> },
|
||||
{ path: '/dashboard/scan', element: <ScanPage /> },
|
||||
];
|
||||
|
||||
const buildRoutes = () =>
|
||||
|
@ -48,6 +48,7 @@ export const NotificationProvider = ({ children }) => {
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
// TODO 弹窗颜色问题及重新渲染问题
|
||||
return (
|
||||
<NotificationContext.Provider value={notify}>
|
||||
{children}
|
||||
|
164
src/frontend/src/pages/ScanPage.jsx
Normal file
164
src/frontend/src/pages/ScanPage.jsx
Normal file
@ -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 (
|
||||
<DocumentTitle title={'网络扫描'}>
|
||||
<DashboardBackground />
|
||||
<PageContainer>
|
||||
<FadeInWrapper delay={0.3} yOffset={-5}>
|
||||
<VStack spacing={8} align={'stretch'}>
|
||||
<Box
|
||||
p={6}
|
||||
bg={'whiteAlpha.100'}
|
||||
borderRadius={'xl'}
|
||||
border={'1px solid'}
|
||||
borderColor={'whiteAlpha.300'}
|
||||
mx={4}
|
||||
transition={'all 0.2s'}
|
||||
_hover={{ transform: 'translateY(-4px)' }}
|
||||
>
|
||||
<Heading fontSize={'2xl'} mb={4} color={'teal.300'}>
|
||||
{'网络扫描'}
|
||||
</Heading>
|
||||
|
||||
<HStack mb={4}>
|
||||
<Text fontWeight={'medium'}>{'后端服务器IP: '}</Text>
|
||||
<Badge colorPalette={'blue'}>{localIp}</Badge>
|
||||
</HStack>
|
||||
|
||||
<HStack mb={4} spacing={4}>
|
||||
<Input
|
||||
placeholder={'输入子网 (如 192.168.1.0/24)'}
|
||||
value={subnet}
|
||||
onChange={(e) => setSubnet(e.target.value)}
|
||||
width={'300px'}
|
||||
bg={'whiteAlpha.200'}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleScan}
|
||||
isLoading={loading}
|
||||
colorPalette={'teal'}
|
||||
variant={'solid'}
|
||||
>
|
||||
{'开始扫描'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleLastScan}
|
||||
isLoading={loading}
|
||||
colorPalette={'blue'}
|
||||
variant={'outline'}
|
||||
>
|
||||
{'显示上次结果'}
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
{loading && (
|
||||
<HStack>
|
||||
<Spinner />
|
||||
<Text>{'正在加载,请稍候...'}</Text>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{!loading && devices.length > 0 && (
|
||||
<Table.Root variant={'outline'} striped={'true'} size={'md'} mt={4}>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.ColumnHeader>{'IP 地址'}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{'MAC 地址'}</Table.ColumnHeader>
|
||||
<Table.ColumnHeader>{'开放端口'}</Table.ColumnHeader>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{devices.map((d) => (
|
||||
<Table.Row key={d.ip}>
|
||||
<Table.Cell>{d.ip}</Table.Cell>
|
||||
<Table.Cell>{d.mac}</Table.Cell>
|
||||
<Table.Cell>{d.ports.join(', ')}</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
)}
|
||||
|
||||
{!loading && devices.length === 0 && (
|
||||
<Text color={'gray.400'}>{'暂无扫描结果,请执行扫描。'}</Text>
|
||||
)}
|
||||
</Box>
|
||||
</VStack>
|
||||
</FadeInWrapper>
|
||||
</PageContainer>
|
||||
</DocumentTitle>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScanPage;
|
Loading…
x
Reference in New Issue
Block a user