实现企业关系图谱(visgraph)

208 阅读1分钟

js地址:

gitee.com/carljin/vue…

gitee.com/carljin/vue…

组件:

<template>
  <div class="graph">
    <div class="graph-panel" id="graph-panel" ref="graphPanelRef"></div>
  </div>
</template>

<script>
import { VisGraph } from "@/assets/GraphVis/visgraph.min.js";
import LayoutFactory from "@/assets/GraphVis/visgraph-layout.min.js";
import screenfull from "screenfull";
export default {
  name: "RelationshipGraph",
  components: {
    VisGraph,
    LayoutFactory,
  },
  props: {
    // 图谱数据
    graphData: {
      type: Object,
      default: null,
    },
    // 中心点id
    centerNodeId: {
      type: [String, Number],
      default: null,
    },
  },
  data() {
    return {
      visgraph: null,
      centerNode: {},
      currentNode: [],
      canvasConfig: {
        node: {
          label: {
            show: true,
            color: "250,250,250",
            font: "normal 14px Microsoft YaHei",
            textPosition: "Middle_Center",
            borderWidth: 0,
            wrapText: true,
          },
          shape: "circle",
          width: 60,
          height: 60,
          color: "62,160,250",
          borderColor: "62,160,250",
          borderWidth: 0,
          borderRadius: 0,
          lineDash: [0],
          alpha: 1,
          selected: {
            borderColor: "136,198,255",
            borderAlpha: 1,
            borderWidth: 3,
            showShadow: true,
            shadowColor: "136,198,255",
          },
          onClick: (event, node) => {
            this.$emit("canvasClick", { node });
          },
          ondblClick: (event, node) => {
            this.$emit("canvasDbClick", { node });
          },
          onMouseOver: (event, node) => {
            this.$emit("canvasMouseOver", { node });
          },
          onMouseOut: (event, node) => {
            this.$emit("canvasMouseOut", { node });
          },
        },
        link: {
          label: {
            show: true,
            color: "100,100,200",
            font: "normal 10px Arial",
          },
          lineType: "direct",
          colorType: "defined",
          color: "200,200,200",
          alpha: 1,
          lineWidth: 1,
          lineDash: [0],
          showArrow: true,
          selected: {
            color: "20,250,50",
            alpha: 1,
            lineWidth: 4,
            showShadow: true,
            shadowColor: "50,250,50",
          },
        },
        highLightNeiber: true,
        wheelZoom: 0.8,
        noElementClick: (event, _graphvis) => {
          // 点击画布其他位置,弹框隐藏
        },
      },
    };
  },
  methods: {
    /**
     * @description: 处理图谱数据
     * @return {*}
     */
    handleData(data) {
      const obj = {
        nodes: [],
        links: [],
      };
      data.nodes.forEach((item) => {
        const nodeObj = {
          id: item.id,
          label: item.label,
          properties: item,
          size: 60,
        };
        if (item.id === this.centerNodeId) {
          nodeObj.color = "38,186,191";
          nodeObj.selectedBorderColor = "131,218,228";
          nodeObj.shadowColor = "131,218,228";
          nodeObj.size = 80;
          nodeObj.x = 250;
          nodeObj.y = 250;
          this.centerNode = nodeObj;
          this.currentNode.push(item.id);
        } else {
          obj.nodes.push(nodeObj);
        }
      });
      if (!this.centerNodeId) {
        this.centerNode = {
          ...obj.nodes[0],
          color: "38,186,191",
          selectedBorderColor: "38,186,191",
          shadowColor: "38,186,191",
          size: 80,
          x: 250,
          y: 250,
        };
        this.currentNode = obj.nodes[0].id;
      }
      obj.links = data.edges.map((item) => {
        const linkObj = {
          id: item.id,
          target: item.to,
          source: item.from,
          label: item.label,
          properties: item,
          strokeColor: this.getRandomColor(),
        };
        switch (item.type) {
          case 11:
            linkObj.color = "40,194,199";
            linkObj.selectedColor = "40,194,199";
            linkObj.shadowColor = "40,194,199";
            break;
          case 12:
            linkObj.color = "250,108,100";
            linkObj.selectedColor = "250,108,100";
            linkObj.shadowColor = "250,108,100";
            break;
          case 13:
            linkObj.color = "0,132,255";
            linkObj.selectedColor = "0,132,255";
            linkObj.shadowColor = "0,132,255";
            break;
          case 15:
            linkObj.color = "250,108,100";
            linkObj.selectedColor = "250,108,100";
            linkObj.shadowColor = "250,108,100";
            break;
        }
        return linkObj;
      });
      this.buildData(obj);
    },
    /**
     * @description: 搭建实例
     * @param {*} gxData
     * @return {*}
     */
    buildData(gxData) {
      const visgraphRef = this.$refs["graphPanelRef"];
      this.visgraph = new VisGraph(visgraphRef, this.canvasConfig);
      const nodeCount = gxData.nodes.length;
      const xyArr = this.getXY(this.centerNode, nodeCount, nodeCount * 20);
      gxData.nodes.forEach((n, i) => {
        n.x = xyArr[i].x;
        n.y = xyArr[i].y;
        n.size = 60;
      });
      gxData.nodes.push(this.centerNode);
      this.visgraph.drawData(gxData);
      this.visgraph.setZoom();
    },
    /**
     * 遍布选中节点周围的点坐标
     * @date 2023-07-14
     * @param {中心节点} centerNode
     * @param {节点数量} nodeCount
     * @param {距离中心点距离} raduis
     * @returns {Array}
     */
    getXY(centerNode, nodeCount, raduis) {
      const aop = 360.0 / nodeCount;
      const arr = [];
      for (let i = 0; i < nodeCount; i++) {
        let r1 = raduis;
        if (nodeCount > 10) {
          r1 = i % 2 === 0 ? raduis + 35 : raduis - 35;
        } else if (nodeCount < 5) {
          r1 = 200;
        }
        const ao = i * aop;
        const o1 = {};
        o1.x = centerNode.x + r1 * Math.cos((ao * Math.PI) / 180);
        o1.y = centerNode.y + r1 * Math.sin((ao * Math.PI) / 180);
        arr[i] = o1;
      }
      return arr;
    },
    /**
     * @description: 以id寻找节点移动到中心
     * @param {*} id
     * @return {*}
     */
    moveCenterThisNode(id) {
      const node = this.visgraph.findNodeById(id);
      this.visgraph.moveNodeToCenter(node);
    },
    /**
     * @description: 动态添加节点
     * @param {*} graphData
     * @param {*} node
     * @return {*}
     */
    activeAddNodeLinks(graphData, node) {
      this.visgraph.restoreHightLight();
      this.currentNode.push(node.id);
      this.visgraph.activeAddNodeLinks(graphData.nodes, graphData.links);
      this.moveCenterThisNode(node.id);
    },
    /**
     * 随机获取颜色
     * @date 2021-07-20
     * @returns {String} 样式
     */
    getRandomColor() {
      const r = Math.floor(Math.random() * 256);
      const g = Math.floor(Math.random() * 256);
      const b = Math.floor(Math.random() * 256);
      return "rgb(" + r + "," + g + "," + b + ")";
    },
    /**
     * 颜色选择框变化赋值
     * @date 2021-07-26
     * @param {String} val
     * @param {String} kay
     */
    colorChange(val, key) {
      this.nodeConfigForm[key] = this.hexToRgba(val);
    },
    /**
     * 16进制色值转rgb
     * @date 2021-07-26
     * @param {String} hex
     * @returns {String}
     */
    hexToRgba(hex) {
      return (
        "rgb(" +
        parseInt("0x" + hex.slice(1, 3)) +
        "," +
        parseInt("0x" + hex.slice(3, 5)) +
        "," +
        parseInt("0x" + hex.slice(5, 7)) +
        ")"
      );
    },
    /**
     * rgb色值转16进制
     * @date 2021-07-26
     * @param {String} color
     * @returns {String}
     */
    rgbToHex(color) {
      const rgb = color.split(",");
      const r = parseInt(rgb[0].split("(")[1]);
      const g = parseInt(rgb[1]);
      const b = parseInt(rgb[2].split(")")[0]);
      const hex =
        "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
      return hex;
    },
    /**
     * 保存图片
     * @date 2021-07-23
     */
    saveImage() {
      this.visgraph.saveImage();
    },
    /**
     * 导出json
     * @date 2021-07-30
     */
    exportJson() {
      this.visgraph.exportJsonFile();
    },
    /**
     * 打开缩略图
     * @date 2021-07-23
     */
    showOverView() {
      this.showOverViewFlag = !this.showOverViewFlag;
      this.visgraph.showOverView(this.showOverView);
    },
    /**
     * 缩小操作
     * @date 2021-07-23
     */
    setZoomIn() {
      this.visgraph.setZoom("zoomIn");
    },
    /**
     * 放大操作
     * @date 2021-07-23
     */
    setZoomOut() {
      this.visgraph.setZoom("zoomOut");
    },
    /**
     * 顺时针旋转
     * @date 2021-07-23
     */
    clockwiseRotate() {
      this.visgraph.rotateGraph(-10);
    },
    /**
     * 逆时针旋转
     * @date 2021-07-23
     */
    counterclockwiseRotate() {
      this.visgraph.rotateGraph(10);
    },
    /**
     * 设置鼠标模式
     * @date 2021-07-23
     * @param {String} type  drag:拖动模式  select:框选模式   normal:正常模式
     */
    setMouseModel(type) {
      this.visgraph.setMouseModel(type);
    },
    /**
     * 全屏显示
     * @date 2021-07-23
     */
    fullScreen() {
      screenfull.request(this.$refs['graphPanelRef']);
    },
  },
};
</script>

