使用antv G6构造带有分支的流程图

920 阅读1分钟

基于G6 3.8.1版本进行构造

主题使用方法:github.com/xitu/juejin…

theme: juejin highlight: github

基本需求: 1.根据后台提供数据生成带有分支的流程图

2.节点自定义 根据具体需求重定义节点样式

基本全部代码:

流程图自定义元素 切换分支
<script>
</script>
<!-- <script src="./g6.js"></script> -->
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/3.8.1/dist/g6.min.js"></script>
<!-- <script src="https://gw.alipayobjects.com/os/antv/assets/lib/jquery-3.2.1.min.js"></script> -->
<!-- 
<script src="https://gw.alipayobjects.com/os/lib/dagre/0.8.4/dist/dagre.min.js"></script> -->
<!-- 3.8.X版本支持插入html节点并且可以成功连线,3.1.x 不支持连线 其他版本未尝试 -->
<script>

    //测试数据
   var nodeArr = []
  
    var jd = [
    {
        "nodeId": "start",
        "nodeName": "开始",
        "relatedIds": [
            "bece31ff-15da-11eb-91c7-14f6d8da8bae"
        ]
    },
    {
        "nodeId": "bece31ff-15da-11eb-91c7-14f6d8da8bae",
        "revision": 1,
        "nodeName": "申请",
        "groupId": "4",
        "groupName": "申请人",
        "taskKey": "sq",
        "relatedIds": ["bmjlsp"]
    },
    {
        "nodeId": "bmjlsp",
        "nodeName": "部门经理审批",
        "seq": 2,
        "relatedIds": [
            "end",
            "fgfzsp"
        ]
    },
    {
        "nodeId": "fgfzsp",
        "nodeName": "分管副总审批",
        "seq": 3,
        "relatedIds": [
            "jtsctzbjl",
            "end"
        ]
    },
    {
        "nodeId": "jtsctzbjl",
        "nodeName": "集团市场拓展部经理",
        "seq": 4,
        "relatedIds": [
            "jtfgfzsp"
        ]
    },
    {
        "nodeId": "jtfgfzsp",
        "nodeName": "集团分管副总审批",
        "seq": 5,
        "relatedIds": [
            "end"
        ]
    },
    {
        "nodeId": "end",
        "nodeName": "结束",
        "seq": 2147483647,
        "relatedIds": []
    }
    ]
    //添加节点

    
    jd.forEach(item => {
        if (item.nodeName == "结束") {
            nodeArr.push({
                id: item.nodeId,
                "shape": "circle",
                "size": 50,
                "label": "结束",
                "spjy": 123, //审批建议
            })
        }else if(item.nodeName == "开始"){
            nodeArr.push( //开始节点 固定添加开始节点
        {
            "id": item.nodeId,
            "label": "开始",
            "shape": "circle",
            "size": 50,
            "source": item.nodeId,
            "next": item.relatedIds
        },
    )
        }  else{
            nodeArr.push({
                id: item.nodeId,
                "borderStatus": computeColor(item.revision, item.outcome), //边框颜色值
                "backColor": compoteBackColor(item.revision), //左上角背景色
                "nowStatus": item.nodeName, //当前所在流程节点
                "name": item.assigneeName,
                "time": item.completedTime,
                "size": [200, 120],
                "spjy": item.approvalComments, //审批建议
                "source": item.nodeId,
                "next": item.relatedIds, //指向的下一节点数组
            })
        }


        

    })
    //数据源结构
    var data = {
        nodes: [],
        edges: []
    };
    data.nodes = nodeArr;
    console.log(data)
    //渲染流程图
    constructEdage(data);
    var gh = renderFn(data);
    //构造边连接
    function constructEdage(data) {
        var edges = []
        data.nodes.forEach(item => {
            if (item.next) {
                item.next.forEach(d => {
                    edges.push({
                        source: item.id,
                        target: String(d),
                    })
                })
            }
        })
        data.edges = edges;
    }
    //判断边框颜色值
    function computeColor(r, c) {
        if (r) {
            if (r == 1) {
                return '#FFC125'
            };
            if (r == 2) {
                if (c == 1 || c == 2) {
                    return '#169bd5'
                };

            };

            if (c == 3 || c == 4 || c == 5) {
                return '#FF4500'
            }

            if (r == 3) {
                if (c == 2 || c == 1) {
                    return '#169bd5'
                }
            }
        } else {
            return '#e0e0e0'
        };
    }
    //通过节点出现的次数 计算层级 设置画布高度
    function computeCount(e) {
        var count = 0;
        var maxCount = 0;
        var countArr = [];
        for (let i = 0; i < e.length; i++) { //遍历边对应的id 对"source"进行计数

            for (let j = 0; j < e.length; j++) {
                if (e[i].source == e[j].source) {
                    count++;
                }
            }
            if (maxCount < count) {
                maxCount = count;
            }
            if (count > 1 && countArr.indexOf(count) < 0) {
                countArr.push(count);
            }
            count = 0;
        }
        var sum = 0;
        countArr.forEach(item => {
            sum += item;
        })
        console.log(countArr)

        return sum || maxCount;

    }
    //计算上标识背景色
    function compoteBackColor(r) {
        if (r == 1 || !r) {
            return '#d1d1d1'
        };
    };

    function renderFn(data) {
        //计算层级
        var floorCount = computeCount(data.edges);
        console.log(floorCount);
        //画布宽度 在更改节点宽度时,需根据具体节点宽度对画布进行调整(节点宽度调整以后需要对节点之间的连线长度进行调整)
        var canvansWidth = (data.nodes.length) * 240;
        //保持画布宽度与容器宽度相同
        document.getElementById("mountNode").style.width = canvansWidth + "px";
        var canvansHeight = floorCount * 220;
        //保持画布高度与容器高度相同
        document.getElementById("mountNode").style.height = canvansHeight + "px";

        // 创建 G6 图实例
        const graph = new G6.Graph({
            renderer: 'svg',
            container: 'mountNode', // 指定图画布的容器 id
            // 画布宽高
            // fitView:true,
            // fitViewPadding: [ 20, 40, 50, 20 ],
            width: canvansWidth,
            height: canvansHeight,
            pixelRatio: 100,
            maxZoom: 0.9,
            animate: true,
            layout: {
                type: 'dagre', //层级自动布局
                rankdir: 'LR', // 从左向右渲染
                align: 'DL', // 可选
                nodesep: 10, // 节点层距离(上下)
                ranksep: 20, // 节点之间距离(左右)
                controlPoints:true
            },
            defaultNode: {
                shape: "dom-node"
            },
            defaultEdge: {
                shape: 'polyline',
                style: {
                    endArrow: true,
                    radius: 0,
                    offset: 20,
                    stroke: '#000',
                    lineWidth:1
                },
            }
        });
        //重定义节点样式自定义节点
        G6.registerNode('dom-node', {
            draw: (cfg, group) => {
                const htmlShape = group.addShape('dom', {
                    attrs: {
                        width: cfg.size[0],
                        height: cfg.size[1],
                        x: -cfg.size[0] / 2,
                        y: -cfg.size[1] / 2,
                        html: `
                        <div id=${cfg.id} class='dom_node' style="background-color: #fff; border: 1px solid ${cfg.borderStatus}; border-radius: 5px; width: ${cfg.size[0] - 5}px; height: ${cfg.size[1] - 5}px; display: inline-block;">
                        <span class="smallStatus" style="background:${cfg.backColor}">${cfg.nowStatus}</span>
                        <p  class="nodeItem first_item" ><span style="display: inline-block; float: left;">${cfg.nowStatus == "申请" ? "申请":"审批"}人:</span><span class="text_right">${cfg.name || ""}</span></p>
                        <p class="nodeItem"><span >审批时间:</span><span style="display: inline-block; padding-left:1px" class="text_right">${cfg.time || " "}</span></p>
                        <p class="nodeItem"><span style="display: inline-block; float: left;">审批意见:</span><span class="nodeText text_right">${cfg.spjy || " "}</span> </p>
                        </div>
                    `
                    },
                }, "rect");
                return htmlShape;
            },
            getAnchorPoints() {
                return [
                    // [0, 0.5], // 左侧中间
                    // [1, 0.5], // 右侧中间
                    // [0.5, 1], // 上侧中间
                    // [0.5, 0], // 下侧中间
                    // [0, 0], // 右侧中间
                    // [0, 1], // 右侧中间
                ];
            },
        });

        //加载数据
        graph.data(data)
        // 渲染图
        graph.render();
        //返回实例对象,需要再刷新视图时,调用销毁对象方法
        return graph;
    }
