You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/03/26 14:52:46 UTC
[skywalking-booster-ui] branch main updated: feat: Implement custom configurations for metrics on dashboards and topology (#39)
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git
The following commit(s) were added to refs/heads/main by this push:
new c00d5d2 feat: Implement custom configurations for metrics on dashboards and topology (#39)
c00d5d2 is described below
commit c00d5d2a051ca9860124641543220128d434930c
Author: Fine0830 <fi...@outlook.com>
AuthorDate: Sat Mar 26 22:52:43 2022 +0800
feat: Implement custom configurations for metrics on dashboards and topology (#39)
---
.../icons/{clearclose.svg => arrow_drop_down.svg} | 3 +-
src/assets/icons/cancel.svg | 1 -
src/assets/icons/clearclose.svg | 1 -
.../{createmode_editedit.svg => mode_edit.svg} | 1 -
src/components/Select.vue | 177 ++++++++++++++++++++
src/components/index.ts | 2 +
src/hooks/data.ts | 9 +
src/hooks/useProcessor.ts | 89 +++++-----
src/locales/lang/en.ts | 5 +-
src/locales/lang/zh.ts | 5 +-
src/store/data.ts | 1 +
src/types/dashboard.ts | 11 +-
src/views/dashboard/configuration/Text.vue | 20 +++
src/views/dashboard/configuration/Widget.vue | 11 +-
.../configuration/widget/StandardOptions.vue | 186 ---------------------
.../configuration/widget/graph-styles/Card.vue | 14 +-
.../widget/{MetricOptions.vue => metric/Index.vue} | 59 ++++++-
.../configuration/widget/metric/Standard.vue | 129 ++++++++++++++
src/views/dashboard/controls/Text.vue | 41 ++---
src/views/dashboard/controls/Widget.vue | 4 +-
src/views/dashboard/data.ts | 14 +-
src/views/dashboard/graphs/Card.vue | 20 ++-
src/views/dashboard/graphs/EndpointList.vue | 27 ++-
src/views/dashboard/graphs/InstanceList.vue | 23 ++-
src/views/dashboard/graphs/ServiceList.vue | 22 ++-
.../related/topology/components/Graph.vue | 46 +++--
.../related/topology/components/Metrics.vue | 170 +++++++++++++++++++
.../related/topology/components/PodTopology.vue | 2 +-
.../related/topology/components/Sankey.vue | 65 ++++---
.../related/topology/components/Settings.vue | 157 +++++++++++++----
30 files changed, 949 insertions(+), 366 deletions(-)
diff --git a/src/assets/icons/clearclose.svg b/src/assets/icons/arrow_drop_down.svg
similarity index 81%
copy from src/assets/icons/clearclose.svg
copy to src/assets/icons/arrow_drop_down.svg
index af9e3a0..63151de 100644
--- a/src/assets/icons/clearclose.svg
+++ b/src/assets/icons/arrow_drop_down.svg
@@ -13,6 +13,5 @@ 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-<title>clearclose</title>
-<path d="M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z"></path>
+<path d="M6.984 9.984h10.031l-5.016 5.016z"></path>
</svg>
diff --git a/src/assets/icons/cancel.svg b/src/assets/icons/cancel.svg
index 00ca795..f52fb3d 100644
--- a/src/assets/icons/cancel.svg
+++ b/src/assets/icons/cancel.svg
@@ -13,6 +13,5 @@ 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-<title>cancel</title>
<path d="M17.016 15.609l-3.609-3.609 3.609-3.609-1.406-1.406-3.609 3.609-3.609-3.609-1.406 1.406 3.609 3.609-3.609 3.609 1.406 1.406 3.609-3.609 3.609 3.609zM12 2.016q4.125 0 7.055 2.93t2.93 7.055-2.93 7.055-7.055 2.93-7.055-2.93-2.93-7.055 2.93-7.055 7.055-2.93z"></path>
</svg>
diff --git a/src/assets/icons/clearclose.svg b/src/assets/icons/clearclose.svg
index af9e3a0..ddc3519 100644
--- a/src/assets/icons/clearclose.svg
+++ b/src/assets/icons/clearclose.svg
@@ -13,6 +13,5 @@ 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-<title>clearclose</title>
<path d="M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z"></path>
</svg>
diff --git a/src/assets/icons/createmode_editedit.svg b/src/assets/icons/mode_edit.svg
similarity index 96%
rename from src/assets/icons/createmode_editedit.svg
rename to src/assets/icons/mode_edit.svg
index 1a1f2d8..51f6cef 100644
--- a/src/assets/icons/createmode_editedit.svg
+++ b/src/assets/icons/mode_edit.svg
@@ -13,6 +13,5 @@ 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. -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-<title>createmode_editedit</title>
<path d="M20.719 7.031l-1.828 1.828-3.75-3.75 1.828-1.828q0.281-0.281 0.703-0.281t0.703 0.281l2.344 2.344q0.281 0.281 0.281 0.703t-0.281 0.703zM3 17.25l11.063-11.063 3.75 3.75-11.063 11.063h-3.75v-3.75z"></path>
</svg>
diff --git a/src/components/Select.vue b/src/components/Select.vue
new file mode 100644
index 0000000..0c101d5
--- /dev/null
+++ b/src/components/Select.vue
@@ -0,0 +1,177 @@
+<!-- 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. -->
+<template>
+ <div class="bar-select cp flex-h" :class="{ active: visible }">
+ <div class="bar-i" @click="setPopper">
+ <span v-if="selected.value">
+ {{ selected.label }}
+ </span>
+ <span class="no-data" v-else>Please select a option</span>
+ <span class="remove-icon" @click="removeSelected" v-if="clearable">
+ ×
+ </span>
+ </div>
+ <div class="opt-wrapper" v-show="visible">
+ <div
+ class="opt ell"
+ @click="handleSelect(i)"
+ :class="{ 'select-disabled': selected.value === i.value }"
+ v-for="i in options"
+ :key="i.value"
+ >
+ {{ i.label }}
+ </div>
+ </div>
+ </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from "vue";
+import type { PropType } from "vue";
+import { Option } from "@/types/app";
+
+/*global defineProps, defineEmits*/
+const emit = defineEmits(["change"]);
+const props = defineProps({
+ options: {
+ type: Array as PropType<(Option & { disabled: boolean })[]>,
+ default: () => [],
+ },
+ value: {
+ type: String as PropType<string>,
+ default: () => "",
+ },
+ clearable: { type: Boolean, default: false },
+});
+const visible = ref<boolean>(false);
+const opt = props.options.find((d: Option) => props.value === d.value);
+const selected = ref<Option>(opt || { label: "", value: "" });
+
+function handleSelect(i: Option) {
+ selected.value = i;
+ emit("change", i.value);
+}
+function removeSelected() {
+ selected.value = { label: "", value: "" };
+ emit("change", "");
+}
+watch(
+ () => props.value,
+ (data) => {
+ const opt = props.options.find((d: Option) => data === d.value);
+ selected.value = opt || { label: "", value: "" };
+ }
+);
+document.body.addEventListener("click", handleClick, false);
+
+function handleClick() {
+ visible.value = false;
+}
+function setPopper(event: any) {
+ event.stopPropagation();
+ visible.value = !visible.value;
+}
+</script>
+<style lang="scss" scoped>
+.bar-select {
+ position: relative;
+ justify-content: space-between;
+ border: 1px solid #ddd;
+ background: #fff;
+ border-radius: 3px;
+ color: #000;
+ font-size: 12px;
+ height: 24px;
+
+ .selected {
+ padding: 0 3px;
+ border-radius: 3px;
+ margin: 3px;
+ color: #409eff;
+ background-color: #fafafa;
+ border: 1px solid #e8e8e8;
+ text-align: center;
+ }
+}
+
+.no-data {
+ color: #c0c4cc;
+}
+
+.bar-i {
+ height: 100%;
+ width: 100%;
+ padding: 2px 10px;
+ overflow: auto;
+ color: #606266;
+ position: relative;
+
+ &:hover {
+ .remove-icon {
+ display: block;
+ }
+ }
+}
+
+.remove-icon {
+ position: absolute;
+ right: 5px;
+ top: 0;
+ font-size: 14px;
+ display: none;
+ color: #aaa;
+ cursor: pointer;
+}
+
+.opt-wrapper {
+ color: #606266;
+ position: absolute;
+ top: 26px;
+ left: 0;
+ background: #fff;
+ box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
+ border: 1px solid #ddd;
+ width: 100%;
+ border-radius: 0 0 3px 3px;
+ border-right-width: 1px !important;
+ z-index: 10;
+ overflow: auto;
+ max-height: 200px;
+ padding-bottom: 2px;
+
+ .close {
+ position: absolute;
+ right: 10px;
+ top: 12px;
+ opacity: 0.6;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+}
+
+.opt {
+ padding: 7px 15px;
+
+ &.select-disabled {
+ color: #409eff;
+ cursor: not-allowed;
+ }
+
+ &:hover {
+ background-color: #f5f5f5;
+ }
+}
+</style>
diff --git a/src/components/index.ts b/src/components/index.ts
index 9a17614..0ce63ae 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -19,6 +19,7 @@ import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue";
import Graph from "./Graph.vue";
import Radio from "./Radio.vue";
+import SelectSingle from "./Select.vue";
import type { App } from "vue";
import VueGridLayout from "vue-grid-layout";
@@ -29,6 +30,7 @@ const components: { [key: string]: any } = {
Selector,
Graph,
Radio,
+ SelectSingle,
};
const componentsName: string[] = Object.keys(components);
diff --git a/src/hooks/data.ts b/src/hooks/data.ts
index 8914aff..5f1fdd8 100644
--- a/src/hooks/data.ts
+++ b/src/hooks/data.ts
@@ -22,6 +22,15 @@ export enum MetricQueryTypes {
READHEATMAP = "readHeatMap",
ReadSampledRecords = "readSampledRecords",
}
+
+export enum Calculations {
+ Percentage = "percentage",
+ ByteToKB = "byteToKB",
+ Apdex = "apdex",
+ Precision = "precision",
+ ConvertSeconds = "convertSeconds",
+ ConvertMilliseconds = "convertMilliseconds",
+}
export enum sizeEnum {
XS = "XS",
SM = "SM",
diff --git a/src/hooks/useProcessor.ts b/src/hooks/useProcessor.ts
index 98f1f76..f91f451 100644
--- a/src/hooks/useProcessor.ts
+++ b/src/hooks/useProcessor.ts
@@ -15,13 +15,13 @@
* limitations under the License.
*/
import dayjs from "dayjs";
-import { RespFields, MetricQueryTypes } from "./data";
+import { RespFields, MetricQueryTypes, Calculations } from "./data";
import { ElMessage } from "element-plus";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Instance, Endpoint, Service } from "@/types/selector";
-import { StandardConfig } from "@/types/dashboard";
+import { MetricConfigOpt } from "@/types/dashboard";
export function useQueryProcessor(config: any) {
if (!(config.metrics && config.metrics[0])) {
@@ -48,6 +48,7 @@ export function useQueryProcessor(config: any) {
}
const fragment = config.metrics.map((name: string, index: number) => {
const metricType = config.metricTypes[index] || "";
+ const c = (config.metricConfig && config.metricConfig[index]) || {};
if (
[
MetricQueryTypes.ReadSampledRecords,
@@ -63,11 +64,11 @@ export function useQueryProcessor(config: any) {
normal: selectorStore.currentService.normal,
scope: dashboardStore.entity,
topN: 10,
- order: config.standard.sortOrder || "DES",
+ order: c.sortOrder || "DES",
};
} else {
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
- const labels = (config.labelsIndex || "")
+ const labels = (c.labelsIndex || "")
.split(",")
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
variables.push(`$labels${index}: [String!]!`);
@@ -128,7 +129,7 @@ export function useSourceProcessor(
config: {
metrics: string[];
metricTypes: string[];
- standard: StandardConfig;
+ metricConfig: MetricConfigOpt[];
}
) {
if (resp.errors) {
@@ -140,23 +141,24 @@ export function useSourceProcessor(
config.metricTypes.forEach((type: string, index) => {
const m = config.metrics[index];
+ const c = (config.metricConfig && config.metricConfig[index]) || {};
if (type === MetricQueryTypes.ReadMetricsValues) {
source[m] = resp.data[keys[index]].values.values.map(
- (d: { value: number }) => aggregation(d.value, config.standard)
+ (d: { value: number }) => aggregation(d.value, c)
);
}
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
const resVal = Object.values(resp.data)[0] || [];
- const labels = (config.standard.metricLabels || "")
+ const labels = (c.label || "")
.split(",")
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
- const labelsIdx = (config.standard.labelsIndex || "")
+ const labelsIdx = (c.labelsIndex || "")
.split(",")
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
for (const item of resVal) {
const values = item.values.values.map((d: { value: number }) =>
- aggregation(Number(d.value), config.standard)
+ aggregation(Number(d.value), c)
);
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
@@ -168,10 +170,7 @@ export function useSourceProcessor(
}
}
if (type === MetricQueryTypes.ReadMetricsValue) {
- source[m] = aggregation(
- Number(Object.values(resp.data)[0]),
- config.standard
- );
+ source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
}
if (
type === MetricQueryTypes.SortMetrics ||
@@ -179,7 +178,7 @@ export function useSourceProcessor(
) {
source[m] = (Object.values(resp.data)[0] || []).map(
(d: { value: unknown; name: string }) => {
- d.value = aggregation(Number(d.value), config.standard);
+ d.value = aggregation(Number(d.value), c);
return d;
}
@@ -258,7 +257,11 @@ export function useQueryPodsMetrics(
export function usePodsSource(
pods: Array<Instance | Endpoint>,
resp: { errors: string; data: { [key: string]: any } },
- config: { metrics: string[]; metricTypes: string[] }
+ config: {
+ metrics: string[];
+ metricTypes: string[];
+ metricConfig: MetricConfigOpt[];
+ }
): any {
if (resp.errors) {
ElMessage.error(resp.errors);
@@ -266,13 +269,14 @@ export function usePodsSource(
}
const data = pods.map((d: Instance | any, idx: number) => {
config.metrics.map((name: string, index: number) => {
+ const c = (config.metricConfig && config.metricConfig[index]) || {};
const key = name + idx + index;
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
- d[name] = resp.data[key];
+ d[name] = aggregation(resp.data[key], c);
}
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
- d[name] = resp.data[key].values.values.map(
- (d: { value: number }) => d.value
+ d[name] = resp.data[key].values.values.map((d: { value: number }) =>
+ aggregation(d.value, c)
);
}
});
@@ -307,32 +311,31 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
return { queryStr, conditions };
}
-function aggregation(val: number, standard: any): number | string {
- let data: number | string = val;
+export function aggregation(val: number, config: any): number | string {
+ let data: number | string = Number(val);
- if (!isNaN(standard.plus)) {
- data = val + Number(standard.plus);
- return data;
- }
- if (!isNaN(standard.minus)) {
- data = val - Number(standard.plus);
- return data;
- }
- if (!isNaN(standard.multiply) && standard.divide !== 0) {
- data = val * Number(standard.multiply);
- return data;
- }
- if (!isNaN(standard.divide) && standard.divide !== 0) {
- data = val / Number(standard.divide);
- return data;
- }
- if (standard.milliseconds) {
- data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
- return data;
- }
- if (standard.milliseconds) {
- data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
- return data;
+ switch (config.calculation) {
+ case Calculations.Percentage:
+ data = val / 100;
+ break;
+ case Calculations.ByteToKB:
+ data = val / 1024;
+ break;
+ case Calculations.Apdex:
+ data = val / 10000;
+ break;
+ case Calculations.ConvertSeconds:
+ data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
+ break;
+ case Calculations.ConvertMilliseconds:
+ data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
+ break;
+ case Calculations.Precision:
+ data = data.toFixed(2);
+ break;
+ default:
+ data;
+ break;
}
return data;
diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index c11b198..4bc2758 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -126,6 +126,9 @@ const msg = {
kubernetesCluster: "Cluster",
kubernetes: "Kubernetes",
textUrl: "Text Hyperlink",
+ textAlign: "Text Align",
+ metricLabel: "Metric Label",
+ showUnit: "Show Unit",
hourTip: "Select Hour",
minuteTip: "Select Minute",
secondTip: "Select Second",
@@ -258,7 +261,7 @@ const msg = {
independentSelector: "Selectors",
unknownMetrics: "Unknown Metrics",
labels: "Labels",
- aggregation: "Data Calculation",
+ aggregation: "Calculation",
unit: "Unit",
labelsIndex: "Label Subscript",
parentService: "Parent Service",
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index 6c1d334..43f09eb 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -126,6 +126,9 @@ const msg = {
kubernetesCluster: "集群",
kubernetes: "Kubernetes",
textUrl: "文本超链接",
+ textAlign: "文本对齐",
+ metricLabel: "指标标签",
+ showUnit: "显示单位",
hourTip: "选择小时",
minuteTip: "选择分钟",
secondTip: "选择秒数",
@@ -260,7 +263,7 @@ const msg = {
independentSelector: "独立选择器",
unknownMetrics: "未知指标",
labels: "标签",
- aggregation: "数据计算",
+ aggregation: "计算",
unit: "单位",
labelsIndex: "标签下标",
parentService: "父级服务",
diff --git a/src/store/data.ts b/src/store/data.ts
index 6fdfdd9..eba3c3b 100644
--- a/src/store/data.ts
+++ b/src/store/data.ts
@@ -28,6 +28,7 @@ export const NewControl = {
standard: {},
metrics: [""],
metricTypes: [""],
+ metricConfig: [],
};
export const TextConfig = {
fontColor: "white",
diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts
index 62d12b9..3a1fe7e 100644
--- a/src/types/dashboard.ts
+++ b/src/types/dashboard.ts
@@ -36,8 +36,17 @@ export interface LayoutConfig {
metricTypes: string[];
children?: any;
activedTabIndex?: number;
+ metricConfig?: MetricConfigOpt[];
}
+export type MetricConfigOpt = {
+ unit: string;
+ label: string;
+ calculation: string;
+ labelsIndex: string;
+ sortOrder: string;
+};
+
export interface WidgetConfig {
title?: string;
tips?: string;
@@ -87,7 +96,7 @@ export interface AreaConfig {
export interface CardConfig {
type?: string;
fontSize?: number;
- showUint?: boolean;
+ showUnit?: boolean;
textAlign?: "center" | "right" | "left";
}
diff --git a/src/views/dashboard/configuration/Text.vue b/src/views/dashboard/configuration/Text.vue
index 268be91..2e29815 100644
--- a/src/views/dashboard/configuration/Text.vue
+++ b/src/views/dashboard/configuration/Text.vue
@@ -30,6 +30,17 @@ limitations under the License. -->
/>
</div>
<div class="item">
+ <span class="label">{{ t("textAlign") }}</span>
+ <Selector
+ :value="textAlign"
+ :options="AlignStyle"
+ size="small"
+ placeholder="Select a color"
+ class="input"
+ @change="changeConfig({ textAlign: $event[0].value })"
+ />
+ </div>
+ <div class="item">
<span class="label">{{ t("backgroundColors") }}</span>
<Selector
:value="backgroundColor"
@@ -85,6 +96,7 @@ const backgroundColor = ref(originConfig.graph.backgroundColor || "green");
const fontColor = ref(originConfig.graph.fontColor || "white");
const content = ref<string>(originConfig.graph.content || "");
const fontSize = ref<number>(originConfig.graph.fontSize || 12);
+const textAlign = ref(originConfig.graph.textAlign || "left");
const Colors = [
{
label: "Green",
@@ -97,6 +109,14 @@ const Colors = [
{ label: "Black", value: "black" },
{ label: "Orange", value: "orange" },
];
+const AlignStyle = [
+ {
+ label: "Left",
+ value: "left",
+ },
+ { label: "Center", value: "center" },
+ { label: "Right", value: "right" },
+];
function changeConfig(param: { [key: string]: unknown }) {
const { selectedGrid } = dashboardStore;
const graph = {
diff --git a/src/views/dashboard/configuration/Widget.vue b/src/views/dashboard/configuration/Widget.vue
index 442e859..67f6949 100644
--- a/src/views/dashboard/configuration/Widget.vue
+++ b/src/views/dashboard/configuration/Widget.vue
@@ -36,7 +36,7 @@ limitations under the License. -->
i: dashboardStore.selectedGrid.i,
metrics: dashboardStore.selectedGrid.metrics,
metricTypes: dashboardStore.selectedGrid.metricTypes,
- standard: dashboardStore.selectedGrid.standard,
+ metricConfig: dashboardStore.selectedGrid.metricConfig,
}"
:isEdit="isEdit"
@changeOpt="setStatus"
@@ -64,9 +64,6 @@ limitations under the License. -->
<el-collapse-item :title="t('widgetOptions')" name="3">
<WidgetOptions />
</el-collapse-item>
- <el-collapse-item :title="t('standardOptions')" name="4">
- <StandardOptions @update="getSource" @loading="setLoading" />
- </el-collapse-item>
</el-collapse>
</div>
<div class="footer">
@@ -88,16 +85,14 @@ import { Option } from "@/types/app";
import graphs from "../graphs";
import configs from "./widget/graph-styles";
import WidgetOptions from "./widget/WidgetOptions.vue";
-import StandardOptions from "./widget/StandardOptions.vue";
-import MetricOptions from "./widget/MetricOptions.vue";
+import MetricOptions from "./widget/metric/Index.vue";
export default defineComponent({
- name: "ConfigEdit",
+ name: "WidgetEdit",
components: {
...graphs,
...configs,
WidgetOptions,
- StandardOptions,
MetricOptions,
},
setup() {
diff --git a/src/views/dashboard/configuration/widget/StandardOptions.vue b/src/views/dashboard/configuration/widget/StandardOptions.vue
deleted file mode 100644
index 5b94f05..0000000
--- a/src/views/dashboard/configuration/widget/StandardOptions.vue
+++ /dev/null
@@ -1,186 +0,0 @@
-<!-- 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. -->
-<template>
- <div class="item">
- <span class="label">{{ t("unit") }}</span>
- <el-input
- class="input"
- v-model="selectedGrid.standard.unit"
- size="small"
- placeholder="Please input Unit"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("sortOrder") }}</span>
- <Selector
- :value="sortOrder"
- :options="SortOrder"
- size="small"
- placeholder="Select a sort order"
- class="selector"
- @change="changeStandardOpt({ sortOrder })"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("labels") }}</span>
- <el-input
- class="input"
- v-model="selectedGrid.standard.metricLabels"
- size="small"
- placeholder="auto"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("labelsIndex") }}</span>
- <el-input
- class="input"
- v-model="selectedGrid.standard.labelsIndex"
- size="small"
- placeholder="auto"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("plus") }}</span>
- <el-input-number
- class="input"
- v-model="selectedGrid.standard.plus"
- :min="0"
- size="small"
- placeholder="Please input"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("minus") }}</span>
- <el-input-number
- class="input"
- v-model="selectedGrid.standard.minus"
- :min="0"
- size="small"
- placeholder="Please input"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("multiply") }}</span>
- <el-input-number
- class="input"
- v-model="selectedGrid.standard.multiply"
- :min="1"
- size="small"
- placeholder="Please input"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("divide") }}</span>
- <el-input-number
- class="input"
- v-model="selectedGrid.standard.divide"
- size="small"
- placeholder="Please input"
- :min="1"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("convertToMilliseconds") }}</span>
- <el-input-number
- class="input"
- :min="0"
- v-model="selectedGrid.standard.milliseconds"
- size="small"
- placeholder="Please input"
- @change="changeStandardOpt"
- />
- </div>
- <div class="item">
- <span class="label">{{ t("convertToSeconds") }}</span>
- <el-input-number
- class="input"
- :min="0"
- v-model="selectedGrid.standard.seconds"
- size="small"
- placeholder="Please input"
- @change="changeStandardOpt"
- />
- </div>
-</template>
-<script lang="ts" setup>
-import { ref } from "vue";
-import { useI18n } from "vue-i18n";
-import { SortOrder } from "../../data";
-import { useDashboardStore } from "@/store/modules/dashboard";
-import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
-import { ElMessage } from "element-plus";
-
-/*global defineEmits */
-const { t } = useI18n();
-const emit = defineEmits(["update", "loading"]);
-const dashboardStore = useDashboardStore();
-const { selectedGrid } = dashboardStore;
-const sortOrder = ref<string>(selectedGrid.standard.sortOrder || "DES");
-
-function changeStandardOpt(param?: any) {
- let standard = dashboardStore.selectedGrid.standard;
- if (param) {
- standard = {
- ...dashboardStore.selectedGrid.standard,
- ...param,
- };
- dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, standard });
- }
- queryMetrics();
-}
-async function queryMetrics() {
- const params = useQueryProcessor(dashboardStore.selectedGrid);
- if (!params) {
- emit("update", {});
- return;
- }
-
- emit("loading", true);
- const json = await dashboardStore.fetchMetricValue(params);
- emit("loading", false);
- if (json.errors) {
- ElMessage.error(json.errors);
- return;
- }
- const source = useSourceProcessor(json, dashboardStore.selectedGrid);
- emit("update", source);
-}
-</script>
-<style lang="scss" scoped>
-.label {
- font-size: 13px;
- font-weight: 500;
- display: block;
- margin-bottom: 5px;
-}
-
-.input {
- width: 500px;
-}
-
-.item {
- margin-bottom: 10px;
-}
-
-.selector {
- width: 500px;
-}
-</style>
diff --git a/src/views/dashboard/configuration/widget/graph-styles/Card.vue b/src/views/dashboard/configuration/widget/graph-styles/Card.vue
index 1a5bcb7..6259b14 100644
--- a/src/views/dashboard/configuration/widget/graph-styles/Card.vue
+++ b/src/views/dashboard/configuration/widget/graph-styles/Card.vue
@@ -26,6 +26,15 @@ limitations under the License. -->
@change="updateConfig({ fontSize })"
/>
</div>
+ <div class="item">
+ <span class="label">{{ t("showUnit") }}</span>
+ <el-switch
+ v-model="showUnit"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateConfig({ showUnit })"
+ />
+ </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
@@ -36,13 +45,14 @@ const { t } = useI18n();
const dashboardStore = useDashboardStore();
const { selectedGrid } = dashboardStore;
const fontSize = ref(selectedGrid.graph.fontSize);
+const showUnit = ref<boolean>(selectedGrid.graph.showUnit);
function updateConfig(param: { [key: string]: unknown }) {
const graph = {
- ...selectedGrid.graph,
+ ...dashboardStore.selectedGrid.graph,
...param,
};
- dashboardStore.selectWidget({ ...selectedGrid, graph });
+ dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, graph });
}
</script>
<style lang="scss" scoped>
diff --git a/src/views/dashboard/configuration/widget/MetricOptions.vue b/src/views/dashboard/configuration/widget/metric/Index.vue
similarity index 87%
rename from src/views/dashboard/configuration/widget/MetricOptions.vue
rename to src/views/dashboard/configuration/widget/metric/Index.vue
index 0e399f3..e118270 100644
--- a/src/views/dashboard/configuration/widget/MetricOptions.vue
+++ b/src/views/dashboard/configuration/widget/metric/Index.vue
@@ -49,6 +49,18 @@ limitations under the License. -->
@change="changeMetricType(index, $event)"
class="selectors"
/>
+ <el-popover placement="top" :width="400" trigger="click">
+ <template #reference>
+ <span @click="setMetricConfig(index)">
+ <Icon class="cp mr-5" iconName="mode_edit" size="middle" />
+ </span>
+ </template>
+ <Standard
+ @update="queryMetrics"
+ :currentMetricConfig="currentMetricConfig"
+ :index="index"
+ />
+ </el-popover>
<span
v-show="states.isList || states.metricTypes[0] === 'readMetricsValues'"
>
@@ -96,12 +108,13 @@ import {
ChartTypes,
PodsChartTypes,
MetricsType,
-} from "../../data";
+} from "../../../data";
import { ElMessage } from "element-plus";
import Icon from "@/components/Icon.vue";
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
import { useI18n } from "vue-i18n";
-import { DashboardItem } from "@/types/dashboard";
+import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
+import Standard from "./Standard.vue";
/*global defineEmits */
const { t } = useI18n();
@@ -127,6 +140,13 @@ const states = reactive<{
dashboardName: graph.dashboardName,
dashboardList: [{ label: "", value: "" }],
});
+const currentMetricConfig = ref<MetricConfigOpt>({
+ unit: "",
+ label: "",
+ labelsIndex: "",
+ calculation: "",
+ sortOrder: "DES",
+});
states.isList = ListChartTypes.includes(graph.type);
const defaultLen = ref<number>(states.isList ? 5 : 20);
@@ -323,8 +343,8 @@ async function queryMetrics() {
if (states.isList) {
return;
}
- const { standard } = dashboardStore.selectedGrid;
- const params = useQueryProcessor({ ...states, standard });
+ const { metricConfig } = dashboardStore.selectedGrid;
+ const params = useQueryProcessor({ ...states, metricConfig });
if (!params) {
emit("update", {});
return;
@@ -337,7 +357,7 @@ async function queryMetrics() {
ElMessage.error(json.errors);
return;
}
- const source = useSourceProcessor(json, { ...states, standard });
+ const source = useSourceProcessor(json, { ...states, metricConfig });
emit("update", source);
}
@@ -372,11 +392,20 @@ function deleteMetric(index: number) {
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
...{ metricTypes: states.metricTypes, metrics: states.metrics },
+ metricConfig: [],
});
return;
}
states.metrics.splice(index, 1);
states.metricTypes.splice(index, 1);
+ const config = dashboardStore.selectedGrid.metricConfig || [];
+ const metricConfig = config[index] ? config.splice(index, 1) : config;
+ dashboardStore.selectWidget({
+ ...dashboardStore.selectedGrid,
+ ...{ metricTypes: states.metricTypes, metrics: states.metrics },
+ metricConfig,
+ });
+ console.log(dashboardStore.selectedGrid);
}
function setMetricTypeList(type: string) {
if (type !== MetricsType.REGULAR_VALUE) {
@@ -393,6 +422,26 @@ function setMetricTypeList(type: string) {
}
return MetricTypes[type];
}
+function setMetricConfig(index: number) {
+ const n = {
+ unit: "",
+ label: "",
+ calculation: "",
+ labelsIndex: "",
+ sortOrder: "DES",
+ };
+ if (
+ !dashboardStore.selectedGrid.metricConfig ||
+ !dashboardStore.selectedGrid.metricConfig[index]
+ ) {
+ currentMetricConfig.value = n;
+ return;
+ }
+ currentMetricConfig.value = {
+ ...n,
+ ...dashboardStore.selectedGrid.metricConfig[index],
+ };
+}
</script>
<style lang="scss" scoped>
.ds-name {
diff --git a/src/views/dashboard/configuration/widget/metric/Standard.vue b/src/views/dashboard/configuration/widget/metric/Standard.vue
new file mode 100644
index 0000000..d0a2ff5
--- /dev/null
+++ b/src/views/dashboard/configuration/widget/metric/Standard.vue
@@ -0,0 +1,129 @@
+<!-- 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. -->
+<template>
+ <div class="config-panel">
+ <div class="item mb-10">
+ <span class="label">{{ t("unit") }}</span>
+ <el-input
+ class="input"
+ v-model="currentMetric.unit"
+ size="small"
+ placeholder="Please input unit"
+ @change="changeConfigs(index, { unit: currentMetric.unit })"
+ />
+ </div>
+ <div class="item mb-10" v-show="metricType === 'readLabeledMetricsValues'">
+ <span class="label">{{ t("labels") }}</span>
+ <el-input
+ class="input"
+ v-model="currentMetric.label"
+ size="small"
+ placeholder="Please input a name"
+ @change="changeConfigs(index, { label: currentMetric.label })"
+ />
+ </div>
+ <div class="item mb-10" v-show="metricType === 'readLabeledMetricsValues'">
+ <span class="label">{{ t("labelsIndex") }}</span>
+ <el-input
+ class="input"
+ v-model="currentMetric.labelsIndex"
+ size="small"
+ placeholder="auto"
+ @change="
+ changeConfigs(index, { labelsIndex: currentMetric.labelsIndex })
+ "
+ />
+ </div>
+ <div class="item mb-10">
+ <span class="label">{{ t("aggregation") }}</span>
+ <SelectSingle
+ :value="currentMetric.calculation"
+ :options="CalculationOpts"
+ @change="changeConfigs(index, { calculation: $event })"
+ class="selectors"
+ :clearable="true"
+ />
+ </div>
+ <div
+ class="item"
+ v-show="['sortMetrics', 'readSampledRecords'].includes(metricType)"
+ >
+ <span class="label">{{ t("sortOrder") }}</span>
+ <SelectSingle
+ :value="currentMetric.sortOrder || 'DES'"
+ :options="SortOrder"
+ class="selectors"
+ @change="changeConfigs(index, { sortOrder: $event })"
+ />
+ </div>
+ </div>
+</template>
+<script lang="ts" setup>
+import { ref } from "vue";
+import type { PropType } from "vue";
+import { useI18n } from "vue-i18n";
+import { SortOrder, CalculationOpts } from "../../../data";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import { MetricConfigOpt } from "@/types/dashboard";
+
+/*global defineEmits, defineProps */
+const props = defineProps({
+ currentMetricConfig: {
+ type: Object as PropType<MetricConfigOpt>,
+ default: () => ({ unit: "" }),
+ },
+ index: { type: Number, default: 0 },
+});
+const { t } = useI18n();
+const emit = defineEmits(["update"]);
+const dashboardStore = useDashboardStore();
+const currentMetric = ref<MetricConfigOpt>(props.currentMetricConfig);
+const metricType = ref<string>(
+ dashboardStore.selectedGrid.metricTypes[props.index]
+);
+
+function changeConfigs(index: number, param: { [key: string]: string }) {
+ const metricConfig = dashboardStore.selectedGrid.metricConfig || [];
+ metricConfig[index] = { ...metricConfig[index], ...param };
+
+ dashboardStore.selectWidget({
+ ...dashboardStore.selectedGrid,
+ metricConfig,
+ });
+ emit("update");
+}
+</script>
+<style lang="scss" scoped>
+.config-panel {
+ padding: 10px 5px;
+ position: relative;
+}
+
+.label {
+ width: 150px;
+ display: inline-block;
+ font-size: 12px;
+}
+
+.close {
+ position: absolute;
+ top: -8px;
+ right: -15px;
+}
+
+.selectors {
+ width: 365px;
+}
+</style>
diff --git a/src/views/dashboard/controls/Text.vue b/src/views/dashboard/controls/Text.vue
index 60f77ed..72c65b8 100644
--- a/src/views/dashboard/controls/Text.vue
+++ b/src/views/dashboard/controls/Text.vue
@@ -36,18 +36,17 @@ limitations under the License. -->
</div>
<div
class="body"
- :style="{ backgroundColor: TextColors[data.graph.backgroundColor] }"
+ :class="data.graph.textAlign === 'center' ? 'center' : ''"
+ :style="{
+ backgroundColor: TextColors[data.graph.backgroundColor],
+ color: TextColors[data.graph.fontColor],
+ fontSize: data.graph.fontSize + 'px',
+ textAlign: data.graph.textAlign,
+ }"
>
- <div
- :style="{
- color: TextColors[data.graph.fontColor],
- fontSize: data.graph.fontSize + 'px',
- }"
- >
- <a :href="data.graph.url" target="_blank">
- {{ data.graph.content }}
- </a>
- </div>
+ <a :href="data.graph.url" target="_blank">
+ {{ data.graph.content }}
+ </a>
</div>
</div>
</template>
@@ -75,14 +74,6 @@ function editConfig() {
dashboardStore.setConfigPanel(true);
dashboardStore.selectWidget(props.data);
}
-function viewText() {
- const path = props.data.graph.url;
- console.log(path);
- if (!path) {
- return;
- }
- window.open(path, "_blank");
-}
</script>
<style lang="scss" scoped>
.topology {
@@ -97,21 +88,21 @@ function viewText() {
.header {
position: absolute;
- top: 10px;
- right: 10px;
+ top: 5px;
+ right: 5px;
}
.body {
text-align: center;
width: 100%;
height: 100%;
+ line-height: 100%;
cursor: pointer;
- box-sizing: border-box;
- color: #333;
- display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
- -webkit-box-align: center;
+ box-sizing: border-box;
+ display: -webkit-box;
+ // -webkit-box-align: left;
}
.tools {
diff --git a/src/views/dashboard/controls/Widget.vue b/src/views/dashboard/controls/Widget.vue
index c7915f1..2d71bf2 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -64,8 +64,8 @@ limitations under the License. -->
metrics: data.metrics,
metricTypes: data.metricTypes,
i: data.i,
+ metricConfig: data.metricConfig,
}"
- :standard="data.standard"
:needQuery="needQuery"
/>
</div>
@@ -128,7 +128,7 @@ export default defineComponent({
const d = {
metrics: props.data.metrics,
metricTypes: props.data.metricTypes,
- standard: props.data.standard,
+ metricConfig: props.data.metricConfig || [],
};
state.source = useSourceProcessor(json, d);
}
diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts
index 9668d76..b938d5a 100644
--- a/src/views/dashboard/data.ts
+++ b/src/views/dashboard/data.ts
@@ -244,7 +244,7 @@ export enum LegendOpt {
}
export const DepthList = [1, 2, 3, 4, 5].map((item: number) => ({
value: item,
- label: item,
+ label: String(item),
}));
export const Status = [
{ label: "All", value: "ALL" },
@@ -264,3 +264,15 @@ export const TextColors: { [key: string]: string } = {
black: "#000",
orange: "#E6A23C",
};
+
+export const CalculationOpts = [
+ { label: "Percentage", value: "percentage" },
+ { label: "Byte to KB", value: "byteToKB" },
+ { label: "Apdex", value: "apdex" },
+ {
+ label: "Convert milliseconds to YYYY-MM-DD HH:mm:ss",
+ value: "convertMilliseconds",
+ },
+ { label: "Convert seconds to YYYY-MM-DD HH:mm:ss", value: "convertSeconds" },
+ { label: "Precision is 2", value: "precision" },
+];
diff --git a/src/views/dashboard/graphs/Card.vue b/src/views/dashboard/graphs/Card.vue
index 1b0422e..b715881 100644
--- a/src/views/dashboard/graphs/Card.vue
+++ b/src/views/dashboard/graphs/Card.vue
@@ -26,12 +26,14 @@ limitations under the License. -->
? null
: singleVal.toFixed(2)
}}
- <span v-show="config.showUint">{{ standard.unit }}</span>
+ <span v-show="config.showUnit">
+ {{ metricConfig[0]?.unit }}
+ </span>
</div>
</template>
<script lang="ts" setup>
import { computed, PropType } from "vue";
-import { CardConfig, StandardConfig } from "@/types/dashboard";
+import { CardConfig, MetricConfigOpt } from "@/types/dashboard";
/*global defineProps */
const props = defineProps({
@@ -40,14 +42,16 @@ const props = defineProps({
default: () => ({}),
},
config: {
- type: Object as PropType<CardConfig>,
- default: () => ({ fontSize: 12, showUint: true, textAlign: "center" }),
- },
- standard: {
- type: Object as PropType<StandardConfig>,
- default: () => ({ unit: "" }),
+ type: Object as PropType<CardConfig & { metricConfig?: MetricConfigOpt[] }>,
+ default: () => ({
+ fontSize: 12,
+ showUnit: true,
+ textAlign: "center",
+ metricConfig: [],
+ }),
},
});
+const metricConfig = computed(() => props.config.metricConfig || []);
const key = computed(() => Object.keys(props.data)[0]);
const singleVal = computed(() => props.data[key.value]);
</script>
diff --git a/src/views/dashboard/graphs/EndpointList.vue b/src/views/dashboard/graphs/EndpointList.vue
index a8d4c35..1191915 100644
--- a/src/views/dashboard/graphs/EndpointList.vue
+++ b/src/views/dashboard/graphs/EndpointList.vue
@@ -44,7 +44,7 @@ limitations under the License. -->
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
- :label="metric"
+ :label="`${metric} ${getUnit(index)}`"
:key="metric + index"
>
<template #default="scope">
@@ -92,6 +92,7 @@ import Card from "./Card.vue";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
+import { MetricConfigOpt } from "@/types/dashboard";
/*global defineProps */
const props = defineProps({
@@ -104,7 +105,7 @@ const props = defineProps({
i: string;
metrics: string[];
metricTypes: string[];
- }
+ } & { metricConfig: MetricConfigOpt[] }
>,
default: () => ({ dashboardName: "", fontSize: 12, i: "" }),
},
@@ -155,7 +156,12 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
ElMessage.error(json.errors);
return;
}
- endpoints.value = usePodsSource(currentPods, json, props.config);
+ const metricConfig = props.config.metricConfig || [];
+
+ endpoints.value = usePodsSource(currentPods, json, {
+ ...props.config,
+ metricConfig: metricConfig,
+ });
return;
}
endpoints.value = currentPods;
@@ -184,12 +190,21 @@ async function searchList() {
const limit = searchText.value ? undefined : total;
await queryEndpoints(limit);
}
+function getUnit(index: number) {
+ const u =
+ (props.config.metricConfig &&
+ props.config.metricConfig[index] &&
+ props.config.metricConfig[index].unit) ||
+ "";
+ if (u) {
+ return `(${u})`;
+ }
+ return u;
+}
watch(
() => [props.config.metricTypes, props.config.metrics],
async () => {
- if (props.isEdit) {
- queryEndpointMetrics(endpoints.value);
- }
+ queryEndpointMetrics(endpoints.value);
// emit("changeOpt", false);
}
);
diff --git a/src/views/dashboard/graphs/InstanceList.vue b/src/views/dashboard/graphs/InstanceList.vue
index b2c7aa7..f918ae2 100644
--- a/src/views/dashboard/graphs/InstanceList.vue
+++ b/src/views/dashboard/graphs/InstanceList.vue
@@ -44,7 +44,7 @@ limitations under the License. -->
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
- :label="metric"
+ :label="`${metric} ${getUnit(index)}`"
:key="metric + index"
>
<template #default="scope">
@@ -112,6 +112,7 @@ import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
+import { MetricConfigOpt } from "@/types/dashboard";
/*global defineProps */
const props = defineProps({
@@ -122,7 +123,7 @@ const props = defineProps({
metrics: string[];
metricTypes: string[];
isEdit: boolean;
- }
+ } & { metricConfig: MetricConfigOpt[] }
>,
default: () => ({
dashboardName: "",
@@ -180,7 +181,11 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
ElMessage.error(json.errors);
return;
}
- instances.value = usePodsSource(currentInstances, json, props.config);
+ const metricConfig = props.config.metricConfig || [];
+ instances.value = usePodsSource(currentInstances, json, {
+ ...props.config,
+ metricConfig,
+ });
return;
}
instances.value = currentInstances;
@@ -214,6 +219,18 @@ function searchList() {
instances.value = searchInstances.value.splice(0, pageSize);
}
+function getUnit(index: number) {
+ const u =
+ (props.config.metricConfig &&
+ props.config.metricConfig[index] &&
+ props.config.metricConfig[index].unit) ||
+ "";
+ if (u) {
+ return `(${u})`;
+ }
+ return u;
+}
+
watch(
() => [props.config.metricTypes, props.config.metrics],
() => {
diff --git a/src/views/dashboard/graphs/ServiceList.vue b/src/views/dashboard/graphs/ServiceList.vue
index 8eae295..4423601 100644
--- a/src/views/dashboard/graphs/ServiceList.vue
+++ b/src/views/dashboard/graphs/ServiceList.vue
@@ -56,7 +56,7 @@ limitations under the License. -->
</el-table-column>
<el-table-column
v-for="(metric, index) in config.metrics"
- :label="metric"
+ :label="`${metric} ${getUnit(index)}`"
:key="metric + index"
>
<template #default="scope">
@@ -104,6 +104,7 @@ import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
+import { MetricConfigOpt } from "@/types/dashboard";
/*global defineProps */
const props = defineProps({
@@ -117,7 +118,7 @@ const props = defineProps({
metrics: string[];
metricTypes: string[];
isEdit: boolean;
- }
+ } & { metricConfig: MetricConfigOpt[] }
>,
default: () => ({ dashboardName: "", fontSize: 12 }),
},
@@ -214,7 +215,11 @@ async function queryServiceMetrics(currentServices: Service[]) {
ElMessage.error(json.errors);
return;
}
- services.value = usePodsSource(currentServices, json, props.config);
+ const metricConfig = props.config.metricConfig || [];
+ services.value = usePodsSource(currentServices, json, {
+ ...props.config,
+ metricConfig: metricConfig || [],
+ });
return;
}
services.value = currentServices;
@@ -250,6 +255,17 @@ function searchList() {
);
services.value = searchServices.splice(0, pageSize);
}
+function getUnit(index: number) {
+ const u =
+ (props.config.metricConfig &&
+ props.config.metricConfig[index] &&
+ props.config.metricConfig[index].unit) ||
+ "";
+ if (u) {
+ return `(${u})`;
+ }
+ return u;
+}
watch(
() => [props.config.metricTypes, props.config.metrics],
() => {
diff --git a/src/views/dashboard/related/topology/components/Graph.vue b/src/views/dashboard/related/topology/components/Graph.vue
index f48705a..f1f84b3 100644
--- a/src/views/dashboard/related/topology/components/Graph.vue
+++ b/src/views/dashboard/related/topology/components/Graph.vue
@@ -90,6 +90,8 @@ import { Option } from "@/types/app";
import { Service } from "@/types/selector";
import { useAppStoreWithOut } from "@/store/modules/app";
import getDashboard from "@/hooks/useDashboardsSession";
+import { MetricConfigOpt } from "@/types/dashboard";
+import { aggregation } from "@/hooks/useProcessor";
/*global Nullable, defineProps */
const props = defineProps({
@@ -275,13 +277,17 @@ function update() {
handleNodeClick: handleNodeClick,
tipHtml: (data: Node) => {
const nodeMetrics: string[] = settings.value.nodeMetrics || [];
- const html = nodeMetrics.map((m) => {
+ const nodeMetricConfig = settings.value.nodeMetricConfig || [];
+ const html = nodeMetrics.map((m, index) => {
const metric =
- topologyStore.nodeMetricValue[m].values.filter(
+ topologyStore.nodeMetricValue[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id
- )[0] || {};
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div class="mb-5"><span class="grey">${m}: </span>${val}</div>`;
+ ) || {};
+ const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
});
return [
` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`,
@@ -306,24 +312,34 @@ function update() {
tipHtml: (data: Call) => {
const linkClientMetrics: string[] =
settings.value.linkClientMetrics || [];
+ const linkServerMetricConfig: MetricConfigOpt[] =
+ settings.value.linkServerMetricConfig || [];
+ const linkClientMetricConfig: MetricConfigOpt[] =
+ settings.value.linkClientMetricConfig || [];
const linkServerMetrics: string[] =
settings.value.linkServerMetrics || [];
- const htmlServer = linkServerMetrics.map((m) => {
- const metric = topologyStore.linkServerMetrics[m].values.filter(
+ const htmlServer = linkServerMetrics.map((m, index) => {
+ const metric = topologyStore.linkServerMetrics[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id
- )[0];
+ );
if (metric) {
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div class="mb-5"><span class="grey">${m}: </span>${val}</div>`;
+ const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
}
});
- const htmlClient = linkClientMetrics.map((m) => {
- const metric = topologyStore.linkClientMetrics[m].values.filter(
+ const htmlClient = linkClientMetrics.map((m: string, index: number) => {
+ const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
+ const metric = topologyStore.linkClientMetrics[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id
- )[0];
+ );
if (metric) {
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div class="mb-5"><span class="grey">${m}: </span>${val}</div>`;
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
}
});
const html = [
diff --git a/src/views/dashboard/related/topology/components/Metrics.vue b/src/views/dashboard/related/topology/components/Metrics.vue
new file mode 100644
index 0000000..43a4298
--- /dev/null
+++ b/src/views/dashboard/related/topology/components/Metrics.vue
@@ -0,0 +1,170 @@
+<!-- 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. -->
+<template>
+ <div class="config-panel">
+ <div class="item mb-10">
+ <span class="label">{{ t("metrics") }}</span>
+ <SelectSingle
+ :value="currentMetric"
+ :options="metrics"
+ @change="changeMetric"
+ class="selectors"
+ />
+ </div>
+ <div class="item mb-10">
+ <span class="label">{{ t("unit") }}</span>
+ <el-input
+ class="input"
+ v-model="currentConfig.unit"
+ size="small"
+ placeholder="Please input unit"
+ @change="changeConfigs({ unit: currentConfig.unit })"
+ />
+ </div>
+ <div class="item mb-10">
+ <span class="label">{{ t("labels") }}</span>
+ <el-input
+ class="input"
+ v-model="currentConfig.label"
+ size="small"
+ placeholder="Please input a label"
+ @change="changeConfigs({ label: currentConfig.label })"
+ />
+ </div>
+ <div class="item mb-10">
+ <span class="label">{{ t("aggregation") }}</span>
+ <SelectSingle
+ :value="currentConfig.calculation"
+ :options="CalculationOpts"
+ @change="changeConfigs({ calculation: $event })"
+ class="selectors"
+ :clearable="true"
+ />
+ </div>
+ </div>
+</template>
+<script lang="ts" setup>
+import { ref, computed, watch } from "vue";
+import type { PropType } from "vue";
+import { useI18n } from "vue-i18n";
+import { CalculationOpts } from "../../../data";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import { MetricConfigOpt } from "@/types/dashboard";
+import { Option } from "element-plus/es/components/select-v2/src/select.types";
+
+/*global defineEmits, defineProps */
+const props = defineProps({
+ currentMetricConfig: {
+ type: Object as PropType<MetricConfigOpt>,
+ default: () => ({ unit: "" }),
+ },
+ type: { type: String, default: "" },
+ metrics: { type: Array as PropType<string[]>, default: () => [] },
+});
+const { t } = useI18n();
+const emit = defineEmits(["update"]);
+const dashboardStore = useDashboardStore();
+const m = props.metrics.map((d: string) => {
+ return { label: d, value: d };
+});
+const metrics = ref<Option[]>(m.length ? m : [{ label: "", value: "" }]);
+const currentMetric = ref<string>(metrics.value[0].value);
+const currentConfig = ref<{ unit: string; calculation: string; label: string }>(
+ {
+ unit: "",
+ calculation: "",
+ label: "",
+ }
+);
+const currentIndex = ref<number>(0);
+const getMetricConfig = computed(() => {
+ let config = [];
+ switch (props.type) {
+ case "linkServerMetricConfig":
+ config = dashboardStore.selectedGrid.linkServerMetricConfig;
+ break;
+ case "linkClientMetricConfig":
+ config = dashboardStore.selectedGrid.linkClientMetricConfig;
+ break;
+ case "nodeMetricConfig":
+ config = dashboardStore.selectedGrid.nodeMetricConfig;
+ break;
+ }
+ return config || [];
+});
+
+function changeConfigs(param: { [key: string]: string }) {
+ const metricConfig = getMetricConfig.value;
+ metricConfig[currentIndex.value] = {
+ ...metricConfig[currentIndex.value],
+ ...param,
+ };
+ emit("update", { [props.type]: metricConfig });
+}
+function changeMetric(val: string) {
+ currentMetric.value = val;
+ const index = metrics.value.findIndex((d: Option) => d.value === val);
+ currentIndex.value = index || 0;
+ const config = getMetricConfig.value || [];
+
+ currentConfig.value = {
+ unit: "",
+ label: "",
+ calculation: "",
+ ...config[index],
+ };
+}
+watch(
+ () => props.type,
+ () => {
+ const m = props.metrics.map((d: string) => {
+ return { label: d, value: d };
+ });
+ metrics.value = m.length ? m : [{ label: "", value: "" }];
+ currentMetric.value = metrics.value[0].value;
+ const config = getMetricConfig.value || [];
+ currentIndex.value = 0;
+ currentConfig.value = {
+ unit: "",
+ label: "",
+ calculation: "",
+ ...config[0],
+ };
+ }
+);
+</script>
+<style lang="scss" scoped>
+.config-panel {
+ padding: 10px 5px;
+ position: relative;
+ min-height: 300px;
+}
+
+.label {
+ width: 150px;
+ display: inline-block;
+ font-size: 12px;
+}
+
+.close {
+ position: absolute;
+ top: -8px;
+ right: -15px;
+}
+
+.selectors {
+ width: 365px;
+}
+</style>
diff --git a/src/views/dashboard/related/topology/components/PodTopology.vue b/src/views/dashboard/related/topology/components/PodTopology.vue
index 8fc4df7..131221b 100644
--- a/src/views/dashboard/related/topology/components/PodTopology.vue
+++ b/src/views/dashboard/related/topology/components/PodTopology.vue
@@ -54,7 +54,7 @@ limitations under the License. -->
element-loading-background="rgba(0, 0, 0, 0)"
@click="handleClick"
>
- <Sankey @click="selectNodeLink" />
+ <Sankey @click="selectNodeLink" :settings="settings" />
</div>
<div
class="operations-list"
diff --git a/src/views/dashboard/related/topology/components/Sankey.vue b/src/views/dashboard/related/topology/components/Sankey.vue
index 47b3cca..e92257c 100644
--- a/src/views/dashboard/related/topology/components/Sankey.vue
+++ b/src/views/dashboard/related/topology/components/Sankey.vue
@@ -17,11 +17,19 @@ limitations under the License. -->
<Graph :option="option" @select="clickChart" />
</template>
<script lang="ts" setup>
-import { computed } from "vue";
+import { computed, PropType } from "vue";
import { useTopologyStore } from "@/store/modules/topology";
import { Node, Call } from "@/types/topology";
+import { MetricConfigOpt } from "@/types/dashboard";
+import { aggregation } from "@/hooks/useProcessor";
-/*global defineEmits */
+/*global defineEmits, defineProps */
+const props = defineProps({
+ settings: {
+ type: Object as PropType<any>,
+ default: () => ({}),
+ },
+});
const emit = defineEmits(["click"]);
const topologyStore = useTopologyStore();
const option = computed(() => getOption());
@@ -77,23 +85,34 @@ function getOption() {
function linkTooltip(data: Call) {
const clientMetrics: string[] = Object.keys(topologyStore.linkClientMetrics);
const serverMetrics: string[] = Object.keys(topologyStore.linkServerMetrics);
- const htmlServer = serverMetrics.map((m) => {
- const metric = topologyStore.linkServerMetrics[m].values.filter(
- (val: { id: string; value: unknown }) => val.id === data.id
- )[0];
+ const linkServerMetricConfig: MetricConfigOpt[] =
+ props.settings.linkServerMetricConfig || [];
+ const linkClientMetricConfig: MetricConfigOpt[] =
+ props.settings.linkClientMetricConfig || [];
+
+ const htmlServer = serverMetrics.map((m, index) => {
+ const metric =
+ topologyStore.linkServerMetrics[m].values.find(
+ (val: { id: string; value: unknown }) => val.id === data.id
+ ) || {};
if (metric) {
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div><span>${m}: </span>${val}</div>`;
+ const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
}
});
- const htmlClient = clientMetrics.map((m) => {
- const metric = topologyStore.linkClientMetrics[m].values.filter(
- (val: { id: string; value: unknown }) => val.id === data.id
- )[0];
- if (metric) {
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div><span>${m}: </span>${val}</div>`;
- }
+ const htmlClient = clientMetrics.map((m, index) => {
+ const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
+ const metric =
+ topologyStore.linkClientMetrics[m].values.find(
+ (val: { id: string; value: unknown }) => val.id === data.id
+ ) || {};
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
});
const html = [
`<div>${data.sourceObj.serviceName} -> ${data.targetObj.serviceName}</div>`,
@@ -106,13 +125,17 @@ function linkTooltip(data: Call) {
function nodeTooltip(data: Node) {
const nodeMetrics: string[] = Object.keys(topologyStore.nodeMetricValue);
- const html = nodeMetrics.map((m) => {
+ const nodeMetricConfig = props.settings.nodeMetricConfig || [];
+ const html = nodeMetrics.map((m, index) => {
const metric =
- topologyStore.nodeMetricValue[m].values.filter(
+ topologyStore.nodeMetricValue[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id
- )[0] || {};
- const val = m.includes("_sla") ? metric.value / 100 : metric.value;
- return ` <div><span>${m}: </span>${val}</div>`;
+ ) || {};
+ const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
+ const v = aggregation(metric.value, opt);
+ return ` <div class="mb-5"><span class="grey">${
+ opt.label || m
+ }: </span>${v} ${opt.unit || ""}</div>`;
});
return [` <div><span>name: </span>${data.serviceName}</div>`, ...html].join(
" "
diff --git a/src/views/dashboard/related/topology/components/Settings.vue b/src/views/dashboard/related/topology/components/Settings.vue
index 21e15c8..bfbc3fb 100644
--- a/src/views/dashboard/related/topology/components/Settings.vue
+++ b/src/views/dashboard/related/topology/components/Settings.vue
@@ -25,7 +25,27 @@ limitations under the License. -->
class="inputs"
:clearable="true"
/>
- <div class="label">{{ t("linkServerMetrics") }}</div>
+ <div class="label">
+ <span>{{ t("linkServerMetrics") }}</span>
+ <el-popover
+ placement="left"
+ :width="400"
+ trigger="click"
+ effect="dark"
+ v-if="states.linkServerMetrics.length"
+ >
+ <template #reference>
+ <span @click="setConfigType('linkServerMetricConfig')">
+ <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
+ </span>
+ </template>
+ <Metrics
+ :type="configType"
+ :metrics="states.linkServerMetrics"
+ @update="changeLinkServerMetrics"
+ />
+ </el-popover>
+ </div>
<Selector
class="inputs"
:multiple="true"
@@ -33,11 +53,29 @@ limitations under the License. -->
:options="states.linkMetricList"
size="small"
placeholder="Select metrics"
- @change="changeLinkServerMetrics"
+ @change="updateLinkServerMetrics"
/>
<span v-show="dashboardStore.entity !== EntityType[2].value">
<div class="label">
- {{ t("linkClientMetrics") }}
+ <span>{{ t("linkClientMetrics") }}</span>
+ <el-popover
+ placement="left"
+ :width="400"
+ trigger="click"
+ effect="dark"
+ v-if="states.linkClientMetrics.length"
+ >
+ <template #reference>
+ <span @click="setConfigType('linkClientMetricConfig')">
+ <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
+ </span>
+ </template>
+ <Metrics
+ :type="configType"
+ :metrics="states.linkClientMetrics"
+ @update="changeLinkClientMetrics"
+ />
+ </el-popover>
</div>
<Selector
class="inputs"
@@ -46,7 +84,7 @@ limitations under the License. -->
:options="states.linkMetricList"
size="small"
placeholder="Select metrics"
- @change="changeLinkClientMetrics"
+ @change="updateLinkClientMetrics"
/>
</span>
</div>
@@ -100,7 +138,27 @@ limitations under the License. -->
/>
</span>
</div>
- <div class="label">{{ t("nodeMetrics") }}</div>
+ <div class="label">
+ <span>{{ t("nodeMetrics") }}</span>
+ <el-popover
+ placement="left"
+ :width="400"
+ trigger="click"
+ effect="dark"
+ v-if="states.nodeMetrics.length"
+ >
+ <template #reference>
+ <span @click="setConfigType('nodeMetricConfig')">
+ <Icon class="cp ml-5" iconName="mode_edit" size="middle" />
+ </span>
+ </template>
+ <Metrics
+ :type="configType"
+ :metrics="states.nodeMetrics"
+ @update="changeNodeMetrics"
+ />
+ </el-popover>
+ </div>
<Selector
class="inputs"
:multiple="true"
@@ -108,7 +166,7 @@ limitations under the License. -->
:options="states.nodeMetricList"
size="small"
placeholder="Select metrics"
- @change="changeNodeMetrics"
+ @change="updateNodeMetrics"
/>
</div>
<div class="legend-settings" v-show="isService">
@@ -157,15 +215,6 @@ limitations under the License. -->
</span>
<div v-show="index !== legend.metric.length - 1">&&</div>
</div>
- <!-- <div class="label">{{ t("conditions") }}</div>
- <Selector
- class="inputs"
- :value="legend.condition"
- :options="LegendConditions"
- size="small"
- placeholder="Select a condition"
- @change="changeCondition"
- /> -->
<el-button
@click="setLegend"
class="legend-btn"
@@ -177,7 +226,7 @@ limitations under the License. -->
</div>
</template>
<script lang="ts" setup>
-import { reactive } from "vue";
+import { reactive, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useTopologyStore } from "@/store/modules/topology";
@@ -186,8 +235,9 @@ import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
import { Option } from "@/types/app";
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
import { Node } from "@/types/topology";
-import { DashboardItem } from "@/types/dashboard";
+import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import { EntityType, LegendOpt, MetricsType } from "../../../data";
+import Metrics from "./Metrics.vue";
/*global defineEmits */
const emit = defineEmits(["update", "updateNodes"]);
@@ -238,6 +288,7 @@ const legend = reactive<{
}>({
metric: l ? selectedGrid.legend : [{ name: "", condition: "", value: "" }],
});
+const configType = ref<string>("");
getMetricList();
async function getMetricList() {
@@ -347,11 +398,12 @@ function deleteItem(index: number) {
items.splice(index, 1);
updateSettings();
}
-function updateSettings() {
+function updateSettings(metricConfig?: { [key: string]: MetricConfigOpt[] }) {
const metrics = legend.metric.filter(
(d: any) => d.name && d.value && d.condition
);
const param = {
+ ...dashboardStore.selectedGrid,
linkDashboard: states.linkDashboard,
nodeDashboard: isService
? items.filter((d: { scope: string; dashboard: string }) => d.dashboard)
@@ -360,32 +412,76 @@ function updateSettings() {
linkClientMetrics: states.linkClientMetrics,
nodeMetrics: states.nodeMetrics,
legend: metrics,
+ ...metricConfig,
};
- dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, ...param });
- dashboardStore.setConfigs({ ...dashboardStore.selectedGrid, ...param });
+ dashboardStore.selectWidget(param);
+ dashboardStore.setConfigs(param);
emit("update", param);
}
-async function changeLinkServerMetrics(options: Option[] | any) {
- states.linkServerMetrics = options.map((d: Option) => d.value);
- updateSettings();
+function updateLinkServerMetrics(options: Option[] | any) {
+ const opt = options.map((d: Option) => d.value);
+ const index = states.linkServerMetrics.findIndex(
+ (d: any) => !opt.includes(d)
+ );
+ states.linkServerMetrics = opt;
+ if (index < 0) {
+ changeLinkServerMetrics();
+ return;
+ }
+ const origin = dashboardStore.selectedGrid.linkServerMetricConfig || [];
+ const config = origin.length === 1 ? [] : origin.splice(index, 1);
+ changeLinkServerMetrics({ linkServerMetricConfig: config });
+}
+async function changeLinkServerMetrics(config?: {
+ [key: string]: MetricConfigOpt[];
+}) {
+ updateSettings(config);
if (!states.linkServerMetrics.length) {
topologyStore.setLinkServerMetrics({});
return;
}
topologyStore.getLinkServerMetrics(states.linkServerMetrics);
}
-async function changeLinkClientMetrics(options: Option[] | any) {
- states.linkClientMetrics = options.map((d: Option) => d.value);
- updateSettings();
+function updateLinkClientMetrics(options: Option[] | any) {
+ const opt = options.map((d: Option) => d.value);
+ const index = states.linkClientMetrics.findIndex(
+ (d: any) => !opt.includes(d)
+ );
+ states.linkClientMetrics = opt;
+ if (index < 0) {
+ changeLinkClientMetrics();
+ return;
+ }
+ const origin = dashboardStore.selectedGrid.linkClientMetricConfig || [];
+ const config = origin.length === 1 ? [] : origin.splice(index, 1);
+ changeLinkClientMetrics({ linkClientMetricConfig: config });
+}
+async function changeLinkClientMetrics(config?: {
+ [key: string]: MetricConfigOpt[];
+}) {
+ updateSettings(config);
if (!states.linkClientMetrics.length) {
topologyStore.setLinkClientMetrics({});
return;
}
topologyStore.getLinkClientMetrics(states.linkClientMetrics);
}
-async function changeNodeMetrics(options: Option[] | any) {
- states.nodeMetrics = options.map((d: Option) => d.value);
- updateSettings();
+function updateNodeMetrics(options: Option[] | any) {
+ const opt = options.map((d: Option) => d.value);
+ const index = states.nodeMetrics.findIndex((d: any) => !opt.includes(d));
+ states.nodeMetrics = opt;
+ if (index < 0) {
+ changeNodeMetrics();
+ return;
+ }
+ const origin = dashboardStore.selectedGrid.nodeMetricConfig || [];
+ const config = origin.length === 1 ? [] : origin.splice(index, 1);
+ changeNodeMetrics({ nodeMetricConfig: config });
+}
+async function changeNodeMetrics(config?: {
+ [key: string]: MetricConfigOpt[];
+}) {
+ updateSettings(config);
if (!states.nodeMetrics.length) {
topologyStore.setNodeMetricValue({});
return;
@@ -402,6 +498,9 @@ function deleteMetric(index: number) {
function addMetric() {
legend.metric.push({ name: "", condition: "", value: "" });
}
+function setConfigType(type: string) {
+ configType.value = type;
+}
</script>
<style lang="scss" scoped>
.link-settings {