Vue.js实现可托拉拽流程图项目

最近一直在研究流程图相关的技术,一次在逛GitHub时发现了一个技术栈为vue+g6+element-ui的项目,基础功能完好,如node与edge的托拉拽,主界面如下:

image.png

GitHub链接为:https://github.com/caoyu48/vue-g6-editor

线上访问地址为(已经失效了):http://62.234.69.136/

g6官方API文档:https://antv-g6.gitee.io/zh/docs/manual/introduction

但由于作者没有写代码的说明文档,本文仅仅只是我本人对读该源码的一些理解,如有不同理解还希望各位朋友指出订正。

一、本地运行

首先从上面的GitHub网址下载该项目,下载该项目需要的依赖包。这里很多人下载完依赖包之后发现启动报错无法运行,需要注意的是这里

不要使用cnpm install,一定要使用npm install!!!具体为什么cnpm不行,我也不知道。

二、给连线加上文字[本人自己增加]

1、修改src/components/DetailPanel/index.vue

<template>
  <div class="detailpannel">
    <div>
      <div v-if="status=='node-selected'" class="pannel" id="node_detailpannel">
        <div class="pannel-title">模型详情</div>
        <div class="block-container">
          <el-row :gutter="10">
            <el-col :span="8">名称</el-col>
            <el-col :span="16">
              <el-input v-model="node.label" @change="handleChangeName" />
            </el-col>
            <el-col :span="8">任意属性</el-col>
            <el-col :span="16">
              <el-input v-model="node.xxx" />
            </el-col>
          </el-row>
        </div>
      </div>
      <div v-if="status==='canvas-selected'" class="pannel" id="canvas_detailpannel">
        <div class="pannel-title">画布</div>
        <div class="block-container">
          <el-checkbox v-model="showGrid" @change="changeGridState">网格对齐</el-checkbox>
        </div>
      </div>
   <!--我添加的-->
      <div v-if="status === 'edge-selected'" id="edge_detailpannel" class="pannel">
        <div class="pannel-title">连线</div>
        <div class="block-container">
          <el-col :span="8">内容</el-col>
          <el-col :span="16">
            <el-input v-model="edge.label" @change="handleChange" />
          </el-col>
          <el-col :span="8">文字颜色</el-col>
          <el-col :span="16">
            <el-color-picker v-model="textColor" @change="handleChangeColor" />
          </el-col>
        </div>
      </div>
      <!-- <div v-if="status==='group-selected'" class="pannel" id="node_detailpannel">
        <div class="pannel-title">群组详情</div>
        <div class="block-container">
          <div class="p">
            名称:
            <el-input v-model="name" />
          </div>
          <div class="p">
            任意属性:
            <el-input v-model="color" />
          </div>
        </div>
      </div>
      -->
    </div>
  </div>
</template>

<script>
import eventBus from "@/utils/eventBus";
import Grid from "@antv/g6/build/grid";
export default {
  data() {
    return {
      status: "canvas-selected",
      showGrid: false,
      page: {},
      graph: {},
      item: {},
      node: {},
   //【我添加的】
      edge:{},
      grid: null,
 //【我添加的】 
      textColor: 'rgba(19, 206, 102, 0.8)'
    };
  },
  created() {
    this.init();
    this.bindEvent();
  },
  methods: {
    init() {},
    bindEvent() {
      let self = this;
      eventBus.$on("afterAddPage", page => {
        self.page = page;
        self.graph = self.page.graph;
        eventBus.$on("nodeselectchange", item => {
          if (item.select === true && item.target.getType() === "node") {
            self.status = "node-selected";
            self.item = item.target;
            self.node = item.target.getModel();
          } 
       //【我添加的】 
      else if (item.select === true && item.target.getType() === "edge") {
            self.status = "edge-selected";
            self.item = item.target;
            self.edge = item.target.getModel();
          } 
       else {
            self.status = "canvas-selected";
            self.item = null;
            self.node = null;
          }
        });
      });
    },
    handleChangeName(e) {
      const model = {
        label: e
      };
      this.graph.update(this.item, model);
    },
    changeGridState(value) {
      if (value) {
        this.grid = new Grid();
        this.graph.addPlugin(this.grid);
      } else {
        this.graph.removePlugin(this.grid);
      }
    },
  //【我添加的】
   handleChange(e) {
      const model = {
        label: e
      };
      console.log(model)
      this.graph.update(this.item, model);
    },
    handleChangeColor(e) {
      const model = {
        textColor: e
      };
      this.graph.update(this .item, model);
    }
  }
};
</script>

