第一步:需要一个flow.js作为依据,可自取。

/************************************************

* 业务流程显示、绘制基础类

* */

// 扩展图形和文本拖动

(function (R) {

R.el.draggable = function (move, start, up) {

this._ui = this._ui || {};

var that = this;

this._ui.onMove = R.is(move, 'function') ? move : function (distanceX, distanceY, x, y, deltaX, deltaY) {

that.translate(deltaX, deltaY);

};

this._ui.onStart = R.is(start, 'function') ? start : function (x, y) { };

this._ui.onEnd = R.is(up, 'function') ? up : function (x, y) { };

function onMove(distanceX, distanceY, x, y) {

var deltaX = x - that._ui.lastX;

var deltaY = y - that._ui.lastY;

that._ui.lastX = x;

that._ui.lastY = y;

if (that.label) {

that.label.attr({ x: that.labelPoint.x + deltaX, y: that.labelPoint.y + deltaY });

that.labelPoint.x = that.labelPoint.x + deltaX;

that.labelPoint.y = that.labelPoint.y + deltaY;

}

that._ui.onMove(distanceX, distanceY, x, y, deltaX, deltaY);

try {

that.paper.safari();

} catch (e) {

console.log("not safari brower, don't care this message.");

}

};

function onStart(x, y) {

that._ui.lastX = x;

that._ui.lastY = y;

that._ui.ddx = x - that.cfg.posLeft;

that._ui.ddy = y - that.cfg.posTop;

that._ui.onStart(x, y);

};

function onEnd(event) {

that.cfg.posLeft = event.x - that._ui.ddx;

that.cfg.posTop = event.y - that._ui.ddy;

that._ui.onEnd(event.x, event.y);

};

return this.drag(onMove, onStart, onEnd);

};

// 每个业务业务流程对应一个 BF 对象

// BF 包含一组静态对象,用于设置节点、连线的样式

// BF 包含一个对象组,包括:大按钮、流程节点(含:链接、分支)、连线、文本;这些对象支持选中、删除、拖动等操作。

BF = {

// 大按钮

ButtonTextStyle: { "font-size": "14pt", "stroke": "#eaeeff", "font-weight": "1", "font-family":"helvetica, arial, verdana, sans-serif"},

ButtonBoxStyle: { "stroke": "#054ea7", "stroke-width": "1", "fill": "#386aa3" },

ButtonLinkStyle: { "font-size": "14pt", "stroke": "#eaeeff", "font-weight": "100", cursor: "pointer" },

ButtonSelectedStyle: { "stroke": "#bd760d", "stroke-width": "2px", "fill": "#bd760d", cursor: "move" },

ButtonProperty: { nodeIdx: 1, nodeType: "button", posLeft: 30, posTop: 30, nodeWidth: 120, nodeHeight: 75, text: "子模块", title: "", jsurl: "" },

// 流程节点

NodeTextStyle: { "font-size": "12px", "stroke": "#09095a", "font-family":"helvetica, arial, verdana, sans-serif" },

NodeBoxStyle: { "stroke": "#b8b8b8", "stroke-width": 1, "fill": "#e5e9f1" },

NodeSelectedStyle: { "stroke": "#bd760d", "stroke-width": "2px", "fill": "#bd760d", cursor: "move" },

NodeLinkStyle: { "font-size": "12px", "stroke": "#093eec", "text-decoration": "underline", cursor: "pointer" },

NodeProperty: { nodeIdx: 2, nodeType: "node", posLeft: 30, posTop: 100, nodeWidth: 120, nodeHeight: 75, text: "流程节点", title: "", inLine: "", outLine: "", jsurl: "" },

BranchProperty: { nodeIdx: 3, nodeType: "branch", posLeft: 30, posTop: 180, nodeWidth: 120, nodeHeight: 75, text: "流程分支", title: "", inLine: "", outLine: "", jsurl: "" },

// 连线

LineTextStyle: { "font-size": "12px", "stroke": "#09095a", "font-family":"helvetica, arial, verdana, sans-serif"},

LineStyle: { "arrow-end": "classic-wide-long", "stroke": "#9baaaf", "stroke-width": 2 },

LineSelectedStyle: { "arrow-end": "classic-wide-long", "stroke": "#d2d426", "stroke-width": 2, cursor: "move" },

LineProperty: { nodeIdx: 4, nodeType: "line", posLeft: 30, posTop: 30, endX: 60, endY: 60, text: "", title: "" },

// 流程备注文本

RemarkBoxStyle: { "stroke": "#cdd0d8", "stroke-width": 1, "fill": "#e5e9f1", "opacity": 30 },

RemarkTextStyle: { "font-size": "12px", "stroke": "#130205", "text-anchor": "start", "font-family":"helvetica, arial, verdana, sans-serif" },

RemarkSelectedStyle: { "font-size": "12px", "stroke": "#bd760d" },

RemarkProperty: { nodeIdx: 5, nodeType: "remark", posLeft: 30, posTop: 270, nodeWidth: 300, nodeHeight: 185, text: "备注:\n这是一段备注文字" },

// 热区样式

HotPointStyle: { "stroke": "#d1d86f", "stroke-width": 1, "fill": "#f34832" },

// 复制属性的函数

ocopy: function (o, c) {

if (o && c && typeof c == 'object') {

for (var p in c) {

o[p] = c[p];

}

}

return o;

},

// 选中节点时的回调函数,调用程序给出,传入选中的图形序号

OnSelectNode: null,

// 划线状态是否已开启

DrawLineMode: false,

// 半径 5 像素的圆圈内认为是热点

HotDistance: 5,

// 执行流程图中点击动作的函数,参数为这里设置的 url

OnClickRun: null,

// 画布尺寸

PaperWidth: 2000,

PaperHeight: 1800

};

// 构造一个业务流程图

// 必须参数 canvas, 画布,指定一个 div 的名称

// 必须参数 ploting, true表示绘制,可以增删改图形;false 表示静态展示流程图

// 可选参数 nodes 为流程图节点配置结构,缺失将仅显示开始和结束两个节点,config 的数据结构如下

BF.create = function (canvas, ploting, nodes) {

var bf = {};

// 初始化画布

bf.paper = R(canvas, BF.PaperWidth, BF.PaperHeight);

// 对象容器,存储绘制的节点+绘制的连线;只包括其配置信息

bf.container = new Array();

// 上述容器中最后选中的对象的序号,-1 表示没有选中任何对象

bf.lastSelect = -1;

// 当前未使用的最大节点序号, 1--9999

bf.maxNodeIndex = 2;

// 判断一条线是否连接在一个图形的热点

bf.ishot = function (lineCfg, boxCfg) {

if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth / 2 - lineCfg.posLeft) + Math.abs(boxCfg.posTop - lineCfg.posTop) < 5) { // 线起点 = 上中点

return 1;

} else if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth - lineCfg.posLeft) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight / 2 - lineCfg.posTop) < 5) { // 线起点 = 右中点

return 2;

} else if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth / 2 - lineCfg.posLeft) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight - lineCfg.posTop) < 5) { // 线起点 = 下中点

return 3;

} else if (Math.abs(boxCfg.posLeft - lineCfg.posLeft) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight / 2 - lineCfg.posTop) < 5) { // 线起点 = 左中点

return 4;

} else if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth / 2 - lineCfg.endX) + Math.abs(boxCfg.posTop - lineCfg.endY) < 5) { // 线终点 = 上中点

return 5;

} else if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth - lineCfg.endX) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight / 2 - lineCfg.endY) < 5) { // 线终点 = 右中点

return 6;

} else if (Math.abs(boxCfg.posLeft + boxCfg.nodeWidth / 2 - lineCfg.endX) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight - lineCfg.endY) < 5) { // 线终点 = 下中点

return 7;

} else if (Math.abs(boxCfg.posLeft - lineCfg.endX) + Math.abs(boxCfg.posTop + boxCfg.nodeHeight / 2 - lineCfg.endY) < 5) { // 线终点 = 左中点

return 8;

} else {

return 0;

}

};