</script>
body { margin: 0; padding: 0; width: 100%; height: 100%; } .box { width: 1800px; height: 550px; overflow-x: scroll; border: 1px solid #eee; white-space: nowrap; position: absolute; position: absolute; top: 50%; left: 50%; right: 0; bottom: 0; transform: translate(-50%, -50%); } .nodeItem { width: 195px; text-align: left; text-indent: 0.5em; color: gray; margin-bottom: 4px; word-wrap: break-word; word-break: break-all; font-size: 10px; overflow: hidden; } .dom_node { display: inline-block; vertical-align: middle; text-align: left; border: 1px solid lightgrey; border-radius: 5px; height: 130px; position: relative; overflow: hidden; width: 210px; font-size: 10px; } .first_item { margin-top: 22px; } .smallStatus { display: inline-block; position: absolute; top: 0; left: 0; background: #eee; height: 20px; padding: 0 5px 0 5px; font-size: 12px; border-radius: 3px 0 0 0; background: #169bd5; opacity: 0.9; border: 1px #fff solider; color: #fff } .nodeText { display: inline-block; width: 120px; white-space: normal; /* word-break: normal; */ display: inline-block; word-wrap: break-word; overflow: hidden; padding: 0 0 5px 0; height: 35px; overflow-y: auto; } .text_right { color: #000 } .nodeText::-webkit-scrollbar { width: 2px; height: 1px; } .nodeText::-webkit-scrollbar-thumb { border-radius: 10px; -webkit-box-shadow: inset 0 0 5px #eee; background: #535353; } .nodeText::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 5px #fff; border-radius: 10px; background: #EDEDED; }