feat: associate metrics with trace widget on dashboards (#174)

This commit is contained in:
Fine0830
2022-10-25 11:36:49 +08:00
committed by GitHub
parent 78f0096c00
commit eda44db0cd
33 changed files with 1041 additions and 192 deletions

View File

@@ -15,6 +15,27 @@ limitations under the License. -->
<template>
<div class="chart" ref="chartRef" :style="`height:${height};width:${width};`">
<div v-if="!available" class="no-data">No Data</div>
<div class="menus" v-show="visMenus" ref="menus">
<div class="tools" @click="associateMetrics">
{{ t("associateMetrics") }}
</div>
<div
class="tools"
@click="viewTrace"
v-if="props.relatedTrace && props.relatedTrace.enableRelate"
>
{{ t("viewTrace") }}
</div>
</div>
<el-drawer
v-model="showTrace"
size="100%"
:destroy-on-close="true"
:before-close="() => (showTrace = false)"
:append-to-body="true"
>
<Trace :data="traceOptions" />
</el-drawer>
</div>
</template>
<script lang="ts" setup>
@@ -28,15 +49,28 @@ import {
computed,
} from "vue";
import type { PropType } from "vue";
import { useI18n } from "vue-i18n";
import { EventParams } from "@/types/app";
import { Filters, RelatedTrace } from "@/types/dashboard";
import { useECharts } from "@/hooks/useEcharts";
import { addResizeListener, removeResizeListener } from "@/utils/event";
import Trace from "@/views/dashboard/related/trace/Index.vue";
import associateProcessor from "@/hooks/useAssociateProcessor";
/*global Nullable, defineProps, defineEmits*/
const emits = defineEmits(["select"]);
const { t } = useI18n();
const chartRef = ref<Nullable<HTMLDivElement>>(null);
const menus = ref<Nullable<HTMLDivElement>>(null);
const visMenus = ref<boolean>(false);
const { setOptions, resize, getInstance } = useECharts(
chartRef as Ref<HTMLDivElement>
);
const currentParams = ref<Nullable<EventParams>>(null);
const showTrace = ref<boolean>(false);
const traceOptions = ref<{ type: string; filters?: unknown }>({
type: "Trace",
});
const props = defineProps({
height: { type: String, default: "100%" },
width: { type: String, default: "100%" },
@@ -45,15 +79,10 @@ const props = defineProps({
default: () => ({}),
},
filters: {
type: Object as PropType<{
duration: {
startTime: string;
endTime: string;
};
isRange: boolean;
dataIndex?: number;
sourceId: string;
}>,
type: Object as PropType<Filters>,
},
relatedTrace: {
type: Object as PropType<RelatedTrace>,
},
});
const available = computed(
@@ -66,14 +95,29 @@ const available = computed(
onMounted(async () => {
await setOptions(props.option);
chartRef.value && addResizeListener(unref(chartRef), resize);
instanceEvent();
});
function instanceEvent() {
setTimeout(() => {
const instance = getInstance();
if (!instance) {
return;
}
instance.on("click", (params: unknown) => {
emits("select", params);
instance.on("click", (params: EventParams) => {
currentParams.value = params;
if (!menus.value || !chartRef.value) {
return;
}
visMenus.value = true;
const w = chartRef.value.getBoundingClientRect().width || 0;
if (w - params.event.offsetX > 125) {
menus.value.style.left = params.event.offsetX + "px";
} else {
menus.value.style.left = params.event.offsetX - 125 + "px";
}
menus.value.style.top = params.event.offsetY + 5 + "px";
});
document.addEventListener(
"click",
@@ -81,9 +125,7 @@ onMounted(async () => {
if (instance.isDisposed()) {
return;
}
instance.dispatchAction({
type: "hideTip",
});
visMenus.value = false;
instance.dispatchAction({
type: "updateAxisPointer",
currTrigger: "leave",
@@ -92,7 +134,13 @@ onMounted(async () => {
true
);
}, 1000);
});
}
function associateMetrics() {
emits("select", currentParams.value);
visMenus.value = true;
updateOptions();
}
function updateOptions() {
const instance = getInstance();
@@ -103,60 +151,26 @@ function updateOptions() {
return;
}
if (props.filters.isRange) {
const { eventAssociate } = associateProcessor(props);
const options = eventAssociate();
setOptions(options || props.option);
} else {
instance.dispatchAction({
type: "showTip",
type: "updateAxisPointer",
dataIndex: props.filters.dataIndex,
seriesIndex: 0,
});
}
}
function eventAssociate() {
if (!props.filters) {
return;
}
if (!props.filters.duration) {
return props.option;
}
if (!props.option.series[0]) {
return;
}
const list = props.option.series[0].data.map(
(d: (number | string)[]) => d[0]
);
if (!list.includes(props.filters.duration.endTime)) {
return;
}
const markArea = {
silent: true,
itemStyle: {
opacity: 0.3,
},
data: [
[
{
xAxis: props.filters.duration.startTime,
},
{
xAxis: props.filters.duration.endTime,
},
],
],
function viewTrace() {
const item = associateProcessor(props).traceFilters(currentParams.value);
traceOptions.value = {
...traceOptions.value,
filters: item,
};
const series = (window as any).structuredClone(props.option.series);
for (const [key, temp] of series.entries()) {
if (key === 0) {
temp.markArea = markArea;
}
}
const options = {
...props.option,
series,
};
return options;
showTrace.value = true;
visMenus.value = true;
}
watch(
@@ -170,6 +184,7 @@ watch(
}
let options;
if (props.filters && props.filters.isRange) {
const { eventAssociate } = associateProcessor(props);
options = eventAssociate();
}
setOptions(options || props.option);
@@ -201,4 +216,28 @@ onBeforeUnmount(() => {
.chart {
overflow: hidden;
}
.menus {
position: absolute;
display: block;
white-space: nowrap;
z-index: 9999999;
box-shadow: #ddd 1px 2px 10px;
transition: all cubic-bezier(0.075, 0.82, 0.165, 1) linear;
background-color: rgb(255, 255, 255);
border-radius: 4px;
color: rgb(51, 51, 51);
padding: 5px;
}
.tools {
padding: 5px;
color: #999;
cursor: pointer;
&:hover {
color: #409eff;
background-color: #eee;
}
}
</style>