// 开启连线绘制,显示所有热区点,禁用所有拖动

bf.hotpoints = new Array();

bf.startLine = function () {

for (var z = 0; z < bf.container.length; z++) {

var obj = bf.container[z].img;

if (obj.cfg.nodeType == "button" || obj.cfg.nodeType == "node") {

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth / 2, obj.cfg.posTop, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth, obj.cfg.posTop + obj.cfg.nodeHeight / 2, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth / 2, obj.cfg.posTop + obj.cfg.nodeHeight, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft, obj.cfg.posTop + obj.cfg.nodeHeight / 2, 5).attr(BF.HotPointStyle));

} else if (obj.cfg.nodeType == "branch") {

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth / 2, obj.cfg.posTop, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth, obj.cfg.posTop + obj.cfg.nodeHeight / 2, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft + obj.cfg.nodeWidth / 2, obj.cfg.posTop + obj.cfg.nodeHeight, 5).attr(BF.HotPointStyle));

bf.hotpoints.push(bf.paper.circle(obj.cfg.posLeft, obj.cfg.posTop + obj.cfg.nodeHeight / 2, 5).attr(BF.HotPointStyle));

}

obj.undrag();

}

BF.DrawLineMode = true;

};

// 停止划线时,清除所有热点,恢复可以拖动

