feat: update

This commit is contained in:
Fine 2024-01-07 17:28:50 +08:00
parent e556ae39ee
commit 2912a6c030
2 changed files with 14 additions and 176 deletions

View File

@ -703,7 +703,7 @@ limitations under the License. -->
}, },
); );
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.hierarchy-related { .hierarchy-related {
height: 100%; height: 100%;
overflow: auto; overflow: auto;

View File

@ -14,14 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div <div
ref="chart" class="hierarchy-services-topo"
class="micro-topo-chart"
v-loading="loading" v-loading="loading"
element-loading-background="rgba(0, 0, 0, 0)" element-loading-background="rgba(0, 0, 0, 0)"
:style="`height: ${height}px`" :style="`height: ${height}px`"
> >
<svg class="svg-topology" :width="width - 100" :height="height" @click="svgEvent"> <svg class="hierarchy-services-svg" :width="width - 100" :height="height" @click="svgEvent">
<g class="svg-graph" :transform="`translate(${diff[0]}, ${diff[1]})`"> <g class="hierarchy-services-graph" :transform="`translate(${diff[0]}, ${diff[1]})`">
<g <g
class="topo-node" class="topo-node"
v-for="(n, index) in topologyLayout.nodes" v-for="(n, index) in topologyLayout.nodes"
@ -63,7 +62,6 @@ limitations under the License. -->
:cy="(l.sourceY + l.targetY) / 2" :cy="(l.sourceY + l.targetY) / 2"
r="4" r="4"
fill="#97B0F8" fill="#97B0F8"
@click="handleLinkClick($event, l)"
@mouseover="showLinkTip($event, l)" @mouseover="showLinkTip($event, l)"
@mouseout="hideTip" @mouseout="hideTip"
/> />
@ -91,19 +89,19 @@ limitations under the License. -->
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from "vue"; import type { PropType } from "vue";
import { ref, onMounted, onBeforeUnmount, reactive, watch, computed, nextTick } from "vue"; import { ref, onMounted, onBeforeUnmount, watch, computed, nextTick } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import * as d3 from "d3"; import * as d3 from "d3";
import type { Node, Call } from "@/types/topology"; import type { Node, Call } from "@/types/topology";
import { useSelectorStore } from "@/store/modules/selectors"; import { useSelectorStore } from "@/store/modules/selectors";
import { useTopologyStore } from "@/store/modules/topology"; import { useTopologyStore } from "@/store/modules/topology";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { EntityType, MetricModes, CallTypes } from "../../../data"; import { EntityType, MetricModes } from "../../../data";
import router from "@/router"; import router from "@/router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import type { Service } from "@/types/selector"; import type { 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 type { MetricConfigOpt } from "@/types/dashboard"; import type { MetricConfigOpt } from "@/types/dashboard";
import { aggregation } from "@/hooks/useMetricsProcessor"; import { aggregation } from "@/hooks/useMetricsProcessor";
import icons from "@/assets/img/icons"; import icons from "@/assets/img/icons";
@ -129,11 +127,7 @@ limitations under the License. -->
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const svg = ref<Nullable<any>>(null); const svg = ref<Nullable<any>>(null);
const graph = ref<Nullable<any>>(null); const graph = ref<Nullable<any>>(null);
const chart = ref<Nullable<HTMLDivElement>>(null);
const showSetting = ref<boolean>(false);
const settings = ref<any>(props.config); const settings = ref<any>(props.config);
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
const items = ref<{ id: string; title: string; func: any; dashboard?: string }[]>([]);
const graphConfig = computed(() => props.config.graph || {}); const graphConfig = computed(() => props.config.graph || {});
const depth = ref<number>(graphConfig.value.depth || 2); const depth = ref<number>(graphConfig.value.depth || 2);
const topologyLayout = ref<any>({}); const topologyLayout = ref<any>({});
@ -156,8 +150,8 @@ limitations under the License. -->
}; };
height.value = dom.height - 40; height.value = dom.height - 40;
width.value = dom.width; width.value = dom.width;
svg.value = d3.select(".svg-topology"); svg.value = d3.select(".hierarchy-services-svg");
graph.value = d3.select(".svg-graph"); graph.value = d3.select(".hierarchy-services-graph");
loading.value = true; loading.value = true;
const json = await selectorStore.fetchServices(dashboardStore.layerId); const json = await selectorStore.fetchServices(dashboardStore.layerId);
if (json.errors) { if (json.errors) {
@ -180,21 +174,10 @@ limitations under the License. -->
} }
async function update() { async function update() {
if (settings.value.metricMode === MetricModes.Expression) {
topologyStore.queryNodeExpressions(settings.value.nodeExpressions || []);
topologyStore.getLinkExpressions(settings.value.linkClientExpressions || [], CallTypes.Client);
topologyStore.getLinkExpressions(settings.value.linkServerExpressions || [], CallTypes.Server);
} else {
topologyStore.queryNodeMetrics(settings.value.nodeMetrics || []);
topologyStore.getLinkClientMetrics(settings.value.linkClientMetrics || []);
topologyStore.getLinkServerMetrics(settings.value.linkServerMetrics || []);
}
window.addEventListener("resize", resize); window.addEventListener("resize", resize);
await initLegendMetrics(); await initLegendMetrics();
draw(); draw();
tooltip.value = d3.select("#tooltip"); tooltip.value = d3.select("#tooltip");
setNodeTools(settings.value.nodeDashboard);
} }
function computeLevels(calls: Call[], nodeList: Node[], levels: any[]) { function computeLevels(calls: Call[], nodeList: Node[], levels: any[]) {
@ -416,41 +399,7 @@ limitations under the License. -->
.html(tipHtml); .html(tipHtml);
} }
function showLinkTip(event: MouseEvent, data: Call) { function showLinkTip(event: MouseEvent, data: Call) {
const linkClientMetrics: string[] = const html = `<div><span class="grey">${t("detectPoint")}:</span>${data.detectPoints.join(" | ")}</div>`;
settings.value.metricMode === MetricModes.Expression
? settings.value.linkClientExpressions
: settings.value.linkClientMetrics || [];
const linkServerMetricConfig: MetricConfigOpt[] = settings.value.linkServerMetricConfig || [];
const linkClientMetricConfig: MetricConfigOpt[] = settings.value.linkClientMetricConfig || [];
const linkServerMetrics: string[] =
settings.value.metricMode === MetricModes.Expression
? settings.value.linkServerExpressions
: settings.value.linkServerMetrics || [];
const htmlServer = linkServerMetrics.map((m, index) => {
const metric = topologyStore.linkServerMetrics[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id,
);
if (metric) {
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
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: string, index: number) => {
const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
const metric = topologyStore.linkClientMetrics[m].values.find(
(val: { id: string; value: unknown }) => val.id === data.id,
);
if (metric) {
const v = aggregation(metric.value, opt);
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${v} ${opt.unit || ""}</div>`;
}
});
const html = [
...htmlServer,
...htmlClient,
`<div><span class="grey">${t("detectPoint")}:</span>${data.detectPoints.join(" | ")}</div>`,
].join(" ");
tooltip.value tooltip.value
.style("top", event.offsetY + "px") .style("top", event.offsetY + "px")
@ -467,69 +416,7 @@ limitations under the License. -->
hideTip(); hideTip();
topologyStore.setNode(d); topologyStore.setNode(d);
topologyStore.setLink(null); topologyStore.setLink(null);
operationsPos.x = event.offsetX; handleGoDashboard(d.name);
operationsPos.y = event.offsetY;
if (d.layer === String(dashboardStore.layerId)) {
setNodeTools(settings.value.nodeDashboard);
return;
}
items.value = [
{ id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alerting", title: "Alerting", func: handleGoAlerting },
];
}
function handleLinkClick(event: MouseEvent, d: Call) {
event.stopPropagation();
if (d.sourceObj.layer !== dashboardStore.layerId || d.targetObj.layer !== dashboardStore.layerId) {
return;
}
topologyStore.setNode(null);
topologyStore.setLink(d);
if (!settings.value.linkDashboard) {
return;
}
const origin = dashboardStore.entity;
const e = dashboardStore.entity === EntityType[1].value ? EntityType[0].value : dashboardStore.entity;
const { dashboard } = getDashboard({
name: settings.value.linkDashboard,
layer: dashboardStore.layerId,
entity: `${e}Relation`,
});
if (!dashboard) {
ElMessage.error(`The dashboard named ${settings.value.linkDashboard} doesn't exist`);
return;
}
dashboardStore.setEntity(dashboard.entity);
const path = `/dashboard/related/${dashboard.layer}/${e}Relation/${d.sourceObj.id}/${d.targetObj.id}/${dashboard.name}`;
const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin);
}
async function handleInspect() {
const id = topologyStore.node.id;
loading.value = true;
const resp = await topologyStore.getDepthServiceTopology([id], Number(depth.value));
loading.value = false;
if (resp && resp.errors) {
ElMessage.error(resp.errors);
}
await update();
topologyStore.setNode(null);
topologyStore.setLink(null);
}
function handleGoEndpoint(name: string) {
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[2].value}/${topologyStore.node.id}/${name}`;
const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin);
}
function handleGoInstance(name: string) {
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[3].value}/${topologyStore.node.id}/${name}`;
const routeUrl = router.resolve({ path });
window.open(routeUrl.href, "_blank");
dashboardStore.setEntity(origin);
} }
function handleGoDashboard(name: string) { function handleGoDashboard(name: string) {
const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${name}`; const path = `/dashboard/${dashboardStore.layerId}/${EntityType[0].value}/${topologyStore.node.id}/${name}`;
@ -544,22 +431,12 @@ limitations under the License. -->
window.open(routeUrl.href, "_blank"); window.open(routeUrl.href, "_blank");
} }
async function backToTopology() {
loading.value = true;
await freshNodes();
topologyStore.setNode(null);
topologyStore.setLink(null);
}
async function getTopology() { async function getTopology() {
const ids = selectorStore.services.map((d: Service) => d.id); const ids = selectorStore.services.map((d: Service) => d.id);
const serviceIds = dashboardStore.entity === EntityType[0].value ? [selectorStore.currentService.id] : ids; const serviceIds = dashboardStore.entity === EntityType[0].value ? [selectorStore.currentService.id] : ids;
const resp = await topologyStore.getDepthServiceTopology(serviceIds, Number(depth.value)); const resp = await topologyStore.getDepthServiceTopology(serviceIds, Number(depth.value));
return resp; return resp;
} }
function setConfig() {
showSetting.value = !showSetting.value;
dashboardStore.selectWidget(props.config);
}
function resize() { function resize() {
const dom = document.querySelector(".topology")?.getBoundingClientRect() || { const dom = document.querySelector(".topology")?.getBoundingClientRect() || {
height: 40, height: 40,
@ -568,45 +445,6 @@ limitations under the License. -->
height.value = dom.height - 40; height.value = dom.height - 40;
width.value = dom.width; width.value = dom.width;
} }
function updateSettings(config: any) {
settings.value = config;
setNodeTools(config.nodeDashboard);
}
function setNodeTools(nodeDashboard: any) {
items.value = [
{ id: "inspect", title: "Inspect", func: handleInspect },
{ id: "alerting", title: "Alerting", func: handleGoAlerting },
];
if (!(nodeDashboard && nodeDashboard.length)) {
return;
}
for (const item of nodeDashboard) {
if (item.scope === EntityType[0].value) {
items.value.push({
id: "dashboard",
title: "Service Dashboard",
func: handleGoDashboard,
...item,
});
}
if (item.scope === EntityType[2].value) {
items.value.push({
id: "endpoint",
title: "Endpoint Dashboard",
func: handleGoEndpoint,
...item,
});
}
if (item.scope === EntityType[3].value) {
items.value.push({
id: "instance",
title: "Service Instance Dashboard",
func: handleGoInstance,
...item,
});
}
}
}
function svgEvent() { function svgEvent() {
topologyStore.setNode(null); topologyStore.setNode(null);
topologyStore.setLink(null); topologyStore.setLink(null);
@ -637,8 +475,8 @@ limitations under the License. -->
}, },
); );
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.micro-topo-chart { .hierarchy-services-topo {
position: relative; position: relative;
overflow: auto; overflow: auto;
margin-top: 30px; margin-top: 30px;
@ -658,7 +496,7 @@ limitations under the License. -->
opacity: 0.9; opacity: 0.9;
} }
.svg-topology { .hierarchy-services-svg {
cursor: move; cursor: move;
background-color: $layout-background; background-color: $layout-background;
} }