阿里andv x6绘制拓扑图

JavaScript 2023-12-01 847

绘制效果

完整代码(有较为详细的注释,不分开讲解)

<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>

 

 

标签:JavaScript

文章评论

评论列表

已有0条评论