bf.stopLine = function () {

for (var i = bf.hotpoints.length - 1; i >= 0; i--) {

bf.hotpoints[i].remove();

}

bf.hotpoints = new Array();

for (var z = 0; z < bf.container.length; z++) {

var obj = bf.container[z].img;

obj.draggable();

}

BF.DrawLineMode = false;

};

// 执行一个JS函数,要求全命名空间且已实例化

bf.runFunc = function (jsurl) {

if (typeof (BF.OnClickRun) == "function") {

BF.OnClickRun(jsurl);

}else{

console.log("尚未指定调用函数 BF.OnClickRun");

}

};

// 一个绘图对象的绘制

bf.rimg = function (plot, cfg) {

// 属性确定

var imgStyle = { title: cfg.title };

var textStyle = { title: cfg.title };

var selStyle = { title: cfg.title };

if (cfg.nodeType == 'button') {

imgStyle = BF.ocopy(imgStyle, BF.ButtonBoxStyle);

selStyle = BF.ocopy(selStyle, BF.ButtonSelectedStyle);

if (cfg.jsurl != "") {

textStyle = BF.ocopy(textStyle, BF.ButtonLinkStyle);

} else {

textStyle = BF.ocopy(textStyle, BF.ButtonTextStyle);

}

} else if (cfg.nodeType == 'node' || cfg.nodeType == 'branch') {

imgStyle = BF.ocopy(imgStyle, BF.NodeBoxStyle);

if (cfg.jsurl != "") {

textStyle = BF.ocopy(textStyle, BF.NodeLinkStyle);

} else {

textStyle = BF.ocopy(textStyle, BF.NodeTextStyle);

}

selStyle = BF.ocopy(selStyle, BF.NodeSelectedStyle);

} else if (cfg.nodeType == 'line') {

imgStyle = BF.ocopy(imgStyle, BF.LineStyle);

textStyle = BF.ocopy(textStyle, BF.LineTextStyle);

selStyle = BF.ocopy(selStyle, BF.LineSelectedStyle);

} else if (cfg.nodeType == 'remark') {

imgStyle = BF.ocopy(imgStyle, BF.RemarkBoxStyle);

textStyle = BF.ocopy(textStyle, BF.RemarkTextStyle);

selStyle = BF.ocopy(selStyle, BF.RemarkSelectedStyle);

}

// 绘制图形

if (cfg.nodeType == "button" || cfg.nodeType == "node" || cfg.nodeType == "branch") {

if (cfg.nodeType == "branch") {

// 画闭合折线 M130,30 L200,30 L160,90 z

this.img = bf.paper.path("M" + cfg.posLeft + "," + (cfg.posTop + cfg.nodeHeight / 2) + " L" + (cfg.posLeft + cfg.nodeWidth / 2) + "," + cfg.posTop + " L" + (cfg.posLeft + cfg.nodeWidth) + "," + (cfg.posTop + cfg.nodeHeight / 2) + " L" + (cfg.posLeft + cfg.nodeWidth / 2) + "," + (cfg.posTop + cfg.nodeHeight) + " z").attr(imgStyle);

} else {

this.img = bf.paper.rect(cfg.posLeft, cfg.posTop, cfg.nodeWidth, cfg.nodeHeight, 0).attr(imgStyle);

}

this.img.labelPoint = { x: cfg.posLeft + cfg.nodeWidth / 2, y: cfg.posTop + cfg.nodeHeight / 2 };

this.img.label = bf.paper.text(this.img.labelPoint.x, this.img.labelPoint.y, cfg.text).attr(textStyle);

} else if (cfg.nodeType == "line") {

this.img = bf.paper.path("M" + cfg.posLeft + "," + cfg.posTop + " L" + cfg.endX + "," + cfg.endY).attr(imgStyle);

if (cfg.text != "") {

if (Math.abs(cfg.endY - cfg.posTop) < 5) { // 视作水平

this.img.labelPoint = { x: (cfg.posLeft + cfg.endX) / 2, y: cfg.endY + 20 };

this.img.label = bf.paper.text(this.img.labelPoint.x, this.img.labelPoint.y, cfg.text).attr(textStyle);

} else if (Math.abs(cfg.endX - cfg.posLeft) < 5) { // 视作垂直

this.img.labelPoint = { x: cfg.posLeft + 20, y: (cfg.posTop + cfg.endY) / 2 };

this.img.label = bf.paper.text(this.img.labelPoint.x, this.img.labelPoint.y, cfg.text).attr(textStyle);

}

}

} else if (cfg.nodeType == "remark") {

this.img = bf.paper.rect(cfg.posLeft, cfg.posTop, cfg.nodeWidth, cfg.nodeHeight, 0).attr(imgStyle);

// 文本是居左、居上的

this.img.labelPoint = { x: cfg.posLeft + 10, y: cfg.posTop + cfg.nodeHeight / 2 };

this.img.label = bf.paper.text(this.img.labelPoint.x, this.img.labelPoint.y, cfg.text).attr(textStyle);

}

// 绑定数据

bf.maxNodeIndex++;

this.img.cfg = BF.ocopy({}, cfg);

this.img.imgStyle = BF.ocopy({}, imgStyle);

this.img.textStyle = BF.ocopy({}, textStyle);

this.img.selStyle = BF.ocopy({}, selStyle);

// 设计状态下配置拖动绘制对象的整体

_n = this;

if (plot) {

_n.img.attr({ cursor: "move" });

_n.img.draggable();

} else {

_n.img.attr({ cursor: "pointer" });

// 浏览状态下的点击响应

_n.img.click(function (e) {

for (var z = 0; z < bf.container.length; z++) {

var obj = bf.container[z].img;

var srco = bf.paper.getById(e.srcElement.raphaelid);

if (obj.cfg.nodeIdx == srco.cfg.nodeIdx) {

bf.runFunc(obj.cfg.jsurl);

}

}

});

if(_n.img.label){

_n.img.label.attr({ cursor: "pointer" });

_n.img.label.click(function (e) {

for (var z = 0; z < bf.container.length; z++) {

var obj = bf.container[z].img;

var srco = bf.paper.getById(e.srcElement.parentElement==null?e.srcElement.parentNode.raphaelid:e.srcElement.parentElement.raphaelid);

if (obj.label && obj.label.id == srco.id) {

bf.runFunc(obj.cfg.jsurl);

}

}

});

};

}

// 返回节点

return this;

};

