修配置页面

This commit is contained in:
Jerry 2025-07-10 22:18:57 +08:00
parent a57e4d4b46
commit c0bb0e6a29
2 changed files with 195 additions and 16 deletions

View File

@ -8,10 +8,12 @@ import {
HStack, HStack,
Portal, Portal,
Select, Select,
Spinner,
Text, Text,
Textarea, Textarea,
VStack, VStack,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import DocumentTitle from '@/components/system/pages/DocumentTitle'; import DocumentTitle from '@/components/system/pages/DocumentTitle';
import PageContainer from '@/components/system/PageContainer'; import PageContainer from '@/components/system/PageContainer';
import DashboardBackground from '@/components/system/pages/DashboardBackground'; import DashboardBackground from '@/components/system/pages/DashboardBackground';
@ -32,6 +34,10 @@ const ConfigPage = () => {
const [editableConfig, setEditableConfig] = useState(''); const [editableConfig, setEditableConfig] = useState('');
const [applying, setApplying] = useState(false); const [applying, setApplying] = useState(false);
const [hasParsed, setHasParsed] = 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([]);
const deviceCollection = createListCollection({ const deviceCollection = createListCollection({
items: devices.map((device) => ({ items: devices.map((device) => ({
@ -55,6 +61,13 @@ const ConfigPage = () => {
} }
try { try {
/**
const response = await api.parseCommand(inputText);
if (response) {
setParsedConfig(response);
setEditableConfig(response);
setHasParsed(true);
}**/
const performParse = async () => { const performParse = async () => {
if (testMode) { if (testMode) {
await Common.sleep(800 + Math.random() * 700); await Common.sleep(800 + Math.random() * 700);
@ -79,12 +92,16 @@ const ConfigPage = () => {
}, },
}); });
const result = await resultWrapper.unwrap(); let result = await resultWrapper.unwrap();
if (result?.data) {
if (result?.config) { setParsedConfig(JSON.stringify(result.data));
setParsedConfig(result.config); setEditableConfig(JSON.stringify(result.data));
setEditableConfig(result.config);
setHasParsed(true); setHasParsed(true);
result = result.data;
if (result.config && Array.isArray(result.config.commands)) {
setConmmand(result.config.commands);
}
setisPeizhi(true);
} }
} catch (error) { } catch (error) {
console.error('配置解析异常:', error); console.error('配置解析异常:', error);
@ -95,6 +112,37 @@ const ConfigPage = () => {
} }
}; };
const applyCommand = async (switch_ip, command, index) => {
try {
setApplyStatus((prevStatus) => {
const updated = [...prevStatus];
updated[index] = 'in-progress';
return updated;
});
const applyResult = testMode
? await Common.sleep(1000)
: await api.applyConfig(switch_ip, [command]);
if (applyResult?.data?.success) {
setApplyStatus((prevStatus) => {
const updated = [...prevStatus];
updated[index] = 'success';
return updated;
});
} else {
Notification.error({ title: '命令应用失败', description: '请检查命令是否合法' });
}
} catch (error) {
setApplyStatus((prevStatus) => {
const updated = [...prevStatus];
updated[index] = 'failed';
return updated;
});
throw error;
}
};
const handleApply = async () => { const handleApply = async () => {
if (!editableConfig) { if (!editableConfig) {
Notification.warn({ Notification.warn({
@ -108,7 +156,7 @@ const ConfigPage = () => {
try { try {
const applyOperation = testMode const applyOperation = testMode
? Common.sleep(1000).then(() => ({ success: true })) ? Common.sleep(1000).then(() => ({ success: true }))
: await api.applyConfig(selectedDevice, editableConfig); : await api.applyConfig(selectedDevice, conmmand);
await Notification.promise({ await Notification.promise({
promise: applyOperation, promise: applyOperation,
@ -250,6 +298,143 @@ const ConfigPage = () => {
</Box> </Box>
</FadeInWrapper> </FadeInWrapper>
)} )}
{isPeizhi && parsedConfig && (
<FadeInWrapper delay={0.2}>
<VStack spacing={4} align={'stretch'}>
{(() => {
let parsed;
try {
parsed = JSON.parse(editableConfig);
} catch (e) {
return <Text color={'red.300'}>配置 JSON 格式错误无法解析</Text>;
}
const config = parsed.config ? [parsed.config] : parsed;
return config.map((cfg, idx) => (
<Box
key={idx}
p={4}
bg={'whiteAlpha.100'}
borderRadius={'xl'}
border={'1px solid'}
borderColor={'whiteAlpha.300'}
>
<Text fontSize="lg" fontWeight="bold" mb={2}>
配置类型: {cfg.type}
</Text>
{Object.entries(cfg).map(([key, value]) => {
if (key === 'type' || key === 'commands') return null;
return (
<Field.Root
key={key}
colorPalette={'teal'}
orientation={'vertical'}
mb={3}
>
<Field.Label fontSize={'sm'}>{key}</Field.Label>
<Textarea
size={'sm'}
value={value}
onChange={(e) => {
const newVal = e.target.value;
const updated = JSON.parse(editableConfig);
updated.config[key] = newVal;
setEditableConfig(JSON.stringify(updated, null, 2));
}}
/>
<Field.HelperText>可编辑字段: {key}</Field.HelperText>
</Field.Root>
);
})}
<Text fontWeight={'semibold'} mt={3} mb={2}>
配置命令:
</Text>
{cfg.commands?.map((cmd, i) => (
<Field.Root key={i} colorPalette={'teal'} orientation={'vertical'} mb={2}>
<Field.Label fontSize="sm">命令 #{i + 1}</Field.Label>
<Textarea
size={'sm'}
fontFamily={'monospace'}
value={cmd}
onChange={(e) => {
const newCmd = e.target.value;
const updated = JSON.parse(editableConfig);
updated.config.commands[i] = newCmd;
setEditableConfig(JSON.stringify(updated, null, 2));
}}
/>
</Field.Root>
))}
<Button
size={'sm'}
mt={3}
colorScheme={'teal'}
onClick={() => {
Notification.success({
title: `配置 ${cfg.type} 已保存`,
description: '修改已同步至内存配置',
});
}}
>
保存当前配置
</Button>
</Box>
));
})()}
{isApplying && (
<FadeInWrapper delay={0.2}>
<VStack spacing={4} align={'stretch'}>
<Box
p={4}
bg={'whiteAlpha.100'}
borderRadius={'xl'}
border={'1px solid'}
borderColor={'whiteAlpha.300'}
>
<Text fontSize={'lg'} fontWeight={'bold'} mb={2}>
应用配置命令
</Text>
<Box>
{conmmand.map((command, index) => (
<HStack key={index} mb={2}>
<Text fontSize={'sm'} flex={1}>
{command}
</Text>
<Spinner
size={'sm'}
color={applyStatus[index] === 'success' ? 'green.500' : 'red.500'}
display={
applyStatus[index] === 'pending' ||
applyStatus[index] === 'in-progress'
? 'inline-block'
: 'none'
}
/>
<Text
color={applyStatus[index] === 'success' ? 'green.500' : 'red.500'}
ml={2}
>
{applyStatus[index] === 'success'
? '成功'
: applyStatus[index] === 'failed'
? '失败'
: applyStatus[index] === 'in-progress'
? '正在应用'
: ''}
</Text>
</HStack>
))}
</Box>
</Box>
</VStack>
</FadeInWrapper>
)}
</VStack>
</FadeInWrapper>
)}
</VStack> </VStack>
</FadeInWrapper> </FadeInWrapper>
</PageContainer> </PageContainer>

View File

@ -41,22 +41,16 @@ export const api = {
* @param text 文本 * @param text 文本
* @returns {Promise<axios.AxiosResponse<any>>} * @returns {Promise<axios.AxiosResponse<any>>}
*/ */
//parseCommand: async (text) => await axios.post(buildUrl('/api/parse_command'), { command: text }), parseCommand: (text) => 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;
},
/** /**
* 应用配置 * 应用配置
* @param switch_ip 交换机ip * @param switch_ip 交换机ip
* @param config 配置 * @param commands 配置,为数组[]
* @returns {Promise<axios.AxiosResponse<any>>} * @returns {Promise<axios.AxiosResponse<any>>}
*/ */
applyConfig: (switch_ip, config) => applyConfig: (switch_ip, commands) =>
axios.post(buildUrl('/api/apply_config'), { switch_ip, config }), axios.post(buildUrl('/api/execute_cli_commands'), { switch_ip, commands }),
/** /**
* 更新基础URL * 更新基础URL