<style scoped>
.detailpannel {
  height: 100%;
  position: absolute;
  right: 0px;
  z-index: 2;
  background: #f7f9fb;
  width: 200px;
  border-left: 1px solid #e6e9ed;
}
.detailpannel .block-container {
  padding: 16px 8px;
}
.block-container .el-col {
  height: 28px;
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}
.pannel-title {
  height: 32px;
  border-top: 1px solid #dce3e8;
  border-bottom: 1px solid #dce3e8;
  background: #ebeef2;
  color: #000;
  line-height: 28px;
  padding-left: 12px;
}
</style>

2、修改src/components/Flow/customEdge.js

import G6 from "@antv/g6/build/g6";
import { uniqueId } from '@/utils'
const MIN_ARROW_SIZE = 3

const customEdge = {
  init() {
    const dashArray = [
      [0, 1],
      [0, 2],
      [1, 2],
      [0, 1, 1, 2],
      [0, 2, 1, 2],
      [1, 2, 1, 2],
      [2, 2, 1, 2],
      [3, 2, 1, 2],
      [4, 2, 1, 2]
    ];

    const lineDash = [4,2,1,2];
    const interval = 9;
    G6.registerEdge('customEdge', {
      draw(cfg, group) {
        let sourceNode, targetNode, start, end
        if (typeof (cfg.souxrce) === 'string') {
          cfg.source = cfg.sourceNode
        }
        if(!cfg.start){
          cfg.start={
            x:0,
            y:17
          }
        }
        if(!cfg.end){
          cfg.end={
            x:0,
            y:-17
          }
        }
        if (!cfg.source.x) {
          sourceNode = cfg.source.getModel()
          start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y }
        } else {
          start = cfg.source
        }
        if (typeof (cfg.target) === 'string') {
          cfg.target = cfg.targetNode
        }
        if (!cfg.target.x) {

          targetNode = cfg.target.getModel()
          end = { x: targetNode.x + cfg.end.x, y: targetNode.y +  cfg.end.y }
        } else {
          end = cfg.target
        }

        let path = []
        let hgap = Math.abs(end.x - start.x)
        if (end.x > start.x) {
          path = [
            ['M', start.x, start.y],
            [
              'C',
              start.x,
              start.y + hgap / (hgap / 50),
              end.x,
              end.y - hgap / (hgap / 50),
              end.x,
              end.y - 4
            ],
            [
              'L',
              end.x,
              end.y
            ]
          ]
        } else {
          path = [
            ['M', start.x, start.y],
            [
              'C',
              start.x,
              start.y + hgap / (hgap / 50),
              end.x,
              end.y - hgap / (hgap / 50),
              end.x,
              end.y - 4
            ],
            [
              'L',
              end.x,
              end.y
            ]
          ]
        }
        let lineWidth = 1;
        lineWidth = lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE;
        const width = lineWidth * 10 / 3;
        const halfHeight = lineWidth * 4 / 3;
        const radius = lineWidth * 4;
        const endArrowPath = [
          ['M', -width, halfHeight],
          ['L', 0, 0],
          ['L', -width, -halfHeight],
          ['A', radius, radius, 0, 0, 1, -width, halfHeight],
          ['Z']
        ];
        const keyShape = group.addShape('path', {
          attrs: {
            id: 'edge' + uniqueId(),
            path: path,
            stroke: '#b8c3ce',
            lineAppendWidth: 10,
            endArrow: {
              path: endArrowPath,
            }
          }
        });
     //此处是我修改的,增加连线的样式即线上文本 if (cfg.label) {
          group.addShape('text', {
            attrs: {
              id: 'edgeText' + uniqueId(),
              x: end.x - (end.x - start.x) / 2,
              y: end.y - (end.y - start.y) / 2,
              text: cfg.label,
              fill: cfg.textColor ? cfg.textColor : '#000000'
            }
          })
        } return keyShape
      },
      afterDraw(cfg, group) {
        if (cfg.source.getModel().isDoingStart && cfg.target.getModel().isDoingEnd) {
          const shape = group.get('children')[0];
          const length = shape.getTotalLength(); // G 增加了 totalLength 的接口
          let totalArray = [];
          for (var i = 0; i < length; i += interval) {
            totalArray = totalArray.concat(lineDash);
          }
          let index = 0;
          shape.animate({
            onFrame() {
              const cfg = {
                lineDash: dashArray[index].concat(totalArray)
              };
              index = (index + 1) % interval;
              return cfg;
            },
            repeat: true
          }, 3000);
        }
      },
      setState(name, value, item) {
        const group = item.getContainer();
        const shape = group.get("children")[0];
        const selectStyles = () => {
          shape.attr("stroke", "#6ab7ff");
        };
        const unSelectStyles = () => {
          shape.attr("stroke", "#b8c3ce");
        };

        switch (name) {
          case "selected":
          case "hover":
            if (value) {
              selectStyles()
            } else {
              unSelectStyles()
            }
            break;
        }
      }
    });
    G6.registerEdge('link-edge', {
      draw(cfg, group) {
        let sourceNode, targetNode, start, end
        if (!cfg.source.x) {
          sourceNode = cfg.source.getModel()
          start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y }
        } else {
          start = cfg.source
        }
        if (!cfg.target.x) {
          targetNode = cfg.target.getModel()
          end = { x: targetNode.x + cfg.end.x, y: targetNode.y + cfg.end.y }
        } else {
          end = cfg.target
        }

        let path = []
        path = [
          ['M', start.x, start.y],
          ['L', end.x, end.y]
        ]
        const keyShape = group.addShape('path', {
          attrs: {
            id: 'edge' + uniqueId(),
            path: path,
            stroke: '#1890FF',
            strokeOpacity: 0.9,
            lineDash: [5, 5]
          }
        });
        return keyShape
      },
    });
  }
}