// 双击取消选中事件

if (ploting) {

// 所有热点可以进行画线

// 1. 鼠标经过图形热点区时,绘制热点提示按下鼠标,按下鼠标时记录热点坐标为直线起点,对象记录流出直线的id;移出热点区域时,删除临时热点

// 2. 鼠标经过图形热点区时,绘制热点提示释放鼠标,释放鼠标时记录热点坐标为直线终点,对象记录流入直线的id;移出热点区域时,删除临时热点

bf.lineCfg = BF.ocopy({}, BF.LineProperty);

bf.tempLine = null;

document.onmousedown = function (event) {

if (BF.DrawLineMode) {

// 查找最近的热点,用热点中心作为起点

for (var h = 0; h < bf.hotpoints.length; h++) {

var hot = bf.hotpoints[h];

if (hot.id == event.srcElement.raphaelid) {

bf.lineCfg.nodeIdx = bf.maxNodeIndex++;

bf.lineCfg.posLeft = hot.attr("cx");

bf.lineCfg.posTop = hot.attr("cy");

bf.lineCfg.endX = isNaN(event.layerX) ? -1 : event.layerX;

bf.lineCfg.endY = isNaN(event.layerY) ? -1 : event.layerY;

if (bf.lineCfg.endX > 0 && bf.lineCfg.endY > 0) {

try {

bf.tempLine = bf.paper.path("M" + bf.lineCfg.posLeft + "," + bf.lineCfg.posTop + "L" + bf.lineCfg.endX*1 + "," + bf.lineCfg.endY*1).attr(BF.LineStyle);

} catch (e) {

}

}

break;

}

}

}

};

document.onmousemove = function (event) {

if (BF.DrawLineMode && bf.tempLine != null) {

// 删除旧线画新线

bf.tempLine.remove();

bf.lineCfg.endX = isNaN(event.layerX) ? -1 : event.layerX;

bf.lineCfg.endY = isNaN(event.layerY) ? -1 : event.layerY;

if (bf.lineCfg.endX > 0 && bf.lineCfg.endY > 0) {

try {

bf.tempLine = bf.paper.path("M" + bf.lineCfg.posLeft + "," + bf.lineCfg.posTop + "L" + bf.lineCfg.endX*1 + "," + bf.lineCfg.endY*1).attr(BF.LineStyle);

} catch (e) {

}

}

}

};

document.onmouseup = function (event) {

if (BF.DrawLineMode && bf.tempLine) {

// 找到最近的热点完成画线;如果未找到最近热点,则画线失败

bf.tempLine.remove();

bf.tempLine = null;

for (var h = 0; h < bf.hotpoints.length; h++) {

var hot = bf.hotpoints[h];

if (hot.id == event.srcElement.raphaelid) {

bf.lineCfg.endX = hot.attr("cx");

bf.lineCfg.endY = hot.attr("cy");

bf.container.push(new bf.rimg(true, bf.lineCfg));

break;

}

}

}

};

document.onclick = function (event, x, y) {

if (!BF.DrawLineMode) {

var imgid = event.srcElement.raphaelid;

var domid = event.srcElement.id;

if (imgid) {

var srco = bf.paper.getById(imgid);

for (var z = 0; z < bf.container.length; z++) {

var obj = bf.container[z].img;

if (obj.cfg.nodeIdx == srco.cfg.nodeIdx) {

obj.attr(obj.selStyle);

bf.lastSelect = z;

bf.runFunc(obj.cfg.jsurl);

} else {

obj.attr(obj.imgStyle);

}

}

if (typeof (BF.OnSelectNode) == "function") {

BF.OnSelectNode(bf.lastSelect);

}

} else if (domid == "") {

// 点击空白区

if (bf.lastSelect >= 0) {

var obj = bf.container[bf.lastSelect].img;

obj.attr(obj.imgStyle);

}

bf.lastSelect = -1;

if (typeof (BF.OnSelectNode) == "function") {

BF.OnSelectNode(bf.lastSelect);

}

}

}

};

};

