需修改

This commit is contained in:
Jerry 2025-06-20 14:02:07 +08:00
parent ae8786f297
commit 921b17e3f5
4 changed files with 167 additions and 0 deletions

View File

@ -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 = () =>

View File

@ -48,6 +48,7 @@ export const NotificationProvider = ({ children }) => {
const MotionBox = motion(Box);
// TODO
return (
<NotificationContext.Provider value={notify}>
{children}

View 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;