feat:交换机可配置username和password

This commit is contained in:
Jerry 2025-07-11 13:54:46 +08:00
parent 8d1889bba7
commit 6ce1c89d8c
3 changed files with 180 additions and 11 deletions

View File

@ -0,0 +1,141 @@
import React, { useState } from 'react';
import {
Button,
Box,
Dialog,
DialogBackdrop,
DialogPositioner,
DialogContent,
DialogHeader,
DialogBody,
DialogFooter,
Field,
Input,
Stack,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { FiCheck } from 'react-icons/fi';
import Notification from '@/libs/system/Notification';
const MotionBox = motion(Box);
/**
* 设备配置弹窗
* @param isOpen 是否打开
* @param onClose 关闭弹窗
* @param onSave 保存修改
* @param device 当前设备
* @returns {JSX.Element}
* @constructor
*/
const DeviceConfigModal = ({ isOpen, onClose, onSave, device }) => {
const [username, setUsername] = useState(device.username || '');
const [password, setPassword] = useState(device.password || '');
const [saved, setSaved] = useState(false);
const handleSave = () => {
const updatedDevice = { ...device, username, password };
onSave(updatedDevice);
setSaved(true);
setTimeout(() => {
setSaved(false);
Notification.success({
title: '设备配置已保存!',
});
onClose();
}, 1200);
};
return (
<Dialog.Root open={isOpen} onClose={onClose}>
<DialogBackdrop />
<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>交换机设备配置</DialogHeader>
<DialogBody>
<Stack gap={4}>
<Field.Root>
<Field.Label>交换机用户名</Field.Label>
<Input
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder={'请输入设备用户名'}
bg={'whiteAlpha.200'}
/>
</Field.Root>
<Field.Root>
<Field.Label>交换机密码</Field.Label>
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder={'请输入设备密码'}
bg={'whiteAlpha.200'}
type={'password'}
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter justifyContent={'space-between'}>
<Button
variant={'outline'}
borderColor={'whiteAlpha.500'}
color={'white'}
onClick={onClose}
_hover={{ bg: 'rgba(0, 0, 255, 0.3)' }}
>
取消
</Button>
<Button
variant={'outline'}
borderColor={'whiteAlpha.500'}
color={'white'}
onClick={handleSave}
isDisabled={saved}
width={'80px'}
position={'relative'}
_hover={{ bg: 'rgba(0, 0, 255, 0.3)' }}
>
{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>
)}
</Button>
</DialogFooter>
</MotionBox>
</DialogPositioner>
</Dialog.Root>
);
};
export default DeviceConfigModal;

View File

@ -34,7 +34,6 @@ const ConfigPage = () => {
const [editableConfig, setEditableConfig] = useState('');
const [applying, setApplying] = useState(false);
const [hasParsed, setHasParsed] = useState(false);
const [conmmand, setConmmand] = useState([]);
const [isPeizhi, setisPeizhi] = useState(false);
const [isApplying, setIsApplying] = useState(false);
const [applyStatus, setApplyStatus] = useState([]);
@ -90,10 +89,6 @@ const ConfigPage = () => {
setParsedConfig(JSON.stringify(result.data));
setEditableConfig(JSON.stringify(result.data));
setHasParsed(true);
result = result.data;
if (result.config && Array.isArray(result.config.commands)) {
setConmmand(result.config.commands);
}
setisPeizhi(true);
}
} catch (error) {
@ -119,7 +114,7 @@ const ConfigPage = () => {
try {
const applyOperation = testMode
? Common.sleep(1000).then(() => ({ success: true }))
: await api.applyConfig(selectedDevice, conmmand);
: await api.applyConfig(selectedDevice, JSON.parse(editableConfig)?.config?.commands);
await Notification.promise({
promise: applyOperation,
@ -150,7 +145,6 @@ const ConfigPage = () => {
<Heading fontSize={'xl'} color={'teal.300'}>
交换机配置中心
</Heading>
<Field.Root>
<Field.Label fontWeight={'bold'} mb={1} fontSize="sm">
选择交换机设备
@ -186,7 +180,6 @@ const ConfigPage = () => {
</Portal>
</Select.Root>
</Field.Root>
<Field.Root>
<Field.Label fontWeight={'bold'} mb={1} fontSize="sm">
配置指令输入
@ -202,7 +195,6 @@ const ConfigPage = () => {
size={'sm'}
/>
</Field.Root>
<Button
colorScheme={'teal'}
variant={'solid'}
@ -212,7 +204,6 @@ const ConfigPage = () => {
>
解析配置
</Button>
{isPeizhi && parsedConfig && (
<FadeInWrapper delay={0.2}>
<VStack spacing={4} align={'stretch'}>
@ -256,7 +247,6 @@ const ConfigPage = () => {
const updated = JSON.parse(editableConfig);
updated.config[key] = newVal;
setEditableConfig(JSON.stringify(updated, null, 2));
setConmmand(updated);
}}
/>
</Field.Root>

View File

@ -19,6 +19,7 @@ import ConfigTool from '@/libs/config/ConfigTool';
import Common from '@/libs/common';
import switchIcon from '@/resources/icon/pages/devices/switch.png';
import Notification from '@/libs/system/Notification';
import DeviceConfigModal from '@/components/pages/config/DeviceConfigModal';
/**
* 交换机管理
@ -29,6 +30,8 @@ const DevicesPage = () => {
const [devices, setDevices] = useState([]);
const [editingIndex, setEditingIndex] = useState(null);
const [editingName, setEditingName] = useState('');
const [isModalOpen, setIsModalOpen] = useState(false);
const [currentDevice, setCurrentDevice] = useState(null);
useEffect(() => {
const config = ConfigTool.load();
@ -53,6 +56,23 @@ const DevicesPage = () => {
});
};
const handleOpenConfigModal = (device) => {
setCurrentDevice(device);
setIsModalOpen(true);
};
const handleSaveDeviceConfig = (updatedDevice) => {
const updatedDevices = devices.map((device) =>
device.ip === updatedDevice.ip ? updatedDevice : device
);
setDevices(updatedDevices);
ConfigTool.save({ ...ConfigTool.load(), devices: updatedDevices });
Notification.success({
title: '设备配置已保存!',
});
setIsModalOpen(false);
};
return (
<DocumentTitle title={'交换机设备'}>
<DashboardBackground />
@ -124,6 +144,15 @@ const DevicesPage = () => {
{'端口: '}
{device.ports.join(', ')}
</Text>
<Button
size={'sm'}
colorPalette={'teal'}
mt={2}
onClick={() => handleOpenConfigModal(device)}
>
配置
</Button>
</Box>
))}
</SimpleGrid>
@ -134,6 +163,15 @@ const DevicesPage = () => {
</VStack>
</FadeInWrapper>
</PageContainer>
{isModalOpen && currentDevice && (
<DeviceConfigModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSave={handleSaveDeviceConfig}
device={currentDevice}
/>
)}
</DocumentTitle>
);
};