• vue +antvX6 根据节点与线,动态设置节点坐标生成流程图


    需求

    vue2 + antvX6完成流程图,但只有节点与线,没有节点的坐标,需要根据节点的顺序显示流程图。
    需求:

    1.根据数据动态生成对应的节点与线;
    2.节点不能重叠;
    3.节点与线可拖拽;
    4.因为线存在重叠可能,所有鼠标移入时线必须高亮显示(红色),鼠标移出复原;
    5.要求有对齐线;
    6.线不能与节点重叠(先不能穿过节点)。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果

    图片
    在这里插入图片描述
    动图
    在这里插入图片描述

    参数

    {
    	"line_data": [ // 线数据
            {
                "name": "条件1-1", // 线名称
                "source_state_id": 6, // 源节点
                "destination_state_id": 5, // 目标节点
                "attribute_type_id": 1 // 成功或失败状态
            },
            {
                "name": "条件2-1",
                "source_state_id": 9,
                "destination_state_id": 6,
                "attribute_type_id": 1
            },
            {
                "name": "条件2-2",
                "source_state_id": 5,
                "destination_state_id": 6,
                "attribute_type_id": 2
            },
            {
                "name": "条件3-1",
                "source_state_id": 10,
                "destination_state_id": 9,
                "attribute_type_id": 1
            },
            {
                "name": "条件3-2",
                "source_state_id": 5,
                "destination_state_id": 9,
                "attribute_type_id": 2
            },
            {
                "name": "条件4-1",
                "source_state_id": 11,
                "destination_state_id": 10,
                "attribute_type_id": 1
            },
            {
                "name": "条件4-2",
                "source_state_id": 5,
                "destination_state_id": 10,
                "attribute_type_id": 2
            },
            {
                "name": "条件5-1",
                "source_state_id": 12,
                "destination_state_id": 11,
                "attribute_type_id": 1
            },
            {
                "name": "条件5-2",
                "source_state_id": 5,
                "destination_state_id": 11,
                "attribute_type_id": 2
            },
            {
                "name": "条件6-1",
                "source_state_id": 13,
                "destination_state_id": 12,
                "attribute_type_id": 1
            },
            {
                "name": "条件6-2",
                "source_state_id": 5,
                "destination_state_id": 12,
                "attribute_type_id": 2
            },
            {
                "name": "条件7-1",
                "source_state_id": 18,
                "destination_state_id": 13,
                "attribute_type_id": 1
            },
            {
                "name": "条件7-2",
                "source_state_id": 5,
                "destination_state_id": 13,
                "attribute_type_id": 2
            },
            {
                "name": "条件8-1",
                "source_state_id": 19,
                "destination_state_id": 6,
                "attribute_type_id": 3
            },
            {
                "name": "条件8-2",
                "source_state_id": 11,
                "destination_state_id": 19,
                "attribute_type_id": 1
            }
        ],
        "node_data": [ // 节点数据
            {
                "id": 1, // 节点id
                "name": "开始", // 节点名称
                "type_id": 1, // 节点状态
                "order_id": 1 // 节点顺序
            },
            {
                "id": 2,
                "name": "过程1",
                "type_id": 0,
                "order_id": 2
            },
            {
                "id": 3,
                "name": "过程2-1",
                "type_id": 0,
                "order_id": 3
            },
            {
                "id": 4,
                "name": "过程2-2",
                "type_id": 0,
                "order_id": 3
            },
            {
                "id": 5,
                "name": "过程3",
                "type_id": 0,
                "order_id": 4
            },
            {
                "id": 6,
                "name": "过程4",
                "type_id": 0,
                "order_id": 5
            },
            {
                "id": 7,
                "name": "过程5",
                "type_id": 0,
                "order_id": 6
            },
            {
                "id": 8,
                "name": "过程6",
                "type_id": 0,
                "order_id": 7
            },
            {
                "id": 9,
                "name": "结束",
                "type_id": 2,
                "order_id": 8
            }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150

    代码

    安装插件

    1.antvX6
    npm install @antv/x6 --save
    2.对齐线
    npm install @antv/x6-plugin-snapline --save
    
    • 1
    • 2
    • 3
    • 4

    html代码

    <template>
      <div class="info-box">
        <div class="top-box"
             id="top-width">
          <el-button type=""
                     @click="zoomToFit">填满el-button>
        div>
        <div class="content-box">
          <div class="container-box">
            <div id="container">div>
          div>
        div>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    js代码

    <script>
    import API from '../api' // 接口
    import { Graph } from '@antv/x6' // 引入antvX6
    import { Snapline } from '@antv/x6-plugin-snapline' // 引入对齐线
    Graph.registerNode( // 设置节点基础样式
      'custom-rect',
      {
        inherit: 'rect',
        width: 200,
        height: 40,
        attrs: {
          body: {
            strokeWidth: 1,
            stroke: '#5F95FF',
            fill: '#EFF4FF'
          },
          text: {
            fontSize: 12,
            fill: '#262626'
          }
        },
        text: {
          fontSize: 12,
          fill: '#262626'
        }
      },
      true
    )
    export default {
      data() {
        return {
          loading: false,
          graph: null, // 画布实例对象
          data: {
            nodes: [],
            edges: []
          }
        }
      },
      mounted() {
      	// 先初始化画布
        this.initGraph()
      },
      beforeDestroy() {
        // 画布的销毁以及回收
        this.graph.dispose()
        this.graph = null
      },
      methods: {
        // 初始化流程图画布
        initGraph() {
          const container = document.getElementById('container')
          this.graph = new Graph({
            container: container, // 画布容器
            width: container.offsetWidth, // 画布宽
            height: container.offsetHeight, // 画布高
            autoResize: true,
            background: { // 背景
              color: '#F2F7FA'
            },
            panning: {
              enabled: true // 支持滚动放大缩小
            },
            mousewheel: {
              enabled: true,
              modifiers: 'Ctrl', // 按住ctrl按键滚动鼠标滚轮缩放
              factor: 1.1,
              maxScale: 10, // 最大放大
              minScale: 0.05 // 最小缩小
            },
            grid: {
              visible: true, // 渲染网格背景
              type: 'doubleMesh',
              args: [
                {
                  color: '#eee', // 主网格线颜色
                  thickness: 1 // 主网格线宽度
                },
                {
                  color: '#ddd', // 次网格线颜色
                  thickness: 1, // 次网格线宽度
                  factor: 4 // 主次网格线间隔
                }
              ]
            }
          })
          this.graph.use( // 启用对齐线
            new Snapline({
              enabled: true
            })
          )
          // 鼠标移入线
          this.graph.on('edge:mouseenter', ({ e, edge, view }) => {
            edge.attr({
              line: {
                stroke: 'red',
                strokeWidth: 3
              }
            })
          })
          // 鼠标移出线
          this.graph.on('edge:mouseleave', ({ edge }) => {
            edge.attr({
              line: {
                stroke: '#8f8f8f',
                strokeWidth: 1
              }
            })
          })
        },
        // 获取数据
        init() {
          this.loading = true
          API.getData().then(res => {
            if (res.code === 200) {
              this.setGraphData(res)
            } else {
              this.$message.error(res.msg)
            }
          }).finally(() => {
            this.loading = false
          })
        },
        // 设置画布数据
        setGraphData(data) {
          // const X = document.getElementById('top-width').offsetWidth / 2 - 100 // 居中
          const X = 200
          this.data = {
            nodes: [],
            edges: []
          }
          const obj = {}
          // 转为对象数组 节点有可能顺序相同,顺序相同的配列在同一行
          data.node_data.map(item => {
            if (obj[item.order_id]) {
              obj[item.order_id].push(item)
            } else {
              obj[item.order_id] = []
              obj[item.order_id].push(item)
            }
          })
          // 遍历对象数组  通过遍历数组,将节点数据转为流程图中需要的数据类型
          Object.keys(obj).forEach((key, objIndex) => {
            obj[key].map((item, index) => {
              const node = {
                id: item.id, // 节点id
                shape: 'custom-rect', // 这是上边定义的节点类型
                label: item.name, // 节点名称
                x: X + 300 * index, // 节点x轴坐标 因为存在顺序相同的节点,需要排在同一行,但是y不一样
                y: 40 + 100 * objIndex, // 节点y轴坐标 顺序不同的节点,y轴坐标不同
                attrs: {
                  body: { // 这里是区分普通节点与开始结束节点的, 具体看效果图
                    rx: item.type_id === 0 ? 4 : 10,
                    ry: item.type_id === 0 ? 4 : 10
                  }
                }
              }
              this.data.nodes.push(node)
            })
          })
          // 遍历线的数据 通过遍历数组,将线数据转为流程图中需要的数据类型
          data.line_data.map((item, index) => {
            const obj = {
              id: item.id, // 线id
              shape: 'edge', // 类型为线
              source: item.destination_state_id, // 源节点
              target: item.source_state_id, // 目标节点
              labels: [ // 线名称样式
                {
                  attrs: {
                    label: {
                      text: item.name // 线名称
                    }
                  },
                  position: 0.4 // 名称在线的相对位置(0-1)一般为0.5
                }
              ],
              router: { // 线的路由
                name: 'manhattan', // 智能路由 移动节点时,线自动避免与节点接触
                args: { // 这里根据线的状态来判断线是从源节点的哪里开始,到目标节点的哪里结束
                // 值为1 线从源节点下方开始,到目标节点上方结束 // 值为2 线从源节点左方开始,到目标节点左方结束 // 值其他 线从源节点右方开始,到目标节点右方结束
                  startDirections: item.attribute_type_id === 1 ? ['bottom'] : item.attribute_type_id === 2 ? ['left'] : ['right'],
                  endDirections: item.attribute_type_id === 1 ? ['top'] : item.attribute_type_id === 2 ? ['left'] : ['right']
                }
              },
              tools: [{
                name: 'segments',
                args: {
                  snapRadius: 20,
                  attrs: {
                    fill: '#444'
                  }
                }
              }],
              attrs: { // 线样式
                line: {
                  stroke: '#8f8f8f',
                  strokeWidth: 1
                }
              }
            }
            this.data.edges.push(obj)
          })
          this.graph.fromJSON(this.data) // 渲染数据 将添加的节点与线画出来
        },
        zoomToFit() {
          this.graph.zoomToFit({
            padding: 20,
            preserveAspectRatio: true,
            maxScale: 1
          })
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215

    css代码

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    这几类外贸订单不要随意接
    C++ Tutorials: C++ Language: Other language features: Exceptions
    Java现在好找工作吗?
    vscode Remote SSH 报错及其解决方案
    数据结构题目收录(三)
    简单!这可能是最快速的个人博客搭建姿势!|原创
    openharmony容器组件之Grid
    基于java冰球馆管理系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署
    类复习【C#】
    Java - NPE(NullPointerException);Optional
  • 原文地址:https://blog.csdn.net/xuelong5201314/article/details/138075093