初始配置组件,密钥认证组件
3
.idea/AI-powered-switches.iml
generated
@ -2,7 +2,7 @@
|
|||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="FacetManager">
|
<component name="FacetManager">
|
||||||
<facet type="Python" name="Python facet">
|
<facet type="Python" name="Python facet">
|
||||||
<configuration sdkName="Python 3.10" />
|
<configuration sdkName="Python 3.13" />
|
||||||
</facet>
|
</facet>
|
||||||
</component>
|
</component>
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
@ -11,5 +11,6 @@
|
|||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Python 3.13 interpreter library" level="application" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -26,3 +26,5 @@
|
|||||||
- []下发配置到交换机
|
- []下发配置到交换机
|
||||||
- []流量预测
|
- []流量预测
|
||||||
- []图表显示
|
- []图表显示
|
||||||
|
|
||||||
|
172.17.99.208
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, Image } from '@chakra-ui/react';
|
import { Box, Image } from '@chakra-ui/react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import image from '@/resources/welcome/image/background.png';
|
import image from '@/resources/image/welcome/background.png';
|
||||||
|
|
||||||
const MotionBox = motion(Box);
|
const MotionBox = motion(Box);
|
||||||
|
|
||||||
|
18
src/frontend/src/components/pages/welcome/ConfigureCard.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import MotionCard from '@/components/ui/MotionCard';
|
||||||
|
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
||||||
|
import configIcon from '@/resources/icon/pages/weclome/config.svg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接配置卡片组件
|
||||||
|
* @param onClick 点击事件
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
const ConfigureCard = ({ onClick }) => {
|
||||||
|
return (
|
||||||
|
<FadeInWrapper delay={0.4} yOffset={-5}>
|
||||||
|
<MotionCard icon={configIcon} text={'连接配置'} onClick={onClick} />
|
||||||
|
</FadeInWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfigureCard;
|
@ -0,0 +1,183 @@
|
|||||||
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Box,
|
||||||
|
Dialog,
|
||||||
|
DialogBackdrop,
|
||||||
|
DialogPositioner,
|
||||||
|
DialogContent,
|
||||||
|
DialogCloseTrigger,
|
||||||
|
DialogHeader,
|
||||||
|
DialogBody,
|
||||||
|
DialogFooter,
|
||||||
|
Field,
|
||||||
|
Input,
|
||||||
|
Portal,
|
||||||
|
Stack,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { IoClose } from 'react-icons/io5';
|
||||||
|
import { FiCheck } from 'react-icons/fi';
|
||||||
|
|
||||||
|
const MotionBox = motion(Box);
|
||||||
|
const defaultConfig = {
|
||||||
|
backendUrl: '',
|
||||||
|
authKey: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConnectionConfigModal = ({ isOpen, onClose, onSave }) => {
|
||||||
|
const [config, setConfig] = useState(defaultConfig);
|
||||||
|
const [saved, setSaved] = useState(false);
|
||||||
|
const backendRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const savedData = localStorage.getItem('connection-config');
|
||||||
|
if (savedData) {
|
||||||
|
setConfig(JSON.parse(savedData));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setConfig((prev) => ({ ...prev, [name]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
localStorage.setItem('connection-config', JSON.stringify(config));
|
||||||
|
onSave(config);
|
||||||
|
setSaved(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setSaved(false);
|
||||||
|
onClose();
|
||||||
|
}, 1200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
localStorage.removeItem('connection-config');
|
||||||
|
setConfig(defaultConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog.Root open={isOpen} onClose={onClose} initialFocusEl={() => backendRef.current}>
|
||||||
|
<Portal>
|
||||||
|
<DialogBackdrop backdropFilter={'blur(8px) hue-rotate(0deg)'} bg={'rgba(0, 0, 0, 0.3)'} />
|
||||||
|
<DialogPositioner>
|
||||||
|
<MotionBox
|
||||||
|
as={DialogContent}
|
||||||
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.8 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
bg={'whiteAlpha.100'}
|
||||||
|
backdropFilter={'blur(12px)'}
|
||||||
|
border={'1px solid'}
|
||||||
|
borderColor={'whiteAlpha.300'}
|
||||||
|
>
|
||||||
|
<DialogHeader color={'white'}>地址配置</DialogHeader>
|
||||||
|
<DialogCloseTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
borderColor={'whiteAlpha.500'}
|
||||||
|
color={'white'}
|
||||||
|
size={'sm'}
|
||||||
|
position={'absolute'}
|
||||||
|
top={3}
|
||||||
|
right={3}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<IoClose size={20} color={'white'} />
|
||||||
|
</Button>
|
||||||
|
</DialogCloseTrigger>
|
||||||
|
|
||||||
|
<DialogBody>
|
||||||
|
<Stack gap="4">
|
||||||
|
<Field.Root>
|
||||||
|
<Field.Label color={'white'}>后端连接地址</Field.Label>
|
||||||
|
<Input
|
||||||
|
ref={backendRef}
|
||||||
|
name={'backendUrl'}
|
||||||
|
value={config.backendUrl}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder={'http://127.0.0.1:8000'}
|
||||||
|
bg={'whiteAlpha.200'}
|
||||||
|
color={'white'}
|
||||||
|
/>
|
||||||
|
</Field.Root>
|
||||||
|
<Field.Root>
|
||||||
|
<Field.Label color={'white'}>连接密钥</Field.Label>
|
||||||
|
<Input
|
||||||
|
name={'authKey'}
|
||||||
|
type={'password'}
|
||||||
|
value={config.authKey}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder={'123456'}
|
||||||
|
bg={'whiteAlpha.200'}
|
||||||
|
color={'white'}
|
||||||
|
/>
|
||||||
|
</Field.Root>
|
||||||
|
</Stack>
|
||||||
|
</DialogBody>
|
||||||
|
|
||||||
|
<DialogFooter justifyContent={'space-between'}>
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
borderColor={'whiteAlpha.500'}
|
||||||
|
color={'white'}
|
||||||
|
onClick={handleClear}
|
||||||
|
>
|
||||||
|
清除配置
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Stack direction={'row'} spacing={3}>
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
borderColor={'whiteAlpha.500'}
|
||||||
|
color={'white'}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
borderColor={'whiteAlpha.500'}
|
||||||
|
color={'white'}
|
||||||
|
onClick={handleSave}
|
||||||
|
isDisabled={saved}
|
||||||
|
position={'relative'}
|
||||||
|
width={'80px'}
|
||||||
|
>
|
||||||
|
<AnimatePresence initial={false} mode={'wait'}>
|
||||||
|
{saved ? (
|
||||||
|
<motion.div
|
||||||
|
key={'saved'}
|
||||||
|
initial={{ opacity: 0, scale: 0.5 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.5 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<FiCheck size={20} color={'lightgreen'} />
|
||||||
|
</motion.div>
|
||||||
|
) : (
|
||||||
|
<motion.div
|
||||||
|
key={'save'}
|
||||||
|
initial={{ opacity: 0, scale: 0.5 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.5 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</DialogFooter>
|
||||||
|
</MotionBox>
|
||||||
|
</DialogPositioner>
|
||||||
|
</Portal>
|
||||||
|
</Dialog.Root>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConnectionConfigModal;
|
@ -1,4 +1,4 @@
|
|||||||
import manageIcon from '@/resources/welcome/image/setting.svg';
|
import manageIcon from '@/resources/icon/pages/weclome/setting.svg';
|
||||||
import MotionCard from '@/components/ui/MotionCard';
|
import MotionCard from '@/components/ui/MotionCard';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
||||||
@ -8,11 +8,17 @@ import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
|||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const DashboardCard = () => {
|
const DashboardCard = ({ isConfigured }) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const handleClick = () => {
|
||||||
|
if (isConfigured) {
|
||||||
|
navigate('/dashboard');
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<FadeInWrapper delay={0.4} yOffset={-5}>
|
<FadeInWrapper delay={0.4} yOffset={-5}>
|
||||||
<MotionCard icon={manageIcon} text={'管理后台'} onClick={() => navigate('/dashboard')} />
|
<MotionCard icon={manageIcon} text={'管理后台'} onClick={() => handleClick()} />
|
||||||
</FadeInWrapper>
|
</FadeInWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import githubIcon from '@/resources/welcome/image/github.svg';
|
import githubIcon from '@/resources/icon/pages/weclome/github.svg';
|
||||||
import MotionCard from '@/components/ui/MotionCard';
|
import MotionCard from '@/components/ui/MotionCard';
|
||||||
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
||||||
|
|
||||||
|
@ -1,18 +1,45 @@
|
|||||||
import { Box, Heading, Text, VStack } from '@chakra-ui/react';
|
import { Box, Heading, Text, VStack, HStack } from '@chakra-ui/react';
|
||||||
import DashboardCard from '@/components/pages/welcome/DashboardCard';
|
import DashboardCard from '@/components/pages/welcome/DashboardCard';
|
||||||
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import ConnectionConfigModal from '@/components/pages/welcome/ConnectionConfigModal';
|
||||||
|
import ConfigureCard from '@/components/pages/welcome/ConfigureCard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 欢迎文字
|
* 欢迎页面内容
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
const WelcomeContent = () => {
|
const WelcomeContent = () => {
|
||||||
|
const [showConfigModal, setShowConfigModal] = useState(false);
|
||||||
|
const [isConfigured, setIsConfigured] = useState(false);
|
||||||
|
//const [showAlert, setShowAlert] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = localStorage.getItem('connection-config');
|
||||||
|
if (saved) {
|
||||||
|
setIsConfigured(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
setIsConfigured(true);
|
||||||
|
//setShowAlert(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfigureClick = () => {
|
||||||
|
if (!isConfigured) {
|
||||||
|
//setShowAlert(true);
|
||||||
|
}
|
||||||
|
setShowConfigModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<VStack spacing={10} py={200} align={'center'} px={4}>
|
<VStack spacing={10} py={200} align={'center'} px={4}>
|
||||||
<FadeInWrapper delay={0.2} yOffset={-5}>
|
<FadeInWrapper delay={0.2} yOffset={-5}>
|
||||||
<Box textAlign={'center'}>
|
<Box textAlign={'center'}>
|
||||||
<Heading size="6xl" fontWeight={'black'} color={'teal.300'}>
|
<Heading size={'6xl'} fontWeight={'black'} color={'teal.300'}>
|
||||||
智能网络交换机
|
智能网络交换机
|
||||||
<br />
|
<br />
|
||||||
管理系统
|
管理系统
|
||||||
@ -26,8 +53,21 @@ const WelcomeContent = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</FadeInWrapper>
|
</FadeInWrapper>
|
||||||
<DashboardCard />
|
|
||||||
|
<FadeInWrapper delay={0.4} yOffset={-5}>
|
||||||
|
<HStack spacing={6}>
|
||||||
|
<DashboardCard isConfigured={isConfigured} />
|
||||||
|
<ConfigureCard onClick={handleConfigureClick} />
|
||||||
|
</HStack>
|
||||||
|
</FadeInWrapper>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
|
<ConnectionConfigModal
|
||||||
|
isOpen={showConfigModal}
|
||||||
|
onClose={() => setShowConfigModal(false)}
|
||||||
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { Button, HStack } from '@chakra-ui/react';
|
import { Button, HStack } from '@chakra-ui/react';
|
||||||
import web from '@/resources/icon/web.svg';
|
import web from '@/resources/icon/pages/weclome/web.svg';
|
||||||
import githubIcon from '@/resources/welcome/image/github.svg';
|
import githubIcon from '@/resources/icon/pages/weclome/github.svg';
|
||||||
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
import FadeInWrapper from '@/components/system/layout/FadeInWrapper';
|
||||||
import MotionCard from '@/components/ui/MotionCard';
|
import MotionCard from '@/components/ui/MotionCard';
|
||||||
|
|
||||||
|
1
src/frontend/src/resources/icon/pages/weclome/config.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 512 512"><path fill="none" stroke="#2EE2E5" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M262.29 192.31a64 64 0 1 0 57.4 57.4a64.13 64.13 0 0 0-57.4-57.4M416.39 256a154 154 0 0 1-1.53 20.79l45.21 35.46a10.81 10.81 0 0 1 2.45 13.75l-42.77 74a10.81 10.81 0 0 1-13.14 4.59l-44.9-18.08a16.11 16.11 0 0 0-15.17 1.75A164.5 164.5 0 0 1 325 400.8a15.94 15.94 0 0 0-8.82 12.14l-6.73 47.89a11.08 11.08 0 0 1-10.68 9.17h-85.54a11.11 11.11 0 0 1-10.69-8.87l-6.72-47.82a16.07 16.07 0 0 0-9-12.22a155 155 0 0 1-21.46-12.57a16 16 0 0 0-15.11-1.71l-44.89 18.07a10.81 10.81 0 0 1-13.14-4.58l-42.77-74a10.8 10.8 0 0 1 2.45-13.75l38.21-30a16.05 16.05 0 0 0 6-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 0 0-6.07-13.94l-38.19-30A10.81 10.81 0 0 1 49.48 186l42.77-74a10.81 10.81 0 0 1 13.14-4.59l44.9 18.08a16.11 16.11 0 0 0 15.17-1.75A164.5 164.5 0 0 1 187 111.2a15.94 15.94 0 0 0 8.82-12.14l6.73-47.89A11.08 11.08 0 0 1 213.23 42h85.54a11.11 11.11 0 0 1 10.69 8.87l6.72 47.82a16.07 16.07 0 0 0 9 12.22a155 155 0 0 1 21.46 12.57a16 16 0 0 0 15.11 1.71l44.89-18.07a10.81 10.81 0 0 1 13.14 4.58l42.77 74a10.8 10.8 0 0 1-2.45 13.75l-38.21 30a16.05 16.05 0 0 0-6.05 14.08c.33 4.14.55 8.3.55 12.47"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |