绘制效果
完整代码(有较为详细的注释,不分开讲解)
<template>
<div class="operate-wrapper">
<div class="left">
<div id="container"></div>
</div>
<div class="right">
<div class="description">
<div class="desc-wrapper">
<div class="label border-green">本地云云互联连接</div>
<div class="label border-yellow">跨域专线连接</div>
<div class="label border-gray">互联网连接</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { Graph } from "@antv/x6"
import { onMounted, ref } from "vue"
import NetworkNode from "./components/NetworkNode.vue"
import RegionStateNode from "./components/RegionStateNode.vue"
import { register } from "@antv/x6-vue-shape"
import { deepClone, groupBy } from "mamba-ui/utils/common"
const nodeMap = ref<any>({})
// 节点数据
const list1 = ref([
{
label: "华东2-上海",
cloudType: "aliyun",
warn: 2,
info: 3,
// source 完整 为 region-1-0, 0表示list1的索引,方便后面添加更多的线
connList: [
{ source: "region-1", target: "network-0", edge: "green-line", padding: 20 },
{ source: "region-1", target: "network-1", edge: "yellow-line", padding: 10 },
{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 },
],
},
{
label: "华东1-杭州",
cloudType: "aliyun",
warn: 1,
info: 3,
connList: [{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 }],
},
{
label: "华东-上海一",
cloudType: "huawei",
warn: 0,
info: 3,
connList: [
{ source: "region-1", target: "network-0", edge: "green-line", padding: 20 },
{ source: "region-1", target: "network-1", edge: "yellow-line", padding: 10 },
{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 },
],
},
{
label: "华东-上海二",
cloudType: "huawei",
warn: 0,
info: 0,
connList: [
{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 },
],
},
{
label: "上海4(合营)",
cloudType: "huawei",
warn: 0,
info: 0,
connList: [{ source: "region-1", target: "network-2", edge: "gray-line", padding: 5 }],
},
])
// 网络数据
const list2 = ref([
{
label: "互联组GT32001云专网跨域互联",
prop: "kyhl",
connList: [{ source: "network-0", target: "region-2-0", edge: "yellow-line", padding: 10 }],
},
{
label: "互联组GT32001本地云云互联",
prop: "yyhl",
connList: [],
},
{
label: "互联网",
prop: "network",
connList: [
{ source: "network-2", target: "region-2-0", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-1", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-2", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-3", edge: "gray-line", padding: 5 },
],
},
])
// 节点数据(2)
const list3 = ref([
{ label: "内蒙3(合营)", cloudType: "ctyun", warn: 1, info: 3 },
{ label: "中卫(合营)", cloudType: "ctyun", warn: 0, info: 0 },
{ label: "华北-乌兰察", cloudType: "aliyun", warn: 0, info: 0 },
{ label: "华北-乌兰察", cloudType: "huawei", warn: 0, info: 0 },
])
// 连接关系(仅用于展示)
const edges = [
{ source: "region-1-0", target: "network-0", edge: "green-line", padding: 20 },
{ source: "region-1-0", target: "network-1", edge: "yellow-line", padding: 10 },
{ source: "region-1-0", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1-1", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1-2", target: "network-0", edge: "green-line", padding: 20 },
{ source: "region-1-2", target: "network-1", edge: "yellow-line", padding: 10 },
{ source: "region-1-2", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1-3", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1-3", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "region-1-4", target: "network-2", edge: "gray-line", padding: 5 },
{ source: "network-0", target: "region-2-0", edge: "yellow-line", padding: 10 },
{ source: "network-2", target: "region-2-0", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-1", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-2", edge: "gray-line", padding: 5 },
{ source: "network-2", target: "region-2-3", edge: "gray-line", padding: 5 },
]
const edgeList = ref<any>([])
// 对分组数据进行处理(核心)
const cleanGroup = (group: any, key: string, result: any[]) => {
Object.keys(group).forEach((value) => {
const list: any[] = group[value]
list.forEach((value1, index) => {
if (!value1[key]) {
// 判断edge是否相同
const list2 = deepClone(list).slice(0, index)
const idx = list2.findIndex((x) => x.edge === value1.edge)
if (idx !== -1) {
value1[key] = list2[idx][key]
} else {
value1[key] = index
}
result.push(value1)
}
})
})
}
// 对edges分组(核心)
const getGroupEdges = () => {
// source相同的,edge不同的赋予dx1不同,否则dx1相同
const list: any[] = []
list1.value.forEach((value, index) => {
const connList = value.connList
list.push(...connList.map((x) => Object.assign(x, { source: `${x.source}-${index}` })))
})
list2.value.forEach((value) => {
const connList = value.connList
list.push(...connList)
})
const copyEdges = deepClone(list)
const sourceGroup = groupBy(copyEdges, "source")
const copyEdges2: any = []
cleanGroup(sourceGroup, "dx1", copyEdges2)
// 当target相同时,edge不同时赋予dx2不同,否则dx2相同
const result: any[] = []
const targetGroup = groupBy(copyEdges2, "target")
cleanGroup(targetGroup, "dx2", result)
edgeList.value = result
console.log(targetGroup, result)
}
getGroupEdges()
// 注册节点
const registerNode = () => {
register({
shape: "region-state-node",
width: 200,
height: 100,
component: RegionStateNode,
})
register({
shape: "network-node",
width: 200,
height: 65,
component: NetworkNode,
})
}
const graphRef = ref<Graph>() // 画布实例
// 渲染节点
const renderNodeList = (list: any[], graph: Graph, y: number, type: string) => {
list.forEach((value, index) => {
const arr = Array(index).fill(200)
const x = arr.reduce((previousValue, currentValue) => previousValue + currentValue, 0) + 30 * (index + 1)
const node = graph.addNode({
shape: "region-state-node",
x,
y,
id: `${type}-${index}`,
data: {
label: value.label,
cloudType: value.cloudType,
shape: "region-state",
warn: value.warn,
info: value.info,
},
})
nodeMap.value[`${type}-${index}`] = node
})
}
const renderNodeList2 = (graph: Graph, y: number) => {
list2.value.forEach((value, index) => {
const arr = Array(index).fill(200)
const x = arr.reduce((previousValue, currentValue) => previousValue + currentValue, 0) + 150 * (index + 1)
const node = graph.addNode({
shape: "network-node",
x,
y,
id: "network-" + index,
data: {
label: value.label,
shape: value.prop,
},
})
nodeMap.value["network-" + index] = node
})
}
// 注册Edge
const registerEdge = () => {
const edges = [
{
color: "#68D897",
name: "green-line",
dx: 10,
},
{
color: "#FFD856",
name: "yellow-line",
dx: 20,
},
{
color: "#e5e5e5",
name: "gray-line",
dx: 30,
},
]
edges.forEach((value) => {
Graph.registerEdge(
value.name,
{
inherit: "edge",
router: {
name: "manhattan",
args: {},
},
attrs: {
line: {
stroke: value.color,
strokeWidth: 1,
targetMarker: null,
},
},
},
true
)
})
}
// 连接节点
const connNodes = (graph: Graph) => {
// 为了构建多个连接点
edgeList.value.forEach((value, index) => {
graph.addEdge({
shape: value.edge,
source: {
// 设置每个连接点间的距离
// 注意节点的距离需要不断调整,找到合适的位置
cell: nodeMap.value[value.source].id,
anchor: {
name: "bottom",
args: {
dx: 20 * value.dx1,
},
},
},
target: {
cell: nodeMap.value[value.target].id,
anchor: {
name: "top",
args: {
dx: 20 * value.dx2,
},
},
},
// 使用x6自带的曼哈顿路由
// https://x6.antv.antgroup.com/api/registry/router#manhattan
router: {
name: "manhattan",
args: {
padding: value.padding,
startDirections: ["bottom"], // 规定方向
endDirections: ["top"],
},
},
})
})
}
// 渲染画布
const renderGraph = () => {
// https://x6.antv.antgroup.com/api/graph/graph
const graph = new Graph({
container: document.querySelector("#container") as HTMLElement,
autoResize: true,
height: 450,
interacting: {
nodeMovable: false, // 禁止节点拖动
},
})
graphRef.value = graph
// 渲染节点
renderNodeList(list1.value, graph, 30, "region-1")
renderNodeList2(graph, 200)
renderNodeList(list3.value, graph, 320, "region-2")
// 节点连线
connNodes(graph)
setTimeout(() => {
graph.zoomToFit()
}, 500)
}
onMounted(() => {
registerEdge()
registerNode()
renderGraph()
})
</script>
<style scoped lang="scss">
.operate-wrapper {
display: flex;
position: relative;
width: 100%;
.left {
flex: 1;
}
.right {
position: absolute;
right: 0;
bottom: 0;
display: flex;
width: 120px;
padding-left: 20px;
padding-bottom: 20px;
font-size: 12px;
flex-direction: column;
justify-content: flex-end;
box-sizing: border-box;
}
}
.desc-wrapper {
.label {
padding-bottom: 5px;
margin-bottom: 10px;
}
}
.border-green {
border-bottom: 2px solid #68d897;
}
.border-yellow {
border-bottom: 2px solid #ffd856;
}
.border-gray {
border-bottom: 2px solid #e5e5e5;
}
</style>
评论列表
已有0条评论