feat: View data value related dashboards in TopList widgets (#425)

This commit is contained in:
Fine0830 2024-11-05 11:21:08 +08:00 committed by GitHub
parent 224e761d70
commit 14fa5d65b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 91 additions and 9 deletions

View File

@ -57,6 +57,16 @@ export const RespFields: Indexable = {
name: id name: id
value value
refId: traceID refId: traceID
owner {
scope
serviceID
serviceName
normal
serviceInstanceID
serviceInstanceName
endpointID
endpointName
}
} }
} }
error error

View File

@ -61,7 +61,7 @@ limitations under the License. -->
<Icon iconName="retry" :loading="appStore.autoRefresh" class="middle" /> <Icon iconName="retry" :loading="appStore.autoRefresh" class="middle" />
</span> </span>
<span class="version ml-5 cp"> <span class="version ml-5 cp">
<el-popover trigger="hover" width="250" placement="bottom" :content="appStore.version"> <el-popover trigger="hover" :width="250" placement="bottom" :content="appStore.version">
<template #reference> <template #reference>
<span> <span>
<Icon iconName="info_outline" size="middle" /> <Icon iconName="info_outline" size="middle" />

View File

@ -388,5 +388,7 @@ const msg = {
hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node", hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node",
valueMappings: "Value Mappings", valueMappings: "Value Mappings",
mappingTip: "Notice: The mapping key is a Regex string, e.g. ^([0-9])$", mappingTip: "Notice: The mapping key is a Regex string, e.g. ^([0-9])$",
valueDashboard: "Data Value Related Dashboard",
viewValueDashboard: "View Dashboard",
}; };
export default msg; export default msg;

View File

@ -388,5 +388,7 @@ const msg = {
hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node", hierarchyNodeDashboard: "As dashboard for Hierarchy Graph Node",
valueMappings: "Value Mappings", valueMappings: "Value Mappings",
mappingTip: "Aviso: La clave de mapeo es una cadena Regex, p. ej. ^([0-9])$", mappingTip: "Aviso: La clave de mapeo es una cadena Regex, p. ej. ^([0-9])$",
valueDashboard: "Data Value Related Dashboard",
viewValueDashboard: "View Dashboard",
}; };
export default msg; export default msg;

View File

@ -386,5 +386,7 @@ const msg = {
hierarchyNodeDashboard: "作为层次图节点的dashboard", hierarchyNodeDashboard: "作为层次图节点的dashboard",
valueMappings: "值映射", valueMappings: "值映射",
mappingTip: "注意: 映射键是一个正则表达式字符串,比如 ^([0-9])$", mappingTip: "注意: 映射键是一个正则表达式字符串,比如 ^([0-9])$",
valueDashboard: "数据值相关的仪表板",
viewValueDashboard: "查看仪表板",
}; };
export default msg; export default msg;

View File

@ -46,6 +46,7 @@ export interface LayoutConfig {
relatedTrace?: RelatedTrace; relatedTrace?: RelatedTrace;
subExpressions?: string[]; subExpressions?: string[];
subTypesOfMQE?: string[]; subTypesOfMQE?: string[];
valueRelatedDashboard?: string;
} }
export type RelatedTrace = { export type RelatedTrace = {
duration: DurationTime; duration: DurationTime;

View File

@ -38,6 +38,7 @@ limitations under the License. -->
typesOfMQE: typesOfMQE || [], typesOfMQE: typesOfMQE || [],
subExpressions: config.subExpressions || [], subExpressions: config.subExpressions || [],
subTypesOfMQE: config.subTypesOfMQE || [], subTypesOfMQE: config.subTypesOfMQE || [],
valueRelatedDashboard: config.valueRelatedDashboard,
}" }"
:needQuery="true" :needQuery="true"
/> />

View File

@ -41,6 +41,7 @@ limitations under the License. -->
typesOfMQE: dashboardStore.selectedGrid.typesOfMQE || [], typesOfMQE: dashboardStore.selectedGrid.typesOfMQE || [],
subExpressions: dashboardStore.selectedGrid.subExpressions || [], subExpressions: dashboardStore.selectedGrid.subExpressions || [],
subTypesOfMQE: dashboardStore.selectedGrid.subTypesOfMQE || [], subTypesOfMQE: dashboardStore.selectedGrid.subTypesOfMQE || [],
valueRelatedDashboard: dashboardStore.selectedGrid.valueRelatedDashboard,
}" }"
:needQuery="true" :needQuery="true"
@expressionTips="getErrors" @expressionTips="getErrors"

