Files
MyGoNavi/frontend/wailsjs/go/models.ts
杨国锋 78e35a5be8 ️ perf(data-grid): 重构批量编辑链路并优化表格渲染性能
- 重构批量改单元格的状态流,减少高频交互时的无效重渲染
- 优化大数据量场景下的表格交互流畅度与响应延迟
- 调整单元格编辑细节,增强与 Navicat 编辑习惯的一致性

🔧 fix(sidebar-connection): 修复多数据源切换后旧连接节点无响应问题

- 修复新建并连接新数据源后,旧数据源点击无响应的问题

 feat(tab-manager): 表与设计标签支持环境前缀显示

- 基于连接名识别 DEV/UAT/PROD/SIT/STG/TEST 环境标记
- 仅对 table/design 标签添加环境前缀,查询等标签保持原样
- 无法识别标准环境时回退显示连接名,提升多环境可辨识性

 feat(connection-config): 新增连接URI复制解析并支持MySQL/Mongo主从配置

- 连接弹窗新增 URI 生成、解析、复制能力,支持参数回填
- MySQL 支持多地址主从拓扑、从库地址列表与从库独立凭据
- Mongo 支持多节点配置、replicaSet、authSource、readPreference
- 扩展前后端连接配置模型并同步 Wails 生成类型文件
- 后端接入主从凭据回退策略,保持旧配置兼容

 feat(mongodb-replica): 对齐Navicat主从配置并补齐成员发现能力

- 新增 mongoSrv、mongoAuthMechanism、savePassword 配置项
- 支持 mongodb+srv URI 构建与解析,并透传 authMechanism
- 新增 MongoDiscoverMembers 接口,返回成员与状态信息
- 驱动侧实现 replSetGetStatus -> hello/isMaster 回退发现链路
- 前端弹窗新增 SRV 开关、验证方式、成员发现按钮与状态表
- 增加 SRV+SSH 冲突提示与后端保护,避免无效连接路径

🔧 fix(app-error-text): 修复连接测试错误信息乱码并完善日志提示

- 新增错误文本编码纠正能力,处理混合编码导致的中文乱码
- 连接错误提示统一走 normalizeErrorMessage 输出
- 增加 GB18030 纠正相关单元测试覆盖 PostgreSQL 认证失败场景
- go.mod 显式引入 golang.org/x/text 依赖

 feat(filter-panel): 筛选条件支持启用停用与批量开关

- 筛选条件新增 enabled 状态,支持按条件勾选启用/停用
- 筛选面板新增“全启用”“全停用”快捷操作
- SQL 组装时自动跳过已停用条件,保留条件内容便于复用
- 同步 DataViewer 与 SQL 工具层类型,确保筛选链路一致性

🔧 fix(connection-modal-scroll): 修复连接弹窗滚动行为并去除外层滚动条

- 连接配置步骤设置弹窗 body 最大高度与内部滚动
- 为连接弹窗增加专用 wrapClassName 并禁用外层滚动
- 修复出现双滚动条的问题,确保仅保留弹窗内部滚动条
2026-02-09 21:54:11 +08:00

285 lines
8.2 KiB
TypeScript
Executable File

