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

Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new a54be93ff5 [fix] keep monitor selection across pages in monitor list 
(#4170)
a54be93ff5 is described below

commit a54be93ff5ee849b91f4d6aed0a7c7dea40a955d
Author: Duansg <[email protected]>
AuthorDate: Sun Jun 28 00:02:18 2026 +0800

    [fix] keep monitor selection across pages in monitor list (#4170)
    
    Co-authored-by: aias00 <[email protected]>
---
 .../monitor-list/monitor-list.component.html       | 37 +++++++++++------
 .../monitor-list/monitor-list.component.less       | 16 ++++++--
 .../monitor/monitor-list/monitor-list.component.ts | 48 ++++++++++++++++------
 web-app/src/assets/i18n/en-US.json                 |  3 ++
 web-app/src/assets/i18n/ja-JP.json                 |  3 ++
 web-app/src/assets/i18n/pt-BR.json                 |  3 ++
 web-app/src/assets/i18n/zh-CN.json                 |  3 ++
 web-app/src/assets/i18n/zh-TW.json                 |  3 ++
 8 files changed, 86 insertions(+), 30 deletions(-)

diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
index 38fa55f548..0990366e09 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.html
@@ -140,19 +140,6 @@
 </app-toolbar>
 
 <div class="monitor-card-list">
-  <div class="monitor-list-header" *ngIf="monitors?.length">
-    <div class="monitor-header-actions">
-      <label nz-checkbox [(ngModel)]="checkedAll" 
(ngModelChange)="onAllChecked($event)"></label>
-      <nz-pagination
-        [(nzPageIndex)]="pageIndex"
-        [nzPageSize]="pageSize"
-        [nzTotal]="total"
-        (nzPageIndexChange)="onPageIndexChange($event)"
-        nzSimple
-      ></nz-pagination>
-    </div>
-  </div>
-
   <nz-spin [nzSpinning]="tableLoading">
     <div class="monitor-card-list-content">
       <div
@@ -293,6 +280,30 @@
       </div>
     </div>
   </nz-spin>
+
+  <div class="monitor-list-footer" *ngIf="monitors?.length">
+    <div class="monitor-footer-actions">
+      <ng-container *ngIf="checkedMonitorIds.size > 0">
+        <label nz-checkbox [ngModel]="checkedAll" 
(ngModelChange)="onAllChecked($event)">
+          {{ 'monitor.select-current-page' | i18n }}
+        </label>
+        <span class="monitor-selected-count">{{ 'monitor.selected-count' | 
i18n : { count: checkedMonitorIds.size } }}</span>
+        <button nz-button nzType="link" nzSize="small" 
(click)="clearSelection()">
+          {{ 'monitor.clear-selection' | i18n }}
+        </button>
+      </ng-container>
+    </div>
+    <nz-pagination
+      [(nzPageIndex)]="pageIndex"
+      [nzPageSize]="pageSize"
+      [nzTotal]="total"
+      [nzShowTotal]="rangeTemplate"
+      nzShowSizeChanger
+      [nzPageSizeOptions]="pageSizeOptions"
+      (nzPageIndexChange)="onPageIndexChange($event)"
+      (nzPageSizeChange)="onPageSizeChange($event)"
+    ></nz-pagination>
+  </div>
 </div>
 
 <ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} 
</ng-template>
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
index 9a288de84b..a47dbb8485 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.less
@@ -86,19 +86,27 @@
   position: relative;
 }
 
