AntvG6绘制链路图(自定义节点)

<template>
  <div>
    <div class="login" :style="{ height: scrollerHeight }" style="width: 100%;">
      <div style="width: 100%;height: 100%" ref="graphWrap" id="graphWrap"></div>
      <el-dialog class="link_graph_fullscreen" :fullscreen="true" title="数据链路" :visible.sync="open" :destroy-on-close="true" width="100%" append-to-body>
        <dataLinkedFull ref='apidatalinked' :flowList="flowList" chart-id="linkedChart"></dataLinkedFull>
      </el-dialog>
    </div>
  </div>
</template>
<script>
import G6 from '@antv/g6';
// import { linkdata } from '@/mock/link1';
import insertCss from 'insert-css';
import dataLinkedFull from "@/components/newLinkedChart/linkFull";
import full from "@/assets/images/app/full.png";

insertCss(`
  .link_graph_fullscreen .g6-component-toolbar {
    background-color: rgba(200, 200, 200, 0.3);
  }
  .link_graph_fullscreen .g6-component-toolbar li {
    margin-left: 0px;
    margin-top: 0px;
  }
  .g6-component-toolbar {
      position: absolute;
      list-style-type: none;
      padding: 6px;
      left: 6px !important;
      top: 6px !important;
      background-color: #FFFFFF;
      border: 0px solid #e2e2e2;
      border-radius: 12px;
      font-size: 12px;
      color: #545454;
      margin: 0;
      width: 46px;
  }
  .g6-component-toolbar li {
    float: left;
    text-align: center;
    width: 35px;
    height: 35px;
    cursor: pointer;
    list-style-type: none;
    list-style: none;
    margin-left: -15px;
    margin-top: -15px;
    line-height: 55px;
  }
  .g6-component-tooltip {
    background-color: rgba(0,0,0, 0.65);
    padding: 10px;
    box-shadow: rgb(174, 174, 174) 0px 0px 10px;
    width: fit-content;
    color: #fff;

    border-radius = 4px;
  }
  .g6-tooltip {
    border: 1px solid #e2e2e2;

    border-radius: 4px;
    font-size: 12px;
    color: #545454;

    background-color: rgba(255, 255, 255, 0.9);
    padding: 10px 8px;
    box-shadow: rgb(174, 174, 174) 0px 0px 10px;
  }
`);

// const data = {nodes:[], edges:[], combos: []};

