左屏服务器运行流程图
@ -11,6 +11,8 @@
|
|||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@antv/x6": "^2.18.1",
|
||||||
|
"@antv/x6-vue-shape": "^2.1.2",
|
||||||
"@turf/turf": "^7.1.0",
|
"@turf/turf": "^7.1.0",
|
||||||
"@visactor/vtable": "^1.13.2",
|
"@visactor/vtable": "^1.13.2",
|
||||||
"@visactor/vtable-gantt": "^1.13.2",
|
"@visactor/vtable-gantt": "^1.13.2",
|
||||||
@ -24,6 +26,7 @@
|
|||||||
"chroma-js": "^3.1.2",
|
"chroma-js": "^3.1.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
|
"elkjs": "^0.10.0",
|
||||||
"es-toolkit": "^1.32.0",
|
"es-toolkit": "^1.32.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"maplibre-gl": "^5.0.1",
|
"maplibre-gl": "^5.0.1",
|
||||||
|
BIN
public/images/topology/a.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/topology/arrow-bottom.png
Normal file
After Width: | Height: | Size: 512 B |
BIN
public/images/topology/arrow-left.png
Normal file
After Width: | Height: | Size: 553 B |
BIN
public/images/topology/com.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/images/topology/cpu.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
public/images/topology/duankai.png
Normal file
After Width: | Height: | Size: 918 B |
BIN
public/images/topology/fwq.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
public/images/topology/hezi.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
public/images/topology/xinhao.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/images/topology/yp.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/images/topology/zhuji.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
1512
public/json/topology.json
Normal file
BIN
src/assets/image/topology/cpuicon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
src/assets/image/topology/neicun.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/assets/image/topology/topology_bg.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
src/assets/image/topology/ypkj.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
@ -46,6 +46,11 @@ const router = createRouter({
|
|||||||
name: 'Test',
|
name: 'Test',
|
||||||
component: () => import('@/views/Test/index.vue'),
|
component: () => import('@/views/Test/index.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/TopologyMap',
|
||||||
|
name: 'TopologyMap',
|
||||||
|
component: () => import('@/views/TopologyMap/index.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
97
src/views/TopologyMap/components/dialogNode.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dialog" :style="{ width: width + 'px' }" v-if="id.indexOf('cpu') != -1">
|
||||||
|
<span class="arrow-bottom" :style="{ left: (width - 17) / 2 + 'px' }"></span>
|
||||||
|
<p>CPU:{{ cpu }}</p>
|
||||||
|
<p>内存:{{ neicun }}</p>
|
||||||
|
<p>硬盘:{{ yingpan }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="dialog" :style="{ width: width + 'px' }" v-if="id.indexOf('yp2') != -1">
|
||||||
|
<span class="arrow-left" :style="{ top: (height - 17) / 2 + 'px' }"></span>
|
||||||
|
<p>数据:{{ shuju }}</p>
|
||||||
|
<p>CPU:{{ cpu }}</p>
|
||||||
|
<p>内存:{{ neicun }}</p>
|
||||||
|
<p>硬盘:{{ yingpan }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="dialog" :style="{ width: width + 'px' }" v-if="id.indexOf('yp1') != -1">
|
||||||
|
<span class="arrow-bottom" :style="{ left: (width - 17) / 2 + 'px' }"></span>
|
||||||
|
<p>数据:{{ shuju }}</p>
|
||||||
|
<p>CPU:{{ cpu }}</p>
|
||||||
|
<p>内存:{{ neicun }}</p>
|
||||||
|
<p>硬盘:{{ yingpan }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent, toRefs, reactive, onMounted } from 'vue'
|
||||||
|
// import { useRoute, useRouter } from 'vue-router';
|
||||||
|
export default defineComponent({
|
||||||
|
name: '',
|
||||||
|
inject: ['getNode'],
|
||||||
|
components: {},
|
||||||
|
setup() {
|
||||||
|
// const route = useRoute();
|
||||||
|
// const router = useRouter();
|
||||||
|
const state = reactive({
|
||||||
|
id: "",
|
||||||
|
"width": 70,
|
||||||
|
"height": 55,
|
||||||
|
"cpu": "80%",
|
||||||
|
"neicun": "70%",
|
||||||
|
"yingpan": "50%",
|
||||||
|
"shuju": "128G/520G"
|
||||||
|
})
|
||||||
|
const getNode: Function | undefined = inject('getNode');
|
||||||
|
onMounted(() => {
|
||||||
|
const node = getNode();
|
||||||
|
let info = node.getData();
|
||||||
|
if (node) {
|
||||||
|
console.log(info, 'info')
|
||||||
|
state.id = info.id;
|
||||||
|
state.width = info.width;
|
||||||
|
state.height = info.height;
|
||||||
|
state.shuju = info.shuju;
|
||||||
|
state.cpu = info.cpu;
|
||||||
|
state.neicun = info.neicun;
|
||||||
|
state.yingpan = info.yingpan;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { ...toRefs(state) };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang='scss'>
|
||||||
|
.dialog {
|
||||||
|
background: #001232;
|
||||||
|
border: 0.4px solid rgba(22, 238, 243, 1);
|
||||||
|
box-shadow: inset 0px 0px 13px 0px rgba(22, 238, 243, 1);
|
||||||
|
border-radius: 4px 4px 4px 0px 0px 0px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: relative;
|
||||||
|
padding: 3px 5px;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
.arrow-bottom {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
width: 17px;
|
||||||
|
height: 8px;
|
||||||
|
bottom: -7px;
|
||||||
|
background: url("./images/topology/arrow-bottom.png") no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-left {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 17px;
|
||||||
|
left: -7px;
|
||||||
|
background: url("./images/topology/arrow-left.png") no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #16EEF3;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
886
src/views/TopologyMap/index.vue
Normal file
@ -0,0 +1,886 @@
|
|||||||
|
<template>
|
||||||
|
<div class="topology-map">
|
||||||
|
<div class="top">
|
||||||
|
<div class="left"><span>互联网</span></div>
|
||||||
|
<div class="center"><span>办公网</span></div>
|
||||||
|
<div class="right"><span>高密网</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div id="container"></div>
|
||||||
|
<TeleportContainer />
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div class="left">
|
||||||
|
<i class="triangle"></i>
|
||||||
|
<i class="rectangle"></i>
|
||||||
|
<div class="b-con">
|
||||||
|
<div class="b-left yingpan">
|
||||||
|
<span>硬盘空间</span>
|
||||||
|
</div>
|
||||||
|
<div class="b-right">
|
||||||
|
<div class="box">
|
||||||
|
<p class="text">服务器1</p>
|
||||||
|
<p class="line"><i class="jd" :style="{ 'width': 127 / 1942 * 100 + '%' }"></i></p>
|
||||||
|
<p class="text">1942T<span class="yy">已用127T</span></p>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<p class="text">服务器2</p>
|
||||||
|
<p class="line"><i class="jd" :style="{ 'width': 127 / 1942 * 100 + '%' }"></i></p>
|
||||||
|
<p class="text">1942T<span class="yy">已用127T</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<i class="triangle"></i>
|
||||||
|
<i class="rectangle"></i>
|
||||||
|
<div class="b-con">
|
||||||
|
<div class="b-left neicun">
|
||||||
|
<span>内存</span>
|
||||||
|
</div>
|
||||||
|
<div class="b-right">
|
||||||
|
<div class="box">
|
||||||
|
<p class="text">服务器1</p>
|
||||||
|
<p class="line"><i class="jd" :style="{ 'width': 27 / 194 * 100 + '%' }"></i></p>
|
||||||
|
<p class="text">194T<span class="yy">已用27T</span></p>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<p class="text">服务器2</p>
|
||||||
|
<p class="line"><i class="jd" :style="{ 'width': 89 / 123 * 100 + '%' }"></i></p>
|
||||||
|
<p class="text">123T<span class="yy">已用89T</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<i class="triangle"></i>
|
||||||
|
<i class="rectangle"></i>
|
||||||
|
<div class="b-con">
|
||||||
|
<div class="b-left cpu">
|
||||||
|
<span>CPU</span>
|
||||||
|
</div>
|
||||||
|
<div class="b-right chart-box">
|
||||||
|
<div ref="chart1Ref" class="chart"></div>
|
||||||
|
<div ref="chart2Ref" class="chart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import { defineComponent, toRefs, reactive, onMounted } from 'vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
import DialogNode from './components/dialogNode.vue';
|
||||||
|
import { Graph, Cell } from '@antv/x6'
|
||||||
|
import { register, getTeleport } from '@antv/x6-vue-shape'
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
Graph.registerNode('elk-node',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'body',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'rgba(255,255,255,0.02)',
|
||||||
|
stroke: 'rgba(142,255,253,0.25)',
|
||||||
|
strokeWidth: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('rect-node',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
strokeWidth: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
name: 'boundary',
|
||||||
|
args: {
|
||||||
|
padding: 5,
|
||||||
|
attrs: {
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'rgba(142,248,255,0.25)',
|
||||||
|
strokeWidth: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-a',
|
||||||
|
{
|
||||||
|
width: 48,
|
||||||
|
height: 39,
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'body',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'rgba(255,255,255,0)',
|
||||||
|
strokeWidth: 0
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/a.png", // 设置图片路径
|
||||||
|
width: 48,
|
||||||
|
height: 39,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-cpu',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// tagName: 'rect',
|
||||||
|
// selector: 'bg',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// tagName: 'text',
|
||||||
|
// selector: 'label',
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/cpu.png", // 设置图片路径
|
||||||
|
width: 74,
|
||||||
|
height: 86,
|
||||||
|
},
|
||||||
|
// bg: {
|
||||||
|
// stroke: '#5F95FF',
|
||||||
|
// strokeWidth: 1,
|
||||||
|
// fill: 'rgba(0,0,0,0.5)',
|
||||||
|
// refWidth: -10,
|
||||||
|
// refHeight: -20,
|
||||||
|
// rx: 5,
|
||||||
|
// ry: 5,
|
||||||
|
// refX: 5,
|
||||||
|
// refY: -80,
|
||||||
|
// },
|
||||||
|
// label: {
|
||||||
|
// refX: 20,
|
||||||
|
// refY: -70,
|
||||||
|
// fontSize: 10,
|
||||||
|
// fill: '#fff',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-fs',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/fs.png", // 设置图片路径
|
||||||
|
width: 48,
|
||||||
|
height: 86,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-fwq',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/fwq.png", // 设置图片路径
|
||||||
|
width: 36,
|
||||||
|
height: 86,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-yp',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/yp.png", // 设置图片路径
|
||||||
|
width: 48,
|
||||||
|
height: 86,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-com',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/com.png", // 设置图片路径
|
||||||
|
width: 64,
|
||||||
|
height: 52,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-zhuji',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/zhuji.png", // 设置图片路径
|
||||||
|
width: 53,
|
||||||
|
height: 81,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-xinhao',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'body',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'rgba(255,255,255,0)',
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/xinhao.png", // 设置图片路径
|
||||||
|
width: 27,
|
||||||
|
height: 28,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
Graph.registerNode('node-hezi',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'body',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'rgba(255,255,255,0)',
|
||||||
|
strokeWidth: 0
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/hezi.png", // 设置图片路径
|
||||||
|
width: 55,
|
||||||
|
height: 56,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
Graph.registerEdge('elk-edge',
|
||||||
|
{
|
||||||
|
inherit: 'edge',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'path',
|
||||||
|
selector: 'p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
p1: {
|
||||||
|
connection: true,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: '#16EEF3',
|
||||||
|
strokeWidth: 1,
|
||||||
|
targetMarker: {
|
||||||
|
name: 'block',
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
img: {
|
||||||
|
'xlink:href': "./images/topology/duankai.png", // 设置图片路径
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
r: 10,
|
||||||
|
y: -8,
|
||||||
|
atConnectionRatio: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// attrs: {
|
||||||
|
// line: {
|
||||||
|
// stroke: '#16EEF3',
|
||||||
|
// strokeWidth: 1,
|
||||||
|
// targetMarker: {
|
||||||
|
// name: 'block',
|
||||||
|
// width: 6,
|
||||||
|
// height: 6,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
register({
|
||||||
|
shape: 'node-dialog',
|
||||||
|
component: DialogNode,
|
||||||
|
zIndex: 15,
|
||||||
|
})
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
let graph: Graph;
|
||||||
|
const TeleportContainer = getTeleport()
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: '',
|
||||||
|
components: { TeleportContainer },
|
||||||
|
setup() {
|
||||||
|
// const route = useRoute();
|
||||||
|
// const router = useRouter();
|
||||||
|
// const elk = new ELK()
|
||||||
|
const portIdToNodeIdMap: Record<string, string> = {}
|
||||||
|
const cells: Cell[] = []
|
||||||
|
const chart1Ref = ref(null);
|
||||||
|
const chart2Ref = ref(null);
|
||||||
|
const state = reactive({
|
||||||
|
json: [],
|
||||||
|
|
||||||
|
})
|
||||||
|
onMounted(async () => {
|
||||||
|
graph = new Graph({
|
||||||
|
container: document.getElementById('container'),
|
||||||
|
width: 1795,
|
||||||
|
height: 700,
|
||||||
|
// background: {
|
||||||
|
// color: 'rgba(255, 255, 255, 1)',
|
||||||
|
// },
|
||||||
|
router: { name: 'manhattan' },
|
||||||
|
connector: { name: 'rounded' },
|
||||||
|
// interacting: {
|
||||||
|
// nodeMovable: false,
|
||||||
|
// edgeMovable: false,
|
||||||
|
// },
|
||||||
|
translating: {
|
||||||
|
restrict(view) {
|
||||||
|
if (view) {
|
||||||
|
const cell = view.cell
|
||||||
|
if (cell.isNode()) {
|
||||||
|
const parent = cell.getParent()
|
||||||
|
if (parent) {
|
||||||
|
return parent.getBBox()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await getData().then(data => {
|
||||||
|
addChildren(data.children || [])
|
||||||
|
addEdges(data.edges || [])
|
||||||
|
graph.zoomTo(1)
|
||||||
|
// graph.centerContent()
|
||||||
|
})
|
||||||
|
|
||||||
|
getCharts();
|
||||||
|
})
|
||||||
|
|
||||||
|
const addChildren = (children, pos?: Position) => {
|
||||||
|
let leftParentNode = graph.addNode({
|
||||||
|
shape: 'elk-node',
|
||||||
|
id: "left",
|
||||||
|
label: "互联网",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
size: {
|
||||||
|
"width": 347,
|
||||||
|
"height": 666,
|
||||||
|
},
|
||||||
|
zIndex: 1,
|
||||||
|
})
|
||||||
|
let centerParentNode = graph.addNode({
|
||||||
|
shape: 'elk-node',
|
||||||
|
id: "center",
|
||||||
|
label: "办公网",
|
||||||
|
"x": 366,
|
||||||
|
"y": 0,
|
||||||
|
size: {
|
||||||
|
"width": 595,
|
||||||
|
"height": 666,
|
||||||
|
},
|
||||||
|
zIndex: 1,
|
||||||
|
})
|
||||||
|
let rightParentNode = graph.addNode({
|
||||||
|
shape: 'elk-node',
|
||||||
|
id: "right",
|
||||||
|
label: "高密网",
|
||||||
|
"x": 981,
|
||||||
|
"y": 0,
|
||||||
|
size: {
|
||||||
|
"width": 814,
|
||||||
|
"height": 666,
|
||||||
|
},
|
||||||
|
zIndex: 1,
|
||||||
|
})
|
||||||
|
children.forEach((child) => {
|
||||||
|
const position = {
|
||||||
|
x: (child.x || 0) + (pos ? pos.x : 0),
|
||||||
|
y: (child.y || 0) + (pos ? pos.y : 0),
|
||||||
|
}
|
||||||
|
let label: string = ''
|
||||||
|
if (typeof child.labels === 'string') {
|
||||||
|
label = child.labels
|
||||||
|
} else if (Array.isArray(child.labels) && child.labels[0]) {
|
||||||
|
label = child.labels[0].text
|
||||||
|
}
|
||||||
|
let type = "elk-node"
|
||||||
|
if (child.type && child.type === 'a') {
|
||||||
|
type = 'node-a'
|
||||||
|
} else if (child.type && child.type === 'cpu') {
|
||||||
|
type = 'node-cpu'
|
||||||
|
} else if (child.type && child.type === 'fwq') {
|
||||||
|
type = 'node-fwq'
|
||||||
|
} else if (child.type && child.type === 'yp') {
|
||||||
|
type = 'node-yp'
|
||||||
|
} else if (child.type && child.type === 'com') {
|
||||||
|
type = 'node-com'
|
||||||
|
} else if (child.type && child.type === 'rect') {
|
||||||
|
type = 'rect-node'
|
||||||
|
} else if (child.type && child.type === 'zhuji') {
|
||||||
|
type = 'node-zhuji'
|
||||||
|
} else if (child.type && child.type === 'xinhao') {
|
||||||
|
type = 'node-xinhao'
|
||||||
|
} else if (child.type && child.type === 'hezi') {
|
||||||
|
type = 'node-hezi'
|
||||||
|
}
|
||||||
|
const node = graph.addNode({
|
||||||
|
shape: type,
|
||||||
|
id: child.id,
|
||||||
|
position,
|
||||||
|
label,
|
||||||
|
body: {
|
||||||
|
width: child.width || 0,
|
||||||
|
height: child.height || 0,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
width: child.width || 0,
|
||||||
|
height: child.height || 0,
|
||||||
|
},
|
||||||
|
zIndex: 2,
|
||||||
|
})
|
||||||
|
if (child.infos) {
|
||||||
|
console.log(child.infos)
|
||||||
|
const dialog = graph.createNode({
|
||||||
|
shape: 'node-dialog',
|
||||||
|
data: {
|
||||||
|
id: child.id,
|
||||||
|
...child.infos
|
||||||
|
},
|
||||||
|
x: child.infos.x || 0,
|
||||||
|
y: child.infos.y || 0,
|
||||||
|
zIndex: 3
|
||||||
|
})
|
||||||
|
node.addChild(dialog)
|
||||||
|
}
|
||||||
|
if (child.parentId === 'left') {
|
||||||
|
leftParentNode.addChild(node)
|
||||||
|
} else if (child.parentId === 'center') {
|
||||||
|
centerParentNode.addChild(node)
|
||||||
|
} else if (child.parentId === 'right') {
|
||||||
|
rightParentNode.addChild(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
cells.push(leftParentNode)
|
||||||
|
cells.push(centerParentNode)
|
||||||
|
cells.push(rightParentNode)
|
||||||
|
}
|
||||||
|
const addEdges = (edges) => {
|
||||||
|
edges.forEach((edge) => {
|
||||||
|
let edgeline = {}
|
||||||
|
let markup = []
|
||||||
|
if (edge.isTrue !== undefined && edge.isTrue === false) {
|
||||||
|
markup = [
|
||||||
|
{
|
||||||
|
tagName: 'path',
|
||||||
|
selector: 'p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'image',
|
||||||
|
selector: 'img',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
markup = [
|
||||||
|
{
|
||||||
|
tagName: 'path',
|
||||||
|
selector: 'p1',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (edge.vertices) {
|
||||||
|
edgeline = {
|
||||||
|
shape: 'elk-edge',
|
||||||
|
source: edge.sources,
|
||||||
|
target: edge.targets,
|
||||||
|
vertices: edge.vertices,
|
||||||
|
markup: markup,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
edgeline = {
|
||||||
|
shape: 'elk-edge',
|
||||||
|
source: edge.sources,
|
||||||
|
target: edge.targets,
|
||||||
|
router: {
|
||||||
|
name: 'er',
|
||||||
|
args: {
|
||||||
|
offset: 'center'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markup: markup,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.addEdge(edgeline)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
return axios.get("./json/topology.json").then(res => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return Promise.resolve(res.data)
|
||||||
|
} else {
|
||||||
|
console.log('获取TLE失败')
|
||||||
|
return Promise.reject(res.statusText)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCharts = () => {
|
||||||
|
const chart1 = echarts.init(chart1Ref.value);
|
||||||
|
const chart2 = echarts.init(chart2Ref.value);
|
||||||
|
let option1 = getOption('服务器1', 46)
|
||||||
|
let option2 = getOption('服务器2', 23)
|
||||||
|
|
||||||
|
// 设置配置项并渲染图表
|
||||||
|
chart1.setOption(option1);
|
||||||
|
chart2.setOption(option2);
|
||||||
|
}
|
||||||
|
const getOption = (title, num) => {
|
||||||
|
// 配置项
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: title,
|
||||||
|
left: 'center',
|
||||||
|
textStyle:
|
||||||
|
{
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['45%', '60%'], // 内外半径,形成环形图
|
||||||
|
center: ['50%', '60%'],
|
||||||
|
data: [
|
||||||
|
{// 背景部分
|
||||||
|
value: 100 - num, name: '未完成值',
|
||||||
|
itemStyle: { color: 'rgba(22, 238, 243,0.3)' },
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ // 值部分
|
||||||
|
value: num, name: '完成值',
|
||||||
|
itemStyle: { color: '#16EEF3' },
|
||||||
|
label: {
|
||||||
|
show: true, // 不显示标签
|
||||||
|
position: 'center',
|
||||||
|
fontSize: 16,
|
||||||
|
formatter: '{d}%',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
labelLine: {
|
||||||
|
show: false, // 不显示标签线
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
scale: false, // 禁用高亮放大效果
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...toRefs(state), chart1Ref, chart2Ref };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang='scss'>
|
||||||
|
.topology-map {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #082857;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.top {
|
||||||
|
width: 1795px;
|
||||||
|
display: flex;
|
||||||
|
padding-top: 52px;
|
||||||
|
margin: 0 auto 30px;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: 347px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
width: 595px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
width: 814px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left,
|
||||||
|
.center,
|
||||||
|
.right {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 262px;
|
||||||
|
height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 32px;
|
||||||
|
color: #fff;
|
||||||
|
background: url("@/assets/image/topology/topology_bg.png") no-repeat center center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: block;
|
||||||
|
min-width: 1800px;
|
||||||
|
// height: 666px;
|
||||||
|
// margin: 0 auto;
|
||||||
|
|
||||||
|
#container {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.left,
|
||||||
|
.center,
|
||||||
|
.right {
|
||||||
|
width: 477px;
|
||||||
|
height: 160px;
|
||||||
|
// border-top: 4px solid rgba(22, 238, 243, 0.6);
|
||||||
|
// border-bottom: 4px solid rgba(22, 238, 243, 0.3);
|
||||||
|
position: relative;
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.b-con {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.b-left {
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #16EEF3;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
margin-top: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.yingpan {
|
||||||
|
background: url("@/assets/image/topology/ypkj.png") no-repeat center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.neicun {
|
||||||
|
background: url("@/assets/image/topology/neicun.png") no-repeat center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cpu {
|
||||||
|
height: 135px;
|
||||||
|
background: url("@/assets/image/topology/cpuicon.png") no-repeat center bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-right {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 40px;
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin-top: 17px;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.yy {
|
||||||
|
color: #F5A623;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
display: block;
|
||||||
|
height: 5px;
|
||||||
|
background: rgba(22, 238, 243, 0.5);
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 2px 0;
|
||||||
|
|
||||||
|
.jd {
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 5px;
|
||||||
|
background: #16EEF3;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.chart-box {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 30px;
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
width: 50%;
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(22, 238, 243, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-right: 22px solid rgba(22, 238, 243, 1);
|
||||||
|
border-bottom: 22px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rectangle {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: rgba(22, 238, 243, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rectangle:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 0 solid transparent;
|
||||||
|
border-right: 50px solid #149eb5;
|
||||||
|
border-bottom: 25px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|