fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178
This commit is contained in:
tianqijiuyun-latiao
2026-03-05 20:42:33 +08:00
parent 5c23722ad8
commit 6ae49d4b84
3 changed files with 71 additions and 1 deletions

View File

@@ -142,10 +142,19 @@ const formatCellValue = (val: any) => {
try {
if (val === null) return <span style={{ color: '#ccc' }}>NULL</span>;
if (typeof val === 'object') {
if (!Array.isArray(val) && !isPlainObject(val)) {
return String(val);
}
const cached = objectCellPreviewCache.get(val);
if (cached !== undefined) {
return cached;
}
const topLevelSize = Array.isArray(val) ? val.length : Object.keys(val || {}).length;
if (topLevelSize > 80) {
const summary = Array.isArray(val) ? `[Array(${topLevelSize})]` : `{Object(${topLevelSize})}`;
objectCellPreviewCache.set(val, summary);
return summary;
}
try {
const nextText = JSON.stringify(val);
const previewText = nextText.length > TABLE_CELL_PREVIEW_MAX_CHARS ? `${nextText.slice(0, TABLE_CELL_PREVIEW_MAX_CHARS)}` : nextText;
@@ -191,6 +200,26 @@ const isCellValueEqualForDiff = (left: any, right: any): boolean => {
return toFormText(left) === toFormText(right);
};
// 渲染阶段轻量比较:避免对象值在 shouldCellUpdate 中反复深度序列化导致卡顿。
const isCellValueEqualForRender = (left: any, right: any): boolean => {
if (left === right) return true;
const leftNullish = left === null || left === undefined;
const rightNullish = right === null || right === undefined;
if (leftNullish || rightNullish) return leftNullish && rightNullish;
const leftType = typeof left;
const rightType = typeof right;
if (leftType === 'object' || rightType === 'object') {
// 对象仅按引用比较;真正的值差异在提交保存时再做严格比对。
return false;
}
if (leftType === 'string' || rightType === 'string') {
return normalizeDateTimeString(String(left)) === normalizeDateTimeString(String(right));
}
return left === right;
};
const INLINE_EDIT_MAX_CHARS = 2000;
const shouldOpenModalEditor = (val: any): boolean => {
@@ -2067,7 +2096,7 @@ const DataGrid: React.FC<DataGridProps> = ({
shouldCellUpdate: (record: Item, prevRecord: Item) => {
const rowKeyChanged = record?.[GONAVI_ROW_KEY] !== prevRecord?.[GONAVI_ROW_KEY];
if (rowKeyChanged) return true;
return !isCellValueEqualForDiff(record?.[key], prevRecord?.[key]);
return !isCellValueEqualForRender(record?.[key], prevRecord?.[key]);
},
onHeaderCell: (column: any) => ({
width: column.width,

View File

@@ -8,6 +8,7 @@ import (
"reflect"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
@@ -86,6 +87,16 @@ func normalizeCompositeQueryValue(v interface{}) interface{} {
items[i] = normalizeQueryValue(rv.Index(i).Interface())
}
return items
case reflect.Struct:
// 部分驱动(如 Kingbase会返回复杂结构体值直接透传会导致前端渲染和比较开销激增。
// 统一降级为可读字符串,避免对象深层序列化触发 UI 卡顿。
if tm, ok := v.(time.Time); ok {
return tm.Format(time.RFC3339Nano)
}
if stringer, ok := v.(fmt.Stringer); ok {
return stringer.String()
}
return fmt.Sprintf("%v", v)
default:
return normalizeUnsafeIntegerForJS(rv, v)
}

View File

@@ -2,7 +2,9 @@ package db
import (
"encoding/json"
"fmt"
"testing"
"time"
)
type duckMapLike map[any]any
@@ -165,3 +167,31 @@ func TestNormalizeQueryValueWithDBType_JSONNumber(t *testing.T) {
})
}
}
type customStructValue struct {
Name string
Age int
}
func (v customStructValue) String() string {
return fmt.Sprintf("%s-%d", v.Name, v.Age)
}
func TestNormalizeQueryValueWithDBType_StructToString(t *testing.T) {
got := normalizeQueryValueWithDBType(customStructValue{Name: "alice", Age: 18}, "")
if got != "alice-18" {
t.Fatalf("结构体应降级为可读字符串,实际=%v(%T)", got, got)
}
}
func TestNormalizeQueryValueWithDBType_TimeStructToRFC3339(t *testing.T) {
input := time.Date(2026, 3, 5, 18, 30, 15, 123456789, time.UTC)
got := normalizeQueryValueWithDBType(input, "")
text, ok := got.(string)
if !ok {
t.Fatalf("time.Time 应转为字符串,实际=%v(%T)", got, got)
}
if text != "2026-03-05T18:30:15.123456789Z" {
t.Fatalf("time.Time 规整值异常,实际=%s", text)
}
}