@bit-sun/business-component 4.2.5-per-alpha.2 → 4.2.5-per-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.fatherrc.ts +4 -0
- package/dist/index.esm.js +827 -353
- package/dist/index.js +827 -353
- package/package.json +5 -4
- package/src/components/Business/BsLayouts/Components/CustomerMenu/globalMenu/DrawContent.tsx +75 -45
- package/src/components/Business/BsLayouts/Components/CustomerMenu/index.tsx +13 -10
- package/src/components/Business/BsLayouts/index.tsx +142 -27
- package/src/components/Business/BsSulaQueryTable/index.tsx +588 -161
- package/src/components/Business/BsSulaQueryTable/setting.tsx +34 -24
- package/src/components/Business/HomePageWrapper/index.tsx +43 -13
- package/src/components/Business/JsonQueryTable/configTree/index.js +29 -2
- package/src/components/Functional/QueryMutipleSelect/index.tsx +33 -4
|
@@ -54,15 +54,8 @@ const ResizeableTitle = (props: any) => {
|
|
|
54
54
|
|
|
55
55
|
const prevWidthRef = useRef(width);
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
markerPosition.current = { left: e.clientX, top: e.clientY }
|
|
60
|
-
setIsResizing(true);
|
|
61
|
-
document.addEventListener('mousemove', handleMouseMove);
|
|
62
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const handleMouseMove = (e: any) => {
|
|
57
|
+
// 使用useCallback优化事件处理函数,避免重复创建
|
|
58
|
+
const handleMouseMove = useCallback((e: any) => {
|
|
66
59
|
e.stopPropagation();
|
|
67
60
|
e.preventDefault();
|
|
68
61
|
// 更新标记位置
|
|
@@ -73,13 +66,21 @@ const ResizeableTitle = (props: any) => {
|
|
|
73
66
|
dom.style.left = `${e.clientX}px`;
|
|
74
67
|
dom.style.top = `${e.clientY - 20}px`;
|
|
75
68
|
}
|
|
76
|
-
};
|
|
69
|
+
}, []);
|
|
77
70
|
|
|
78
|
-
const handleMouseUp = (e: any) => {
|
|
71
|
+
const handleMouseUp = useCallback((e: any) => {
|
|
79
72
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
80
73
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
81
74
|
setIsResizing(false);
|
|
82
|
-
};
|
|
75
|
+
}, [handleMouseMove]);
|
|
76
|
+
|
|
77
|
+
const handleMouseDown = useCallback((e: any) => {
|
|
78
|
+
currentStart.current = e.clientX;
|
|
79
|
+
markerPosition.current = { left: e.clientX, top: e.clientY }
|
|
80
|
+
setIsResizing(true);
|
|
81
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
82
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
83
|
+
}, [handleMouseMove, handleMouseUp]);
|
|
83
84
|
|
|
84
85
|
const handleresize = (e: any, data: any, title: string) => {
|
|
85
86
|
const newWidth = data?.size?.width || 0;
|
|
@@ -104,6 +105,15 @@ const ResizeableTitle = (props: any) => {
|
|
|
104
105
|
}
|
|
105
106
|
}, [width]);
|
|
106
107
|
|
|
108
|
+
// 组件卸载时清理事件监听器
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
return () => {
|
|
111
|
+
// 确保在组件卸载时移除可能残留的事件监听器
|
|
112
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
113
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
114
|
+
};
|
|
115
|
+
}, [handleMouseMove, handleMouseUp]);
|
|
116
|
+
|
|
107
117
|
const thStyle = {
|
|
108
118
|
boxShadow: isResizing ? '2px 2px 10px rgba(0, 0, 0, 0.3)' : 'none',
|
|
109
119
|
};
|
|
@@ -173,12 +183,9 @@ export default (props: any) => {
|
|
|
173
183
|
// 定时器引用,用于清理
|
|
174
184
|
const debounceTimer = useRef(null);
|
|
175
185
|
const resizeTimer = useRef(null);
|
|
186
|
+
const tableHeightTimer = useRef(null); // 新增:管理getTableHeight的定时器
|
|
176
187
|
|
|
177
|
-
//
|
|
178
|
-
const handleBeforeUnload = useRef(null);
|
|
179
|
-
const handleGlobalClick = useRef(null);
|
|
180
|
-
const handleGlobalKeydown = useRef(null);
|
|
181
|
-
const handleStorageChange = useRef(null);
|
|
188
|
+
// 移除未使用的事件处理函数引用,避免内存泄漏
|
|
182
189
|
|
|
183
190
|
// 获取 table columns中所有的 key 防止有的地方是 dataindex
|
|
184
191
|
const checkedList = useMemo(
|
|
@@ -291,54 +298,73 @@ export default (props: any) => {
|
|
|
291
298
|
const [showExportColumn, setShowExportColumns] = useState([]); // 导出列字段
|
|
292
299
|
|
|
293
300
|
const [height, setHeight]: any = useState('');
|
|
301
|
+
// 使用ref避免setHeight的闭包引用
|
|
302
|
+
const heightRef = useRef(height);
|
|
303
|
+
|
|
304
|
+
// 同步height状态到ref
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
heightRef.current = height;
|
|
307
|
+
}, [height]);
|
|
294
308
|
|
|
295
309
|
const bsTableCodeExport = `${bsTableCode}___Export`; //设置导出列字段的唯一标识
|
|
296
310
|
|
|
297
311
|
// 获取table高度
|
|
298
|
-
const getTableHeight = () => {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
312
|
+
const getTableHeight = useCallback(() => {
|
|
313
|
+
// 清理之前的定时器
|
|
314
|
+
if (tableHeightTimer.current) {
|
|
315
|
+
clearTimeout(tableHeightTimer.current);
|
|
316
|
+
tableHeightTimer.current = null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
tableHeightTimer.current = setTimeout(() => {
|
|
320
|
+
try {
|
|
321
|
+
const cancelHeight = window.top == window ? 303 : 223;
|
|
322
|
+
const isFullScreen: any =
|
|
323
|
+
window.top.document.fullScreen ||
|
|
324
|
+
window.top.document.webkitIsFullScreen ||
|
|
325
|
+
window.top.document.mozFullScreen;
|
|
326
|
+
|
|
327
|
+
// wujie子应用iframe首次加载获取不到client以及dom元素高度兼容处理
|
|
328
|
+
let realIframeClientHeight = document.body.clientHeight
|
|
329
|
+
? document.body.clientHeight
|
|
330
|
+
: window.top?.document.body.clientHeight - 76;
|
|
331
|
+
let summaryHeight = document.querySelector(
|
|
332
|
+
`.ant-tabs-tabpane-active .table-bssula-summary`,
|
|
333
|
+
)
|
|
334
|
+
? document.querySelector(
|
|
335
|
+
`.ant-tabs-tabpane-active .table-bssula-summary`,
|
|
336
|
+
)?.clientHeight || 22
|
|
337
|
+
: 0;
|
|
338
|
+
let listTabHeight = document.querySelector(
|
|
339
|
+
`.ant-tabs-tabpane-active .list_top_tab .ant-tabs-nav`,
|
|
340
|
+
)
|
|
341
|
+
? document.querySelector(
|
|
342
|
+
`.ant-tabs-tabpane-active .list_top_tab .ant-tabs-nav`,
|
|
343
|
+
)?.clientHeight || 48
|
|
344
|
+
: 0;
|
|
345
|
+
|
|
346
|
+
const h =
|
|
347
|
+
realIframeClientHeight -
|
|
348
|
+
summaryHeight -
|
|
349
|
+
listTabHeight -
|
|
350
|
+
(document.querySelector(
|
|
351
|
+
`.ant-tabs-tabpane-active .ant-form ant-form-horizontal`,
|
|
352
|
+
)?.clientHeight || 0) -
|
|
353
|
+
(isFullScreen
|
|
354
|
+
? 0
|
|
355
|
+
: document.querySelector(
|
|
356
|
+
`.ant-tabs-tabpane-active .ant-pro-page-container-warp`,
|
|
357
|
+
)?.clientHeight || 0) -
|
|
358
|
+
cancelHeight +
|
|
359
|
+
'px';
|
|
360
|
+
setHeight(h);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.warn('Failed to calculate table height:', error);
|
|
363
|
+
} finally {
|
|
364
|
+
tableHeightTimer.current = null;
|
|
365
|
+
}
|
|
340
366
|
}, 0);
|
|
341
|
-
};
|
|
367
|
+
}, []);
|
|
342
368
|
|
|
343
369
|
//监测是否按下esc键
|
|
344
370
|
function checkFull() {
|
|
@@ -483,24 +509,15 @@ export default (props: any) => {
|
|
|
483
509
|
resizeTimer.current = null;
|
|
484
510
|
}
|
|
485
511
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (handleBeforeUnload.current) {
|
|
490
|
-
window.removeEventListener('beforeunload', handleBeforeUnload.current);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (handleGlobalClick.current) {
|
|
494
|
-
document.removeEventListener('click', handleGlobalClick.current);
|
|
512
|
+
if (tableHeightTimer.current) {
|
|
513
|
+
clearTimeout(tableHeightTimer.current);
|
|
514
|
+
tableHeightTimer.current = null;
|
|
495
515
|
}
|
|
496
516
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
517
|
+
// 清理所有事件监听器
|
|
518
|
+
window.removeEventListener('resize', handleWindowResize);
|
|
500
519
|
|
|
501
|
-
|
|
502
|
-
window.removeEventListener('storage', handleStorageChange.current);
|
|
503
|
-
}
|
|
520
|
+
// 移除未使用的事件监听器清理代码
|
|
504
521
|
|
|
505
522
|
// 优化的子组件卸载时序控制
|
|
506
523
|
const cleanupPromises: Promise<void>[] = [];
|
|
@@ -570,6 +587,10 @@ export default (props: any) => {
|
|
|
570
587
|
// 继续处理其他组件
|
|
571
588
|
}
|
|
572
589
|
});
|
|
590
|
+
|
|
591
|
+
// 清理临时数组和WeakSet
|
|
592
|
+
tempInstancesRef.current = [];
|
|
593
|
+
summaryInstancesRef.current = new WeakSet();
|
|
573
594
|
|
|
574
595
|
resolve();
|
|
575
596
|
} catch (error) {
|
|
@@ -586,21 +607,7 @@ export default (props: any) => {
|
|
|
586
607
|
// 清理全局事件监听器
|
|
587
608
|
window.removeEventListener('resize', handleWindowResize);
|
|
588
609
|
|
|
589
|
-
|
|
590
|
-
window.removeEventListener('beforeunload', handleBeforeUnload.current);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
if (handleGlobalClick.current) {
|
|
594
|
-
document.removeEventListener('click', handleGlobalClick.current);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (handleGlobalKeydown.current) {
|
|
598
|
-
document.removeEventListener('keydown', handleGlobalKeydown.current);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
if (handleStorageChange.current) {
|
|
602
|
-
window.removeEventListener('storage', handleStorageChange.current);
|
|
603
|
-
}
|
|
610
|
+
// 全局事件监听器已在上层清理
|
|
604
611
|
|
|
605
612
|
resolve();
|
|
606
613
|
} catch (error) {
|
|
@@ -633,6 +640,166 @@ export default (props: any) => {
|
|
|
633
640
|
console.error('[BsSulaQueryTable] 清理任务队列执行失败:', error);
|
|
634
641
|
});
|
|
635
642
|
|
|
643
|
+
// 设置卸载标志,防止后续创建新的Summary组件
|
|
644
|
+
isUnmountedRef.current = true;
|
|
645
|
+
|
|
646
|
+
// 清理Summary相关的DOM节点和React Fiber引用
|
|
647
|
+
try {
|
|
648
|
+
// 清理Summary组件实例
|
|
649
|
+
if (tempInstancesRef.current && tempInstancesRef.current.length > 0) {
|
|
650
|
+
console.log('[BsSulaQueryTable] 开始清理Summary组件实例:', tempInstancesRef.current.length);
|
|
651
|
+
|
|
652
|
+
tempInstancesRef.current.forEach((instance) => {
|
|
653
|
+
try {
|
|
654
|
+
// 首先调用实例的自定义清理方法
|
|
655
|
+
if (typeof instance._bsQueryTableCleanup === 'function') {
|
|
656
|
+
instance._bsQueryTableCleanup();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// 清理React Fiber节点引用
|
|
660
|
+
if (instance._reactInternalFiber) {
|
|
661
|
+
// 断开stateNode引用链
|
|
662
|
+
if (instance._reactInternalFiber.stateNode) {
|
|
663
|
+
instance._reactInternalFiber.stateNode = null;
|
|
664
|
+
}
|
|
665
|
+
// 断开child引用链
|
|
666
|
+
if (instance._reactInternalFiber.child) {
|
|
667
|
+
instance._reactInternalFiber.child = null;
|
|
668
|
+
}
|
|
669
|
+
// 断开memoizedProps引用链
|
|
670
|
+
if (instance._reactInternalFiber.memoizedProps) {
|
|
671
|
+
instance._reactInternalFiber.memoizedProps = null;
|
|
672
|
+
}
|
|
673
|
+
// 清理updateQueue
|
|
674
|
+
if (instance._reactInternalFiber.updateQueue) {
|
|
675
|
+
instance._reactInternalFiber.updateQueue = null;
|
|
676
|
+
}
|
|
677
|
+
// 清理lastEffect
|
|
678
|
+
if (instance._reactInternalFiber.lastEffect) {
|
|
679
|
+
instance._reactInternalFiber.lastEffect = null;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// 清理React 18+ Fiber节点引用
|
|
684
|
+
if (instance._reactInternals) {
|
|
685
|
+
if (instance._reactInternals.stateNode) {
|
|
686
|
+
instance._reactInternals.stateNode = null;
|
|
687
|
+
}
|
|
688
|
+
if (instance._reactInternals.child) {
|
|
689
|
+
instance._reactInternals.child = null;
|
|
690
|
+
}
|
|
691
|
+
if (instance._reactInternals.memoizedProps) {
|
|
692
|
+
instance._reactInternals.memoizedProps = null;
|
|
693
|
+
}
|
|
694
|
+
if (instance._reactInternals.updateQueue) {
|
|
695
|
+
instance._reactInternals.updateQueue = null;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// 清理React实例的内部引用
|
|
700
|
+
if (instance && typeof instance === 'object') {
|
|
701
|
+
// 清理可能的DOM引用
|
|
702
|
+
if (instance.current && typeof instance.current === 'object') {
|
|
703
|
+
const domNode = instance.current;
|
|
704
|
+
// 清理DOM节点上的React Fiber引用
|
|
705
|
+
const reactKeys = Object.keys(domNode).filter(key =>
|
|
706
|
+
key.startsWith('__reactFiber') ||
|
|
707
|
+
key.startsWith('__reactInternalInstance') ||
|
|
708
|
+
key.startsWith('__reactEventHandlers')
|
|
709
|
+
);
|
|
710
|
+
reactKeys.forEach(key => {
|
|
711
|
+
try {
|
|
712
|
+
delete domNode[key];
|
|
713
|
+
} catch (e) {
|
|
714
|
+
console.warn('[BsSulaQueryTable] 清理DOM React引用失败:', e);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
} catch (error) {
|
|
721
|
+
console.warn('[BsSulaQueryTable] 清理Summary实例失败:', error);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// DOM节点引用已简化,无需额外清理
|
|
728
|
+
|
|
729
|
+
// 清理context相关的闭包引用
|
|
730
|
+
try {
|
|
731
|
+
console.log('[BsSulaQueryTable] 开始清理context闭包引用');
|
|
732
|
+
|
|
733
|
+
// 清理getTableSummaryInfo函数中可能的context引用
|
|
734
|
+
if (typeof getTableSummaryInfo === 'function') {
|
|
735
|
+
// 尝试清理函数闭包中的context引用
|
|
736
|
+
Object.defineProperty(getTableSummaryInfo, 'context', {
|
|
737
|
+
value: null,
|
|
738
|
+
writable: true,
|
|
739
|
+
configurable: true
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// 清理可能的全局context引用
|
|
744
|
+
if (typeof window !== 'undefined') {
|
|
745
|
+
// 清理可能存在的全局context缓存
|
|
746
|
+
const contextKeys = Object.keys(window).filter(key =>
|
|
747
|
+
key.includes('context') ||
|
|
748
|
+
key.includes('Context') ||
|
|
749
|
+
key.includes('restProps') ||
|
|
750
|
+
key.includes('setPagePath')
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
contextKeys.forEach(key => {
|
|
754
|
+
try {
|
|
755
|
+
if (window[key] && typeof window[key] === 'object') {
|
|
756
|
+
// 清理context对象的引用
|
|
757
|
+
Object.keys(window[key]).forEach(contextKey => {
|
|
758
|
+
if (window[key][contextKey] && typeof window[key][contextKey] === 'object') {
|
|
759
|
+
window[key][contextKey] = null;
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
} catch (e) {
|
|
764
|
+
console.warn(`[BsSulaQueryTable] 清理全局context ${key} 失败:`, e);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// 清理组件内部可能的context引用
|
|
770
|
+
if (config && typeof config === 'object') {
|
|
771
|
+
// 清理config中可能的context引用
|
|
772
|
+
Object.keys(config).forEach(key => {
|
|
773
|
+
if (config[key] && typeof config[key] === 'object' && config[key].context) {
|
|
774
|
+
config[key].context = null;
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
} catch (contextError) {
|
|
780
|
+
console.warn('[BsSulaQueryTable] 清理context引用失败:', contextError);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
} catch (error) {
|
|
784
|
+
console.error('[BsSulaQueryTable] Summary清理过程中发生错误:', error);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// 清理memoConfig引用,断开与外部库的连接
|
|
788
|
+
if (memoConfigRef.current) {
|
|
789
|
+
console.log('[BsSulaQueryTable] 清理memoConfig引用');
|
|
790
|
+
// 将summaryList设置为null,通知外部库释放引用
|
|
791
|
+
if (memoConfigRef.current.summaryList) {
|
|
792
|
+
memoConfigRef.current.summaryList = null;
|
|
793
|
+
}
|
|
794
|
+
// 清理其他可能的函数引用
|
|
795
|
+
Object.keys(memoConfigRef.current).forEach(key => {
|
|
796
|
+
if (typeof memoConfigRef.current[key] === 'function') {
|
|
797
|
+
memoConfigRef.current[key] = null;
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
memoConfigRef.current = null;
|
|
801
|
+
}
|
|
802
|
+
|
|
636
803
|
// 清理所有ref引用
|
|
637
804
|
if (rowsRef.current) {
|
|
638
805
|
rowsRef.current = null;
|
|
@@ -650,10 +817,44 @@ export default (props: any) => {
|
|
|
650
817
|
exportTableRef.current = null;
|
|
651
818
|
}
|
|
652
819
|
|
|
820
|
+
// 强制断开与外部库的引用关系
|
|
821
|
+
try {
|
|
822
|
+
// 清理全局事件监听器
|
|
823
|
+
if (typeof window !== 'undefined') {
|
|
824
|
+
window.removeEventListener('resize', watchWinResize);
|
|
825
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// 清理可能的全局变量引用
|
|
829
|
+
if (typeof window !== 'undefined' && window.__bsSulaQueryTableInstances) {
|
|
830
|
+
const instances = window.__bsSulaQueryTableInstances;
|
|
831
|
+
const instanceIndex = instances.findIndex(instance => instance.id === componentId);
|
|
832
|
+
if (instanceIndex > -1) {
|
|
833
|
+
instances.splice(instanceIndex, 1);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 强制垃圾回收提示(仅开发环境)
|
|
838
|
+
if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && window.gc) {
|
|
839
|
+
setTimeout(() => {
|
|
840
|
+
try {
|
|
841
|
+
window.gc();
|
|
842
|
+
console.log('[BsSulaQueryTable] 手动触发垃圾回收');
|
|
843
|
+
} catch (e) {
|
|
844
|
+
// 忽略垃圾回收错误
|
|
845
|
+
}
|
|
846
|
+
}, 100);
|
|
847
|
+
}
|
|
848
|
+
} catch (error) {
|
|
849
|
+
console.warn('[BsSulaQueryTable] 外部库引用断开过程中出现错误:', error);
|
|
850
|
+
}
|
|
851
|
+
|
|
653
852
|
// 最终状态验证
|
|
654
853
|
console.log('[BsSulaQueryTable] 组件卸载流程完成,清理状态:', {
|
|
655
854
|
refsCleared: !rowsRef.current && !sortTableRef.current && !searchTableRef.current,
|
|
656
|
-
timersCleared: !debounceTimer.current && !resizeTimer.current
|
|
855
|
+
timersCleared: !debounceTimer.current && !resizeTimer.current,
|
|
856
|
+
memoConfigCleared: !memoConfigRef.current,
|
|
857
|
+
tempInstancesCleared: tempInstancesRef.current.length === 0
|
|
657
858
|
});
|
|
658
859
|
|
|
659
860
|
console.log('[BsSulaQueryTable] 主组件及所有子组件已完全卸载,内存已清理');
|
|
@@ -698,23 +899,34 @@ export default (props: any) => {
|
|
|
698
899
|
// 清理之前的定时器
|
|
699
900
|
if (debounceTimer.current) {
|
|
700
901
|
clearTimeout(debounceTimer.current);
|
|
902
|
+
debounceTimer.current = null;
|
|
701
903
|
}
|
|
702
904
|
|
|
703
905
|
// 设置新的定时器
|
|
704
906
|
debounceTimer.current = setTimeout(() => {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
// isFullScreen 为true 此时为全屏状态 false 为非全屏状态
|
|
710
|
-
if (!isFullScreen) {
|
|
711
|
-
// 按下esc键退出全屏
|
|
712
|
-
setIsFnllScreen(false);
|
|
713
|
-
} else {
|
|
714
|
-
setIsFnllScreen(false);
|
|
907
|
+
try {
|
|
908
|
+
// 检查组件是否已卸载
|
|
909
|
+
if (isUnmountedRef.current) {
|
|
910
|
+
return;
|
|
715
911
|
}
|
|
912
|
+
|
|
913
|
+
// getTableHeight();
|
|
914
|
+
if (!checkFull()) {
|
|
915
|
+
// addTabsNavStyle(true);
|
|
916
|
+
// 全屏下按键esc后要执行的动作
|
|
917
|
+
// isFullScreen 为true 此时为全屏状态 false 为非全屏状态
|
|
918
|
+
if (!isFullScreen) {
|
|
919
|
+
// 按下esc键退出全屏
|
|
920
|
+
setIsFnllScreen(false);
|
|
921
|
+
} else {
|
|
922
|
+
setIsFnllScreen(false);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
} catch (error) {
|
|
926
|
+
console.warn('Error in watchWinResize callback:', error);
|
|
927
|
+
} finally {
|
|
928
|
+
debounceTimer.current = null;
|
|
716
929
|
}
|
|
717
|
-
debounceTimer.current = null;
|
|
718
930
|
}, 10);
|
|
719
931
|
}, [isFullScreen]);
|
|
720
932
|
|
|
@@ -726,8 +938,8 @@ export default (props: any) => {
|
|
|
726
938
|
});
|
|
727
939
|
};
|
|
728
940
|
|
|
729
|
-
// 处理 table 基本参数
|
|
730
|
-
const setTableProps = () => {
|
|
941
|
+
// 处理 table 基本参数 - 使用useCallback避免闭包引用
|
|
942
|
+
const setTableProps = useCallback(() => {
|
|
731
943
|
const defaultPageSize = 20;
|
|
732
944
|
const baseTableProps = {
|
|
733
945
|
size: value.size || 'middle',
|
|
@@ -737,7 +949,7 @@ export default (props: any) => {
|
|
|
737
949
|
expandable: props.expandable,
|
|
738
950
|
scroll: {
|
|
739
951
|
x: props.overScrollX || 'max-content',
|
|
740
|
-
y: props?.overScrollY ||
|
|
952
|
+
y: props?.overScrollY || heightRef.current,
|
|
741
953
|
},
|
|
742
954
|
bordered: typeof value.bordered === 'boolean' ? value.bordered : true,
|
|
743
955
|
sticky: typeof value.sticky === 'boolean' ? value.sticky : true,
|
|
@@ -769,7 +981,7 @@ export default (props: any) => {
|
|
|
769
981
|
...value.tableProps?.initialPaging?.pagination,
|
|
770
982
|
};
|
|
771
983
|
|
|
772
|
-
const handleRowClick = (record) => {
|
|
984
|
+
const handleRowClick = useCallback((record) => {
|
|
773
985
|
const { rowKey } = value;
|
|
774
986
|
|
|
775
987
|
let newSelectedRowKeys = [...(rowsRef?.current?.selectedRowKeys || [])];
|
|
@@ -797,15 +1009,15 @@ export default (props: any) => {
|
|
|
797
1009
|
// 直接将 record 作为数组元素传递
|
|
798
1010
|
rowSelection.onChange(newSelectedRowKeys, newSelectedRows);
|
|
799
1011
|
}
|
|
800
|
-
};
|
|
1012
|
+
}, [value.rowKey, value.rowSelection]);
|
|
801
1013
|
|
|
802
|
-
const handleRowDoubleClick = (record) => {
|
|
1014
|
+
const handleRowDoubleClick = useCallback((record) => {
|
|
803
1015
|
console.log('handleRowDoubleClick', record);
|
|
804
1016
|
if (props.viewPagePath) {
|
|
805
1017
|
const path = eval(`\`${props.viewPagePath.replace(/'/g, '`').replace(/#{(.*?)}/g, (match, p) => `\${${p}}`)}\``);
|
|
806
1018
|
history.push(path);
|
|
807
1019
|
}
|
|
808
|
-
};
|
|
1020
|
+
}, [props.viewPagePath, history]);
|
|
809
1021
|
|
|
810
1022
|
const tableProps = {
|
|
811
1023
|
...baseTableProps,
|
|
@@ -827,7 +1039,7 @@ export default (props: any) => {
|
|
|
827
1039
|
tableProps.initialPaging.pagination.showSizeChanger = true;
|
|
828
1040
|
}
|
|
829
1041
|
return tableProps;
|
|
830
|
-
};
|
|
1042
|
+
}, [value, props, rowsRef, history]); // 不包含height依赖,使用heightRef.current
|
|
831
1043
|
|
|
832
1044
|
const ShowFullScreen = () => {
|
|
833
1045
|
const isFullScreen: any =
|
|
@@ -1072,14 +1284,87 @@ export default (props: any) => {
|
|
|
1072
1284
|
config.ref?.current?.tableRef?.current?.refreshTable();
|
|
1073
1285
|
}
|
|
1074
1286
|
}
|
|
1075
|
-
setTimeout(() => {
|
|
1076
|
-
|
|
1077
|
-
|
|
1287
|
+
const timeoutId = setTimeout(() => {
|
|
1288
|
+
try {
|
|
1289
|
+
// 检查组件是否已卸载
|
|
1290
|
+
if (isUnmountedRef.current) {
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
// 处理页面刷新两面
|
|
1294
|
+
localStorage.removeItem('isTabChange');
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
console.warn('Error in localStorage cleanup:', error);
|
|
1297
|
+
}
|
|
1078
1298
|
}, 0);
|
|
1299
|
+
|
|
1300
|
+
return () => {
|
|
1301
|
+
clearTimeout(timeoutId);
|
|
1302
|
+
};
|
|
1079
1303
|
}, [pathname]);
|
|
1304
|
+
|
|
1305
|
+
// 增强的外部库引用管理
|
|
1306
|
+
useEffect(() => {
|
|
1307
|
+
// 生成唯一组件ID
|
|
1308
|
+
const componentId = `bsSulaQueryTable_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1309
|
+
|
|
1310
|
+
// 注册到全局实例管理器(如果存在)
|
|
1311
|
+
if (typeof window !== 'undefined') {
|
|
1312
|
+
if (!window.__bsSulaQueryTableInstances) {
|
|
1313
|
+
window.__bsSulaQueryTableInstances = [];
|
|
1314
|
+
}
|
|
1315
|
+
window.__bsSulaQueryTableInstances.push({
|
|
1316
|
+
id: componentId,
|
|
1317
|
+
cleanup: () => {
|
|
1318
|
+
// 提供给外部库的清理接口
|
|
1319
|
+
isUnmountedRef.current = true;
|
|
1320
|
+
if (memoConfigRef.current) {
|
|
1321
|
+
memoConfigRef.current.summaryList = null;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
return () => {
|
|
1328
|
+
// 从全局实例管理器中移除
|
|
1329
|
+
if (typeof window !== 'undefined' && window.__bsSulaQueryTableInstances) {
|
|
1330
|
+
const instances = window.__bsSulaQueryTableInstances;
|
|
1331
|
+
const instanceIndex = instances.findIndex(instance => instance.id === componentId);
|
|
1332
|
+
if (instanceIndex > -1) {
|
|
1333
|
+
instances.splice(instanceIndex, 1);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
}, []);
|
|
1080
1338
|
|
|
1081
1339
|
const expandedRowKeys = props?.expandable?.expandedRowKeys;
|
|
1082
1340
|
|
|
1341
|
+
// 组件卸载状态跟踪
|
|
1342
|
+
const isUnmountedRef = useRef(false);
|
|
1343
|
+
// 使用WeakSet减少强引用,避免内存泄漏
|
|
1344
|
+
const summaryInstancesRef = useRef(new WeakSet());
|
|
1345
|
+
// 临时存储强引用用于清理,清理完成后立即清空
|
|
1346
|
+
const tempInstancesRef = useRef([]);
|
|
1347
|
+
// 存储memoConfig引用,用于卸载时清理
|
|
1348
|
+
const memoConfigRef = useRef(null);
|
|
1349
|
+
|
|
1350
|
+
// 使用ref存储状态,避免getTableSummaryInfo函数的闭包依赖
|
|
1351
|
+
const stateRef = useRef({
|
|
1352
|
+
summaryList: props.summaryList,
|
|
1353
|
+
rowSelection: props.rowSelection,
|
|
1354
|
+
expandable: props.expandable,
|
|
1355
|
+
showColumn: showColumn
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
// 更新stateRef
|
|
1359
|
+
useEffect(() => {
|
|
1360
|
+
stateRef.current = {
|
|
1361
|
+
summaryList: props.summaryList,
|
|
1362
|
+
rowSelection: props.rowSelection,
|
|
1363
|
+
expandable: props.expandable,
|
|
1364
|
+
showColumn: showColumn
|
|
1365
|
+
};
|
|
1366
|
+
}, [props.summaryList, props.rowSelection, props.expandable, showColumn]);
|
|
1367
|
+
|
|
1083
1368
|
//todo summary属性已经被使用,为了兼容之前的,现在使用 summaryList
|
|
1084
1369
|
//结构为了实现多行总结栏 定义如下(lableShow: boolean 是否显示列文本)
|
|
1085
1370
|
/**
|
|
@@ -1091,56 +1376,181 @@ export default (props: any) => {
|
|
|
1091
1376
|
* @returns []
|
|
1092
1377
|
*/
|
|
1093
1378
|
const getTableSummaryInfo = useCallback(() => {
|
|
1094
|
-
|
|
1379
|
+
// 检查组件是否已卸载,避免创建新的JSX元素
|
|
1380
|
+
if (isUnmountedRef.current) {
|
|
1381
|
+
console.warn('[BsSulaQueryTable] 组件已卸载,跳过Summary创建');
|
|
1382
|
+
return undefined;
|
|
1383
|
+
}
|
|
1384
|
+
// 使用ref访问状态,避免闭包依赖
|
|
1385
|
+
const { summaryList, rowSelection, expandable, showColumn } = stateRef.current;
|
|
1095
1386
|
if (summaryList && Array.isArray(summaryList)) {
|
|
1096
1387
|
const summaryRow = rowSelection ? [{}, ...showColumn] : [...showColumn];
|
|
1097
1388
|
if (expandable) {
|
|
1098
1389
|
summaryRow.unshift({});
|
|
1099
1390
|
}
|
|
1100
1391
|
// let summaryInitial = summary().cont;
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
</Table.Summary.Cell>
|
|
1133
|
-
);
|
|
1392
|
+
// 创建Summary组件并跟踪实例,添加强化的清理逻辑
|
|
1393
|
+
const summaryElement = (
|
|
1394
|
+
<Table.Summary
|
|
1395
|
+
fixed
|
|
1396
|
+
ref={(ref) => {
|
|
1397
|
+
if (ref && !isUnmountedRef.current) {
|
|
1398
|
+
summaryInstancesRef.current.add(ref);
|
|
1399
|
+
tempInstancesRef.current.push(ref);
|
|
1400
|
+
|
|
1401
|
+
// 为每个Summary实例添加清理标记
|
|
1402
|
+
if (ref && typeof ref === 'object') {
|
|
1403
|
+
ref._bsQueryTableCleanup = () => {
|
|
1404
|
+
try {
|
|
1405
|
+
// 清理可能的context引用
|
|
1406
|
+
if (ref.context) {
|
|
1407
|
+
ref.context = null;
|
|
1408
|
+
}
|
|
1409
|
+
// 清理可能的props引用
|
|
1410
|
+
if (ref.props) {
|
|
1411
|
+
Object.keys(ref.props).forEach(key => {
|
|
1412
|
+
if (typeof ref.props[key] === 'function') {
|
|
1413
|
+
ref.props[key] = null;
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
// 清理可能的state引用
|
|
1418
|
+
if (ref.state) {
|
|
1419
|
+
ref.state = null;
|
|
1420
|
+
}
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
console.warn('[BsSulaQueryTable] Summary实例清理失败:', error);
|
|
1134
1423
|
}
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
)
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
} else if (ref && isUnmountedRef.current) {
|
|
1427
|
+
// 如果组件已卸载但仍有ref回调,立即清理
|
|
1428
|
+
console.warn('[BsSulaQueryTable] 检测到卸载后的Summary ref回调,立即清理');
|
|
1429
|
+
if (typeof ref._bsQueryTableCleanup === 'function') {
|
|
1430
|
+
ref._bsQueryTableCleanup();
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}}
|
|
1434
|
+
>
|
|
1435
|
+
{Array.isArray(summaryList) &&
|
|
1436
|
+
summaryList.map((summaryItem, summaryIndex) => {
|
|
1437
|
+
// 再次检查卸载状态
|
|
1438
|
+
if (isUnmountedRef.current) return null;
|
|
1439
|
+
|
|
1440
|
+
return (
|
|
1441
|
+
<Table.Summary.Row
|
|
1442
|
+
key={summaryIndex}
|
|
1443
|
+
ref={(ref) => {
|
|
1444
|
+
if (ref && !isUnmountedRef.current) {
|
|
1445
|
+
summaryInstancesRef.current.add(ref);
|
|
1446
|
+
tempInstancesRef.current.push(ref);
|
|
1447
|
+
// 为Summary.Row添加清理标记
|
|
1448
|
+
if (ref && typeof ref === 'object') {
|
|
1449
|
+
ref._bsQueryTableCleanup = () => {
|
|
1450
|
+
try {
|
|
1451
|
+
if (ref.context) ref.context = null;
|
|
1452
|
+
if (ref.props) {
|
|
1453
|
+
Object.keys(ref.props).forEach(key => {
|
|
1454
|
+
if (typeof ref.props[key] === 'function') {
|
|
1455
|
+
ref.props[key] = null;
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
if (ref.state) ref.state = null;
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
console.warn('[BsSulaQueryTable] Summary.Row清理失败:', error);
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
} else if (ref && isUnmountedRef.current) {
|
|
1466
|
+
if (typeof ref._bsQueryTableCleanup === 'function') {
|
|
1467
|
+
ref._bsQueryTableCleanup();
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}}
|
|
1471
|
+
>
|
|
1472
|
+
{summaryRow.map((item: any, index: number) => {
|
|
1473
|
+
// 检查卸载状态
|
|
1474
|
+
if (isUnmountedRef.current) return null;
|
|
1475
|
+
|
|
1476
|
+
const { cout = [] } = summaryItem;
|
|
1477
|
+
const target = cout.filter(
|
|
1478
|
+
(inner: any) =>
|
|
1479
|
+
inner.key &&
|
|
1480
|
+
(inner.key === item.dataIndex || inner.key === item.key),
|
|
1481
|
+
)[0];
|
|
1482
|
+
|
|
1483
|
+
const cellRef = (ref) => {
|
|
1484
|
+
if (ref && !isUnmountedRef.current) {
|
|
1485
|
+
summaryInstancesRef.current.add(ref);
|
|
1486
|
+
tempInstancesRef.current.push(ref);
|
|
1487
|
+
// 为Summary.Cell添加清理标记
|
|
1488
|
+
if (ref && typeof ref === 'object') {
|
|
1489
|
+
ref._bsQueryTableCleanup = () => {
|
|
1490
|
+
try {
|
|
1491
|
+
if (ref.context) ref.context = null;
|
|
1492
|
+
if (ref.props) {
|
|
1493
|
+
Object.keys(ref.props).forEach(key => {
|
|
1494
|
+
if (typeof ref.props[key] === 'function') {
|
|
1495
|
+
ref.props[key] = null;
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
if (ref.state) ref.state = null;
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
console.warn('[BsSulaQueryTable] Summary.Cell清理失败:', error);
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
} else if (ref && isUnmountedRef.current) {
|
|
1506
|
+
if (typeof ref._bsQueryTableCleanup === 'function') {
|
|
1507
|
+
ref._bsQueryTableCleanup();
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
if (target) {
|
|
1513
|
+
const labelText = target.labelShow ? `${item.title}:` : ``;
|
|
1514
|
+
return (
|
|
1515
|
+
<Table.Summary.Cell
|
|
1516
|
+
index={index}
|
|
1517
|
+
key={`Table.Summary.Cell_${item.index}`}
|
|
1518
|
+
ref={cellRef}
|
|
1519
|
+
>
|
|
1520
|
+
<Text
|
|
1521
|
+
type="danger"
|
|
1522
|
+
>
|
|
1523
|
+
{`${labelText} ${target.value ?? ''}`}
|
|
1524
|
+
</Text>
|
|
1525
|
+
</Table.Summary.Cell>
|
|
1526
|
+
);
|
|
1527
|
+
} else {
|
|
1528
|
+
return (
|
|
1529
|
+
<Table.Summary.Cell
|
|
1530
|
+
index={index}
|
|
1531
|
+
key={`Table.Summary.Cell_${item.index}`}
|
|
1532
|
+
ref={cellRef}
|
|
1533
|
+
>
|
|
1534
|
+
<Text
|
|
1535
|
+
type="danger"
|
|
1536
|
+
>
|
|
1537
|
+
{` `}
|
|
1538
|
+
</Text>
|
|
1539
|
+
</Table.Summary.Cell>
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
})}
|
|
1543
|
+
</Table.Summary.Row>
|
|
1544
|
+
);
|
|
1545
|
+
})}
|
|
1138
1546
|
</Table.Summary>
|
|
1139
1547
|
);
|
|
1548
|
+
|
|
1549
|
+
return summaryElement;
|
|
1140
1550
|
} else {
|
|
1141
1551
|
return undefined;
|
|
1142
1552
|
}
|
|
1143
|
-
}, [
|
|
1553
|
+
}, []); // 移除所有依赖项,使用stateRef访问最新状态
|
|
1144
1554
|
|
|
1145
1555
|
const columnsDom = <span className="ant-dropdown-link">
|
|
1146
1556
|
<div onClick={sortTableRef?.current?.showModal} className="ant-dropdown-link">
|
|
@@ -1157,17 +1567,30 @@ export default (props: any) => {
|
|
|
1157
1567
|
/>
|
|
1158
1568
|
|
|
1159
1569
|
const memoConfig = useMemo(
|
|
1160
|
-
() =>
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1570
|
+
() => {
|
|
1571
|
+
// 检查组件是否已卸载
|
|
1572
|
+
if (isUnmountedRef.current) {
|
|
1573
|
+
console.warn('[BsSulaQueryTable] 组件已卸载,跳过memoConfig创建');
|
|
1574
|
+
return {};
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
const memoConfigObject = {
|
|
1578
|
+
...config,
|
|
1579
|
+
summary: props.summary,
|
|
1580
|
+
summaryList: props.summaryList ? getTableSummaryInfo : undefined,
|
|
1581
|
+
statusMapping: props.statusMapping,
|
|
1582
|
+
isBsSulaQueryTable: true,
|
|
1583
|
+
columnsDom:columnsDom,
|
|
1584
|
+
queryFieldsDom,
|
|
1585
|
+
};
|
|
1586
|
+
|
|
1587
|
+
// 存储memoConfig引用用于清理
|
|
1588
|
+
memoConfigRef.current = memoConfigObject;
|
|
1589
|
+
|
|
1590
|
+
return memoConfigObject;
|
|
1591
|
+
},
|
|
1169
1592
|
[
|
|
1170
|
-
|
|
1593
|
+
// 只保留必要的依赖项,避免不必要的重新计算
|
|
1171
1594
|
showColumn,
|
|
1172
1595
|
props.statusMapping,
|
|
1173
1596
|
showSearchFields,
|
|
@@ -1175,6 +1598,10 @@ export default (props: any) => {
|
|
|
1175
1598
|
getTableSummaryInfo,
|
|
1176
1599
|
props.summaryList,
|
|
1177
1600
|
props.summary,
|
|
1601
|
+
props.rowSelection,
|
|
1602
|
+
props.expandable,
|
|
1603
|
+
config,
|
|
1604
|
+
bsTableCode,
|
|
1178
1605
|
],
|
|
1179
1606
|
);
|
|
1180
1607
|
|