-.monitor-list-header {
+.monitor-selected-count {
+  color: rgba(0, 0, 0, 0.45);
+  font-size: 12px;
+}
+
+.monitor-list-footer {
   display: flex;
-  align-items: center;
   justify-content: space-between;
-  margin-bottom: 16px;
+  align-items: center;
+  margin-top: 16px;
   padding: 8px 0;
   flex-wrap: wrap;
   gap: 8px;
 }
 
-.monitor-header-actions {
+.monitor-footer-actions {
   display: flex;
   align-items: center;
+  gap: 8px;
+  // Align the select-all checkbox with the per-row checkboxes, which sit at 
the card's 12px padding.
+  padding-left: 12px;
 }
 
 .monitor-card-list-content {
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
index ef58fd6afd..2ab3443179 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts
@@ -53,7 +53,8 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
   app!: string | undefined;
   labels!: string | undefined;
   pageIndex: number = 1;
-  pageSize: number = 8;
+  pageSize: number = 10;
+  pageSizeOptions: number[] = [10, 20, 50];
   total: number = 0;
   monitors!: Monitor[];
   tableLoading: boolean = true;
@@ -98,7 +99,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
         this.app = undefined;
       }
       this.pageIndex = 1;
-      this.pageSize = 8;
+      this.pageSize = 10;
       this.checkedMonitorIds = new Set<number>();
       this.tableLoading = true;
       this.loadMonitorTable();
@@ -134,7 +135,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
         message => {
           filter$.unsubscribe();
           this.tableLoading = false;
-          this.checkedAll = false;
+          // Filtering changes the result set, so the previous selection no 
longer applies.
           this.checkedMonitorIds.clear();
           if (message.code === 0) {
             let page = message.data;
@@ -180,8 +181,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
       .subscribe(
         message => {
           this.tableLoading = false;
-          this.checkedAll = false;
-          this.checkedMonitorIds.clear();
+          // Keep the selection across reloads (pagination, auto-refresh, 
post-CRUD) so multi-page checks persist (#4164).
           if (message.code === 0) {
             let page = message.data;
             this.monitors = reconcile ? 
this.reconcileMonitorStates(page.content) : 
this.resetMonitorStates(page.content);
@@ -205,8 +205,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
       .subscribe(
         message => {
           this.tableLoading = false;
-          this.checkedAll = false;
-          this.checkedMonitorIds.clear();
+          // Keep the selection across pagination so checks made on other 
pages persist (#4164).
           if (message.code === 0) {
             let page = message.data;
             // Pagination changes the result set, so reconcile here would flag 
the previous page as "disappeared" (#4156).
@@ -305,6 +304,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
         deleteMonitors$.unsubscribe();
         if (message.code === 0) {
           
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
+          monitors.forEach(id => this.checkedMonitorIds.delete(id));
           this.updatePageIndex(monitors.size);
           this.loadMonitorTable();
         } else {
@@ -450,6 +450,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
         cancelManage$.unsubscribe();
         if (message.code === 0) {
           
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.cancel-success'), '');
+          monitors.forEach(id => this.checkedMonitorIds.delete(id));
           this.loadMonitorTable();
         } else {
           this.tableLoading = false;
@@ -511,6 +512,7 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
         enableManage$.unsubscribe();
         if (message.code === 0) {
           
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.enable-success'), '');
+          monitors.forEach(id => this.checkedMonitorIds.delete(id));
           this.loadMonitorTable();
         } else {
           this.tableLoading = false;
@@ -537,14 +539,28 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
   }
 
   // begin: List multiple choice paging
-  checkedAll: boolean = false;
+  // Reflects only the current page: checked when every selectable monitor on 
this page is selected.
+  get checkedAll(): boolean {
+    const selectable = this.monitors?.filter(monitor => 
!this.isMonitorDisabled(monitor)) ?? [];
+    return selectable.length > 0 && selectable.every(monitor => 
this.checkedMonitorIds.has(monitor.id));
+  }
 
   onAllChecked(checked: boolean) {
-    if (checked) {
-      this.monitors.forEach(monitor => this.checkedMonitorIds.add(monitor.id));
-    } else {
-      this.checkedMonitorIds.clear();
-    }
+    // Only toggle the current page's selectable monitors, leaving selections 
on other pages untouched (#4164).
+    this.monitors
+      ?.filter(monitor => !this.isMonitorDisabled(monitor))
+      .forEach(monitor => {
+        if (checked) {
+          this.checkedMonitorIds.add(monitor.id);
+        } else {
+          this.checkedMonitorIds.delete(monitor.id);
+        }
+      });
+  }
+
+  // Clear the whole cross-page selection in one click, so users aren't forced 
to page back to deselect (#4164).
+  clearSelection() {
+    this.checkedMonitorIds.clear();
   }
 
   onItemChecked(monitorId: number, checked: boolean) {
@@ -566,6 +582,12 @@ export class MonitorListComponent implements OnInit, 
OnDestroy {
     this.changeMonitorTable(this.currentSortField, this.currentSortOrder);
   }
 
+  onPageSizeChange(pageSize: number) {
+    this.pageSize = pageSize;
+    this.pageIndex = 1;
+    this.changeMonitorTable(this.currentSortField, this.currentSortOrder);
+  }
+
   // begin: app type search filter
 
   onSearchAppClicked() {
diff --git a/web-app/src/assets/i18n/en-US.json 
b/web-app/src/assets/i18n/en-US.json
index f3a8781df1..b4ad4a6677 100644
--- a/web-app/src/assets/i18n/en-US.json
+++ b/web-app/src/assets/i18n/en-US.json
@@ -863,6 +863,9 @@
   "monitor.edit.success": "Update Monitor Success",
   "monitor.enable": "Enable",
   "monitor.export": "Export Selected",
+  "monitor.selected-count": "{{count}} selected",
+  "monitor.clear-selection": "Clear",
+  "monitor.select-current-page": "Select this page",
   "monitor.export-all": "Export All",
   "monitor.export.switch-type": "Please select the export file format",
   "monitor.export.use-type": "Export selected monitors in {{type}} format",
diff --git a/web-app/src/assets/i18n/ja-JP.json 
b/web-app/src/assets/i18n/ja-JP.json
index 1d0fc78779..ad17160459 100644
--- a/web-app/src/assets/i18n/ja-JP.json
+++ b/web-app/src/assets/i18n/ja-JP.json
@@ -822,6 +822,9 @@
   "monitor.edit.success": "モニターの更新に成功しました",
   "monitor.enable": "モニターを再開",
   "monitor.export": "モニターをエクスポート",
+  "monitor.selected-count": "{{count}} 件選択中",
+  "monitor.clear-selection": "クリア",
+  "monitor.select-current-page": "現在のページを全選択",
   "monitor.export.switch-type": "エクスポートファイル形式を選択してください!",
   "monitor.export.use-type": "{{type}}ファイル形式でモニターをエクスポート",
   "monitor.grafana.enabled.label": "Grafanaを有効化",
diff --git a/web-app/src/assets/i18n/pt-BR.json 
b/web-app/src/assets/i18n/pt-BR.json
index c6d1e0da48..72bff765b1 100644
--- a/web-app/src/assets/i18n/pt-BR.json
+++ b/web-app/src/assets/i18n/pt-BR.json
@@ -634,6 +634,9 @@
   "monitor.edit.success": "Monitor modificado com sucesso",
   "monitor.enable": "Retomar monitoramento",
   "monitor.export": "Exportar monitor",
+  "monitor.selected-count": "{{count}} selecionado(s)",
+  "monitor.clear-selection": "Limpar",
+  "monitor.select-current-page": "Selecionar esta página",
   "monitor.export.switch-type": "Selecione o formato do arquivo de 
exportação!",
   "monitor.export.use-type": "Exportar monitor no formato {{type}}",
   "monitor.grafana.enabled.label": "Habilitar Grafana",
diff --git a/web-app/src/assets/i18n/zh-CN.json 
b/web-app/src/assets/i18n/zh-CN.json
index 6d4a71744d..768473d64f 100644
--- a/web-app/src/assets/i18n/zh-CN.json
+++ b/web-app/src/assets/i18n/zh-CN.json
@@ -866,6 +866,9 @@
   "monitor.edit.success": "修改监控成功",
   "monitor.enable": "恢复监控",
   "monitor.export": "导出所选",
+  "monitor.selected-count": "已选 {{count}} 项",
+  "monitor.clear-selection": "清空",
+  "monitor.select-current-page": "全选当前页",
   "monitor.export-all": "导出全部",
   "monitor.export.switch-type": "请选择导出文件格式!",
   "monitor.export.use-type": "以 {{type}} 文件格式导出所选监控",
diff --git a/web-app/src/assets/i18n/zh-TW.json 
b/web-app/src/assets/i18n/zh-TW.json
index ce8ee447ff..a05dc1b7c5 100644
--- a/web-app/src/assets/i18n/zh-TW.json
+++ b/web-app/src/assets/i18n/zh-TW.json
@@ -826,6 +826,9 @@
   "monitor.edit.success": "修改監控成功",
   "monitor.enable": "恢復監控",
   "monitor.export": "匯出監控",
+  "monitor.selected-count": "已選 {{count}} 項",
+  "monitor.clear-selection": "清空",
+  "monitor.select-current-page": "全選當前頁",
   "monitor.export.switch-type": "請選擇匯出文件格式!",
   "monitor.export.use-type": "以 {{type}} 檔案格式匯出監控",
   "monitor.grafana.enabled.label": "啓用Grafana監控",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to