Files
hms/apps/web/src/hooks/usePaginatedData.ts
iven 0bf1822fa9
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
fix: QA 第二轮修复 — PatientDetail 重构/测试覆盖/id_number 列宽/小程序 URL 规范化
- refactor(web): PatientDetail.tsx 拆分为 4 个子组件(737→334行)
- refactor(web): 提取 usePaginatedData hook 消除重复分页状态
- feat(db): patient.id_number varchar(20)→varchar(255) 容纳加密值
- test(health): 添加预约模块集成测试(创建/列表/租户隔离)
- test(plugin): 添加 6 个 SQL 注入 sanitize 测试
- fix(miniprogram): 7 个 service 文件 URL 构建规范化(params 对象)
- fix(miniprogram): 跨平台字段名对齐(birth_date/start_time/end_time)
2026-04-25 10:22:44 +08:00

78 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useCallback, useRef, useEffect } from 'react';
import { message } from 'antd';
interface PaginatedState<T> {
data: T[];
total: number;
page: number;
loading: boolean;
}
/**
* 通用分页数据 Hook封装 data / total / page / loading / fetch 逻辑。
*
* 支持两种签名:
* 1. 三参数 (page, pageSize, search) — 带搜索的列表页
* 2. 两参数 (page, pageSize) — 纯分页,不含搜索
*
* @param fetchFn - 数据获取函数
* @param pageSize - 每页条数,默认 20
* @param autoFetch - 是否在 mount / fetchFn 变化时自动请求第一页,默认 true
*/
export function usePaginatedData<T>(
fetchFn:
| ((page: number, pageSize: number, search: string) => Promise<{ data: T[]; total: number }>)
| ((page: number, pageSize: number) => Promise<{ data: T[]; total: number }>),
pageSize = 20,
autoFetch = true,
) {
const [state, setState] = useState<PaginatedState<T>>({
data: [],
total: 0,
page: 1,
loading: false,
});
const [searchText, setSearchText] = useState('');
// 用 ref 保存最新 fetchFn避免 refresh 因闭包引用过期 fetchFn 而频繁重建
const fetchFnRef = useRef(fetchFn);
fetchFnRef.current = fetchFn;
// 用 ref 保存最新 searchText同理
const searchTextRef = useRef(searchText);
searchTextRef.current = searchText;
const refresh = useCallback(
async (p?: number) => {
const targetPage = p ?? state.page;
setState((s) => ({ ...s, loading: true }));
try {
// 统一按三参数调用;若 fetchFn 只接受两参数,第三个参数会被忽略
const result = await (fetchFnRef.current as (
page: number,
pageSize: number,
search: string,
) => Promise<{ data: T[]; total: number }>)(
targetPage,
pageSize,
searchTextRef.current,
);
setState({ data: result.data, total: result.total, page: targetPage, loading: false });
} catch {
message.error('加载数据失败');
setState((s) => ({ ...s, loading: false }));
}
},
[pageSize, state.page],
);
// mount 或 fetchFn 变化时自动请求
useEffect(() => {
if (autoFetch) {
refresh(1);
}
}, [autoFetch, refresh]);
return { ...state, searchText, setSearchText, refresh };
}