@hsu-react/ui 2.5.3 → 2.5.5
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/es/components/ChainGraph/_hooks/useChainGraphData.js +29 -8
- package/es/components/ChainGraph/_hooks/useChainGraphLayout.js +14 -3
- package/es/components/Chart/Bar/index.js +49 -19
- package/es/components/Chart/Bubble/index.js +21 -6
- package/es/components/Chart/Common/index.js +7 -2
- package/es/components/Chart/Heatmap/index.js +21 -6
- package/es/components/Chart/Line/index.js +49 -19
- package/es/components/Chart/Pie/Pie3D/index.js +22 -6
- package/es/components/Chart/Pie/index.js +21 -6
- package/es/components/Chart/Sankey/index.js +11 -2
- package/es/components/Chart/Tree/index.js +11 -2
- package/es/components/Chat/ChatInput/index.js +7 -2
- package/es/components/Icon/index.js +8 -2
- package/es/components/Input/Number/index.js +28 -21
- package/es/components/Input/Password/index.js +44 -36
- package/es/components/Input/Search/index.js +44 -36
- package/es/components/Input/TextArea/index.js +64 -42
- package/es/components/Input/index.js +40 -34
- package/es/components/Select/_hooks/useSelectComposition.js +6 -2
- package/es/components/Select/_hooks/useSelectInputOptions.js +8 -2
- package/es/components/Slider/index.js +24 -15
- package/es/components/Table/_hooks/useAutoScrolling.js +16 -7
- package/es/components/Upload/_hooks/useUploadFileList.js +8 -2
- package/es/components/Upload/_hooks/useUploadOperations.js +8 -2
- package/es/hooks/useLatestRef.d.ts +24 -0
- package/es/hooks/useLatestRef.js +29 -0
- package/es/layout/Menu/_hooks/useOnlyLvOneMenu.js +9 -2
- package/lib/components/ChainGraph/_hooks/useChainGraphData.js +24 -7
- package/lib/components/ChainGraph/_hooks/useChainGraphLayout.js +12 -2
- package/lib/components/Chart/Bar/index.js +45 -18
- package/lib/components/Chart/Bubble/index.js +19 -6
- package/lib/components/Chart/Common/index.js +6 -2
- package/lib/components/Chart/Heatmap/index.js +19 -6
- package/lib/components/Chart/Line/index.js +45 -18
- package/lib/components/Chart/Pie/Pie3D/index.js +20 -6
- package/lib/components/Chart/Pie/index.js +19 -6
- package/lib/components/Chart/Sankey/index.js +10 -2
- package/lib/components/Chart/Tree/index.js +10 -2
- package/lib/components/Chat/ChatInput/index.js +6 -2
- package/lib/components/Icon/index.js +7 -2
- package/lib/components/Input/Number/index.js +27 -18
- package/lib/components/Input/Password/index.js +40 -26
- package/lib/components/Input/Search/index.js +42 -26
- package/lib/components/Input/TextArea/index.js +54 -26
- package/lib/components/Input/index.js +37 -24
- package/lib/components/Select/_hooks/useSelectComposition.js +6 -2
- package/lib/components/Select/_hooks/useSelectInputOptions.js +7 -2
- package/lib/components/Slider/index.js +23 -11
- package/lib/components/Table/_hooks/useAutoScrolling.js +16 -7
- package/lib/components/Upload/_hooks/useUploadFileList.js +7 -2
- package/lib/components/Upload/_hooks/useUploadOperations.js +7 -2
- package/lib/hooks/useLatestRef.d.ts +24 -0
- package/lib/hooks/useLatestRef.js +35 -0
- package/lib/layout/Menu/_hooks/useOnlyLvOneMenu.js +8 -2
- package/package.json +9 -3
- package/src/__tests__/callbackDepsGuard.test.ts +119 -0
- package/src/__tests__/setup.ts +26 -0
- package/src/components/ChainGraph/_hooks/callbacks.test.tsx +155 -0
- package/src/components/ChainGraph/_hooks/useChainGraphData.ts +38 -10
- package/src/components/ChainGraph/_hooks/useChainGraphLayout.ts +16 -3
- package/src/components/Chart/Bar/index.tsx +59 -21
- package/src/components/Chart/Bubble/index.tsx +22 -7
- package/src/components/Chart/Common/index.tsx +7 -2
- package/src/components/Chart/Heatmap/index.tsx +25 -6
- package/src/components/Chart/Line/index.tsx +59 -21
- package/src/components/Chart/Pie/Pie3D/index.tsx +22 -7
- package/src/components/Chart/Pie/index.tsx +22 -7
- package/src/components/Chart/Sankey/index.tsx +10 -4
- package/src/components/Chart/Tree/index.tsx +10 -4
- package/src/components/Chart/callbacks.test.tsx +315 -0
- package/src/components/Chat/ChatInput/index.tsx +7 -2
- package/src/components/Icon/index.tsx +7 -2
- package/src/components/Input/Number/index.tsx +25 -23
- package/src/components/Input/Password/index.tsx +37 -26
- package/src/components/Input/Search/index.tsx +39 -26
- package/src/components/Input/TextArea/index.test.tsx +208 -0
- package/src/components/Input/TextArea/index.tsx +56 -30
- package/src/components/Input/index.test.tsx +189 -0
- package/src/components/Input/index.tsx +36 -29
- package/src/components/Select/_hooks/useSelectComposition.test.ts +47 -0
- package/src/components/Select/_hooks/useSelectComposition.ts +6 -2
- package/src/components/Select/_hooks/useSelectInputOptions.ts +6 -9
- package/src/components/Slider/index.tsx +22 -12
- package/src/components/Table/_hooks/useAutoScrolling.test.tsx +95 -0
- package/src/components/Table/_hooks/useAutoScrolling.ts +16 -7
- package/src/components/Upload/_hooks/useUploadFileList.ts +6 -2
- package/src/components/Upload/_hooks/useUploadOperations.ts +7 -2
- package/src/hooks/useLatestRef.ts +31 -0
- package/src/layout/Menu/_hooks/useOnlyLvOneMenu.test.tsx +56 -0
- package/src/layout/Menu/_hooks/useOnlyLvOneMenu.ts +8 -2
|
@@ -17,6 +17,7 @@ import AntdTextArea from "antd/es/input/TextArea";
|
|
|
17
17
|
import React, { useEffect, useRef, useState } from "react";
|
|
18
18
|
import classNames from "classnames";
|
|
19
19
|
import styles from "./index.module.scss";
|
|
20
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
20
21
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
21
22
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
23
|
var TextArea = function TextArea(props) {
|
|
@@ -40,56 +41,74 @@ var TextArea = function TextArea(props) {
|
|
|
40
41
|
autoSize = props.autoSize,
|
|
41
42
|
style = props.style,
|
|
42
43
|
inputConfig = _objectWithoutProperties(props, _excluded);
|
|
44
|
+
var ref = useRef(null);
|
|
43
45
|
var _useState = useState(false),
|
|
44
46
|
_useState2 = _slicedToArray(_useState, 2),
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
var ref = useRef(null);
|
|
48
|
-
var _useState3 = useState(false),
|
|
49
|
-
_useState4 = _slicedToArray(_useState3, 2),
|
|
50
|
-
focused = _useState4[0],
|
|
51
|
-
setFocused = _useState4[1];
|
|
47
|
+
focused = _useState2[0],
|
|
48
|
+
setFocused = _useState2[1];
|
|
52
49
|
|
|
53
50
|
// On initialization, prefer value, then fall back to defaultValue
|
|
54
51
|
var initialValue = value !== undefined ? (_value$toString = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString !== void 0 ? _value$toString : "" : defaultValue !== undefined ? (_defaultValue$toStrin = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()) !== null && _defaultValue$toStrin !== void 0 ? _defaultValue$toStrin : "" : "";
|
|
55
|
-
var
|
|
52
|
+
var _useState3 = useState(initialValue),
|
|
53
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
54
|
+
_value = _useState4[0],
|
|
55
|
+
setValue = _useState4[1];
|
|
56
|
+
var _useState5 = useState(false),
|
|
56
57
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
var _useState7 = useState(initialValue),
|
|
60
|
-
_useState8 = _slicedToArray(_useState7, 2),
|
|
61
|
-
lastValue = _useState8[0],
|
|
62
|
-
setLastValue = _useState8[1];
|
|
63
|
-
var _useState9 = useState(false),
|
|
64
|
-
_useState10 = _slicedToArray(_useState9, 2),
|
|
65
|
-
hasError = _useState10[0],
|
|
66
|
-
setHasError = _useState10[1];
|
|
58
|
+
hasError = _useState6[0],
|
|
59
|
+
setHasError = _useState6[1];
|
|
67
60
|
var prevValueRef = useRef(undefined);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
var getRefRef = useLatestRef(getRef);
|
|
62
|
+
|
|
63
|
+
// 输入法组字期间不往外通知。放 ref 不放 state:它只影响「要不要发通知」,
|
|
64
|
+
// 不参与渲染,而且必须在同一次事件里立刻生效
|
|
65
|
+
var composingRef = useRef(false);
|
|
66
|
+
// 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
|
|
67
|
+
var notifiedRef = useRef(initialValue);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
71
|
+
* 所以它在事件处理里发,不在 effect 里发。
|
|
72
|
+
*
|
|
73
|
+
* 早先是一个 effect 在发:依赖数组里放着 `onChange`,effect 体内又调它,
|
|
74
|
+
* 还用一个 `lastValue` state 记「这个值通知过没有」。两处都是错的——
|
|
75
|
+
* 1. 消费方传内联箭头(`onChange={(v) => setX(v)}`,React 里最常见的写法)时
|
|
76
|
+
* 每次渲染都是新引用,effect 每次渲染都重跑;
|
|
77
|
+
* 2. 「通知过没有」的记账放在 React state 里就**不可靠**:父级用 MobX /
|
|
78
|
+
* `useSyncExternalStore` 这类外部 store 回写 `value` 时,React 会以更高的
|
|
79
|
+
* 同步优先级重渲一次,effect 里排的那次 `setLastValue` 会被这一趟**跳过**,
|
|
80
|
+
* 于是 effect 读到过期的记账、判定「还没通知」,再发一次 → 父级再回写 →
|
|
81
|
+
* 死循环,React 抛 `Maximum update depth exceeded`,输入框被上层
|
|
82
|
+
* ErrorBoundary 整块摘掉。
|
|
83
|
+
*
|
|
84
|
+
* 现在 `onChange` 的引用稳不稳定完全不影响正确性:永远调到最新的那个回调,
|
|
85
|
+
* 且只在真的有新文本时调一次。
|
|
86
|
+
*/
|
|
87
|
+
var notify = function notify(next) {
|
|
88
|
+
if (next === notifiedRef.current) return;
|
|
89
|
+
notifiedRef.current = next;
|
|
90
|
+
// 全是空白等同于空:外部拿到的是 "",但输入框里用户敲的空格照旧留着
|
|
91
|
+
onChange === null || onChange === void 0 || onChange(next.trim() === "" ? "" : next);
|
|
92
|
+
};
|
|
76
93
|
useEffect(function () {
|
|
77
94
|
// Update internal state only when the external value prop actually changes
|
|
78
95
|
if (prevValueRef.current !== value) {
|
|
96
|
+
var _value$toString2;
|
|
79
97
|
prevValueRef.current = value;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
setValue("");
|
|
86
|
-
setLastValue("");
|
|
87
|
-
}
|
|
98
|
+
|
|
99
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
100
|
+
var next = value !== undefined ? (_value$toString2 = value === null || value === void 0 ? void 0 : value.toString()) !== null && _value$toString2 !== void 0 ? _value$toString2 : "" : "";
|
|
101
|
+
notifiedRef.current = next;
|
|
102
|
+
setValue(next);
|
|
88
103
|
}
|
|
89
104
|
}, [value]);
|
|
105
|
+
|
|
106
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑,
|
|
107
|
+
// 消费方在里面 setState 就是一个死循环
|
|
90
108
|
useEffect(function () {
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
var _getRefRef$current;
|
|
110
|
+
(_getRefRef$current = getRefRef.current) === null || _getRefRef$current === void 0 || _getRefRef$current.call(getRefRef, ref.current);
|
|
111
|
+
}, [getRefRef]);
|
|
93
112
|
useEffect(function () {
|
|
94
113
|
var _ref$current2;
|
|
95
114
|
// Use a MutationObserver to watch for error state changes
|
|
@@ -124,19 +143,22 @@ var TextArea = function TextArea(props) {
|
|
|
124
143
|
children: [prefix, /*#__PURE__*/_jsx(AntdTextArea, _objectSpread({
|
|
125
144
|
ref: ref,
|
|
126
145
|
onCompositionStart: function onCompositionStart() {
|
|
127
|
-
|
|
146
|
+
composingRef.current = true;
|
|
128
147
|
},
|
|
129
|
-
onCompositionEnd: function onCompositionEnd() {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
onCompositionEnd: function onCompositionEnd(e) {
|
|
149
|
+
composingRef.current = false;
|
|
150
|
+
// 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
|
|
151
|
+
// 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
|
|
152
|
+
notify(e.currentTarget.value);
|
|
133
153
|
},
|
|
134
154
|
value: _value,
|
|
135
155
|
autoSize: disabled && text ? autoSize || {
|
|
136
156
|
minRows: 1
|
|
137
157
|
} : autoSize,
|
|
138
158
|
onChange: function onChange(e) {
|
|
139
|
-
|
|
159
|
+
var next = e.target.value;
|
|
160
|
+
setValue(next);
|
|
161
|
+
if (!composingRef.current) notify(next);
|
|
140
162
|
},
|
|
141
163
|
onFocus: function onFocus(e) {
|
|
142
164
|
setFocused(true);
|
|
@@ -22,7 +22,7 @@ import TextArea from "./TextArea";
|
|
|
22
22
|
import classNames from "classnames";
|
|
23
23
|
import styles from "./index.module.scss";
|
|
24
24
|
import RangeInput from "./Range";
|
|
25
|
-
import {
|
|
25
|
+
import { useLatestRef } from "../../hooks/useLatestRef";
|
|
26
26
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
27
27
|
var Input = function Input(props) {
|
|
28
28
|
var onChange = props.onChange,
|
|
@@ -39,11 +39,11 @@ var Input = function Input(props) {
|
|
|
39
39
|
tooltip = props.tooltip,
|
|
40
40
|
escapeCharacters = props.escapeCharacters,
|
|
41
41
|
inputConfig = _objectWithoutProperties(props, _excluded);
|
|
42
|
-
var _useState = useState(false),
|
|
43
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
44
|
-
isComposing = _useState2[0],
|
|
45
|
-
setComposing = _useState2[1];
|
|
46
42
|
var ref = useRef(null);
|
|
43
|
+
var getRefRef = useLatestRef(getRef);
|
|
44
|
+
// 输入法组字期间不往外通知。放 ref 不放 state:它只决定「要不要发通知」,
|
|
45
|
+
// 不参与渲染,而且必须在同一次事件里立刻生效
|
|
46
|
+
var composingRef = useRef(false);
|
|
47
47
|
|
|
48
48
|
// Handle escaping: if the value contains characters listed in escapeCharacters, prefix them with an escape
|
|
49
49
|
var escapeValue = useCallback(function (val) {
|
|
@@ -112,27 +112,27 @@ var Input = function Input(props) {
|
|
|
112
112
|
return unescapeValue(rawValue);
|
|
113
113
|
};
|
|
114
114
|
var initialValue = getInitialValue();
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
_value =
|
|
118
|
-
setValue =
|
|
119
|
-
var _useState5 = useState(initialValue),
|
|
120
|
-
_useState6 = _slicedToArray(_useState5, 2),
|
|
121
|
-
lastValue = _useState6[0],
|
|
122
|
-
setLastValue = _useState6[1];
|
|
115
|
+
var _useState = useState(initialValue),
|
|
116
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
117
|
+
_value = _useState2[0],
|
|
118
|
+
setValue = _useState2[1];
|
|
123
119
|
var prevValueRef = useRef(undefined);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
// 最近一次通知出去的原文(未转义)。只用来去掉 compositionend 与 input
|
|
121
|
+
// 两个事件的重复通知
|
|
122
|
+
var notifiedRef = useRef(initialValue);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
126
|
+
* 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
|
|
127
|
+
* 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
|
|
128
|
+
* 会让传内联箭头的消费方死循环。
|
|
129
|
+
*/
|
|
130
|
+
var notify = function notify(next) {
|
|
131
|
+
if (next === notifiedRef.current) return;
|
|
132
|
+
notifiedRef.current = next;
|
|
133
|
+
// 全是空白等同于空;escapeCharacters 命中时对外给转义后的值
|
|
134
|
+
onChange === null || onChange === void 0 || onChange(escapeValue(next.trim() === "" ? "" : next));
|
|
135
|
+
};
|
|
136
136
|
useEffect(function () {
|
|
137
137
|
// Update internal state only when the external value prop actually changes
|
|
138
138
|
if (prevValueRef.current !== value) {
|
|
@@ -141,18 +141,22 @@ var Input = function Input(props) {
|
|
|
141
141
|
var rawValue = typeof value === "number" ? "".concat(value) : value === null || value === void 0 ? void 0 : value.toString();
|
|
142
142
|
// Unescape before displaying
|
|
143
143
|
var newValue = unescapeValue(rawValue);
|
|
144
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
145
|
+
notifiedRef.current = newValue;
|
|
144
146
|
setValue(newValue);
|
|
145
|
-
setLastValue(newValue);
|
|
146
147
|
} else {
|
|
147
148
|
// Clear only on initialization or when explicitly set to undefined externally
|
|
149
|
+
notifiedRef.current = "";
|
|
148
150
|
setValue("");
|
|
149
|
-
setLastValue("");
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
}, [value, unescapeValue]);
|
|
154
|
+
|
|
155
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
|
|
153
156
|
useEffect(function () {
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
var _getRefRef$current;
|
|
158
|
+
(_getRefRef$current = getRefRef.current) === null || _getRefRef$current === void 0 || _getRefRef$current.call(getRefRef, ref.current);
|
|
159
|
+
}, [getRefRef]);
|
|
156
160
|
return /*#__PURE__*/_jsx(Tooltip, _objectSpread(_objectSpread({
|
|
157
161
|
placement: "topLeft"
|
|
158
162
|
}, tooltip), {}, {
|
|
@@ -161,12 +165,13 @@ var Input = function Input(props) {
|
|
|
161
165
|
disabled: disabled,
|
|
162
166
|
placeholder: disabled ? "" : placeholder,
|
|
163
167
|
onCompositionStart: function onCompositionStart() {
|
|
164
|
-
|
|
168
|
+
composingRef.current = true;
|
|
165
169
|
},
|
|
166
|
-
onCompositionEnd: function onCompositionEnd() {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
onCompositionEnd: function onCompositionEnd(e) {
|
|
171
|
+
composingRef.current = false;
|
|
172
|
+
// 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
|
|
173
|
+
// 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
|
|
174
|
+
notify(unescapeValue(e.currentTarget.value));
|
|
170
175
|
},
|
|
171
176
|
value: _value,
|
|
172
177
|
onChange: function onChange(e) {
|
|
@@ -174,6 +179,7 @@ var Input = function Input(props) {
|
|
|
174
179
|
var inputValue = e.target.value;
|
|
175
180
|
var unescapedInput = unescapeValue(inputValue);
|
|
176
181
|
setValue(unescapedInput);
|
|
182
|
+
if (!composingRef.current) notify(unescapedInput);
|
|
177
183
|
},
|
|
178
184
|
className: classNames(styles.antdInput, className),
|
|
179
185
|
type: type,
|
|
@@ -14,6 +14,7 @@ export function useSelectComposition(_ref) {
|
|
|
14
14
|
_useState2 = _slicedToArray(_useState, 2),
|
|
15
15
|
isComposing = _useState2[0],
|
|
16
16
|
setComposing = _useState2[1];
|
|
17
|
+
var hasOnSearch = !!onSearch;
|
|
17
18
|
useEffect(function () {
|
|
18
19
|
var compositionend = function compositionend() {
|
|
19
20
|
setComposing(false);
|
|
@@ -21,7 +22,7 @@ export function useSelectComposition(_ref) {
|
|
|
21
22
|
var compositionstart = function compositionstart() {
|
|
22
23
|
setComposing(true);
|
|
23
24
|
};
|
|
24
|
-
if (
|
|
25
|
+
if (hasOnSearch) {
|
|
25
26
|
window.addEventListener("compositionstart", compositionstart);
|
|
26
27
|
window.addEventListener("compositionend", compositionend);
|
|
27
28
|
}
|
|
@@ -29,7 +30,10 @@ export function useSelectComposition(_ref) {
|
|
|
29
30
|
window.removeEventListener("compositionstart", compositionstart);
|
|
30
31
|
window.removeEventListener("compositionend", compositionend);
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
+
// onSearch 在这里只被当作「要不要监听输入法事件」的真值判断,effect 体从不调它。
|
|
34
|
+
// 直接进依赖数组的话,消费方传内联箭头就会每渲染一次拆一次、装一次 window 监听;
|
|
35
|
+
// 只取「有没有传」这个布尔量,从无到有时照样会重新注册。
|
|
36
|
+
}, [hasOnSearch]);
|
|
33
37
|
return {
|
|
34
38
|
isComposing: isComposing
|
|
35
39
|
};
|
|
@@ -5,6 +5,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
|
|
|
5
5
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
6
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
7
|
import { useEffect, useState } from "react";
|
|
8
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
8
9
|
/**
|
|
9
10
|
* Manage input options (when there are no matching options)
|
|
10
11
|
*/
|
|
@@ -18,11 +19,16 @@ export function useSelectInputOptions(_ref) {
|
|
|
18
19
|
_useState2 = _slicedToArray(_useState, 2),
|
|
19
20
|
inputOptions = _useState2[0],
|
|
20
21
|
setInputOptions = _useState2[1];
|
|
22
|
+
var onSearchRef = useLatestRef(onSearch);
|
|
23
|
+
|
|
24
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
25
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
21
26
|
useEffect(function () {
|
|
22
27
|
if (!isComposing) {
|
|
23
|
-
|
|
28
|
+
var _onSearchRef$current;
|
|
29
|
+
(_onSearchRef$current = onSearchRef.current) === null || _onSearchRef$current === void 0 || _onSearchRef$current.call(onSearchRef, searchValue);
|
|
24
30
|
}
|
|
25
|
-
}, [inputOptions, isComposing,
|
|
31
|
+
}, [inputOptions, isComposing, onSearchRef, optionsLength, searchValue]);
|
|
26
32
|
return {
|
|
27
33
|
inputOptions: inputOptions,
|
|
28
34
|
setInputOptions: setInputOptions
|
|
@@ -14,7 +14,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
14
14
|
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
15
15
|
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
16
16
|
import { Slider as AntdSlider } from "antd";
|
|
17
|
-
import React, { useEffect, useState } from "react";
|
|
17
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
18
18
|
import classNames from "classnames";
|
|
19
19
|
import styles from "./index.module.scss";
|
|
20
20
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -29,28 +29,37 @@ var Slider = function Slider(props) {
|
|
|
29
29
|
_useState2 = _slicedToArray(_useState, 2),
|
|
30
30
|
_value = _useState2[0],
|
|
31
31
|
setValue = _useState2[1];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// 最近一次通知出去的值,代替原来那个用 state 记账的写法
|
|
33
|
+
var notifiedRef = useRef(value);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `onChange` 是「用户拖了滑块」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
37
|
+
* 所以在事件处理里发,不在 effect 里发。详见 `Input/TextArea/index.tsx` 里那段
|
|
38
|
+
* 说明——旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
|
|
39
|
+
* 会让传内联箭头的消费方死循环。
|
|
40
|
+
*/
|
|
41
|
+
var handleChange = function handleChange(next) {
|
|
42
|
+
setValue(next);
|
|
43
|
+
if (next === notifiedRef.current) return;
|
|
44
|
+
notifiedRef.current = next;
|
|
45
|
+
onChange === null || onChange === void 0 || onChange(next);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// 外部值(`value` / `topValue`)变了才回写,不跟着通知走
|
|
36
49
|
useEffect(function () {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
var next = topValue || value;
|
|
51
|
+
if (next !== notifiedRef.current) {
|
|
52
|
+
notifiedRef.current = next;
|
|
53
|
+
setValue(next);
|
|
40
54
|
}
|
|
41
|
-
}, [
|
|
42
|
-
useEffect(function () {
|
|
43
|
-
if ((topValue || value) !== lastValue) {
|
|
44
|
-
setValue(topValue || value);
|
|
45
|
-
}
|
|
46
|
-
}, [lastValue, topValue, value]);
|
|
55
|
+
}, [topValue, value]);
|
|
47
56
|
return /*#__PURE__*/_jsx(AntdSlider, _objectSpread({
|
|
48
57
|
className: classNames([styles.slider, className]),
|
|
49
58
|
tooltip: {
|
|
50
59
|
open: false
|
|
51
60
|
},
|
|
52
61
|
value: _value,
|
|
53
|
-
onChange:
|
|
62
|
+
onChange: handleChange
|
|
54
63
|
}, sliderConfig));
|
|
55
64
|
};
|
|
56
65
|
export default Slider;
|
|
@@ -7,6 +7,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
|
|
|
7
7
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
8
8
|
import { useMutationObserver } from "ahooks";
|
|
9
9
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
10
11
|
var DEFAULT_INTERVAL = 2000;
|
|
11
12
|
var DEFAULT_SPEED = 25;
|
|
12
13
|
var SMOOTH_SCROLL_TICK = 10;
|
|
@@ -29,6 +30,13 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
29
30
|
autoScrollLoopMode = _props$autoScrollLoop2 === void 0 ? "reset" : _props$autoScrollLoop2,
|
|
30
31
|
_props$autoScrollingO = props.autoScrollingOffset,
|
|
31
32
|
autoScrollingOffset = _props$autoScrollingO === void 0 ? 0 : _props$autoScrollingO;
|
|
33
|
+
|
|
34
|
+
// onAutoScrollEndAdd 在这里身兼两职:既是「有没有加载更多」的开关,又是真正去加载的动作。
|
|
35
|
+
// 整个 rAF 循环(loop → 依赖它 → 启停 effect 依赖 loop)都挂在它的引用上,
|
|
36
|
+
// 消费方传内联箭头就会每渲染一次重启一次滚动循环 —— 表格滚到一半被打回起点。
|
|
37
|
+
// 拆成两半:开关看布尔量(从无到有传入时仍会正确接上加载更多),动作取 ref 里的最新引用。
|
|
38
|
+
var onAutoScrollEndAddRef = useLatestRef(onAutoScrollEndAdd);
|
|
39
|
+
var hasOnAutoScrollEndAdd = !!onAutoScrollEndAdd;
|
|
32
40
|
var validSpeed = autoScrollingSpeed > 0 ? autoScrollingSpeed : DEFAULT_SPEED;
|
|
33
41
|
var smoothStep = validSpeed * SMOOTH_SCROLL_TICK / 1000;
|
|
34
42
|
var _useState = useState(false),
|
|
@@ -260,11 +268,12 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
260
268
|
}, SMOOTH_SCROLL_TICK);
|
|
261
269
|
}, [bindHover, checkSeamlessReset, clearSmoothTimer, getBody, interval, isAtBottom, isBodyScrollable, isSeamless, smoothStep]);
|
|
262
270
|
var runLoadMore = useCallback(function () {
|
|
263
|
-
|
|
271
|
+
var loadMore = onAutoScrollEndAddRef.current;
|
|
272
|
+
if (!loadMore || pendingLoadMoreRef.current) return;
|
|
264
273
|
pendingLoadMoreRef.current = true;
|
|
265
274
|
phaseRef.current = "loading";
|
|
266
275
|
var token = ++loadTokenRef.current;
|
|
267
|
-
|
|
276
|
+
loadMore().then(function (hasMore) {
|
|
268
277
|
if (token !== loadTokenRef.current) return;
|
|
269
278
|
var currentBody = getBody();
|
|
270
279
|
if (hasMore) {
|
|
@@ -284,7 +293,7 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
284
293
|
if (token !== loadTokenRef.current) return;
|
|
285
294
|
pendingLoadMoreRef.current = false;
|
|
286
295
|
});
|
|
287
|
-
}, [autoScrollLoop, getBody,
|
|
296
|
+
}, [autoScrollLoop, getBody, onAutoScrollEndAddRef]);
|
|
288
297
|
var loop = useCallback(function () {
|
|
289
298
|
rafRef.current = requestAnimationFrame(loop);
|
|
290
299
|
if (!autoScrolling || !ready || !(dataSource !== null && dataSource !== void 0 && dataSource.length) || !(ref !== null && ref !== void 0 && ref.current)) {
|
|
@@ -316,7 +325,7 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
316
325
|
// In seamless mode the scroll never actually reaches the bottom; just keep scrolling
|
|
317
326
|
phaseRef.current = "idle";
|
|
318
327
|
} else if (isAtBottom(body)) {
|
|
319
|
-
if (
|
|
328
|
+
if (hasOnAutoScrollEndAdd) {
|
|
320
329
|
runLoadMore();
|
|
321
330
|
} else {
|
|
322
331
|
if (autoScrollLoop) {
|
|
@@ -362,7 +371,7 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
362
371
|
return;
|
|
363
372
|
}
|
|
364
373
|
startSmoothStep(body, targetTop);
|
|
365
|
-
}, [autoScrollMode, autoScrollLoop, autoScrolling, bindHover, checkSeamlessReset, dataSource, getBody, getRowHeight, interval, isAtBottom, isBodyScrollable, isSeamless,
|
|
374
|
+
}, [autoScrollMode, autoScrollLoop, autoScrolling, bindHover, checkSeamlessReset, dataSource, getBody, getRowHeight, interval, isAtBottom, isBodyScrollable, isSeamless, hasOnAutoScrollEndAdd, ready, ref, runLoadMore, setupSeamlessClones, startSmoothStep]);
|
|
366
375
|
useEffect(function () {
|
|
367
376
|
if (!autoScrolling || !ready || !(dataSource !== null && dataSource !== void 0 && dataSource.length) || !(ref !== null && ref !== void 0 && ref.current)) {
|
|
368
377
|
cleanup();
|
|
@@ -387,7 +396,7 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
387
396
|
cleanupClones();
|
|
388
397
|
return;
|
|
389
398
|
}
|
|
390
|
-
if (
|
|
399
|
+
if (hasOnAutoScrollEndAdd) {
|
|
391
400
|
// In load-more mode, if scrolling had stopped, resume auto-scrolling when new data arrives
|
|
392
401
|
if (phaseRef.current === "stopped") {
|
|
393
402
|
phaseRef.current = autoScrollMode === "row" ? "wait" : "idle";
|
|
@@ -400,7 +409,7 @@ var useAutoScrolling = function useAutoScrolling(props) {
|
|
|
400
409
|
phaseRef.current = autoScrollMode === "row" ? "wait" : "idle";
|
|
401
410
|
waitUntilRef.current = autoScrollMode === "row" ? performance.now() + interval : 0;
|
|
402
411
|
}
|
|
403
|
-
}, [autoScrollMode, cleanupClones, dataSource, getBody, interval, isBodyScrollable, isSeamless,
|
|
412
|
+
}, [autoScrollMode, cleanupClones, dataSource, getBody, interval, isBodyScrollable, isSeamless, hasOnAutoScrollEndAdd]);
|
|
404
413
|
useEffect(function () {
|
|
405
414
|
if (!autoScrolling || !(ref !== null && ref !== void 0 && ref.current)) return;
|
|
406
415
|
var resizeRaf = null;
|
|
@@ -7,6 +7,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
7
7
|
import { useEffect, useState } from "react";
|
|
8
8
|
import { deepCopy, Equal } from "hsu-utils";
|
|
9
9
|
import { extractFileUrl } from "../_utils";
|
|
10
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
10
11
|
/**
|
|
11
12
|
* Manage the upload file list state
|
|
12
13
|
*/
|
|
@@ -22,6 +23,10 @@ export function useUploadFileList(_ref) {
|
|
|
22
23
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
23
24
|
lastFileList = _useState4[0],
|
|
24
25
|
setLastFileList = _useState4[1];
|
|
26
|
+
var onChangeRef = useLatestRef(onChange);
|
|
27
|
+
|
|
28
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
29
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
25
30
|
useEffect(function () {
|
|
26
31
|
if (rmFile) {
|
|
27
32
|
var file = _fileList.find(function (item) {
|
|
@@ -33,14 +38,15 @@ export function useUploadFileList(_ref) {
|
|
|
33
38
|
if (file) {
|
|
34
39
|
setFilelist(filteredList);
|
|
35
40
|
setTimeout(function () {
|
|
36
|
-
|
|
41
|
+
var _onChangeRef$current;
|
|
42
|
+
(_onChangeRef$current = onChangeRef.current) === null || _onChangeRef$current === void 0 || _onChangeRef$current.call(onChangeRef, {
|
|
37
43
|
file: file,
|
|
38
44
|
fileList: filteredList
|
|
39
45
|
});
|
|
40
46
|
}, 100);
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
|
-
}, [_fileList,
|
|
49
|
+
}, [_fileList, onChangeRef, rmFile]);
|
|
44
50
|
useEffect(function () {
|
|
45
51
|
if (!Equal.ObjEqual(fileList !== null && fileList !== void 0 ? fileList : [], lastFileList)) {
|
|
46
52
|
if (fileList !== null && fileList !== void 0 && fileList.length) {
|
|
@@ -6,6 +6,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
|
|
|
6
6
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
7
|
import { useEffect, useRef, useState } from "react";
|
|
8
8
|
import { deepCopy, Equal } from "hsu-utils";
|
|
9
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
9
10
|
/**
|
|
10
11
|
* Manage upload and download operations
|
|
11
12
|
*/
|
|
@@ -27,6 +28,7 @@ export function useUploadOperations(_ref) {
|
|
|
27
28
|
downloading: {},
|
|
28
29
|
uploadingList: {}
|
|
29
30
|
});
|
|
31
|
+
var onUploadingListRef = useLatestRef(onUploadingList);
|
|
30
32
|
|
|
31
33
|
// Sync to the ref on update
|
|
32
34
|
useEffect(function () {
|
|
@@ -35,12 +37,16 @@ export function useUploadOperations(_ref) {
|
|
|
35
37
|
useEffect(function () {
|
|
36
38
|
operationsRef.current.uploadingList = uploadingList;
|
|
37
39
|
}, [uploadingList]);
|
|
40
|
+
|
|
41
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
42
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
38
43
|
useEffect(function () {
|
|
39
44
|
if (!Equal.ObjEqual(uploadingList, lastUploadList)) {
|
|
40
|
-
|
|
45
|
+
var _onUploadingListRef$c;
|
|
46
|
+
(_onUploadingListRef$c = onUploadingListRef.current) === null || _onUploadingListRef$c === void 0 || _onUploadingListRef$c.call(onUploadingListRef, deepCopy(uploadingList));
|
|
41
47
|
setLastUploadList(deepCopy(uploadingList));
|
|
42
48
|
}
|
|
43
|
-
}, [uploadingList, lastUploadList,
|
|
49
|
+
}, [uploadingList, lastUploadList, onUploadingListRef]);
|
|
44
50
|
|
|
45
51
|
// Cleanup function
|
|
46
52
|
useEffect(function () {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* 把一个每次渲染都可能换引用的值(通常是回调 prop)装进一个**引用恒定**的 ref。
|
|
4
|
+
*
|
|
5
|
+
* 用途只有一个:让 effect 不必把回调放进依赖数组。
|
|
6
|
+
* 回调放进依赖数组、effect 体内又调它,是一条会让消费方死循环的写法——
|
|
7
|
+
* 消费方传内联箭头(`onChange={(v) => setX(v)}`,React 里最常见的写法)时
|
|
8
|
+
* 每次渲染都是新引用 → effect 重跑 → 调回调 → 父级 setState → 再渲染 →
|
|
9
|
+
* 又是新引用 …… 直到 React 抛 `Maximum update depth exceeded`。
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const onDoneRef = useLatestRef(onDone);
|
|
13
|
+
* useEffect(() => {
|
|
14
|
+
* const t = setTimeout(() => onDoneRef.current?.(), 1000);
|
|
15
|
+
* return () => clearTimeout(t);
|
|
16
|
+
* }, [onDoneRef]); // 只在挂载时跑一次,且永远调到最新的 onDone
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 注意:渲染期间就地赋值。React 官方对 ref 的约束是「不要在渲染中**读**可变值」,
|
|
20
|
+
* 写入最新的 props 是 useEffectEvent 落地前的通行做法(ahooks 的 `useLatest`、
|
|
21
|
+
* React 文档里的 `useEvent` polyfill 都是这么写的)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function useLatestRef<T>(value: T): import("react").MutableRefObject<T>;
|
|
24
|
+
export default useLatestRef;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 把一个每次渲染都可能换引用的值(通常是回调 prop)装进一个**引用恒定**的 ref。
|
|
5
|
+
*
|
|
6
|
+
* 用途只有一个:让 effect 不必把回调放进依赖数组。
|
|
7
|
+
* 回调放进依赖数组、effect 体内又调它,是一条会让消费方死循环的写法——
|
|
8
|
+
* 消费方传内联箭头(`onChange={(v) => setX(v)}`,React 里最常见的写法)时
|
|
9
|
+
* 每次渲染都是新引用 → effect 重跑 → 调回调 → 父级 setState → 再渲染 →
|
|
10
|
+
* 又是新引用 …… 直到 React 抛 `Maximum update depth exceeded`。
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const onDoneRef = useLatestRef(onDone);
|
|
14
|
+
* useEffect(() => {
|
|
15
|
+
* const t = setTimeout(() => onDoneRef.current?.(), 1000);
|
|
16
|
+
* return () => clearTimeout(t);
|
|
17
|
+
* }, [onDoneRef]); // 只在挂载时跑一次,且永远调到最新的 onDone
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* 注意:渲染期间就地赋值。React 官方对 ref 的约束是「不要在渲染中**读**可变值」,
|
|
21
|
+
* 写入最新的 props 是 useEffectEvent 落地前的通行做法(ahooks 的 `useLatest`、
|
|
22
|
+
* React 文档里的 `useEvent` polyfill 都是这么写的)。
|
|
23
|
+
*/
|
|
24
|
+
export function useLatestRef(value) {
|
|
25
|
+
var ref = useRef(value);
|
|
26
|
+
ref.current = value;
|
|
27
|
+
return ref;
|
|
28
|
+
}
|
|
29
|
+
export default useLatestRef;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
+
import { useLatestRef } from "../../../hooks/useLatestRef";
|
|
2
3
|
import { useLocation } from "react-router";
|
|
3
4
|
/**
|
|
4
5
|
* Handle the top-level-only menu logic
|
|
@@ -10,16 +11,22 @@ export var useOnlyLvOneMenu = function useOnlyLvOneMenu(_ref) {
|
|
|
10
11
|
setOpenkeys = _ref.setOpenkeys,
|
|
11
12
|
setMenuKey = _ref.setMenuKey;
|
|
12
13
|
var location = useLocation();
|
|
14
|
+
// getCurrChildItems 是 effect 体里发出去的通知,本身进依赖数组是这一类死循环的标准配方:
|
|
15
|
+
// 消费方传内联箭头 → 每次渲染新引用 → effect 重跑 → setOpenkeys([item.key]) 每次都是
|
|
16
|
+
// 新数组必定触发重渲染 → 又是新引用 …… 直到 React 抛 Maximum update depth exceeded。
|
|
17
|
+
// 通知取 ref 里的最新引用,依赖数组只留真正的输入。
|
|
18
|
+
var getCurrChildItemsRef = useLatestRef(getCurrChildItems);
|
|
13
19
|
useEffect(function () {
|
|
14
20
|
if (onlyLvOneMenu) {
|
|
15
21
|
var item = items.find(function (i) {
|
|
16
22
|
return i.key === "/" + location.pathname.split("/").filter(Boolean)[0];
|
|
17
23
|
});
|
|
18
24
|
if (item) {
|
|
19
|
-
|
|
25
|
+
var _getCurrChildItemsRef;
|
|
26
|
+
(_getCurrChildItemsRef = getCurrChildItemsRef.current) === null || _getCurrChildItemsRef === void 0 || _getCurrChildItemsRef.call(getCurrChildItemsRef, item.children || []);
|
|
20
27
|
setOpenkeys([item.key]);
|
|
21
28
|
setMenuKey(item.key);
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
|
-
}, [items,
|
|
31
|
+
}, [items, getCurrChildItemsRef, location, onlyLvOneMenu, setOpenkeys, setMenuKey]);
|
|
25
32
|
};
|