<style lang="scss" scoped>
.graph {
  width: 100%;
  height: 100%;
  position: relative;

  &-panel {
    width: 100%;
    height: 100%;
  }
}
</style>

使用组件:

<template>
  <div id="container">
    <relationship-graph
      ref="relationshipGraph"
      :graphData="graphData"
      :centerNodeId="20"
      @canvasDbClick="canvasDbClick"
    />
  </div>
</template>

<script>
import RelationshipGraph from "@/components/relationship-graph";
export default {
  name: "Demo",
  components: {
    RelationshipGraph,
  },
  data() {
    return {
      graphData: {
        edges: [
          {
            from: 20,
            id: 24,
            label: "股东",
            to: 57,
          },
          {
            from: 20,
            id: 25,
            label: "股东",
            to: 58,
          },
          {
            from: 20,
            id: 35,
            label: "100.000000",
            to: 81,
          },
          {
            from: 20,
            id: 27,
            label: "股东",
            to: 59,
          },
          {
            from: 20,
            id: 30,
            label: "高管",
            to: 21,
          },
          {
            from: 20,
            id: 36,
            label: "100.000000",
            to: 82,
          },
          {
            from: 20,
            id: 37,
            label: "60.000000",
            to: 83,
            type: 15,
          },
          {
            from: 20,
            id: 32,
            label: "高管",
            to: 62,
          },
          {
            from: 20,
            id: 33,
            label: "高管",
            to: 63,
          },
          {
            from: 20,
            id: 31,
            label: "高管",
            to: 58,
          },
          {
            from: 20,
            id: 5,
            label: "法人",
            to: 21,
          },
          {
            from: 20,
            id: 26,
            label: "股东",
            to: 21,
          },
          {
            from: 20,
            id: 28,
            label: "股东",
            to: 60,
          },
        ],
        nodes: [
          {
            id: 83,
            label: "子公司1",
          },
          {
            id: 81,
            label: "子公司2",
          },
          {
            id: 21,
            label: "法人1",
          },
          {
            id: 57,
            label: "合伙1",
          },
          {
            id: 59,
            label: "法人2",
          },
          {
            id: 62,
            label: "法人3",
          },
          {
            id: 20,
            label: "总公司",
          },
          {
            id: 63,
            label: "法人4",
          },
          {
            id: 82,
            label: "子公司3",
          },
          {
            id: 58,
            label: "法人5",
          },
          {
            id: 60,
            label: "法人6",
          },
        ],
      },
    };
  },
  mounted() {
    this.$refs["relationshipGraph"].handleData(this.graphData);
  },
  methods: {
    canvasDbClick({ node }) {
      console.log(node);
      const findNodeNum = Math.round(Math.random() * 50);
      const increamData = this.buildIncreamData(node, findNodeNum);
      this.$refs["relationshipGraph"].activeAddNodeLinks(increamData, node)
    },
    buildIncreamData(centerNode, nodeNum) {
      const gxData = {
        nodes: [],
        links: [],
      };
      const count = nodeNum;
      const nodeIdPrefix = "node_" + Math.round(Math.random() * 1000) + "_";
      for (let i = 0; i < count; i++) {
        gxData.nodes.push({
          id: nodeIdPrefix + i,
          label: "子节点+" + i,
          size: 60,
          color: this.getRandomColor(),
        });
        gxData.links.push({
          source: centerNode.id,
          target: nodeIdPrefix + i,
          label: "关系" + i,
        });
      }
      return gxData;
    },
    getRandomColor() {
      const r = Math.floor(Math.random() * 256);
      const g = Math.floor(Math.random() * 256);
      const b = Math.floor(Math.random() * 256);
      return "rgb(" + r + "," + g + "," + b + ")";
    },
  },
};
</script>

<style lang="scss" scoped>
#container {
  width: 100%;
  height: 100%;
}
</style>

最终效果:

1689313539155.jpg

image.png