mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-12 03:09:42 +08:00
- 重构批量改单元格的状态流,减少高频交互时的无效重渲染 - 优化大数据量场景下的表格交互流畅度与响应延迟 - 调整单元格编辑细节,增强与 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 并禁用外层滚动 - 修复出现双滚动条的问题,确保仅保留弹窗内部滚动条
101 lines
1.9 KiB
Go
101 lines
1.9 KiB
Go
package app
|
||
|
||
import (
|
||
"strings"
|
||
"unicode"
|
||
"unicode/utf8"
|
||
|
||
"golang.org/x/text/encoding/simplifiedchinese"
|
||
"golang.org/x/text/transform"
|
||
)
|
||
|
||
func normalizeErrorMessage(err error) string {
|
||
if err == nil {
|
||
return ""
|
||
}
|
||
return normalizeMixedEncodingText(err.Error())
|
||
}
|
||
|
||
func normalizeMixedEncodingText(text string) string {
|
||
if text == "" {
|
||
return text
|
||
}
|
||
|
||
raw := []byte(text)
|
||
output := make([]byte, 0, len(raw)+16)
|
||
suspect := make([]byte, 0, 16)
|
||
|
||
flushSuspect := func() {
|
||
if len(suspect) == 0 {
|
||
return
|
||
}
|
||
|
||
fallback := strings.ToValidUTF8(string(suspect), "<22>")
|
||
decoded, _, err := transform.Bytes(simplifiedchinese.GB18030.NewDecoder(), suspect)
|
||
if err == nil && utf8.Valid(decoded) {
|
||
candidate := string(decoded)
|
||
if scoreDecodedText(candidate) > scoreDecodedText(fallback) {
|
||
output = append(output, []byte(candidate)...)
|
||
} else {
|
||
output = append(output, []byte(fallback)...)
|
||
}
|
||
} else {
|
||
output = append(output, []byte(fallback)...)
|
||
}
|
||
|
||
suspect = suspect[:0]
|
||
}
|
||
|
||
for len(raw) > 0 {
|
||
r, size := utf8.DecodeRune(raw)
|
||
if r == utf8.RuneError && size == 1 {
|
||
suspect = append(suspect, raw[0])
|
||
raw = raw[1:]
|
||
continue
|
||
}
|
||
|
||
if isLikelyMojibakeRune(r) {
|
||
suspect = append(suspect, raw[:size]...)
|
||
} else {
|
||
flushSuspect()
|
||
output = append(output, raw[:size]...)
|
||
}
|
||
raw = raw[size:]
|
||
}
|
||
|
||
flushSuspect()
|
||
return string(output)
|
||
}
|
||
|
||
func isLikelyMojibakeRune(r rune) bool {
|
||
if r == utf8.RuneError {
|
||
return true
|
||
}
|
||
if r >= 0x00C0 && r <= 0x02FF {
|
||
return true
|
||
}
|
||
if unicode.In(r, unicode.Hebrew, unicode.Arabic, unicode.Cyrillic, unicode.Greek) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
func scoreDecodedText(text string) int {
|
||
score := 0
|
||
for _, r := range text {
|
||
switch {
|
||
case r == '<27>':
|
||
score -= 6
|
||
case unicode.Is(unicode.Han, r):
|
||
score += 4
|
||
case isLikelyMojibakeRune(r):
|
||
score -= 3
|
||
case unicode.IsPrint(r):
|
||
score += 1
|
||
default:
|
||
score -= 2
|
||
}
|
||
}
|
||
return score
|
||
}
|