export default {
  name: 'dataLinkeVue',
  components: { dataLinkedFull },
  props: {
    // flowList: {
    //   type: Array,
    //   default: [],
    //   require: true
    // },
    // titles: {
    //   type: Array,
    //   default: [],
    //   require: true
    // },
    // tags: {
    //   type: Array,
    //   default: [],
    //   require: true
    // },
    isFull: {
      type: Boolean,
      default: false
    },
    appName: { type: String },
    appCode: { type: String },
    chartId: {
      type: String,
      default: () => {
        return 'linkedChart'
      }
    }
  },
  data() {
    return {
      flowList:[{
        "detailInfo": {},
        "id": "xq1",
        "isFmqd": "0",
        "nodeName": "数据分析应用建设需求",
        "nodeType": "XQ",
        "preNodes": []
      },{
        "detailInfo": {},
        "id": "xq2",
        "isFmqd": "0",
        "nodeName": "资产数据分析需求",
        "nodeType": "XQ",
        "preNodes": []
      }, {
        "detailInfo": {},
        "id": "yd1",
        "isFmqd": "0",
        "nodeName": "yd_db1_inst_elec_cons_tmp_today",
        "nodeType": "YD",
        "preNodes": ["xq1"]
      }, {
        "detailInfo": {},
        "id": "yd2",
        "isFmqd": "0",
        "nodeName": "yd_db2_inst_elec_cons_tmp_today2",
        "nodeType": "YD",
        "preNodes": ["xq1"],
        "projectName": "js_ods_prod"
      }, {
        "detailInfo": {
          "tbDatabase": "js_ods_prod",
          "nodeCnName": "贴源表1",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "ty1",
        "isFmqd": "0",
        "accreditStatus": "1",
        "nodeName": "ods_db1_inst_elec_cons_tmp_today",
        "nodeType": "TY",
        "preNodes": ["yd1"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_ods_prod",
          "nodeCnName": "贴源表2",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "ty2",
        "isFmqd": "0",
        "accreditStatus": "3",
        "nodeName": "ods_db2_inst_elec_cons_tmp_today2",
        "nodeType": "TY",
        "preNodes": ["yd2"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_dwd_prod",
          "nodeCnName": "共享层表1",
          "projectEnv": "测试环境",
          "isDateSet": false
        },
        "id": "gx1",
        "isFmqd": "0",
        "accreditStatus": "4",
        "nodeName": "dwd_db1_inst_elec_cons_tmp_today",
        "nodeType": "GX",
        "preNodes": ["ty1"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_dwd_prod",
          "nodeCnName": "共享层表2",
          "projectEnv": "测试环境",
          "isDateSet": false
        },
        "id": "gx2",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "dwd_db2_inst_elec_cons_tmp_today2",
        "nodeType": "GX",
        "preNodes": ["ty2"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表1",
          "projectEnv": "测试环境",
          "isDateSet": false
        },
        "id": "fx1",
        "isFmqd": "0",
        "accreditStatus": "4",
        "nodeName": "asd_db1_inst_elec_cons_tmp_today",
        "nodeType": "FX",
        "preNodes": ["gx1"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表2",
          "projectEnv": "生产环境",
          "isDateSet": true
        },
        // 模拟虚拟节点
        "id": "fx2",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "asd_db2_inst_elec_cons_tmp_today2",
        "nodeType": "EMPTY",
        "preNodes": ["gx2"]
      }, {
        "detailInfo": {},
        "id": "rds1",
        "isFmqd": "0",
        "nodeName": "rds_db1_inst_elec_cons_tmp_today",
        "nodeType": "RDS",
        "preNodes": ["fx1"]
      }, {
        "detailInfo": {},
        "id": "rds2",
        "isFmqd": "0",
        "nodeName": "rds_db2_inst_elec_cons_tmp_today2",
        "nodeType": "RDS",
        "preNodes": ["fx2"]
      }, {
        "detailInfo": {},
        "id": "yd3",
        "isFmqd": "0",
        "nodeName": "yd_mgja",
        "nodeType": "YD",
        "preNodes": ["xq2"]
      }, {
        "detailInfo": {},
        "id": "yd4",
        "isFmqd": "0",
        "nodeName": "yd_summary_day_total",
        "nodeType": "YD",
        "preNodes": ["xq2"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_ods_prod",
          "nodeCnName": "贴源3",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "ty3",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "ods_mgjs_palw",
        "nodeType": "TY",
        "preNodes": ["yd3"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表3",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "fx3",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "dws_grid_grid_abn_stats_day_ts_dj",
        "nodeType": "FX",
        "preNodes": ["ty3"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表4",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "fx4",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "dws_cst_bus_app_form_scale_yc_dqgds",
        "nodeType": "FX",
        "preNodes": ["ty3"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表5",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "fx5",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "dws_cst_bus_app_form_scale_sz_linhugds",
        "nodeType": "FX",
        "preNodes": ["ty3"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_ods_prod",
          "nodeCnName": "贴源4",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "ty4",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "ods_grid_grid_abn_stats_day_sz_xukougds",
        "nodeType": "TY",
        "preNodes": ["yd4"]
      }, {
        "detailInfo": {
          "tbDatabase": "js_js_dws_proj_prod",
          "nodeCnName": "分析层表6",
          "projectEnv": "生产环境",
          "isDateSet": false
        },
        "id": "fx6",
        "isFmqd": "0",
        "accreditStatus": "2",
        "nodeName": "dws_grid_grid_abn_stats_day_sz_xukougds",
        "nodeType": "FX",
        "preNodes": ["ty4"]
      }, {
        "detailInfo": {},
        "id": "rds3",
        "isFmqd": "0",
        "nodeName": "ava_cap_mon_sz_fengqiaogds",
        "nodeType": "RDS",
        "preNodes": ["fx3","fx4","fx5"]
      }, {
        "detailInfo": {},
        "id": "rds4",
        "isFmqd": "0",
        "nodeName": "cst_bus_app_form_scale_ts_mp",
        "nodeType": "RDS",
        "preNodes": ["fx6"]
      }],
      scrollerHeight: '520px',
      zoomRate: 1,
      graph: null,
      toolbar: null,
      combos: {},
      lazy: false,
      data: { nodes: [], edges: [], combos: [] },
      animateCfg: { duration: 200, easing: 'easeCubic' },
      nodeExtraAttrs: { //
        XQ: { fill: "#00a096", font: "#144b32", title: '需求', layer: 1 },
        YD: { fill: "#eefaf9", font: "#144b32", title: '源端', layer: 2 },
        TY: { fill: "#78BEB3", font: "#144b32", title: '贴源层', layer: 3 },
        GX: { fill: "#81E380", font: "#144b32", title: '共享层', layer: 4 },
        FX: { fill: "#92A4EB ", font: "#144b32", title: '分析层', layer: 5 },
        RDS: { fill: "#ebf5ff", font: "#144b32", title: 'RDS', layer: 9 },
        SERVICE: { fill: "#FFD496", font: "#144b32", title: '服务', layer: 35 },
        API: { fill: "#FFD496", font: "#144b32", title: '接口', layer: 36 },
        APP: { fill: "#EDBBAB", font: "#144b32", title: 'API聚合', layer: 40 },
        EMPTY:{fill:"pink",font:"#1b32",title:"过渡点",layer:7}
      },
      nodeHeight: 25,
      nodeWidth: 220,
      padding: 7,
      open: false
    };
  },
  created() {
    // this.scrollerHeight=(window.innerHeight - 110)+"px";
  },
  mounted() {
    let _this = this;
    //初始化链路图
    this.initDraw();
    if (this.graph) {
      if (_this.flowList.length > 1000) { //大于100个节点就分批加载
        this.lazy = true
        this.$modal.notifyWarning("链路数据过大,请点击具体节点展开!");
        //只加载服务节点,点击服务展开
        _this.combos["APP"] = { id: "APP", label: "应用" }
        // _this.combos["SERVICE"] = {id:"SERVICE", label: "服务"}
        _this.flowList
          .forEach((item, idx) => {
            switch (item.nodeType) {
              case 'XQ':
                item.icon = 'a1.png';
                item.fill = "#00a096";
                item.stroke = "#00a096";
                item.labelfill = "white";
                break;
              case 'YD':
                item.icon = 'a2.png';
                item.fill = "#eefaf9";
                item.stroke = "#00a096";
                item.labelfill = "black";
                break;
              case 'TY':
              case 'GX':
              case 'FX':
                // 根据 accreditStatus 的值判断
                switch (item.accreditStatus) {
                  case '1':
                    item.icon = 'a3_g.png';
                    item.fill = "#e4e4e4";
                    item.stroke = "#929292";
                    item.labelfill = "black";
                    break;
                  case '2':
                    item.icon = 'a3_b.png';
                    item.fill = "l(0) 0:#B5C1FD 0.4:#eefaf9";
                    item.stroke = "#B5C1FD";
                    item.labelfill = "black";
                    break;
                  case '3':
                    item.icon = 'a3.png';
                    item.fill = "#ebf5ff";
                    item.stroke = "#0a84fc";
                    item.labelfill = "black";
                    break;
                  case '4':
                    item.icon = 'a3_g.png';
                    item.fill = "#fff";
                    item.stroke = "#7C7C7C";
                    item.labelfill = "black";
                    break;
                }
                break;
              case 'RDS':
                item.icon = 'a4.png';
                item.fill = "#ebf5ff";
                item.stroke = "#0a84fc";
                item.labelfill = "black";
                break;
            }
            //节点
            let _node = {
              id: item.id,
              label: item.nodeName,
              name: item.nodeName,
              comboId: item.nodeType,
              nodeType: item.nodeType,
              icon: item.icon,
              fill: item.fill,
              stroke: item.stroke,
              labelfill: item.labelfill,
              accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
              isFmqd: item.isFmqd,
              preNodes: item.preNodes,
              detailInfo: { ...item.detailInfo },
              layer: _this.getLayer(item)
            }
          
            _this.data.nodes.push(_node)
            //处理第一个节点的子父节点
            if (idx === 0 && item.preNodes) {
              _node.loaded = true
              _this.flowList.filter(n => item.preNodes.includes(n.id))
                .forEach((pNode, i) => {
                  //设置分组
                  if (!_this.combos[pNode.nodeType]) {
                    let _attr = this.nodeExtraAttrs[pNode.nodeType]
                    _this.combos[pNode.nodeType] = { id: pNode.nodeType, label: _attr.title, labelCfg: { position: 'top', refX: 60, refY: 40 } }
                  }
                  _this.data.nodes.push({
                    id: pNode.id,
                    label: pNode.nodeName,
                    name: item.nodeName,
                    comboId: pNode.nodeType,
                    // type: pNode.nodeType,
                    nodeType: pNode.nodeType,
                    accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
                    preNodes: pNode.preNodes,
                    detailInfo: { ...item.detailInfo },
                    isFmqd: item.isFmqd,
                    layer: _this.getLayer(pNode)
                  })
                  //处理连线
                  _this.data.edges.push({ //父节点到当前节点的连线
                    source: pNode.id,
                    type: 'cubic-horizontal',
                    target: item.id
                  })
                })
            }
            //连线,此处不连线,放入实际环境,需要添加到应用的连线,并且需要动态控制combo的加载
          })
      } else {
        this.lazy = false;
        _this.flowList.forEach((item, idx) => {

          switch (item.nodeType) {
            case 'XQ':
              item.icon = 'a1.png';
              item.fill = "#00a096";
              item.stroke = "#00a096";
              item.labelfill = "white";
              break;
            case 'YD':
              item.icon = 'a2.png';
              item.fill = "#eefaf9";
              item.stroke = "#00a096";
              item.labelfill = "black";
              break;
            case 'TY':
            case 'GX':
            case 'FX':
              // 根据 accreditStatus 的值判断
              switch (item.accreditStatus) {
                case '1':
                  item.icon = 'a3_g.png';
                  item.fill = "#e4e4e4";
                  item.stroke = "#929292";
                  item.labelfill = "black";
                  break;
                case '2':
                  item.icon = 'a3_b.png';
                  item.fill = "l(0) 0:#B5C1FD 0.4:#eefaf9";
                  item.stroke = "#B5C1FD";
                  item.labelfill = "black";
                  break;
                case '3':
                  item.icon = 'a3.png';
                  item.fill = "#ebf5ff";
                  item.stroke = "#0a84fc";
                  item.labelfill = "black";
                  break;
                case '4':
                  item.icon = 'a3_g.png';
                  item.fill = "#fff";
                  item.stroke = "#7C7C7C";
                  item.labelfill = "black";
                  break;
              }
              break;
            case 'RDS':
              item.icon = 'a4.png';
              item.fill = "#ebf5ff";
              item.stroke = "#0a84fc";
              item.labelfill = "black";
              break;
          }
          //设置分组
          if (!_this.combos[item.nodeType]) {//&& item.nodeType !== 'SERVICE'
            let _attr = this.nodeExtraAttrs[item.nodeType]
            _this.combos[item.nodeType] = { id: item.nodeType, label:item.nodeType == "EMPTY" ? "" : _attr.title, labelCfg: { position: 'top', refX: 145, refY: item.nodeType==="XQ" ? -69 :  -40 } }
          }
          _this.data.nodes.push({
            id: item.id,
            label: item.nodeName,
            name: item.nodeName,
            comboId: item.nodeType,
            isFmqd: item.isFmqd,
            icon: item.icon,
            fill: item.fill,
            stroke: item.stroke,
            labelfill: item.labelfill,
            // groupId:item.nodeType,
            // type: item.nodeType,
            nodeType: item.nodeType,
            accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
            preNodes: item.preNodes,
            detailInfo: { ...item.detailInfo },
            layer: _this.getLayer(item)
          })

          if (item.preNodes && item.preNodes.length > 0) {
            item.preNodes.forEach((p, i) => {
              _this.data.edges.push(
                {
                  source: p,
                  type: 'cubic-horizontal',
                  target: item.id
                }
              )
            })
          }
        })
      }

      //设置分组
      _this.data.combos = Object.values(_this.combos)

      this.procLayer(_this.data)
      this.graph.data(_this.data);

      this.graph.node((node) => {
        const styleAttrs = this.nodeExtraAttrs[node.nodeType] || {};
        const style = Object.assign({
          fillOpacity: 1,
          radius: 5,
          fontSize: 14
        }, styleAttrs);

        switch (node.nodeType) {
          case 'XQ':
            style.fill = "#00a096";
            style.stroke = "#00a096";
            // labelCfg.style.fill = "#fff";
            // 添加图标
            // style.icon = "your_xq_icon_url";
            break;
          case 'YD':
            style.fill = "#eefaf9";
            style.stroke = "#00a096";
            // labelCfg.style.fill = "black";
            // 添加图标
            // style.icon = "your_yd_icon_url";
            break;
          case 'TY':
          case 'GX':
          case 'FX':
            // 根据 accreditStatus 的值判断
            switch (node.accreditStatus) {
              case '1':
                style.fill = "#e4e4e4";
                style.stroke = "#acacac";
                // labelCfg.style.fill = "black";
                break;
              case '2':
                style.fill = "l(0) 0:#c1cbfd 0.4:#eefaf9";
                style.stroke = "#4a67fa";
                // labelCfg.style.fill = "black";
                break;
              case '3':
                style.fill = "#ebf5ff";
                style.stroke = "#0a84fc";
                // labelCfg.style.fill = "black"
                break;
              case '4':
                style.fill = "#fff";
                style.stroke = "#0a84fc";
                style.lineDash = [2];
                // labelCfg.style.fill = "black";
                break;
            }
            break;
          case 'RDS':
            style.fill = "#ebf5ff";
            style.stroke = "#0a84fc";
            // labelCfg.style.fill = "black";
            break;
        }

        let _style = Object.assign({
          fillOpacity: 1,
          radius: 15,
          fontSize: 14
        }, style)

        return {
          id: node.id,
          label: _this.fittingString(node.label, _this.nodeWidth - _this.padding * 2 - 10, 14),
          name: node.label,
          style: _style,
          type: 'service',
          // type: 'service',
          // type:'modelRect',
          size: [280, 40],
          // labelCfg: {
          //   style: {
          //     fill: 'black',
          //     fontSize: 14,
          //   },
          //   offset: 30,
          // },
        };
      });

      //更新数据
      this.graph.render();
      if (typeof window !== 'undefined') {
        window.onresize = () => {
          const container = this.$refs.graphWrap;
          if (!this.graph || this.graph.get('destroyed')) return;
          if (!container || !container.scrollWidth || !container.scrollHeight) return;
          this.graph.changeSize(container.scrollWidth, container.scrollHeight);
        };
      }
      // this.graph.zoom(this.zoomRate);
    }
  },
  methods: {
    getLayer(target) {
      return this.nodeExtraAttrs[target.nodeType].layer
    },
    procLayer(data) {
      data.nodes.forEach(node => {
        if (node.preNodes && node.preNodes.length > 0) {
          // 查找同一层级父节点
          // let groupNodes = data.nodes.filter(n => n.nodeType === node.nodeType);
          // let pNodes = groupNodes.filter(n => node.preNodes.includes(n.id));
          let pNodes = data.nodes.filter(n => (n.nodeType === node.nodeType && node.preNodes.includes(n.id)));
          console.log(pNodes,"pNodes");
          //如果同组有多个节点,取最深一个节点的layer数加1,否则如果没有同组的节点,则直接用组的layer,即不修改
          if (pNodes && pNodes.length > 0) {
            let maxLevel = 1;
            pNodes.forEach(_pNode => {
              maxLevel = Math.max(maxLevel, this.getGroupParentLevel(_pNode, maxLevel))//groupNodes,
              console.log(maxLevel,"maxLevel");
            })
            node.layer += maxLevel; //如果没变
          }
        }
      })
    },
    getGroupParentLevel(node, level) {//groupNodes,
      //继续找该节点的父级
      if (!node.preNodes) {
        return level;
      }
      let max = level;
      if (node.preNodes.length === 1) {
        if (node.id == node.preNodes[0]) {
          return max;
        }
        let _node = this.data.nodes.find(n => (n.nodeType === node.nodeType && n.id === node.preNodes[0]));
        // let _node = groupNodes.find(n => n.id === node.preNodes[0]);

        if (_node) {
          max = Math.max(max, this.getGroupParentLevel(_node, max + 1))//groupNodes,
        }
        return max;
      }
      if (node.preNodes.length > 1) {
        for (let _node in this.data.nodes) {
          if (_node.nodeType === node.nodeType && node.preNodes.includes(_node.id)) {
            max = Math.max(max, this.getGroupParentLevel(_node, max + 1))//groupNodes,
          }
        }
        return max;
      }
    },
    initDraw() {
      let _this = this;
      const container = this.$refs.graphWrap;
      G6.registerNode(
        "service", //第一个参数自定义节点的名字
        // 第二个参数是这个节点的图形分组
        {
          draw: function (cfg, group) {
            console.log(cfg,"cfg内容");
           //字符串截取,中英文都适用
           function beautySub(str, len) {
              var reg = /[\u4e00-\u9fa5]/g, slice = str.substring(0, len),
                cCharNum = (~~(slice.match(reg) && slice.match(reg).length)),
                realen = slice.length * 2 - cCharNum - 1;
              return str.substr(0, realen) + (realen < str.length ? "..." : "");
            }
           if(cfg.nodeType != "EMPTY"){
            var keyShape = group.addShape('rect', {
              //    代表矩形的一些属性
              attrs: {
                //    相对定位
                x: 0,
                y: 0,
                width: 186,
                height: 41,
                stroke: cfg.stroke,   //描边色
                fill: cfg.fill,
                radius: 12,
                lineWidth: 1,

              },
              name: "card-node-keyShape" //起个唯一名字便于识别
            });
            // 节点
            group.addShape("image", {
              attrs: {
                x: 8,
                y: 6,
                width: 32,
                height: 32,
                img: require(`../../assets/images/appDetail/${cfg.icon}`),
              },
              name: "card-node-yewuico"
            });
            // 文字
            group.addShape("text", {
              attrs: {
                text: cfg.name.length > 8 ? beautySub(cfg.name,8) : cfg.name,
                x: 50,
                y: 30,
                fontSize: 14,
                fill: cfg.labelfill,
              },
              name: "card-node-text1",
            });
           }else{
            var keyShape = group.addShape('circle', {
              //    代表矩形的一些属性
              attrs: {
                //    相对定位
                x: 0,
                y: 0,
                r:1,
                fill: "#00a096",

              },
              name: "card-node-keyShape1" //起个唯一名字便于识别
            });
           }

            return keyShape;
          },

        }, 'rect');

      //图例初始化
      this.initToolbar();

      // const width = container.scrollWidth;
      // const height = container.scrollHeight || 500;
      this.graph = new G6.Graph({
        // width:width,
        // height: 520,
        container: container,
        plugins: [_this.toolbar],
        fitView: true,
        // fitCenter: true,
        fitViewPadding: 5,
        animate: true,
        animateCfg: {
          duration: 500, // Number,一次动画的时长
          easing: 'easeCubic', // String,动画函数
        },
        layout: {
          type: 'dagre',
          rankdir: 'LR', // 可选,默认为图的中心
          // align: 'DL', // 可选
          nodesep: 8, // 可选, 纵向间距
          ranksep: 20, // 可选,横向间距
          gpuEnabled: true,
          preventOverlap: true, // 防止节点重叠
          workerEnabled: false, // 开启 Web-Worker
          controlPoints: true, // 可选
          // sortByCombo: true,
        },
        modes: {
          // default: ['drag-combo',"zoom-canvas", 'drag-canvas',"drag-node",
          default: [
            // default: [
            // { type: "activate-relations", activeState: 'active', inactiveState: 'inactive' },
            {
              type: 'tooltip',
              offset: 10,
              formatText(model) {
                let accreditStatusName = '';
                if(model.accreditStatus=='1'){
                  accreditStatusName = "未接入";
                } else if(model.accreditStatus=='2'){
                  accreditStatusName = "未授权";
                } else if(model.accreditStatus=='3'){
                  accreditStatusName = "已授权";
                } else if(model.accreditStatus=='4'){
                  accreditStatusName = "未申请";
                } else if(model.nodeType=='RDS'){
                  accreditStatusName = "已授权";
                }
                let text = '<span style=\'margin-top:10px;font-size:16px\'>表名: ' + model.name + '</span>';
                if(model.nodeType == 'XQ'){
                  text = '<span style=\'margin-top:10px;font-size:16px\'>需求: ' + model.name + '</span>';
                }
                // if (model.nodeType != 'APP' && model.nodeType != 'SERVICE' && model.nodeType != 'RDS' && model.nodeType != 'API' && model.nodeType != 'TY') {
                //   if (model.nodeType == 'YD') {
                //     text += '<br/><span style="margin-top:10px;font-size:16px">数据库: ' + model.detailInfo.tbDatabase;
                //   }if ( model.nodeType == 'GX'|| model.nodeType == 'FX') {
                //     text += '<br/><span style="margin-top:10px;font-size:16px;color:#4a67fa;line-height:16px;">  <span style="display:block;width:10px;height:10px;border-radius:50%;margin-right:20px;background-color:#4a67fa;float:left"></span><span>'+ model.accreditStatus+'</span> ' ;
                //   }
                //    else {
                //     text += '<br/><span style=\'margin-top:10px;font-size:16px\'>项目空间: ' + model.detailInfo.tbDatabase;
                //   }
                // }
                if (model.nodeType == 'GX' || model.nodeType == 'FX' || model.nodeType == 'TY') {
                  text += '<br/><span style=\'margin-top:10px;font-size:16px\'>' + model.detailInfo.nodeCnName + '</span>'
                    + '<br/><span style=\'margin-top:10px;font-size:16px\'>项目空间: ' + model.detailInfo.tbDatabase + '</span>'
                    + '<br/><span style=\'width:65px;text-align:center;display:block;border:1px solid #85e4d3;border-radius:5px;margin-top:10px;font-size:14px;color:#85e4d3;\'>' + model.detailInfo.projectEnv + '</span>'
                    + '<br/><span style="float:left;display:block;margin-top:-10px;font-size:14px;color:#4a67fa;line-height:16px;">  <span style="display:block;margin-top:3px;width:10px;height:10px;border-radius:50%;margin-right:5px;background-color:#4a67fa;float:left"></span>'
                    + accreditStatusName + '</span> ' ;
                    if(model.detailInfo.isDateSet){
                      text += '<span style=\'display:block;margin-top:-13px;float:left;width:60px;height:20px;line-height:20px;text-align:center;;border-radius:5px;background-color: #ffead3;font-size:14px;color:#dc7e17;margin-left:5px;\'>' + '数据集'+ '</span>';
                    }
                }
                text += '</span>';
                // text += '<br/><span style="margin-top:10px;font-size:16px">负面清单: ' + (model.isFmqd == '1' ? '是' : '否') + '</span>';
                return text;
              }
            }
          ]//'click-select','collapse-expand-combo',
        },
        nodeStateStyles: {
          hover: {
            lineWidth: 2,
            stroke: '#6cc9f5',
            fill: '#e6f7ff',
            shadowColor: "#ddd",
            cursor: "pointer"
          },
          highlight: {
            fill: "#db4437",
            shadowColor: '#fff',
            stroke: "#db4437",
            cursor: "pointer",
            'text-shape': {
              lineWidth: 1,
              fill: "#db4437",
              stroke: "#db4437",
            },
          },
          // click: {
          //   stroke: '#000',
          //   lineWidth: 3,
          // }
        },
        edgeStateStyles: {
          // 鼠标点击边,即 click 状态为 true 时的样式
          // click: {
          //   stroke: 'steelblue',
          // },
          hover: {
            lineWidth: 3,
            stroke: '#e6f7ff',
            // fill: '#e6f7ff',
            cursor: "pointer"
          },
        },
        comboStateStyles: {
          highlight: {
            fill: "#f6cd6b",
            opacity: 0.7,
            cursor: "pointer",
            'text-shape': {
              fill: "#A5E4F0",
              stroke: "#A5E4F0",
              lineWidth: 1,
            },
          },
          inactive: {
            stroke: '#eee',
            lineWidth: 1,
            'text-shape': {
              fill: "#eee",
              stroke: "#eee",
            },
          },
          hover:{
            stroke: '#00a096',
          }
        },
        groupByTypes: false,
        defaultCombo: {
          type: 'rect',
          label: '',
          // collapsed:true,
          // collapsedSubstituteIcon:{show:true}, //分组收缩(双击)
          style: {
            fill: '#fff',
            stroke: 'transparent',
            cursor: "grab",
            // radius: 4
          },
          // size: [300, 50],
          padding: [90, 100, 200, 100],
          labelCfg: {
            position: 'top',
            style: {
              fill: '#00a096',
              fontSize: 30,
              fontWeight: 400,
              shadowOffsetX: 10,
              shadowOffsetY: 10,
              // shadowColor: '#00a096',
              // shadowBlur: 10,
            },
          },
        },
        defaultNode: {
          type: 'service',
          // size: [220, 25],
          // style: {
          //   radius: 20,
          //   stroke: '#fff',
          //   fill: '#cc0'
          // },
          // labelCfg: {
          //   style: {
          //     fill: "#144b32",
          //     fontSize: 14
          //   }
          // }
        },
        defaultEdge: {
          // shape: "line-with-arrow",
          style: {
            endArrow: {
              path: 'M 0,0 L 8,4 L 8,-4 Z',
              fill: '#00a096'
            },
            lineWidth: 1,
            stroke: "#00a096",

          }
        },
      })
     
        // 关闭局部刷新,解决残影问题
        this.graph.get('canvas').set('localRefresh', false);
      this.graph.on('node:click', ({ item }) => {
        let _this = this;
        const id = item.getID();
        const model = item.getModel();
        if (this.lazy && !model.loaded) {
          if (model.preNodes) {
            _this.flowList.filter(node => model.preNodes.includes(node.id))
              .forEach((pNode, i) => {

                //设置分组
                if (!_this.combos[pNode.nodeType]) {
                  let _attr = _this.nodeExtraAttrs[pNode.nodeType]
                  _this.combos[pNode.nodeType] = { id: pNode.nodeType, label: _attr.title, labelCfg: { position: 'top', refX: 60, refY: 40 } }
                }

                //添加节点
                _this.data.nodes.push({
                  id: pNode.id,
                  label: pNode.nodeName,
                  name: item.nodeName,
                  comboId: pNode.nodeType,
                  type: pNode.nodeType,
                  nodeType: pNode.nodeType,
                  accreditStatus: item.accreditStatus ? item.accreditStatus : "0",
                  preNodes: pNode.preNodes,
                  detailInfo: { ...item.detailInfo },
                  layer: _this.getLayer(pNode)
                })
                // this.graph.addItem('node', {
                //   id: pNode.id,
                //   label: pNode.nodeName,
                //   comboId:pNode.nodeType,
                //   type: pNode.nodeType,
                //   nodeType: pNode.nodeType,
                //   preNodes: pNode.preNodes,
                //   layer: _this.getLayer(pNode)
                // });

                //添加线条
                _this.data.edges.push({ //父节点到当前节点的连线
                  source: pNode.id,
                  type: 'cubic-horizontal',
                  target: id
                })
                // this.graph.addItem('edge', {
                //   source: pNode.id,
                //   type: 'cubic-horizontal',
                //   target: id
                // });

              })

            //重新计算布局
            // this.graph.layout();

            _this.data.combos = Object.values(_this.combos)
            this.procLayer(_this.data);
            this.graph.changeData(_this.data)
            // this.graph.render();
          } else {
            this.$modal.notifyWarning("没有下级节点");
          }

          model.loaded = true
        }
        else if (this.lazy && model.loaded) { //已加载的情况下直接移除所有子节点

        }

      });


    },
    initToolbar() {
      let fullDiv = ``;
      if (this.isFull) {
        fullDiv = `<li code='full'>
                <img src=`+full+`
                     style="position: relative; margin-right: 10px; width: 46px; height: 46px; top: 2px;" />
              </li>`;
      }
      this.toolbar = new G6.ToolBar({
        position: { x: 0, y: 0 },
        getContent: () => `
          <ul class='g6-component-toolbar'>`
            + fullDiv +
          `</ul>`,
        handleClick: (code, graph) => {
          switch (code) {
            case 'full':
              this.open = true;
              break;
          }
        }
      })
    },
    /**
     * format the string
     * @param {string} str The origin string
     * @param {number} maxWidth max width
     * @param {number} fontSize font size
     * @return {string} the processed result
     */
    fittingString(str, maxWidth, fontSize) {
      if (typeof (str) == 'undefined') {
        str = "";
      }
      const ellipsis = "...";
      const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
      let currentWidth = 0;
      let res = str;
      const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese charactors and letters
      str.split("").forEach((letter, i) => {
        if (currentWidth > maxWidth - ellipsisLength) return;
        if (pattern.test(letter)) {
          // Chinese charactors
          currentWidth += fontSize;
        } else {
          // get the width of single letter according to the fontSize
          currentWidth += G6.Util.getLetterWidth(letter, fontSize);
        }
        if (currentWidth > maxWidth - ellipsisLength) {
          res = `${str.substr(0, i)}${ellipsis}`;
        }
      });
      return res;
    },
    copyStr(str) {
      const input = document.createElement("textarea");
      input.value = str;
      document.body.appendChild(input);
      input.select();
      document.execCommand("Copy");
      document.body.removeChild(input);
      alert("Copy Success!");
    },
  }
};
</script>
注意的点:
  1. 根据不同节点类型添加不同节点样式属性,绘制不同的节点的样式,分组
  2. 分组函数定义,设置的是每个分组的起始节点的位置
  3. 要是一个节点内有多个图形,使用addShape()多次累加,设置定位,组成一个大的节点
  4. 如果要在边上面添加自定义标签或者图标
  5.   G6.registerEdge('flow-line-resource', {
            draw: function (cfg, group) {
              const startPoint = cfg.startPoint;
              const endPoint = cfg.endPoint;
    
              const { style } = cfg;
              const shape = group.addShape('path', {
                attrs: {
                  stroke: style.stroke,
                  endArrow: style.endArrow,
                  path: [
                    ['M', startPoint.x, startPoint.y],
                    ['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y],
                  ],
                },
              });
    
              // 添加文本背景
              group.addShape('rect', {
                attrs: {
                  x: (startPoint.x + endPoint.x) / 2 - 45,
                  y: (startPoint.y + endPoint.y) / 2 - 12,
                  width: 90,
                  height: 24,
                  fill: '#ffffff',
                  stroke: cfg.call_num ? '#5bc2ba' : '#a5a5a5',
                  radius: 5,
                },
              });
    
    
              // 添加文本
              group.addShape('text', {
                attrs: {
                  x: (startPoint.x + endPoint.x) / 2,
                  y: (startPoint.y + endPoint.y) / 2,
                  text: cfg.call_num ? "调度次数:" + cfg.call_num : "无调度",
                  fill:cfg.call_num ? '#5bc2ba' : '#a5a5a5',
                  textAlign: 'center',
                  textBaseline: 'middle',
                },
              });
    
              return shape;
            },
          });
    
    自定义边样式效果如图:
  6. 当需要拖动画布的时候,出现分组的文字显示残影,请关闭局部刷新来解决残影问题
  // 关闭局部刷新,解决残影问题

        this.graph.get('canvas').set('localRefresh', false);
  1. 如果后台返回一个无数据的虚拟节点,添加empty空类型节点,动态判断节点类型,添加模拟节点类型样式(小圆点)
链路图效果如下:

添加了虚拟节点样式

此时还有个小箭头,然后在设置边上的属性里面添加nodeType,将边设置成自定义的边,根据nodeType来设置不同的边的类型

  G6.registerEdge('flow-line-resource', {
        draw: function (cfg, group) {
          console.log(cfg,"cfg的11111");
          const startPoint = cfg.startPoint;
          const endPoint = cfg.endPoint;

          const { style } = cfg;
          if(cfg.nodeType != "EMPTY"){
            var shape = group.addShape('path', {
            attrs: {
              stroke: style.stroke,
              endArrow: style.endArrow,
              path: [
                ['M', startPoint.x, startPoint.y],
                ['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y],
              ],
            },
          });
          }else{
            var shape = group.addShape('path', {
            attrs: {
              stroke: style.stroke,
              endArrow: false,
              path: [
                ['M', startPoint.x, startPoint.y],
                ['C', startPoint.x + (endPoint.x - startPoint.x) / 2, startPoint.y, startPoint.x + (endPoint.x - startPoint.x) / 2, endPoint.y, endPoint.x, endPoint.y],
              ],
            },
          });
          }
          return shape;
        },
      });

完成!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值