瀏覽代碼

fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能

- 在interceptor.ts中添加updateAxiosBaseUrl函数,用于动态更新axios baseURL
- 在app.tsx中添加服务器连接检测进度覆盖层,提升用户体验
- 在ServerConfigModal.tsx中添加表单值变更时的实时配置更新
- 在ConfigService.ts中重构testConnection方法,采用连续检测10次并提供进度回调
- 在serverConfigSlice.ts中修改thunk以支持进度回调
- 新增serverConfigPing.ts模块,提供ping接口用于连接测试

禅道bug编号:209

改动文件:
- src/API/interceptor.ts
- src/app.tsx
- src/features/serverConfig/components/ServerConfigModal.tsx
- src/features/serverConfig/services/ConfigService.ts
- src/features/serverConfig/state/serverConfigSlice.ts
- src/API/serverConfigPing.ts (新增)
dengdx 3 天之前
父節點
當前提交
ad2af90675

+ 4 - 0
src/API/interceptor.ts

@@ -108,4 +108,8 @@ axiosInstance.interceptors.response.use(
   }
 );
 
+export function updateAxiosBaseUrl() {
+  axiosInstance.defaults.baseURL = getApiBaseUrl();
+}
+
 export default axiosInstance;

+ 21 - 0
src/API/serverConfigPing.ts

@@ -0,0 +1,21 @@
+import axiosInstance from './interceptor';
+
+export interface PingResponse {
+  code: string;
+  data: string;
+  description: string;
+  solution: string;
+}
+
+/**
+ * Ping 接口 - 测试服务器连接
+ * @param baseURL 服务器基础 URL,如 "http://192.168.1.100:8080/dr/api/v1"
+ * @returns Ping 响应
+ */
+export async function ping(): Promise<PingResponse> {
+  const response = await axiosInstance.get(`/pub/ping`, {
+    timeout: 2000, // 2秒超时
+  });
+
+  return response.data;
+}

+ 39 - 3
src/app.tsx

@@ -24,6 +24,7 @@ import { theme } from 'antd';
 import { getAntdLocale, setDayjsLocale } from './utils/localeHelper';
 import { setupGlobalErrorHandlers } from './utils/errorHandler';
 import ServerConfigModal from './features/serverConfig/components/ServerConfigModal';
+import { ServerConfig } from './features/serverConfig/types';
 console.log = logger.log;
 console.warn = logger.warn;
 console.error = logger.error;
@@ -60,6 +61,8 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
   const [isI18nReady, setIsI18nReady] = useState(false);
   const [showConfigModal, setShowConfigModal] = useState(false);
   const [connectionChecked, setConnectionChecked] = useState(false);
+  const [connectionCheckMessage, setConnectionCheckMessage] = useState('');
+  const [currentServerConfig, setCurrentServerConfig] = useState<ServerConfig | null>(null);
   const themeWithAlgorithm = {
     ...currentTheme,
     algorithm:
@@ -87,10 +90,13 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
     }
 
     // Electron/Cordova 环境:检查服务器连接
