♻️ refactor(全局): 统一错误消息中文化,补充 godoc 与测试,修复横向滚动和 Vite 代理

错误消息中文化:
- 19 个驱动实现文件中 connection not open / table name required 等英文消息替换为中文
- methods_file.go / methods_db.go / methods_driver.go 英文消息中文化
- 前端 App/Sidebar/DataGrid/ConnectionModal/DriverManagerModal 同步替换 "Cancelled" → "已取消"

文档与测试:
- database.go Database/BatchApplier 接口、types.go 12 个类型、driver_support.go 导出函数补充中文 godoc
- 新增 logger_test.go(ErrorChain 5 个用例)和 methods_db_conn_test.go(连接管理 7 个用例)

Bug 修复:
- DataGrid: 将 liveTargets 空检查移至非虚拟路径,修复外部横向滚动条拖动时内容不跟随
- vite.config.ts: server.host 指定 127.0.0.1,修复 IPv6 回环被拦截导致 Wails 代理 502

基础设施:
- .gitignore 新增 .gemini/ 规则,tmpclaude-* 改为 **/tmpclaude-* 覆盖子目录
This commit is contained in:
杨国锋
2026-03-17 21:44:50 +08:00
parent c62f4b7d3c
commit 064cdc34be
34 changed files with 478 additions and 267 deletions

View File

@@ -1 +1 @@
5b8157374dae5f9340e31b2d0bd2c00e
d0f9366af59a6367ad3c7e2d4185ead4

View File

@@ -953,7 +953,7 @@ function App() {
} catch (e) {
void message.error("解析 JSON 失败");
}
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("导入失败: " + res.message);
}
};
@@ -966,7 +966,7 @@ function App() {
const res = await (window as any).go.app.App.ExportData(connections, ['id','name','config','includeDatabases','includeRedisDatabases'], "connections", "json");
if (res.success) {
void message.success("导出成功");
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("导出失败: " + res.message);
}
};

View File

