@mi-avalon/libs 0.0.6 → 0.0.7
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/components/ItemsRow/index.d.ts +43 -0
- package/dist/components/MBreadcrumb/index.d.ts +18 -0
- package/dist/components/MDescriptions/index.d.ts +65 -0
- package/dist/components/MForm/MFormItemConst.d.ts +27 -0
- package/dist/components/MForm/index.d.ts +10 -0
- package/dist/components/MForm/type.d.ts +184 -0
- package/dist/components/MSearch/index.d.ts +27 -0
- package/dist/components/MTable/index.d.ts +12 -0
- package/dist/components/MiModal/index.d.ts +16 -0
- package/dist/components/ThemeContext/CompThemeProvider.d.ts +9 -0
- package/dist/components/ThemeContext/index.d.ts +8 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/useFuncRequest.d.ts +16 -0
- package/dist/hooks/useInterval.d.ts +8 -0
- package/dist/hooks/usePagination.d.ts +42 -0
- package/dist/hooks/useQuery.d.ts +3 -0
- package/dist/hooks/useReactive.d.ts +2 -0
- package/dist/hooks/useTimeout.d.ts +8 -0
- package/dist/hooks/useVirtualList.d.ts +74 -0
- package/dist/index.d.ts +2 -0
- package/dist/libs.cjs.development.js +1534 -62
- package/dist/libs.cjs.development.js.map +1 -1
- package/dist/libs.cjs.production.min.js +1 -1
- package/dist/libs.cjs.production.min.js.map +1 -1
- package/dist/libs.esm.js +1518 -63
- package/dist/libs.esm.js.map +1 -1
- package/package.json +9 -4
- package/src/components/ItemsRow/index.scss +18 -0
- package/src/components/ItemsRow/index.tsx +85 -0
- package/src/components/MBreadcrumb/index.scss +82 -0
- package/src/components/MBreadcrumb/index.tsx +93 -0
- package/src/components/MDescriptions/index.tsx +131 -0
- package/src/components/MForm/MFormItemConst.tsx +206 -0
- package/src/components/MForm/index.scss +13 -0
- package/src/components/MForm/index.tsx +97 -0
- package/src/components/MForm/type.ts +229 -0
- package/src/components/MSearch/index.scss +63 -0
- package/src/components/MSearch/index.tsx +173 -0
- package/src/components/MTable/index.tsx +22 -0
- package/src/components/MiModal/index.tsx +106 -0
- package/src/components/ThemeContext/CompThemeProvider.tsx +19 -0
- package/src/components/ThemeContext/index.ts +20 -0
- package/src/components/index.ts +9 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useFuncRequest.ts +100 -0
- package/src/hooks/useInterval.ts +50 -0
- package/src/hooks/usePagination.ts +184 -0
- package/src/hooks/useQuery.ts +6 -0
- package/src/hooks/useReactive.ts +26 -0
- package/src/hooks/useTimeout.ts +50 -0
- package/src/hooks/useVirtualList.ts +209 -0
- package/src/index.tsx +2 -2
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
|
|
4
|
+
|
|
5
|
+
export type UseFuncRequestOptions<
|
|
6
|
+
T extends (...args: any[]) => Promise<any>
|
|
7
|
+
> = {
|
|
8
|
+
// 自动执行时的参数
|
|
9
|
+
autoRunArgs?: Parameters<T>;
|
|
10
|
+
// 开始执行前的回调函数 false 不执行 true 执行
|
|
11
|
+
onBefore?: (...args: Parameters<T>) => boolean | Promise<boolean>;
|
|
12
|
+
// 执行成功后的回调函数
|
|
13
|
+
onSuccess?: (data: UnwrapPromise<ReturnType<T>>) => void | Promise<void>;
|
|
14
|
+
// 执行失败后的回调函数
|
|
15
|
+
onError?: (error: Error) => void | Promise<void>;
|
|
16
|
+
// 结束后的回调函数
|
|
17
|
+
onFinally?: () => void | Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function useFuncRequest<T extends (...args: any[]) => Promise<any>>(
|
|
21
|
+
// 异步函数
|
|
22
|
+
asyncFunc: T,
|
|
23
|
+
// 配置项
|
|
24
|
+
options?: UseFuncRequestOptions<T>
|
|
25
|
+
) {
|
|
26
|
+
const [loading, setLoading] = useState(false);
|
|
27
|
+
const [error, setError] = useState<Error | null>(null);
|
|
28
|
+
const [data, setData] = useState<UnwrapPromise<ReturnType<T>> | null>(null);
|
|
29
|
+
const abortControllerRef = useRef<AbortController>(undefined);
|
|
30
|
+
const requestIdRef = useRef(0);
|
|
31
|
+
|
|
32
|
+
const cancel = () => {
|
|
33
|
+
abortControllerRef.current?.abort();
|
|
34
|
+
setLoading(false);
|
|
35
|
+
setError(null);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const run = async (...args: Parameters<T>) => {
|
|
39
|
+
const currentRequestId = ++requestIdRef.current;
|
|
40
|
+
cancel();
|
|
41
|
+
|
|
42
|
+
const abortController = new AbortController();
|
|
43
|
+
abortControllerRef.current = abortController;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
setLoading(true);
|
|
47
|
+
setError(null);
|
|
48
|
+
if (options?.onBefore) {
|
|
49
|
+
const beforeRes = await options?.onBefore?.(...args);
|
|
50
|
+
if (!beforeRes) {
|
|
51
|
+
cancel();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 安全传递 AbortSignal
|
|
57
|
+
const funcArgs =
|
|
58
|
+
args.length >= asyncFunc.length
|
|
59
|
+
? args
|
|
60
|
+
: [...args, { signal: abortController.signal }];
|
|
61
|
+
|
|
62
|
+
const res = await asyncFunc(...(funcArgs as Parameters<T>));
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
!abortController.signal.aborted &&
|
|
66
|
+
currentRequestId === requestIdRef.current
|
|
67
|
+
) {
|
|
68
|
+
setData(res);
|
|
69
|
+
await options?.onSuccess?.(res);
|
|
70
|
+
}
|
|
71
|
+
return res;
|
|
72
|
+
} catch (err) {
|
|
73
|
+
if (
|
|
74
|
+
!abortController.signal.aborted &&
|
|
75
|
+
currentRequestId === requestIdRef.current
|
|
76
|
+
) {
|
|
77
|
+
setError(err as Error);
|
|
78
|
+
await options?.onError?.(err as Error);
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
} finally {
|
|
82
|
+
if (
|
|
83
|
+
!abortController.signal.aborted &&
|
|
84
|
+
currentRequestId === requestIdRef.current
|
|
85
|
+
) {
|
|
86
|
+
setLoading(false);
|
|
87
|
+
await options?.onFinally?.();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (options?.autoRunArgs) {
|
|
94
|
+
run(...options.autoRunArgs).catch(() => {});
|
|
95
|
+
}
|
|
96
|
+
return cancel;
|
|
97
|
+
}, [JSON.stringify(options?.autoRunArgs)]);
|
|
98
|
+
|
|
99
|
+
return { run, cancel, loading, error, data };
|
|
100
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
type Callback = () => void;
|
|
4
|
+
|
|
5
|
+
interface UseTimeoutReturn {
|
|
6
|
+
start: Callback;
|
|
7
|
+
isRunning: boolean;
|
|
8
|
+
clear: Callback;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function useInterval(
|
|
12
|
+
callback: Callback,
|
|
13
|
+
delay: number | null,
|
|
14
|
+
immediate: boolean = false
|
|
15
|
+
): UseTimeoutReturn {
|
|
16
|
+
const timerRef = useRef<ReturnType<typeof setInterval>>(undefined);
|
|
17
|
+
const savedCallback = useRef(callback);
|
|
18
|
+
|
|
19
|
+
// Update callback ref if callback changes
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
savedCallback.current = callback;
|
|
22
|
+
}, [callback]);
|
|
23
|
+
|
|
24
|
+
const clear = useCallback(() => {
|
|
25
|
+
if (timerRef.current) {
|
|
26
|
+
clearInterval(timerRef.current);
|
|
27
|
+
timerRef.current = undefined;
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const start = useCallback(() => {
|
|
32
|
+
clear();
|
|
33
|
+
if (delay !== null && delay !== undefined) {
|
|
34
|
+
timerRef.current = setInterval(() => savedCallback.current(), delay);
|
|
35
|
+
}
|
|
36
|
+
}, [delay, clear]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (immediate) {
|
|
40
|
+
savedCallback.current();
|
|
41
|
+
}
|
|
42
|
+
start();
|
|
43
|
+
|
|
44
|
+
return clear;
|
|
45
|
+
}, [delay, start, clear, immediate]);
|
|
46
|
+
|
|
47
|
+
return { start, clear, isRunning: !!timerRef.current };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { useInterval };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { debounce } from 'lodash';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
interface IPaginationProps {
|
|
5
|
+
current: number;
|
|
6
|
+
pageSize: number;
|
|
7
|
+
total: number;
|
|
8
|
+
onChange: (page: number, pageSize: number) => Promise<void>;
|
|
9
|
+
onShowSizeChange: (size: number, current: number) => Promise<void>;
|
|
10
|
+
showTotal?: (t: number) => React.ReactNode;
|
|
11
|
+
showQuickJumper?: boolean;
|
|
12
|
+
showSizeChanger?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PaginationResult<T> {
|
|
16
|
+
tableProps: {
|
|
17
|
+
loading?: boolean;
|
|
18
|
+
dataSource: T[];
|
|
19
|
+
};
|
|
20
|
+
loading?: boolean;
|
|
21
|
+
dataSource: T[];
|
|
22
|
+
paginationProps: IPaginationProps;
|
|
23
|
+
isFirstComplete: boolean;
|
|
24
|
+
refresh: (resetPage?: boolean) => Promise<void>;
|
|
25
|
+
debounceRefresh: (resetPage?: boolean) => void;
|
|
26
|
+
setDataSource: (data: T[]) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface IServerParams {
|
|
30
|
+
offset: number;
|
|
31
|
+
limit: number;
|
|
32
|
+
current: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type IInfoBack<T> = { dataSource: T[]; total: number };
|
|
36
|
+
|
|
37
|
+
type IInfoServer<T> = (params: IServerParams) => IInfoBack<T> | Promise<IInfoBack<T>>;
|
|
38
|
+
|
|
39
|
+
interface IInfoOption<T> {
|
|
40
|
+
isReady?: boolean;
|
|
41
|
+
dataSource?: T[];
|
|
42
|
+
current?: number;
|
|
43
|
+
pageSize?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const usePagination = <T>(
|
|
47
|
+
server: IInfoServer<T>,
|
|
48
|
+
deps?: any[], // 依赖条件 数据更新默认执行server
|
|
49
|
+
option?: IInfoOption<T>,
|
|
50
|
+
): PaginationResult<T> => {
|
|
51
|
+
const {
|
|
52
|
+
isReady = true,
|
|
53
|
+
dataSource: propDataSource = [],
|
|
54
|
+
current: propCurrent = 1,
|
|
55
|
+
pageSize: propPageSize = 10,
|
|
56
|
+
} = option || {};
|
|
57
|
+
|
|
58
|
+
const [current, setCurrent] = useState(propCurrent);
|
|
59
|
+
const [pageSize, setPageSize] = useState(propPageSize);
|
|
60
|
+
const [dataSource, setDataSource] = useState<T[]>(propDataSource);
|
|
61
|
+
const [total, setTotal] = useState(0);
|
|
62
|
+
const [loading, setLoading] = useState(false);
|
|
63
|
+
const [isFirstComplete, setIsFirstComplete] = useState(false);
|
|
64
|
+
const currentRef = useRef(current);
|
|
65
|
+
const pageSizeRef = useRef(pageSize);
|
|
66
|
+
|
|
67
|
+
// 在状态更新时同步更新ref
|
|
68
|
+
const setCurrentAndRef = (val: number) => {
|
|
69
|
+
setCurrent(val);
|
|
70
|
+
currentRef.current = val;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const setPageSizeAndRef = (val: number) => {
|
|
74
|
+
setPageSize(val);
|
|
75
|
+
pageSizeRef.current = val;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// 计数器
|
|
79
|
+
const seq = useRef(0);
|
|
80
|
+
|
|
81
|
+
const doSearch = async () => {
|
|
82
|
+
if (!isReady) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let _current = currentRef.current;
|
|
86
|
+
let _pageSize = pageSizeRef.current;
|
|
87
|
+
setLoading(true);
|
|
88
|
+
|
|
89
|
+
seq.current++;
|
|
90
|
+
const _seq = seq.current;
|
|
91
|
+
try {
|
|
92
|
+
// 发送请求
|
|
93
|
+
let offset = Math.round((_current - 1) * _pageSize);
|
|
94
|
+
if (offset < 0) {
|
|
95
|
+
offset = 0;
|
|
96
|
+
}
|
|
97
|
+
if (_pageSize < 1) {
|
|
98
|
+
_pageSize = 1;
|
|
99
|
+
}
|
|
100
|
+
let { dataSource: data_source, total: _total } = await server({
|
|
101
|
+
limit: _pageSize,
|
|
102
|
+
offset,
|
|
103
|
+
current: _current,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (_seq !== seq.current) return;
|
|
107
|
+
if (pageSize * (_current - 1) >= _total && _current !== 1) {
|
|
108
|
+
_current = 1;
|
|
109
|
+
const totalPage = Math.ceil(_total / pageSize);
|
|
110
|
+
({ dataSource: data_source, total: _total } = await server({
|
|
111
|
+
limit: _pageSize,
|
|
112
|
+
offset,
|
|
113
|
+
current: _current,
|
|
114
|
+
}));
|
|
115
|
+
if (_seq !== seq.current) return;
|
|
116
|
+
_current = totalPage;
|
|
117
|
+
}
|
|
118
|
+
setDataSource(data_source);
|
|
119
|
+
setCurrentAndRef(_current);
|
|
120
|
+
setPageSizeAndRef(_pageSize);
|
|
121
|
+
setTotal(_total);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
// eslint-disable-next-line no-console
|
|
124
|
+
console.error('fetch err', error);
|
|
125
|
+
if (_seq !== seq.current) return;
|
|
126
|
+
} finally {
|
|
127
|
+
setIsFirstComplete(true);
|
|
128
|
+
setLoading(false);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const refresh = async (resetPage?: boolean) => {
|
|
133
|
+
if (resetPage) {
|
|
134
|
+
setDataSource([]);
|
|
135
|
+
setCurrentAndRef(propCurrent);
|
|
136
|
+
setPageSizeAndRef(propPageSize);
|
|
137
|
+
}
|
|
138
|
+
await doSearch();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const debounceRefresh = useCallback(debounce(refresh, 500), [refresh]);
|
|
142
|
+
|
|
143
|
+
/* 重置逻辑 */
|
|
144
|
+
const _deps = [...(deps || []), isReady];
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (!isReady) return;
|
|
148
|
+
debounceRefresh(true);
|
|
149
|
+
}, _deps);
|
|
150
|
+
|
|
151
|
+
const onChange = async (page: number, pageSize: number) => {
|
|
152
|
+
setCurrentAndRef(page);
|
|
153
|
+
setPageSizeAndRef(pageSize);
|
|
154
|
+
await refresh();
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const onShowSizeChange = async (size: number, current: number) => {
|
|
158
|
+
setPageSizeAndRef(size);
|
|
159
|
+
setCurrentAndRef(current);
|
|
160
|
+
await refresh();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const paginationProps: IPaginationProps = {
|
|
164
|
+
current,
|
|
165
|
+
pageSize,
|
|
166
|
+
total,
|
|
167
|
+
onChange,
|
|
168
|
+
onShowSizeChange,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
tableProps: {
|
|
173
|
+
loading,
|
|
174
|
+
dataSource,
|
|
175
|
+
},
|
|
176
|
+
loading,
|
|
177
|
+
dataSource,
|
|
178
|
+
paginationProps,
|
|
179
|
+
isFirstComplete,
|
|
180
|
+
refresh,
|
|
181
|
+
debounceRefresh,
|
|
182
|
+
setDataSource,
|
|
183
|
+
};
|
|
184
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const useReactive = <T extends object>(initialState: T) => {
|
|
4
|
+
const [state, setState] = useState<T>(initialState);
|
|
5
|
+
const stateRef = useRef<T>(state);
|
|
6
|
+
|
|
7
|
+
const updateState = useCallback((newState: Partial<T> | ((prev: T) => Partial<T>)) => {
|
|
8
|
+
setState(prev => {
|
|
9
|
+
const updatedPart = typeof newState === 'function' ? newState(prev) : newState;
|
|
10
|
+
const newFullState = { ...prev, ...updatedPart };
|
|
11
|
+
stateRef.current = newFullState; // 同步更新ref
|
|
12
|
+
return newFullState;
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
const getState = useCallback(() => stateRef.current, []);
|
|
17
|
+
|
|
18
|
+
const resetState = useCallback(() => {
|
|
19
|
+
setState(initialState);
|
|
20
|
+
stateRef.current = initialState;
|
|
21
|
+
}, [initialState]);
|
|
22
|
+
|
|
23
|
+
return [state, updateState, getState, resetState] as const;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { useReactive };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
type Callback = () => void;
|
|
4
|
+
|
|
5
|
+
interface UseTimeoutReturn {
|
|
6
|
+
start: Callback;
|
|
7
|
+
isRunning: boolean;
|
|
8
|
+
clear: Callback;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function useTimeout(
|
|
12
|
+
callback: Callback,
|
|
13
|
+
delay: number | null,
|
|
14
|
+
immediate: boolean = false
|
|
15
|
+
): UseTimeoutReturn {
|
|
16
|
+
const timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
17
|
+
const savedCallback = useRef(callback);
|
|
18
|
+
|
|
19
|
+
// Update callback ref if callback changes
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
savedCallback.current = callback;
|
|
22
|
+
}, [callback]);
|
|
23
|
+
|
|
24
|
+
const clear = useCallback(() => {
|
|
25
|
+
if (timerRef.current) {
|
|
26
|
+
clearTimeout(timerRef.current);
|
|
27
|
+
timerRef.current = undefined;
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const start = useCallback(() => {
|
|
32
|
+
clear();
|
|
33
|
+
if (delay !== null && delay !== undefined) {
|
|
34
|
+
timerRef.current = setTimeout(() => savedCallback.current(), delay);
|
|
35
|
+
}
|
|
36
|
+
}, [delay, clear]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (immediate) {
|
|
40
|
+
savedCallback.current();
|
|
41
|
+
}
|
|
42
|
+
start();
|
|
43
|
+
|
|
44
|
+
return clear;
|
|
45
|
+
}, [delay, start, clear, immediate]);
|
|
46
|
+
|
|
47
|
+
return { start, clear, isRunning: !!timerRef.current };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { useTimeout };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// 虚拟列表加载
|
|
2
|
+
import { debounce } from 'lodash';
|
|
3
|
+
import { DependencyList, useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { PAGE_SIZE } from '../constants';
|
|
5
|
+
|
|
6
|
+
export interface IPaginationResult<T> {
|
|
7
|
+
/**
|
|
8
|
+
* 数据
|
|
9
|
+
*
|
|
10
|
+
* @memberof IPaginationResult
|
|
11
|
+
*/
|
|
12
|
+
dataSource: T[];
|
|
13
|
+
/**
|
|
14
|
+
* 加载状态
|
|
15
|
+
*
|
|
16
|
+
* @memberof IPaginationResult
|
|
17
|
+
*/
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* 是否完成了一次请求
|
|
21
|
+
*
|
|
22
|
+
* @memberof IPaginationResult
|
|
23
|
+
*/
|
|
24
|
+
isFirstComplete: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 分页信息
|
|
27
|
+
*
|
|
28
|
+
* @memberof IPaginationResult
|
|
29
|
+
*/
|
|
30
|
+
paginationProps: {
|
|
31
|
+
current: number;
|
|
32
|
+
pageSize: number;
|
|
33
|
+
total: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* reset 是否重置查询
|
|
37
|
+
*
|
|
38
|
+
* @memberof IPaginationResult
|
|
39
|
+
*/
|
|
40
|
+
refresh: (reset?: boolean) => Promise<void>;
|
|
41
|
+
debounceRefresh: (reset?: boolean) => void;
|
|
42
|
+
/* 快速逃避方案 */
|
|
43
|
+
setDataSource: (data: T[]) => void;
|
|
44
|
+
setTotal: (total: number) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface IServerParams {
|
|
48
|
+
offset: number;
|
|
49
|
+
limit: number;
|
|
50
|
+
current: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type IInfoBack<T> = { dataSource: T[]; total: number };
|
|
54
|
+
|
|
55
|
+
type IInfoServer<T> = (params: IServerParams) => IInfoBack<T> | Promise<IInfoBack<T>>;
|
|
56
|
+
|
|
57
|
+
interface IInfoOption<T> {
|
|
58
|
+
/**
|
|
59
|
+
* 第一次是否执行server
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
isReady?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* 默认的数据
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
dataSource?: T[];
|
|
68
|
+
/**
|
|
69
|
+
* 默认当前第几页
|
|
70
|
+
*
|
|
71
|
+
*/
|
|
72
|
+
current?: number;
|
|
73
|
+
/**
|
|
74
|
+
* 默认一页几条数据
|
|
75
|
+
*
|
|
76
|
+
*/
|
|
77
|
+
pageSize?: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const useVirtualList = <T>(
|
|
81
|
+
server: IInfoServer<T>,
|
|
82
|
+
deps?: DependencyList, // 依赖条件 数据更新默认执行server
|
|
83
|
+
option?: IInfoOption<T>,
|
|
84
|
+
): IPaginationResult<T> => {
|
|
85
|
+
const {
|
|
86
|
+
isReady = true,
|
|
87
|
+
dataSource: propDataSource = [],
|
|
88
|
+
current: propCurrent = 1,
|
|
89
|
+
pageSize: propPageSize = PAGE_SIZE,
|
|
90
|
+
} = option || {};
|
|
91
|
+
|
|
92
|
+
// 是否完成了一次请求
|
|
93
|
+
const [isFirstComplete, setIsFirstComplete] = useState(false);
|
|
94
|
+
// 分页
|
|
95
|
+
const [current, setCurrent] = useState(propCurrent);
|
|
96
|
+
const [pageSize, setPageSize] = useState(propPageSize);
|
|
97
|
+
const [total, setTotal] = useState(0);
|
|
98
|
+
|
|
99
|
+
// 表格
|
|
100
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
101
|
+
const [dataSource, setDataSource] = useState(propDataSource);
|
|
102
|
+
|
|
103
|
+
const currentRef = useRef(current);
|
|
104
|
+
const pageSizeRef = useRef(pageSize);
|
|
105
|
+
|
|
106
|
+
// 在状态更新时同步更新ref
|
|
107
|
+
const setCurrentAndRef = (val: number) => {
|
|
108
|
+
setCurrent(val);
|
|
109
|
+
currentRef.current = val;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const setPageSizeAndRef = (val: number) => {
|
|
113
|
+
setPageSize(val);
|
|
114
|
+
pageSizeRef.current = val;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// 计数器
|
|
118
|
+
const seq = useRef(0);
|
|
119
|
+
const doSearch = async (reset?: boolean) => {
|
|
120
|
+
if (!isReady) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
let _current = currentRef.current;
|
|
124
|
+
let _pageSize = pageSizeRef.current;
|
|
125
|
+
setIsLoading(true);
|
|
126
|
+
|
|
127
|
+
seq.current += 1;
|
|
128
|
+
const _seq = seq.current;
|
|
129
|
+
try {
|
|
130
|
+
// 发送请求
|
|
131
|
+
let offset = Math.round((_current - 1) * _pageSize);
|
|
132
|
+
if (offset < 0) {
|
|
133
|
+
offset = 0;
|
|
134
|
+
}
|
|
135
|
+
if (_pageSize < 1) {
|
|
136
|
+
_pageSize = 1;
|
|
137
|
+
}
|
|
138
|
+
let { dataSource: data0, total: total0 } = await server({
|
|
139
|
+
limit: _pageSize,
|
|
140
|
+
offset,
|
|
141
|
+
current: _current,
|
|
142
|
+
});
|
|
143
|
+
if (_seq !== seq.current) return;
|
|
144
|
+
if (pageSize * (_current - 1) >= total0 && _current !== 1) {
|
|
145
|
+
const totalPage = Math.ceil(total0 / pageSize);
|
|
146
|
+
({ dataSource: data0, total: total0 } = await server({
|
|
147
|
+
limit: _pageSize,
|
|
148
|
+
offset: Math.round((totalPage - 1) * _pageSize),
|
|
149
|
+
current: _current,
|
|
150
|
+
}));
|
|
151
|
+
if (_seq !== seq.current) return;
|
|
152
|
+
_current = totalPage;
|
|
153
|
+
// message.error('数据源发生变化,该页没有数据,自动加载最后一页');
|
|
154
|
+
}
|
|
155
|
+
const d = reset ? data0 : dataSource.concat(data0 as T[]);
|
|
156
|
+
setDataSource(d);
|
|
157
|
+
setCurrentAndRef(_current);
|
|
158
|
+
setPageSizeAndRef(_pageSize);
|
|
159
|
+
setTotal(total0);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('fetch err', error);
|
|
162
|
+
if (_seq !== seq.current) return;
|
|
163
|
+
}
|
|
164
|
+
setIsFirstComplete(true);
|
|
165
|
+
setIsLoading(false);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// 加载下一页数据
|
|
169
|
+
const refresh = async (reset?: boolean) => {
|
|
170
|
+
if (reset) {
|
|
171
|
+
setDataSource([]);
|
|
172
|
+
setCurrentAndRef(propCurrent);
|
|
173
|
+
setPageSizeAndRef(propPageSize);
|
|
174
|
+
} else {
|
|
175
|
+
const curTotal = pageSize * current;
|
|
176
|
+
if (total && total <= curTotal) return;
|
|
177
|
+
setCurrentAndRef(current + 1);
|
|
178
|
+
}
|
|
179
|
+
await doSearch(reset);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// 防抖 加载下一页数据
|
|
183
|
+
const debounceRefresh = useCallback(debounce(refresh, 500), [refresh]);
|
|
184
|
+
|
|
185
|
+
/* 重置逻辑 */
|
|
186
|
+
const _deps = [...(deps || []), isReady];
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (!isReady) return;
|
|
190
|
+
debounceRefresh(true);
|
|
191
|
+
}, _deps);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
loading: isLoading,
|
|
195
|
+
dataSource,
|
|
196
|
+
paginationProps: {
|
|
197
|
+
current,
|
|
198
|
+
pageSize,
|
|
199
|
+
total,
|
|
200
|
+
},
|
|
201
|
+
isFirstComplete,
|
|
202
|
+
refresh: async (reset?: boolean) => {
|
|
203
|
+
await refresh(reset);
|
|
204
|
+
},
|
|
205
|
+
debounceRefresh,
|
|
206
|
+
setDataSource,
|
|
207
|
+
setTotal,
|
|
208
|
+
};
|
|
209
|
+
};
|
package/src/index.tsx
CHANGED