@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
|
@@ -48,12 +48,12 @@ class SortableTable extends React.Component {
|
|
|
48
48
|
isDefaultValue: false,
|
|
49
49
|
defaultValue: [],
|
|
50
50
|
bsTableCode: '', //设置table 列的标识
|
|
51
|
-
}
|
|
52
|
-
|
|
51
|
+
}
|
|
53
52
|
// 实例变量
|
|
54
53
|
this.isUnmounted = false;
|
|
55
54
|
this.requestController = null;
|
|
56
55
|
this.debounceTimer = null;
|
|
56
|
+
this.cleanupTimer = null;
|
|
57
57
|
|
|
58
58
|
// Modal组件ref,用于直接访问DOM进行清理
|
|
59
59
|
this.modalRef = React.createRef();
|
|
@@ -927,37 +927,47 @@ class SortableTable extends React.Component {
|
|
|
927
927
|
onSearchSort: '',
|
|
928
928
|
});
|
|
929
929
|
|
|
930
|
+
// 清理之前的定时器
|
|
931
|
+
if (this.cleanupTimer) {
|
|
932
|
+
clearTimeout(this.cleanupTimer);
|
|
933
|
+
this.cleanupTimer = null;
|
|
934
|
+
}
|
|
935
|
+
|
|
930
936
|
// 强制清理React Fiber节点和DOM引用
|
|
931
|
-
setTimeout(() => {
|
|
937
|
+
this.cleanupTimer = setTimeout(() => {
|
|
932
938
|
try {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
939
|
+
if (!this.isUnmounted) {
|
|
940
|
+
// 清理所有带有sort_table类名的DOM节点的React引用
|
|
941
|
+
const sortTableElements = document.querySelectorAll('.sort_table, .sort_table_column_wrapper, .sort_table_column, .sort_table_content_wrapper, .sort_table_column_count');
|
|
942
|
+
sortTableElements.forEach(element => {
|
|
943
|
+
// 清理React内部实例引用
|
|
944
|
+
const reactKeys = Object.keys(element).filter(key => key.startsWith('__reactInternalInstance') || key.startsWith('__reactEventHandlers'));
|
|
945
|
+
reactKeys.forEach(key => {
|
|
946
|
+
try {
|
|
947
|
+
delete element[key];
|
|
948
|
+
} catch (e) {
|
|
949
|
+
// 忽略删除失败
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// 清理React Fiber引用
|
|
954
|
+
if (element._reactInternalFiber) {
|
|
955
|
+
element._reactInternalFiber = null;
|
|
956
|
+
}
|
|
957
|
+
if (element._reactInternalInstance) {
|
|
958
|
+
element._reactInternalInstance = null;
|
|
943
959
|
}
|
|
944
960
|
});
|
|
945
961
|
|
|
946
|
-
//
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
if (element._reactInternalInstance) {
|
|
951
|
-
element._reactInternalInstance = null;
|
|
962
|
+
// 强制垃圾回收提示
|
|
963
|
+
if (window.gc) {
|
|
964
|
+
window.gc();
|
|
952
965
|
}
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
// 强制垃圾回收提示
|
|
956
|
-
if (window.gc) {
|
|
957
|
-
window.gc();
|
|
958
966
|
}
|
|
959
967
|
} catch (error) {
|
|
960
968
|
console.warn('[SortableTable] Modal afterClose清理失败:', error);
|
|
969
|
+
} finally {
|
|
970
|
+
this.cleanupTimer = null;
|
|
961
971
|
}
|
|
962
972
|
}, 100);
|
|
963
973
|
}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import React, { useEffect, useState, useMemo } from 'react';
|
|
2
|
+
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
|
3
3
|
import { memoizeOneFormatter } from '@/utils/utils';
|
|
4
4
|
import { Breadcrumb, Space } from 'antd';
|
|
5
5
|
import { useLocation, formatMessage } from 'umi';
|
|
@@ -10,11 +10,10 @@ import './index.less';
|
|
|
10
10
|
|
|
11
11
|
export default (props: any) => {
|
|
12
12
|
const { pathname } = useLocation();
|
|
13
|
-
const [id]: any = useState(pathname + 'id');
|
|
14
13
|
const { children, ...restProps} = props;
|
|
15
14
|
|
|
16
15
|
return (
|
|
17
|
-
<div id=
|
|
16
|
+
<div id="home-page-wrapper" className={'home_page_wrapper'}>
|
|
18
17
|
<HeaderWrapper
|
|
19
18
|
pathname={pathname}
|
|
20
19
|
{
|
|
@@ -37,6 +36,7 @@ const HeaderWrapper = React.memo(
|
|
|
37
36
|
pathToRegexp, //改为从项目代码透传该方法
|
|
38
37
|
}: { pathname: string, routes: any[], itemPath: string, alertProps: any }) => {
|
|
39
38
|
const [breadcrumbArr, setBreadCrumbArr]: any = useState([]);
|
|
39
|
+
const isMountedRef = React.useRef(true);
|
|
40
40
|
const basePath = window.top == window ? '' : `/${itemPath}`;
|
|
41
41
|
|
|
42
42
|
const menuRoutes = useMemo(() => {
|
|
@@ -49,15 +49,15 @@ const HeaderWrapper = React.memo(
|
|
|
49
49
|
return getMainCrumbNameMap(memoizeOneFormatter(menuRoutes, ''));
|
|
50
50
|
}, [menuRoutes]); // 只有menuRoutes变化时才重新创建
|
|
51
51
|
|
|
52
|
-
const matchParamsPath = (pathname: string, breadcrumbNameMap: object) => {
|
|
52
|
+
const matchParamsPath = useCallback((pathname: string, breadcrumbNameMap: object) => {
|
|
53
53
|
const pathKey: any = Object.keys(breadcrumbNameMap).find((key) =>
|
|
54
54
|
pathToRegexp ? pathToRegexp(key).test(pathname) : false
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
return pathKey ? breadcrumbNameMap[pathKey] : undefined;
|
|
58
|
-
};
|
|
58
|
+
}, [pathToRegexp]);
|
|
59
59
|
|
|
60
|
-
const getPageTitle = (pathname: string) => {
|
|
60
|
+
const getPageTitle = useCallback((pathname: string) => {
|
|
61
61
|
const currRouterData = matchParamsPath(
|
|
62
62
|
`${basePath}${pathname}`,
|
|
63
63
|
breadcrumbNameMap,
|
|
@@ -72,18 +72,39 @@ const HeaderWrapper = React.memo(
|
|
|
72
72
|
breadcrumbArrs.pop();
|
|
73
73
|
breadcrumbArrs.push(title)
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// 只在组件挂载时更新状态,避免内存泄漏
|
|
76
|
+
if (isMountedRef.current) {
|
|
77
|
+
setBreadCrumbArr([...breadcrumbArrs]);
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
const pageName = formatMessage({
|
|
78
81
|
id: currRouterData.locale || currRouterData.name,
|
|
79
82
|
defaultMessage: currRouterData.name,
|
|
80
83
|
});
|
|
81
84
|
return title ? title : window.top === window ? pageName : `${currRouterData.name}`;
|
|
82
|
-
};
|
|
85
|
+
}, [matchParamsPath, breadcrumbNameMap, basePath, title]);
|
|
86
|
+
|
|
87
|
+
const pageTitle = useMemo(() => getPageTitle(pathname), [getPageTitle, pathname]);
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
// 路径变化时的清理逻辑
|
|
91
|
+
return () => {
|
|
92
|
+
// 只在组件仍挂载时清理状态
|
|
93
|
+
if (isMountedRef.current) {
|
|
94
|
+
setBreadCrumbArr([]);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}, [pathname]);
|
|
85
98
|
|
|
86
|
-
|
|
99
|
+
// 组件卸载时清理所有引用
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
return () => {
|
|
102
|
+
// 标记组件已卸载
|
|
103
|
+
isMountedRef.current = false;
|
|
104
|
+
// 清理所有状态和引用
|
|
105
|
+
setBreadCrumbArr([]);
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
87
108
|
|
|
88
109
|
return (
|
|
89
110
|
<div className='bs_home_page_head_wrapper'>
|
|
@@ -124,9 +145,18 @@ const HeaderWrapper = React.memo(
|
|
|
124
145
|
);
|
|
125
146
|
},
|
|
126
147
|
(prevProps, nextProps) => {
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
// 如果关键属性发生变化,则需要重新渲染
|
|
149
|
+
if (
|
|
150
|
+
prevProps.pathname !== nextProps.pathname ||
|
|
151
|
+
prevProps.title !== nextProps.title ||
|
|
152
|
+
prevProps.itemPath !== nextProps.itemPath ||
|
|
153
|
+
prevProps.routes !== nextProps.routes ||
|
|
154
|
+
prevProps.pathToRegexp !== nextProps.pathToRegexp ||
|
|
155
|
+
JSON.stringify(prevProps.alertProps) !== JSON.stringify(nextProps.alertProps) ||
|
|
156
|
+
prevProps.extra !== nextProps.extra
|
|
157
|
+
) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
130
160
|
return true;
|
|
131
161
|
},
|
|
132
162
|
);
|
|
@@ -33,6 +33,8 @@ export default props => {
|
|
|
33
33
|
} = props;
|
|
34
34
|
const [treeData, setTreeData] = useState(parseData(data));
|
|
35
35
|
const [activeLine, setActiveLine] = useState(-1);
|
|
36
|
+
const highlightTimerRef = React.useRef(null);
|
|
37
|
+
const isUnmountedRef = React.useRef(false);
|
|
36
38
|
|
|
37
39
|
useEffect(() => {
|
|
38
40
|
setTreeData(parseData(data));
|
|
@@ -47,10 +49,35 @@ export default props => {
|
|
|
47
49
|
}, [treeData, currentLine]);
|
|
48
50
|
|
|
49
51
|
useEffect(() => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
// 清理之前的定时器
|
|
53
|
+
if (highlightTimerRef.current) {
|
|
54
|
+
clearTimeout(highlightTimerRef.current);
|
|
55
|
+
highlightTimerRef.current = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
highlightTimerRef.current = setTimeout(() => {
|
|
59
|
+
try {
|
|
60
|
+
if (!isUnmountedRef.current) {
|
|
61
|
+
setTreeData(createHighLightTreeData(treeData, activeLine));
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.warn('Failed to update tree highlight:', error);
|
|
65
|
+
} finally {
|
|
66
|
+
highlightTimerRef.current = null;
|
|
67
|
+
}
|
|
52
68
|
});
|
|
53
69
|
}, [activeLine]);
|
|
70
|
+
|
|
71
|
+
// 组件卸载时清理定时器
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
return () => {
|
|
74
|
+
isUnmountedRef.current = true;
|
|
75
|
+
if (highlightTimerRef.current) {
|
|
76
|
+
clearTimeout(highlightTimerRef.current);
|
|
77
|
+
highlightTimerRef.current = null;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
54
81
|
|
|
55
82
|
const handleSelect = (node, hasChildren) => {
|
|
56
83
|
const noActiveData = clearActiveNode(treeData);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
3
|
import { useDebounceFn } from 'ahooks';
|
|
3
4
|
import _, { isNil } from "lodash"
|
|
4
5
|
import { Select, Input, Button, Modal, ConfigProvider, Spin, message } from 'antd';
|
|
@@ -45,6 +46,8 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
|
|
|
45
46
|
const [fetching, setFetching] = useState(false);
|
|
46
47
|
const [searchValue, setSearchValue] = useState('');
|
|
47
48
|
const [uniqueValue, setUniqueValue] = useState(resultSourceKey);
|
|
49
|
+
const selectTimerRef = useRef(null);
|
|
50
|
+
const isUnmountedRef = useRef(false);
|
|
48
51
|
|
|
49
52
|
useEffect(() => {
|
|
50
53
|
setPopValue(value);
|
|
@@ -54,6 +57,17 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
|
|
|
54
57
|
useEffect(() => {
|
|
55
58
|
setUniqueValue(makeUniqueValue());
|
|
56
59
|
}, [resultSourceKey])
|
|
60
|
+
|
|
61
|
+
// 组件卸载时清理定时器
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
return () => {
|
|
64
|
+
isUnmountedRef.current = true;
|
|
65
|
+
if (selectTimerRef.current) {
|
|
66
|
+
clearTimeout(selectTimerRef.current);
|
|
67
|
+
selectTimerRef.current = null;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
57
71
|
|
|
58
72
|
const showModal = () => {
|
|
59
73
|
setIsModalVisible(true);
|
|
@@ -155,9 +169,24 @@ const QueryMutipleSearchSelect = ({ onValueChange, requestConfig={}, selectProps
|
|
|
155
169
|
onSearch={(v) => onSearchChange(v)}
|
|
156
170
|
onSelect={() => {
|
|
157
171
|
if(selectMode) return;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
172
|
+
|
|
173
|
+
// 清理之前的定时器
|
|
174
|
+
if (selectTimerRef.current) {
|
|
175
|
+
clearTimeout(selectTimerRef.current);
|
|
176
|
+
selectTimerRef.current = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
selectTimerRef.current = setTimeout(() => {
|
|
180
|
+
try {
|
|
181
|
+
if (!isUnmountedRef.current) {
|
|
182
|
+
setOpen(false);
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.warn('Failed to close select:', error);
|
|
186
|
+
} finally {
|
|
187
|
+
selectTimerRef.current = null;
|
|
188
|
+
}
|
|
189
|
+
}, 0);
|
|
161
190
|
}}
|
|
162
191
|
style={{ width: 'calc(100%)' }}
|
|
163
192
|
suffixIcon={<div className={`query_select_expand_button`} onClick={showModal}><DashOutlined /></div>}
|