@mi-avalon/libs 0.0.23 → 0.0.25
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/MSearch/index.d.ts +26 -0
- package/dist/components/MSearch/index.js +68 -0
- package/dist/components/MiModal/index.d.ts +15 -0
- package/dist/components/MiModal/index.js +70 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -2
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/useFuncRequest.d.ts +16 -0
- package/dist/hooks/useFuncRequest.js +67 -0
- package/dist/hooks/useInterval.d.ts +8 -0
- package/dist/hooks/useInterval.js +30 -0
- package/dist/hooks/usePagination.d.ts +42 -0
- package/dist/hooks/usePagination.js +124 -0
- package/dist/hooks/useQuery.d.ts +3 -0
- package/dist/hooks/useQuery.js +6 -0
- package/dist/hooks/useReactive.d.ts +2 -0
- package/dist/hooks/useReactive.js +20 -0
- package/dist/hooks/useTimeout.d.ts +8 -0
- package/dist/hooks/useTimeout.js +30 -0
- package/dist/hooks/useVirtualList.d.ts +75 -0
- package/dist/hooks/useVirtualList.js +121 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +1411 -932
- package/dist/index.js +1 -1
- package/dist/index.umd.js +16 -16
- package/dist/style.css +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -1
- package/dist/utils/openModal.d.ts +15 -0
- package/dist/utils/openModal.js +98 -0
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ButtonProps } from 'antd';
|
|
2
|
+
import { FormInstance } from 'antd/lib';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { IMFormItem } from '../MForm';
|
|
5
|
+
import './index.scss';
|
|
6
|
+
export interface IMSearchProps {
|
|
7
|
+
form?: FormInstance;
|
|
8
|
+
/**
|
|
9
|
+
* 需要的数据: [ { label: ...,id: ..., required: ..., type: ..., }, ... ]
|
|
10
|
+
*/
|
|
11
|
+
searchItems: IMFormItem[];
|
|
12
|
+
className?: string;
|
|
13
|
+
/**
|
|
14
|
+
* 搜索按钮 params返回form表单的数据
|
|
15
|
+
*/
|
|
16
|
+
onSearch?: (params: any, isReset: boolean) => any;
|
|
17
|
+
/**
|
|
18
|
+
* 搜索项默认展开,不填写默认false
|
|
19
|
+
*/
|
|
20
|
+
defaultShowAll?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 自定义按钮
|
|
23
|
+
*/
|
|
24
|
+
customButtons?: ButtonProps[];
|
|
25
|
+
}
|
|
26
|
+
export declare const MSearch: React.FC<IMSearchProps>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CaretDownOutlined, CaretUpOutlined } from '@ant-design/icons';
|
|
3
|
+
import { Button, Card, Form } from 'antd';
|
|
4
|
+
import { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { getClassName, removeNull } from '../../utils';
|
|
6
|
+
import MForm from '../MForm';
|
|
7
|
+
import { CompThemeProvider } from '../ThemeContext';
|
|
8
|
+
import './index.scss';
|
|
9
|
+
const ENTER_KEY_CODE = 13; // 回车键的值为13
|
|
10
|
+
const classname = (n = '') => {
|
|
11
|
+
const cn = 'm-search';
|
|
12
|
+
return getClassName(cn, n);
|
|
13
|
+
};
|
|
14
|
+
export const MSearch = props => {
|
|
15
|
+
const { className, searchItems = [], onSearch, defaultShowAll = false, customButtons = [], form: propsForm, } = props;
|
|
16
|
+
const [showAll, setShowAll] = useState(defaultShowAll);
|
|
17
|
+
const [curForm] = Form.useForm();
|
|
18
|
+
const searchWrap = useRef(null);
|
|
19
|
+
const form = propsForm ?? curForm;
|
|
20
|
+
// 键盘按下事件处理
|
|
21
|
+
const onEnterKeySearch = (keyObj) => {
|
|
22
|
+
const { keyCode } = keyObj;
|
|
23
|
+
if (keyCode === ENTER_KEY_CODE) {
|
|
24
|
+
search();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
// 添加和移除事件监听
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const currentWrap = searchWrap.current;
|
|
30
|
+
currentWrap?.addEventListener('keydown', onEnterKeySearch);
|
|
31
|
+
return () => {
|
|
32
|
+
currentWrap?.removeEventListener('keydown', onEnterKeySearch);
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
// 重置表单
|
|
36
|
+
const reset = () => {
|
|
37
|
+
form?.resetFields();
|
|
38
|
+
search(true);
|
|
39
|
+
};
|
|
40
|
+
// 搜索函数
|
|
41
|
+
const search = (isReset = false) => {
|
|
42
|
+
form?.validateFields().then(values => {
|
|
43
|
+
removeNull(values);
|
|
44
|
+
onSearch?.(values, isReset);
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
// 渲染搜索按钮
|
|
48
|
+
const renderSearchButtons = () => {
|
|
49
|
+
const hasMore = searchItems.length > 2;
|
|
50
|
+
return (_jsxs("div", { className: classname('btn-wrapper'), children: [customButtons?.length > 0 ? (_jsx(_Fragment, { children: customButtons.map((buttonProps, index) => (_jsx(Button, { className: classname('btn'), ...buttonProps, children: buttonProps.children }, `custom-btn-${index}`))) })) : (_jsxs(_Fragment, { children: [_jsx(Button, { className: classname('btn btn-reset'), onClick: reset, children: "\u91CD\u7F6E" }), _jsx(Button, { className: classname('btn btn-search'), onClick: () => search(), type: 'primary', children: "\u641C\u7D22" })] })), hasMore && (_jsxs("div", { className: classname('btn btn-collapse'), onClick: () => setShowAll(!showAll), children: [_jsx("span", { children: showAll ? '收起' : '展开' }), showAll ? _jsx(CaretUpOutlined, {}) : _jsx(CaretDownOutlined, {})] }))] }));
|
|
51
|
+
};
|
|
52
|
+
// 处理搜索项显示/隐藏
|
|
53
|
+
const processedSearchItems = searchItems.map((item, idx) => {
|
|
54
|
+
const newItem = { ...item };
|
|
55
|
+
if (searchItems.length > 2 && idx >= 2 && !showAll) {
|
|
56
|
+
newItem.formItemProps = {
|
|
57
|
+
...item.formItemProps,
|
|
58
|
+
style: {
|
|
59
|
+
...item.formItemProps?.style,
|
|
60
|
+
display: 'none',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return newItem;
|
|
65
|
+
});
|
|
66
|
+
const hasMore = searchItems.length > 2;
|
|
67
|
+
return (_jsx(CompThemeProvider, { children: _jsx("div", { className: `${classname('')} ${showAll ? '' : classname('collapsed')} ${className || ''}`, ref: searchWrap, children: _jsxs(Card, { children: [_jsxs("div", { className: classname('form'), children: [_jsx(MForm, { form: form, formItems: processedSearchItems, column: 3 }), (!hasMore || (hasMore && !showAll)) && renderSearchButtons()] }), hasMore && showAll && _jsx("div", { className: classname('footer'), children: renderSearchButtons() })] }) }) }));
|
|
68
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ThemeConfig } from 'antd';
|
|
2
|
+
import React, { Component } from 'react';
|
|
3
|
+
import { IModalBaseProps } from '../../utils';
|
|
4
|
+
export type IMiModalProps = IModalBaseProps & {
|
|
5
|
+
mode?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class MiModal extends Component<IMiModalProps> {
|
|
8
|
+
static setMode: (mode: string) => void;
|
|
9
|
+
static setThemeConfig: (fn: (m: string) => ThemeConfig) => void;
|
|
10
|
+
static open: (props: IMiModalProps) => import("../../utils").IModalControls<IMiModalProps>;
|
|
11
|
+
getTheme(): ThemeConfig;
|
|
12
|
+
handleCancel: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
13
|
+
handleOk: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
14
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Modal as AntModal, ConfigProvider, theme } from 'antd';
|
|
3
|
+
import { Component } from 'react';
|
|
4
|
+
import { openModal } from '../../utils';
|
|
5
|
+
// 全局状态管理
|
|
6
|
+
let globalMode = 'default';
|
|
7
|
+
const modalInstances = [];
|
|
8
|
+
let getThemeConfig = (mode) => {
|
|
9
|
+
switch (mode) {
|
|
10
|
+
case 'light':
|
|
11
|
+
return {
|
|
12
|
+
algorithm: [theme.defaultAlgorithm],
|
|
13
|
+
};
|
|
14
|
+
case 'dark':
|
|
15
|
+
return {
|
|
16
|
+
algorithm: [theme.darkAlgorithm],
|
|
17
|
+
};
|
|
18
|
+
default:
|
|
19
|
+
return {
|
|
20
|
+
algorithm: [theme.defaultAlgorithm],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export class MiModal extends Component {
|
|
25
|
+
// 静态方法设置全局模式并更新所有弹窗
|
|
26
|
+
static setMode = (mode) => {
|
|
27
|
+
globalMode = mode;
|
|
28
|
+
// 更新所有已打开的弹窗
|
|
29
|
+
modalInstances.forEach(instance => {
|
|
30
|
+
instance.update({ mode });
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
// 静态方法设置全局ThemeConfig
|
|
34
|
+
static setThemeConfig = (fn) => {
|
|
35
|
+
getThemeConfig = fn;
|
|
36
|
+
};
|
|
37
|
+
static open = (props) => {
|
|
38
|
+
const instance = openModal(MiModal, {
|
|
39
|
+
mode: globalMode, // 默认使用全局主题
|
|
40
|
+
...props,
|
|
41
|
+
});
|
|
42
|
+
// 注册实例以便全局更新
|
|
43
|
+
modalInstances.push(instance);
|
|
44
|
+
// 添加销毁时的清理逻辑
|
|
45
|
+
const originalDestroy = instance.destroy;
|
|
46
|
+
instance.destroy = (...args) => {
|
|
47
|
+
const index = modalInstances.indexOf(instance);
|
|
48
|
+
if (index !== -1) {
|
|
49
|
+
modalInstances.splice(index, 1);
|
|
50
|
+
}
|
|
51
|
+
return originalDestroy(...args);
|
|
52
|
+
};
|
|
53
|
+
return instance;
|
|
54
|
+
};
|
|
55
|
+
getTheme() {
|
|
56
|
+
const mode = this.props.mode || globalMode;
|
|
57
|
+
return getThemeConfig(mode);
|
|
58
|
+
}
|
|
59
|
+
handleCancel = (e) => {
|
|
60
|
+
this.props.onCancel?.(e);
|
|
61
|
+
this.props.onClosed?.({ cancel: true });
|
|
62
|
+
};
|
|
63
|
+
handleOk = (e) => {
|
|
64
|
+
this.props.onOk?.(e);
|
|
65
|
+
this.props.onClosed?.({ ok: true });
|
|
66
|
+
};
|
|
67
|
+
render() {
|
|
68
|
+
return (_jsx(ConfigProvider, { theme: this.getTheme(), componentSize: 'middle', componentDisabled: false, children: _jsx(AntModal, { maskClosable: false, open: this.props.open, onCancel: this.handleCancel, onOk: this.handleOk, okText: '\u786E\u5B9A', cancelText: '\u53D6\u6D88', ...this.props, children: this.props.children }) }));
|
|
69
|
+
}
|
|
70
|
+
}
|
package/dist/components/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export * from './MBreadcrumb';
|
|
|
3
3
|
export * from './MDescriptions';
|
|
4
4
|
export * from './MForm';
|
|
5
5
|
export { default as MForm } from './MForm';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export * from './MiModal';
|
|
7
|
+
export * from './MSearch';
|
|
8
8
|
export * from './MTable';
|
|
9
9
|
export * from './ThemeContext';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
|
|
2
|
+
export type UseFuncRequestOptions<T extends (...args: any[]) => Promise<any>> = {
|
|
3
|
+
autoRunArgs?: Parameters<T>;
|
|
4
|
+
onBefore?: (...args: Parameters<T>) => boolean | Promise<boolean>;
|
|
5
|
+
onSuccess?: (data: UnwrapPromise<ReturnType<T>>) => void | Promise<void>;
|
|
6
|
+
onError?: (error: Error) => void | Promise<void>;
|
|
7
|
+
onFinally?: () => void | Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare function useFuncRequest<T extends (...args: any[]) => Promise<any>>(asyncFunc: T, options?: UseFuncRequestOptions<T>): {
|
|
10
|
+
run: (...args: Parameters<T>) => Promise<any>;
|
|
11
|
+
cancel: () => void;
|
|
12
|
+
loading: boolean;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
data: UnwrapPromise<ReturnType<T>> | null;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
export function useFuncRequest(
|
|
3
|
+
// 异步函数
|
|
4
|
+
asyncFunc,
|
|
5
|
+
// 配置项
|
|
6
|
+
options) {
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const [data, setData] = useState(null);
|
|
10
|
+
const abortControllerRef = useRef(undefined);
|
|
11
|
+
const requestIdRef = useRef(0);
|
|
12
|
+
const cancel = () => {
|
|
13
|
+
abortControllerRef.current?.abort();
|
|
14
|
+
setLoading(false);
|
|
15
|
+
setError(null);
|
|
16
|
+
};
|
|
17
|
+
const run = async (...args) => {
|
|
18
|
+
const currentRequestId = ++requestIdRef.current;
|
|
19
|
+
cancel();
|
|
20
|
+
const abortController = new AbortController();
|
|
21
|
+
abortControllerRef.current = abortController;
|
|
22
|
+
try {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
setError(null);
|
|
25
|
+
if (options?.onBefore) {
|
|
26
|
+
const beforeRes = await options?.onBefore?.(...args);
|
|
27
|
+
if (!beforeRes) {
|
|
28
|
+
cancel();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 安全传递 AbortSignal
|
|
33
|
+
const funcArgs = args.length >= asyncFunc.length
|
|
34
|
+
? args
|
|
35
|
+
: [...args, { signal: abortController.signal }];
|
|
36
|
+
const res = await asyncFunc(...funcArgs);
|
|
37
|
+
if (!abortController.signal.aborted &&
|
|
38
|
+
currentRequestId === requestIdRef.current) {
|
|
39
|
+
setData(res);
|
|
40
|
+
await options?.onSuccess?.(res);
|
|
41
|
+
}
|
|
42
|
+
return res;
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (!abortController.signal.aborted &&
|
|
46
|
+
currentRequestId === requestIdRef.current) {
|
|
47
|
+
setError(err);
|
|
48
|
+
await options?.onError?.(err);
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
if (!abortController.signal.aborted &&
|
|
54
|
+
currentRequestId === requestIdRef.current) {
|
|
55
|
+
setLoading(false);
|
|
56
|
+
await options?.onFinally?.();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (options?.autoRunArgs) {
|
|
62
|
+
run(...options.autoRunArgs).catch(() => { });
|
|
63
|
+
}
|
|
64
|
+
return cancel;
|
|
65
|
+
}, [JSON.stringify(options?.autoRunArgs)]);
|
|
66
|
+
return { run, cancel, loading, error, data };
|
|
67
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
function useInterval(callback, delay, immediate = false) {
|
|
3
|
+
const timerRef = useRef(undefined);
|
|
4
|
+
const savedCallback = useRef(callback);
|
|
5
|
+
// Update callback ref if callback changes
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
savedCallback.current = callback;
|
|
8
|
+
}, [callback]);
|
|
9
|
+
const clear = useCallback(() => {
|
|
10
|
+
if (timerRef.current) {
|
|
11
|
+
clearInterval(timerRef.current);
|
|
12
|
+
timerRef.current = undefined;
|
|
13
|
+
}
|
|
14
|
+
}, []);
|
|
15
|
+
const start = useCallback(() => {
|
|
16
|
+
clear();
|
|
17
|
+
if (delay !== null && delay !== undefined) {
|
|
18
|
+
timerRef.current = setInterval(() => savedCallback.current(), delay);
|
|
19
|
+
}
|
|
20
|
+
}, [delay, clear]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (immediate) {
|
|
23
|
+
savedCallback.current();
|
|
24
|
+
}
|
|
25
|
+
start();
|
|
26
|
+
return clear;
|
|
27
|
+
}, [delay, start, clear, immediate]);
|
|
28
|
+
return { start, clear, isRunning: !!timerRef.current };
|
|
29
|
+
}
|
|
30
|
+
export { useInterval };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
interface IPaginationProps {
|
|
2
|
+
current: number;
|
|
3
|
+
pageSize: number;
|
|
4
|
+
total: number;
|
|
5
|
+
onChange: (page: number, pageSize: number) => Promise<void>;
|
|
6
|
+
onShowSizeChange: (size: number, current: number) => Promise<void>;
|
|
7
|
+
showTotal?: (t: number) => React.ReactNode;
|
|
8
|
+
showQuickJumper?: boolean;
|
|
9
|
+
showSizeChanger?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface PaginationResult<T> {
|
|
12
|
+
tableProps: {
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
dataSource: T[];
|
|
15
|
+
};
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
dataSource: T[];
|
|
18
|
+
paginationProps: IPaginationProps;
|
|
19
|
+
isFirstComplete: boolean;
|
|
20
|
+
refresh: (resetPage?: boolean) => Promise<void>;
|
|
21
|
+
debounceRefresh: (resetPage?: boolean) => void;
|
|
22
|
+
setDataSource: (data: T[]) => void;
|
|
23
|
+
}
|
|
24
|
+
interface IServerParams {
|
|
25
|
+
offset: number;
|
|
26
|
+
limit: number;
|
|
27
|
+
current: number;
|
|
28
|
+
}
|
|
29
|
+
type IInfoBack<T> = {
|
|
30
|
+
dataSource: T[];
|
|
31
|
+
total: number;
|
|
32
|
+
};
|
|
33
|
+
type IInfoServer<T> = (params: IServerParams) => IInfoBack<T> | Promise<IInfoBack<T>>;
|
|
34
|
+
interface IInfoOption<T> {
|
|
35
|
+
isReady?: boolean;
|
|
36
|
+
dataSource?: T[];
|
|
37
|
+
current?: number;
|
|
38
|
+
pageSize?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare const usePagination: <T>(server: IInfoServer<T>, deps?: any[], // 依赖条件 数据更新默认执行server
|
|
41
|
+
option?: IInfoOption<T>) => PaginationResult<T>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { debounce } from '../utils';
|
|
3
|
+
export const usePagination = (server, deps, // 依赖条件 数据更新默认执行server
|
|
4
|
+
option) => {
|
|
5
|
+
const { isReady = true, dataSource: propDataSource = [], current: propCurrent = 1, pageSize: propPageSize = 10, } = option || {};
|
|
6
|
+
const [current, setCurrent] = useState(propCurrent);
|
|
7
|
+
const [pageSize, setPageSize] = useState(propPageSize);
|
|
8
|
+
const [dataSource, setDataSource] = useState(propDataSource);
|
|
9
|
+
const [total, setTotal] = useState(0);
|
|
10
|
+
const [loading, setLoading] = useState(false);
|
|
11
|
+
const [isFirstComplete, setIsFirstComplete] = useState(false);
|
|
12
|
+
const currentRef = useRef(current);
|
|
13
|
+
const pageSizeRef = useRef(pageSize);
|
|
14
|
+
// 在状态更新时同步更新ref
|
|
15
|
+
const setCurrentAndRef = (val) => {
|
|
16
|
+
setCurrent(val);
|
|
17
|
+
currentRef.current = val;
|
|
18
|
+
};
|
|
19
|
+
const setPageSizeAndRef = (val) => {
|
|
20
|
+
setPageSize(val);
|
|
21
|
+
pageSizeRef.current = val;
|
|
22
|
+
};
|
|
23
|
+
// 计数器
|
|
24
|
+
const seq = useRef(0);
|
|
25
|
+
const doSearch = async () => {
|
|
26
|
+
if (!isReady) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let _current = currentRef.current;
|
|
30
|
+
let _pageSize = pageSizeRef.current;
|
|
31
|
+
setLoading(true);
|
|
32
|
+
seq.current++;
|
|
33
|
+
const _seq = seq.current;
|
|
34
|
+
try {
|
|
35
|
+
// 发送请求
|
|
36
|
+
let offset = Math.round((_current - 1) * _pageSize);
|
|
37
|
+
if (offset < 0) {
|
|
38
|
+
offset = 0;
|
|
39
|
+
}
|
|
40
|
+
if (_pageSize < 1) {
|
|
41
|
+
_pageSize = 1;
|
|
42
|
+
}
|
|
43
|
+
let { dataSource: data_source, total: _total } = await server({
|
|
44
|
+
limit: _pageSize,
|
|
45
|
+
offset,
|
|
46
|
+
current: _current,
|
|
47
|
+
});
|
|
48
|
+
if (_seq !== seq.current)
|
|
49
|
+
return;
|
|
50
|
+
if (pageSize * (_current - 1) >= _total && _current !== 1) {
|
|
51
|
+
_current = 1;
|
|
52
|
+
const totalPage = Math.ceil(_total / pageSize);
|
|
53
|
+
({ dataSource: data_source, total: _total } = await server({
|
|
54
|
+
limit: _pageSize,
|
|
55
|
+
offset,
|
|
56
|
+
current: _current,
|
|
57
|
+
}));
|
|
58
|
+
if (_seq !== seq.current)
|
|
59
|
+
return;
|
|
60
|
+
_current = totalPage;
|
|
61
|
+
}
|
|
62
|
+
setDataSource(data_source);
|
|
63
|
+
setCurrentAndRef(_current);
|
|
64
|
+
setPageSizeAndRef(_pageSize);
|
|
65
|
+
setTotal(_total);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.error('fetch err', error);
|
|
70
|
+
if (_seq !== seq.current)
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
setIsFirstComplete(true);
|
|
75
|
+
setLoading(false);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const refresh = async (resetPage) => {
|
|
79
|
+
if (resetPage) {
|
|
80
|
+
setDataSource([]);
|
|
81
|
+
setCurrentAndRef(propCurrent);
|
|
82
|
+
setPageSizeAndRef(propPageSize);
|
|
83
|
+
}
|
|
84
|
+
await doSearch();
|
|
85
|
+
};
|
|
86
|
+
const debounceRefresh = useCallback(debounce(refresh, 500), [refresh]);
|
|
87
|
+
/* 重置逻辑 */
|
|
88
|
+
const _deps = [...(deps || []), isReady];
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!isReady)
|
|
91
|
+
return;
|
|
92
|
+
debounceRefresh(true);
|
|
93
|
+
}, _deps);
|
|
94
|
+
const onChange = async (page, pageSize) => {
|
|
95
|
+
setCurrentAndRef(page);
|
|
96
|
+
setPageSizeAndRef(pageSize);
|
|
97
|
+
await refresh();
|
|
98
|
+
};
|
|
99
|
+
const onShowSizeChange = async (size, current) => {
|
|
100
|
+
setPageSizeAndRef(size);
|
|
101
|
+
setCurrentAndRef(current);
|
|
102
|
+
await refresh();
|
|
103
|
+
};
|
|
104
|
+
const paginationProps = {
|
|
105
|
+
current,
|
|
106
|
+
pageSize,
|
|
107
|
+
total,
|
|
108
|
+
onChange,
|
|
109
|
+
onShowSizeChange,
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
tableProps: {
|
|
113
|
+
loading,
|
|
114
|
+
dataSource,
|
|
115
|
+
},
|
|
116
|
+
loading,
|
|
117
|
+
dataSource,
|
|
118
|
+
paginationProps,
|
|
119
|
+
isFirstComplete,
|
|
120
|
+
refresh,
|
|
121
|
+
debounceRefresh,
|
|
122
|
+
setDataSource,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from 'react';
|
|
2
|
+
const useReactive = (initialState) => {
|
|
3
|
+
const [state, setState] = useState(initialState);
|
|
4
|
+
const stateRef = useRef(state);
|
|
5
|
+
const updateState = useCallback((newState) => {
|
|
6
|
+
setState(prev => {
|
|
7
|
+
const updatedPart = typeof newState === 'function' ? newState(prev) : newState;
|
|
8
|
+
const newFullState = { ...prev, ...updatedPart };
|
|
9
|
+
stateRef.current = newFullState; // 同步更新ref
|
|
10
|
+
return newFullState;
|
|
11
|
+
});
|
|
12
|
+
}, []);
|
|
13
|
+
const getState = useCallback(() => stateRef.current, []);
|
|
14
|
+
const resetState = useCallback(() => {
|
|
15
|
+
setState(initialState);
|
|
16
|
+
stateRef.current = initialState;
|
|
17
|
+
}, [initialState]);
|
|
18
|
+
return [state, updateState, getState, resetState];
|
|
19
|
+
};
|
|
20
|
+
export { useReactive };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
function useTimeout(callback, delay, immediate = false) {
|
|
3
|
+
const timerRef = useRef(undefined);
|
|
4
|
+
const savedCallback = useRef(callback);
|
|
5
|
+
// Update callback ref if callback changes
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
savedCallback.current = callback;
|
|
8
|
+
}, [callback]);
|
|
9
|
+
const clear = useCallback(() => {
|
|
10
|
+
if (timerRef.current) {
|
|
11
|
+
clearTimeout(timerRef.current);
|
|
12
|
+
timerRef.current = undefined;
|
|
13
|
+
}
|
|
14
|
+
}, []);
|
|
15
|
+
const start = useCallback(() => {
|
|
16
|
+
clear();
|
|
17
|
+
if (delay !== null && delay !== undefined) {
|
|
18
|
+
timerRef.current = setTimeout(() => savedCallback.current(), delay);
|
|
19
|
+
}
|
|
20
|
+
}, [delay, clear]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (immediate) {
|
|
23
|
+
savedCallback.current();
|
|
24
|
+
}
|
|
25
|
+
start();
|
|
26
|
+
return clear;
|
|
27
|
+
}, [delay, start, clear, immediate]);
|
|
28
|
+
return { start, clear, isRunning: !!timerRef.current };
|
|
29
|
+
}
|
|
30
|
+
export { useTimeout };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DependencyList } from 'react';
|
|
2
|
+
export interface IPaginationResult<T> {
|
|
3
|
+
/**
|
|
4
|
+
* 数据
|
|
5
|
+
*
|
|
6
|
+
* @memberof IPaginationResult
|
|
7
|
+
*/
|
|
8
|
+
dataSource: T[];
|
|
9
|
+
/**
|
|
10
|
+
* 加载状态
|
|
11
|
+
*
|
|
12
|
+
* @memberof IPaginationResult
|
|
13
|
+
*/
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* 是否完成了一次请求
|
|
17
|
+
*
|
|
18
|
+
* @memberof IPaginationResult
|
|
19
|
+
*/
|
|
20
|
+
isFirstComplete: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 分页信息
|
|
23
|
+
*
|
|
24
|
+
* @memberof IPaginationResult
|
|
25
|
+
*/
|
|
26
|
+
paginationProps: {
|
|
27
|
+
current: number;
|
|
28
|
+
pageSize: number;
|
|
29
|
+
total: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* reset 是否重置查询
|
|
33
|
+
*
|
|
34
|
+
* @memberof IPaginationResult
|
|
35
|
+
*/
|
|
36
|
+
refresh: (reset?: boolean) => Promise<void>;
|
|
37
|
+
debounceRefresh: (reset?: boolean) => void;
|
|
38
|
+
setDataSource: (data: T[]) => void;
|
|
39
|
+
setTotal: (total: number) => void;
|
|
40
|
+
}
|
|
41
|
+
interface IServerParams {
|
|
42
|
+
offset: number;
|
|
43
|
+
limit: number;
|
|
44
|
+
current: number;
|
|
45
|
+
}
|
|
46
|
+
type IInfoBack<T> = {
|
|
47
|
+
dataSource: T[];
|
|
48
|
+
total: number;
|
|
49
|
+
};
|
|
50
|
+
type IInfoServer<T> = (params: IServerParams) => IInfoBack<T> | Promise<IInfoBack<T>>;
|
|
51
|
+
interface IInfoOption<T> {
|
|
52
|
+
/**
|
|
53
|
+
* 第一次是否执行server
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
isReady?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* 默认的数据
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
dataSource?: T[];
|
|
62
|
+
/**
|
|
63
|
+
* 默认当前第几页
|
|
64
|
+
*
|
|
65
|
+
*/
|
|
66
|
+
current?: number;
|
|
67
|
+
/**
|
|
68
|
+
* 默认一页几条数据
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
pageSize?: number;
|
|
72
|
+
}
|
|
73
|
+
export declare const useVirtualList: <T>(server: IInfoServer<T>, deps?: DependencyList, // 依赖条件 数据更新默认执行server
|
|
74
|
+
option?: IInfoOption<T>) => IPaginationResult<T>;
|
|
75
|
+
export {};
|