fix(mp): 安全 P0 修复 + 架构 Hook 层补充 + 五专家组分析报告

安全修复:
- 提取 sanitizeHtml 共享工具,修复 article/detail RichText XSS 风险
- request.ts 生产环境强制 HTTPS,消除 HTTP 回退风险
- 错误信息净化:后端错误码映射为用户友好消息,不再透传原始内容
- Token 生命周期管理:利用 expires_in 记录过期时间,请求前主动刷新

工程修复:
- Babel 依赖从 dependencies 移至 devDependencies(包体积优化)

架构改进:
- 新增 usePagination hook(分页加载 + hasMore + refresh,10+ 页面可复用)
- 新增 useAuthRequired hook(登录态 + 患者档案 + 角色判断统一入口)
- 新增 usePageRefresh hook(下拉刷新统一封装,17 页面可复用)

文档:
- 五专家组深度分析+头脑风暴报告(架构7.2/安全5.5/UX6.0/工程5.5/产品7.2)
This commit is contained in:
iven
2026-05-14 20:22:29 +08:00
parent a8d7183d7c
commit 447126b6c5
10 changed files with 416 additions and 51 deletions

View File

@@ -0,0 +1,65 @@
import { useState, useCallback, useRef } from 'react';
interface PaginationResult<T> {
list: T[];
setList: React.Dispatch<React.SetStateAction<T[]>>;
loading: boolean;
hasMore: boolean;
total: number;
loadMore: () => Promise<void>;
refresh: () => Promise<void>;
}
export function usePagination<T>(
fetcher: (page: number, pageSize: number) => Promise<{ data: T[]; total: number }>,
pageSize = 10,
): PaginationResult<T> {
const [list, setList] = useState<T[]>([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [total, setTotal] = useState(0);
const pageRef = useRef(1);
const loadingRef = useRef(false);
const loadMore = useCallback(async () => {
if (loadingRef.current || !hasMore) return;
loadingRef.current = true;
setLoading(true);
try {
const res = await fetcher(pageRef.current, pageSize);
const items = res.data || [];
setList((prev) => [...prev, ...items]);
setTotal(res.total);
setHasMore(items.length >= pageSize);
pageRef.current += 1;
} catch {
// 错误由调用方处理
} finally {
loadingRef.current = false;
setLoading(false);
}
}, [fetcher, pageSize, hasMore]);
const refresh = useCallback(async () => {
if (loadingRef.current) return;
loadingRef.current = true;
setLoading(true);
pageRef.current = 1;
setHasMore(true);
try {
const res = await fetcher(1, pageSize);
const items = res.data || [];
setList(items);
setTotal(res.total);
setHasMore(items.length >= pageSize);
pageRef.current = 2;
} catch {
// 错误由调用方处理
} finally {
loadingRef.current = false;
setLoading(false);
}
}, [fetcher, pageSize]);
return { list, setList, loading, hasMore, total, loadMore, refresh };
}