mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-07-18 14:45:25 +00:00
feat: add operations for topology nodes
This commit is contained in:
parent
b85313d231
commit
9f77830a1a
@ -25,6 +25,7 @@ import { routesLog } from "./log";
|
|||||||
import { routesEvent } from "./event";
|
import { routesEvent } from "./event";
|
||||||
import { routesAlert } from "./alert";
|
import { routesAlert } from "./alert";
|
||||||
import { routesSetting } from "./setting";
|
import { routesSetting } from "./setting";
|
||||||
|
import { routesAlarm } from "./alarm";
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
...routesGen,
|
...routesGen,
|
||||||
@ -36,6 +37,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
...routesEvent,
|
...routesEvent,
|
||||||
...routesAlert,
|
...routesAlert,
|
||||||
...routesSetting,
|
...routesSetting,
|
||||||
|
...routesAlarm,
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
@ -26,12 +26,24 @@ limitations under the License. -->
|
|||||||
@click="setConfig"
|
@click="setConfig"
|
||||||
class="switch-icon"
|
class="switch-icon"
|
||||||
size="middle"
|
size="middle"
|
||||||
iconName="format_indent_decrease"
|
iconName="settings"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="operations-list"
|
||||||
|
v-if="topologyStore.node && topologyStore.node.isReal"
|
||||||
|
:style="{
|
||||||
|
top: operationsPos.y + 'px',
|
||||||
|
left: operationsPos.x + 'px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span v-for="(item, index) of items" :key="index" @click="item.func">
|
||||||
|
{{ item.title }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
|
import { ref, onMounted, onBeforeUnmount, watch, reactive } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import d3tip from "d3-tip";
|
import d3tip from "d3-tip";
|
||||||
@ -39,7 +51,6 @@ import zoom from "./utils/zoom";
|
|||||||
import { simulationInit, simulationSkip } from "./utils/simulation";
|
import { simulationInit, simulationSkip } from "./utils/simulation";
|
||||||
import nodeElement from "./utils/nodeElement";
|
import nodeElement from "./utils/nodeElement";
|
||||||
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
|
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
|
||||||
import tool from "./utils/tool";
|
|
||||||
import topoLegend from "./utils/legend";
|
import topoLegend from "./utils/legend";
|
||||||
import { Node, Call } from "@/types/topology";
|
import { Node, Call } from "@/types/topology";
|
||||||
import { useTopologyStore } from "@/store/modules/topology";
|
import { useTopologyStore } from "@/store/modules/topology";
|
||||||
@ -65,10 +76,16 @@ const node = ref<any>(null);
|
|||||||
const link = ref<any>(null);
|
const link = ref<any>(null);
|
||||||
const anchor = ref<any>(null);
|
const anchor = ref<any>(null);
|
||||||
const arrow = ref<any>(null);
|
const arrow = ref<any>(null);
|
||||||
const tools = ref<any>(null);
|
|
||||||
const legend = ref<any>(null);
|
const legend = ref<any>(null);
|
||||||
const showSetting = ref<boolean>(false);
|
const showSetting = ref<boolean>(false);
|
||||||
const settings = ref<any>({});
|
const settings = ref<any>({});
|
||||||
|
const operationsPos = reactive<{ x: number; y: number }>({ x: NaN, y: NaN });
|
||||||
|
const items = [
|
||||||
|
{ title: "Endpoint", func: handleGoEndpoint },
|
||||||
|
{ title: "Instance", func: handleGoInstance },
|
||||||
|
{ title: "Dashboard", func: handleGoDashboard },
|
||||||
|
{ title: "Alarm", func: handleGoAlarm },
|
||||||
|
];
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -98,21 +115,14 @@ onMounted(async () => {
|
|||||||
anchor.value = graph.value.append("g").selectAll(".topo-line-anchor");
|
anchor.value = graph.value.append("g").selectAll(".topo-line-anchor");
|
||||||
arrow.value = graph.value.append("g").selectAll(".topo-line-arrow");
|
arrow.value = graph.value.append("g").selectAll(".topo-line-arrow");
|
||||||
svg.value.call(zoom(d3, graph.value));
|
svg.value.call(zoom(d3, graph.value));
|
||||||
tools.value = tool(graph.value, tip.value, [
|
|
||||||
{ icon: "API", title: "Endpoint", func: handleGoEndpoint },
|
|
||||||
{ icon: "INSTANCE", title: "Instance", func: handleGoInstance },
|
|
||||||
{ icon: "TRACE", title: "Dashboard", func: handleGoDashboard },
|
|
||||||
{ icon: "ALARM", title: "Alarm", func: handleGoAlarm },
|
|
||||||
{ icon: "" },
|
|
||||||
{ icon: "" },
|
|
||||||
]);
|
|
||||||
// legend
|
// legend
|
||||||
legend.value = graph.value.append("g").attr("class", "topo-legend");
|
legend.value = graph.value.append("g").attr("class", "topo-legend");
|
||||||
topoLegend(legend.value, height.value, width.value);
|
topoLegend(legend.value, height.value, width.value);
|
||||||
svg.value.on("click", (event: any) => {
|
svg.value.on("click", (event: any) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
tools.value.attr("style", "display: none");
|
topologyStore.setNode(null);
|
||||||
|
showSetting.value = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
function ticked() {
|
function ticked() {
|
||||||
@ -156,13 +166,15 @@ function dragended(d: any) {
|
|||||||
simulation.value.alphaTarget(0);
|
simulation.value.alphaTarget(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleNodeClick(d: Node) {
|
function handleNodeClick(d: Node & { x: number; y: number }) {
|
||||||
topologyStore.setNode(d);
|
topologyStore.setNode(d);
|
||||||
topologyStore.setLink({});
|
topologyStore.setLink(null);
|
||||||
|
operationsPos.x = d.x;
|
||||||
|
operationsPos.y = d.y + 30;
|
||||||
}
|
}
|
||||||
function handleLinkClick(event: any, d: Call) {
|
function handleLinkClick(event: any, d: Call) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
topologyStore.setNode({});
|
topologyStore.setNode(null);
|
||||||
topologyStore.setLink(d);
|
topologyStore.setLink(d);
|
||||||
const path = `/dashboard/${dashboardStore.layerId}/${dashboardStore.entity}Relation/${d.source.id}/${d.target.id}/${settings.value.linkDashboard}`;
|
const path = `/dashboard/${dashboardStore.layerId}/${dashboardStore.entity}Relation/${d.source.id}/${d.target.id}/${settings.value.linkDashboard}`;
|
||||||
const routeUrl = router.resolve({ path });
|
const routeUrl = router.resolve({ path });
|
||||||
@ -175,7 +187,6 @@ function update() {
|
|||||||
node.value = nodeElement(
|
node.value = nodeElement(
|
||||||
d3,
|
d3,
|
||||||
node.value.enter(),
|
node.value.enter(),
|
||||||
tools.value,
|
|
||||||
{
|
{
|
||||||
dragstart: dragstart,
|
dragstart: dragstart,
|
||||||
dragged: dragged,
|
dragged: dragged,
|
||||||
@ -321,11 +332,32 @@ watch(
|
|||||||
background-color: #2b3037;
|
background-color: #2b3037;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border-radius: 4px;
|
border-radius: 3px;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
transition: all 0.5ms linear;
|
transition: all 0.5ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operations-list {
|
||||||
|
position: absolute;
|
||||||
|
padding: 10px;
|
||||||
|
color: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
height: 30px;
|
||||||
|
width: 100px;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
span:hover {
|
||||||
|
color: #217ef2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.switch-icon {
|
.switch-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 22px;
|
top: 22px;
|
||||||
|
@ -19,11 +19,8 @@ export const linkElement = (graph: any) => {
|
|||||||
const linkEnter = graph
|
const linkEnter = graph
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "topo-line")
|
.attr("class", "topo-line")
|
||||||
// .attr("stroke-width", (d: { cpm: number }) => (d.cpm ? 8 : 2))
|
|
||||||
.attr("marker-end", "url(#arrow)")
|
.attr("marker-end", "url(#arrow)")
|
||||||
.attr("stroke", (d: { cpm: number }) =>
|
.attr("stroke", "#217EF25f");
|
||||||
d.cpm ? "#217EF25f" : "#6a6d7777"
|
|
||||||
);
|
|
||||||
return linkEnter;
|
return linkEnter;
|
||||||
};
|
};
|
||||||
export const anchorElement = (graph: any, funcs: any, tip: any) => {
|
export const anchorElement = (graph: any, funcs: any, tip: any) => {
|
||||||
@ -31,7 +28,7 @@ export const anchorElement = (graph: any, funcs: any, tip: any) => {
|
|||||||
.append("circle")
|
.append("circle")
|
||||||
.attr("class", "topo-line-anchor")
|
.attr("class", "topo-line-anchor")
|
||||||
.attr("r", 5)
|
.attr("r", 5)
|
||||||
.attr("fill", (d: { cpm: number }) => (d.cpm ? "#217EF25f" : "#6a6d7777"))
|
.attr("fill", "#217EF25f")
|
||||||
.on("mouseover", function (event: unknown, d: unknown) {
|
.on("mouseover", function (event: unknown, d: unknown) {
|
||||||
tip.html(funcs.$tip).show(d, this);
|
tip.html(funcs.$tip).show(d, this);
|
||||||
})
|
})
|
||||||
@ -58,9 +55,6 @@ export const arrowMarker = (graph: any) => {
|
|||||||
.attr("orient", "auto");
|
.attr("orient", "auto");
|
||||||
const arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2";
|
const arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2";
|
||||||
|
|
||||||
arrow
|
arrow.append("path").attr("d", arrowPath).attr("fill", "#217EF25f");
|
||||||
.append("path")
|
|
||||||
.attr("d", arrowPath)
|
|
||||||
.attr("fill", (d: { cpm: number }) => (d.cpm ? "#217EF25f" : "#6a6d7777"));
|
|
||||||
return arrow;
|
return arrow;
|
||||||
};
|
};
|
||||||
|
@ -18,14 +18,7 @@ import icons from "@/assets/img/icons";
|
|||||||
import { Node } from "@/types/topology";
|
import { Node } from "@/types/topology";
|
||||||
|
|
||||||
icons["KAFKA-CONSUMER"] = icons.KAFKA;
|
icons["KAFKA-CONSUMER"] = icons.KAFKA;
|
||||||
export default (
|
export default (d3: any, graph: any, funcs: any, tip: any, t: any) => {
|
||||||
d3: any,
|
|
||||||
graph: any,
|
|
||||||
tool: any,
|
|
||||||
funcs: any,
|
|
||||||
tip: any,
|
|
||||||
t: any
|
|
||||||
) => {
|
|
||||||
const nodeEnter = graph
|
const nodeEnter = graph
|
||||||
.append("g")
|
.append("g")
|
||||||
.call(
|
.call(
|
||||||
@ -59,13 +52,7 @@ export default (
|
|||||||
.on("click", (event: any, d: Node | any) => {
|
.on("click", (event: any, d: Node | any) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
tool.attr("style", "display: none");
|
|
||||||
funcs.handleNodeClick(d);
|
funcs.handleNodeClick(d);
|
||||||
if (d.isReal) {
|
|
||||||
tool
|
|
||||||
.attr("transform", `translate(${d.x},${d.y - 20})`)
|
|
||||||
.attr("style", "display: block");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
nodeEnter
|
nodeEnter
|
||||||
.append("image")
|
.append("image")
|
||||||
|
@ -1,61 +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.
|
|
||||||
*/
|
|
||||||
import icons from "@/assets/img/icons";
|
|
||||||
import { Node } from "@/types/topology";
|
|
||||||
|
|
||||||
const Hexagon = (side: number, r: number, cx: number, cy: number) => {
|
|
||||||
let path = "";
|
|
||||||
for (let i = 0; i < side; i += 1) {
|
|
||||||
const x = Math.cos(((2 / side) * i + 1 / side) * Math.PI) * r + cx;
|
|
||||||
const y = -Math.sin(((2 / side) * i + 1 / side) * Math.PI) * r + cy;
|
|
||||||
path += !i ? `M${x},${y} ` : `L${x},${y} `;
|
|
||||||
if (i == side - 1) path += "Z";
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default (graph: any, tip: any, data: any) => {
|
|
||||||
const tool = graph.append("g").attr("class", "topo-tool");
|
|
||||||
const side = 6;
|
|
||||||
for (let i = 0; i < data.length; i += 1) {
|
|
||||||
const x = Math.cos((2 / side) * i * Math.PI) * 34;
|
|
||||||
const y = -Math.sin((2 / side) * i * Math.PI) * 34;
|
|
||||||
const tool_g = tool
|
|
||||||
.append("g")
|
|
||||||
.attr("class", "topo-tool-i")
|
|
||||||
.on("mouseover", function (event: any, d: Node) {
|
|
||||||
tip.html(() => data[i].title).show(d, this);
|
|
||||||
})
|
|
||||||
.on("mouseout", function () {
|
|
||||||
tip.hide(this);
|
|
||||||
})
|
|
||||||
.on("click", data[i].func);
|
|
||||||
tool_g
|
|
||||||
.append("path")
|
|
||||||
.attr("class", "tool-hexagon")
|
|
||||||
.attr("d", Hexagon(6, 17, x, y));
|
|
||||||
tool_g
|
|
||||||
.append("svg:image")
|
|
||||||
.attr("width", 14)
|
|
||||||
.attr("height", 14)
|
|
||||||
.attr("x", x - 7)
|
|
||||||
.attr("y", y - 7)
|
|
||||||
.attr("style", "opacity: 0.8")
|
|
||||||
.attr("xlink:href", icons[data[i].icon]);
|
|
||||||
}
|
|
||||||
return tool;
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user