// 删除绘图对象,包括节点和连线两种

bf.remove = function () {

if (bf.lastSelect < 0) {

return;

}

var delo = bf.container[bf.lastSelect].img;

if (delo) {

if (delo.label) {

delo.label.remove();

}

delo.remove();

}

bf.container.splice(bf.lastSelect, 1);

// 取消选中

bf.lastSelect = -1;

if (typeof (BF.OnSelectNode) == "function") {

BF.OnSelectNode(bf.lastSelect);

}

};

// 提取绘制数据

bf.getNodes = function () {

var data = new Array();

for (var i = 0; i < bf.container.length; i++) {

if (bf.container[i].img.cfg.nodeIdx > 0) {

data.push(bf.container[i].img.cfg);

}

}

return data;

};

// 设置节点属性,需要重绘图像

bf.configNode = function (config) {

if (bf.lastSelect < 0) {

return;

}

// 强制数字转换

config.posLeft = config.posLeft * 1;

config.posTop = config.posTop * 1;

config.nodeWidth = config.nodeWidth * 1;

config.nodeHeight = config.nodeHeight * 1;

if(config.endX && config.endX != ""){

config.endX = config.endX*1;

config.endY = config.endY*1;

}

var obj = bf.container[bf.lastSelect].img;

var cfg = BF.ocopy({}, obj.cfg);

cfg = BF.ocopy(cfg, config);

// 重绘

if (obj.label) {

obj.label.remove();

}

obj.remove();

bf.container.splice(bf.lastSelect, 1);

bf.container.push(new bf.rimg(ploting, cfg));

bf.lastSelect = bf.container.length - 1;

// if (typeof (BF.OnSelectNode) == "function") {

// BF.OnSelectNode(bf.lastSelect);

// }

};

// 添加任务节点,分大按钮、流程节点、分支、备注文本

bf.addNode = function (node) {

node.nodeIdx = bf.maxNodeIndex++;

bf.container.push(new bf.rimg(ploting, node));

bf.lastSelect = bf.container.length - 1;

if (typeof (BF.OnSelectNode) == "function") {

BF.OnSelectNode(bf.lastSelect);

}

};

// 初始化时,如果未指定nodes,则画出两个示例块

if (typeof (nodes) == "undefined" || nodes.length == 0) {

nodes = new Array();

}

// 绘制对象

for (var i = 0; i < nodes.length; i++) {

var node = nodes[i];

if (bf.maxNodeIndex < node.nodeIdx) {

bf.maxNodeIndex = node.nodeIdx;

}

bf.container.push(new bf.rimg(ploting, node));

}

return bf;

};

})(Raphael);

之后再你的组件中将这个文件引入,

import '@/untils/flow.js';

之后就是对 其中的方法进行调用即可,废话不多说,直接上代码

当然这是我的练习,方便大家观看,如果有什么问题可以私信我。。。。。

有小伙伴不知道效果图我现在贴在这里

参考阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。