From c00d5d2a051ca9860124641543220128d434930c Mon Sep 17 00:00:00 2001 From: Fine0830 Date: Sat, 26 Mar 2022 22:52:43 +0800 Subject: [PATCH] feat: Implement custom configurations for metrics on dashboards and topology (#39) --- src/assets/icons/arrow_drop_down.svg | 17 ++ 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 ------------------ .../widget/graph-styles/Card.vue | 14 +- .../{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 ++++++++++++++++ .../topology/components/PodTopology.vue | 2 +- .../related/topology/components/Sankey.vue | 65 ++++-- .../related/topology/components/Settings.vue | 157 ++++++++++++--- 30 files changed, 965 insertions(+), 364 deletions(-) create mode 100644 src/assets/icons/arrow_drop_down.svg rename src/assets/icons/{createmode_editedit.svg => mode_edit.svg} (96%) create mode 100644 src/components/Select.vue delete mode 100644 src/views/dashboard/configuration/widget/StandardOptions.vue rename src/views/dashboard/configuration/widget/{MetricOptions.vue => metric/Index.vue} (87%) create mode 100644 src/views/dashboard/configuration/widget/metric/Standard.vue create mode 100644 src/views/dashboard/related/topology/components/Metrics.vue diff --git a/src/assets/icons/arrow_drop_down.svg b/src/assets/icons/arrow_drop_down.svg new file mode 100644 index 00000000..63151de2 --- /dev/null +++ b/src/assets/icons/arrow_drop_down.svg @@ -0,0 +1,17 @@ + + + + diff --git a/src/assets/icons/cancel.svg b/src/assets/icons/cancel.svg index 00ca7954..f52fb3dc 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. --> -cancel diff --git a/src/assets/icons/clearclose.svg b/src/assets/icons/clearclose.svg index af9e3a0a..ddc35190 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. --> -clearclose 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 1a1f2d8b..51f6cef8 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. --> -createmode_editedit diff --git a/src/components/Select.vue b/src/components/Select.vue new file mode 100644 index 00000000..0c101d56 --- /dev/null +++ b/src/components/Select.vue @@ -0,0 +1,177 @@ + + + + diff --git a/src/components/index.ts b/src/components/index.ts index 9a176148..0ce63ae1 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 8914aff6..5f1fdd88 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 98f1f762..f91f4515 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, 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 c11b198f..4bc27589 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 6c1d334a..43f09eb6 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 6fdfdd9b..eba3c3bb 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 62d12b9d..3a1fe7e1 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 268be91a..2e298154 100644 --- a/src/views/dashboard/configuration/Text.vue +++ b/src/views/dashboard/configuration/Text.vue @@ -29,6 +29,17 @@ limitations under the License. --> @change="changeConfig({ content })" /> +
+ {{ t("textAlign") }} + +
{{ t("backgroundColors") }} (originConfig.graph.content || ""); const fontSize = ref(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 442e8595..67f6949e 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. --> - - -
+
+ {{ t("showUnit") }} + +
diff --git a/src/views/dashboard/controls/Text.vue b/src/views/dashboard/controls/Text.vue index 60f77ede..72c65b83 100644 --- a/src/views/dashboard/controls/Text.vue +++ b/src/views/dashboard/controls/Text.vue @@ -36,18 +36,17 @@ limitations under the License. --> @@ -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"); -} diff --git a/src/views/dashboard/related/topology/components/PodTopology.vue b/src/views/dashboard/related/topology/components/PodTopology.vue index 8fc4df70..131221b8 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" > - +