vue + gojs 实现拖拽流程图(实战项目)
后台-插件-广告管理-内容页头部广告(手机) |
一、流程图效果
最近一段时间在研究go.js,它是一款前端开发画流程图的一个插件,也是一个难点,要说为什么是难点,首先,它是依赖画布canvas知识开发。其次,要依赖于内部API开发需求,开发项目实战需求的时候就要花费大量的时间去熟悉go.js的API,然后才能进行开发。话不多说,我就先把我最近做的项目案例效果图展示一下:
看到效果图大家可能会想这个挺简单的,会想没什么难点,其实真正开发的时候才会知道的、才会领悟到。
二、为什么选go.js流程图插件去开发项目?
在项目开发一期的时候我用的不是go.js,而用的是一款轻便的流程插件jsplumb.js,它也集成了各种功能性API,但是在开发二期的时候它的内部功能已经满足不了需求了,所以我就开始在网上查找流程插件来学习,看了很多插件,比如:G6,D3 等这些可视化流程插件都是不能满足需求。要说为什么不能满足需求,原因如下:
一、首先,看到效果图里的内置多点和其他模块单点连线问题,其他插件是无法这个满足需求的,可能我没有深入去了解其他的流程插件吧,但是go.js里内置点连线可以让开发者很快的理解代码逻辑,不用耗费大量的时间去想点与点的连线。
二、代码上的数据结构问题,其他插件里的API数据字段繁琐量多,不够清晰明了,而go.js里的数据结构就两个重要字段,一是所有模块的字段集合二是连线字段集合,根据需求可以随意加字段。
三、项目开发
(一)、首先直接使用go.js,画布中是有水印的
其实这个问题不大,替换一行代码就可以去除水印
引入go.js后,直接在编辑器中全局搜索7eba17a4ca3b1a8346,找到类似这样结构的代码
a.ir=b.W[Ra("7eba17a4ca3b1a8346")][Ra("78a118b7")](b.W,ok,4,4);
注:不同的版本代码不是完全相同的,可能是a.jv(属性名是会变的) =‘xxxxx’,将这行代码替换成
a.ir=function(){return true;}; //a.属性名 要保持一致
去除水印的效果
(二)、HTML
<--第一种-->
- <template>
- <div id="wrap">
- <div id="chart-wrap">
- <div id="chart-palette">div><-- 画布一 -->
- <div id="chart-diagram">div><-- 画布二 -->
- div>
- div>
- template>
如图:
第二种
结合vue的拖拽组件vuedraggable 实现业务需求。
- <template>
- <div id="chart-wrap">
- <div v-for="tab in tabLIst" :key="tab.id" class="tab">
- //拖动
- <vuedraggable @end.stop="end" @start.stop="move">
- <i :class="tab.icon" /> {{ tab.text }}
- <el-tooltip effect="dark" :content="tab.tooltip" placement="top">
- <i class="el-icon-question" />
- el-tooltip>
- vuedraggable>
- div>
- <div id="chart-diagram"/> <--画布-->
- div>
- template>
如图:
(三)、画布的基本设置 。
- this.diagram = $(go.Diagram, "chart-diagram",
- {
- // 画布初始位置
- initialContentAlignment: go.Spot.LeftSide, // 居中显示
- "undoManager.isEnabled": true, // 支持 Ctrl-Z 和 Ctrl-Y 操作
- // 初始坐标
- // initialPosition: new go.Point(0, 0),
- //allowSelect:false, ///禁止选中
- // "toolManager.hoverDelay": 100, //tooltip提示显示延时
- // "toolManager.toolTipDuration": 10000, //tooltip持续显示时间
- // isReadOnly:true,//只读
- //禁止水平拖动画布
- //禁止水平滚动条
- allowHorizontalScroll: false,
- // 禁止垂直拖动画布
- //禁止垂直滚动条
- allowVerticalScroll: false,
- allowZoom: true,//画布是否可以缩放
- "grid.visible": false, //显示网格
- // allowMove: true, //允许拖动
- // allowDragOut:true,
- allowDelete: true,//禁止删除节点
- allowCopy: true,//禁止复制
- // 禁止撤销和重做
- // "undoManager.isEnabled": false,
- // 画布比例
- // scale:1.5,
- // minScale:1.2,//画布最小比例
- // maxScale:2.0,//画布最大比例
- // 画布初始化动画时间
- // "animationManager.duration": 600,
- // 禁止画布初始化动画
- "animationManager.isEnabled": false,
- // autoScale:go.Diagram.Uniform,//自适应
- // autoScale:go.Diagram.UniformToFill,//自适应
- // "draggingTool.dragsLink": false,//拖动线
- // autoScale:go.Diagram.None,//默认值不自适应
- // 画布边距padding
- // padding:80或者new go.Margin(2, 0)或new go.Margin(1, 0, 0, 1)
- // validCycle: go.Diagram.CycleDestinationTree,//只允许有一个父节点
- //节点模块动画 S
- // "animationManager.initialAnimationStyle":go.Animation.EaseOutExpo,
- // "animationManager.initialAnimationStyle": go.Animation.EaseInOutQuad,
- "animationManager.initialAnimationStyle": go.AnimationManager.None,
- // "animationManager.initialAnimationStyle":go.AnimationManager.AnimateLocations,
- //节点模块动画 D
- // validCycle: go.Diagram.CycleNotUndirected,
- // validCycle: go.Diagram.CycleNotDirected,
- // validCycle: go.Diagram.CycleSourceTree,
- //ismodelfied:true //禁止拖拽
- // 禁止鼠标拖动区域选中
- // "dragSelectingTool.isEnabled" : false,
- //允许使用delete键删除模块
- "commandHandler.deletesTree": true,
- // "hasHorizontalScrollbar":false,//去除水平滚动条
- // "hasVerticalScrollbar":false,//去除竖直滚动条
- // "canStart":false,
- // allowClipboard: true,
- // "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom, //有鼠标滚轮事件放大和缩小,而不是向上和向下滚动
- // layout: $(go.TreeLayout,
- // { angle: 90, layerSpacing: 80 }),
- }
- );
(三)、整体画布事件及节点的监听
- // 监听连线
- this.diagram.addDiagramListener("LinkDrawn", (e) => {
- console.log(e.subject.part);
- });
- // 监听删除
- this.diagram.addDiagramListener("SelectionDeleted", (e) => {
- e.subject.each(function (n) {
- console.log(n.data.key);
- });
- })
- // 修改节点
- this.diagram.addDiagramListener("TextEdited", (evt) => {
- console.log(e.subject.part);
- });
- // 监听点击
- this.diagram.addDiagramListener("ObjectSingleClicked", (e) => {
- //这是清除高亮的
- // e.diagram.commit((d) => {
- // d.clearHighlighteds();
- // }, "no highlighteds");
- });
- // // 移动事件
- this.diagram.addDiagramListener("SelectionMoved", (e) => {
- console.log(e.diagram.lastInput.documentPoint);
- });
(四)、连线点封装函数
// 节点连接线
- makePort (name, align, spot, output, input) {
- var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom)
- return $(go.Shape, {
- fill: 'transparent', // 默认透明不现实
- strokeWidth: 0, // 无边框
- fromMaxLinks: 1, //
- width: horizontal ? NaN : 10, // 垂直"port"则8像素宽
- height: !horizontal ? NaN : 5, // 水平"port"则8像素
- alignment: align, // 同其模块对齐
- stretch: horizontal
- ? go.GraphObject.Horizontal
- : go.GraphObject.Vertical, // 自动同其模块一同伸缩
- portId: name, // 声明ID
- fromSpot: spot, // 声明连线头连出此"port"的位置
- fromLinkable: output, // 布尔型,是否允许连线从此"port"连出
- toLinkable: input, // 布尔型,是否允许连线从此"port"连出
- toSpot: spot, // 声明连线尾连入此"port"的位置
- cursor: 'pointer', // 鼠标由指针改为手指,表示此处可点击生成连线
- mouseEnter: function (e, port) {
- // 鼠标移到"port"位置后,高亮
- if (!e.diagram.isReadOnly) port.fill = 'rgba(255,0,255,0.3)'
- },
- mouseLeave: function (e, port) {
- // 鼠标移出"port"位置后,透明
- port.fill = 'transparent'
- }
- })
- }
(五)、节点连线的高亮函数。
- isHighlightedFun(link) {
- return $$(go.Shape,
- "RoundedRectangle", {
- fill: "rgba(217,236,255,.2)",
- stroke: "rgba(39,154,242,.1)",
- strokeWidth: 1,
- },
- new go.Binding("stroke", "isHighlighted", (h) => {
- return h ? "rgba(39,154,242,1)" : "rgba(39,154,242,.8)";
- }).ofObject(),
- new go.Binding("strokeWidth", "isHighlighted", (h) => {
- return h ? 2.5 : 1.3;
- }).ofObject(),
- )
- }
如图:
(六)、 节点连接线。(方法封装)
- makePort(name, align, spot, output, input) {
- var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
- return $$(go.Shape,
- {
- fill: "transparent", // 默认透明
- strokeWidth: 0, // 无边框
- fromMaxLinks: 1, //最大连接数
- width: horizontal ? NaN : 10, // 垂直"port"则8像素宽
- height: !horizontal ? NaN : 2, // 水平"port"则8像素
- alignment: align, // 同其模块对齐
- stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),//自动同其模块一同伸缩
- portId: name, // 声明ID
- fromSpot: spot, // 声明连线头连出此"port"的位置
- fromLinkable: output, // 布尔型,是否允许连线从此"port"连出
- toLinkable: input, // 布尔型,是否允许连线从此"port"连出
- toSpot: spot, // 声明连线尾连入此"port"的位置
- cursor: "pointer", // 鼠标由指针改为手指,表示此处可点击生成连线
- mouseEnter: (e, port) => { //鼠标移到"port"位置后,高亮
- if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.3)";
- },
- mouseLeave: (e, port) => {// 鼠标移出"port"位置后,透明
- port.fill = "transparent";
- }
- });
- }
注意:要在this.diagram.nodeTemplateMap.add 增加模块里调用。
示例:
- this.diagram.nodeTemplateMap.add(type,
- $$(go.Node, "Auto",
- this.makePort("T", go.Spot.Top, go.Spot.TopCenter, false, true)
- ))
四、当前画布的监听事件名称包括
"AnimationStarting" :动画渲染前事件,加载图表的动画即将开始;
"AnimationFinished" :动画渲染完事件,加载图表刚刚完成的动画;
"BackgroundSingleClicked": 背景单击事件, 单击图表背景;
"BackgroundDoubleClicked" :背景双击事件, 双击图表背景;
"BackgroundContextClicked" :背景右键事件, 右键单击图表背景;
"ChangingSelection": 改变选择前事件, 一个操作即将改变Diagram.selection图表选择集合,
"ChangedSelection" :改变选择后事件, 一个操作已经改变?Diagram.selection图表选择集合,
"ClipboardChanged" :剪切板改变事件, 零部件已被CommandHandler.copySelection复制到剪贴板上;
"ClipboardPasted" :剪切板粘贴事件,零部件已由CommandHandler.pasteSelection从剪贴板复制到图表中;
"DocumentBoundsChanged": 文档范围改变事件, 图表中各零部件的面积,?Diagram.documentBounds,已经改变了;
"ExternalObjectsDropped" :(节点或线等)零部件拖放生成事件, 零部件已经通过拖拽从图的外部复制到图中;
"GainedFocus" :获得键盘焦点事件, 该图获得了键盘焦点,例如在调用Diagram.focus之后.
"LayoutCompleted" :布局完成事件, 整个图表布局刚刚更新;
"LinkDrawn": 线创建事件,用户刚刚使用LinkingTool创建了一个新链接;?
"LinkRelinked": 线重新连接事件, 用户刚刚通过RelinkingTool?或?DraggingTool重新连接了现有线;
"LinkReshaped": 线路径改变事件, 用户刚刚通过LinkReshapingTool调整了线的路径;
"LostFocus" :图表失去焦点事件,这个图表失去了键盘焦点,
"Modified" :图表改变事件,?Diagram.isModified?属性已被设置为一个新值——用于将窗口标记为自上次保存以来已被修改;
"ObjectSingleClicked": 对象单击事件, 单击了图形对象(节点和线等);
"ObjectDoubleClicked":双击了图形对象(节点和线等);
"ObjectContextClicked":右键单击了图形对象(节点和线等);
"PartCreated" :Part创建事件,用户通过?ClickCreatingTool插入新的零部件;
"PartResized" :Part大小改变事件, 用户通过ResizingTool 调整工具改变了一个图形对象的大小;
"PartRotated" :Part旋转事件, 用户通过RotatingTool 旋转工具改变了一个图形对象的角度 ;
"SelectionMoved" :拖动事件, 用户通过DraggingTool 拖动工具移动了选定的部分;
"SelectionCopied" :复制事件,户通过DraggingTool 拖动工具复制了选定的部分;
"SelectionDeleted" :删除后事件,用户通过?CommandHandler.deleteSelection?已经删除了选定的部分;
"SelectionDeleting" :删除前事件,用户通过?CommandHandler.deleteSelection即将删除选定的部分;
"SelectionGrouped" :选择创建分组事件, 通过CommandHandler.groupSelection已经从选择的零部件中创建了一个新的组 ;
"SelectionUngrouped":用户已删除选定的组,但通过CommandHandler.ungroupSelection保留其成员;
"SubGraphCollapsed" :子图折叠事件, 用户通过CommandHandler.collapseSubGraph将选定的组折叠;
"SubGraphExpanded" :子图展开事件, 用户通过CommandHandler.expandSubGraph将选定的组展开;
"TextEdited" :文本块修改事件,用户通过文本编辑工具改变了文本块的字符串值;
"TreeCollapsed": 树折叠事件, 用户通过CommandHandler.collapseTree折叠所选节点的子树;
"TreeExpanded" :树展开事件, 用户通过CommandHandler.expandTree展开了所选节点的子树;
"ViewportBoundsChanged":视窗范围改变事件,图表中可见的区域,?Diagram.viewportBounds,发生了改变;
五、迭代篇
(一)、项目的迭代。
从数据结构到ui风格的整体重构,有了新的变化。
整体流程 如图一:
模块流程侧边设置图二:
伸缩当前灰色意图如图三:
为什么收起意图会有多条连线,是因为当前模块下的意图和部分模块有关系线,所以在收起时要知道当前模块下有几个意图和其他模块有关系。
(二)、整体用的js方法
1、画布里的功能js:
2、工具条js 方法:
(三)、功能分析
1、工具条中的功能方法
(1)、查看
注: 是通过gojs 实例中的makeSVG方法打开一个新窗口生成的一个svg
- printDiagram() {
- var svgWindow = window.open();
- if (!svgWindow) return;
- var printSize = new go.Size(3000, 2000);
- var bnds = this.diagram.documentBounds;
- var x = bnds.x;
- var y = bnds.y;
- while (y < bnds.bottom) {
- while (x < bnds.right) {
- var svg = this.diagram.makeSVG({scale: 1.0, position: new go.Point(x, y), size: printSize});
- svgWindow.document.body.appendChild(svg);
- x += printSize.width;
- }
- x = bnds.x;
- y += printSize.height;
- }
- }
(2)、居中
这里将调用CommandHandler.scrollToPart,先还原画布初始状态后执行动画以将视口滚动到节点所在的位置。
注:如果节点靠近边缘,除非您增加Diagram.scrollMargin,否则无法滚动视口,以使该节点更靠近视口的中心。
- centerDiagram() {
- this.resetCanvas();
- this.diagram.commandHandler.scrollToPart(this.diagram.findNodeForKey(this.initModuleId));
- },
- // 还原
- resetCanvas() {
- this.diagram.scale = 1;
- this.$refs.set.zoom = 100;
- },
尽请等待周六周日继续完善。如有不足可共享技术经验。。。。。。。。。。。。。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |