效果展示

在这里插入图片描述

实现思路

  1. 桑基图实现的本质也是一条条source-target,节点与节点之间的连线
  2. 做到完全可配置化,需抽离出大量可配置的属性
  3. 无数据的节点要进行隐藏,因此需要加是否有数据的判断
  4. 节点颜色和线条颜色通过设置itemStyle和lineStyle来配置
  5. 分辨率自适应则需要设置函数来动态计算

实现代码

//now-size.js
let nowClientWidth = document.documentElement.clientWidth;
export function nowSize(val, initWidth = 1920) {
  return val * (nowClientWidth / initWidth);
}

在这里插入图片描述

<template>
  <div class="situation-visit-relation" ref="chart"></div>
</template>

<script>
import { nowSize } from "@/utils/now-size"
import { getAppRelation } from "@/service/situation-service"
import { PAGE_URL_WEBAPP } from "@/constant/page-url-constants"
export default {
  data() {
    return {
      PAGE_URL_WEBAPP,
      isLoading: false,
      linkMap: {},//连线数据
      itemMap: {},//节点-名称映射表
      itemList: {},//节点枚举列表
      colorMap: {},//连线颜色枚举列表
      labelMap: {},//节点-标签名映射表
      sourceList: {},//源节点枚举列表
      targetList: {},//目标节点枚举列表
      itemColorMap: {}//节点-颜色映射表
    }
  },
  mounted() {
    this.init();
  },
  methods: {
    // 初始化
    async init() {
      await getAppRelation().then(res => {
        this.itemMap = res.data.itemMap;
        this.labelMap = res.data.labelMap;
        this.linkMap = res.data.linkMap;
        this.sourceList = res.data.sourceList;
        this.targetList = res.data.targetList;
        this.itemList = res.data.itemList;
        this.colorMap = res.data.colorMap;
        this.itemColorMap = Object.assign(res.data.sourceColorMap, res.data.targetColorMap);
      }).catch(() => {
        this.$message.error("获取应用访问关系数据失败")
      });
      this.paintChart();
    },
    // 获取图表数据
    getChartData() {
      let chartData = {
        linkData: [],
        itemData: [],
      };
      let itemDataTmp = [];
      for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
          if (this.linkMap[`${this.sourceList[i]}2${this.targetList[j]}`] !== 0) {
            if (itemDataTmp.indexOf(this.sourceList[i]) === -1) {
              itemDataTmp.push(this.sourceList[i]);
            }
            if (itemDataTmp.indexOf(this.targetList[j]) === -1) {
              itemDataTmp.push(this.targetList[j]);
            }
            chartData.linkData.push({
              source: this.sourceList[i],
              target: this.targetList[j],
              value: this.linkMap[`${this.sourceList[i]}2${this.targetList[j]}`],
              lineStyle: {
                color: this.colorMap[`${this.sourceList[i]}2${this.targetList[j]}`],
              },
              tooltip: {
                show: true,
                formatter: (params) => {
                  return `${this.itemMap[params.data.target]}访问${this.itemMap[params.data.source]
                    }应用个数:${this.linkMap[
                      `${params.data.source}2${params.data.target}`
                    ]?.toLocaleString()}`;
                },
              },
            });
          }
        }
      }
      itemDataTmp.forEach((item) => {
        let index = this.itemList.indexOf(item);
        if (index !== -1) {
          chartData.itemData.push({
            name: item,
            itemStyle: {
              color: this.itemColorMap[item],
              borderColor: this.itemColorMap[item]
            },
            tooltip: {
              show: false,
            },
          });
        }
      });
      return chartData;
    },
    // 绘制图表
    paintChart() {
      let chartData = this.getChartData();
      let linkData = chartData.linkData;
      let itemData = chartData.itemData;
      let chartDom = this.$echarts.init(this.$refs.chart);
      if (chartDom) {
        let option = {
          tooltip: {
            show: true,
          },
          series: {
            type: "sankey",
            layout: "none",
            top: nowSize(75),
            left: nowSize(30),
            right: nowSize(100),
            nodeWidth: 15,
            draggable: false,
            emphasis: {
              focus: "adjacency",
            },
            label: {
              color: "#fff",
              formatter: (params) => {
                return this.labelMap[params.name];
              },
              fontSize: nowSize(12),
              size: nowSize(10)
            },
            data: itemData,
            links: linkData,
          },
        };
        chartDom && chartDom.setOption(option);
        chartDom.on('click', (params) => {
          let query = {};
          if (params.data.name) {
            if (params.data.name[0] === "v") {
              query = {
                tab: "list",
                visitDomains: this.labelMap[params.data.name],
              };
            } else {
              query = {
                tab: "list",
                deployDomains: this.labelMap[params.data.name],
              };
            }
            this.$linkTo({
              path: PAGE_URL_WEBAPP,
              query: query,
            });
          }
          if (params.data.source) {
            this.$linkTo({
              path: PAGE_URL_WEBAPP,
              query: {
                tab: "list",
                deployDomains: this.labelMap[params.data.source],
                visitDomains: this.labelMap[params.data.target],
              },
              type: "_blank"
            });
          }
        });
        let sizeFun = () => {
          chartDom.resize();
        };
        window.addEventListener("resize", sizeFun);
        this.$once("hook:beforeDestroy", function () {
          this.$echarts.dispose(chartDom);
        });
      }
    },
  }
}
</script>

<style lang="less">
.situation-visit-relation {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
}
</style>

Logo

前往低代码交流专区

更多推荐