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 b37d65e feat: enhance the legend of metrics graph widget with the
summary table (#181)
b37d65e is described below
commit b37d65eaace09f19b196be71c52ebb43d4e226ba
Author: Fine0830 <[email protected]>
AuthorDate: Thu Nov 10 14:55:19 2022 +0800
feat: enhance the legend of metrics graph widget with the summary table
(#181)
---
src/assets/icons/circle.svg | 15 ++
src/components/Graph.vue | 1 +
src/hooks/useLegendProcessor.ts | 142 +++++++++++++++++
.../{useProcessor.ts => useMetricsProcessor.ts} | 2 +-
src/locales/lang/en.ts | 18 ++-
src/locales/lang/es.ts | 8 +
src/locales/lang/zh.ts | 8 +
src/store/modules/topology.ts | 2 +-
src/styles/lib.scss | 2 +-
src/styles/reset.scss | 4 +
src/types/dashboard.d.ts | 12 ++
src/views/dashboard/configuration/Widget.vue | 1 +
.../configuration/widget/graph-styles/Area.vue | 2 +
.../configuration/widget/graph-styles/Bar.vue | 2 +
.../configuration/widget/graph-styles/Line.vue | 6 +-
.../widget/graph-styles/components/Legend.vue | 138 ++++++++++++++++
.../configuration/widget/metric/Index.vue | 2 +-
.../configuration/widget/metric/Standard.vue | 2 +-
src/views/dashboard/controls/Widget.vue | 2 +-
src/views/dashboard/graphs/Area.vue | 3 +-
src/views/dashboard/graphs/Bar.vue | 50 +++---
src/views/dashboard/graphs/EndpointList.vue | 5 +-
src/views/dashboard/graphs/InstanceList.vue | 5 +-
src/views/dashboard/graphs/Line.vue | 67 ++++----
src/views/dashboard/graphs/ServiceList.vue | 5 +-
src/views/dashboard/graphs/components/Legend.vue | 175 +++++++++++++++++++++
.../related/topology/components/Graph.vue | 4 +-
.../related/topology/components/Sankey.vue | 2 +-
.../related/topology/components/Settings.vue | 2 +-
29 files changed, 595 insertions(+), 92 deletions(-)
diff --git a/src/assets/icons/circle.svg b/src/assets/icons/circle.svg
new file mode 100644
index 0000000..f2584ff
--- /dev/null
+++ b/src/assets/icons/circle.svg
@@ -0,0 +1,15 @@
+<!-- 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. -->
+<svg t="1667899293763" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="4705" width="16" height="16"><path
d="M512 512m-368 0a368 368 0 1 0 736 0 368 368 0 1 0-736 0Z"
p-id="4706"></path></svg>
\ No newline at end of file
diff --git a/src/components/Graph.vue b/src/components/Graph.vue
index 5bc8974..4a6523e 100644
--- a/src/components/Graph.vue
+++ b/src/components/Graph.vue
@@ -215,6 +215,7 @@ onBeforeUnmount(() => {
.chart {
overflow: hidden;
+ flex: 1;
}
.menus {
diff --git a/src/hooks/useLegendProcessor.ts b/src/hooks/useLegendProcessor.ts
new file mode 100644
index 0000000..ba6413d
--- /dev/null
+++ b/src/hooks/useLegendProcessor.ts
@@ -0,0 +1,142 @@
+/**
+ * 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 { LegendOptions } from "@/types/dashboard";
+import { isDef } from "@/utils/is";
+
+export default function useLegendProcess(legend?: LegendOptions) {
+ let isRight = false;
+ if (legend && legend.toTheRight) {
+ isRight = true;
+ }
+ function showEchartsLegend(keys: string[]) {
+ if (legend && isDef(legend.show)) {
+ if (legend.asTable && legend.show) {
+ return false;
+ }
+ return legend.show;
+ }
+ if (keys.length === 1) {
+ return false;
+ }
+ if (legend && legend.asTable) {
+ return false;
+ }
+ return true;
+ }
+ function aggregations(
+ data: { [key: string]: number[] },
+ intervalTime: string[]
+ ) {
+ const source: { [key: string]: unknown }[] = [];
+ const keys = Object.keys(data || {}).filter(
+ (i: any) => Array.isArray(data[i]) && data[i].length
+ );
+ const headers = [];
+
+ for (const [key, value] of keys.entries()) {
+ const arr = JSON.parse(JSON.stringify(data[value]));
+ const item: { [key: string]: unknown } = {
+ name: value,
+ topN: arr
+ .map((d: number, index: number) => {
+ return {
+ key: intervalTime[index],
+ value: d,
+ };
+ })
+ .sort(
+ (
+ a: { key: string; value: number },
+ b: { key: string; value: number }
+ ) => b.value - a.value
+ )
+ .filter((_: unknown, index: number) => index < 10),
+ };
+ if (legend) {
+ if (legend.min) {
+ item.min = Math.min(...data[value]).toFixed(2);
+ if (key === 0) {
+ headers.push({ value: "min", label: "Min" });
+ }
+ }
+ if (legend.max) {
+ item.max = Math.max(...data[value]).toFixed(2);
+ if (key === 0) {
+ headers.push({ value: "max", label: "Max" });
+ }
+ }
+ if (legend.mean) {
+ const total = data[value].reduce((prev: number, next: number) => {
+ prev += Number(next);
+ return prev;
+ }, 0);
+ item.mean = (total / data[value].length).toFixed(4);
+ if (key === 0) {
+ headers.push({ value: "mean", label: "Mean" });
+ }
+ }
+ if (legend.total) {
+ item.total = data[value]
+ .reduce((prev: number, next: number) => {
+ prev += Number(next);
+ return prev;
+ }, 0)
+ .toFixed(2);
+ if (key === 0) {
+ headers.push({ value: "total", label: "Total" });
+ }
+ }
+ }
+ source.push(item);
+ }
+
+ return { source, headers };
+ }
+ function chartColors(keys: string[]) {
+ let color: string[] = [];
+ switch (keys.length) {
+ case 2:
+ color = ["#FF6A84", "#a0b1e6"];
+ break;
+ case 1:
+ color = ["#3f96e3"];
+ break;
+ default:
+ color = [
+ "#30A4EB",
+ "#45BFC0",
+ "#FFCC55",
+ "#FF6A84",
+ "#a0a7e6",
+ "#c23531",
+ "#2f4554",
+ "#61a0a8",
+ "#d48265",
+ "#91c7ae",
+ "#749f83",
+ "#ca8622",
+ "#bda29a",
+ "#6e7074",
+ "#546570",
+ "#c4ccd3",
+ ];
+ break;
+ }
+ return color;
+ }
+ return { showEchartsLegend, isRight, aggregations, chartColors };
+}
diff --git a/src/hooks/useProcessor.ts b/src/hooks/useMetricsProcessor.ts
similarity index 99%
rename from src/hooks/useProcessor.ts
rename to src/hooks/useMetricsProcessor.ts
index 97b22e4..54dc3a1 100644
--- a/src/hooks/useProcessor.ts
+++ b/src/hooks/useMetricsProcessor.ts
@@ -184,7 +184,7 @@ export function useSourceProcessor(
const c = (config.metricConfig && config.metricConfig[index]) || {};
if (type === MetricQueryTypes.ReadMetricsValues) {
- source[m] =
+ source[c.label || m] =
(resp.data[keys[index]] &&
calculateExp(resp.data[keys[index]].values.values, c)) ||
[];
diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index 0171ef0..7609aa0 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -52,17 +52,19 @@ const msg = {
instance: "Instance",
create: "Create",
loading: "Loading",
- selectVisualization: "Visualize your metrics",
+ selectVisualization: "Visualize Metrics",
visualization: "Visualization",
- graphStyles: "Graph styles",
- widgetOptions: "Widget options",
- standardOptions: "Standard options",
+ graphStyles: "Graph Styles",
+ widgetOptions: "Widget Options",
+ standardOptions: "Standard Options",
max: "Max",
min: "Min",
plus: "Plus",
+ mean: "Mean",
minus: "Minus",
multiply: "Multiply",
divide: "Divide",
+ total: "Total",
convertToMilliseconds: "Convert Unix Timestamp(milliseconds)",
convertToSeconds: "Convert Unix Timestamp(seconds)",
smooth: "Smooth",
@@ -167,6 +169,11 @@ const msg = {
enableRelatedTrace: "Enable Related Trace",
maxTraceDuration: "Maximum Duration",
minTraceDuration: "Minimum Duration",
+ legendOptions: "Legend Options",
+ showLegend: "Show Legend",
+ asTable: "As Table",
+ toTheRight: "To The Right",
+ legendValues: "Legend Values",
seconds: "Seconds",
hourTip: "Select Hour",
minuteTip: "Select Minute",
@@ -259,7 +266,7 @@ const msg = {
entityType: "Entity Type",
maxItemNum: "Max number of Item",
unknownMetrics: "Unknown Metrics",
- labels: "Labels",
+ labels: "Label",
aggregation: "Calculation",
unit: "Unit",
labelsIndex: "Label Subscript",
@@ -320,6 +327,7 @@ const msg = {
eventsParameters: "Event Parameters",
eventDetail: "Event Detail",
value: "Value",
+ key: "Key",
show: "Show",
hide: "Hide",
statistics: "Statistics",
diff --git a/src/locales/lang/es.ts b/src/locales/lang/es.ts
index 66fb561..cb743e6 100644
--- a/src/locales/lang/es.ts
+++ b/src/locales/lang/es.ts
@@ -59,9 +59,11 @@ const msg = {
standardOptions: "Opciones estandar",
max: "Máx",
min: "Mín",
+ mean: "Promedio",
plus: "Más",
minus: "Menoss",
multiply: "Multiplcar",
+ total: "Todo",
divide: "Dividir",
convertToMilliseconds: "Convertir Unix Timestamp(milisegundos)",
convertToSeconds: "Convertir Unix Timestamp(segundos)",
@@ -160,6 +162,7 @@ const msg = {
queryOrder: "Orden de consulta",
latency: "Retraso",
metricValues: "Valor métrico",
+ legendValues: "Valor de la leyenda",
seconds: "Segundos",
hourTip: "Seleccione Hora",
minuteTip: "Seleccione Minuto",
@@ -171,6 +174,10 @@ const msg = {
queryConditions: "Condiciones de consulta",
maxTraceDuration: "Duración máxima",
minTraceDuration: "Duración mínima",
+ legendOptions: "Opciones de leyenda",
+ showLegend: "Mostrar leyenda",
+ asTable: "Como tabla",
+ toTheRight: "Derecha",
second: "s",
yearSuffix: "Año",
monthsHead: "Ene_Feb_Mar_Abr_May_Jun_Jul_Ago_Set_Oct_Nov_Dic",
@@ -320,6 +327,7 @@ const msg = {
eventsParameters: "Parámetro del Evento",
eventDetail: "Detalle del Evento",
value: "Valor",
+ key: "Clave",
show: "Mostrar",
hide: "Oculatr",
statistics: "Estadísticas",
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index 141e60d..aacde22 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -56,10 +56,12 @@ const msg = {
standardOptions: "标准选项",
max: "最大值",
min: "最小值",
+ mean: "平均值",
plus: "加法",
minus: "减法",
multiply: "乘法",
divide: "除法",
+ total: "总计",
convertToMilliseconds: "转换Unix时间戳(毫秒)",
convertToSeconds: "转换Unix时间戳(秒)",
smooth: "光滑的",
@@ -164,6 +166,11 @@ const msg = {
queryConditions: "查询条件",
maxTraceDuration: "最大持续时间",
minTraceDuration: "最小持续时间",
+ legendOptions: "图例选项",
+ showLegend: "显示图例",
+ asTable: "作为表格",
+ toTheRight: "在右边",
+ legendValues: "图例值",
seconds: "秒",
hourTip: "选择小时",
minuteTip: "选择分钟",
@@ -318,6 +325,7 @@ const msg = {
eventsParameters: "事件参数",
eventDetail: "事件详情",
value: "数值",
+ key: "Key",
tableHeader: "表头名称",
tableValues: "表值",
show: "展示",
diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts
index c72a452..8b2205e 100644
--- a/src/store/modules/topology.ts
+++ b/src/store/modules/topology.ts
@@ -23,7 +23,7 @@ import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { AxiosResponse } from "axios";
import query from "@/graphql/fetch";
-import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
+import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
import { ElMessage } from "element-plus";
interface MetricVal {
diff --git a/src/styles/lib.scss b/src/styles/lib.scss
index 1d2a508..f991ab4 100644
--- a/src/styles/lib.scss
+++ b/src/styles/lib.scss
@@ -173,7 +173,7 @@
}
.scroll_bar_style::-webkit-scrollbar {
- width: 9px;
+ width: 4px;
height: 4px;
background-color: #eee;
}
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
index 06af9ce..41ef283 100644
--- a/src/styles/reset.scss
+++ b/src/styles/reset.scss
@@ -153,6 +153,10 @@ pre {
margin-left: 5px;
}
+.el-switch__label * {
+ font-size: 12px;
+}
+
.el-drawer__header {
margin-bottom: 0;
}
diff --git a/src/types/dashboard.d.ts b/src/types/dashboard.d.ts
index ceabab3..a74d081 100644
--- a/src/types/dashboard.d.ts
+++ b/src/types/dashboard.d.ts
@@ -95,6 +95,7 @@ export type GraphConfig =
export interface BarConfig {
type?: string;
showBackground?: boolean;
+ legend?: LegendOptions;
}
export interface LineConfig extends AreaConfig {
type?: string;
@@ -110,6 +111,7 @@ export interface LineConfig extends AreaConfig {
export interface AreaConfig {
type?: string;
opacity?: number;
+ legend?: LegendOptions;
}
export interface CardConfig {
@@ -180,3 +182,13 @@ export type EventParams = {
value: number | number[];
color: string;
};
+export type LegendOptions = {
+ show: boolean;
+ total: boolean;
+ min: boolean;
+ max: boolean;
+ mean: boolean;
+ asTable: boolean;
+ toTheRight: boolean;
+ width: number;
+};
diff --git a/src/views/dashboard/configuration/Widget.vue
b/src/views/dashboard/configuration/Widget.vue
index 4582b70..c09a4c4 100644
--- a/src/views/dashboard/configuration/Widget.vue
+++ b/src/views/dashboard/configuration/Widget.vue
@@ -32,6 +32,7 @@ limitations under the License. -->
:data="states.source"
:config="{
...graph,
+ legend: (dashboardStore.selectedGrid.graph || {}).legend,
i: dashboardStore.selectedGrid.i,
metrics: dashboardStore.selectedGrid.metrics,
metricTypes: dashboardStore.selectedGrid.metricTypes,
diff --git a/src/views/dashboard/configuration/widget/graph-styles/Area.vue
b/src/views/dashboard/configuration/widget/graph-styles/Area.vue
index 9c4b3e0..620c803 100644
--- a/src/views/dashboard/configuration/widget/graph-styles/Area.vue
+++ b/src/views/dashboard/configuration/widget/graph-styles/Area.vue
@@ -13,6 +13,7 @@ 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>
+ <Legend />
<div>
<span class="label">{{ t("areaOpacity") }}</span>
<el-slider
@@ -31,6 +32,7 @@ limitations under the License. -->
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
+import Legend from "./components/Legend.vue";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
diff --git a/src/views/dashboard/configuration/widget/graph-styles/Bar.vue
b/src/views/dashboard/configuration/widget/graph-styles/Bar.vue
index 0cffd9e..e526847 100644
--- a/src/views/dashboard/configuration/widget/graph-styles/Bar.vue
+++ b/src/views/dashboard/configuration/widget/graph-styles/Bar.vue
@@ -13,6 +13,7 @@ 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>
+ <Legend />
<div>
<span class="label">{{ t("showBackground") }}</span>
<el-switch
@@ -27,6 +28,7 @@ limitations under the License. -->
import { ref } from "vue";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useI18n } from "vue-i18n";
+import Legend from "./components/Legend.vue";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
diff --git a/src/views/dashboard/configuration/widget/graph-styles/Line.vue
b/src/views/dashboard/configuration/widget/graph-styles/Line.vue
index 66b7c78..de05726 100644
--- a/src/views/dashboard/configuration/widget/graph-styles/Line.vue
+++ b/src/views/dashboard/configuration/widget/graph-styles/Line.vue
@@ -13,6 +13,7 @@ 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>
+ <Legend />
<div>
<span class="label">{{ t("showXAxis") }}</span>
<el-switch
@@ -63,6 +64,7 @@ limitations under the License. -->
import { ref, computed } from "vue";
import { useI18n } from "vue-i18n";
import { useDashboardStore } from "@/store/modules/dashboard";
+import Legend from "./components/Legend.vue";
const { t } = useI18n();
const dashboardStore = useDashboardStore();
@@ -82,8 +84,8 @@ function updateConfig(param: { [key: string]: unknown }) {
<style lang="scss" scoped>
.label {
font-size: 13px;
- font-weight: 500;
display: block;
- margin-bottom: 5px;
+ margin-top: 5px;
+ margin-bottom: -5px;
}
</style>
diff --git
a/src/views/dashboard/configuration/widget/graph-styles/components/Legend.vue
b/src/views/dashboard/configuration/widget/graph-styles/components/Legend.vue
new file mode 100644
index 0000000..9efea04
--- /dev/null
+++
b/src/views/dashboard/configuration/widget/graph-styles/components/Legend.vue
@@ -0,0 +1,138 @@
+<!-- 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>
+ <span class="label mr-5">{{ t("showLegend") }}</span>
+ <el-switch
+ v-model="legend.show"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ show: legend.show })"
+ />
+ </div>
+ <div>
+ <span class="label">{{ t("asTable") }}</span>
+ <el-switch
+ v-model="legend.asTable"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ asTable: legend.asTable })"
+ />
+ </div>
+ <div v-show="legend.asTable">
+ <span class="label">{{ t("legendOptions") }}</span>
+ <span class="title mr-5">{{ t("toTheRight") }}</span>
+ <el-switch
+ v-model="legend.toTheRight"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ toTheRight: legend.toTheRight })"
+ />
+ <span class="title ml-20 mr-5">{{ t("width") }}</span>
+ <el-input
+ v-model="legend.width"
+ class="inputs"
+ size="small"
+ placeholder="Please input the width"
+ @change="updateLegendConfig({ width: legend.width })"
+ />
+ </div>
+ <div v-show="legend.asTable">
+ <span class="label">{{ t("legendValues") }}</span>
+ <span class="title mr-5">{{ t("min") }}</span>
+ <el-switch
+ v-model="legend.min"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ min: legend.min })"
+ />
+ <span class="title ml-20 mr-5">{{ t("max") }}</span>
+ <el-switch
+ v-model="legend.max"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ max: legend.max })"
+ />
+ <span class="title ml-20 mr-5">{{ t("mean") }}</span>
+ <el-switch
+ v-model="legend.mean"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ mean: legend.mean })"
+ />
+ <span class="title ml-20 mr-5">{{ t("total") }}</span>
+ <el-switch
+ v-model="legend.total"
+ active-text="Yes"
+ inactive-text="No"
+ @change="updateLegendConfig({ total: legend.total })"
+ />
+ </div>
+</template>
+<script lang="ts" setup>
+import { computed, reactive } from "vue";
+import { useI18n } from "vue-i18n";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import { LegendOptions } from "@/types/dashboard";
+
+const { t } = useI18n();
+const dashboardStore = useDashboardStore();
+const graph = computed(() => dashboardStore.selectedGrid.graph || {});
+const legend = reactive<LegendOptions>({
+ show: true,
+ total: false,
+ min: false,
+ max: false,
+ mean: false,
+ asTable: false,
+ toTheRight: false,
+ width: 130,
+ ...graph.value.legend,
+});
+
+function updateLegendConfig(param: { [key: string]: unknown }) {
+ const g = {
+ ...dashboardStore.selectedGrid.graph,
+ legend: {
+ ...dashboardStore.selectedGrid.graph.legend,
+ ...param,
+ },
+ };
+ dashboardStore.selectWidget({
+ ...dashboardStore.selectedGrid,
+ graph: g,
+ });
+}
+</script>
+<style lang="scss" scoped>
+.label {
+ font-size: 13px;
+ display: block;
+ margin-top: 5px;
+ margin-bottom: -5px;
+}
+
+.title {
+ font-size: 12px;
+ display: inline-flex;
+ height: 32px;
+ line-height: 34px;
+ vertical-align: middle;
+}
+
+.inputs {
+ width: 120px;
+}
+</style>
diff --git a/src/views/dashboard/configuration/widget/metric/Index.vue
b/src/views/dashboard/configuration/widget/metric/Index.vue
index cdb31f0..6c66f05 100644
--- a/src/views/dashboard/configuration/widget/metric/Index.vue
+++ b/src/views/dashboard/configuration/widget/metric/Index.vue
@@ -113,7 +113,7 @@ import {
useQueryProcessor,
useSourceProcessor,
useGetMetricEntity,
-} from "@/hooks/useProcessor";
+} from "@/hooks/useMetricsProcessor";
import { useI18n } from "vue-i18n";
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import Standard from "./Standard.vue";
diff --git a/src/views/dashboard/configuration/widget/metric/Standard.vue
b/src/views/dashboard/configuration/widget/metric/Standard.vue
index 1ba282c..213baf4 100644
--- a/src/views/dashboard/configuration/widget/metric/Standard.vue
+++ b/src/views/dashboard/configuration/widget/metric/Standard.vue
@@ -122,7 +122,7 @@ const hasLabel = computed(() => {
const graph = dashboardStore.selectedGrid.graph || {};
return (
ListChartTypes.includes(graph.type) ||
- metricType.value === "readLabeledMetricsValues"
+ ["readLabeledMetricsValues",
"readMetricsValues"].includes(metricType.value)
);
});
const isTopn = computed(() =>
diff --git a/src/views/dashboard/controls/Widget.vue
b/src/views/dashboard/controls/Widget.vue
index 8593d43..cccd42f 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -86,7 +86,7 @@ import {
useQueryProcessor,
useSourceProcessor,
useGetMetricEntity,
-} from "@/hooks/useProcessor";
+} from "@/hooks/useMetricsProcessor";
import { EntityType, ListChartTypes } from "../data";
import { EventParams } from "@/types/dashboard";
import getDashboard from "@/hooks/useDashboardsSession";
diff --git a/src/views/dashboard/graphs/Area.vue
b/src/views/dashboard/graphs/Area.vue
index 46927f9..5e4336c 100644
--- a/src/views/dashboard/graphs/Area.vue
+++ b/src/views/dashboard/graphs/Area.vue
@@ -44,7 +44,8 @@ defineProps({
AreaConfig & {
filters: Filters;
relatedTrace: RelatedTrace;
- } & { id: string }
+ id: string;
+ }
>,
default: () => ({}),
},
diff --git a/src/views/dashboard/graphs/Bar.vue
b/src/views/dashboard/graphs/Bar.vue
index b236d28..ad52e48 100644
--- a/src/views/dashboard/graphs/Bar.vue
+++ b/src/views/dashboard/graphs/Bar.vue
@@ -13,7 +13,10 @@ 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>
- <Graph :option="option" @select="clickEvent" :filters="config.filters" />
+ <div class="graph" :class="isRight ? 'flex-h' : 'flex-v'">
+ <Graph :option="option" @select="clickEvent" :filters="config.filters" />
+ <Legend :config="config.legend" :data="data" :intervalTime="intervalTime"
/>
+ </div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
@@ -24,6 +27,7 @@ import {
RelatedTrace,
Filters,
} from "@/types/dashboard";
+import useLegendProcess from "@/hooks/useLegendProcessor";
/*global defineProps, defineEmits */
const emits = defineEmits(["click"]);
@@ -39,11 +43,15 @@ const props = defineProps({
BarConfig & {
filters: Filters;
relatedTrace: RelatedTrace;
- } & { id: string }
+ id: string;
+ }
>,
default: () => ({}),
},
});
+const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
+ props.config.legend
+);
const option = computed(() => getOption());
function getOption() {
@@ -73,35 +81,7 @@ function getOption() {
},
};
});
- let color: string[] = [];
- switch (keys.length) {
- case 2:
- color = ["#FF6A84", "#a0b1e6"];
- break;
- case 1:
- color = ["#3f96e3"];
- break;
- default:
- color = [
- "#30A4EB",
- "#45BFC0",
- "#FFCC55",
- "#FF6A84",
- "#a0a7e6",
- "#c23531",
- "#2f4554",
- "#61a0a8",
- "#d48265",
- "#91c7ae",
- "#749f83",
- "#ca8622",
- "#bda29a",
- "#6e7074",
- "#546570",
- "#c4ccd3",
- ];
- break;
- }
+ const color: string[] = chartColors(keys);
return {
color,
tooltip: {
@@ -114,7 +94,7 @@ function getOption() {
},
legend: {
type: "scroll",
- show: keys.length === 1 ? false : true,
+ show: showEchartsLegend(keys),
icon: "circle",
top: 0,
left: 0,
@@ -160,3 +140,9 @@ function clickEvent(params: EventParams) {
emits("click", params);
}
</script>
+<style lang="scss" scoped>
+.graph {
+ width: 100%;
+ height: 100%;
+}
+</style>
diff --git a/src/views/dashboard/graphs/EndpointList.vue
b/src/views/dashboard/graphs/EndpointList.vue
index 138865c..8209ee6 100644
--- a/src/views/dashboard/graphs/EndpointList.vue
+++ b/src/views/dashboard/graphs/EndpointList.vue
@@ -66,7 +66,10 @@ import type { PropType } from "vue";
import { EndpointListConfig } from "@/types/dashboard";
import { Endpoint } from "@/types/selector";
import { useDashboardStore } from "@/store/modules/dashboard";
-import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
+import {
+ useQueryPodsMetrics,
+ usePodsSource,
+} from "@/hooks/useMetricsProcessor";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
diff --git a/src/views/dashboard/graphs/InstanceList.vue
b/src/views/dashboard/graphs/InstanceList.vue
index 2963102..ef171dd 100644
--- a/src/views/dashboard/graphs/InstanceList.vue
+++ b/src/views/dashboard/graphs/InstanceList.vue
@@ -95,7 +95,10 @@ import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { InstanceListConfig } from "@/types/dashboard";
import { Instance } from "@/types/selector";
-import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
+import {
+ useQueryPodsMetrics,
+ usePodsSource,
+} from "@/hooks/useMetricsProcessor";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
diff --git a/src/views/dashboard/graphs/Line.vue
b/src/views/dashboard/graphs/Line.vue
index ae27ff7..c26c6b1 100644
--- a/src/views/dashboard/graphs/Line.vue
+++ b/src/views/dashboard/graphs/Line.vue
@@ -13,15 +13,18 @@ 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>
- <Graph
- :option="option"
- @select="clickEvent"
- :filters="config.filters"
- :relatedTrace="config.relatedTrace"
- />
+ <div class="graph flex-v" :class="setRight ? 'flex-h' : 'flex-v'">
+ <Graph
+ :option="option"
+ @select="clickEvent"
+ :filters="config.filters"
+ :relatedTrace="config.relatedTrace"
+ />
+ <Legend :config="config.legend" :data="data" :intervalTime="intervalTime"
/>
+ </div>
</template>
<script lang="ts" setup>
-import { computed } from "vue";
+import { computed, ref } from "vue";
import type { PropType } from "vue";
import {
LineConfig,
@@ -29,6 +32,8 @@ import {
RelatedTrace,
Filters,
} from "@/types/dashboard";
+import Legend from "./components/Legend.vue";
+import useLegendProcess from "@/hooks/useLegendProcessor";
/*global defineProps, defineEmits */
const emits = defineEmits(["click"]);
@@ -44,7 +49,8 @@ const props = defineProps({
LineConfig & {
filters?: Filters;
relatedTrace?: RelatedTrace;
- } & { id?: string }
+ id?: string;
+ }
>,
default: () => ({
step: false,
@@ -58,8 +64,13 @@ const props = defineProps({
}),
},
});
+const setRight = ref<boolean>(false);
const option = computed(() => getOption());
function getOption() {
+ const { showEchartsLegend, isRight, chartColors } = useLegendProcess(
+ props.config.legend
+ );
+ setRight.value = isRight;
const keys = Object.keys(props.data || {}).filter(
(i: any) => Array.isArray(props.data[i]) && props.data[i].length
);
@@ -88,35 +99,7 @@ function getOption() {
}
return serie;
});
- let color: string[] = [];
- switch (keys.length) {
- case 2:
- color = ["#FF6A84", "#a0b1e6"];
- break;
- case 1:
- color = ["#3f96e3"];
- break;
- default:
- color = [
- "#30A4EB",
- "#45BFC0",
- "#FFCC55",
- "#FF6A84",
- "#a0a7e6",
- "#c23531",
- "#2f4554",
- "#61a0a8",
- "#d48265",
- "#91c7ae",
- "#749f83",
- "#ca8622",
- "#bda29a",
- "#6e7074",
- "#546570",
- "#c4ccd3",
- ];
- break;
- }
+ const color: string[] = chartColors(keys);
const tooltip = {
trigger: "none",
axisPointer: {
@@ -151,7 +134,7 @@ function getOption() {
tooltip: props.config.smallTips ? tips : tooltip,
legend: {
type: "scroll",
- show: keys.length === 1 ? false : true,
+ show: showEchartsLegend(keys),
icon: "circle",
top: 0,
left: 0,
@@ -167,7 +150,7 @@ function getOption() {
},
},
grid: {
- top: keys.length === 1 ? 15 : 55,
+ top: showEchartsLegend(keys) ? 35 : 10,
left: 0,
right: 10,
bottom: 5,
@@ -205,3 +188,9 @@ function clickEvent(params: EventParams) {
emits("click", params);
}
</script>
+<style lang="scss" scoped>
+.graph {
+ width: 100%;
+ height: 100%;
+}
+</style>
diff --git a/src/views/dashboard/graphs/ServiceList.vue
b/src/views/dashboard/graphs/ServiceList.vue
index 3a59762..4409181 100644
--- a/src/views/dashboard/graphs/ServiceList.vue
+++ b/src/views/dashboard/graphs/ServiceList.vue
@@ -93,7 +93,10 @@ import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Service } from "@/types/selector";
-import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
+import {
+ useQueryPodsMetrics,
+ usePodsSource,
+} from "@/hooks/useMetricsProcessor";
import { EntityType } from "../data";
import router from "@/router";
import getDashboard from "@/hooks/useDashboardsSession";
diff --git a/src/views/dashboard/graphs/components/Legend.vue
b/src/views/dashboard/graphs/components/Legend.vue
new file mode 100644
index 0000000..b9839be
--- /dev/null
+++ b/src/views/dashboard/graphs/components/Legend.vue
@@ -0,0 +1,175 @@
+<!-- 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
+ v-if="tableData.length && config.asTable"
+ role="region"
+ aria-labelledby="caption"
+ tabindex="0"
+ :style="`width: ${width}; maxHeight:${isRight ? '100%' : 130}`"
+ class="scroll_bar_style"
+ >
+ <table>
+ <thead>
+ <tr>
+ <th></th>
+ <th v-for="h in headerRow" :key="h.value">
+ {{ h.label }}
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr v-for="(item, index) in tableData" :key="index">
+ <th>
+ <el-popover placement="bottom" :width="230" trigger="click">
+ <template #reference>
+ <div class="name">
+ <Icon iconName="circle" :style="`color: ${colors[index]};`"
/>
+ <i>{{ item.name }}</i>
+ </div>
+ </template>
+ <div class="list">
+ <div class="value">
+ <span>{{ t("key") }}</span>
+ <span>{{ t("value") }}</span>
+ </div>
+ <div class="value" v-for="(d, index) in item.topN"
:key="index">
+ <span>{{ d.key }}</span>
+ <span>{{ d.value }}</span>
+ </div>
+ </div>
+ </el-popover>
+ </th>
+ <td v-for="h in headerRow" :key="h.value">
+ {{ item[h.value] }}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</template>
+<script lang="ts" setup>
+import { computed } from "vue";
+import type { PropType } from "vue";
+import { useI18n } from "vue-i18n";
+import { LegendOptions } from "@/types/dashboard";
+import useLegendProcess from "@/hooks/useLegendProcessor";
+
+/*global defineProps */
+const props = defineProps({
+ data: {
+ type: Object as PropType<{ [key: string]: number[] }>,
+ default: () => ({}),
+ },
+ config: {
+ type: Object as PropType<LegendOptions>,
+ default: () => ({}),
+ },
+ intervalTime: { type: Array as PropType<string[]>, default: () => [] },
+});
+const { t } = useI18n();
+const tableData: any = computed(() => {
+ const { aggregations } = useLegendProcess(props.config);
+ return aggregations(props.data, props.intervalTime).source;
+});
+const headerRow = computed(() => {
+ const { aggregations } = useLegendProcess(props.config);
+ return aggregations(props.data, props.intervalTime).headers;
+});
+const isRight = computed(() => useLegendProcess(props.config).isRight);
+const width = computed(() =>
+ props.config.width
+ ? props.config.width + "px"
+ : isRight.value
+ ? "150px"
+ : "100%"
+);
+const colors = computed(() => {
+ const keys = Object.keys(props.data || {}).filter(
+ (i: any) => Array.isArray(props.data[i]) && props.data[i].length
+ );
+ const { chartColors } = useLegendProcess(props.config);
+ return chartColors(keys);
+});
+</script>
+<style lang="scss" scoped>
+table {
+ font-size: 12px;
+ white-space: nowrap;
+ margin: 0;
+ border: none;
+ border-collapse: separate;
+ border-spacing: 0;
+ table-layout: fixed;
+}
+
+table th {
+ padding: 5px;
+}
+
+table thead th {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ width: 25vw;
+ background: #fff;
+}
+
+.name {
+ cursor: pointer;
+}
+
+table td {
+ padding: 5px;
+ text-align: center;
+}
+
+table thead th:first-child {
+ position: sticky;
+ left: 0;
+ z-index: 2;
+}
+
+table tbody th {
+ font-weight: bold;
+ font-style: italic;
+ text-align: left;
+ background: #fff;
+ position: sticky;
+ left: 0;
+ z-index: 1;
+}
+
+[role="region"][aria-labelledby][tabindex] {
+ overflow: auto;
+}
+
+i {
+ font-style: normal;
+}
+
+.value {
+ span {
+ display: inline-block;
+ padding: 5px;
+ width: 80px;
+ }
+}
+
+.list {
+ height: 360px;
+ overflow: auto;
+}
+</style>
diff --git a/src/views/dashboard/related/topology/components/Graph.vue
b/src/views/dashboard/related/topology/components/Graph.vue
index 6a319b5..29e7ce9 100644
--- a/src/views/dashboard/related/topology/components/Graph.vue
+++ b/src/views/dashboard/related/topology/components/Graph.vue
@@ -111,9 +111,9 @@ 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";
+import { aggregation } from "@/hooks/useMetricsProcessor";
import icons from "@/assets/img/icons";
-import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
+import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
/*global Nullable, defineProps */
const props = defineProps({
diff --git a/src/views/dashboard/related/topology/components/Sankey.vue
b/src/views/dashboard/related/topology/components/Sankey.vue
index afa86d1..7c091fc 100644
--- a/src/views/dashboard/related/topology/components/Sankey.vue
+++ b/src/views/dashboard/related/topology/components/Sankey.vue
@@ -21,7 +21,7 @@ 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";
+import { aggregation } from "@/hooks/useMetricsProcessor";
/*global defineEmits, defineProps */
const props = defineProps({
diff --git a/src/views/dashboard/related/topology/components/Settings.vue
b/src/views/dashboard/related/topology/components/Settings.vue
index 0473510..9af3022 100644
--- a/src/views/dashboard/related/topology/components/Settings.vue
+++ b/src/views/dashboard/related/topology/components/Settings.vue
@@ -248,7 +248,7 @@ import { useTopologyStore } from "@/store/modules/topology";
import { ElMessage } from "element-plus";
import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
import { Option } from "@/types/app";
-import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
+import { useQueryTopologyMetrics } from "@/hooks/useMetricsProcessor";
import { Node } from "@/types/topology";
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
import { EntityType, LegendOpt, MetricsType } from "../../../data";