View File

@ -95,6 +95,20 @@ limitations under the License. -->
{{ type.label }} {{ type.label }}
</span> </span>
</div> </div>
<div v-if="states.isTopList" class="mt-10">
<div>{{ t("valueDashboard") }}</div>
<div>
<Selector
:value="states.valueRelatedDashboard || ''"
:options="states.dashboardList"
size="small"
placeholder="Please select a dashboard name"
@change="changeValueDashboard"
class="selectors"
:clearable="true"
/>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, computed } from "vue"; import { reactive, ref, computed } from "vue";
@ -140,21 +154,25 @@ limitations under the License. -->
metricTypes: string[]; metricTypes: string[];
metricTypeList: Option[][]; metricTypeList: Option[][];
isList: boolean; isList: boolean;
isTopList: boolean;
dashboardName: string; dashboardName: string;
dashboardList: ((DashboardItem & { label: string; value: string }) | any)[]; dashboardList: ((DashboardItem & { label: string; value: string }) | any)[];
tips: string[]; tips: string[];
subTips: string[]; subTips: string[];
valueRelatedDashboard: string;
}>({ }>({
metrics: metrics.value.length ? metrics.value : [""], metrics: metrics.value.length ? metrics.value : [""],
metricTypes: typesOfMQE.value.length ? typesOfMQE.value : [""], metricTypes: typesOfMQE.value.length ? typesOfMQE.value : [""],
metricTypeList: [], metricTypeList: [],
isList: false, isList: false,
isTopList: false,
dashboardName: graph.value.dashboardName, dashboardName: graph.value.dashboardName,
dashboardList: [{ label: "", value: "" }], dashboardList: [{ label: "", value: "" }],
tips: [], tips: [],
subTips: [], subTips: [],
subMetrics: subMetrics.value.length ? subMetrics.value : [""], subMetrics: subMetrics.value.length ? subMetrics.value : [""],
subMetricTypes: subMetricTypes.value.length ? subMetricTypes.value : [""], subMetricTypes: subMetricTypes.value.length ? subMetricTypes.value : [""],
valueRelatedDashboard: dashboardStore.selectedGrid.valueRelatedDashboard,
}); });
const currentMetricConfig = ref<MetricConfigOpt>({ const currentMetricConfig = ref<MetricConfigOpt>({
unit: "", unit: "",
@ -163,6 +181,7 @@ limitations under the License. -->
sortOrder: "DES", sortOrder: "DES",
}); });
states.isTopList = graph.value.type === ChartTypes[4].value;
states.isList = ListChartTypes.includes(graph.value.type); states.isList = ListChartTypes.includes(graph.value.type);
const defaultLen = ref<number>(states.isList ? 5 : 20); const defaultLen = ref<number>(states.isList ? 5 : 20);
@ -187,9 +206,10 @@ limitations under the License. -->
const arr = list.reduce((prev: (DashboardItem & { label: string; value: string })[], d: DashboardItem) => { const arr = list.reduce((prev: (DashboardItem & { label: string; value: string })[], d: DashboardItem) => {
if (d.layer === dashboardStore.layerId) { if (d.layer === dashboardStore.layerId) {
if ( if (
(d.entity === EntityType[0].value && chart === "ServiceList") || (d.entity === EntityType[0].value && chart === ChartTypes[8].value) ||
(d.entity === EntityType[2].value && chart === "EndpointList") || (d.entity === EntityType[2].value && chart === ChartTypes[9].value) ||
(d.entity === EntityType[3].value && chart === "InstanceList") (d.entity === EntityType[3].value && chart === ChartTypes[10].value) ||
states.isTopList
) { ) {
prev.push({ prev.push({
...d, ...d,
@ -254,9 +274,28 @@ limitations under the License. -->
typesOfMQE: states.metricTypes, typesOfMQE: states.metricTypes,
}); });
emit("update", params.source || {}); emit("update", params.source || {});
if (states.isTopList) {
const values: any = Object.values(params.source)[0];
if (!values) {
return;
}
states.dashboardList = states.dashboardList.filter((d) => d.entity === values[0].owner.scope);
}
} }
function changeDashboard(opt: any) { function changeValueDashboard(opt: { value: string }[]) {
if (!opt[0]) {
states.valueRelatedDashboard = "";
} else {
states.valueRelatedDashboard = opt[0].value;
}
dashboardStore.selectWidget({
...dashboardStore.selectedGrid,
valueRelatedDashboard: states.valueRelatedDashboard,
});
}
function changeDashboard(opt: { value: string }[]) {
if (!opt[0]) { if (!opt[0]) {
states.dashboardName = ""; states.dashboardName = "";
} else { } else {

View File

@ -61,6 +61,7 @@ limitations under the License. -->
typesOfMQE: typesOfMQE || [], typesOfMQE: typesOfMQE || [],
subExpressions: data.subExpressions || [], subExpressions: data.subExpressions || [],
subTypesOfMQE: data.subTypesOfMQE || [], subTypesOfMQE: data.subTypesOfMQE || [],
valueRelatedDashboard: data.valueRelatedDashboard,
}" }"
:needQuery="needQuery" :needQuery="needQuery"
@click="clickHandle" @click="clickHandle"

View File

@ -35,6 +35,9 @@ limitations under the License. -->
<div class="operation" @click="viewTrace(i)" v-show="refIdType === RefIdTypes[0].value"> <div class="operation" @click="viewTrace(i)" v-show="refIdType === RefIdTypes[0].value">
<span>{{ t("viewTrace") }}</span> <span>{{ t("viewTrace") }}</span>
</div> </div>
<div class="operation" @click="viewDashboard(i)">
<span>{{ t("viewValueDashboard") }}</span>
</div>
</el-popover> </el-popover>
</div> </div>
<el-progress <el-progress
@ -61,12 +64,14 @@ limitations under the License. -->
import type { PropType } from "vue"; import type { PropType } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import router from "@/router";
import { useDashboardStore } from "@/store/modules/dashboard";
import copy from "@/utils/copy"; import copy from "@/utils/copy";
import { TextColors } from "@/views/dashboard/data"; import { TextColors, MetricCatalog } from "@/views/dashboard/data";
import Trace from "@/views/dashboard/related/trace/Index.vue"; import Trace from "@/views/dashboard/related/trace/Index.vue";
import { WidgetType, QueryOrders, Status, RefIdTypes, ExpressionResultType } from "@/views/dashboard/data"; import { WidgetType, QueryOrders, Status, RefIdTypes, ExpressionResultType } from "@/views/dashboard/data";
/*global defineProps */ /*global defineProps, Recordable*/
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object as PropType<{ type: Object as PropType<{
@ -80,12 +85,14 @@ limitations under the License. -->
expressions: string[]; expressions: string[];
typesOfMQE: string[]; typesOfMQE: string[];
relatedTrace: any; relatedTrace: any;
valueRelatedDashboard: string;
}>, }>,
default: () => ({ color: "purple" }), default: () => ({ color: "purple" }),
}, },
intervalTime: { type: Array as PropType<string[]>, default: () => [] }, intervalTime: { type: Array as PropType<string[]>, default: () => [] },
}); });
const { t } = useI18n(); const { t } = useI18n();
const dashboardStore = useDashboardStore();
const showTrace = ref<boolean>(false); const showTrace = ref<boolean>(false);
const traceOptions = ref<{ type: string; filters?: unknown }>({ const traceOptions = ref<{ type: string; filters?: unknown }>({
type: WidgetType.Trace, type: WidgetType.Trace,
@ -122,6 +129,23 @@ limitations under the License. -->
}; };
showTrace.value = true; showTrace.value = true;
} }
function viewDashboard(item: Recordable) {
const { owner } = item;
let path;
if (owner.scope === MetricCatalog.SERVICE) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${props.config.valueRelatedDashboard}`;
}
if (owner.scope === MetricCatalog.SERVICE_INSTANCE) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${owner.serviceInstanceID}/${props.config.valueRelatedDashboard}`;
}
if (owner.scope === MetricCatalog.ENDPOINT) {
path = `/dashboard/${dashboardStore.layerId}/${owner.scope}/${owner.serviceID}/${owner.endpointID}/${props.config.valueRelatedDashboard}`;
}
if (!path) {
return;
}
router.push(path);
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.top-list { .top-list {
@ -187,11 +211,10 @@ limitations under the License. -->
} }
.operation { .operation {
padding: 5px 0; padding: 5px;
color: $font-color; color: $font-color;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
text-align: center;
font-size: $font-size-smaller; font-size: $font-size-smaller;
&:hover { &:hover {