diff --git a/src/frontend/src/components/pages/config/DeviceConfigModal.jsx b/src/frontend/src/components/pages/config/DeviceConfigModal.jsx
new file mode 100644
index 0000000..744ad81
--- /dev/null
+++ b/src/frontend/src/components/pages/config/DeviceConfigModal.jsx
@@ -0,0 +1,138 @@
+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);
+ onClose();
+ }, 1200);
+ };
+
+ return (
+
+
+
+
+ 交换机设备配置
+
+
+
+
+ 交换机用户名
+ setUsername(e.target.value)}
+ placeholder={'请输入设备用户名'}
+ bg={'whiteAlpha.200'}
+ />
+
+
+
+ 交换机密码
+ setPassword(e.target.value)}
+ placeholder={'请输入设备密码'}
+ bg={'whiteAlpha.200'}
+ type={'password'}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DeviceConfigModal;
diff --git a/src/frontend/src/pages/ConfigPage.jsx b/src/frontend/src/pages/ConfigPage.jsx
index 5e77270..bc5d98a 100644
--- a/src/frontend/src/pages/ConfigPage.jsx
+++ b/src/frontend/src/pages/ConfigPage.jsx
@@ -8,10 +8,12 @@ import {
HStack,
Portal,
Select,
+ Spinner,
Text,
Textarea,
VStack,
} from '@chakra-ui/react';
+
import DocumentTitle from '@/components/system/pages/DocumentTitle';
import PageContainer from '@/components/system/PageContainer';
import DashboardBackground from '@/components/system/pages/DashboardBackground';
@@ -32,6 +34,9 @@ const ConfigPage = () => {
const [editableConfig, setEditableConfig] = useState('');
const [applying, setApplying] = useState(false);
const [hasParsed, setHasParsed] = useState(false);
+ const [isPeizhi, setisPeizhi] = useState(false);
+ const [isApplying, setIsApplying] = useState(false);
+ const [applyStatus, setApplyStatus] = useState([]);
const deviceCollection = createListCollection({
items: devices.map((device) => ({
@@ -79,12 +84,12 @@ const ConfigPage = () => {
},
});
- const result = await resultWrapper.unwrap();
-
- if (result?.config) {
- setParsedConfig(result.config);
- setEditableConfig(result.config);
+ let result = await resultWrapper.unwrap();
+ if (result?.data) {
+ setParsedConfig(JSON.stringify(result.data));
+ setEditableConfig(JSON.stringify(result.data));
setHasParsed(true);
+ setisPeizhi(true);
}
} catch (error) {
console.error('配置解析异常:', error);
@@ -105,10 +110,11 @@ const ConfigPage = () => {
}
setApplying(true);
+ setIsApplying(true);
try {
const applyOperation = testMode
? Common.sleep(1000).then(() => ({ success: true }))
- : await api.applyConfig(selectedDevice, editableConfig);
+ : await api.applyConfig(selectedDevice, JSON.parse(editableConfig)?.config?.commands);
await Notification.promise({
promise: applyOperation,
@@ -139,7 +145,6 @@ const ConfigPage = () => {
交换机配置中心
-
选择交换机设备
@@ -175,21 +180,21 @@ const ConfigPage = () => {
-
配置指令输入
-
+ {isPeizhi && parsedConfig && (
+
+
+ {(() => {
+ let parsed;
+ try {
+ parsed = JSON.parse(editableConfig);
+ } catch (e) {
+ return 配置 JSON 格式错误,无法解析;
+ }
- {hasParsed && (
-
-
-
- 生成配置:
-
-
+ ));
+ })()}
+
+ {
+
+
+
+
+ 应用配置命令
+
+
+ {JSON.parse(editableConfig).config?.commands.map((command, index) => (
+
+
+ {command}
+
+
+
+ {applyStatus[index] === 'success'
+ ? '成功'
+ : applyStatus[index] === 'failed'
+ ? '失败'
+ : applyStatus[index] === 'in-progress'
+ ? '正在应用'
+ : ''}
+
+
+ ))}
+
+
+
+
+ }
+
)}
diff --git a/src/frontend/src/pages/DevicesPage.jsx b/src/frontend/src/pages/DevicesPage.jsx
index 8a63112..ec3214b 100644
--- a/src/frontend/src/pages/DevicesPage.jsx
+++ b/src/frontend/src/pages/DevicesPage.jsx
@@ -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 (
@@ -124,6 +144,15 @@ const DevicesPage = () => {
{'端口: '}
{device.ports.join(', ')}
+
+
))}
@@ -134,6 +163,15 @@ const DevicesPage = () => {
+
+ {isModalOpen && currentDevice && (
+ setIsModalOpen(false)}
+ onSave={handleSaveDeviceConfig}
+ device={currentDevice}
+ />
+ )}
);
};
diff --git a/src/frontend/src/services/api/api.js b/src/frontend/src/services/api/api.js
index 9c9dd26..36b0a29 100644
--- a/src/frontend/src/services/api/api.js
+++ b/src/frontend/src/services/api/api.js
@@ -41,22 +41,16 @@ export const api = {
* @param text 文本
* @returns {Promise>}
*/
- //parseCommand: async (text) => await axios.post(buildUrl('/api/parse_command'), { command: text }),
- async parseCommand(text) {
- const res = await axios.post(buildUrl('/api/parse_command', { command: text }));
- if (res) {
- return res;
- } else return null;
- },
+ parseCommand: (text) => axios.post(buildUrl('/api/parse_command'), { command: text }),
/**
* 应用配置
* @param switch_ip 交换机ip
- * @param config 配置
+ * @param commands 配置,为数组[]
* @returns {Promise>}
*/
- applyConfig: (switch_ip, config) =>
- axios.post(buildUrl('/api/apply_config'), { switch_ip, config }),
+ applyConfig: (switch_ip, commands) =>
+ axios.post(buildUrl('/api/execute_cli_commands'), { switch_ip, commands }),
/**
* 更新基础URL