|
|
@@ -151,8 +151,8 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
151
|
151
|
ajaxInitialLoad: true,
|
|
152
|
152
|
ajaxContentType: 'json',
|
|
153
|
153
|
|
|
154
|
|
- // 设置为 false,强制 Tabulator 使用 ajaxRequestFunc
|
|
155
|
|
- ajaxURL: false,
|
|
|
154
|
+ // 设置为 api,强制 Tabulator 使用 ajaxRequestFunc
|
|
|
155
|
+ ajaxURL: '/api',
|
|
156
|
156
|
|
|
157
|
157
|
// 分页显示配置
|
|
158
|
158
|
paginationCounter: 'rows',
|
|
|
@@ -241,6 +241,20 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
241
|
241
|
finalPageSize: pageSize
|
|
242
|
242
|
});
|
|
243
|
243
|
|
|
|
244
|
+ // 安全提取字段名函数,处理列组件和其他对象类型
|
|
|
245
|
+ function safeGetField(fieldObj: any): string {
|
|
|
246
|
+ if (typeof fieldObj === 'string') return fieldObj;
|
|
|
247
|
+ if (fieldObj?.getField && typeof fieldObj.getField === 'function') return fieldObj.getField();
|
|
|
248
|
+ if (fieldObj?.field) return String(fieldObj.field);
|
|
|
249
|
+ if (typeof fieldObj === 'object' && fieldObj !== null) {
|
|
|
250
|
+ try {
|
|
|
251
|
+ const str = String(fieldObj);
|
|
|
252
|
+ if (str !== '[object Object]') return str;
|
|
|
253
|
+ } catch (e) {}
|
|
|
254
|
+ }
|
|
|
255
|
+ return String(fieldObj);
|
|
|
256
|
+ }
|
|
|
257
|
+
|
|
244
|
258
|
// 安全字符串化函数,避免循环引用
|
|
245
|
259
|
function safeStringify(obj: any, space?: number): string {
|
|
246
|
260
|
const seen = new WeakSet();
|
|
|
@@ -250,6 +264,17 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
250
|
264
|
return '[Circular]';
|
|
251
|
265
|
}
|
|
252
|
266
|
seen.add(value);
|
|
|
267
|
+ // 特殊处理Tabulator列组件,避免调用toJSON
|
|
|
268
|
+ if (value.getField && typeof value.getField === 'function') {
|
|
|
269
|
+ return `[TabulatorColumn:${value.getField()}]`;
|
|
|
270
|
+ }
|
|
|
271
|
+ // 特殊处理其他常见Tabulator对象
|
|
|
272
|
+ if (value.constructor && value.constructor.name) {
|
|
|
273
|
+ const name = value.constructor.name;
|
|
|
274
|
+ if (name.includes('Column') || name.includes('Component')) {
|
|
|
275
|
+ return `[TabulatorObject:${name}]`;
|
|
|
276
|
+ }
|
|
|
277
|
+ }
|
|
253
|
278
|
}
|
|
254
|
279
|
return value;
|
|
255
|
280
|
}, space);
|
|
|
@@ -261,26 +286,21 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
261
|
286
|
return filters.map(f => {
|
|
262
|
287
|
// 提取基本属性,避免循环引用
|
|
263
|
288
|
const result: any = {};
|
|
264
|
|
- if (f.field !== undefined) result.field = f.field;
|
|
|
289
|
+ if (f.field !== undefined) result.field = safeGetField(f.field);
|
|
265
|
290
|
if (f.type !== undefined) result.type = f.type;
|
|
266
|
291
|
if (f.value !== undefined) {
|
|
267
|
|
- // 安全处理值,避免循环引用
|
|
|
292
|
+ // 安全处理值,避免循环引用和复杂对象
|
|
268
|
293
|
if (typeof f.value === 'object' && f.value !== null) {
|
|
269
|
294
|
try {
|
|
270
|
|
- // 尝试序列化并解析,去除循环引用
|
|
271
|
|
- const seen = new WeakSet();
|
|
272
|
|
- const safeValue = JSON.parse(JSON.stringify(f.value, (key, val) => {
|
|
273
|
|
- if (typeof val === 'object' && val !== null) {
|
|
274
|
|
- if (seen.has(val)) {
|
|
275
|
|
- return '[Circular]';
|
|
276
|
|
- }
|
|
277
|
|
- seen.add(val);
|
|
278
|
|
- }
|
|
279
|
|
- return val;
|
|
280
|
|
- }));
|
|
281
|
|
- result.value = safeValue;
|
|
|
295
|
+ // 使用safeStringify安全处理对象值
|
|
|
296
|
+ const safeValueStr = safeStringify(f.value);
|
|
|
297
|
+ if (safeValueStr.includes('[Circular]') || safeValueStr.includes('[TabulatorObject]')) {
|
|
|
298
|
+ result.value = '[Complex Object]';
|
|
|
299
|
+ } else {
|
|
|
300
|
+ result.value = JSON.parse(safeValueStr);
|
|
|
301
|
+ }
|
|
282
|
302
|
} catch (e) {
|
|
283
|
|
- console.warn('筛选值包含循环引用,使用简化值:', e);
|
|
|
303
|
+ console.warn('筛选值处理失败,使用简化值:', e);
|
|
284
|
304
|
result.value = '[Complex Object]';
|
|
285
|
305
|
}
|
|
286
|
306
|
} else {
|
|
|
@@ -298,27 +318,7 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
298
|
318
|
// 提取基本属性,避免循环引用
|
|
299
|
319
|
const result: any = {};
|
|
300
|
320
|
if (s.field !== undefined) {
|
|
301
|
|
- // 安全处理字段值
|
|
302
|
|
- if (typeof s.field === 'object' && s.field !== null) {
|
|
303
|
|
- try {
|
|
304
|
|
- const seen = new WeakSet();
|
|
305
|
|
- const safeField = JSON.parse(JSON.stringify(s.field, (key, val) => {
|
|
306
|
|
- if (typeof val === 'object' && val !== null) {
|
|
307
|
|
- if (seen.has(val)) {
|
|
308
|
|
- return '[Circular]';
|
|
309
|
|
- }
|
|
310
|
|
- seen.add(val);
|
|
311
|
|
- }
|
|
312
|
|
- return val;
|
|
313
|
|
- }));
|
|
314
|
|
- result.field = safeField;
|
|
315
|
|
- } catch (e) {
|
|
316
|
|
- console.warn('排序字段包含循环引用,使用简化值:', e);
|
|
317
|
|
- result.field = '[Complex Object]';
|
|
318
|
|
- }
|
|
319
|
|
- } else {
|
|
320
|
|
- result.field = s.field;
|
|
321
|
|
- }
|
|
|
321
|
+ result.field = safeGetField(s.field);
|
|
322
|
322
|
}
|
|
323
|
323
|
if (s.dir !== undefined) result.dir = s.dir;
|
|
324
|
324
|
return result;
|
|
|
@@ -349,14 +349,48 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
349
|
349
|
|
|
350
|
350
|
console.groupEnd();
|
|
351
|
351
|
|
|
|
352
|
+ // 最终验证:确保所有参数都是可序列化的基本类型
|
|
|
353
|
+ function ensureSerializable(obj: any): any {
|
|
|
354
|
+ if (obj === null || obj === undefined) return obj;
|
|
|
355
|
+ if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') return obj;
|
|
|
356
|
+ if (Array.isArray(obj)) {
|
|
|
357
|
+ return obj.map(item => ensureSerializable(item));
|
|
|
358
|
+ }
|
|
|
359
|
+ if (typeof obj === 'object') {
|
|
|
360
|
+ const result: any = {};
|
|
|
361
|
+ for (const key in obj) {
|
|
|
362
|
+ if (obj.hasOwnProperty(key)) {
|
|
|
363
|
+ result[key] = ensureSerializable(obj[key]);
|
|
|
364
|
+ }
|
|
|
365
|
+ }
|
|
|
366
|
+ return result;
|
|
|
367
|
+ }
|
|
|
368
|
+ // 其他类型转换为字符串
|
|
|
369
|
+ return String(obj);
|
|
|
370
|
+ }
|
|
|
371
|
+
|
|
352
|
372
|
const params = {
|
|
353
|
373
|
page,
|
|
354
|
374
|
size: pageSize,
|
|
355
|
|
- filter: filterData,
|
|
356
|
|
- sort: sortData
|
|
|
375
|
+ filter: ensureSerializable(filterData),
|
|
|
376
|
+ sort: ensureSerializable(sortData)
|
|
357
|
377
|
};
|
|
358
|
378
|
|
|
359
|
379
|
console.log(`🚀 TabulatorGrid[${componentId}] 返回参数:`, safeStringify(params, 2));
|
|
|
380
|
+
|
|
|
381
|
+ // 最终验证:确保参数可以安全序列化
|
|
|
382
|
+ try {
|
|
|
383
|
+ JSON.stringify(params);
|
|
|
384
|
+ } catch (e) {
|
|
|
385
|
+ console.error('❌ 参数序列化失败,使用安全备选值:', e);
|
|
|
386
|
+ return {
|
|
|
387
|
+ page,
|
|
|
388
|
+ size: pageSize,
|
|
|
389
|
+ filter: [],
|
|
|
390
|
+ sort: []
|
|
|
391
|
+ };
|
|
|
392
|
+ }
|
|
|
393
|
+
|
|
360
|
394
|
return params;
|
|
361
|
395
|
};
|
|
362
|
396
|
|
|
|
@@ -385,9 +419,9 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
385
|
419
|
|
|
386
|
420
|
// 合并用户自定义选项(优先级最高)
|
|
387
|
421
|
console.log('🔍 合并前配置:', {
|
|
388
|
|
- defaultAjaxURL: defaultOptions.ajaxURL,
|
|
389
|
|
- userOptions: this.options,
|
|
390
|
|
- userAjaxURL: this.options?.['ajaxURL']
|
|
|
422
|
+ defaultAjaxURL: typeof defaultOptions.ajaxURL,
|
|
|
423
|
+ userOptionsKeys: Object.keys(this.options || {}),
|
|
|
424
|
+ userAjaxURLType: typeof this.options?.['ajaxURL']
|
|
391
|
425
|
});
|
|
392
|
426
|
const finalOptions = { ...defaultOptions, ...this.options };
|
|
393
|
427
|
|
|
|
@@ -416,13 +450,33 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
416
|
450
|
this.cellClick.emit(cell.getData());
|
|
417
|
451
|
});
|
|
418
|
452
|
|
|
419
|
|
- // 调试事件:监听排序和筛选变化
|
|
|
453
|
+ // 调试事件:监听排序和筛选变化 - 使用安全日志记录避免序列化问题
|
|
|
454
|
+ const safeStringifyForLog = (obj: any): string => {
|
|
|
455
|
+ const seen = new WeakSet();
|
|
|
456
|
+ return JSON.stringify(obj, (key, value) => {
|
|
|
457
|
+ if (typeof value === 'object' && value !== null) {
|
|
|
458
|
+ if (seen.has(value)) return '[Circular]';
|
|
|
459
|
+ seen.add(value);
|
|
|
460
|
+ if (value.getField && typeof value.getField === 'function') {
|
|
|
461
|
+ return `[TabulatorColumn:${value.getField()}]`;
|
|
|
462
|
+ }
|
|
|
463
|
+ if (value.constructor && value.constructor.name) {
|
|
|
464
|
+ const name = value.constructor.name;
|
|
|
465
|
+ if (name.includes('Column') || name.includes('Component')) {
|
|
|
466
|
+ return `[TabulatorObject:${name}]`;
|
|
|
467
|
+ }
|
|
|
468
|
+ }
|
|
|
469
|
+ }
|
|
|
470
|
+ return value;
|
|
|
471
|
+ }, 2);
|
|
|
472
|
+ };
|
|
|
473
|
+
|
|
420
|
474
|
this.tabulator.on('dataSorting', (sorters: any) => {
|
|
421
|
|
- console.log(`🔄 TabulatorGrid[${this.containerId}] 排序变化:`, JSON.stringify(sorters, null, 2));
|
|
|
475
|
+ console.log(`🔄 TabulatorGrid[${this.containerId}] 排序变化:`, safeStringifyForLog(sorters));
|
|
422
|
476
|
});
|
|
423
|
477
|
|
|
424
|
478
|
this.tabulator.on('dataFiltering', (filters: any) => {
|
|
425
|
|
- console.log(`🔍 TabulatorGrid[${this.containerId}] 筛选变化:`, JSON.stringify(filters, null, 2));
|
|
|
479
|
+ console.log(`🔍 TabulatorGrid[${this.containerId}] 筛选变化:`, safeStringifyForLog(filters));
|
|
426
|
480
|
});
|
|
427
|
481
|
|
|
428
|
482
|
this.tabulator.on('dataFiltered', (filters: any, rows: any) => {
|
|
|
@@ -489,19 +543,30 @@ export class TabulatorGridComponent implements OnInit, AfterViewInit, OnDestroy
|
|
489
|
543
|
sortLength: Array.isArray(params?.sort) ? params.sort.length : 0
|
|
490
|
544
|
});
|
|
491
|
545
|
|
|
492
|
|
- // 安全输出参数,避免循环引用
|
|
493
|
|
- const safeStringify = (obj: any, space?: number): string => {
|
|
494
|
|
- const seen = new WeakSet();
|
|
495
|
|
- return JSON.stringify(obj, (key, value) => {
|
|
496
|
|
- if (typeof value === 'object' && value !== null) {
|
|
497
|
|
- if (seen.has(value)) {
|
|
498
|
|
- return '[Circular]';
|
|
499
|
|
- }
|
|
500
|
|
- seen.add(value);
|
|
501
|
|
- }
|
|
502
|
|
- return value;
|
|
503
|
|
- }, space);
|
|
504
|
|
- };
|
|
|
546
|
+ // 安全输出参数,避免循环引用
|
|
|
547
|
+ const safeStringify = (obj: any, space?: number): string => {
|
|
|
548
|
+ const seen = new WeakSet();
|
|
|
549
|
+ return JSON.stringify(obj, (key, value) => {
|
|
|
550
|
+ if (typeof value === 'object' && value !== null) {
|
|
|
551
|
+ if (seen.has(value)) {
|
|
|
552
|
+ return '[Circular]';
|
|
|
553
|
+ }
|
|
|
554
|
+ seen.add(value);
|
|
|
555
|
+ // 特殊处理Tabulator列组件,避免调用toJSON
|
|
|
556
|
+ if (value.getField && typeof value.getField === 'function') {
|
|
|
557
|
+ return `[TabulatorColumn:${value.getField()}]`;
|
|
|
558
|
+ }
|
|
|
559
|
+ // 特殊处理其他常见Tabulator对象
|
|
|
560
|
+ if (value.constructor && value.constructor.name) {
|
|
|
561
|
+ const name = value.constructor.name;
|
|
|
562
|
+ if (name.includes('Column') || name.includes('Component')) {
|
|
|
563
|
+ return `[TabulatorObject:${name}]`;
|
|
|
564
|
+ }
|
|
|
565
|
+ }
|
|
|
566
|
+ }
|
|
|
567
|
+ return value;
|
|
|
568
|
+ }, space);
|
|
|
569
|
+ };
|
|
505
|
570
|
|
|
506
|
571
|
console.log('📦 原始参数值 (安全显示):', safeStringify(params, 2));
|
|
507
|
572
|
|