This is an automated email from the ASF dual-hosted git repository.
gongchao 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 4ae6ba9a3c feat: update add monitors pop selected menus modal (#3878)
4ae6ba9a3c is described below
commit 4ae6ba9a3cf10315d79594f7ab6a220f168e920f
Author: Tomsun28 <[email protected]>
AuthorDate: Thu Dec 4 23:55:10 2025 +0800
feat: update add monitors pop selected menus modal (#3878)
Signed-off-by: tomsun28 <[email protected]>
Co-authored-by: aias00 <[email protected]>
---
.../docs/help/{mcp_sse_server.md => mcp_server.md} | 2 +-
.../help/{mcp_sse_server.md => mcp_server.md} | 2 +-
home/sidebars.json | 2 +-
.../monitor-list/monitor-list.component.html | 10 +-
.../monitor-list/monitor-list.component.less | 17 ++-
.../routes/setting/define/define.component.html | 4 +-
.../monitor-select-list.component.html} | 0
.../monitor-select-list.component.less} | 0
.../monitor-select-list.component.spec.ts | 43 ++++++
.../monitor-select-list.component.ts} | 8 +-
.../monitor-select-menu.component.html | 45 +++---
.../monitor-select-menu.component.less | 157 ++++++++++++++++++---
.../monitor-select-menu.component.ts | 21 +++
web-app/src/app/shared/shared.module.ts | 2 +
web-app/src/assets/app-data.json | 2 +-
15 files changed, 263 insertions(+), 52 deletions(-)
diff --git a/home/docs/help/mcp_sse_server.md b/home/docs/help/mcp_server.md
similarity index 99%
rename from home/docs/help/mcp_sse_server.md
rename to home/docs/help/mcp_server.md
index 0b600e70a4..82ec71cefa 100644
--- a/home/docs/help/mcp_sse_server.md
+++ b/home/docs/help/mcp_server.md
@@ -1,5 +1,5 @@
---
-id: mcp_sse_server
+id: mcp_server
title: MCP Server
sidebar_label: MCP Server
keywords: [MCP, SSE, streaming, server]
diff --git
a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_sse_server.md
b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_server.md
similarity index 99%
rename from
home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_sse_server.md
rename to
home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_server.md
index 7e260a5e4d..0e04620e19 100644
---
a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_sse_server.md
+++ b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/mcp_server.md
@@ -1,5 +1,5 @@
---
-id: mcp_sse_server
+id: mcp_server
title: MCP 服务器
sidebar_label: MCP 服务器
keywords: [MCP, SSE, 流式传输, 服务器]
diff --git a/home/sidebars.json b/home/sidebars.json
index 1ea4b10d6d..4c9a149072 100755
--- a/home/sidebars.json
+++ b/home/sidebars.json
@@ -67,7 +67,7 @@
"help/guide",
"help/security_model",
"help/ai_agent",
- "help/mcp_sse_server",
+ "help/mcp_server",
{
"type": "category",
"label": "use-case",
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 0dbd76053d..b980116f17 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
@@ -370,16 +370,17 @@
<nz-modal
[(nzVisible)]="appSwitchModalVisible"
(nzOnCancel)="onAppSwitchModalCancel()"
- nzClosable="false"
- nzWidth="30%"
+ nzClosable="true"
+ nzWidth="900px"
nzWrapClassName="monitor-select-menu-modal"
+ [nzTitle]="appSwitchModalVisibleType === 1 ? ('monitor.search.app' | i18n) :
('monitor.new-monitor' | i18n)"
[nzFooter]="null"
[nzOkLoading]="appSearchLoading"
+ nzCentered
>
<div *nzModalContent class="-inner-content">
<app-monitor-select-menu
- listStyle="border-right: 0px"
- [searchPlaceholder]="(appSwitchModalVisibleType === 1 ?
'monitor.search.app' : 'monitor.center.search.placeholder') | i18n"
+ [searchPlaceholder]="'monitor.center.search.placeholder' | i18n"
[loading]="appSearchLoading"
[data]="appSearchOrigin"
(selectedChanged)="gotoMonitorAddDetail($event)"
@@ -387,7 +388,6 @@
<ng-template let-app="item" #prefix>
<i nz-icon [nzType]="getAppIconName(app.value)" nzTheme="outline"></i>
</ng-template>
- <ng-template #suffix>@if (appSwitchModalVisibleType !== 1) {<i nz-icon
nzType="right"></i>}</ng-template>
</app-monitor-select-menu>
</div>
</nz-modal>
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 7817988a06..9a288de84b 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
@@ -2,10 +2,25 @@
::ng-deep {
.monitor-select-menu-modal {
+ .ant-modal-body {
+ padding: 0;
+ max-height: 70vh;
+ overflow: hidden;
+ }
+
+ .-inner-content {
+ height: 70vh;
+ max-height: 70vh;
+ }
+
.ant-spin-container {
- max-height: 80vh;
+ max-height: 70vh;
overflow: hidden;
}
+
+ app-monitor-select-menu {
+ height: 100%;
+ }
}
}
diff --git a/web-app/src/app/routes/setting/define/define.component.html
b/web-app/src/app/routes/setting/define/define.component.html
index 8cf4cf1dff..cbf0cff988 100755
--- a/web-app/src/app/routes/setting/define/define.component.html
+++ b/web-app/src/app/routes/setting/define/define.component.html
@@ -28,7 +28,7 @@
<nz-layout style="height: 100vh; overflow: hidden">
<nz-sider [nzTheme]="theme == 'dark' ? 'dark' : 'light'" style="height:
100%; overflow: hidden" [nzTrigger]="null">
- <app-monitor-select-menu
+ <app-monitor-select-list
[loading]="menuLoading"
[data]="appMenusArr"
[selected]="currentApp"
@@ -62,7 +62,7 @@
<i nz-icon nzType="eye" nzTheme="outline"></i>
</button>
</ng-template>
- </app-monitor-select-menu>
+ </app-monitor-select-list>
</nz-sider>
<nz-content style="display: flex; flex-direction: column">
<app-toolbar wrapperStyle="margin: 8px 8px 4px 8px">
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.html
similarity index 100%
copy from
web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
copy to
web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.html
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.less
similarity index 100%
copy from
web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
copy to
web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.less
diff --git
a/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
new file mode 100755
index 0000000000..fd903d3ed3
--- /dev/null
+++
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MonitorSelectListComponent } from './monitor-select-list.component';
+
+describe('MonitorSelectListComponent', () => {
+ let component: MonitorSelectListComponent;
+ let fixture: ComponentFixture<MonitorSelectListComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [MonitorSelectListComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MonitorSelectListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.ts
similarity index 90%
copy from
web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
copy to
web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.ts
index 20315dea20..31aef6f30d 100755
---
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
+++
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.ts
@@ -20,11 +20,11 @@
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef }
from '@angular/core';
@Component({
- selector: 'app-monitor-select-menu',
- templateUrl: './monitor-select-menu.component.html',
- styleUrls: ['./monitor-select-menu.component.less']
+ selector: 'app-monitor-select-list',
+ templateUrl: './monitor-select-list.component.html',
+ styleUrls: ['./monitor-select-list.component.less']
})
-export class MonitorSelectMenuComponent {
+export class MonitorSelectListComponent {
@ContentChild('prefix', { static: true }) prefixTemplateRef:
TemplateRef<any> | undefined;
@ContentChild('suffix', { static: true }) suffixTemplateRef:
TemplateRef<any> | undefined;
@Input() data!: any[][];
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
index fd2c2b472f..44a7ddd46e 100755
---
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
+++
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.html
@@ -18,7 +18,7 @@
-->
<nz-spin [nzSpinning]="loading" style="height: 100%">
- <div style="margin: 8px 8px -4px 8px">
+ <div class="search-container">
<app-multi-func-input
type="search"
[placeholder]="searchPlaceholder || ('common.search' | i18n)"
@@ -26,22 +26,29 @@
(valueChange)="filter($event)"
/>
</div>
- <nz-divider></nz-divider>
- <ul [style]="'flex: 1; overflow: hidden; overflow-y: auto;' + listStyle"
nz-menu nzMode="inline" nzInlineCollapsed="false">
- <li *ngFor="let menuItem of !!search ? dataByFilter : data" nz-submenu
[nzTitle]="menuItem[1].label" [nzOpen]="!!search">
- <ul>
- <li class="item" nz-menu-item *ngFor="let app of menuItem[1].child"
[nzSelected]="app.value === selected">
- <ng-container *ngIf="prefixTemplateRef; else noPrefixContent">
- <ng-template [ngTemplateOutlet]="prefixTemplateRef"
[ngTemplateOutletContext]="{ item: app }"></ng-template>
- </ng-container>
- <ng-template #noPrefixContent></ng-template>
- <span class="label" [title]="app.label"
(click)="onSelectedChanged(app.value)">{{ app.label }}</span>
- <ng-container *ngIf="suffixTemplateRef; else noSuffixContent">
- <ng-template [ngTemplateOutlet]="suffixTemplateRef"
[ngTemplateOutletContext]="{ item: app }"></ng-template>
- </ng-container>
- <ng-template #noSuffixContent></ng-template>
- </li>
- </ul>
- </li>
- </ul>
+ <div class="category-container" [style]="listStyle">
+ <div class="category-section" *ngFor="let menuItem of !!search ?
dataByFilter : data">
+ <div class="category-header">
+ <span>
+ <i nz-icon [nzType]="getCategoryIcon(menuItem[0])"
nzTheme="outline"></i>
+ </span>
+ <span class="category-title">{{ menuItem[1].label }}</span>
+ <span class="category-count">{{ menuItem[1].child.length }}</span>
+ </div>
+ <div class="category-items">
+ <div
+ class="monitor-type-card"
+ *ngFor="let app of menuItem[1].child"
+ [class.selected]="app.value === selected"
+ (click)="onSelectedChanged(app.value)"
+ >
+ <span class="card-label">{{ app.label }}</span>
+ </div>
+ </div>
+ </div>
+ <div class="empty-state" *ngIf="(!!search ? dataByFilter : data)?.length
=== 0">
+ <i nz-icon nzType="inbox" nzTheme="outline"></i>
+ <span>{{ 'common.no-data' | i18n }}</span>
+ </div>
+ </div>
</nz-spin>
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
index a723c9414e..89e8fc707f 100755
---
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
+++
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.less
@@ -1,31 +1,154 @@
+@import '~src/styles/theme';
+
:host ::ng-deep {
+ .ant-spin-container {
+ height: 100%;
+ display: flex;
+ overflow: hidden;
+ flex-direction: column;
+ }
+}
- .item{
- padding-left: 32px!important;
+:host {
+ display: block;
+ height: 100%;
- .ant-menu-title-content {
- gap: 8px;
- display: flex;
- align-items: center;
+ .search-container {
+ padding: 16px 16px 0 16px;
+ }
- :first-child {
- flex-shrink: 0;
- }
+ .category-container {
+ flex: 1;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 16px;
+ }
- .label {
- flex: 1;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
+ .category-section {
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
}
}
- .ant-spin-container {
- height: 100%;
+ .category-header {
display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 12px;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .category-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #1f1f1f;
+ }
+
+ .category-count {
+ font-size: 12px;
+ color: #8c8c8c;
+ background: #f5f5f5;
+ padding: 2px 8px;
+ border-radius: 10px;
+ margin-left: auto;
+ }
+
+ .category-items {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 10px;
+ }
+
+ .monitor-type-card {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 12px;
+ background: #fafafa;
+ border: 1px solid #e8e8e8;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-align: center;
+
+ &:hover {
+ border-color: @primary-color;
+ background: #e6f4ff;
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.15);
+ }
+
+ &.selected {
+ border-color: @primary-color;
+ background: #e6f4ff;
+ box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+ }
+ }
+
+ .card-label {
+ font-size: 13px;
+ color: #333;
overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ line-height: 1.4;
+ }
+
+ .empty-state {
+ display: flex;
flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 48px 0;
+ color: #8c8c8c;
+
+ i {
+ font-size: 48px;
+ margin-bottom: 12px;
+ }
}
+}
+
+// Dark theme support
+[data-theme='dark'] {
+ :host {
+ .category-header {
+ border-bottom-color: #303030;
+ }
+
+ .category-title {
+ color: #d9d9d9;
+ }
+
+ .category-count {
+ background: #303030;
+ color: #a0a0a0;
+ }
+
+ .monitor-type-card {
+ background: #1f1f1f;
+ border-color: #303030;
+ &:hover {
+ border-color: @primary-color;
+ background: #111d2c;
+ }
+
+ &.selected {
+ border-color: @primary-color;
+ background: #111d2c;
+ }
+ }
+
+ .card-label {
+ color: #d9d9d9;
+ }
+
+ .empty-state {
+ color: #6c6c6c;
+ }
+ }
}
diff --git
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
index 20315dea20..89f42e3555 100755
---
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
+++
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.ts
@@ -51,4 +51,25 @@ export class MonitorSelectMenuComponent {
onSelectedChanged(selected: string) {
this.selectedChanged.emit(selected);
}
+
+ getCategoryIcon(category: string): string {
+ const iconMap: Record<string, string> = {
+ os: 'desktop',
+ webserver: 'global',
+ bigdata: 'cluster',
+ cache: 'database',
+ service: 'api',
+ server: 'hdd',
+ mid: 'deployment-unit',
+ db: 'database',
+ custom: 'tool',
+ llm: 'robot',
+ network: 'wifi',
+ cn: 'cloud-server',
+ cicd: 'branches',
+ auto: 'thunderbolt',
+ program: 'code'
+ };
+ return iconMap[category] || 'appstore';
+ }
}
diff --git a/web-app/src/app/shared/shared.module.ts
b/web-app/src/app/shared/shared.module.ts
index 0d499cddfb..e111363ddb 100644
--- a/web-app/src/app/shared/shared.module.ts
+++ b/web-app/src/app/shared/shared.module.ts
@@ -25,6 +25,7 @@ import { ConfigurableFieldComponent } from
'./components/configurable-field/conf
import { FormFieldComponent } from
'./components/form-field/form-field.component';
import { HelpMessageShowComponent } from
'./components/help-message-show/help-message-show.component';
import { LabelSelectorComponent } from
'./components/label-selector/label-selector.component';
+import { MonitorSelectListComponent } from
'./components/monitor-select-list/monitor-select-list.component';
import { MonitorSelectMenuComponent } from
'./components/monitor-select-menu/monitor-select-menu.component';
import { MultiFuncInputComponent } from
'./components/multi-func-input/multi-func-input.component';
import { ToolbarComponent } from './components/toolbar/toolbar.component';
@@ -42,6 +43,7 @@ const COMPONENTS: Array<Type<void>> = [
ConfigurableFieldComponent,
FormFieldComponent,
MonitorSelectMenuComponent,
+ MonitorSelectListComponent,
LabelSelectorComponent
];
const DIRECTIVES: Array<Type<void>> = [TimezonePipe, I18nElsePipe,
ElapsedTimePipe];
diff --git a/web-app/src/assets/app-data.json b/web-app/src/assets/app-data.json
index b232d4fd00..c880a953fe 100644
--- a/web-app/src/assets/app-data.json
+++ b/web-app/src/assets/app-data.json
@@ -223,7 +223,7 @@
"i18n": "menu.advanced.mcp-server",
"icon": "anticon-compass",
"target": "_blank",
- "externalLink":
"https://hertzbeat.apache.org/docs/help/mcp_sse_server"
+ "externalLink": "https://hertzbeat.apache.org/docs/help/mcp_server"
},
{
"text": "status",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]