This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 8d7201fe5 fix(web): recover pagination from server after deletes 
(#2992)
8d7201fe5 is described below

commit 8d7201fe528fba389373f41ff06865bce5ec8ad4
Author: aias00 <[email protected]>
AuthorDate: Fri Sep 4 15:40:50 2026 +0800

    fix(web): recover pagination from server after deletes (#2992)
    
    Topic, Consumer, and ACL pages should reconcile destructive actions through 
their server-backed pagination responses so counts, last-page recovery, and 
failed selections stay coherent after deletes.
    
    Constraint: base rocketmq-studio already includes #2841 page clamp 
behavior; preserve that behavior while adding #2687 reload semantics
    
    Rejected: merging the stale source branch | its historical base carries 
broad unrelated changes
    
    Tested: npm test -- src/pages/instance/__tests__/TopicPage.test.tsx 
src/pages/instance/__tests__/ConsumerPage.test.tsx 
src/pages/instance/__tests__/AclPage.test.tsx
    
    Tested: npm run lint
    
    Tested: npm run build
    
    Confidence: high
    
    Scope-risk: narrow
    
    Signed-off-by: liuhy <[email protected]>
---
 web/src/pages/instance/__tests__/AclPage.test.tsx  |  46 +++++++-
 .../pages/instance/__tests__/ConsumerPage.test.tsx | 116 +++++++++++++++++++++
 .../pages/instance/__tests__/TopicPage.test.tsx    | 116 ++++++++++++++++++++-
 web/src/pages/instance/acl.tsx                     |   6 +-
 web/src/pages/instance/consumer.tsx                |  68 ++++++------
 web/src/pages/instance/topic.tsx                   |  73 +++++++------
 6 files changed, 356 insertions(+), 69 deletions(-)

diff --git a/web/src/pages/instance/__tests__/AclPage.test.tsx 
b/web/src/pages/instance/__tests__/AclPage.test.tsx
index 309903968..e916f9c76 100644
--- a/web/src/pages/instance/__tests__/AclPage.test.tsx
+++ b/web/src/pages/instance/__tests__/AclPage.test.tsx
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-import { App, message } from 'antd';
+import { App, Modal, message } from 'antd';
 import { act, fireEvent, render, screen, waitFor, within } from 
'@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import type React from 'react';
@@ -131,6 +131,50 @@ describe('ACL page', () => {
     expect(aclService.pageAclUsers).toHaveBeenCalledTimes(1);
   });
 
+  it('reloads the server rule page after deleting one ACL rule', async () => {
+    const user = userEvent.setup();
+    const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
+      void config.onOk?.();
+      return { destroy: vi.fn(), update: vi.fn() } as unknown as 
ReturnType<typeof Modal.confirm>;
+    });
+    vi.mocked(aclService.deleteAclRule).mockResolvedValue(undefined);
+    vi.mocked(aclService.listAclRules)
+      .mockResolvedValueOnce({
+        items: [
+          {
+            id: 1,
+            principal: 'remote-user',
+            resource: 'remote-topic',
+            resourceType: 'Topic',
+            resourcePattern: 'LITERAL',
+            actions: ['PUB'],
+            decision: 'ALLOW',
+            scope: 'cluster',
+            aclVersion: 2,
+            gmtCreate: '2026-07-23T00:00:00Z',
+          },
+        ],
+        total: 1,
+        page: 1,
+        size: 20,
+      })
+      .mockResolvedValueOnce({
+        items: [],
+        total: 0,
+        page: 1,
+        size: 20,
+      });
+    renderWithProviders(<AclPage />);
+
+    const row = await screen.findByRole('row', { name: /remote-topic/ });
+    await user.click(within(row).getByRole('button', { name: /删除/ }));
+
+    await waitFor(() => 
expect(aclService.deleteAclRule).toHaveBeenCalledWith(1, undefined));
+    await waitFor(() => 
expect(aclService.listAclRules).toHaveBeenCalledTimes(2));
+    expect(screen.queryByText('remote-topic')).not.toBeInTheDocument();
+    confirmSpy.mockRestore();
+  });
+
   it('shows a non-copyable placeholder when an ACL user has no access key', 
async () => {
     const user = userEvent.setup();
     vi.mocked(aclService.pageAclUsers).mockResolvedValue({
diff --git a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx 
b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
index 761ea2858..12f8e9256 100644
--- a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
+++ b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
@@ -330,6 +330,122 @@ describe('Consumer page', () => {
     );
   });
 
+  it('reloads the server page after deleting one consumer group', async () => {
+    const user = userEvent.setup();
+    const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
+      void config.onOk?.();
+      return { destroy: vi.fn(), update: vi.fn() } as unknown as 
ReturnType<typeof Modal.confirm>;
+    });
+    
vi.mocked(consumerService.deleteConsumerGroup).mockResolvedValue(undefined);
+    vi.mocked(consumerService.listConsumerGroupPage)
+      .mockResolvedValueOnce(groupPage([group]))
+      .mockResolvedValueOnce(groupPage([]));
+    renderWithProviders(<ConsumerPage />);
+
+    const row = await screen.findByRole('row', { name: /remote-cg/ });
+    await user.click(within(row).getByRole('button', { name: /删除/ }));
+
+    await waitFor(() =>
+      
expect(consumerService.deleteConsumerGroup).toHaveBeenCalledWith('remote-cg', 
'instance-1'),
+    );
+    await waitFor(() => 
expect(consumerService.listConsumerGroupPage).toHaveBeenCalledTimes(2));
+    expect(screen.getByText('管理消费者组订阅关系与消费进度,共 0 个 
Group')).toBeInTheDocument();
+    confirmSpy.mockRestore();
+  });
+
+  it('moves back from an emptied last consumer group page after batch 
deletion', async () => {
+    const user = userEvent.setup();
+    const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
+      void config.onOk?.();
+      return { destroy: vi.fn(), update: vi.fn() } as unknown as 
ReturnType<typeof Modal.confirm>;
+    });
+    const firstPage = buildGroups(20).map((item, index) => ({
+      ...item,
+      name: `cg-${String(index + 1).padStart(2, '0')}`,
+    }));
+    const lastGroup = { ...group, name: 'cg-21' };
+    let deletedLastPage = false;
+    vi.mocked(consumerService.listConsumerGroupPage).mockImplementation(async 
(params) => {
+      if (params?.page === 2) {
+        return groupPage(deletedLastPage ? [] : [lastGroup], {
+          total: deletedLastPage ? 20 : 21,
+          page: 2,
+          size: 20,
+        });
+      }
+      return groupPage(firstPage, { total: deletedLastPage ? 20 : 21, page: 1, 
size: 20 });
+    });
+    
vi.mocked(consumerService.batchDeleteConsumerGroups).mockImplementation(async 
() => {
+      deletedLastPage = true;
+      return {
+        deleted: ['cg-21'],
+        failed: [],
+      };
+    });
+    renderWithProviders(<ConsumerPage />);
+
+    expect(await screen.findByText('cg-01')).toBeInTheDocument();
+    await user.click(document.querySelector('.ant-pagination-item-2') as 
HTMLElement);
+    expect(await screen.findByText('cg-21')).toBeInTheDocument();
+    await user.click(screen.getAllByRole('checkbox')[0]);
+    await user.click(screen.getByRole('button', { name: /删除 \(1\)$/ }));
+
+    await waitFor(() =>
+      expect(consumerService.batchDeleteConsumerGroups).toHaveBeenCalledWith(
+        ['cg-21'],
+        'instance-1',
+      ),
+    );
+    await waitFor(() => 
expect(screen.queryByText('cg-21')).not.toBeInTheDocument());
+    expect(screen.getByText('cg-01')).toBeInTheDocument();
+    expect(consumerService.listConsumerGroupPage).toHaveBeenLastCalledWith({
+      instanceId: 'instance-1',
+      search: undefined,
+      page: 1,
+      pageSize: 20,
+    });
+    confirmSpy.mockRestore();
+  });
+
+  it('keeps failed consumer groups selected after a partially successful batch 
deletion', async () => {
+    const user = userEvent.setup();
+    const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
+      void config.onOk?.();
+      return { destroy: vi.fn(), update: vi.fn() } as unknown as 
ReturnType<typeof Modal.confirm>;
+    });
+    const groups = ['cg-01', 'cg-02', 'cg-03'].map((name) => ({ ...group, name 
}));
+    vi.mocked(consumerService.listConsumerGroupPage)
+      .mockResolvedValueOnce(groupPage(groups))
+      .mockResolvedValueOnce(groupPage([{ ...group, name: 'cg-02' }]));
+    vi.mocked(consumerService.batchDeleteConsumerGroups).mockResolvedValue({
+      deleted: ['cg-01', 'cg-03'],
+      failed: ['cg-02'],
+    });
+    renderWithProviders(<ConsumerPage />);
+
+    expect(await screen.findByText('cg-01')).toBeInTheDocument();
+    await user.click(screen.getAllByRole('checkbox')[0]);
+    await user.click(screen.getByRole('button', { name: /删除 \(3\)$/ }));
+
+    await waitFor(() =>
+      expect(consumerService.batchDeleteConsumerGroups).toHaveBeenCalledWith(
+        ['cg-01', 'cg-02', 'cg-03'],
+        'instance-1',
+      ),
+    );
+    await waitFor(() => 
expect(screen.queryByText('cg-01')).not.toBeInTheDocument());
+    expect(screen.getByText('cg-02')).toBeInTheDocument();
+    expect(screen.queryByText('cg-03')).not.toBeInTheDocument();
+    expect(screen.getByRole('button', { name: /删除 \(1\)$/ 
})).toBeInTheDocument();
+    expect(consumerService.listConsumerGroupPage).toHaveBeenLastCalledWith({
+      instanceId: 'instance-1',
+      search: undefined,
+      page: 1,
+      pageSize: 20,
+    });
+    confirmSpy.mockRestore();
+  });
+
   it('submits the canonical global delivery order type', async () => {
     const user = userEvent.setup();
     const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
diff --git a/web/src/pages/instance/__tests__/TopicPage.test.tsx 
b/web/src/pages/instance/__tests__/TopicPage.test.tsx
index b230c8ef7..ed4f1f301 100644
--- a/web/src/pages/instance/__tests__/TopicPage.test.tsx
+++ b/web/src/pages/instance/__tests__/TopicPage.test.tsx
@@ -395,6 +395,43 @@ describe('TopicPage', () => {
     expect(within(getTableBody()).getByText('topic-15')).toBeInTheDocument();
   });
 
+  it('reloads the server page after deleting one topic', async () => {
+    const user = userEvent.setup();
+    const topic = buildTopics(1)[0];
+    let call = 0;
+    topicServiceMocks.listTopicsPage.mockImplementation(async () => {
+      call += 1;
+      return call === 1
+        ? {
+            items: [topic],
+            total: 1,
+            page: 1,
+            size: 20,
+          }
+        : {
+            items: [],
+            total: 0,
+            page: 1,
+            size: 20,
+          };
+    });
+    topicServiceMocks.deleteTopic.mockResolvedValue(undefined);
+    renderWithProviders();
+
+    const row = await screen.findByRole('row', { name: /topic-01/ });
+    await user.click(within(row).getByRole('button', { name: /删除/ }));
+    const dialog = (await screen.findByText(/确定要删除 Topic「topic-01」/)).closest(
+      '.ant-modal',
+    ) as HTMLElement;
+    await user.click(within(dialog).getByRole('button', { name: /删\s*除/ }));
+
+    await waitFor(() =>
+      expect(topicServiceMocks.deleteTopic).toHaveBeenCalledWith('topic-01', 
'instance-proxy-1'),
+    );
+    await waitFor(() => 
expect(topicServiceMocks.listTopicsPage).toHaveBeenCalledTimes(2));
+    expect(screen.getByText('共 0 个 Topic')).toBeInTheDocument();
+  });
+
   it('keeps the selected instance when rebuilding a topic without a broker 
route', async () => {
     const user = userEvent.setup();
     const topic = { ...buildTopics(1)[0], instanceId: 'instance-a' };
@@ -482,7 +519,20 @@ describe('TopicPage', () => {
 
   it('keeps failed topics selected after a partially successful batch 
deletion', async () => {
     const user = userEvent.setup();
-    mockTopicsList(buildTopics(3));
+    const remainingTopics = [buildTopics(3)[1]];
+    topicServiceMocks.listTopicsPage
+      .mockResolvedValueOnce({
+        items: buildTopics(3),
+        total: 3,
+        page: 1,
+        size: 20,
+      })
+      .mockResolvedValueOnce({
+        items: remainingTopics,
+        total: 1,
+        page: 1,
+        size: 20,
+      });
     topicServiceMocks.batchDeleteTopics.mockResolvedValue({
       deleted: ['topic-01', 'topic-03'],
       failed: ['topic-02'],
@@ -501,6 +551,70 @@ describe('TopicPage', () => {
     expect(screen.queryByText('topic-03')).not.toBeInTheDocument();
     expect(screen.getByRole('button', { name: /删除 \(1\)$/ 
})).toBeInTheDocument();
     expect(screen.getByText('已删除 2 个 Topic,1 个删除失败')).toBeInTheDocument();
+    expect(topicServiceMocks.listTopicsPage).toHaveBeenLastCalledWith({
+      instanceId: 'instance-proxy-1',
+      type: undefined,
+      search: undefined,
+      page: 1,
+      pageSize: 20,
+    });
+  });
+
+  it('moves back from an emptied last topic page after batch deletion', async 
() => {
+    const user = userEvent.setup();
+    const firstPage = buildTopics(20);
+    const secondPage = [buildTopics(21)[20]];
+    let deletedLastPage = false;
+    topicServiceMocks.listTopicsPage.mockImplementation(async (params) => {
+      if (params?.page === 2) {
+        return {
+          items: deletedLastPage ? [] : secondPage,
+          total: deletedLastPage ? 20 : 21,
+          page: 2,
+          size: 20,
+        };
+      }
+      return {
+        items: firstPage,
+        total: deletedLastPage ? 20 : 21,
+        page: 1,
+        size: 20,
+      };
+    });
+    topicServiceMocks.batchDeleteTopics.mockImplementation(async () => {
+      deletedLastPage = true;
+      return {
+        deleted: ['topic-21'],
+        failed: [],
+      };
+    });
+    renderWithProviders();
+
+    expect(await screen.findByText('topic-01')).toBeInTheDocument();
+    await user.click(document.querySelector('.ant-pagination-item-2') as 
HTMLElement);
+    expect(await screen.findByText('topic-21')).toBeInTheDocument();
+    await user.click(screen.getAllByRole('checkbox')[0]);
+    await user.click(screen.getByRole('button', { name: /删除 \(1\)$/ }));
+    const dialog = (await screen.findByText(/确定要删除选中的 1 个 Topic/)).closest(
+      '.ant-modal',
+    ) as HTMLElement;
+    await user.click(within(dialog).getByRole('button', { name: /删\s*除/ }));
+
+    await waitFor(() =>
+      expect(topicServiceMocks.batchDeleteTopics).toHaveBeenCalledWith(
+        ['topic-21'],
+        'instance-proxy-1',
+      ),
+    );
+    await waitFor(() => 
expect(screen.queryByText('topic-21')).not.toBeInTheDocument());
+    expect(screen.getByText('topic-01')).toBeInTheDocument();
+    expect(topicServiceMocks.listTopicsPage).toHaveBeenLastCalledWith({
+      instanceId: 'instance-proxy-1',
+      type: undefined,
+      search: undefined,
+      page: 1,
+      pageSize: 20,
+    });
   });
 
   it('filters topics by the instance from the route and shows its endpoint', 
async () => {
diff --git a/web/src/pages/instance/acl.tsx b/web/src/pages/instance/acl.tsx
index 58f8924d1..c04566cc4 100644
--- a/web/src/pages/instance/acl.tsx
+++ b/web/src/pages/instance/acl.tsx
@@ -340,11 +340,7 @@ const AclPageContent = ({
   const handleDeleteRule = async (id: AclEntityId) => {
     try {
       await deleteAclRule(id, selectedInstanceId);
-      if (rules.length === 1 && rulePage > 1) {
-        setRulePage((prev) => prev - 1);
-      } else {
-        setRuleRefreshKey((prev) => prev + 1);
-      }
+      setRuleRefreshKey((prev) => prev + 1);
       message.success(t('acl.ruleDeleted'));
     } catch {
       message.error(t('common.operationFailed'));
diff --git a/web/src/pages/instance/consumer.tsx 
b/web/src/pages/instance/consumer.tsx
index 7ddf7da24..2c9c9b998 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -327,6 +327,40 @@ const ConsumerPageContent = ({
   }, []);
   const selectedGroupName = selectedGroup?.name;
 
+  const loadConsumerGroupPage = useCallback(
+    async (pageToLoad: number, pageSizeToLoad: number, silent = false) => {
+      if (!selectedInstanceId) return undefined;
+      const requestId = ++groupRequestIdRef.current;
+      if (!silent) setLoading(true);
+      try {
+        const result = await listConsumerGroupPage({
+          instanceId: selectedInstanceId,
+          search: search.trim() || undefined,
+          page: pageToLoad,
+          pageSize: pageSizeToLoad,
+        });
+        if (requestId === groupRequestIdRef.current) {
+          setGroups(result.items);
+          setTotalGroups(result.total);
+          if (result.items.length === 0 && result.total > 0 && pageToLoad > 1) 
{
+            setPage(Math.max(1, Math.ceil(result.total / pageSizeToLoad)));
+          }
+        }
+        return requestId === groupRequestIdRef.current ? result : undefined;
+      } catch {
+        if (requestId === groupRequestIdRef.current) 
message.error(t('consumer.fetchListFailed'));
+        return undefined;
+      } finally {
+        if (requestId === groupRequestIdRef.current) setLoading(false);
+      }
+    },
+    [t, selectedInstanceId, search],
+  );
+
+  const reloadConsumerGroupPageAfterDelete = useCallback(async () => {
+    await loadConsumerGroupPage(page, pageSize);
+  }, [loadConsumerGroupPage, page, pageSize]);
+
   useEffect(() => {
     if (!selectedInstanceId) {
       groupRequestIdRef.current += 1;
@@ -342,35 +376,13 @@ const ConsumerPageContent = ({
     }
     const silent = silentRefreshRef.current;
     silentRefreshRef.current = false;
-    const requestId = ++groupRequestIdRef.current;
     const timer = window.setTimeout(() => {
-      if (!silent) setLoading(true);
-      void listConsumerGroupPage({
-        instanceId: selectedInstanceId,
-        search: search.trim() || undefined,
-        page,
-        pageSize,
-      })
-        .then((result) => {
-          if (requestId === groupRequestIdRef.current) {
-            setGroups(result.items);
-            setTotalGroups(result.total);
-            if (result.items.length === 0 && result.total > 0 && page > 1) {
-              setPage(Math.max(1, Math.ceil(result.total / pageSize)));
-            }
-          }
-        })
-        .catch(() => {
-          if (requestId === groupRequestIdRef.current) 
message.error(t('consumer.fetchListFailed'));
-        })
-        .finally(() => {
-          if (requestId === groupRequestIdRef.current) setLoading(false);
-        });
+      void loadConsumerGroupPage(page, pageSize, silent);
     }, 0);
     return () => {
       window.clearTimeout(timer);
     };
-  }, [t, selectedInstanceId, search, page, pageSize, instancesLoading, 
refreshKey]);
+  }, [selectedInstanceId, page, pageSize, instancesLoading, refreshKey, 
loadConsumerGroupPage]);
 
   useEffect(() => {
     if (!autoRefresh || !selectedInstanceId) {
@@ -978,7 +990,7 @@ const ConsumerPageContent = ({
                 cancelText: '取消',
                 onOk: async () => {
                   await deleteConsumerGroup(record.name, selectedInstanceId || 
undefined);
-                  setGroups((prev) => prev.filter((group) => group.name !== 
record.name));
+                  await reloadConsumerGroupPageAfterDelete();
                   setSelectedRowKeys((prev) => prev.filter((key) => key !== 
record.name));
                   message.success(`消费组 ${record.name} 已删除`);
                 },
@@ -1409,14 +1421,12 @@ const ConsumerPageContent = ({
                       names,
                       selectedInstanceId || undefined,
                     );
-                    setGroups((prev) => prev.filter((g) => 
!deleted.includes(g.name)));
+                    if (deleted.length > 0) await 
reloadConsumerGroupPageAfterDelete();
                     if (failed.length > 0) {
                       message.warning(
                         `已删除 ${deleted.length} 个,失败 ${failed.length} 
个:${failed.join(', ')}`,
                       );
-                      setSelectedRowKeys((prev) =>
-                        prev.filter((key) => !deleted.includes(String(key))),
-                      );
+                      setSelectedRowKeys(failed);
                     } else {
                       message.success(`已删除 ${deleted.length} 个 Group`);
                       setSelectedRowKeys([]);
diff --git a/web/src/pages/instance/topic.tsx b/web/src/pages/instance/topic.tsx
index 8ec7335f8..e029b5da7 100644
--- a/web/src/pages/instance/topic.tsx
+++ b/web/src/pages/instance/topic.tsx
@@ -431,6 +431,42 @@ const TopicPage = () => {
     ],
   );
 
+  const loadTopicPage = useCallback(
+    async (pageToLoad: number, pageSizeToLoad: number) => {
+      if (!selectedInstanceId) return undefined;
+      const requestId = ++topicRequestIdRef.current;
+      setLoading(true);
+      try {
+        const result = await listTopicsPage({
+          instanceId: selectedInstanceId,
+          type: typeFilter || undefined,
+          search: searchText.trim() || undefined,
+          page: pageToLoad,
+          pageSize: pageSizeToLoad,
+        });
+        if (requestId === topicRequestIdRef.current) {
+          setTopics(result.items);
+          setTotalTopics(result.total);
+          if (result.items.length === 0 && result.total > 0 && pageToLoad > 1) 
{
+            setTablePage(Math.max(1, Math.ceil(result.total / 
pageSizeToLoad)));
+          }
+        }
+        return requestId === topicRequestIdRef.current ? result : undefined;
+      } catch {
+        if (requestId === topicRequestIdRef.current)
+          message.error('Topic 列表加载失败,请稍后重试');
+        return undefined;
+      } finally {
+        if (requestId === topicRequestIdRef.current) setLoading(false);
+      }
+    },
+    [selectedInstanceId, typeFilter, searchText],
+  );
+
+  const reloadTopicPageAfterDelete = useCallback(async () => {
+    await loadTopicPage(tablePage, tablePageSize);
+  }, [loadTopicPage, tablePage, tablePageSize]);
+
   useEffect(() => {
     if (!selectedInstanceId) {
       topicRequestIdRef.current += 1;
@@ -444,38 +480,14 @@ const TopicPage = () => {
         window.clearTimeout(resetTimer);
       };
     }
-    const requestId = ++topicRequestIdRef.current;
     const timer = window.setTimeout(() => {
-      setLoading(true);
-      void listTopicsPage({
-        instanceId: selectedInstanceId,
-        type: typeFilter || undefined,
-        search: searchText.trim() || undefined,
-        page: tablePage,
-        pageSize: tablePageSize,
-      })
-        .then((result) => {
-          if (requestId === topicRequestIdRef.current) {
-            setTopics(result.items);
-            setTotalTopics(result.total);
-            if (result.items.length === 0 && result.total > 0 && tablePage > 
1) {
-              setTablePage(Math.max(1, Math.ceil(result.total / 
tablePageSize)));
-            }
-          }
-        })
-        .catch(() => {
-          if (requestId === topicRequestIdRef.current)
-            message.error('Topic 列表加载失败,请稍后重试');
-        })
-        .finally(() => {
-          if (requestId === topicRequestIdRef.current) setLoading(false);
-        });
+      void loadTopicPage(tablePage, tablePageSize);
     }, 0);
 
     return () => {
       window.clearTimeout(timer);
     };
-  }, [selectedInstanceId, typeFilter, searchText, tablePage, tablePageSize, 
instancesLoading]);
+  }, [selectedInstanceId, tablePage, tablePageSize, instancesLoading, 
loadTopicPage]);
 
   // ─── Filtered data ─────────────────────────────────────────────
   const filteredTopics = useMemo(
@@ -642,7 +654,7 @@ const TopicPage = () => {
         onOk: async () => {
           try {
             await deleteTopic(topic.name, selectedInstanceId || undefined);
-            setTopics((previous) => previous.filter((item) => item.name !== 
topic.name));
+            await reloadTopicPageAfterDelete();
             message.success(`Topic「${topic.name}」已删除`);
           } catch {
             message.error('删除 Topic 失败,请稍后重试');
@@ -1500,12 +1512,7 @@ const TopicPage = () => {
                         names,
                         selectedInstanceId || undefined,
                       );
-                      if (deleted.length > 0) {
-                        const deletedNames = new Set(deleted);
-                        setTopics((previous) =>
-                          previous.filter((topic) => 
!deletedNames.has(topic.name)),
-                        );
-                      }
+                      if (deleted.length > 0) await 
reloadTopicPageAfterDelete();
                       setSelectedRowKeys(failed);
 
                       if (failed.length === 0) {

Reply via email to