mirror of
https://github.com/Jerryplusy/AI-powered-switches.git
synced 2025-07-04 05:09:19 +00:00
优化api模块
This commit is contained in:
parent
921b17e3f5
commit
d6787a11dd
@ -18,12 +18,9 @@ import {
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { IoClose } from 'react-icons/io5';
|
||||
import { FiCheck } from 'react-icons/fi';
|
||||
import ConfigTool, { defaultConfig } from '@/libs/config/ConfigTool';
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
const defaultConfig = {
|
||||
backendUrl: '',
|
||||
authKey: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* 连接地址配置组件
|
||||
@ -39,10 +36,7 @@ const ConnectionConfigModal = ({ isOpen, onClose, onSave }) => {
|
||||
const backendRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const savedData = localStorage.getItem('connection-config');
|
||||
if (savedData) {
|
||||
setConfig(JSON.parse(savedData));
|
||||
}
|
||||
setConfig(ConfigTool.load());
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
@ -51,7 +45,7 @@ const ConnectionConfigModal = ({ isOpen, onClose, onSave }) => {
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
localStorage.setItem('connection-config', JSON.stringify(config));
|
||||
ConfigTool.save(config);
|
||||
onSave(config);
|
||||
setSaved(true);
|
||||
setTimeout(() => {
|
||||
@ -61,8 +55,9 @@ const ConnectionConfigModal = ({ isOpen, onClose, onSave }) => {
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
localStorage.removeItem('connection-config');
|
||||
ConfigTool.clear();
|
||||
setConfig(defaultConfig);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
@ -155,8 +150,8 @@ const ConnectionConfigModal = ({ isOpen, onClose, onSave }) => {
|
||||
color={'white'}
|
||||
onClick={handleSave}
|
||||
isDisabled={saved}
|
||||
position={'relative'}
|
||||
width={'80px'}
|
||||
position={'relative'}
|
||||
_hover={{ bg: 'rgba(0, 0, 255, 0.3)' }}
|
||||
>
|
||||
<AnimatePresence initial={false} mode={'wait'}>
|
||||
|
@ -4,6 +4,7 @@ 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';
|
||||
import ConfigTool from '@/libs/config/ConfigTool';
|
||||
|
||||
/**
|
||||
* 欢迎页面内容
|
||||
@ -16,7 +17,7 @@ const WelcomeContent = () => {
|
||||
//const [showAlert, setShowAlert] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem('connection-config');
|
||||
const saved = ConfigTool.load();
|
||||
if (saved) {
|
||||
setIsConfigured(true);
|
||||
}
|
||||
|
85
src/frontend/src/libs/config/ConfigTool.js
Normal file
85
src/frontend/src/libs/config/ConfigTool.js
Normal file
@ -0,0 +1,85 @@
|
||||
const CONFIG_KEY = 'app_config';
|
||||
|
||||
export const defaultConfig = {
|
||||
backendUrl: '',
|
||||
authKey: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* 浏览器环境配置操作工具
|
||||
*/
|
||||
const ConfigTool = {
|
||||
/**
|
||||
* 从本地存储读取配置
|
||||
* @returns {{backendUrl: string, authKey: string}}
|
||||
*/
|
||||
load: () => {
|
||||
try {
|
||||
const stored = localStorage.getItem(CONFIG_KEY);
|
||||
if (stored) {
|
||||
return { ...defaultConfig, ...JSON.parse(stored) };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('读取配置失败:', e);
|
||||
localStorage.removeItem(CONFIG_KEY);
|
||||
}
|
||||
return { ...defaultConfig };
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存配置到本地存储
|
||||
* @param {Object} config - 要保存的配置对象
|
||||
* @returns {boolean} 是否保存成功
|
||||
*/
|
||||
save: (config) => {
|
||||
try {
|
||||
localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
|
||||
//console.log(`正在保存配置:${JSON.stringify(config)}`);
|
||||
//console.log(`测试读取配置:${JSON.stringify(ConfigTool.load())}`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('保存配置失败:', e);
|
||||
if (e.name === 'QuotaExceededError') {
|
||||
alert('存储空间不足,请清理后重试');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除配置
|
||||
* @returns {boolean} 是否清除成功
|
||||
*/
|
||||
clear: () => {
|
||||
try {
|
||||
localStorage.removeItem(CONFIG_KEY);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('清除配置失败:', e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取存储标识(浏览器环境返回固定值)
|
||||
* @returns {string}
|
||||
*/
|
||||
getConfigPath: () => `localStorage:${CONFIG_KEY}`,
|
||||
|
||||
/**
|
||||
* 检查存储是否可用
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStorageAvailable: () => {
|
||||
try {
|
||||
const testKey = 'test';
|
||||
localStorage.setItem(testKey, testKey);
|
||||
localStorage.removeItem(testKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default ConfigTool;
|
@ -1,5 +1,5 @@
|
||||
import React, { createContext, useContext, useState, useCallback } from 'react';
|
||||
import { Box, Alert, Button, Icon, Text } from '@chakra-ui/react';
|
||||
import { Box, Button, Icon, Text } from '@chakra-ui/react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { AiOutlineInfoCircle, AiFillWarning } from 'react-icons/ai';
|
||||
|
||||
|
@ -15,7 +15,7 @@ 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 { api } from '@/services/api/api';
|
||||
import { useNotification } from '@/libs/system/Notification';
|
||||
|
||||
/**
|
||||
@ -32,18 +32,10 @@ const ScanPage = () => {
|
||||
|
||||
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: '获取后端信息失败' });
|
||||
setLocalIp('172.17.99.208');
|
||||
if (!subnet) {
|
||||
const ipParts = '172.17.99.208'.split('.');
|
||||
setSubnet(`${ipParts[0]}.${ipParts[1]}.${ipParts[2]}.0/24`);
|
||||
}
|
||||
};
|
||||
fetchLocalInfo();
|
||||
|
@ -1,51 +0,0 @@
|
||||
/**
|
||||
* 获取config
|
||||
* @returns {any|null}
|
||||
*/
|
||||
export const getConfig = () => {
|
||||
const cfg = localStorage.getItem('connection-config');
|
||||
return cfg ? JSON.parse(cfg) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取后端url
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getBaseUrl = () => {
|
||||
const cfg = getConfig();
|
||||
return cfg?.backendUrl || '';
|
||||
};
|
||||
|
||||
/**
|
||||
* fetchUrl
|
||||
* @param path 路径
|
||||
* @param options 选项
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
const fetchWithBase = async (path, options = {}) => {
|
||||
const base = getBaseUrl();
|
||||
const res = await fetch(`${base}${path}`, options);
|
||||
return res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* api模块
|
||||
* @type {{test: (function(): Promise<*>), scan: (function(*): Promise<*>), listDevices: (function(): Promise<*>), parseCommand: (function(*): Promise<*>), applyConfig: (function(*, *): Promise<*>)}}
|
||||
*/
|
||||
export const api = {
|
||||
test: () => fetchWithBase('/api/test'),
|
||||
scan: (subnet) => fetchWithBase(`/api/scan_network?subnet=${encodeURIComponent(subnet)}`),
|
||||
listDevices: () => fetchWithBase('/api/list_devices'),
|
||||
parseCommand: (text) =>
|
||||
fetchWithBase('/api/parse_command', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ command: text }),
|
||||
}),
|
||||
applyConfig: (switch_ip, config) =>
|
||||
fetchWithBase('/api/apply_config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ switch_ip, config }),
|
||||
}),
|
||||
};
|
100
src/frontend/src/services/api/api.js
Normal file
100
src/frontend/src/services/api/api.js
Normal file
@ -0,0 +1,100 @@
|
||||
import axios from 'axios';
|
||||
import ConfigTool from '@/libs/config/ConfigTool';
|
||||
|
||||
/**
|
||||
* 创建带基础URL的axios实例
|
||||
*/
|
||||
const apiClient = axios.create({
|
||||
baseURL: ConfigTool.load().backendUrl || '',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
const cfg = ConfigTool.load();
|
||||
if (cfg?.authKey) {
|
||||
config.headers['Authorization'] = `Bearer ${cfg.authKey}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response.data,
|
||||
(error) => {
|
||||
console.error('API请求错误:', error);
|
||||
return Promise.reject(error.response?.data || error.message);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* API模块
|
||||
*/
|
||||
export const api = {
|
||||
/**
|
||||
* 测试API连接
|
||||
* @returns {Promise<axios.AxiosResponse<any>>}
|
||||
*/
|
||||
test: () => apiClient.get('/api/test'),
|
||||
|
||||
/**
|
||||
* 扫描网络
|
||||
* @param subnet 子网地址
|
||||
* @returns {Promise<axios.AxiosResponse<any>>}
|
||||
*/
|
||||
scan: (subnet) => apiClient.get(`/api/scan_network`, { params: { subnet } }),
|
||||
|
||||
/**
|
||||
* 列出所有设备
|
||||
* @returns {Promise<axios.AxiosResponse<any>>}
|
||||
*/
|
||||
listDevices: () => apiClient.get('/api/list_devices'),
|
||||
|
||||
/**
|
||||
* 解析命令
|
||||
* @param text 文本
|
||||
* @returns {Promise<axios.AxiosResponse<any>>}
|
||||
*/
|
||||
parseCommand: (text) => apiClient.post('/api/parse_command', { command: text }),
|
||||
|
||||
/**
|
||||
* 应用配置
|
||||
* @param switch_ip 交换机ip
|
||||
* @param config 配置
|
||||
* @returns {Promise<axios.AxiosResponse<any>>}
|
||||
*/
|
||||
applyConfig: (switch_ip, config) => apiClient.post('/api/apply_config', { switch_ip, config }),
|
||||
|
||||
/**
|
||||
* 更新基础URL
|
||||
* @param url
|
||||
*/
|
||||
updateBaseUrl: (url) => {
|
||||
const config = ConfigTool.load();
|
||||
config.backendUrl = url;
|
||||
ConfigTool.save(config);
|
||||
apiClient.defaults.baseURL = url;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前配置
|
||||
* @returns {{backendUrl: string, authKey: string}}
|
||||
*/
|
||||
export const getConfig = () => ConfigTool.load();
|
||||
|
||||
/**
|
||||
* 获取基础URL
|
||||
* @returns {string|string}
|
||||
*/
|
||||
export const getBaseUrl = () => ConfigTool.load().backendUrl || '';
|
||||
|
||||
export default apiClient;
|
Loading…
x
Reference in New Issue
Block a user