[fix] keep monitor selection across pages in monitor list (#4170)

Co-authored-by: aias00 <liuhongyu@apache.org>
This commit is contained in:
Duansg
2026-06-28 00:02:18 +08:00
committed by GitHub
co-authored by aias00
parent 660bda3d0f
commit a54be93ff5
8 changed files with 86 additions and 30 deletions
@@ -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>
@@ -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 {
@@ -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() {
+3
View File
@@ -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",
+3
View File
@@ -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を有効化",
+3
View File
@@ -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",
+3
View File
@@ -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}} 文件格式导出所选监控",
+3
View File
@@ -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監控",