blob: eb281d80eef76e8e96d82d4f2e53a18eb4596ff4 [file] [log] [blame]
/**
* 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="micro-topo-chart"></div>
</template>
<script lang="js">
import * as d3 from 'd3';
import d3tip from 'd3-tip';
import zoom from './utils/zoom';
import { simulationInit, simulationSkip } from './utils/simulation';
import nodeElement from './utils/nodeElement';
import { linkElement, anchorElement } from './utils/linkElement';
import tool from './utils/tool';
export default {
props: {
current: {
type: Object,
default: () => ({}),
},
nodes: {
type: Array,
default: () => [],
},
links: {
type: Array,
default: () => [],
},
},
data() {
return {
height: 600,
simulation: '',
};
},
beforeDestroy() {
window.removeEventListener('resize', this.resize);
},
mounted() {
window.addEventListener('resize', this.resize);
this.svg = d3
.select(this.$el)
.append('svg')
.attr('class', 'topo-svg')
.attr('height', this.$el.clientHeight);
this.tip = d3tip()
.attr('class', 'd3-tip')
.offset([-8, 0]);
this.graph = this.svg.append('g').attr('class', 'topo-svg_graph');
this.graph.call(this.tip);
this.simulation = simulationInit(d3, this.nodes, this.links, this.ticked);
this.svg.call(zoom(d3, this.graph));
this.node = this.graph.append('g').selectAll('.topo-node');
this.link = this.graph.append('g').selectAll('.topo-line');
this.anchor = this.graph.append('g').selectAll('.topo-line-anchor');
this.tool = tool(this.graph, [
{icon: 'API', click: this.handleGoEndpoint},
{icon: 'INSTANCE', click: this.handleGoInstance},
{icon: 'TRACE', click: this.handleGoTrace},
{icon: 'ALARM', click: this.handleGoAlarm},
{icon: ''},
{icon: ''},
]);
this.svg.on('click', (d, i) => {
event.stopPropagation();
event.preventDefault();
this.$store.commit('rocketTopo/SET_NODE', {});
this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
this.tool.attr('style', 'display: none');
});
},
watch: {
nodes: 'update',
links: 'update',
},
methods: {
// alarm hexagon
handleGoAlarm() {
this.$emit('setDialog', 'alarm');
},
// trace hexagon
handleGoTrace() {
this.$emit('setDialog', 'trace');
},
// instace hexagon
handleGoInstance() {
this.$emit('setDialog', 'instance');
},
// endpoint hexagon
handleGoEndpoint() {
this.$store.dispatch('SELECT_SERVICE', {
service: { key: this.current.id, label: this.current.name },
duration: this.$store.getters.durationTime,
});
this.$emit('setDialog', 'endpoint');
},
handleNodeClick(d) {
this.$emit('setCurrent', { key: d.id, label: d.name });
const {x, y, vx, vy, fx, fy, index, ...rest} = d;
this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
this.$store.commit('rocketTopo/SET_NODE', rest);
},
handleLinkClick(d) {
event.stopPropagation();
this.$store.commit('rocketTopo/SET_NODE', {});
this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
this.$store.commit('rocketTopo/SET_MODE', d.detectPoints);
this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' :
'rocketTopo/GET_TOPO_CLIENT_INFO', { ...d, duration: this.$store.getters.durationTime });
this.$store.commit('rocketTopo/SET_CALLBACK', () => {
this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' :
'rocketTopo/GET_TOPO_CLIENT_INFO', { ...d, duration: this.$store.getters.durationTime });
});
},
resize() {
this.svg.attr('height', this.$el.clientHeight);
},
update() {
// node element
const that = this;
this.node = this.node.data(this.nodes, (d) => d.id);
this.node.exit().remove();
this.node = nodeElement(d3, this.node.enter(), this.tool, {
dragstart: this.dragstart,
dragged: this.dragged,
dragended: this.dragended,
handleNodeClick: this.handleNodeClick,
}, this.tip).merge(this.node);
// line element
this.link = this.link.data(this.links, (d) => d.id);
this.link.exit().remove();
this.link = linkElement(this.link.enter()).merge(this.link);
// anchorElement
this.anchor = this.anchor.data(this.links, (d) => d.id);
this.anchor.exit().remove();
this.anchor = anchorElement(this.anchor.enter(), {
handleLinkClick: this.handleLinkClick,
$tip: (data) =>
`
<div class="mb-5"><span class="grey">${this.$t('cpm')}: </span>${data.cpm}</div>
<div class="mb-5"><span class="grey">${this.$t('latency')}: </span>${data.latency}</div>
<div><span class="grey">${this.$t('detectPoint')}:</span>${data.detectPoints.join(' | ')}</div>
`,
}, this.tip).merge(this.anchor);
// force element
this.simulation.nodes(this.nodes);
this.simulation.force('link').links(this.links).id((d) => d.id);
simulationSkip(d3, this.simulation, this.ticked);
},
ticked() {
this.link.attr('d', (d) => `M${d.source.x} ${d.source.y} Q ${(d.source.x + d.target.x) / 2} ${(d.target.y + d.source.y) / 2 - 90} ${d.target.x} ${d.target.y}`);
this.anchor.attr('transform', (d) => `translate(${(d.source.x + d.target.x) / 2}, ${(d.target.y + d.source.y) / 2 - 45})`);
this.node.attr('transform', (d) => `translate(${d.x - 22},${d.y - 22})`);
},
dragstart(d) {
this.node._groups[0].forEach((g) => {
g.__data__.fx = g.__data__.x;
g.__data__.fy = g.__data__.y;
});
if (!d3.event.active) {
this.simulation.alphaTarget(0.1).restart();
}
d3.event.sourceEvent.stopPropagation();
},
dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
d.x = d.fx;
d.y = d.fy;
},
dragended() {
if (!d3.event.active) {
this.simulation.alphaTarget(0);
}
},
},
};
</script>
<style lang="scss">
.micro-topo-chart {
height: 100%;
width: 100%;
.topo-svg {
display: block;
width: 100%;
}
.topo-line {
stroke-linecap: round;
stroke-width: 1.3px !important;
stroke-dasharray: 13 7;
fill: none;
animation: topo-dash 1s linear infinite !important;
}
.topo-line-anchor {
cursor: pointer;
}
.topo-text {
font-family: 'Lato', 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
fill: #ddd;
font-size: 11px;
opacity: 0.8;
}
.topo-tool {
display: none;
}
.topo-tool-i {
cursor: pointer;
.tool-hexagon {
fill: #3f4450;
stroke: #217ef2;
stroke-width: 2;
stroke-opacity: 0.5;
}
&:hover {
.tool-hexagon {
stroke-opacity: 1;
}
}
}
}
@keyframes topo-dash {
from {
stroke-dashoffset: 20;
}
to {
stroke-dashoffset: 0;
}
}
</style>