export default customEdge

三、发现的BUG

当删除文本框中的内容时,会发现连节点也删除了,解决办法就是修改src/behavior/keyboard.js

文末小彩蛋,自建摸鱼网站,各大网站热搜一览,上班和摸鱼很配哦!

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
实现 WPF 中的可拉控件生成,可以使用拖放操作。以下是一个基本的示例: 首先,在 XAML 中设置一个源控件,允许它被拖动。例如,你可以使用一个 Button 控件,并在 MouseDown 事件处理程序中启动一个拖动操作: ``` <Button Content="Drag Me" MouseDown="Button_MouseDown"/> ``` 在 MouseDown 事件处理程序中,可以使用 DragDrop.DoDragDrop 方法启动拖放操作。此方法需要三个参数:源控件、要传递的数据和拖动操作的类型。例如: ``` private void Button_MouseDown(object sender, MouseButtonEventArgs e) { DragDrop.DoDragDrop((Button)sender, "Some data to drag", DragDropEffects.Copy); } ``` 接下来,在 XAML 中设置一个接收控件,允许它接收拖放的数据。例如,你可以使用一个 StackPanel 控件,并在 Drop 事件处理程序中处理拖放操作: ``` <StackPanel Drop="StackPanel_Drop"/> ``` 在 Drop 事件处理程序中,可以使用 e.Data.GetData 方法获取传递的数据,并创建一个新控件以显示该数据。例如: ``` private void StackPanel_Drop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.StringFormat)) { string data = (string)e.Data.GetData(DataFormats.StringFormat); Button newButton = new Button() { Content = data }; ((StackPanel)sender).Children.Add(newButton); } } ``` 这将在 StackPanel 中添加一个新的 Button 控件,其中包含拖放操作中传递的数据。你可以根据需要自定义此过程,例如,可以使用其他控件作为源控件,将更多数据传递给接收控件等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sum墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值