@@ -1013,7 +1013,7 @@ const ConnectionModal: React.FC<{
if (selectedPath) {
form.setFieldValue('sshKeyPath', selectedPath);
}
} else if (res?.message !== 'Cancelled') {
} else if (res?.message !== '已取消') {
message.error(`选择私钥文件失败: ${res?.message || '未知错误'}`);
}
} catch (e: any) {
@@ -1037,7 +1037,7 @@ const ConnectionModal: React.FC<{
if (selectedPath) {
form.setFieldValue('host', normalizeFileDbPath(selectedPath));
}
} else if (res?.message !== 'Cancelled') {
} else if (res?.message !== '已取消') {
message.error(`选择数据库文件失败: ${res?.message || '未知错误'}`);
}
} catch (e: any) {

View File

@@ -1086,7 +1086,7 @@ const DataGrid: React.FC<DataGridProps> = ({
const res = await ExportData(cleanRows, displayColumnNames, tableName || 'export', format);
if (res.success) {
void message.success("导出成功");
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("导出失败: " + res.message);
}
} catch (e: any) {
@@ -2829,7 +2829,7 @@ const DataGrid: React.FC<DataGridProps> = ({
const res = await ExportQuery(config as any, dbName || '', sql, defaultName || 'export', format);
if (res.success) {
void message.success("导出成功");
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("导出失败: " + res.message);
}
} catch (e: any) {
@@ -2947,7 +2947,7 @@ const DataGrid: React.FC<DataGridProps> = ({
const res = await ExportTable(config as any, dbName || '', tableName, format);
if (res.success) {
void message.success("导出成功");
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("导出失败: " + res.message);
}
} catch (e: any) {
@@ -3023,7 +3023,7 @@ const DataGrid: React.FC<DataGridProps> = ({
if (res.success && res.data && res.data.filePath) {
setImportFilePath(res.data.filePath);
setImportPreviewVisible(true);
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
void message.error("选择文件失败: " + res.message);
}
};
@@ -3371,11 +3371,6 @@ const DataGrid: React.FC<DataGridProps> = ({
return;
}
const liveTargets = tableScrollTargetsRef.current;
if (liveTargets.length === 0) {
return;
}
if (Math.abs(lastExternalScrollLeftRef.current - externalScroll.scrollLeft) < 1) {
return;
}
@@ -3383,6 +3378,9 @@ const DataGrid: React.FC<DataGridProps> = ({
horizontalSyncSourceRef.current = 'external';
const tableContainer = tableContainerRef.current;
// 虚拟表格路径:直接操作 DOM 的 marginLeft / scrollLeft不依赖 liveTargets。
// 将此分支提前到 liveTargets 检查之前,避免 targets 在数据加载时暂时为空
// 导致虚拟表格的横向滚动同步被永久阻塞。
if (enableVirtual && tableContainer instanceof HTMLElement) {
if (applyVirtualHorizontalOffset(tableContainer, externalScroll.scrollLeft)) {
lastTableScrollLeftRef.current = externalScroll.scrollLeft;
@@ -3390,6 +3388,12 @@ const DataGrid: React.FC<DataGridProps> = ({
horizontalSyncSourceRef.current = '';
return;
}
// 非虚拟表格路径:依赖 liveTargets 进行 scrollLeft 同步
const liveTargets = tableScrollTargetsRef.current;
if (liveTargets.length === 0) {
horizontalSyncSourceRef.current = '';
return;
}
liveTargets.forEach((target) => {
if (target.scrollWidth <= target.clientWidth + 1) {
return;

View File

@@ -847,7 +847,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
const installDriverFromLocalFile = useCallback(async (row: DriverStatusRow) => {
const fileRes = await SelectDriverPackageFile(downloadDir);
if (!fileRes?.success) {
if (String(fileRes?.message || '') !== 'Cancelled') {
if (String(fileRes?.message || '') !== '已取消') {
message.error(fileRes?.message || '选择本地驱动包文件失败');
}
return;
@@ -863,7 +863,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG
const installDriversFromDirectory = useCallback(async () => {
const directoryRes = await SelectDriverPackageDirectory(downloadDir);
if (!directoryRes?.success) {
if (String(directoryRes?.message || '') !== 'Cancelled') {
if (String(directoryRes?.message || '') !== '已取消') {
message.error(directoryRes?.message || '选择本地驱动包目录失败');
}
return;

View File

@@ -1561,7 +1561,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
hide();
if (res.success) {
message.success('导出成功');
} else if (res.message !== 'Cancelled') {
} else if (res.message !== '已取消') {
message.error('导出失败: ' + res.message);
}
};
@@ -1584,7 +1584,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
hide();
if (res.success) {
message.success('导出成功');
} else if (res.message !== 'Cancelled') {
} else if (res.message !== '已取消') {
message.error('导出失败: ' + res.message);
}
} catch (e: any) {
@@ -1611,7 +1611,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
hide();
if (res.success) {
message.success('导出成功');
} else if (res.message !== 'Cancelled') {
} else if (res.message !== '已取消') {
message.error('导出失败: ' + res.message);
}
} catch (e: any) {
@@ -1800,7 +1800,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
} else {
message.success('导出成功');
}
} else if (res.message !== 'Cancelled') {
} else if (res.message !== '已取消') {
message.error('导出失败: ' + res.message);
}
} catch (e: any) {
@@ -1940,7 +1940,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
hide();
if (res.success) {
message.success(`${db.dbName} 导出成功`);
} else if (res.message !== 'Cancelled') {
} else if (res.message !== '已取消') {
message.error(`${db.dbName} 导出失败: ` + res.message);
break;
} else {
@@ -1981,7 +1981,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
dbName: dbName,
query: sqlContent
});
} else if (res.message !== "Cancelled") {
} else if (res.message !== "已取消") {
message.error("读取文件失败: " + res.message);
}
};

View File

@@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
host: '127.0.0.1',
port: 5173,
strictPort: true,
},