-    dispatch(checkServerConnection())
+    setConnectionCheckMessage('正在检测服务器连接...');
+    dispatch(checkServerConnection({ onProgress: handleConnectionProgress }))
       .unwrap()
       .then(async (result) => {
         setConnectionChecked(true);
+        setConnectionCheckMessage('');
+        setCurrentServerConfig(result.config);
 
         if (result.needsConfig) {
           // 需要配置,显示对话框
@@ -105,6 +111,8 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
       .catch((error) => {
         console.error('连接检查失败:', error);
         setConnectionChecked(true);
+        setConnectionCheckMessage('');
+        setCurrentServerConfig(null);
         setShowConfigModal(true);
       });
 
@@ -140,14 +148,20 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
     }
   };
 
+  // 进度更新函数
+  const handleConnectionProgress = (progress: { current: number; total: number }) => {
+    setConnectionCheckMessage(`正在检测服务器连接... (${progress.current}/${progress.total})`);
+  };
+
   // 配置保存后的处理
   const handleConfigSaved = () => {
     setShowConfigModal(false);
     // 重新检查连接并初始化应用
-    dispatch(checkServerConnection())
+    dispatch(checkServerConnection({ onProgress: handleConnectionProgress }))
       .unwrap()
       .then((result) => {
         if (!result.needsConfig) {
+          setConnectionCheckMessage('');//通了,隐藏服务器连接检测覆盖层
           initializeApp();
         }
       })
@@ -177,7 +191,7 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
           }`}
         </style>
 
-        {/* 加载状态覆盖层 */}
+        {/* 多语言加载状态覆盖层 */}
         {(loading || (!isI18nReady && !connectionChecked)) && (
           <div
             style={{
@@ -198,11 +212,33 @@ function AppContent({ children }: { children: ReactNode }): JSX.Element {
           </div>
         )}
 
+        {/* 服务器连接检测覆盖层 */}
+        {connectionCheckMessage && (
+          <div
+            style={{
+              position: 'fixed',
+              top: 0,
+              left: 0,
+              right: 0,
+              bottom: 0,
+              zIndex: 10000, // 比多语言加载覆盖层更高
+              display: 'flex',
+              justifyContent: 'center',
+              alignItems: 'center',
+              backgroundColor: currentTheme.token.colorBgLayout,
+              color: currentTheme.token.colorText,
+            }}
+          >
+            <div>{connectionCheckMessage}</div>
+          </div>
+        )}
+
         {/* 服务器配置对话框 */}
         <ServerConfigModal
           open={showConfigModal}
           onSave={handleConfigSaved}
           onCancel={() => setShowConfigModal(false)}
+          initialConfig={currentServerConfig}
         />
 
         {/* 错误状态覆盖层 */}

+ 24 - 7
src/features/serverConfig/components/ServerConfigModal.tsx

@@ -5,10 +5,12 @@ import {
   testConnectionThunk,
   saveAndApplyConfig,
   setTestResult,
-  clearTestResult
+  clearTestResult,
+  setServerConfig
 } from '../state/serverConfigSlice';
 import { ServerConfig } from '../types';
 import { DEFAULT_SERVER_CONFIG } from '../services/ConfigService';
+import {updateAxiosBaseUrl} from '@/API/interceptor';
 
 interface ServerConfigModalProps {
   open: boolean;
@@ -45,17 +47,32 @@ const ServerConfigModal: React.FC<ServerConfigModalProps> = ({
     }
   }, [open, initialConfig, form]);
 
+  // 表单变更时实时更新 Redux 状态
+  const handleFormValuesChange = () => {
+    const values = form.getFieldsValue();
+    // 更新拦截器的 API 基础地址
+    updateAxiosBaseUrl();
+    // 只有当所有必需字段都有值时才更新
+    if (values.ip && values.apiPort && values.mqttPort) {
+      const config: ServerConfig = {
+        ip: values.ip,
+        apiPort: parseInt(values.apiPort),
+        mqttPort: parseInt(values.mqttPort),
+      };
+
+      // 实时更新 Redux 状态
+      dispatch(setServerConfig(config));
+    }
+  };
+
   // 测试连接
   const handleTest = async () => {
     try {
-      const values = await form.validateFields();
+
       dispatch(clearTestResult());
 
       // 传递 IP 和端口
-      const result = await dispatch(testConnectionThunk({ 
-        ip: values.ip, 
-        apiPort: parseInt(values.apiPort) 
-      })).unwrap();
+      const result = await dispatch(testConnectionThunk({ })).unwrap();
       dispatch(setTestResult(result));
     } catch (error) {
       // 表单验证失败
@@ -148,7 +165,7 @@ const ServerConfigModal: React.FC<ServerConfigModalProps> = ({
         style={{ marginBottom: 24 }}
       />
 
-      <Form form={form} layout="vertical">
+      <Form form={form} layout="vertical" onValuesChange={handleFormValuesChange}>
         <Form.Item
           label="服务器 IP 地址"
           name="ip"

+ 35 - 38
src/features/serverConfig/services/ConfigService.ts

@@ -1,6 +1,6 @@
 import { ServerConfig, ConnectionTestResult, ConfigValidation } from '../types';
 import { createStorageAdapter } from '../storage';
-import axios from 'axios';
+import { ping } from '../../../API/serverConfigPing';
 
 /**
  * 默认服务器配置
@@ -123,48 +123,45 @@ export class ConfigService {
   }
 
   /**
-   * 测试连接
+   * 测试连接 - 连续检测 10 次,每隔 1 秒
    */
-  async testConnection(ip: string, apiPort: number): Promise<ConnectionTestResult> {
-    try {
-      const testInstance = axios.create({
-        baseURL: `http://${ip}:${apiPort}/dr/api/v1`,
-        timeout: 10000,
-      });
+  async testConnection(
+    onProgress?: (progress: { current: number; total: number }) => void
+  ): Promise<ConnectionTestResult> {
 
-      const response = await testInstance.get('/pub/software_info');
-      
-      if (response.data?.code === '0x000000') {
-        return {
-          success: true,
-          message: '连接成功',
-          serverInfo: {
-            version: response.data.data.server?.version || 'unknown',
-            product: response.data.data.product || 'unknown',
-          },
-        };
-      } else {
-        return {
-          success: false,
-          message: '服务器响应异常: ' + response.data?.description,
-        };
+    // 连续检测 10 次
+    for (let i = 0; i < 10; i++) {
+      // 更新进度
+      onProgress?.({ current: i + 1, total: 10 });
+
+      try {
+        console.log(`检测服务器连接 (${i + 1}/10)...`);
+        const response = await ping();
+
+        // 检查响应
+        if (response.code === '0x000000' && response.data === 'pong') {
+          return {
+            success: true,
+            message: `连接成功 (第 ${i + 1} 次检测)`,
+          };
+        } else {
+          console.warn(`检测失败 (${i + 1}/10): 服务器响应异常 - ${response.description}`);
+        }
+      } catch (error: any) {
+        console.warn(`检测失败 (${i + 1}/10):`, error.message);
       }
-    } catch (error: any) {
-      let message = '连接失败';
-      
-      if (error.code === 'ECONNABORTED') {
-        message = '连接超时,请检查服务器地址';
-      } else if (error.code === 'ERR_NETWORK') {
-        message = '网络错误,请检查网络连接';
-      } else if (error.message) {
-        message = error.message;
+
+      // 等待 1 秒后进行下一次检测(最后一次不需要等待)
+      if (i < 9) {
+        await new Promise(resolve => setTimeout(resolve, 1000));
       }
-      
-      return {
-        success: false,
-        message,
-      };
     }
+
+    // 所有检测都失败
+    return {
+      success: false,
+      message: '连接失败,所有 10 次检测均未成功',
+    };
   }
 
   /**

+ 8 - 7
src/features/serverConfig/state/serverConfigSlice.ts

@@ -62,8 +62,8 @@ export const initializeServerConfig = createAsyncThunk(
  */
 export const testConnectionThunk = createAsyncThunk(
   'serverConfig/testConnection',
-  async ({ ip, apiPort }: { ip: string; apiPort: number }) => {
-    const result = await ConfigService.testConnection(ip, apiPort);
+  async ({  onProgress }: { onProgress?: (progress: { current: number; total: number }) => void }) => {
+    const result = await ConfigService.testConnection( onProgress);
     return result;
   }
 );
@@ -74,11 +74,11 @@ export const testConnectionThunk = createAsyncThunk(
  */
 export const checkServerConnection = createAsyncThunk(
   'serverConfig/checkConnection',
-  async (_, { dispatch }) => {
+  async ({ onProgress }: { onProgress?: (progress: { current: number; total: number }) => void } = {}, { dispatch }) => {
     try {
       // 1. 加载本地配置
       const config = await ConfigService.getServerConfig();
-
+      console.log('检查服务器连接,加载到的配置:', config);
       // // 2. 如果是默认配置(未配置过),需要配置
       // const defaultConfig = ConfigService.getDefaultConfig();
       // const isDefaultConfig = !config ||
@@ -87,13 +87,14 @@ export const checkServerConnection = createAsyncThunk(
       // if (isDefaultConfig) {
       //   return { needsConfig: true, config: null };
       // }
+      //从配置中得到的网络信息先设置到 slice 中
+      dispatch(setServerConfig(config));
 
       // 3. 测试连接
-      const testResult = await ConfigService.testConnection(config.ip, config.apiPort);
+      const testResult = await ConfigService.testConnection( onProgress);
 
       if (testResult.success) {
-        // 连接成功,更新 Redux 状态
-        dispatch(setServerConfig(config));
+        
         return { needsConfig: false, config };
       } else {
         // 连接失败,需要重新配置