feat: Implement custom configurations for metrics on dashboards and topology (#39)
17
src/assets/icons/arrow_drop_down.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!-- 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 version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M6.984 9.984h10.031l-5.016 5.016z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 922 B |
@ -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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<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>
|
<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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<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="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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<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>
|
<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>
|
</svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
177
src/components/Select.vue
Normal file
@ -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>
|
@ -19,6 +19,7 @@ import TimePicker from "./TimePicker.vue";
|
|||||||
import Selector from "./Selector.vue";
|
import Selector from "./Selector.vue";
|
||||||
import Graph from "./Graph.vue";
|
import Graph from "./Graph.vue";
|
||||||
import Radio from "./Radio.vue";
|
import Radio from "./Radio.vue";
|
||||||
|
import SelectSingle from "./Select.vue";
|
||||||
import type { App } from "vue";
|
import type { App } from "vue";
|
||||||
import VueGridLayout from "vue-grid-layout";
|
import VueGridLayout from "vue-grid-layout";
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ const components: { [key: string]: any } = {
|
|||||||
Selector,
|
Selector,
|
||||||
Graph,
|
Graph,
|
||||||
Radio,
|
Radio,
|
||||||
|
SelectSingle,
|
||||||
};
|
};
|
||||||
const componentsName: string[] = Object.keys(components);
|
const componentsName: string[] = Object.keys(components);
|
||||||
|
|
||||||
|
@ -22,6 +22,15 @@ export enum MetricQueryTypes {
|
|||||||
READHEATMAP = "readHeatMap",
|
READHEATMAP = "readHeatMap",
|
||||||
ReadSampledRecords = "readSampledRecords",
|
ReadSampledRecords = "readSampledRecords",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Calculations {
|
||||||
|
Percentage = "percentage",
|
||||||
|
ByteToKB = "byteToKB",
|
||||||
|
Apdex = "apdex",
|
||||||
|
Precision = "precision",
|
||||||
|
ConvertSeconds = "convertSeconds",
|
||||||
|
ConvertMilliseconds = "convertMilliseconds",
|
||||||
|
}
|
||||||
export enum sizeEnum {
|
export enum sizeEnum {
|
||||||
XS = "XS",
|
XS = "XS",
|
||||||
SM = "SM",
|
SM = "SM",
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { RespFields, MetricQueryTypes } from "./data";
|
import { RespFields, MetricQueryTypes, Calculations } from "./data";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { Instance, Endpoint, Service } from "@/types/selector";
|
import { Instance, Endpoint, Service } from "@/types/selector";
|
||||||
import { StandardConfig } from "@/types/dashboard";
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
|
||||||
export function useQueryProcessor(config: any) {
|
export function useQueryProcessor(config: any) {
|
||||||
if (!(config.metrics && config.metrics[0])) {
|
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 fragment = config.metrics.map((name: string, index: number) => {
|
||||||
const metricType = config.metricTypes[index] || "";
|
const metricType = config.metricTypes[index] || "";
|
||||||
|
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
MetricQueryTypes.ReadSampledRecords,
|
MetricQueryTypes.ReadSampledRecords,
|
||||||
@ -63,11 +64,11 @@ export function useQueryProcessor(config: any) {
|
|||||||
normal: selectorStore.currentService.normal,
|
normal: selectorStore.currentService.normal,
|
||||||
scope: dashboardStore.entity,
|
scope: dashboardStore.entity,
|
||||||
topN: 10,
|
topN: 10,
|
||||||
order: config.standard.sortOrder || "DES",
|
order: c.sortOrder || "DES",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
if (metricType === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||||
const labels = (config.labelsIndex || "")
|
const labels = (c.labelsIndex || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
variables.push(`$labels${index}: [String!]!`);
|
variables.push(`$labels${index}: [String!]!`);
|
||||||
@ -128,7 +129,7 @@ export function useSourceProcessor(
|
|||||||
config: {
|
config: {
|
||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
standard: StandardConfig;
|
metricConfig: MetricConfigOpt[];
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (resp.errors) {
|
if (resp.errors) {
|
||||||
@ -140,23 +141,24 @@ export function useSourceProcessor(
|
|||||||
|
|
||||||
config.metricTypes.forEach((type: string, index) => {
|
config.metricTypes.forEach((type: string, index) => {
|
||||||
const m = config.metrics[index];
|
const m = config.metrics[index];
|
||||||
|
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||||
|
|
||||||
if (type === MetricQueryTypes.ReadMetricsValues) {
|
if (type === MetricQueryTypes.ReadMetricsValues) {
|
||||||
source[m] = resp.data[keys[index]].values.values.map(
|
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) {
|
if (type === MetricQueryTypes.ReadLabeledMetricsValues) {
|
||||||
const resVal = Object.values(resp.data)[0] || [];
|
const resVal = Object.values(resp.data)[0] || [];
|
||||||
const labels = (config.standard.metricLabels || "")
|
const labels = (c.label || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
const labelsIdx = (config.standard.labelsIndex || "")
|
const labelsIdx = (c.labelsIndex || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
.map((item: string) => item.replace(/^\s*|\s*$/g, ""));
|
||||||
for (const item of resVal) {
|
for (const item of resVal) {
|
||||||
const values = item.values.values.map((d: { value: number }) =>
|
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);
|
const indexNum = labelsIdx.findIndex((d: string) => d === item.label);
|
||||||
@ -168,10 +170,7 @@ export function useSourceProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type === MetricQueryTypes.ReadMetricsValue) {
|
if (type === MetricQueryTypes.ReadMetricsValue) {
|
||||||
source[m] = aggregation(
|
source[m] = aggregation(Number(Object.values(resp.data)[0]), c);
|
||||||
Number(Object.values(resp.data)[0]),
|
|
||||||
config.standard
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
type === MetricQueryTypes.SortMetrics ||
|
type === MetricQueryTypes.SortMetrics ||
|
||||||
@ -179,7 +178,7 @@ export function useSourceProcessor(
|
|||||||
) {
|
) {
|
||||||
source[m] = (Object.values(resp.data)[0] || []).map(
|
source[m] = (Object.values(resp.data)[0] || []).map(
|
||||||
(d: { value: unknown; name: string }) => {
|
(d: { value: unknown; name: string }) => {
|
||||||
d.value = aggregation(Number(d.value), config.standard);
|
d.value = aggregation(Number(d.value), c);
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@ -258,7 +257,11 @@ export function useQueryPodsMetrics(
|
|||||||
export function usePodsSource(
|
export function usePodsSource(
|
||||||
pods: Array<Instance | Endpoint>,
|
pods: Array<Instance | Endpoint>,
|
||||||
resp: { errors: string; data: { [key: string]: any } },
|
resp: { errors: string; data: { [key: string]: any } },
|
||||||
config: { metrics: string[]; metricTypes: string[] }
|
config: {
|
||||||
|
metrics: string[];
|
||||||
|
metricTypes: string[];
|
||||||
|
metricConfig: MetricConfigOpt[];
|
||||||
|
}
|
||||||
): any {
|
): any {
|
||||||
if (resp.errors) {
|
if (resp.errors) {
|
||||||
ElMessage.error(resp.errors);
|
ElMessage.error(resp.errors);
|
||||||
@ -266,13 +269,14 @@ export function usePodsSource(
|
|||||||
}
|
}
|
||||||
const data = pods.map((d: Instance | any, idx: number) => {
|
const data = pods.map((d: Instance | any, idx: number) => {
|
||||||
config.metrics.map((name: string, index: number) => {
|
config.metrics.map((name: string, index: number) => {
|
||||||
|
const c = (config.metricConfig && config.metricConfig[index]) || {};
|
||||||
const key = name + idx + index;
|
const key = name + idx + index;
|
||||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
|
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValue) {
|
||||||
d[name] = resp.data[key];
|
d[name] = aggregation(resp.data[key], c);
|
||||||
}
|
}
|
||||||
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
|
if (config.metricTypes[index] === MetricQueryTypes.ReadMetricsValues) {
|
||||||
d[name] = resp.data[key].values.values.map(
|
d[name] = resp.data[key].values.values.map((d: { value: number }) =>
|
||||||
(d: { value: number }) => d.value
|
aggregation(d.value, c)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -307,32 +311,31 @@ export function useQueryTopologyMetrics(metrics: string[], ids: string[]) {
|
|||||||
return { queryStr, conditions };
|
return { queryStr, conditions };
|
||||||
}
|
}
|
||||||
|
|
||||||
function aggregation(val: number, standard: any): number | string {
|
export function aggregation(val: number, config: any): number | string {
|
||||||
let data: number | string = val;
|
let data: number | string = Number(val);
|
||||||
|
|
||||||
if (!isNaN(standard.plus)) {
|
switch (config.calculation) {
|
||||||
data = val + Number(standard.plus);
|
case Calculations.Percentage:
|
||||||
return data;
|
data = val / 100;
|
||||||
}
|
break;
|
||||||
if (!isNaN(standard.minus)) {
|
case Calculations.ByteToKB:
|
||||||
data = val - Number(standard.plus);
|
data = val / 1024;
|
||||||
return data;
|
break;
|
||||||
}
|
case Calculations.Apdex:
|
||||||
if (!isNaN(standard.multiply) && standard.divide !== 0) {
|
data = val / 10000;
|
||||||
data = val * Number(standard.multiply);
|
break;
|
||||||
return data;
|
case Calculations.ConvertSeconds:
|
||||||
}
|
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
|
||||||
if (!isNaN(standard.divide) && standard.divide !== 0) {
|
break;
|
||||||
data = val / Number(standard.divide);
|
case Calculations.ConvertMilliseconds:
|
||||||
return data;
|
data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
|
||||||
}
|
break;
|
||||||
if (standard.milliseconds) {
|
case Calculations.Precision:
|
||||||
data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
|
data = data.toFixed(2);
|
||||||
return data;
|
break;
|
||||||
}
|
default:
|
||||||
if (standard.milliseconds) {
|
data;
|
||||||
data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
|
break;
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -126,6 +126,9 @@ const msg = {
|
|||||||
kubernetesCluster: "Cluster",
|
kubernetesCluster: "Cluster",
|
||||||
kubernetes: "Kubernetes",
|
kubernetes: "Kubernetes",
|
||||||
textUrl: "Text Hyperlink",
|
textUrl: "Text Hyperlink",
|
||||||
|
textAlign: "Text Align",
|
||||||
|
metricLabel: "Metric Label",
|
||||||
|
showUnit: "Show Unit",
|
||||||
hourTip: "Select Hour",
|
hourTip: "Select Hour",
|
||||||
minuteTip: "Select Minute",
|
minuteTip: "Select Minute",
|
||||||
secondTip: "Select Second",
|
secondTip: "Select Second",
|
||||||
@ -258,7 +261,7 @@ const msg = {
|
|||||||
independentSelector: "Selectors",
|
independentSelector: "Selectors",
|
||||||
unknownMetrics: "Unknown Metrics",
|
unknownMetrics: "Unknown Metrics",
|
||||||
labels: "Labels",
|
labels: "Labels",
|
||||||
aggregation: "Data Calculation",
|
aggregation: "Calculation",
|
||||||
unit: "Unit",
|
unit: "Unit",
|
||||||
labelsIndex: "Label Subscript",
|
labelsIndex: "Label Subscript",
|
||||||
parentService: "Parent Service",
|
parentService: "Parent Service",
|
||||||
|
@ -126,6 +126,9 @@ const msg = {
|
|||||||
kubernetesCluster: "集群",
|
kubernetesCluster: "集群",
|
||||||
kubernetes: "Kubernetes",
|
kubernetes: "Kubernetes",
|
||||||
textUrl: "文本超链接",
|
textUrl: "文本超链接",
|
||||||
|
textAlign: "文本对齐",
|
||||||
|
metricLabel: "指标标签",
|
||||||
|
showUnit: "显示单位",
|
||||||
hourTip: "选择小时",
|
hourTip: "选择小时",
|
||||||
minuteTip: "选择分钟",
|
minuteTip: "选择分钟",
|
||||||
secondTip: "选择秒数",
|
secondTip: "选择秒数",
|
||||||
@ -260,7 +263,7 @@ const msg = {
|
|||||||
independentSelector: "独立选择器",
|
independentSelector: "独立选择器",
|
||||||
unknownMetrics: "未知指标",
|
unknownMetrics: "未知指标",
|
||||||
labels: "标签",
|
labels: "标签",
|
||||||
aggregation: "数据计算",
|
aggregation: "计算",
|
||||||
unit: "单位",
|
unit: "单位",
|
||||||
labelsIndex: "标签下标",
|
labelsIndex: "标签下标",
|
||||||
parentService: "父级服务",
|
parentService: "父级服务",
|
||||||
|
@ -28,6 +28,7 @@ export const NewControl = {
|
|||||||
standard: {},
|
standard: {},
|
||||||
metrics: [""],
|
metrics: [""],
|
||||||
metricTypes: [""],
|
metricTypes: [""],
|
||||||
|
metricConfig: [],
|
||||||
};
|
};
|
||||||
export const TextConfig = {
|
export const TextConfig = {
|
||||||
fontColor: "white",
|
fontColor: "white",
|
||||||
|
@ -36,8 +36,17 @@ export interface LayoutConfig {
|
|||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
children?: any;
|
children?: any;
|
||||||
activedTabIndex?: number;
|
activedTabIndex?: number;
|
||||||
|
metricConfig?: MetricConfigOpt[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MetricConfigOpt = {
|
||||||
|
unit: string;
|
||||||
|
label: string;
|
||||||
|
calculation: string;
|
||||||
|
labelsIndex: string;
|
||||||
|
sortOrder: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface WidgetConfig {
|
export interface WidgetConfig {
|
||||||
title?: string;
|
title?: string;
|
||||||
tips?: string;
|
tips?: string;
|
||||||
@ -87,7 +96,7 @@ export interface AreaConfig {
|
|||||||
export interface CardConfig {
|
export interface CardConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
showUint?: boolean;
|
showUnit?: boolean;
|
||||||
textAlign?: "center" | "right" | "left";
|
textAlign?: "center" | "right" | "left";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,17 @@ limitations under the License. -->
|
|||||||
@change="changeConfig({ content })"
|
@change="changeConfig({ content })"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div class="item">
|
||||||
<span class="label">{{ t("backgroundColors") }}</span>
|
<span class="label">{{ t("backgroundColors") }}</span>
|
||||||
<Selector
|
<Selector
|
||||||
@ -85,6 +96,7 @@ const backgroundColor = ref(originConfig.graph.backgroundColor || "green");
|
|||||||
const fontColor = ref(originConfig.graph.fontColor || "white");
|
const fontColor = ref(originConfig.graph.fontColor || "white");
|
||||||
const content = ref<string>(originConfig.graph.content || "");
|
const content = ref<string>(originConfig.graph.content || "");
|
||||||
const fontSize = ref<number>(originConfig.graph.fontSize || 12);
|
const fontSize = ref<number>(originConfig.graph.fontSize || 12);
|
||||||
|
const textAlign = ref(originConfig.graph.textAlign || "left");
|
||||||
const Colors = [
|
const Colors = [
|
||||||
{
|
{
|
||||||
label: "Green",
|
label: "Green",
|
||||||
@ -97,6 +109,14 @@ const Colors = [
|
|||||||
{ label: "Black", value: "black" },
|
{ label: "Black", value: "black" },
|
||||||
{ label: "Orange", value: "orange" },
|
{ label: "Orange", value: "orange" },
|
||||||
];
|
];
|
||||||
|
const AlignStyle = [
|
||||||
|
{
|
||||||
|
label: "Left",
|
||||||
|
value: "left",
|
||||||
|
},
|
||||||
|
{ label: "Center", value: "center" },
|
||||||
|
{ label: "Right", value: "right" },
|
||||||
|
];
|
||||||
function changeConfig(param: { [key: string]: unknown }) {
|
function changeConfig(param: { [key: string]: unknown }) {
|
||||||
const { selectedGrid } = dashboardStore;
|
const { selectedGrid } = dashboardStore;
|
||||||
const graph = {
|
const graph = {
|
||||||
|
@ -36,7 +36,7 @@ limitations under the License. -->
|
|||||||
i: dashboardStore.selectedGrid.i,
|
i: dashboardStore.selectedGrid.i,
|
||||||
metrics: dashboardStore.selectedGrid.metrics,
|
metrics: dashboardStore.selectedGrid.metrics,
|
||||||
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
metricTypes: dashboardStore.selectedGrid.metricTypes,
|
||||||
standard: dashboardStore.selectedGrid.standard,
|
metricConfig: dashboardStore.selectedGrid.metricConfig,
|
||||||
}"
|
}"
|
||||||
:isEdit="isEdit"
|
:isEdit="isEdit"
|
||||||
@changeOpt="setStatus"
|
@changeOpt="setStatus"
|
||||||
@ -64,9 +64,6 @@ limitations under the License. -->
|
|||||||
<el-collapse-item :title="t('widgetOptions')" name="3">
|
<el-collapse-item :title="t('widgetOptions')" name="3">
|
||||||
<WidgetOptions />
|
<WidgetOptions />
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item :title="t('standardOptions')" name="4">
|
|
||||||
<StandardOptions @update="getSource" @loading="setLoading" />
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
@ -88,16 +85,14 @@ import { Option } from "@/types/app";
|
|||||||
import graphs from "../graphs";
|
import graphs from "../graphs";
|
||||||
import configs from "./widget/graph-styles";
|
import configs from "./widget/graph-styles";
|
||||||
import WidgetOptions from "./widget/WidgetOptions.vue";
|
import WidgetOptions from "./widget/WidgetOptions.vue";
|
||||||
import StandardOptions from "./widget/StandardOptions.vue";
|
import MetricOptions from "./widget/metric/Index.vue";
|
||||||
import MetricOptions from "./widget/MetricOptions.vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ConfigEdit",
|
name: "WidgetEdit",
|
||||||
components: {
|
components: {
|
||||||
...graphs,
|
...graphs,
|
||||||
...configs,
|
...configs,
|
||||||
WidgetOptions,
|
WidgetOptions,
|
||||||
StandardOptions,
|
|
||||||
MetricOptions,
|
MetricOptions,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -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>
|
|
@ -26,6 +26,15 @@ limitations under the License. -->
|
|||||||
@change="updateConfig({ fontSize })"
|
@change="updateConfig({ fontSize })"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
@ -36,13 +45,14 @@ const { t } = useI18n();
|
|||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const { selectedGrid } = dashboardStore;
|
const { selectedGrid } = dashboardStore;
|
||||||
const fontSize = ref(selectedGrid.graph.fontSize);
|
const fontSize = ref(selectedGrid.graph.fontSize);
|
||||||
|
const showUnit = ref<boolean>(selectedGrid.graph.showUnit);
|
||||||
|
|
||||||
function updateConfig(param: { [key: string]: unknown }) {
|
function updateConfig(param: { [key: string]: unknown }) {
|
||||||
const graph = {
|
const graph = {
|
||||||
...selectedGrid.graph,
|
...dashboardStore.selectedGrid.graph,
|
||||||
...param,
|
...param,
|
||||||
};
|
};
|
||||||
dashboardStore.selectWidget({ ...selectedGrid, graph });
|
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, graph });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -49,6 +49,18 @@ limitations under the License. -->
|
|||||||
@change="changeMetricType(index, $event)"
|
@change="changeMetricType(index, $event)"
|
||||||
class="selectors"
|
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
|
<span
|
||||||
v-show="states.isList || states.metricTypes[0] === 'readMetricsValues'"
|
v-show="states.isList || states.metricTypes[0] === 'readMetricsValues'"
|
||||||
>
|
>
|
||||||
@ -96,12 +108,13 @@ import {
|
|||||||
ChartTypes,
|
ChartTypes,
|
||||||
PodsChartTypes,
|
PodsChartTypes,
|
||||||
MetricsType,
|
MetricsType,
|
||||||
} from "../../data";
|
} from "../../../data";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import Icon from "@/components/Icon.vue";
|
import Icon from "@/components/Icon.vue";
|
||||||
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
|
import { useQueryProcessor, useSourceProcessor } from "@/hooks/useProcessor";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { DashboardItem } from "@/types/dashboard";
|
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
import Standard from "./Standard.vue";
|
||||||
|
|
||||||
/*global defineEmits */
|
/*global defineEmits */
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@ -127,6 +140,13 @@ const states = reactive<{
|
|||||||
dashboardName: graph.dashboardName,
|
dashboardName: graph.dashboardName,
|
||||||
dashboardList: [{ label: "", value: "" }],
|
dashboardList: [{ label: "", value: "" }],
|
||||||
});
|
});
|
||||||
|
const currentMetricConfig = ref<MetricConfigOpt>({
|
||||||
|
unit: "",
|
||||||
|
label: "",
|
||||||
|
labelsIndex: "",
|
||||||
|
calculation: "",
|
||||||
|
sortOrder: "DES",
|
||||||
|
});
|
||||||
|
|
||||||
states.isList = ListChartTypes.includes(graph.type);
|
states.isList = ListChartTypes.includes(graph.type);
|
||||||
const defaultLen = ref<number>(states.isList ? 5 : 20);
|
const defaultLen = ref<number>(states.isList ? 5 : 20);
|
||||||
@ -323,8 +343,8 @@ async function queryMetrics() {
|
|||||||
if (states.isList) {
|
if (states.isList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { standard } = dashboardStore.selectedGrid;
|
const { metricConfig } = dashboardStore.selectedGrid;
|
||||||
const params = useQueryProcessor({ ...states, standard });
|
const params = useQueryProcessor({ ...states, metricConfig });
|
||||||
if (!params) {
|
if (!params) {
|
||||||
emit("update", {});
|
emit("update", {});
|
||||||
return;
|
return;
|
||||||
@ -337,7 +357,7 @@ async function queryMetrics() {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const source = useSourceProcessor(json, { ...states, standard });
|
const source = useSourceProcessor(json, { ...states, metricConfig });
|
||||||
emit("update", source);
|
emit("update", source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,11 +392,20 @@ function deleteMetric(index: number) {
|
|||||||
dashboardStore.selectWidget({
|
dashboardStore.selectWidget({
|
||||||
...dashboardStore.selectedGrid,
|
...dashboardStore.selectedGrid,
|
||||||
...{ metricTypes: states.metricTypes, metrics: states.metrics },
|
...{ metricTypes: states.metricTypes, metrics: states.metrics },
|
||||||
|
metricConfig: [],
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
states.metrics.splice(index, 1);
|
states.metrics.splice(index, 1);
|
||||||
states.metricTypes.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) {
|
function setMetricTypeList(type: string) {
|
||||||
if (type !== MetricsType.REGULAR_VALUE) {
|
if (type !== MetricsType.REGULAR_VALUE) {
|
||||||
@ -393,6 +422,26 @@ function setMetricTypeList(type: string) {
|
|||||||
}
|
}
|
||||||
return MetricTypes[type];
|
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>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.ds-name {
|
.ds-name {
|
129
src/views/dashboard/configuration/widget/metric/Standard.vue
Normal file
@ -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>
|
@ -36,18 +36,17 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="body"
|
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
|
<a :href="data.graph.url" target="_blank">
|
||||||
:style="{
|
{{ data.graph.content }}
|
||||||
color: TextColors[data.graph.fontColor],
|
</a>
|
||||||
fontSize: data.graph.fontSize + 'px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<a :href="data.graph.url" target="_blank">
|
|
||||||
{{ data.graph.content }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -75,14 +74,6 @@ function editConfig() {
|
|||||||
dashboardStore.setConfigPanel(true);
|
dashboardStore.setConfigPanel(true);
|
||||||
dashboardStore.selectWidget(props.data);
|
dashboardStore.selectWidget(props.data);
|
||||||
}
|
}
|
||||||
function viewText() {
|
|
||||||
const path = props.data.graph.url;
|
|
||||||
console.log(path);
|
|
||||||
if (!path) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.open(path, "_blank");
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.topology {
|
.topology {
|
||||||
@ -97,21 +88,21 @@ function viewText() {
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 5px;
|
||||||
right: 10px;
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
line-height: 100%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
|
||||||
color: #333;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: horizontal;
|
-webkit-box-orient: horizontal;
|
||||||
-webkit-box-pack: center;
|
-webkit-box-pack: center;
|
||||||
-webkit-box-align: center;
|
box-sizing: border-box;
|
||||||
|
display: -webkit-box;
|
||||||
|
// -webkit-box-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools {
|
.tools {
|
||||||
|
@ -64,8 +64,8 @@ limitations under the License. -->
|
|||||||
metrics: data.metrics,
|
metrics: data.metrics,
|
||||||
metricTypes: data.metricTypes,
|
metricTypes: data.metricTypes,
|
||||||
i: data.i,
|
i: data.i,
|
||||||
|
metricConfig: data.metricConfig,
|
||||||
}"
|
}"
|
||||||
:standard="data.standard"
|
|
||||||
:needQuery="needQuery"
|
:needQuery="needQuery"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -128,7 +128,7 @@ export default defineComponent({
|
|||||||
const d = {
|
const d = {
|
||||||
metrics: props.data.metrics,
|
metrics: props.data.metrics,
|
||||||
metricTypes: props.data.metricTypes,
|
metricTypes: props.data.metricTypes,
|
||||||
standard: props.data.standard,
|
metricConfig: props.data.metricConfig || [],
|
||||||
};
|
};
|
||||||
state.source = useSourceProcessor(json, d);
|
state.source = useSourceProcessor(json, d);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ export enum LegendOpt {
|
|||||||
}
|
}
|
||||||
export const DepthList = [1, 2, 3, 4, 5].map((item: number) => ({
|
export const DepthList = [1, 2, 3, 4, 5].map((item: number) => ({
|
||||||
value: item,
|
value: item,
|
||||||
label: item,
|
label: String(item),
|
||||||
}));
|
}));
|
||||||
export const Status = [
|
export const Status = [
|
||||||
{ label: "All", value: "ALL" },
|
{ label: "All", value: "ALL" },
|
||||||
@ -264,3 +264,15 @@ export const TextColors: { [key: string]: string } = {
|
|||||||
black: "#000",
|
black: "#000",
|
||||||
orange: "#E6A23C",
|
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" },
|
||||||
|
];
|
||||||
|
@ -26,12 +26,14 @@ limitations under the License. -->
|
|||||||
? null
|
? null
|
||||||
: singleVal.toFixed(2)
|
: singleVal.toFixed(2)
|
||||||
}}
|
}}
|
||||||
<span v-show="config.showUint">{{ standard.unit }}</span>
|
<span v-show="config.showUnit">
|
||||||
|
{{ metricConfig[0]?.unit }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, PropType } from "vue";
|
import { computed, PropType } from "vue";
|
||||||
import { CardConfig, StandardConfig } from "@/types/dashboard";
|
import { CardConfig, MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -40,14 +42,16 @@ const props = defineProps({
|
|||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object as PropType<CardConfig>,
|
type: Object as PropType<CardConfig & { metricConfig?: MetricConfigOpt[] }>,
|
||||||
default: () => ({ fontSize: 12, showUint: true, textAlign: "center" }),
|
default: () => ({
|
||||||
},
|
fontSize: 12,
|
||||||
standard: {
|
showUnit: true,
|
||||||
type: Object as PropType<StandardConfig>,
|
textAlign: "center",
|
||||||
default: () => ({ unit: "" }),
|
metricConfig: [],
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const metricConfig = computed(() => props.config.metricConfig || []);
|
||||||
const key = computed(() => Object.keys(props.data)[0]);
|
const key = computed(() => Object.keys(props.data)[0]);
|
||||||
const singleVal = computed(() => props.data[key.value]);
|
const singleVal = computed(() => props.data[key.value]);
|
||||||
</script>
|
</script>
|
||||||
|
@ -44,7 +44,7 @@ limitations under the License. -->
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="(metric, index) in config.metrics"
|
v-for="(metric, index) in config.metrics"
|
||||||
:label="metric"
|
:label="`${metric} ${getUnit(index)}`"
|
||||||
:key="metric + index"
|
:key="metric + index"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -92,6 +92,7 @@ import Card from "./Card.vue";
|
|||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -104,7 +105,7 @@ const props = defineProps({
|
|||||||
i: string;
|
i: string;
|
||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
}
|
} & { metricConfig: MetricConfigOpt[] }
|
||||||
>,
|
>,
|
||||||
default: () => ({ dashboardName: "", fontSize: 12, i: "" }),
|
default: () => ({ dashboardName: "", fontSize: 12, i: "" }),
|
||||||
},
|
},
|
||||||
@ -155,7 +156,12 @@ async function queryEndpointMetrics(currentPods: Endpoint[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
endpoints.value = usePodsSource(currentPods, json, props.config);
|
const metricConfig = props.config.metricConfig || [];
|
||||||
|
|
||||||
|
endpoints.value = usePodsSource(currentPods, json, {
|
||||||
|
...props.config,
|
||||||
|
metricConfig: metricConfig,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
endpoints.value = currentPods;
|
endpoints.value = currentPods;
|
||||||
@ -184,12 +190,21 @@ async function searchList() {
|
|||||||
const limit = searchText.value ? undefined : total;
|
const limit = searchText.value ? undefined : total;
|
||||||
await queryEndpoints(limit);
|
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(
|
watch(
|
||||||
() => [props.config.metricTypes, props.config.metrics],
|
() => [props.config.metricTypes, props.config.metrics],
|
||||||
async () => {
|
async () => {
|
||||||
if (props.isEdit) {
|
queryEndpointMetrics(endpoints.value);
|
||||||
queryEndpointMetrics(endpoints.value);
|
|
||||||
}
|
|
||||||
// emit("changeOpt", false);
|
// emit("changeOpt", false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -44,7 +44,7 @@ limitations under the License. -->
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="(metric, index) in config.metrics"
|
v-for="(metric, index) in config.metrics"
|
||||||
:label="metric"
|
:label="`${metric} ${getUnit(index)}`"
|
||||||
:key="metric + index"
|
:key="metric + index"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -112,6 +112,7 @@ import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
|
|||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -122,7 +123,7 @@ const props = defineProps({
|
|||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
}
|
} & { metricConfig: MetricConfigOpt[] }
|
||||||
>,
|
>,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
dashboardName: "",
|
dashboardName: "",
|
||||||
@ -180,7 +181,11 @@ async function queryInstanceMetrics(currentInstances: Instance[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instances.value = usePodsSource(currentInstances, json, props.config);
|
const metricConfig = props.config.metricConfig || [];
|
||||||
|
instances.value = usePodsSource(currentInstances, json, {
|
||||||
|
...props.config,
|
||||||
|
metricConfig,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instances.value = currentInstances;
|
instances.value = currentInstances;
|
||||||
@ -214,6 +219,18 @@ function searchList() {
|
|||||||
instances.value = searchInstances.value.splice(0, pageSize);
|
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(
|
watch(
|
||||||
() => [props.config.metricTypes, props.config.metrics],
|
() => [props.config.metricTypes, props.config.metrics],
|
||||||
() => {
|
() => {
|
||||||
|
@ -56,7 +56,7 @@ limitations under the License. -->
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="(metric, index) in config.metrics"
|
v-for="(metric, index) in config.metrics"
|
||||||
:label="metric"
|
:label="`${metric} ${getUnit(index)}`"
|
||||||
:key="metric + index"
|
:key="metric + index"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@ -104,6 +104,7 @@ import { useQueryPodsMetrics, usePodsSource } from "@/hooks/useProcessor";
|
|||||||
import { EntityType } from "../data";
|
import { EntityType } from "../data";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -117,7 +118,7 @@ const props = defineProps({
|
|||||||
metrics: string[];
|
metrics: string[];
|
||||||
metricTypes: string[];
|
metricTypes: string[];
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
}
|
} & { metricConfig: MetricConfigOpt[] }
|
||||||
>,
|
>,
|
||||||
default: () => ({ dashboardName: "", fontSize: 12 }),
|
default: () => ({ dashboardName: "", fontSize: 12 }),
|
||||||
},
|
},
|
||||||
@ -214,7 +215,11 @@ async function queryServiceMetrics(currentServices: Service[]) {
|
|||||||
ElMessage.error(json.errors);
|
ElMessage.error(json.errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
services.value = usePodsSource(currentServices, json, props.config);
|
const metricConfig = props.config.metricConfig || [];
|
||||||
|
services.value = usePodsSource(currentServices, json, {
|
||||||
|
...props.config,
|
||||||
|
metricConfig: metricConfig || [],
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
services.value = currentServices;
|
services.value = currentServices;
|
||||||
@ -250,6 +255,17 @@ function searchList() {
|
|||||||
);
|
);
|
||||||
services.value = searchServices.splice(0, pageSize);
|
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(
|
watch(
|
||||||
() => [props.config.metricTypes, props.config.metrics],
|
() => [props.config.metricTypes, props.config.metrics],
|
||||||
() => {
|
() => {
|
||||||
|
@ -90,6 +90,8 @@ import { Option } from "@/types/app";
|
|||||||
import { Service } from "@/types/selector";
|
import { Service } from "@/types/selector";
|
||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import getDashboard from "@/hooks/useDashboardsSession";
|
import getDashboard from "@/hooks/useDashboardsSession";
|
||||||
|
import { MetricConfigOpt } from "@/types/dashboard";
|
||||||
|
import { aggregation } from "@/hooks/useProcessor";
|
||||||
|
|
||||||
/*global Nullable, defineProps */
|
/*global Nullable, defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -275,13 +277,17 @@ function update() {
|
|||||||
handleNodeClick: handleNodeClick,
|
handleNodeClick: handleNodeClick,
|
||||||
tipHtml: (data: Node) => {
|
tipHtml: (data: Node) => {
|
||||||
const nodeMetrics: string[] = settings.value.nodeMetrics || [];
|
const nodeMetrics: string[] = settings.value.nodeMetrics || [];
|
||||||
const html = nodeMetrics.map((m) => {
|
const nodeMetricConfig = settings.value.nodeMetricConfig || [];
|
||||||
|
const html = nodeMetrics.map((m, index) => {
|
||||||
const metric =
|
const metric =
|
||||||
topologyStore.nodeMetricValue[m].values.filter(
|
topologyStore.nodeMetricValue[m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
(val: { id: string; value: unknown }) => val.id === data.id
|
||||||
)[0] || {};
|
) || {};
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||||
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>`;
|
||||||
});
|
});
|
||||||
return [
|
return [
|
||||||
` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`,
|
` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`,
|
||||||
@ -306,24 +312,34 @@ function update() {
|
|||||||
tipHtml: (data: Call) => {
|
tipHtml: (data: Call) => {
|
||||||
const linkClientMetrics: string[] =
|
const linkClientMetrics: string[] =
|
||||||
settings.value.linkClientMetrics || [];
|
settings.value.linkClientMetrics || [];
|
||||||
|
const linkServerMetricConfig: MetricConfigOpt[] =
|
||||||
|
settings.value.linkServerMetricConfig || [];
|
||||||
|
const linkClientMetricConfig: MetricConfigOpt[] =
|
||||||
|
settings.value.linkClientMetricConfig || [];
|
||||||
const linkServerMetrics: string[] =
|
const linkServerMetrics: string[] =
|
||||||
settings.value.linkServerMetrics || [];
|
settings.value.linkServerMetrics || [];
|
||||||
const htmlServer = linkServerMetrics.map((m) => {
|
const htmlServer = linkServerMetrics.map((m, index) => {
|
||||||
const metric = topologyStore.linkServerMetrics[m].values.filter(
|
const metric = topologyStore.linkServerMetrics[m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
(val: { id: string; value: unknown }) => val.id === data.id
|
||||||
)[0];
|
);
|
||||||
if (metric) {
|
if (metric) {
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
|
||||||
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 htmlClient = linkClientMetrics.map((m) => {
|
const htmlClient = linkClientMetrics.map((m: string, index: number) => {
|
||||||
const metric = topologyStore.linkClientMetrics[m].values.filter(
|
const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
|
||||||
|
const metric = topologyStore.linkClientMetrics[m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
(val: { id: string; value: unknown }) => val.id === data.id
|
||||||
)[0];
|
);
|
||||||
if (metric) {
|
if (metric) {
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
const v = aggregation(metric.value, opt);
|
||||||
return ` <div class="mb-5"><span class="grey">${m}: </span>${val}</div>`;
|
return ` <div class="mb-5"><span class="grey">${
|
||||||
|
opt.label || m
|
||||||
|
}: </span>${v} ${opt.unit || ""}</div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const html = [
|
const html = [
|
||||||
|
170
src/views/dashboard/related/topology/components/Metrics.vue
Normal file
@ -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>
|
@ -54,7 +54,7 @@ limitations under the License. -->
|
|||||||
element-loading-background="rgba(0, 0, 0, 0)"
|
element-loading-background="rgba(0, 0, 0, 0)"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<Sankey @click="selectNodeLink" />
|
<Sankey @click="selectNodeLink" :settings="settings" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="operations-list"
|
class="operations-list"
|
||||||
|
@ -17,11 +17,19 @@ limitations under the License. -->
|
|||||||
<Graph :option="option" @select="clickChart" />
|
<Graph :option="option" @select="clickChart" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed, PropType } from "vue";
|
||||||
import { useTopologyStore } from "@/store/modules/topology";
|
import { useTopologyStore } from "@/store/modules/topology";
|
||||||
import { Node, Call } from "@/types/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 emit = defineEmits(["click"]);
|
||||||
const topologyStore = useTopologyStore();
|
const topologyStore = useTopologyStore();
|
||||||
const option = computed(() => getOption());
|
const option = computed(() => getOption());
|
||||||
@ -77,23 +85,34 @@ function getOption() {
|
|||||||
function linkTooltip(data: Call) {
|
function linkTooltip(data: Call) {
|
||||||
const clientMetrics: string[] = Object.keys(topologyStore.linkClientMetrics);
|
const clientMetrics: string[] = Object.keys(topologyStore.linkClientMetrics);
|
||||||
const serverMetrics: string[] = Object.keys(topologyStore.linkServerMetrics);
|
const serverMetrics: string[] = Object.keys(topologyStore.linkServerMetrics);
|
||||||
const htmlServer = serverMetrics.map((m) => {
|
const linkServerMetricConfig: MetricConfigOpt[] =
|
||||||
const metric = topologyStore.linkServerMetrics[m].values.filter(
|
props.settings.linkServerMetricConfig || [];
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
const linkClientMetricConfig: MetricConfigOpt[] =
|
||||||
)[0];
|
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) {
|
if (metric) {
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
|
||||||
return ` <div><span>${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 htmlClient = clientMetrics.map((m) => {
|
const htmlClient = clientMetrics.map((m, index) => {
|
||||||
const metric = topologyStore.linkClientMetrics[m].values.filter(
|
const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
const metric =
|
||||||
)[0];
|
topologyStore.linkClientMetrics[m].values.find(
|
||||||
if (metric) {
|
(val: { id: string; value: unknown }) => val.id === data.id
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
) || {};
|
||||||
return ` <div><span>${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 = [
|
const html = [
|
||||||
`<div>${data.sourceObj.serviceName} -> ${data.targetObj.serviceName}</div>`,
|
`<div>${data.sourceObj.serviceName} -> ${data.targetObj.serviceName}</div>`,
|
||||||
@ -106,13 +125,17 @@ function linkTooltip(data: Call) {
|
|||||||
|
|
||||||
function nodeTooltip(data: Node) {
|
function nodeTooltip(data: Node) {
|
||||||
const nodeMetrics: string[] = Object.keys(topologyStore.nodeMetricValue);
|
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 =
|
const metric =
|
||||||
topologyStore.nodeMetricValue[m].values.filter(
|
topologyStore.nodeMetricValue[m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id
|
(val: { id: string; value: unknown }) => val.id === data.id
|
||||||
)[0] || {};
|
) || {};
|
||||||
const val = m.includes("_sla") ? metric.value / 100 : metric.value;
|
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||||
return ` <div><span>${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>`;
|
||||||
});
|
});
|
||||||
return [` <div><span>name: </span>${data.serviceName}</div>`, ...html].join(
|
return [` <div><span>name: </span>${data.serviceName}</div>`, ...html].join(
|
||||||
" "
|
" "
|
||||||
|
@ -25,7 +25,27 @@ limitations under the License. -->
|
|||||||
class="inputs"
|
class="inputs"
|
||||||
:clearable="true"
|
: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
|
<Selector
|
||||||
class="inputs"
|
class="inputs"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
@ -33,11 +53,29 @@ limitations under the License. -->
|
|||||||
:options="states.linkMetricList"
|
:options="states.linkMetricList"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="Select metrics"
|
placeholder="Select metrics"
|
||||||
@change="changeLinkServerMetrics"
|
@change="updateLinkServerMetrics"
|
||||||
/>
|
/>
|
||||||
<span v-show="dashboardStore.entity !== EntityType[2].value">
|
<span v-show="dashboardStore.entity !== EntityType[2].value">
|
||||||
<div class="label">
|
<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>
|
</div>
|
||||||
<Selector
|
<Selector
|
||||||
class="inputs"
|
class="inputs"
|
||||||
@ -46,7 +84,7 @@ limitations under the License. -->
|
|||||||
:options="states.linkMetricList"
|
:options="states.linkMetricList"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="Select metrics"
|
placeholder="Select metrics"
|
||||||
@change="changeLinkClientMetrics"
|
@change="updateLinkClientMetrics"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -100,7 +138,27 @@ limitations under the License. -->
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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
|
<Selector
|
||||||
class="inputs"
|
class="inputs"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
@ -108,7 +166,7 @@ limitations under the License. -->
|
|||||||
:options="states.nodeMetricList"
|
:options="states.nodeMetricList"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="Select metrics"
|
placeholder="Select metrics"
|
||||||
@change="changeNodeMetrics"
|
@change="updateNodeMetrics"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="legend-settings" v-show="isService">
|
<div class="legend-settings" v-show="isService">
|
||||||
@ -157,15 +215,6 @@ limitations under the License. -->
|
|||||||
</span>
|
</span>
|
||||||
<div v-show="index !== legend.metric.length - 1">&&</div>
|
<div v-show="index !== legend.metric.length - 1">&&</div>
|
||||||
</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
|
<el-button
|
||||||
@click="setLegend"
|
@click="setLegend"
|
||||||
class="legend-btn"
|
class="legend-btn"
|
||||||
@ -177,7 +226,7 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
import { useTopologyStore } from "@/store/modules/topology";
|
import { useTopologyStore } from "@/store/modules/topology";
|
||||||
@ -186,8 +235,9 @@ import { MetricCatalog, ScopeType, MetricConditions } from "../../../data";
|
|||||||
import { Option } from "@/types/app";
|
import { Option } from "@/types/app";
|
||||||
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
|
import { useQueryTopologyMetrics } from "@/hooks/useProcessor";
|
||||||
import { Node } from "@/types/topology";
|
import { Node } from "@/types/topology";
|
||||||
import { DashboardItem } from "@/types/dashboard";
|
import { DashboardItem, MetricConfigOpt } from "@/types/dashboard";
|
||||||
import { EntityType, LegendOpt, MetricsType } from "../../../data";
|
import { EntityType, LegendOpt, MetricsType } from "../../../data";
|
||||||
|
import Metrics from "./Metrics.vue";
|
||||||
|
|
||||||
/*global defineEmits */
|
/*global defineEmits */
|
||||||
const emit = defineEmits(["update", "updateNodes"]);
|
const emit = defineEmits(["update", "updateNodes"]);
|
||||||
@ -238,6 +288,7 @@ const legend = reactive<{
|
|||||||
}>({
|
}>({
|
||||||
metric: l ? selectedGrid.legend : [{ name: "", condition: "", value: "" }],
|
metric: l ? selectedGrid.legend : [{ name: "", condition: "", value: "" }],
|
||||||
});
|
});
|
||||||
|
const configType = ref<string>("");
|
||||||
|
|
||||||
getMetricList();
|
getMetricList();
|
||||||
async function getMetricList() {
|
async function getMetricList() {
|
||||||
@ -347,11 +398,12 @@ function deleteItem(index: number) {
|
|||||||
items.splice(index, 1);
|
items.splice(index, 1);
|
||||||
updateSettings();
|
updateSettings();
|
||||||
}
|
}
|
||||||
function updateSettings() {
|
function updateSettings(metricConfig?: { [key: string]: MetricConfigOpt[] }) {
|
||||||
const metrics = legend.metric.filter(
|
const metrics = legend.metric.filter(
|
||||||
(d: any) => d.name && d.value && d.condition
|
(d: any) => d.name && d.value && d.condition
|
||||||
);
|
);
|
||||||
const param = {
|
const param = {
|
||||||
|
...dashboardStore.selectedGrid,
|
||||||
linkDashboard: states.linkDashboard,
|
linkDashboard: states.linkDashboard,
|
||||||
nodeDashboard: isService
|
nodeDashboard: isService
|
||||||
? items.filter((d: { scope: string; dashboard: string }) => d.dashboard)
|
? items.filter((d: { scope: string; dashboard: string }) => d.dashboard)
|
||||||
@ -360,32 +412,76 @@ function updateSettings() {
|
|||||||
linkClientMetrics: states.linkClientMetrics,
|
linkClientMetrics: states.linkClientMetrics,
|
||||||
nodeMetrics: states.nodeMetrics,
|
nodeMetrics: states.nodeMetrics,
|
||||||
legend: metrics,
|
legend: metrics,
|
||||||
|
...metricConfig,
|
||||||
};
|
};
|
||||||
dashboardStore.selectWidget({ ...dashboardStore.selectedGrid, ...param });
|
dashboardStore.selectWidget(param);
|
||||||
dashboardStore.setConfigs({ ...dashboardStore.selectedGrid, ...param });
|
dashboardStore.setConfigs(param);
|
||||||
emit("update", param);
|
emit("update", param);
|
||||||
}
|
}
|
||||||
async function changeLinkServerMetrics(options: Option[] | any) {
|
function updateLinkServerMetrics(options: Option[] | any) {
|
||||||
states.linkServerMetrics = options.map((d: Option) => d.value);
|
const opt = options.map((d: Option) => d.value);
|
||||||
updateSettings();
|
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) {
|
if (!states.linkServerMetrics.length) {
|
||||||
topologyStore.setLinkServerMetrics({});
|
topologyStore.setLinkServerMetrics({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
topologyStore.getLinkServerMetrics(states.linkServerMetrics);
|
topologyStore.getLinkServerMetrics(states.linkServerMetrics);
|
||||||
}
|
}
|
||||||
async function changeLinkClientMetrics(options: Option[] | any) {
|
function updateLinkClientMetrics(options: Option[] | any) {
|
||||||
states.linkClientMetrics = options.map((d: Option) => d.value);
|
const opt = options.map((d: Option) => d.value);
|
||||||
updateSettings();
|
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) {
|
if (!states.linkClientMetrics.length) {
|
||||||
topologyStore.setLinkClientMetrics({});
|
topologyStore.setLinkClientMetrics({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
topologyStore.getLinkClientMetrics(states.linkClientMetrics);
|
topologyStore.getLinkClientMetrics(states.linkClientMetrics);
|
||||||
}
|
}
|
||||||
async function changeNodeMetrics(options: Option[] | any) {
|
function updateNodeMetrics(options: Option[] | any) {
|
||||||
states.nodeMetrics = options.map((d: Option) => d.value);
|
const opt = options.map((d: Option) => d.value);
|
||||||
updateSettings();
|
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) {
|
if (!states.nodeMetrics.length) {
|
||||||
topologyStore.setNodeMetricValue({});
|
topologyStore.setNodeMetricValue({});
|
||||||
return;
|
return;
|
||||||
@ -402,6 +498,9 @@ function deleteMetric(index: number) {
|
|||||||
function addMetric() {
|
function addMetric() {
|
||||||
legend.metric.push({ name: "", condition: "", value: "" });
|
legend.metric.push({ name: "", condition: "", value: "" });
|
||||||
}
|
}
|
||||||
|
function setConfigType(type: string) {
|
||||||
|
configType.value = type;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.link-settings {
|
.link-settings {
|
||||||
|