-
+
运行概况
}
diff --git a/src/apps/report-notifications/ReportNotificationsPage.test.tsx b/src/apps/report-notifications/ReportNotificationsPage.test.tsx
index 0b5ec0b..41e95dc 100644
--- a/src/apps/report-notifications/ReportNotificationsPage.test.tsx
+++ b/src/apps/report-notifications/ReportNotificationsPage.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen, waitFor } from '@testing-library/react';
+import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, expect, it, vi } from 'vitest';
import { ReportNotificationsPage } from './ReportNotificationsPage';
@@ -57,3 +57,21 @@ it('reports a failed read without pretending success', async () => {
expect(await screen.findByRole('alert')).toHaveTextContent('标记失败');
expect(screen.getByRole('button', { name: '标记已读' })).toBeEnabled();
});
+
+it('does not reopen a closed dialog when the read response arrives later', async () => {
+ const user = userEvent.setup();
+ render(
);
+ await user.click(await screen.findByRole('button', { name: '查看消息' }));
+ let finish!: (result: unknown) => void;
+ api.request.mockImplementationOnce(
+ () =>
+ new Promise((resolve) => {
+ finish = resolve;
+ }),
+ );
+ await user.click(await screen.findByRole('button', { name: '标记已读' }));
+ const closeButtons = screen.getAllByRole('button', { name: /^关闭$/ });
+ await user.click(closeButtons[closeButtons.length - 1]);
+ await act(async () => finish({ success: true }));
+ await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
+});
diff --git a/src/apps/report-notifications/ReportNotificationsPage.tsx b/src/apps/report-notifications/ReportNotificationsPage.tsx
index 1c4f129..0250d35 100644
--- a/src/apps/report-notifications/ReportNotificationsPage.tsx
+++ b/src/apps/report-notifications/ReportNotificationsPage.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
import { Breadcrumb, Button, Modal, Table, Tag, type TableColumn } from '@/components/ui';
import { request } from '@/api/core/httpClient';
import './report-notifications.css';
@@ -32,6 +32,12 @@ export function ReportNotificationsPage({ portal = 'client' }: { portal?: 'admin
const [detail, setDetail] = useState<{ hour: Hour; items: Event[]; total: number; page: number } | null>(null);
const [detailError, setDetailError] = useState('');
const [busy, setBusy] = useState(false);
+ const detailRequest = useRef(0);
+ const closeDetail = () => {
+ detailRequest.current++;
+ setDetail(null);
+ setBusy(false);
+ };
const base = `/${portal}/report-notifications`;
const load = useCallback(
async (signal?: AbortSignal) => {
@@ -54,32 +60,40 @@ export function ReportNotificationsPage({ portal = 'client' }: { portal?: 'admin
return () => controller.abort();
}, [load]);
async function open(item: Hour, detailPage = 1) {
+ const sequence = ++detailRequest.current;
setBusy(true);
setDetailError('');
try {
const result = await request<{ hour: Hour } & Page
>(`${base}/${item.id}?page=${detailPage}`);
+ if (sequence !== detailRequest.current) return;
setDetail({ ...result, hour: { ...result.hour, unread: item.unread } });
} catch (e) {
- setError(e instanceof Error ? e.message : '详情加载失败');
+ if (sequence !== detailRequest.current) return;
+ const message = e instanceof Error ? e.message : '详情加载失败';
+ if (detail) setDetailError(message);
+ else setError(message);
} finally {
- setBusy(false);
+ if (sequence === detailRequest.current) setBusy(false);
}
}
async function markRead() {
if (!detail) return;
+ const sequence = ++detailRequest.current;
+ setDetailError('');
setBusy(true);
try {
await request(`${base}/${detail.hour.id}/read`, {
method: 'POST',
body: JSON.stringify({ revision: detail.hour.revision }),
});
- setDetail({ ...detail, hour: { ...detail.hour, unread: false } });
+ if (sequence === detailRequest.current)
+ setDetail((current) => (current ? { ...current, hour: { ...current.hour, unread: false } } : null));
window.dispatchEvent(new Event('cmpp-report-notification-refresh'));
await load();
} catch (e) {
- setDetailError(e instanceof Error ? e.message : '标记已读失败');
+ if (sequence === detailRequest.current) setDetailError(e instanceof Error ? e.message : '标记已读失败');
} finally {
- setBusy(false);
+ if (sequence === detailRequest.current) setBusy(false);
}
}
const columns: TableColumn[] = [
@@ -163,10 +177,10 @@ export function ReportNotificationsPage({ portal = 'client' }: { portal?: 'admin
open
title="报备状态变化消息"
size="xl"
- onClose={() => setDetail(null)}
+ onClose={closeDetail}
footer={
<>
-