diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index 52185d2..85aa4c6 100644 --- a/frontend/src/components/ConnectionModal.tsx +++ b/frontend/src/components/ConnectionModal.tsx @@ -1196,19 +1196,24 @@ const ConnectionModal: React.FC<{ ? await RedisConnect(config as any) : await TestConnection(config as any); - if (res.success) { - setTestResult({ type: 'success', message: res.message }); - if (isRedisType) { - setRedisDbList(Array.from({ length: 16 }, (_, i) => i)); - } else { - // Other databases: fetch database list - const dbRes = await DBGetDatabases(config as any); - if (dbRes.success) { - const dbs = (dbRes.data as any[]).map((row: any) => row.Database || row.database); - setDbList(dbs); - } - } - } else { + if (res.success) { + setTestResult({ type: 'success', message: res.message }); + if (isRedisType) { + setRedisDbList(Array.from({ length: 16 }, (_, i) => i)); + } else { + // Other databases: fetch database list + const dbRes = await DBGetDatabases(config as any); + if (dbRes.success) { + const dbRows = Array.isArray(dbRes.data) ? dbRes.data : []; + const dbs = dbRows + .map((row: any) => row?.Database || row?.database) + .filter((name: any) => typeof name === 'string' && name.trim() !== ''); + setDbList(dbs); + } else { + setDbList([]); + } + } + } else { const failMessage = buildTestFailureMessage( res?.message, '连接被拒绝或参数无效,请检查后重试' diff --git a/frontend/src/components/DataSyncModal.tsx b/frontend/src/components/DataSyncModal.tsx index cfde4cb..57c4033 100644 --- a/frontend/src/components/DataSyncModal.tsx +++ b/frontend/src/components/DataSyncModal.tsx @@ -264,32 +264,38 @@ const DataSyncModal: React.FC<{ open: boolean; onClose: () => void }> = ({ open, setSourceConnId(connId); setSourceDb(''); const conn = connections.find(c => c.id === connId); - if (conn) { - setLoading(true); - try { - const res = await DBGetDatabases(normalizeConnConfig(conn) as any); - if (res.success) { - setSourceDbs((res.data as any[]).map((r: any) => r.Database || r.database || r.username)); - } - } catch(e) { message.error("Failed to fetch source databases"); } - setLoading(false); - } + if (conn) { + setLoading(true); + try { + const res = await DBGetDatabases(normalizeConnConfig(conn) as any); + if (res.success) { + const dbRows = Array.isArray(res.data) ? res.data : []; + setSourceDbs(dbRows + .map((r: any) => r?.Database || r?.database || r?.username) + .filter((name: any) => typeof name === 'string' && name.trim() !== '')); + } + } catch(e) { message.error("Failed to fetch source databases"); } + setLoading(false); + } }; const handleTargetConnChange = async (connId: string) => { setTargetConnId(connId); setTargetDb(''); const conn = connections.find(c => c.id === connId); - if (conn) { - setLoading(true); - try { - const res = await DBGetDatabases(normalizeConnConfig(conn) as any); - if (res.success) { - setTargetDbs((res.data as any[]).map((r: any) => r.Database || r.database || r.username)); - } - } catch(e) { message.error("Failed to fetch target databases"); } - setLoading(false); - } + if (conn) { + setLoading(true); + try { + const res = await DBGetDatabases(normalizeConnConfig(conn) as any); + if (res.success) { + const dbRows = Array.isArray(res.data) ? res.data : []; + setTargetDbs(dbRows + .map((r: any) => r?.Database || r?.database || r?.username) + .filter((name: any) => typeof name === 'string' && name.trim() !== '')); + } + } catch(e) { message.error("Failed to fetch target databases"); } + setLoading(false); + } }; const nextToTables = async () => { @@ -301,14 +307,17 @@ const DataSyncModal: React.FC<{ open: boolean; onClose: () => void }> = ({ open, try { const conn = connections.find(c => c.id === sourceConnId); if (conn) { - const config = normalizeConnConfig(conn, sourceDb); - const res = await DBGetTables(config as any, sourceDb); - if (res.success) { - // DBGetTables returns [{Table: "name"}, ...] - const tables = (res.data as any[]).map((row: any) => row.Table || row.table || row.TABLE_NAME || Object.values(row)[0]); - setAllTables(tables as string[]); - setCurrentStep(1); - } else { + const config = normalizeConnConfig(conn, sourceDb); + const res = await DBGetTables(config as any, sourceDb); + if (res.success) { + // DBGetTables returns [{Table: "name"}, ...] + const tableRows = Array.isArray(res.data) ? res.data : []; + const tables = tableRows + .map((row: any) => row?.Table || row?.table || row?.TABLE_NAME || Object.values(row || {})[0]) + .filter((name: any) => typeof name === 'string' && name.trim() !== ''); + setAllTables(tables as string[]); + setCurrentStep(1); + } else { message.error(res.message); } } diff --git a/internal/app/methods_db.go b/internal/app/methods_db.go index d1ef4a9..d8529a9 100644 --- a/internal/app/methods_db.go +++ b/internal/app/methods_db.go @@ -547,6 +547,13 @@ func sqlSnippet(query string) string { return q[:max] + "..." } +func ensureNonNilSlice[T any](items []T) []T { + if items == nil { + return make([]T, 0) + } + return items +} + func (a *App) DBGetDatabases(config connection.ConnectionConfig) connection.QueryResult { runConfig := normalizeRunConfig(config, "") dbInst, err := a.getDatabase(runConfig) @@ -571,7 +578,7 @@ func (a *App) DBGetDatabases(config connection.ConnectionConfig) connection.Quer return connection.QueryResult{Success: false, Message: err.Error()} } - var resData []map[string]string + resData := make([]map[string]string, 0, len(dbs)) for _, name := range dbs { resData = append(resData, map[string]string{"Database": name}) } @@ -604,7 +611,7 @@ func (a *App) DBGetTables(config connection.ConnectionConfig, dbName string) con return connection.QueryResult{Success: false, Message: err.Error()} } - var resData []map[string]string + resData := make([]map[string]string, 0, len(tables)) for _, name := range tables { resData = append(resData, map[string]string{"Table": name}) } @@ -786,7 +793,7 @@ func (a *App) DBGetColumns(config connection.ConnectionConfig, dbName string, ta return connection.QueryResult{Success: false, Message: err.Error()} } - return connection.QueryResult{Success: true, Data: columns} + return connection.QueryResult{Success: true, Data: ensureNonNilSlice(columns)} } func (a *App) DBGetIndexes(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { @@ -803,7 +810,7 @@ func (a *App) DBGetIndexes(config connection.ConnectionConfig, dbName string, ta return connection.QueryResult{Success: false, Message: err.Error()} } - return connection.QueryResult{Success: true, Data: indexes} + return connection.QueryResult{Success: true, Data: ensureNonNilSlice(indexes)} } func (a *App) DBGetForeignKeys(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { @@ -820,7 +827,7 @@ func (a *App) DBGetForeignKeys(config connection.ConnectionConfig, dbName string return connection.QueryResult{Success: false, Message: err.Error()} } - return connection.QueryResult{Success: true, Data: fks} + return connection.QueryResult{Success: true, Data: ensureNonNilSlice(fks)} } func (a *App) DBGetTriggers(config connection.ConnectionConfig, dbName string, tableName string) connection.QueryResult { @@ -837,7 +844,7 @@ func (a *App) DBGetTriggers(config connection.ConnectionConfig, dbName string, t return connection.QueryResult{Success: false, Message: err.Error()} } - return connection.QueryResult{Success: true, Data: triggers} + return connection.QueryResult{Success: true, Data: ensureNonNilSlice(triggers)} } func (a *App) DropView(config connection.ConnectionConfig, dbName string, viewName string) connection.QueryResult { @@ -975,5 +982,5 @@ func (a *App) DBGetAllColumns(config connection.ConnectionConfig, dbName string) return connection.QueryResult{Success: false, Message: err.Error()} } - return connection.QueryResult{Success: true, Data: cols} + return connection.QueryResult{Success: true, Data: ensureNonNilSlice(cols)} }