diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-escalation-set.vue b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-escalation-set.vue index 8a69e88587a..6fcb0f4f1cc 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-escalation-set.vue +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-escalation-set.vue @@ -145,34 +145,44 @@ :show-validate.sync="rule.protocol" :validator="{ content: $t('必填项') }" > -
- +
- {{ protocol.name }} - + + + +
+
{{ protocol.name }}
+
{{ protocol.desc }}
+
+
- - {{ protocolDes }} - + -
+ { private currentItemIdName = 'bkEventGroupId'; // 当前 ID 字段 private disableSubmit = false; // 是否禁用提交按钮(防止重复点击) private btnLoadingIcon = ''; // 提交时显示按钮loading效果 - private protocolList: Array<{ id: string; name: string }> = [ - { id: 'json', name: 'JSON' }, - { id: 'prometheus', name: 'Prometheus' }, + private protocolList: Array<{ id: string; name: string; desc: string; icon: string }> = [ + { id: 'json', name: 'JSON', desc: '首次发现后进入待分类', icon: 'icon-code' }, + { id: 'prometheus', name: 'Prometheus', desc: '自动读取 TYPE 元数据', icon: 'icon-profiling' }, ]; // 上报协议字典 private scopeList: Array<{ id: boolean; name: string }> = [ { id: false, name: '本业务' }, @@ -298,7 +308,7 @@ export default class CustomEscalationSet extends Vue { token: '', dataLabel: '', isPlatform: false, - protocol: '', + protocol: 'json', desc: '', }; @@ -334,15 +344,6 @@ export default class CustomEscalationSet extends Vue { return this.$t('提交'); } - /** 上报协议提示 */ - get protocolDes() { - const des = { - json: this.$t('蓝鲸监控自有的JSON数据格式,创建完后有具体的格式说明'), - prometheus: this.$t('支持Prometheus的标准输出格式'), - }; - return des[this.formData.protocol]; - } - get type() { return this.$route.name === 'custom-set-event' ? 'customEvent' : 'customTimeSeries'; } @@ -599,17 +600,94 @@ export default class CustomEscalationSet extends Vue { padding: 23px 20px 4px 37px; font-size: 12px; background: $whiteColor; - border-radius: 2px; + border-radius: 4px; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1); - @include border-1px($color: $defaultBorderColor); - .bk-button-group { .bk-button { width: 134px; } } + .protocol-block-group { + display: flex; + gap: 12px; + width: 100%; + } + + .protocol-block { + display: flex; + flex: 1; + gap: 10px; + align-items: center; + box-sizing: border-box; + min-width: 0; + padding: 12px 16px; + cursor: pointer; + background: #fff; + border: 1px solid #dcdee5; + border-radius: 4px; + transition: all 0.15s ease; + + &.is-selected { + background: #f0f5ff; + border-color: #3a84ff; + + .protocol-block-icon-wrap { + color: #fff; + background: #3a84ff; + } + + .protocol-block-title { + color: #3a84ff; + } + + .protocol-block-desc { + color: #699df4; + } + } + + .protocol-block-icon-wrap { + display: inline-flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + color: #4d4f56; + background: #f0f1f5; + border-radius: 4px; + } + + .protocol-block-icon { + font-size: 18px; + line-height: 1; + } + + .protocol-block-content { + min-width: 0; + } + + .protocol-block-title { + font-size: 14px; + font-weight: 700; + line-height: 22px; + color: #111214; + } + + .protocol-block-desc { + margin-top: 2px; + font-size: 12px; + line-height: 20px; + color: #979ba5; + } + } + + .protocol-alert { + width: 100%; + margin-top: 12px; + } + .description { padding-top: 9px; font-size: 12px; @@ -618,27 +696,62 @@ export default class CustomEscalationSet extends Vue { } .bk-radio-group { - padding-top: 6px; + display: flex; + align-items: center; + height: 32px; + padding-top: 0; + line-height: 32px; .bk-form-radio { + display: inline-flex; + align-items: center; + height: 32px; margin-right: 24px; + margin-top: 0; + font-size: 12px; + line-height: 32px; + + :deep(.bk-radio-text) { + font-size: 12px; + line-height: 32px; + } } } &-title { - font-weight: bold; + font-size: 14px; + font-weight: 700; + line-height: 22px; + color: #111214; } &-content { margin-top: 25px; + :deep(.bk-label), + :deep(.bk-form-item > label), + :deep(.bk-label-text) { + font-size: 12px; + } + .content-basic { width: 576px; margin-bottom: 20px; + &.content-scope { + :deep(.bk-label) { + line-height: 32px; + } + + :deep(.bk-form-content) { + line-height: 32px; + } + } + .platform-tips { display: flex; - width: max-content; + width: 100%; + box-sizing: border-box; padding: 6px 9px; margin-top: 6px; line-height: 20px; @@ -647,10 +760,18 @@ export default class CustomEscalationSet extends Vue { border-radius: 2px; .icon-tixing { + flex-shrink: 0; margin-right: 9px; font-size: 16px; color: #ff9c01; } + + > span:last-child { + flex: 1; + min-width: 0; + word-break: break-word; + white-space: normal; + } } } @@ -660,7 +781,7 @@ export default class CustomEscalationSet extends Vue { } .form-content-select { - width: 240px; + width: 100%; } } } diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-report.scss b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-report.scss index 313f00617a9..d9dc3ba772c 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-report.scss +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/custom-report.scss @@ -9,6 +9,7 @@ .custom-report-page { position: relative; + background: #f5f7fa; @include layout-flex(row, stretch, flex-start); @@ -131,7 +132,7 @@ .content-left { flex-basis: 680px; - background: $whiteColor; + background: #f5f7fa; border-radius: 2px; .content-left-header { padding: 16px 24px; diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/components/metrics-select/components/render-metrics-group/index.tsx b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/components/metrics-select/components/render-metrics-group/index.tsx index 236839842a6..28b504c04f3 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/components/metrics-select/components/render-metrics-group/index.tsx +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/components/metrics-select/components/render-metrics-group/index.tsx @@ -31,6 +31,7 @@ import { makeMap } from 'monitor-common/utils/make-map'; import customEscalationViewStore from 'monitor-pc/store/modules/custom-escalation-view'; import RenderMetric from './render-metric'; +import { ensureDemoMetricTreeIfNeeded } from '../../../../../../../metric-manage/metric-type'; import type { RequestHandlerMap } from '../../../../../../type'; @@ -214,7 +215,11 @@ export default class RenderMetricsGroup extends tsc { }); } const result = await this.requestHandlerMap.getCustomTsMetricGroups(params); - customEscalationViewStore.updateMetricGroupList(result.metric_groups); + const metricGroups = (result.metric_groups || []).map(group => ({ + ...group, + metrics: ensureDemoMetricTreeIfNeeded(group.metrics || []), + })); + customEscalationViewStore.updateMetricGroupList(metricGroups); } finally { this.isLoading = false; } diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/index.tsx b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/index.tsx index 2e5809dc5c5..8ae5fc3beeb 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/index.tsx +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-detail/components/view-main/index.tsx @@ -38,6 +38,7 @@ import { getSceneView, // 详情页 modifyCustomTsFields, // 详情页 } from '../../../service'; +import { ensureDemoMetricTreeIfNeeded } from '../../../metric-manage/metric-type'; import HeaderBox from './components/header-box'; import MetricsSelect from './components/metrics-select'; import PanelChartView from './components/panel-chart-view'; @@ -184,7 +185,12 @@ export default class ViewContent extends tsc { }); } const result = await this.requestHandlerMap.getCustomTsMetricGroups(params); - customEscalationViewStore.updateMetricGroupList(result.metric_groups); + const metricGroups = (result.metric_groups || []).map(group => ({ + ...group, + // Histogram 只展示聚合指标名称,不再显示子指标(Mock 收敛) + metrics: ensureDemoMetricTreeIfNeeded(group.metrics || []), + })); + customEscalationViewStore.updateMetricGroupList(metricGroups); } finally { this.handleCustomTsMetricGroups(); } diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/help-info/index.tsx b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/help-info/index.tsx index ed8d2439f26..1bf049cd945 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/help-info/index.tsx +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/help-info/index.tsx @@ -52,8 +52,8 @@ export default class HelpInfo extends tsc { /** 用于复制Python Prometheus SDK代码的隐藏文本域引用 */ @Ref('pythonCopy') readonly pythonCopy!: HTMLTextAreaElement; - /** 是否显示右侧帮助栏 */ - isShowHelpPanel = true; + /** 是否显示右侧帮助栏(默认收起,由 isShow prop 同步) */ + isShowHelpPanel = false; /** 云区域分类数据,包含各云区域的IP和ID信息 */ proxyInfo: ServiceReturnType = []; /** 数据上报格式样例(JSON协议) */ diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.scss b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.scss index 110d9adf942..10809a39ae5 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.scss +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.scss @@ -29,9 +29,40 @@ display: flex; flex-direction: column; height: 100%; - padding-top: 12px; + padding-top: 16px; overflow: hidden; + .sidebar-section { + display: flex; + flex-direction: column; + } + + .sidebar-section-title { + padding: 0 16px 8px; + font-size: 14px; + font-weight: 700; + line-height: 22px; + color: #313238; + } + + .metric-type-section { + flex-shrink: 0; + padding-bottom: 8px; + } + + .custom-group-section { + display: flex; + flex: 1; + flex-direction: column; + min-height: 0; + padding-top: 12px; + border-top: 1px solid #dcdee5; + } + + .metric-type-group { + flex-shrink: 0; + } + .group { display: flex; align-items: center; @@ -39,81 +70,74 @@ width: 100%; height: 32px; padding: 0 16px; - margin-bottom: 4px; overflow: hidden; font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; font-size: 13px; + line-height: 20px; color: #4d4f56; - &.is-dragover { - background-color: #f0f1f5; + .group-name { + display: flex; + flex: 1; + align-items: center; + min-width: 0; + margin-right: 8px; + overflow: hidden; + + .name-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } .group-count { + flex-shrink: 0; min-width: 20px; - height: 20px; font-family: Arial, Helvetica, sans-serif; - font-size: 12px; - font-weight: 700; - line-height: 20px; - color: #a3b1cc; - text-align: center; - } - - .icon-monitor { - margin-right: 6px; font-size: 13px; + font-weight: 400; + line-height: 20px; color: #a3b1cc; + text-align: right; } &:hover { cursor: pointer; + background: #eaebf0; } &.group-selected { color: #3a84ff; background: #e1ecff !important; - .icon-monitor, + .name-text, .group-count { + font-weight: 700; color: #3a84ff; } } } .top-group { - padding-bottom: 4px; - border-bottom: 1px solid #dcdee5; - - .group { - &:hover { - background: #eaebf0; - } - } - - .group-name { - display: flex; - align-items: center; - - .icon-monitor { - font-size: 16px; - } - } + flex-shrink: 0; } .custom-group-set { display: flex; - justify-content: center; - padding: 16px; + align-items: center; + padding: 0 16px 12px; .add-group { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; width: 32px; height: 32px; margin-right: 8px; - font-size: 20px; - line-height: 32px; + font-size: 16px; color: #3a84ff; - text-align: center; background: #cddffe; border-radius: 2px; @@ -175,42 +199,6 @@ .group-popover { display: block; } - - .item-drag { - visibility: inherit; - cursor: move; - } - } - - .item-drag { - position: absolute; - top: 4px; - left: 4px; - visibility: hidden; - width: 7px; - height: 10px; - font-size: 10px; - color: #979ba5; - } - - .group-name { - display: flex; - flex: 1; - align-items: center; - margin-right: 6px; - overflow: hidden; - - .name-text { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - font-size: 12px; - white-space: nowrap; - } - - .icon-FileFold-Close { - font-size: 16px; - } } } } diff --git a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.tsx b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.tsx index 75ad3b1b618..621abdd76ec 100644 --- a/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.tsx +++ b/bkmonitor/webpack/src/monitor-pc/pages/custom-escalation/metric-manage/components/indicator-dimension/components/group-list/index.tsx @@ -28,6 +28,12 @@ import { Component as tsc } from 'vue-tsx-support'; import { Debounce } from 'monitor-common/utils'; import { type IGroupListItem, ALL_LABEL, NULL_LABEL } from '../../../../type'; +import { + type MetricTypeValue, + METRIC_TYPE_ALL, + METRIC_TYPE_OPTIONS, + enrichAndCollapseMetricTypeList, +} from '../../../../metric-type'; import EditGroupDialog from '../edit-group'; import type { IGroupingRule } from '../../../../../service'; @@ -39,6 +45,8 @@ import './index.scss'; interface IEmits { /** 切换分组时触发 */ onChangeGroup: (groupInfo: { id: number; name: string }) => void; + /** 切换指标类型筛选 */ + onChangeMetricType: (metricType: MetricTypeValue | typeof METRIC_TYPE_ALL) => void; /** 编辑分组成功时触发 */ onEditGroupSuccess: (config: Partial, isCreate: boolean) => void; /** 删除分组时触发 */ @@ -80,6 +88,8 @@ export default class CustomGroupingList extends tsc { showDelDialog = false; /** 当前选中的分组信息 */ selectedGroupInfo = { id: 0, name: ALL_LABEL }; + /** 当前选中的指标类型(与分组 AND 筛选) */ + selectedMetricType: MetricTypeValue | typeof METRIC_TYPE_ALL = METRIC_TYPE_ALL; /** 当前编辑的分组信息 */ currentGroupInfo: IGroupingRule = { id: 0, @@ -90,11 +100,18 @@ export default class CustomGroupingList extends tsc { dimension_config: [], }; - /** 顶部分组列表(全部、未分组) */ - topGroupList = [ - { id: ALL_LABEL, name: this.$t('全部') as string, icon: 'icon-all' }, - { id: NULL_LABEL, name: this.$t('默认分组') as string, icon: 'icon-FileFold-Close' }, - ]; + /** 顶部分组列表(仅默认分组;「全部」由指标类型承担,自定义分组下不展示) */ + topGroupList = [{ id: NULL_LABEL, name: this.$t('默认分组') as string, icon: 'icon-FileFold-Close' }]; + + /** 指标类型数量(含全部) */ + metricTypeCountMap: Record = { + [METRIC_TYPE_ALL]: 0, + unclassified: 0, + gauge: 0, + counter: 0, + histogram: 0, + summary: 0, + }; /** 搜索关键词 */ searchGroupKeyword = ''; @@ -151,6 +168,7 @@ export default class CustomGroupingList extends tsc { /** 监听分组列表变化,初始化时自动选中"全部"分组 */ @Watch('groupList', { immediate: true }) handleGroupListChange(list: IGroupListItem[]) { + this.fetchMetricTypeCounts(); if (list.length > 0 && !this.isInit) { this.isInit = true; if (this.isAPM) { @@ -185,6 +203,66 @@ export default class CustomGroupingList extends tsc { } } + /** + * 拉取指标并统计各类型数量(后端无类型计数字段时前端 Mock/收敛后统计) + */ + async fetchMetricTypeCounts() { + if (!this.isAPM && !this.timeSeriesGroupId) { + return; + } + try { + const params: Record = { + time_series_group_id: this.timeSeriesGroupId, + page: 1, + page_size: 1000, + conditions: [ + { + key: 'scope_id', + values: [], + search_type: 'exact', + }, + ], + }; + if (this.isAPM) { + delete params.time_series_group_id; + Object.assign(params, { + app_name: this.appName, + service_name: this.serviceName, + }); + } + const data = await this.requestHandlerMap.getCustomTsFields(params); + const rawList = data?.list || []; + const list = enrichAndCollapseMetricTypeList(rawList); + // 「全部」用接口 total(全量指标数);各类型按收敛后列表统计 + const nextMap: Record = { + [METRIC_TYPE_ALL]: typeof data?.total === 'number' ? data.total : this.totalMetricNum, + unclassified: 0, + gauge: 0, + counter: 0, + histogram: 0, + summary: 0, + }; + for (const item of list) { + const type = item.metric_type || 'unclassified'; + nextMap[type] = (nextMap[type] || 0) + 1; + } + this.metricTypeCountMap = nextMap; + } catch (e) { + console.error(e); + } + } + + /** + * 获取指标类型数量 + * 「全部」展示当下所有指标数量(与分组 metric_count 汇总一致),不受指标族收敛影响 + */ + getMetricTypeCount(type: string) { + if (type === METRIC_TYPE_ALL) { + return this.totalMetricNum; + } + return this.metricTypeCountMap[type] ?? 0; + } + /** * 处理新增分组操作 * 打开新增分组对话框 @@ -323,6 +401,15 @@ export default class CustomGroupingList extends tsc { this.$emit('changeGroup', groupInfo); } + /** + * 切换指标类型筛选(可与自定义分组同时选中) + */ + changeSelectedMetricType(type: MetricTypeValue | typeof METRIC_TYPE_ALL) { + if (type === this.selectedMetricType) return; + this.selectedMetricType = type; + this.$emit('changeMetricType', type); + } + /** * 执行删除分组操作 * 关闭删除对话框并触发删除事件 @@ -353,143 +440,139 @@ export default class CustomGroupingList extends tsc { render() { return (
-
- {this.topGroupList.map(group => ( -
- this.changeSelectedLabel({ - id: group.id === ALL_LABEL ? -1 : this.defaultGroupInfo.id, - name: group.id === ALL_LABEL ? group.id : this.defaultGroupInfo.name, - }) - } - > -
- - {group.name} +
-
+