为错误报告列表增加分页

扩展管理员列表契约支持 offset、total 和 hasMore

后台页面增加页码状态、上一页和下一页操作
This commit is contained in:
2026-09-01 16:28:41 +08:00
parent a2827eb4cc
commit d9a7260996
3 changed files with 50 additions and 3 deletions
+5
View File
@@ -280,6 +280,7 @@ export function listAdminErrorReports(
fingerprint?: string;
source?: string;
limit?: number;
offset?: number;
} = {},
) {
return request<AdminErrorReportListResponse>(
@@ -866,6 +867,7 @@ function buildErrorReportQueryString(query: {
fingerprint?: string;
source?: string;
limit?: number;
offset?: number;
}) {
const params = new URLSearchParams();
appendQueryParam(params, 'status', query.status);
@@ -874,6 +876,9 @@ function buildErrorReportQueryString(query: {
if (typeof query.limit === 'number' && Number.isFinite(query.limit)) {
params.set('limit', String(query.limit));
}
if (typeof query.offset === 'number' && Number.isFinite(query.offset)) {
params.set('offset', String(query.offset));
}
const queryString = params.toString();
return queryString ? `?${queryString}` : '';
}
+4
View File
@@ -116,6 +116,10 @@ export interface AdminErrorReportEntry {
export interface AdminErrorReportListResponse {
reports: AdminErrorReportEntry[];
total: number;
offset: number;
limit: number;
hasMore: boolean;
}
export interface AdminErrorReportDetail extends AdminErrorReportEntry {
@@ -16,6 +16,7 @@ import type {
type Props = { token: string; onUnauthorized: (message?: string) => void };
const ADMIN_ERROR_REPORT_STATUSES = ['new', 'in-progress', 'resolved'] as const;
const ERROR_REPORT_PAGE_SIZE = 50;
export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
const [reports, setReports] = useState<AdminErrorReportEntry[]>([]);
@@ -23,6 +24,8 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
const [status, setStatus] = useState('');
const [busy, setBusy] = useState(false);
const [filterStatus, setFilterStatus] = useState('');
const [pageOffset, setPageOffset] = useState(0);
const [pageInfo, setPageInfo] = useState({ total: 0, hasMore: false });
const openReportRequestId = useRef(0);
const loadRequestId = useRef(0);
@@ -32,15 +35,20 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
try {
const response = await listAdminErrorReports(token, {
status: filterStatus || undefined,
limit: ERROR_REPORT_PAGE_SIZE,
offset: pageOffset,
});
if (requestId === loadRequestId.current) setReports(response.reports);
if (requestId === loadRequestId.current) {
setReports(response.reports);
setPageInfo({ total: response.total, hasMore: response.hasMore });
}
} catch (error) {
if (requestId !== loadRequestId.current) return;
if (isAdminApiError(error) && error.status === 401)
return onUnauthorized();
setStatus(formatAdminApiError(error));
}
}, [filterStatus, onUnauthorized, token]);
}, [filterStatus, onUnauthorized, pageOffset, token]);
useEffect(() => {
void load();
@@ -109,7 +117,10 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
{' '}
<select
value={filterStatus}
onChange={(event) => setFilterStatus(event.target.value)}
onChange={(event) => {
setFilterStatus(event.target.value);
setPageOffset(0);
}}
>
<option value=""></option>
{ADMIN_ERROR_REPORT_STATUSES.map((value) => (
@@ -162,6 +173,33 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
</tbody>
</table>
</div>
<div className="admin-detail-modal__actions" aria-label="错误报告分页">
<span>
{pageInfo.total === 0
? '暂无报告'
: `${pageOffset + 1}-${Math.min(pageOffset + reports.length, pageInfo.total)} 条,共 ${pageInfo.total}`}
</span>
<button
type="button"
onClick={() =>
setPageOffset((offset) =>
Math.max(0, offset - ERROR_REPORT_PAGE_SIZE),
)
}
disabled={pageOffset === 0}
>
</button>
<button
type="button"
onClick={() =>
setPageOffset((offset) => offset + ERROR_REPORT_PAGE_SIZE)
}
disabled={!pageInfo.hasMore}
>
</button>
</div>
{selected ? (
<div
className="admin-detail-modal"