@bit-sun/business-component 4.2.5-per-alpha.1 → 4.2.5-per-alpha.2
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/dist/index.esm.js +98 -264
- package/dist/index.js +98 -264
- package/package.json +1 -1
- package/src/components/Business/BsLayouts/Components/CustomerMenu/globalMenu/DrawContent.tsx +94 -25
- package/src/components/Business/BsLayouts/index.tsx +11 -261
- package/src/components/Business/BsSulaQueryTable/index.tsx +5 -2
package/src/components/Business/BsLayouts/Components/CustomerMenu/globalMenu/DrawContent.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import React, { useEffect, useState, useLayoutEffect, useImperativeHandle } from 'react';
|
|
2
|
+
import React, { useEffect, useState, useLayoutEffect, useImperativeHandle, useCallback, useRef } from 'react';
|
|
3
3
|
import { List, Tooltip, Input, Button } from 'antd';
|
|
4
4
|
import { Link, formatMessage, history } from 'umi';
|
|
5
5
|
import classNames from 'classnames';
|
|
@@ -27,6 +27,12 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
27
27
|
|
|
28
28
|
const [moreBtnShow, setMoreBtnShow] = useState(false);
|
|
29
29
|
const [showScroll, setShowScroll] = useState(false);
|
|
30
|
+
|
|
31
|
+
// 添加refs来跟踪组件状态和清理资源
|
|
32
|
+
const isUnmountedRef = useRef(false);
|
|
33
|
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
34
|
+
const resizeHandlerRef = useRef<(() => void) | null>(null);
|
|
35
|
+
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
30
36
|
|
|
31
37
|
useEffect(() => {
|
|
32
38
|
getMenuContentHeight();
|
|
@@ -51,28 +57,64 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
51
57
|
setroutesData(routesDataList)
|
|
52
58
|
setAuthorityMenu(authorityMenuList)
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
// 创建resize处理函数并保存引用
|
|
61
|
+
const handleResize = () => {
|
|
62
|
+
if (!isUnmountedRef.current) {
|
|
63
|
+
getMenuContentHeight();
|
|
64
|
+
}
|
|
56
65
|
};
|
|
66
|
+
resizeHandlerRef.current = handleResize;
|
|
67
|
+
window.addEventListener('resize', handleResize);
|
|
68
|
+
|
|
69
|
+
// 清理函数
|
|
70
|
+
return () => {
|
|
71
|
+
isUnmountedRef.current = true;
|
|
72
|
+
if (resizeHandlerRef.current) {
|
|
73
|
+
window.removeEventListener('resize', resizeHandlerRef.current);
|
|
74
|
+
resizeHandlerRef.current = null;
|
|
75
|
+
}
|
|
76
|
+
if (timeoutRef.current) {
|
|
77
|
+
clearTimeout(timeoutRef.current);
|
|
78
|
+
timeoutRef.current = null;
|
|
79
|
+
}
|
|
80
|
+
if (debounceTimerRef.current) {
|
|
81
|
+
clearTimeout(debounceTimerRef.current);
|
|
82
|
+
debounceTimerRef.current = null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
57
85
|
}, []);
|
|
58
86
|
|
|
59
87
|
useLayoutEffect(() => {
|
|
60
|
-
setTimeout(() => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
timeoutRef.current = setTimeout(() => {
|
|
89
|
+
if (!isUnmountedRef.current) {
|
|
90
|
+
const drawContentElement = document.getElementById("drawContent");
|
|
91
|
+
if (drawContentElement) {
|
|
92
|
+
let drawContentHeight = drawContentElement.scrollHeight;
|
|
93
|
+
if (drawContentHeight > rightMenuHeight) {
|
|
94
|
+
setMoreBtnShow(true);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, 0);
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
if (timeoutRef.current) {
|
|
102
|
+
clearTimeout(timeoutRef.current);
|
|
103
|
+
timeoutRef.current = null;
|
|
64
104
|
}
|
|
65
|
-
}
|
|
66
|
-
}, [])
|
|
105
|
+
};
|
|
106
|
+
}, [rightMenuHeight])
|
|
67
107
|
|
|
68
|
-
const getMenuContentHeight = () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
const getMenuContentHeight = useCallback(() => {
|
|
109
|
+
if (!isUnmountedRef.current) {
|
|
110
|
+
let clientHeight = document.body.clientHeight;
|
|
111
|
+
setHeight(clientHeight - 190);
|
|
112
|
+
setDrawHeight(clientHeight - 70);
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
73
115
|
|
|
74
116
|
|
|
75
|
-
const renderChildItem = (child) => {
|
|
117
|
+
const renderChildItem = useCallback((child) => {
|
|
76
118
|
if (!child.hideInMenu && child.children) {
|
|
77
119
|
return (
|
|
78
120
|
<>
|
|
@@ -102,9 +144,11 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
102
144
|
</List.Item>
|
|
103
145
|
);
|
|
104
146
|
}
|
|
105
|
-
}
|
|
147
|
+
}, [onMenuClick]);
|
|
106
148
|
|
|
107
|
-
const onMenuClick = (e, item) => {
|
|
149
|
+
const onMenuClick = useCallback((e, item) => {
|
|
150
|
+
if (isUnmountedRef.current) return;
|
|
151
|
+
|
|
108
152
|
e.stopPropagation();
|
|
109
153
|
e.preventDefault();
|
|
110
154
|
let searchHistory = JSON.parse(localStorage.getItem(`${itemPath}_search_history`) || '[]');
|
|
@@ -127,7 +171,25 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
127
171
|
pathname: item.path
|
|
128
172
|
})
|
|
129
173
|
onClose();
|
|
130
|
-
};
|
|
174
|
+
}, [itemPath, onClose]);
|
|
175
|
+
|
|
176
|
+
// 创建debounce搜索函数
|
|
177
|
+
const debouncedSearch = useCallback((value: string) => {
|
|
178
|
+
if (debounceTimerRef.current) {
|
|
179
|
+
clearTimeout(debounceTimerRef.current);
|
|
180
|
+
}
|
|
181
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
182
|
+
if (!isUnmountedRef.current) {
|
|
183
|
+
searchMenuData(authorityMenu, value, setSearchMenuData);
|
|
184
|
+
}
|
|
185
|
+
}, 600);
|
|
186
|
+
}, [authorityMenu]);
|
|
187
|
+
|
|
188
|
+
const handleSearchChange = useCallback((e: any) => {
|
|
189
|
+
if (!isUnmountedRef.current) {
|
|
190
|
+
debouncedSearch(e.target.value);
|
|
191
|
+
}
|
|
192
|
+
}, [debouncedSearch]);
|
|
131
193
|
|
|
132
194
|
|
|
133
195
|
let searchHistoryList = JSON.parse(localStorage.getItem(`${itemPath}_search_history`) || '[]');
|
|
@@ -144,6 +206,7 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
144
206
|
color: currentOneLevel === item.path ? '#005cff' : '#000000'
|
|
145
207
|
}}
|
|
146
208
|
onClick={(e) => {
|
|
209
|
+
if (isUnmountedRef.current) return;
|
|
147
210
|
e.stopPropagation();
|
|
148
211
|
e.preventDefault()
|
|
149
212
|
if (item.component) {
|
|
@@ -164,7 +227,11 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
164
227
|
</div>
|
|
165
228
|
<div style={{flexGrow: 1, position: 'relative'}}>
|
|
166
229
|
<img
|
|
167
|
-
onClick={() => {
|
|
230
|
+
onClick={() => {
|
|
231
|
+
if (!isUnmountedRef.current) {
|
|
232
|
+
onClose();
|
|
233
|
+
}
|
|
234
|
+
}}
|
|
168
235
|
style={{position: 'absolute', right: '15px', top: '17px', cursor: 'pointer'}} width={24} src={closeicon} />
|
|
169
236
|
|
|
170
237
|
<div style={{ padding: '10px', marginBottom: '10px', width: '100%', }}>
|
|
@@ -173,9 +240,7 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
173
240
|
placeholder="请输入关键词"
|
|
174
241
|
allowClear
|
|
175
242
|
prefix={<SearchOutlined />}
|
|
176
|
-
onChange={
|
|
177
|
-
searchMenuData(authorityMenu, e.target.value, setSearchMenuData);
|
|
178
|
-
}, 600)}
|
|
243
|
+
onChange={handleSearchChange}
|
|
179
244
|
/>
|
|
180
245
|
<div style={{marginTop: '10px'}}>
|
|
181
246
|
<span style={{ color: '#8c8c8c', opacity: '0.6' }}>最近访问: </span>
|
|
@@ -195,7 +260,9 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
195
260
|
{
|
|
196
261
|
SearhData.map((item: any) => (
|
|
197
262
|
<div onClick={(e) => {
|
|
198
|
-
|
|
263
|
+
if (!isUnmountedRef.current) {
|
|
264
|
+
onMenuClick(e, item);
|
|
265
|
+
}
|
|
199
266
|
}} key={item.path}>{item.name}</div>
|
|
200
267
|
))
|
|
201
268
|
}
|
|
@@ -263,8 +330,10 @@ const DrawContent = ({onClose, originRoutes=[], itemPath}: any) => {
|
|
|
263
330
|
}}>
|
|
264
331
|
<span
|
|
265
332
|
onClick={() => {
|
|
266
|
-
|
|
267
|
-
|
|
333
|
+
if (!isUnmountedRef.current) {
|
|
334
|
+
setShowScroll(true);
|
|
335
|
+
setMoreBtnShow(false);
|
|
336
|
+
}
|
|
268
337
|
}}
|
|
269
338
|
style={{color: '#8c8c8c'}}
|
|
270
339
|
>
|
|
@@ -364,17 +364,7 @@ class BasicLayout extends React.PureComponent {
|
|
|
364
364
|
pathToRegexp,
|
|
365
365
|
} = this.props;
|
|
366
366
|
|
|
367
|
-
|
|
368
|
-
window.testTabDeletion = this.testTabDeletion;
|
|
369
|
-
window.countTabPaneDOMNodes = this.countTabPaneDOMNodes;
|
|
370
|
-
window.testDeleteTab = (key) => {
|
|
371
|
-
console.log(`[测试] 手动删除页签: ${key}`);
|
|
372
|
-
this.tabActions.remove(key);
|
|
373
|
-
};
|
|
374
|
-
window.forceCleanupDOM = this.forceCleanupDOM;
|
|
375
|
-
window.analyzeMemoryLeaks = this.analyzeMemoryLeaks;
|
|
376
|
-
window.forceClearTabPanes = this.forceClearTabPanes;
|
|
377
|
-
console.log('[调试工具] 已暴露全局方法: window.testTabDeletion(), window.countTabPaneDOMNodes(), window.testDeleteTab(key), window.forceCleanupDOM(), window.analyzeMemoryLeaks(), window.forceClearTabPanes()');
|
|
367
|
+
|
|
378
368
|
|
|
379
369
|
let istParent = 0;
|
|
380
370
|
|
|
@@ -838,143 +828,18 @@ class BasicLayout extends React.PureComponent {
|
|
|
838
828
|
this.tabActions[action](targetKey);
|
|
839
829
|
};
|
|
840
830
|
|
|
841
|
-
// DOM节点计数辅助函数
|
|
842
|
-
countTabPaneDOMNodes = () => {
|
|
843
|
-
const tabPanes = document.querySelectorAll('.ant-tabs-tabpane');
|
|
844
|
-
const globalTabsTabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane');
|
|
845
|
-
const activeTabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane-active');
|
|
846
|
-
const hiddenTabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane:not(.ant-tabs-tabpane-active)');
|
|
847
|
-
|
|
848
|
-
console.log(`[DOM计数] 全局TabPane节点数: ${tabPanes.length}, globalTabs内TabPane节点数: ${globalTabsTabPanes.length}`);
|
|
849
|
-
console.log(`[DOM计数] 活跃TabPane节点数: ${activeTabPanes.length}, 非活跃TabPane节点数: ${hiddenTabPanes.length}`);
|
|
850
|
-
|
|
851
|
-
// 详细列出每个TabPane的key和状态
|
|
852
|
-
globalTabsTabPanes.forEach((pane, index) => {
|
|
853
|
-
const isActive = pane.classList.contains('ant-tabs-tabpane-active');
|
|
854
|
-
const key = pane.getAttribute('data-node-key') || pane.id || `未知-${index}`;
|
|
855
|
-
console.log(`[DOM详情] TabPane ${index + 1}: key=${key}, 活跃=${isActive}`);
|
|
856
|
-
});
|
|
857
|
-
|
|
858
|
-
return {
|
|
859
|
-
total: tabPanes.length,
|
|
860
|
-
globalTabs: globalTabsTabPanes.length,
|
|
861
|
-
active: activeTabPanes.length,
|
|
862
|
-
hidden: hiddenTabPanes.length
|
|
863
|
-
};
|
|
864
|
-
};
|
|
865
831
|
|
|
866
|
-
// 测试方法:验证页签删除后DOM状态
|
|
867
|
-
testTabDeletion = () => {
|
|
868
|
-
console.log('=== 页签删除测试开始 ===');
|
|
869
|
-
console.log('当前页签状态:', this.state.listenRouterState.map(item => item.key));
|
|
870
|
-
console.log('当前活跃页签:', this.state.activeKey);
|
|
871
|
-
this.countTabPaneDOMNodes();
|
|
872
|
-
|
|
873
|
-
// 提供删除建议
|
|
874
|
-
const { listenRouterState } = this.state;
|
|
875
|
-
if (listenRouterState.length > 1) {
|
|
876
|
-
const testKey = listenRouterState.find(item => item.key !== '/')?.key;
|
|
877
|
-
if (testKey) {
|
|
878
|
-
console.log(`建议测试:删除页签 ${testKey}`);
|
|
879
|
-
console.log(`执行命令:window.testDeleteTab('${testKey}')`);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
console.log('=== 页签删除测试结束 ===');
|
|
883
|
-
};
|
|
884
832
|
|
|
885
|
-
// 强制清理DOM节点的方法
|
|
886
|
-
forceCleanupDOM = () => {
|
|
887
|
-
console.log('[强制清理] 开始强制清理DOM节点...');
|
|
888
|
-
const beforeCount = this.countTabPaneDOMNodes();
|
|
889
|
-
console.log(`[强制清理] 清理前DOM节点数: ${beforeCount.total}`);
|
|
890
|
-
|
|
891
|
-
// 强制垃圾回收
|
|
892
|
-
if (window.gc) {
|
|
893
|
-
window.gc();
|
|
894
|
-
console.log('[强制清理] 已触发垃圾回收');
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// 清理所有可能的引用
|
|
898
|
-
const tabsContainer = document.getElementById('globalTabs');
|
|
899
|
-
if (tabsContainer) {
|
|
900
|
-
const tabPanes = tabsContainer.querySelectorAll('.ant-tabs-tabpane');
|
|
901
|
-
tabPanes.forEach((pane, index) => {
|
|
902
|
-
if (pane.style.display === 'none' || pane.getAttribute('aria-hidden') === 'true') {
|
|
903
|
-
console.log(`[强制清理] 发现隐藏的TabPane节点 ${index}, 尝试清理...`);
|
|
904
|
-
// 清理事件监听器
|
|
905
|
-
const clone = pane.cloneNode(true);
|
|
906
|
-
pane.parentNode?.replaceChild(clone, pane);
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
setTimeout(() => {
|
|
912
|
-
const afterCount = this.countTabPaneDOMNodes();
|
|
913
|
-
console.log(`[强制清理] 清理后DOM节点数: ${afterCount.total}`);
|
|
914
|
-
console.log(`[强制清理] DOM节点减少: ${beforeCount.total - afterCount.total}`);
|
|
915
|
-
}, 200);
|
|
916
|
-
};
|
|
917
833
|
|
|
918
|
-
// 分析内存泄漏的方法
|
|
919
|
-
analyzeMemoryLeaks = () => {
|
|
920
|
-
console.log('[内存分析] 开始分析可能的内存泄漏...');
|
|
921
|
-
|
|
922
|
-
// 检查全局事件监听器
|
|
923
|
-
const listeners = window.getEventListeners ? window.getEventListeners(document) : {};
|
|
924
|
-
console.log('[内存分析] 全局事件监听器:', listeners);
|
|
925
|
-
|
|
926
|
-
// 检查定时器
|
|
927
|
-
console.log('[内存分析] 当前活跃定时器数量:', Object.keys(window).filter(key => key.includes('timeout') || key.includes('interval')).length);
|
|
928
|
-
|
|
929
|
-
// 检查TabPane节点详情
|
|
930
|
-
const tabsContainer = document.getElementById('globalTabs');
|
|
931
|
-
if (tabsContainer) {
|
|
932
|
-
const tabPanes = tabsContainer.querySelectorAll('.ant-tabs-tabpane');
|
|
933
|
-
console.log(`[内存分析] 发现 ${tabPanes.length} 个TabPane节点:`);
|
|
934
|
-
tabPanes.forEach((pane, index) => {
|
|
935
|
-
const key = pane.getAttribute('data-node-key') || pane.id;
|
|
936
|
-
const isVisible = pane.style.display !== 'none' && pane.getAttribute('aria-hidden') !== 'true';
|
|
937
|
-
const hasContent = pane.children.length > 0;
|
|
938
|
-
console.log(` TabPane ${index}: key=${key}, visible=${isVisible}, hasContent=${hasContent}, children=${pane.children.length}`);
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// 检查React组件实例
|
|
943
|
-
const reactInstances = document.querySelectorAll('[data-reactroot], [data-react-checksum]');
|
|
944
|
-
console.log(`[内存分析] React实例数量: ${reactInstances.length}`);
|
|
945
|
-
};
|
|
946
834
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
const tabsContainer = document.getElementById('globalTabs');
|
|
953
|
-
if (tabsContainer) {
|
|
954
|
-
const tabPanes = tabsContainer.querySelectorAll('.ant-tabs-tabpane');
|
|
955
|
-
console.log(`[强制清除] 发现 ${tabPanes.length} 个TabPane节点,准备清除...`);
|
|
956
|
-
|
|
957
|
-
tabPanes.forEach((pane, index) => {
|
|
958
|
-
const key = pane.getAttribute('data-node-key') || pane.id;
|
|
959
|
-
const isActive = !pane.getAttribute('aria-hidden') && pane.style.display !== 'none';
|
|
960
|
-
|
|
961
|
-
if (!isActive) {
|
|
962
|
-
console.log(`[强制清除] 移除非活跃TabPane ${index} (key: ${key})`);
|
|
963
|
-
pane.remove();
|
|
964
|
-
}
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
setTimeout(() => {
|
|
969
|
-
const afterCount = this.countTabPaneDOMNodes();
|
|
970
|
-
console.log(`[强制清除] 清除前: ${beforeCount.total}, 清除后: ${afterCount.total}, 减少: ${beforeCount.total - afterCount.total}`);
|
|
971
|
-
}, 100);
|
|
972
|
-
};
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
|
|
973
840
|
|
|
974
841
|
tabActions: any = {
|
|
975
842
|
remove: (targetKey: string) => {
|
|
976
|
-
console.log(`[页签删除] 开始删除页签: ${targetKey}`);
|
|
977
|
-
this.countTabPaneDOMNodes();
|
|
978
843
|
|
|
979
844
|
const { listenRouterState, activeKey, customerMatchs, listenRouterKey } =
|
|
980
845
|
this.state;
|
|
@@ -1024,12 +889,6 @@ class BasicLayout extends React.PureComponent {
|
|
|
1024
889
|
pathname: newKey,
|
|
1025
890
|
});
|
|
1026
891
|
this.checkisNavSlide();
|
|
1027
|
-
|
|
1028
|
-
// 延迟检查DOM节点变化,确保React完成渲染
|
|
1029
|
-
setTimeout(() => {
|
|
1030
|
-
console.log(`[页签删除] 删除页签 ${targetKey} 后的DOM状态:`);
|
|
1031
|
-
this.countTabPaneDOMNodes();
|
|
1032
|
-
}, 100);
|
|
1033
892
|
},
|
|
1034
893
|
);
|
|
1035
894
|
},
|
|
@@ -1754,118 +1613,23 @@ class BasicLayout extends React.PureComponent {
|
|
|
1754
1613
|
class WrapperComponent extends React.Component {
|
|
1755
1614
|
constructor(props) {
|
|
1756
1615
|
super(props);
|
|
1757
|
-
console.log(`WrapperComponent 构造函数: 页签 ${props.item.key} 创建`);
|
|
1758
1616
|
|
|
1759
|
-
//
|
|
1760
|
-
this.domObserver = null;
|
|
1617
|
+
// 初始化组件状态
|
|
1761
1618
|
this.isUnmounted = false;
|
|
1762
|
-
|
|
1763
|
-
// 统计当前DOM节点数
|
|
1764
|
-
setTimeout(() => {
|
|
1765
|
-
const tabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane');
|
|
1766
|
-
console.log(`[组件创建] 页签 ${props.item.key} 创建后,DOM中TabPane节点数: ${tabPanes.length}`);
|
|
1767
|
-
}, 0);
|
|
1768
1619
|
}
|
|
1769
1620
|
|
|
1770
1621
|
componentDidMount() {
|
|
1771
|
-
|
|
1772
|
-
const tabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane');
|
|
1773
|
-
console.log(`[组件挂载] 页签 ${this.props.item.key} 挂载后,DOM中TabPane节点数: ${tabPanes.length}`);
|
|
1774
|
-
|
|
1775
|
-
// 设置MutationObserver监控DOM变化
|
|
1776
|
-
this.setupDOMObserver();
|
|
1622
|
+
// 组件挂载完成
|
|
1777
1623
|
}
|
|
1778
1624
|
|
|
1779
|
-
|
|
1780
|
-
if (this.domObserver || this.isUnmounted) return;
|
|
1781
|
-
|
|
1782
|
-
const globalTabsContainer = document.getElementById('globalTabs');
|
|
1783
|
-
if (!globalTabsContainer) return;
|
|
1784
|
-
|
|
1785
|
-
this.domObserver = new MutationObserver((mutations) => {
|
|
1786
|
-
if (this.isUnmounted) return;
|
|
1787
|
-
|
|
1788
|
-
mutations.forEach((mutation) => {
|
|
1789
|
-
if (mutation.type === 'childList') {
|
|
1790
|
-
// 检查是否有TabPane节点被添加或删除
|
|
1791
|
-
const addedTabPanes = Array.from(mutation.addedNodes).filter(node =>
|
|
1792
|
-
node.nodeType === Node.ELEMENT_NODE &&
|
|
1793
|
-
node.classList &&
|
|
1794
|
-
node.classList.contains('ant-tabs-tabpane')
|
|
1795
|
-
);
|
|
1796
|
-
|
|
1797
|
-
const removedTabPanes = Array.from(mutation.removedNodes).filter(node =>
|
|
1798
|
-
node.nodeType === Node.ELEMENT_NODE &&
|
|
1799
|
-
node.classList &&
|
|
1800
|
-
node.classList.contains('ant-tabs-tabpane')
|
|
1801
|
-
);
|
|
1802
|
-
|
|
1803
|
-
if (addedTabPanes.length > 0) {
|
|
1804
|
-
console.log(`[DOM监控] 检测到 ${addedTabPanes.length} 个TabPane节点被添加`);
|
|
1805
|
-
addedTabPanes.forEach(node => {
|
|
1806
|
-
const key = node.getAttribute('data-node-key') || '未知';
|
|
1807
|
-
console.log(`[DOM监控] 添加的TabPane: ${key}`);
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
if (removedTabPanes.length > 0) {
|
|
1812
|
-
console.log(`[DOM监控] 检测到 ${removedTabPanes.length} 个TabPane节点被删除`);
|
|
1813
|
-
removedTabPanes.forEach(node => {
|
|
1814
|
-
const key = node.getAttribute('data-node-key') || '未知';
|
|
1815
|
-
console.log(`[DOM监控] 删除的TabPane: ${key}`);
|
|
1816
|
-
|
|
1817
|
-
// 检查是否是当前组件对应的TabPane
|
|
1818
|
-
if (key === this.props.item.key) {
|
|
1819
|
-
console.log(`[DOM监控] 当前组件 ${this.props.item.key} 对应的TabPane已从DOM中删除`);
|
|
1820
|
-
}
|
|
1821
|
-
});
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
// 统计当前DOM中的TabPane数量
|
|
1825
|
-
const currentTabPanes = document.querySelectorAll('#globalTabs .ant-tabs-tabpane');
|
|
1826
|
-
console.log(`[DOM监控] 当前DOM中TabPane节点总数: ${currentTabPanes.length}`);
|
|
1827
|
-
}
|
|
1828
|
-
});
|
|
1829
|
-
});
|
|
1830
|
-
|
|
1831
|
-
// 开始观察
|
|
1832
|
-
this.domObserver.observe(globalTabsContainer, {
|
|
1833
|
-
childList: true,
|
|
1834
|
-
subtree: true
|
|
1835
|
-
});
|
|
1836
|
-
|
|
1837
|
-
console.log(`[DOM监控] 已为页签 ${this.props.item.key} 设置DOM变化监控`);
|
|
1838
|
-
};
|
|
1625
|
+
|
|
1839
1626
|
|
|
1840
1627
|
componentWillUnmount() {
|
|
1841
|
-
console.log(`WrapperComponent 卸载: 页签 ${this.props.item.key} 组件正在销毁`);
|
|
1842
|
-
|
|
1843
1628
|
// 设置卸载标志
|
|
1844
1629
|
this.isUnmounted = true;
|
|
1845
1630
|
|
|
1846
|
-
|
|
1847
|
-
console.log(`[组件卸载] 页签 ${this.props.item.key} 卸载前,DOM中TabPane节点数: ${tabPanes.length}`);
|
|
1848
|
-
|
|
1849
|
-
// 清理MutationObserver
|
|
1850
|
-
if (this.domObserver) {
|
|
1851
|
-
this.domObserver.disconnect();
|
|
1852
|
-
this.domObserver = null;
|
|
1853
|
-
console.log(`[清理] 已断开页签 ${this.props.item.key} 的DOM监控`);
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
// 强制清理所有可能的引用和事件监听器
|
|
1631
|
+
// 清理可能的DOM事件监听器
|
|
1857
1632
|
try {
|
|
1858
|
-
// 清理组件内部的所有引用
|
|
1859
|
-
if (this.props && typeof this.props === 'object') {
|
|
1860
|
-
Object.keys(this.props).forEach(key => {
|
|
1861
|
-
if (this.props[key] && typeof this.props[key] === 'object') {
|
|
1862
|
-
// 不直接删除props,但清理可能的循环引用
|
|
1863
|
-
console.log(`[清理] 检查prop: ${key}`);
|
|
1864
|
-
}
|
|
1865
|
-
});
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
// 清理可能的DOM事件监听器
|
|
1869
1633
|
const currentElement = document.querySelector(`#globalTabs .ant-tabs-tabpane[data-node-key="${this.props.item.key}"]`);
|
|
1870
1634
|
if (currentElement) {
|
|
1871
1635
|
// 移除所有可能的事件监听器
|
|
@@ -1874,30 +1638,16 @@ class WrapperComponent extends React.Component {
|
|
|
1874
1638
|
currentElement.removeEventListener(eventType, this.handleEvent, true);
|
|
1875
1639
|
currentElement.removeEventListener(eventType, this.handleEvent, false);
|
|
1876
1640
|
});
|
|
1877
|
-
console.log(`[清理] 已清理页签 ${this.props.item.key} 的DOM事件监听器`);
|
|
1878
1641
|
}
|
|
1879
1642
|
|
|
1880
1643
|
// 强制垃圾回收提示
|
|
1881
1644
|
if (window.gc && typeof window.gc === 'function') {
|
|
1882
1645
|
window.gc();
|
|
1883
|
-
console.log(`[清理] 已触发垃圾回收`);
|
|
1884
1646
|
}
|
|
1885
1647
|
|
|
1886
1648
|
} catch (error) {
|
|
1887
|
-
|
|
1649
|
+
// 清理过程中的错误处理
|
|
1888
1650
|
}
|
|
1889
|
-
|
|
1890
|
-
// 延迟检查卸载后的DOM状态
|
|
1891
|
-
setTimeout(() => {
|
|
1892
|
-
const tabPanesAfter = document.querySelectorAll('#globalTabs .ant-tabs-tabpane');
|
|
1893
|
-
console.log(`[组件卸载] 页签 ${this.props.item.key} 卸载后,DOM中TabPane节点数: ${tabPanesAfter.length}`);
|
|
1894
|
-
|
|
1895
|
-
// 强制检查内存使用情况
|
|
1896
|
-
if (window.performance && window.performance.memory) {
|
|
1897
|
-
const memory = window.performance.memory;
|
|
1898
|
-
console.log(`[内存监控] 卸载后内存使用: ${(memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}MB / ${(memory.totalJSHeapSize / 1024 / 1024).toFixed(2)}MB`);
|
|
1899
|
-
}
|
|
1900
|
-
}, 100);
|
|
1901
1651
|
}
|
|
1902
1652
|
|
|
1903
1653
|
// 通用事件处理器,用于清理
|
|
@@ -1090,7 +1090,7 @@ export default (props: any) => {
|
|
|
1090
1090
|
* ]
|
|
1091
1091
|
* @returns []
|
|
1092
1092
|
*/
|
|
1093
|
-
const getTableSummaryInfo = () => {
|
|
1093
|
+
const getTableSummaryInfo = useCallback(() => {
|
|
1094
1094
|
const { summaryList, rowSelection, expandable }: any = props;
|
|
1095
1095
|
if (summaryList && Array.isArray(summaryList)) {
|
|
1096
1096
|
const summaryRow = rowSelection ? [{}, ...showColumn] : [...showColumn];
|
|
@@ -1140,7 +1140,7 @@ export default (props: any) => {
|
|
|
1140
1140
|
} else {
|
|
1141
1141
|
return undefined;
|
|
1142
1142
|
}
|
|
1143
|
-
};
|
|
1143
|
+
}, [props.summaryList, props.rowSelection, props.expandable, showColumn]);
|
|
1144
1144
|
|
|
1145
1145
|
const columnsDom = <span className="ant-dropdown-link">
|
|
1146
1146
|
<div onClick={sortTableRef?.current?.showModal} className="ant-dropdown-link">
|
|
@@ -1172,6 +1172,9 @@ export default (props: any) => {
|
|
|
1172
1172
|
props.statusMapping,
|
|
1173
1173
|
showSearchFields,
|
|
1174
1174
|
expandedRowKeys,
|
|
1175
|
+
getTableSummaryInfo,
|
|
1176
|
+
props.summaryList,
|
|
1177
|
+
props.summary,
|
|
1175
1178
|
],
|
|
1176
1179
|
);
|
|
1177
1180
|
|