export namespace connection {
export class UpdateRow {
keys: Record<string, any>;
values: Record<string, any>;
static createFrom(source: any = {}) {
return new UpdateRow(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.keys = source["keys"];
this.values = source["values"];
}
}
export class ChangeSet {
inserts: any[];
updates: UpdateRow[];
deletes: any[];
static createFrom(source: any = {}) {
return new ChangeSet(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.inserts = source["inserts"];
this.updates = this.convertValues(source["updates"], UpdateRow);
this.deletes = source["deletes"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class SSHConfig {
host: string;
port: number;
user: string;
password: string;
keyPath: string;
static createFrom(source: any = {}) {
return new SSHConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.host = source["host"];
this.port = source["port"];
this.user = source["user"];
this.password = source["password"];
this.keyPath = source["keyPath"];
}
}
export class ConnectionConfig {
type: string;
host: string;
port: number;
user: string;
password: string;
savePassword?: boolean;
database: string;
useSSH: boolean;
ssh: SSHConfig;
driver?: string;
dsn?: string;
timeout?: number;
redisDB?: number;
uri?: string;
hosts?: string[];
topology?: string;
mysqlReplicaUser?: string;
mysqlReplicaPassword?: string;
replicaSet?: string;
authSource?: string;
readPreference?: string;
mongoSrv?: boolean;
mongoAuthMechanism?: string;
mongoReplicaUser?: string;
mongoReplicaPassword?: string;
static createFrom(source: any = {}) {
return new ConnectionConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.type = source["type"];
this.host = source["host"];
this.port = source["port"];
this.user = source["user"];
this.password = source["password"];
this.savePassword = source["savePassword"];
this.database = source["database"];
this.useSSH = source["useSSH"];
this.ssh = this.convertValues(source["ssh"], SSHConfig);
this.driver = source["driver"];
this.dsn = source["dsn"];
this.timeout = source["timeout"];
this.redisDB = source["redisDB"];
this.uri = source["uri"];
this.hosts = source["hosts"];
this.topology = source["topology"];
this.mysqlReplicaUser = source["mysqlReplicaUser"];
this.mysqlReplicaPassword = source["mysqlReplicaPassword"];
this.replicaSet = source["replicaSet"];
this.authSource = source["authSource"];
this.readPreference = source["readPreference"];
this.mongoSrv = source["mongoSrv"];
this.mongoAuthMechanism = source["mongoAuthMechanism"];
this.mongoReplicaUser = source["mongoReplicaUser"];
this.mongoReplicaPassword = source["mongoReplicaPassword"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class QueryResult {
success: boolean;
message: string;
data: any;
fields?: string[];
static createFrom(source: any = {}) {
return new QueryResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.success = source["success"];
this.message = source["message"];
this.data = source["data"];
this.fields = source["fields"];
}
}
}
export namespace redis {
export class ZSetMember {
member: string;
score: number;
static createFrom(source: any = {}) {
return new ZSetMember(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.member = source["member"];
this.score = source["score"];
}
}
}
export namespace sync {
export class TableOptions {
insert?: boolean;
update?: boolean;
delete?: boolean;
selectedInsertPks?: string[];
selectedUpdatePks?: string[];
selectedDeletePks?: string[];
static createFrom(source: any = {}) {
return new TableOptions(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.insert = source["insert"];
this.update = source["update"];
this.delete = source["delete"];
this.selectedInsertPks = source["selectedInsertPks"];
this.selectedUpdatePks = source["selectedUpdatePks"];
this.selectedDeletePks = source["selectedDeletePks"];
}
}
export class SyncConfig {
sourceConfig: connection.ConnectionConfig;
targetConfig: connection.ConnectionConfig;
tables: string[];
content?: string;
mode: string;
jobId?: string;
autoAddColumns?: boolean;
tableOptions?: Record<string, TableOptions>;
static createFrom(source: any = {}) {
return new SyncConfig(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.sourceConfig = this.convertValues(source["sourceConfig"], connection.ConnectionConfig);
this.targetConfig = this.convertValues(source["targetConfig"], connection.ConnectionConfig);
this.tables = source["tables"];
this.content = source["content"];
this.mode = source["mode"];
this.jobId = source["jobId"];
this.autoAddColumns = source["autoAddColumns"];
this.tableOptions = this.convertValues(source["tableOptions"], TableOptions, true);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class SyncResult {
success: boolean;
message: string;
logs: string[];
tablesSynced: number;
rowsInserted: number;
rowsUpdated: number;
rowsDeleted: number;
static createFrom(source: any = {}) {
return new SyncResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.success = source["success"];
this.message = source["message"];
this.logs = source["logs"];
this.tablesSynced = source["tablesSynced"];
this.rowsInserted = source["rowsInserted"];
this.rowsUpdated = source["rowsUpdated"];
this.rowsDeleted = source["rowsDeleted"];
}
}
}