最近公司需要开发一个工作流引擎,现有的流行的引擎框架都是基于桌面客户端的,B/S的基本没有,为了满足公司的业务需要,决定开发一个基于B/S的工作流引擎,
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>流程图</title> 6 <link rel="stylesheet" type="text/css" 7 href="res/css/easyUI/themes/metro-green/easyui.css"> 8 <link rel="stylesheet" type="text/css" 9 href="res/css/easyUI/themes/icon.css"> 10 <link rel="stylesheet" type="text/css" 11 href="res/css/easyUI/themes/demo.css"> 12 <link rel="stylesheet" type="text/css" 13 href="res/css/easyUI/themes/color.css"> 14 <script type="text/javascript" src="res/js/jquery.min.js"></script> 15 <script type="text/javascript" src="res/js/jquery.easyui.min.js"></script> 16 <script type="text/javascript" src="res/js/jquery.portal.js"></script> 17 <script type="text/javascript" src="res/js/easyui-lang-zh_CN.js"></script> 18 </style> 19 </head> 20 <body> 21 <div class="workflow-diagram" style="width: 800px; height: 600px;"> 22 23 </div> 24 <script type="text/javascript"> 25 "use strict"; 26 $(function(){ 27 /* 28 *定义抽象类 29 */ 30 function Shape(text) { 31 this.text = text; 32 } 33 //定义抽象方法 34 Shape.prototype.draw = null; 35 //定义抽象方法 36 Shape.prototype.move = null; 37 //定义默认方法 38 Shape.prototype.isInner = function(x, y) { 39 return false; 40 }; 41 //定义实例方法 42 Shape.prototype.getText = function() { 43 return this.text; 44 }; 45 //定义实例方法 46 Shape.prototype.setText = function(text) { 47 this.text = text; 48 }; 49 50 /* 51 *工具函数 52 */ 53 //创建2D上下文 54 function createContext2d(canvas) { 55 if (canvas.getContext) { 56 var context2d = canvas.getContext('2d'); 57 //半透明蓝色描边色 58 context2d.strokeStyle = '#b1c242'; 59 //context2d.fillStyle = '#b1c242'; 60 context2d.linewidth=2; 61 context2d.lineJoin='round'; 62 //字体属性 63 context2d.font = 'bold 14px Arial'; 64 context2d.textAlign = 'center'; 65 context2d.textBaseline = 'middle'; 66 } else 67 throw new Error('不支持2D绘图.'); 68 } 69 //绘制图像 70 function drawImage(context2d,x,y,w,h) 71 { 72 context2d.drawImage(WorkflowEngine.image,x,y,w,h); 73 } 74 //获取文本宽 75 function getTextWidth(context2d, text) { 76 return context2d.measureText(text).width; 77 } 78 //绘制圆 79 function drawCircle(context2d, x, y, radius) { 80 //开始一个路径 81 context2d.beginPath(); 82 //画圆:圆心,半径,起始弧度,终止弧度,顺时针绘制 83 context2d.arc(x, y, radius, 0, 2 * Math.PI, false); 84 //画十字线 85 context2d.moveTo(x - radius, y); 86 context2d.lineTo(x + radius, y); 87 context2d.moveTo(x, y - radius); 88 context2d.lineTo(x, y + radius); 89 //描边路径 90 context2d.stroke(); 91 } 92 //绘制文本 93 function drawText(context2d, x, y, text) { 94 context2d.strokeText(text, x, y); 95 } 96 //绘制矩形 97 function drawRect(context2d, x, y, width, height) { 98 context2d.strokeRect(x, y, width, height); 99 } 100 //绘制连接线 101 function drawLine(context2d, x0, y0, x1, y1) { 102 //开始一个路径 103 context2d.beginPath(); 104 //画直线 105 context2d.moveTo(x0, y0); 106 context2d.lineTo(x1, y1); 107 //描边路径 108 context2d.stroke(); 109 } 110 //绘制一个箭头 111 function drawArrow(context2d, x0, y0, x1, y1) { 112 //总长 113 var len=Math.sqrt((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1)); 114 var dx=x0-x1; 115 var dy=y0-y1; 116 var x=dx*10/len+x1; 117 var y=dy*10/len+y1; 118 //开始一个路径 119 context2d.beginPath(); 120 //画直线 121 context2d.moveTo(x1, y1); 122 //逆时针旋转30度 123 var x2 = (x - x1) * Math.cos(Math.PI/6) - (y - y1) * Math.sin(Math.PI/6) + x1; 124 var y2 = (x - x1) * Math.sin(Math.PI/6) + (y - y1) * Math.cos(Math.PI/6) + y1; 125 context2d.lineTo(x2, y2); 126 x2 = (x - x1) * Math.cos(Math.PI*11/6) - (y - y1) * Math.sin(Math.PI*11/6) + x1; 127 y2 = (x - x1) * Math.sin(Math.PI*11/6) + (y - y1) * Math.cos(Math.PI*11/6) + y1; 128 context2d.lineTo(x2, y2); 129 context2d.lineTo(x1, y1); 130 //描边路径 131 context2d.fill(); 132 133 } 134 //绑定事件给canvas 135 function bindEvents(canvas) { 136 //鼠标移动 137 canvas.mousemove(function(e) { 138 WorkflowEngine.onMousemove(e); 139 140 }); 141 //鼠标按下 142 canvas.mousedown(function(e) { 143 WorkflowEngine.onMousedown(e); 144 145 }); 146 //鼠标松开 147 canvas.mouseup(function(e) { 148 WorkflowEngine.onMouseup(e); 149 150 }); 151 //鼠标离开 152 canvas.mouseleave(function(e) { 153 WorkflowEngine.onMouseup(e); 154 155 }); 156 } 157 158 /* 159 *定义一个单例对象:WorkflowEngine 160 */ 161 var WorkflowEngine = {}; 162 WorkflowEngine.Dragger={}; 163 //定义事件 164 WorkflowEngine.onMouseup = function(event) { 165 WorkflowEngine.readyDrag = false; 166 }; 167 //定义事件 168 WorkflowEngine.onMousedown = function(event) { 169 var mustRepaint = false; 170 //拾取当前的Activity or Actor 171 var pick = WorkflowEngine.pick(event); 172 if(WorkflowEngine.hitTopEar(event.offsetX,event.offsetY)) 173 { 174 WorkflowEngine.readyAddNewActor=true; 175 } 176 else if(WorkflowEngine.hitRightEar(event.offsetX,event.offsetY)) 177 { 178 WorkflowEngine.readyLink=true; 179 } 180 else if(WorkflowEngine.hitActivity(pick)) 181 { 182 if(WorkflowEngine.readyLink) 183 { 184 var arrow=new Arrow(WorkflowEngine.currentChecked().id(),pick.id()); 185 WorkflowEngine.workflow.add('Arrow',arrow); 186 WorkflowEngine.readyLink=false; 187 WorkflowEngine.cursor=null; 188 mustRepaint=true; 189 } 190 191 } 192 else if(WorkflowEngine.hitBlank(pick)) 193 { 194 if(WorkflowEngine.readyAddNewActor) 195 { 196 var actor=new Actor(WorkflowEngine.currentChecked().id(), 197 WorkflowEngine.context2d, '参与者', 198 event.offsetX,event.offsetY); 199 WorkflowEngine.workflow.add('Actor',actor); 200 WorkflowEngine.readyAddNewActor=false; 201 WorkflowEngine.cursor=null; 202 mustRepaint=true; 203 204 } 205 else if(WorkflowEngine.readyAddNewActivity) 206 { 207 var activity=new Activity(WorkflowEngine.workflow.id(), 208 WorkflowEngine.context2d, '新建节点',event.offsetX,event.offsetY); 209 WorkflowEngine.workflow.add('Activity',activity); 210 WorkflowEngine.readyAddNewActivity=false; 211 WorkflowEngine.cursor=null; 212 mustRepaint=true; 213 } 214 else 215 { 216 var checked=WorkflowEngine.currentChecked(); 217 //让先前的当前节点正常化 218 if (checked) { 219 checked.setNormaled(); 220 WorkflowEngine.currentChecked(null); 221 mustRepaint = true; 222 } 223 if(WorkflowEngine.readyLink) 224 { 225 WorkflowEngine.readyLink=false; 226 mustRepaint = true; 227 } 228 229 if(WorkflowEngine.readyAddNewActivity) 230 { 231 WorkflowEngine.readyAddNewActivity=false; 232 mustRepaint = true; 233 } 234 235 if(WorkflowEngine.readyAddNewActor) 236 { 237 WorkflowEngine.readyAddNewActor=false; 238 mustRepaint = true; 239 } 240 WorkflowEngine.cursor=null; 241 } 242 } 243 if(WorkflowEngine.hit(pick)) 244 { 245 WorkflowEngine.readyDrag=true; 246 WorkflowEngine.Dragger.x = event.offsetX; 247 WorkflowEngine.Dragger.y = event.offsetY; 248 var checked = WorkflowEngine.currentChecked(); 249 //让先前的当前节点正常化 250 if (checked && checked!=pick) { 251 checked.setNormaled(); 252 WorkflowEngine.currentChecked(null); 253 mustRepaint = true; 254 } 255 //当前节点被选中 256 if (!pick.isPicked()) { 257 pick.setPicked(); 258 WorkflowEngine.currentFocused(null); 259 WorkflowEngine.currentChecked(pick); 260 mustRepaint = true; 261 } 262 } 263 if (mustRepaint) { 264 WorkflowEngine.repaint(); 265 } 266 267 }; 268 269 /* 270 *定义事件,处理两件事:1,拖动.2,焦点. 271 */ 272 WorkflowEngine.onMousemove = function(event) { 273 var mustRepaint = false; 274 if(WorkflowEngine.readyDrag) 275 { 276 var checked = WorkflowEngine.currentChecked(); 277 if (checked) { 278 //计算x,y的改变量 279 var dx = event.offsetX - WorkflowEngine.Dragger.x; 280 var dy = event.offsetY - WorkflowEngine.Dragger.y; 281 //修改当前位置 282 WorkflowEngine.Dragger.x = event.offsetX; 283 WorkflowEngine.Dragger.y = event.offsetY; 284 //移动 285 WorkflowEngine.workflow.move(dx, dy); 286 mustRepaint = true; 287 } 288 } 289 else if(WorkflowEngine.readyAddNewActivity) 290 { 291 this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Activity'); 292 mustRepaint = true; 293 } 294 else if(WorkflowEngine.readyAddNewActor) 295 { 296 this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Actor'); 297 mustRepaint = true; 298 } 299 300 else if(WorkflowEngine.readyLink) 301 { 302 this.cursor=new Cursor(WorkflowEngine.workflow,event.offsetX,event.offsetY,'Arrow'); 303 mustRepaint = true; 304 } 305 //移动 306 else { 307 var pick = WorkflowEngine.pick(event); 308 //在某个节点上移动或者进入 309 if (pick) { 310 var foucused = WorkflowEngine.currentFocused(); 311 //如果焦点不在当前节点上 312 if (foucused && foucused!=pick) { 313 foucused.setNormaled(); 314 WorkflowEngine.currentFocused(null); 315 mustRepaint = true; 316 317 } 318 if (pick.isNormaled()) { 319 pick.setFocused(); 320 WorkflowEngine.currentFocused(pick); 321 mustRepaint = true; 322 } 323 } 324 //在空白处移动 325 else { 326 var foucused = WorkflowEngine.currentFocused(); 327 if (foucused) { 328 foucused.setNormaled(); 329 WorkflowEngine.currentFocused(null); 330 mustRepaint = true; 331 332 } 333 } 334 } 335 if (mustRepaint) { 336 WorkflowEngine.repaint(); 337 } 338 339 }; 340 // 341 WorkflowEngine.hitActivity=function(pick){ 342 return pick && pick.constructor==Activity; 343 }; 344 // 345 WorkflowEngine.hitActor=function(pick){ 346 return pick && pick.constructor==Actor; 347 }; 348 // 349 WorkflowEngine.hitBlank=function(pick){ 350 return !pick; 351 }; 352 WorkflowEngine.hit=function(pick){ 353 return pick; 354 }; 355 // 356 WorkflowEngine.hitTopEar=function(x,y){ 357 return this.workflow && this.workflow.checked && this.workflow.checked.constructor==Activity && WorkflowEngine.workflow.getTopEar() && WorkflowEngine.workflow.getTopEar().isInner(x,y); 358 }; 359 // 360 WorkflowEngine.hitRightEar=function(x,y){ 361 return this.workflow && this.workflow.checked && this.workflow.checked.constructor==Activity && WorkflowEngine.workflow.getRightEar() && WorkflowEngine.workflow.getRightEar().isInner(x,y); 362 }; 363 // 364 WorkflowEngine.readyAddNewActivity=false; 365 WorkflowEngine.readyLink=false; 366 WorkflowEngine.readyAddNewActor=false; 367 WorkflowEngine.readyDrag=false; 368 //getter/setter 369 WorkflowEngine.currentChecked = function(obj) { 370 if(arguments && arguments.length==1) 371 return WorkflowEngine.workflow && (WorkflowEngine.workflow.checked=obj); 372 else 373 return WorkflowEngine.workflow && WorkflowEngine.workflow.checked; 374 375 }; 376 //getter/setter 377 WorkflowEngine.currentFocused = function(obj) { 378 if(arguments && arguments.length==1) 379 return WorkflowEngine.workflow && (WorkflowEngine.workflow.focused=obj); 380 else 381 return WorkflowEngine.workflow && WorkflowEngine.workflow.focused; 382 }; 383 //拾取选中的节点 384 WorkflowEngine.pick = function(event) { 385 return WorkflowEngine.workflow 386 && WorkflowEngine.workflow.pick(event.offsetX, 387 event.offsetY); 388 }; 389 //定义一个方法 390 WorkflowEngine.draw = (function() { 391 return function() { 392 //清除屏幕 393 WorkflowEngine.context2d.clearRect(0, 0, 394 WorkflowEngine.canvasWidth, 395 WorkflowEngine.canvasHeight); 396 //绘制光标 397 if(WorkflowEngine.cursor) 398 WorkflowEngine.cursor.draw(WorkflowEngine.context2d); 399 WorkflowEngine.workflow.draw(WorkflowEngine.context2d); 400 }; 401 })(); 402 //定义一个方法,重绘屏幕 403 WorkflowEngine.repaint = function() { 404 //刷新属性列表视图 405 var checked=WorkflowEngine.currentChecked() || WorkflowEngine.workflow; 406 var data=WorkflowEngine.propertygrid(); 407 //切换属性 408 if(data && data.rows[0] && data.rows[0].value!=checked.id()) 409 { 410 //显示工作流属性 411 if(checked.constructor==Workflow) 412 { 413 WorkflowEngine.propertyTitle(checked.getText()); 414 } 415 //显示节点属性 416 else 417 { 418 //只有activity有转移条件 419 if(checked.constructor==Activity) 420 { 421 var arrows=this.workflow.findArrows(checked.id(),true); 422 checked.addConditions(arrows); 423 } 424 WorkflowEngine.propertyTitle(checked.node.getText()); 425 } 426 //覆盖右边属性 427 WorkflowEngine.propertygrid(checked.propertygrid()); 428 } 429 //只有activity有转移条件 430 else if(checked.constructor==Activity) 431 { 432 var arrows=this.workflow.findArrows(checked.id(),true); 433 checked.addConditions(arrows); 434 WorkflowEngine.propertygrid(checked.propertygrid()); 435 } 436 437 //使用浏览器的自动动画支持机制 438 if (window.requestAnimationFrame) { 439 window.requestAnimationFrame(this.draw); 440 } 441 //使用火狐浏览器的自动动画支持机制 442 else if (window.mozRequestAnimationFrame) { 443 window.mozRequestAnimationFrame(this.draw); 444 } 445 //如果不支持HTML5动画绘制函数 446 else { 447 this.draw(); 448 } 449 450 }; 451 //getter/setter如果不存在属性视图就创建 452 WorkflowEngine.propertygrid=function(data){ 453 this.propertyGrid = this.propertyGrid || this.table.propertygrid({ 454 showGroup: true, 455 scrollbarSize: 0, 456 columns: [[{field:'name',title:'属性名',width:80,sortable:true},{field:'value',title:'属性值',width:200,resizable:false}]], 457 458 }); 459 if(data) 460 this.propertyGrid.propertygrid('loadData',data); 461 else 462 return this.propertyGrid.propertygrid('getData'); 463 464 }; 465 //seter方法:修改title 466 WorkflowEngine.propertyTitle=function(propertyName){ 467 this.propertyView.panel('setTitle','属性表-'+propertyName); 468 }; 469 WorkflowEngine.workflowTitle=function(workflowName){ 470 workflowName='流程图-'+workflowName; 471 this.workflowView.panel('setTitle',workflowName); 472 }; 473 //定义方法 474 WorkflowEngine.initialize = function() { 475 this.image=new Image(); 476 this.image.src='icons/man.png'; 477 478 var left=$('<div></div>').appendTo('.workflow-diagram').css('width','60%'); 479 var right=$('<div></div>').appendTo('.workflow-diagram').css('width','40%'); 480 481 $('.workflow-diagram').portal({ 482 border : true, 483 fit : true 484 }); 485 486 var p = $('<div></div>').appendTo(left); 487 p.panel({ 488 title : '流程图' 489 }); 490 $('.workflow-diagram').portal('add', { 491 panel: p, 492 columnIndex: 0 493 }); 494 495 //追加工具栏 496 var tb = $('<div></div>').appendTo(p); 497 var add = $('<a></a>').appendTo(tb); 498 //新建 499 add.linkbutton({ 500 iconCls : 'icon-add', 501 onClick : function() { 502 //GC 503 WorkflowEngine.workflow=null; 504 WorkflowEngine.workflow = new Workflow('新建工作流'); 505 WorkflowEngine.workflowTitle(WorkflowEngine.workflow.getText()); 506 WorkflowEngine.workflow.add('Activity',new Activity(WorkflowEngine.workflow.id(),WorkflowEngine.context2d, '开始', 10, WorkflowEngine.canvasHeight/3,true)); 507 WorkflowEngine.propertygrid(WorkflowEngine.workflow.propertygrid()); 508 WorkflowEngine.repaint(); 509 510 } 511 }); 512 //新建工具提示 513 add.tooltip({ 514 position : 'top', 515 content : '<span style="color:#fff">新建一个工作流.</span>', 516 onShow : function() { 517 $(this).tooltip('tip').css({ 518 backgroundColor : '#666', 519 borderColor : '#666' 520 }); 521 } 522 }); 523 //删除 524 var del = $('<a></a>').appendTo(tb); 525 del.linkbutton({ 526 iconCls : 'icon-remove', 527 onClick : function() { 528 if(WorkflowEngine.workflow && WorkflowEngine.workflow.del()) 529 WorkflowEngine.repaint(); 530 531 } 532 }); 533 //删除工具提示 534 del.tooltip({ 535 position : 'top', 536 content : '<span style="color:#fff">删除</span>', 537 onShow : function() { 538 $(this).tooltip('tip').css({ 539 backgroundColor : '#666', 540 borderColor : '#666' 541 }); 542 } 543 }); 544 //撤销 545 var undo = $('<a></a>').appendTo(tb); 546 undo.linkbutton({ 547 iconCls : 'icon-undo', 548 onClick : function() { 549 if(WorkflowEngine.workflow && WorkflowEngine.workflow.undo()) 550 WorkflowEngine.repaint(); 551 } 552 }); 553 //撤销工具提示 554 undo.tooltip({ 555 position : 'top', 556 content : '<span style="color:#fff">撤销</span>', 557 onShow : function() { 558 $(this).tooltip('tip').css({ 559 backgroundColor : '#666', 560 borderColor : '#666' 561 }); 562 } 563 }); 564 //恢复 565 var redo = $('<a></a>').appendTo(tb); 566 redo.linkbutton({ 567 iconCls : 'icon-redo', 568 onClick : function() { 569 if(WorkflowEngine.workflow && WorkflowEngine.workflow.redo()) 570 WorkflowEngine.repaint(); 571 } 572 }); 573 //恢复工具提示 574 redo.tooltip({ 575 position : 'top', 576 content : '<span style="color:#fff">恢复</span>', 577 onShow : function() { 578 $(this).tooltip('tip').css({ 579 backgroundColor : '#666', 580 borderColor : '#666' 581 }); 582 } 583 }); 584 //保存 585 var save = $('<a></a>').appendTo(tb); 586 save.linkbutton({ 587 iconCls : 'icon-save', 588 onClick : function() { 589 if(WorkflowEngine.workflow) 590 alert(JSON.stringify(WorkflowEngine.workflow.toJson())); 591 /* $.post('hawk/wws/new',{data:JSON.stringify(WorkflowEngine.workflow.toJson())},function(data){ 592 alert(data); 593 }); */ 594 } 595 }); 596 //保存工具提示 597 save.tooltip({ 598 position : 'top', 599 content : '<span style="color:#fff">保存</span>', 600 onShow : function() { 601 $(this).tooltip('tip').css({ 602 backgroundColor : '#666', 603 borderColor : '#666' 604 }); 605 } 606 }); 607 //清除 608 var clear = $('<a></a>').appendTo(tb); 609 clear.linkbutton({ 610 iconCls : 'icon-clear', 611 onClick : function() { 612 if(WorkflowEngine.workflow && WorkflowEngine.workflow.clear()) 613 WorkflowEngine.repaint(); 614 } 615 }); 616 //清除工具提示 617 clear.tooltip({ 618 position : 'top', 619 content : '<span style="color:#fff">清除</span>', 620 onShow : function() { 621 $(this).tooltip('tip').css({ 622 backgroundColor : '#666', 623 borderColor : '#666' 624 }); 625 } 626 }); 627 //新建浮动节点 628 var node = $('<a></a>').appendTo(tb); 629 node.linkbutton({ 630 iconCls : 'icon-more', 631 onClick : function() { 632 if(WorkflowEngine.workflow) 633 WorkflowEngine.readyAddNewActivity=true; 634 } 635 }); 636 //清除工具提示 637 node.tooltip({ 638 position : 'top', 639 content : '<span style="color:#fff">新建浮动节点</span>', 640 onShow : function() { 641 $(this).tooltip('tip').css({ 642 backgroundColor : '#666', 643 borderColor : '#666' 644 }); 645 } 646 }); 647 //追加画布 648 var canvas = $('<canvas></canvas>').appendTo(p); 649 this.canvasWidth = p.width(); 650 this.canvasHeight = $('.workflow-diagram').innerHeight()-2*tb.height(); 651 canvas.attr('width', this.canvasWidth); 652 canvas.attr('height', this.canvasHeight); 653 //私有方法 654 bindEvents(canvas); 655 //还原成Dom对象 656 var c = canvas.get(0); 657 this.context2d = c.getContext ? c.getContext('2d') : null; 658 if (!this.context2d) 659 throw new Error('不支持2D绘图.'); 660 661 //保存为属性 662 this.workflowView=p; 663 664 p = $('<div/></div>').appendTo(right); 665 p.panel({ 666 title : '属性表' 667 668 }); 669 670 $('.workflow-diagram').portal('add', { 671 panel: p, 672 columnIndex: 1 673 }); 674 675 this.table = $('<table></table>').appendTo(p); 676 //保存为属性 677 this.propertyView=p; 678 679 }; 680 681 /* 682 *定义一个类:Workflow 继承了Shape 683 */ 684 function Workflow(text) { 685 Shape.call(this, text); 686 this.activities = []; 687 this.actors=[]; 688 this.arrows=[]; 689 this.pointer=-1; 690 this.acts=[]; 691 var self=this; 692 //属性 693 var id='Workflow'+new Date().getTime(); 694 var data=[{text:"boolean"},{text:"short"},{text:"String",selected:true},{text:"int"},{text:"long"},{text:"float"},{text:"double"},{text:"java.util.Date"},{text:"java.math.BigDecimal"}]; 695 var demo="[{\"field\":\"agree\",\"name\":\"是否同意\"}]"; 696 var properties=[ 697 {"field":"id","name":"标识符","value":id,"group":"不可编辑"}, 698 {"field":"name","name":"文本","value":this.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){ 699 700 if(newValue && oldValue && newValue!=oldValue) 701 { 702 self.setText(newValue); 703 WorkflowEngine.workflowTitle(newValue); 704 WorkflowEngine.propertyTitle(newValue); 705 } 706 707 }}}}, 708 {"field":"form","name":"定义表单","value":demo,"group":"表单","editor":{"type":"textbox","options":{"required": true,"multiline":true,"width":100,"height":120,"onChange":function(newValue,oldValue){ 709 710 if(newValue && oldValue && newValue!=oldValue) 711 { 712 try 713 { 714 var json=JSON.parse($.trim(newValue)); 715 } 716 catch(e) 717 { 718 console.error(e); 719 return; 720 } 721 //刷新数据 722 for(var i=0;i<json.length;i++) 723 { 724 json[i].value=null; 725 json[i].group="表单数据"; 726 json[i].editor={type:"combobox",options:{required: true,editable:false,panelHeight:100,valueField:"text",textField:"text",data:data}}; 727 } 728 //刷新数据 729 self.properties=properties.concat(json); 730 //刷新视图 731 WorkflowEngine.propertygrid(self.properties); 732 } 733 734 }}}} 735 ]; 736 this.properties=properties; 737 738 }; 739 //定义内部类 740 Workflow.Action=function(type,data){ 741 this.data=data; 742 this.type=type; 743 }; 744 //枚举 745 Workflow.Action.Type={add:1,del:2}; 746 //继承Shape 747 Workflow.prototype = new Shape(); 748 //修改构造器 749 Workflow.prototype.constructor = Workflow; 750 //获取表单数据 751 Workflow.prototype.form=function(){ 752 var ret=[]; 753 for(var i=0;i<this.properties.length;i++) 754 { 755 if(this.properties[i]['group']=='表单数据') 756 { 757 ret.push({name:this.properties[i].field,text:this.properties[i].name,type:this.properties[i].value}); 758 } 759 } 760 return ret; 761 }; 762 //return={id:id,name:name,activities:[{activity_id,id,name:name,actors:[],conditions:[]},{activity_id,id,name:name,actors:[],conditions:[]}...]} 763 Workflow.prototype.toJson=function(){ 764 var result={id:this.id(),name:this.properties[1].value,form:this.form()}; 765 result.activities=[]; 766 for(var i=0;i<this.activities.length;i++) 767 { 768 var activity=this.activities[i]; 769 var json=activity.toJson(); 770 result.activities.push(json); 771 var actors=this.findActors(activity.id()); 772 json.actors=[]; 773 for(var j=0;j<actors.length;j++) 774 { 775 var actor=actors[j]; 776 json.actors.push(actor.toJson()); 777 } 778 var arrows=this.findArrows(activity.id(),true); 779 json.conditions=[]; 780 for(var j=0;j<arrows.length;j++) 781 { 782 var arrow=arrows[j]; 783 json.conditions.push(arrow.toJson()); 784 } 785 } 786 787 return result; 788 }; 789 790 //查找Activity 或者Actor 791 Workflow.prototype.findById=function(id){ 792 for(var i=0;i<this.activities.length;i+=1) 793 { 794 if(this.activities[i].id()==id) 795 return this.activities[i]; 796 } 797 for(var i=0;i<this.actors.length;i+=1) 798 { 799 if(this.actors[i].id()==id) 800 return this.actors[i]; 801 } 802 return null; 803 }; 804 //findByActivityId 805 Workflow.prototype.findActors=function(activity_id){ 806 var actors=[]; 807 for(var i=0;i<this.actors.length;i+=1) 808 { 809 if(this.actors[i].foreignKey()==activity_id) 810 actors[actors.length]=this.actors[i]; 811 } 812 return actors; 813 }; 814 //查找连线,重载 815 Workflow.prototype.findArrows=function(activity_id,isAway){ 816 var arrows=[]; 817 for(var i=0;i<this.arrows.length;i+=1) 818 { 819 var cond=isAway?(this.arrows[i].id1==activity_id):(this.arrows[i].id1==activity_id || this.arrows[i].id2==activity_id); 820 if(cond) 821 arrows[arrows.length]=this.arrows[i]; 822 } 823 return arrows; 824 }; 825 //根据id查找节点 826 Workflow.prototype.findActivity=function(activity_id){ 827 for(var i=0;i<this.activities.length;i+=1) 828 { 829 if(this.activities[i].id()==activity_id) 830 return this.activities[i]; 831 } 832 return null; 833 }; 834 //find foreignkey!=activity_id的Actor 835 Workflow.prototype.notActors=function(activity_id){ 836 var actors=[]; 837 for(var i=0;i<this.actors.length;i+=1) 838 { 839 if(this.actors[i].foreignKey()!=activity_id) 840 actors[actors.length]=this.actors[i]; 841 } 842 return actors; 843 }; 844 //find 跟activity_id无关的连线 845 Workflow.prototype.notArrows=function(activity_id){ 846 var arrows=[]; 847 for(var i=0;i<this.arrows.length;i+=1) 848 { 849 if(this.arrows[i].id1!=activity_id && this.arrows[i].id2!=activity_id) 850 arrows[arrows.length]=this.arrows[i]; 851 } 852 return arrows; 853 }; 854 855 //getter flow_id 856 Workflow.prototype.id=function(id){ 857 return id?(this.properties[0].value=id):this.properties[0].value; 858 }; 859 //处理属性 860 Workflow.prototype.propertygrid=function(){ 861 return this.properties; 862 }; 863 864 //处理动作数组acts的方法 865 Workflow.prototype.handle=function(){ 866 //清空数组 867 this.activities.length=0; 868 this.actors.length=0; 869 this.arrows.length=0; 870 this.checked=null; 871 for(var i=0;i<=this.pointer;i+=1) 872 { 873 var act=this.acts[i]; 874 switch(act.type) 875 { 876 case Workflow.Action.Type.add: 877 if(act.data.constructor==Activity) 878 { 879 if(act.data.isPicked()) 880 this.checked=act.data; 881 this.activities[this.activities.length]=act.data; 882 } 883 884 else if(act.data.constructor==Actor) 885 { 886 if(act.data.isPicked()) 887 this.checked=act.data; 888 this.actors[this.actors.length]=act.data; 889 } 890 891 else if(act.data.constructor==Arrow) 892 this.arrows[this.arrows.length]=act.data; 893 break; 894 case Workflow.Action.Type.del: 895 this.remove(act.data); 896 break; 897 898 } 899 } 900 }; 901 //定义事件:redo 902 Workflow.prototype.redo=function(){ 903 if(this.pointer==this.acts.length-1) 904 return false; 905 this.pointer++; 906 this.handle(); 907 return true; 908 }; 909 //定义事件:undo 910 Workflow.prototype.undo=function(){ 911 if(this.pointer==0) 912 return false; 913 this.pointer--; 914 this.handle(); 915 return true; 916 }; 917 //定义事件:del 918 Workflow.prototype.del=function(){ 919 if(this.checked) 920 { 921 if(this.checked.constructor==Activity && this.checked.isHead()) 922 return false; 923 var del=this.addDelAction(); 924 this.remove(del.data); 925 return true; 926 } 927 return false; 928 }; 929 //定义事件:add Activity 或者 Actor 930 Workflow.prototype.add=function(type,obj){ 931 if(type=='Actor') 932 { 933 this.actors[this.actors.length]=obj; 934 } 935 else if(type=='Activity') 936 { 937 this.activities[this.activities.length]=obj; 938 } 939 else if(type=='Arrow') 940 { 941 this.arrows[this.arrows.length]=obj; 942 } 943 this.addAddAction(obj); 944 }; 945 //定义事件:clear 946 Workflow.prototype.clear=function(){ 947 if(this.activities.length==1) 948 return false; 949 this.activities.splice(1,this.activities.length-1); 950 this.pointer=0; 951 this.acts.splice(1,this.acts.length-1); 952 this.actors=[]; 953 this.arrows=[]; 954 return true; 955 }; 956 //重写draw方法 957 Workflow.prototype.draw = function(context2d) { 958 for (var i = 0; i < this.activities.length; i += 1) { 959 //当前节点 960 var child = this.activities[i]; 961 //画右耳朵 962 if(child.isPicked()) 963 { 964 this.rightEar=new Ear(child.node.maxX + Ear.RADIUS,(child.node.minY + child.node.maxY) / 2); 965 this.rightEar.draw(WorkflowEngine.context2d); 966 if(!child.isHead()) 967 { 968 //画上耳朵 969 if(child.isPicked()&&!child.isHead()) 970 { 971 this.topEar=new Ear((child.node.minX+child.node.maxX)/2, child.node.minY- Ear.RADIUS); 972 this.topEar.draw(WorkflowEngine.context2d); 973 } 974 } 975 } 976 977 child.draw(context2d); 978 var actors=this.findActors(child.id()); 979 for(var j=0;j<actors.length;j+=1) 980 { 981 actors[j].draw(context2d); 982 //画连线 983 drawLine(context2d, (child.node.minX+child.node.maxX)/2, 984 child.node.minY, 985 (actors[j].node.minX+actors[j].node.maxX)/2, 986 actors[j].node.maxY); 987 } 988 989 } 990 //画连线 991 for(var i=0;i<this.arrows.length;i+=1) 992 { 993 var arrow=this.arrows[i]; 994 arrow.draw(WorkflowEngine.context2d,this.findActivity(arrow.id1),this.findActivity(arrow.id2)); 995 } 996 997 }; 998 //获取上耳朵 999 Workflow.prototype.getTopEar=function(){ 1000 return this.topEar; 1001 }; 1002 //获取右耳朵 1003 Workflow.prototype.getRightEar=function(){ 1004 return this.rightEar; 1005 }; 1006 1007 //定义实例方法:addAddAction,有三种可能,Actor,Activity,Arrow 1008 Workflow.prototype.addAddAction = function(data) { 1009 //删除pointer后面的动作(不包括当前位置) 1010 this.acts.splice(this.pointer+1,this.acts.length-this.pointer-1); 1011 this.pointer=this.acts.length; 1012 this.acts.push(new Workflow.Action(Workflow.Action.Type.add,data)); 1013 1014 }; 1015 1016 //定义实例方法:addDelAction,两种可能,Actor,Activity 1017 Workflow.prototype.addDelAction = function() { 1018 var del=this.checked; 1019 //删除pointer后面的动作(不包括当前位置) 1020 this.acts.splice(this.pointer+1,this.acts.length-this.pointer-1); 1021 this.pointer=this.acts.length; 1022 this.acts.push(del=new Workflow.Action(Workflow.Action.Type.del,del)); 1023 this.checked.setNormaled(); 1024 this.checked=null; 1025 return del; 1026 }; 1027 //定义实例方法:remove Activity 或者 Actor 1028 Workflow.prototype.remove = function(obj) { 1029 if(obj.constructor==Actor) 1030 { 1031 this.actors.splice(obj.index,1); 1032 } 1033 else if(obj.constructor==Activity) 1034 { 1035 //删除节点 1036 this.activities.splice(obj.index,1); 1037 //删除连线 1038 this.arrows=this.notArrows(obj.id()); 1039 //删除参与者 1040 this.actors=this.notActors(obj.id()); 1041 } 1042 1043 }; 1044 //定义实例方法:pick Activity 或者 Actor 1045 Workflow.prototype.pick = function(x, y) { 1046 for (var i = 0; i < this.activities.length; i += 1) { 1047 if (this.activities[i].isInner(x, y)) { 1048 //保存索引 1049 this.activities[i].index = i; 1050 return this.activities[i]; 1051 } 1052 1053 } 1054 for(var i=0;i<this.actors.length;i+=1) 1055 { 1056 if (this.actors[i].isInner(x, y)) { 1057 //保存索引 1058 this.actors[i].index = i; 1059 return this.actors[i]; 1060 } 1061 } 1062 return null; 1063 }; 1064 //重写实例方法;move 1065 Workflow.prototype.move = function(dx, dy) { 1066 if(this.checked) 1067 { 1068 if(this.checked.constructor==Activity) 1069 { 1070 //是头 1071 if(this.checked.isHead()) 1072 { 1073 for(var i=1;i<this.activities.length;i+=1) 1074 { 1075 this.activities[i].move(dx, dy); 1076 var actors=this.findActors(this.activities[i].id()); 1077 for(var j=0;j<actors.length;j+=1) 1078 actors[j].move(dx,dy); 1079 } 1080 } 1081 this.checked.move(dx, dy); 1082 var actors=this.findActors(this.checked.id()); 1083 for(var j=0;j<actors.length;j+=1) 1084 actors[j].move(dx,dy); 1085 1086 } 1087 else if(this.checked.constructor==Actor) 1088 { 1089 this.checked.move(dx,dy); 1090 } 1091 1092 } 1093 1094 }; 1095 //重写方法:isInner 1096 Workflow.prototype.isInner = function(x, y) { 1097 return this.pick(x, y) != null; 1098 }; 1099 //定义连接线的长度 1100 Workflow.LINE_LENGTH = 30; 1101 1102 /* 1103 *辅助类:Node 继承了Shape 1104 */ 1105 function Node(text, minX, minY, maxX, maxY) { 1106 //调用Shape的构造器,构造器继承 1107 Shape.call(this, text); 1108 this.minX = minX; 1109 this.minY = minY; 1110 this.maxX = maxX; 1111 this.maxY = maxY; 1112 1113 } 1114 //原型继承 1115 Node.prototype = new Shape(); 1116 //new Node().getText() 1117 //修改构造器类型 1118 Node.prototype.constructor = Node; 1119 //重写实例方法:draw 1120 Node.prototype.draw = function(context2d) { 1121 drawRect(context2d, this.minX, this.minY, 1122 this.maxX - this.minX, this.maxY - this.minY); 1123 drawText(context2d, this.minX, (this.minY + this.maxY) / 2+5, 1124 this.getText()); 1125 1126 }; 1127 //重写实例方法;move 1128 Node.prototype.move = function(dx, dy) { 1129 this.minX += dx; 1130 this.minY += dy; 1131 this.maxX += dx; 1132 this.maxY += dy; 1133 }; 1134 //重写方法:isInner 1135 Node.prototype.isInner = function(x, y) { 1136 return x > this.minX && x<this.maxX && y>this.minY 1137 && y < this.maxY; 1138 }; 1139 //选中高亮重绘 1140 Node.prototype.checkedHighlight = function(context2d) { 1141 //半透明红色 1142 context2d.strokeStyle = 'rgba(255,0,0,0.5)'; 1143 this.draw(context2d); 1144 //恢复半透明蓝色 1145 context2d.strokeStyle = '#b1c242'; 1146 }; 1147 1148 //悬浮高亮重绘 1149 Node.prototype.hoveHighlight = function(context2d) { 1150 //半透明绿色 1151 context2d.strokeStyle = 'rgba(0,255,0,0.5)'; 1152 this.draw(context2d); 1153 //恢复半透明蓝色 1154 context2d.strokeStyle = '#b1c242'; 1155 }; 1156 1157 /* 1158 *辅助类:Ear 继承了Shape 1159 */ 1160 function Ear(x, y) { 1161 //调用Shape的构造器,构造器继承 1162 Shape.call(this, ''); 1163 this.x = x; 1164 this.y = y; 1165 this.radius = Ear.RADIUS; 1166 } 1167 //原型继承 1168 Ear.prototype = new Shape(); 1169 //修改构造器类型 1170 Ear.prototype.constructor = Ear; 1171 //重写实例方法:draw 1172 Ear.prototype.draw = function(context2d) { 1173 drawCircle(context2d, this.x, this.y, this.radius); 1174 }; 1175 //重写实例方法;move 1176 Ear.prototype.move = function(dx, dy) { 1177 this.x += dx; 1178 this.y += dy; 1179 }; 1180 //重写方法:isInner 1181 Ear.prototype.isInner = function(x, y) { 1182 return Math.sqrt((x - this.x) * (x - this.x) + (y - this.y) 1183 * (y - this.y)) <= this.radius; 1184 }; 1185 //定义常量 1186 Ear.RADIUS = 5; 1187 1188 /* 1189 *定义一个类:Activity 1190 */ 1191 function Activity(flow_id,context2d, text, x, y,head) { 1192 var w = context2d?getTextWidth(context2d, text):0; 1193 this.node = new Node(text, x, y, x + w, y + Activity.HEIGHT); 1194 this.status = Activity.NORMAL; 1195 this.head=head || false; 1196 var self=this; 1197 //属性 1198 var id=(head?'Activity-Head':'Activity')+new Date().getTime(); 1199 var data=[{text:"boolean"},{text:"short"},{text:"String",selected:true},{text:"int"},{text:"long"},{text:"float"},{text:"double"},{text:"java.util.Date"},{text:"java.math.BigDecimal"}]; 1200 var demo="[{\"field\":\"agree\",\"name\":\"是否同意\"}]"; 1201 this.fixed=[ {"field":"id","name":"标识符","value":id,"group":"不可编辑"}, 1202 {"field":"flow_id","name":"流程编号","value":flow_id,"group":"不可编辑"}, 1203 {"field":"name","name":"文本","value":this.node.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){ 1204 1205 if(newValue && oldValue && newValue!=oldValue) 1206 { 1207 self.node.setText(newValue); 1208 self.node.maxX=self.node.minX+getTextWidth(context2d, newValue); 1209 WorkflowEngine.repaint(); 1210 } 1211 1212 }}}}, 1213 {"field":"form","name":"定义属性","value":demo,"group":"动态属性","editor":{"type":"textbox","options":{"required": true,"multiline":true,"width":100,"height":120,"onChange":function(newValue,oldValue){ 1214 1215 if(newValue && oldValue && newValue!=oldValue) 1216 { 1217 try 1218 { 1219 var json=JSON.parse($.trim(newValue)); 1220 } 1221 catch(e) 1222 { 1223 console.error(e); 1224 return; 1225 } 1226 //刷新数据 1227 for(var i=0;i<json.length;i++) 1228 { 1229 json[i].value=null; 1230 json[i].group="动态属性"; 1231 json[i].editor={type:"combobox",options:{required: true,editable:false,panelHeight:100,valueField:"text",textField:"text",data:data}}; 1232 } 1233 self.properties=self.fixed.concat(json); 1234 //刷新视图 1235 WorkflowEngine.propertygrid(self.properties); 1236 } 1237 1238 }}}} 1239 ]; 1240 if(head) 1241 { 1242 delete this.fixed[2]["editor"]; 1243 } 1244 this.properties=this.fixed; 1245 }; 1246 Activity.prototype.toJson=function(){ 1247 return {id:this.id(),name:this.properties[2].value,properties:this.getProperties()}; 1248 }; 1249 Activity.prototype.getProperties=function(){ 1250 var ret=[]; 1251 for(var i=0;i<this.properties.length;i+=1) 1252 { 1253 var prop=this.properties[i]; 1254 if(prop.group=='动态属性' && prop.field!='form') 1255 ret.push({name:prop.field,text:prop.name,type:prop.value}); 1256 } 1257 return ret; 1258 }; 1259 Activity.prototype.addConditions=function(arrows){ 1260 this.conditions=[]; 1261 for(var i=0;i<arrows.length;i++) 1262 { 1263 this.conditions.push(arrows[i].conditions[0]); 1264 this.conditions.push(arrows[i].conditions[1]); 1265 } 1266 //this.properties=this.fixed.concat(conditions); 1267 }; 1268 //setter/getter 方法 1269 Activity.prototype.id=function(id){ 1270 return id?(this.properties[0].value=id):this.properties[0].value; 1271 }; 1272 //setter/getter flow_id的值 1273 Activity.prototype.foreignKey=function(flow_id){ 1274 return flow_id?(this.properties[1].value=flow_id):this.properties[1].value; 1275 }; 1276 //读属性 1277 Activity.prototype.propertygrid=function(){ 1278 return this.properties.concat(this.conditions); 1279 }; 1280 1281 //draw方法 1282 Activity.prototype.draw = function(context2d) { 1283 switch (this.status) { 1284 case Activity.NORMAL: 1285 this.node.draw(context2d); 1286 break; 1287 case Activity.FOCUS: 1288 this.node.hoveHighlight(context2d); 1289 break; 1290 case Activity.PICKED: 1291 this.node.checkedHighlight(context2d); 1292 break; 1293 } 1294 1295 }; 1296 Activity.prototype.isHead=function(){ 1297 return this.head; 1298 }; 1299 //方法:isInner 1300 Activity.prototype.isInner = function(x, y) { 1301 return this.node.isInner(x, y); 1302 }; 1303 //实例方法;move 1304 Activity.prototype.move = function(dx, dy) { 1305 this.node.move(dx, dy); 1306 }; 1307 // 1308 Activity.prototype.setPicked = function() { 1309 this.status = Activity.PICKED; 1310 }; 1311 // 1312 Activity.prototype.setFocused = function() { 1313 this.status = Activity.FOCUS; 1314 }; 1315 // 1316 Activity.prototype.setNormaled = function() { 1317 this.status = Activity.NORMAL; 1318 }; 1319 // 1320 Activity.prototype.isPicked = function() { 1321 return this.status == Activity.PICKED; 1322 }; 1323 // 1324 Activity.prototype.isFocused = function() { 1325 return this.status == Activity.FOCUS; 1326 }; 1327 // 1328 Activity.prototype.isNormaled = function() { 1329 return this.status == Activity.NORMAL; 1330 }; 1331 //定义常量高度 1332 Activity.HEIGHT = 20; 1333 //定义状态常量 1334 Activity.NORMAL = 1; 1335 //表示获得了焦点 1336 Activity.FOCUS = 2; 1337 //被选中 1338 Activity.PICKED = 3; 1339 1340 /* 1341 *定义一个类:Actor,继承Activity 1342 */ 1343 function Actor(activity_id,context2d, text, x, y) 1344 { 1345 Activity.call(this,activity_id,context2d,text,x,y,true); 1346 this.node.maxX+=Actor.LENGTH; 1347 //初始化ID 1348 var id='Actor'+new Date().getTime(); 1349 var self=this; 1350 //重新定义 1351 this.properties=[ 1352 {"field":"id","name":"标识符","value":id,"group":"可编辑的","editor":{"type":"textbox","options":{"required": true}}}, 1353 {"field":"activity_id","name":"活动节点","value":activity_id,"group":"不可编辑的"}, 1354 {"field":"name","name":"文本","value":this.node.getText(),"group":"可编辑的","editor":{"type":"textbox","options":{"required": true,"validType":"length[1,20]","onChange":function(newValue,oldValue){ 1355 1356 if(newValue && oldValue && newValue!=oldValue) 1357 { 1358 self.node.setText(newValue); 1359 self.node.maxX=self.node.minX+getTextWidth(context2d, newValue)+Actor.LENGTH; 1360 WorkflowEngine.repaint(); 1361 } 1362 1363 }}}}, 1364 1365 ]; 1366 1367 } 1368 //继承方法 1369 Actor.prototype=new Activity(); 1370 //修改构造器 1371 Actor.prototype.constructor=Actor; 1372 //覆盖 1373 Actor.prototype.propertygrid=function(){ 1374 return this.properties; 1375 }; 1376 //覆盖 1377 Actor.prototype.toJson=function(){ 1378 return {id:this.id(),name:this.properties[2].value}; 1379 }; 1380 //覆盖draw方法 1381 Actor.prototype.draw = function(context2d) { 1382 drawImage(context2d,this.node.maxX-Actor.LENGTH,this.node.minY,Actor.LENGTH,Actor.LENGTH); 1383 switch (this.status) { 1384 case Activity.NORMAL: 1385 drawText(context2d, this.node.minX, (this.node.minY + this.node.maxY) / 2+5, 1386 this.node.getText()); 1387 break; 1388 case Activity.FOCUS: 1389 this.node.hoveHighlight(context2d); 1390 break; 1391 case Activity.PICKED: 1392 this.node.checkedHighlight(context2d); 1393 break; 1394 } 1395 1396 }; 1397 Actor.LENGTH=16; 1398 1399 /* 1400 *定义一个带箭头的连线 1401 */ 1402 function Arrow(id1,id2) 1403 { 1404 this.id1=id1; 1405 this.id2=id2; 1406 var self=this; 1407 this.conditions=[{"field":"case","name":"判断","value":"","group":"条件(->"+id2+")","editor":{"type":"textbox","options":{"required": true},"onChange":function(newValue,oldValue){ 1408 if(newValue && oldValue && newValue!=oldValue) 1409 { 1410 self.condition(newValue); 1411 } 1412 }}}, 1413 {"field":"status","name":"跳转","value":id2,"group":"条件(->"+id2+")"} 1414 ]; 1415 1416 } 1417 //getter 条件 1418 Arrow.prototype.toJson=function(){ 1419 return {id2:this.id2,condition:this.condition()}; 1420 }; 1421 //setter/getter判断值 1422 Arrow.prototype.condition=function(cond){ 1423 return cond?(this.conditions[0].value=cond):this.conditions[0].value; 1424 }; 1425 //getter 1426 Arrow.prototype.id1=function(){ 1427 return this.id1; 1428 }; 1429 Arrow.prototype.id2=function(){ 1430 return this.id2; 1431 }; 1432 Arrow.prototype.draw=function(context2d,parent,child){ 1433 //画连线 1434 drawLine(context2d, parent.node.maxX, 1435 (parent.node.minY + parent.node.maxY) / 2, 1436 child.node.minX, 1437 (child.node.minY + child.node.maxY) / 2); 1438 //绘制箭头 1439 drawArrow(context2d, parent.node.maxX, 1440 (parent.node.minY + parent.node.maxY) / 2, 1441 child.node.minX, 1442 (child.node.minY + child.node.maxY) / 2); 1443 1444 }; 1445 /* 1446 *定义虚拟光标类:Cursor 1447 */ 1448 function Cursor(workflow,x,y,type) 1449 { 1450 this.x=x; 1451 this.y=y; 1452 this.type=type; 1453 this.workflow=workflow; 1454 } 1455 1456 Cursor.prototype.draw=function(context2d){ 1457 switch(this.type) 1458 { 1459 case 'Activity': 1460 drawText(context2d, this.x, this.y+5, 1461 '新建节点'); 1462 break; 1463 case 'Actor': 1464 //画连线 1465 drawLine(context2d, (this.workflow.checked.node.minX+this.workflow.checked.node.maxX)/2, 1466 this.workflow.checked.node.minY, 1467 this.x+Actor.LENGTH/2,this.y+Actor.LENGTH); 1468 drawImage(context2d,this.x,this.y,Actor.LENGTH,Actor.LENGTH); 1469 break; 1470 case 'Arrow': 1471 //画连线 1472 drawLine(context2d, this.workflow.checked.node.maxX, 1473 (this.workflow.checked.node.minY + this.workflow.checked.node.maxY) / 2, 1474 this.x,this.y); 1475 drawArrow(context2d, this.x-10, this.y, this.x, this.y); 1476 break; 1477 } 1478 }; 1479 WorkflowEngine.initialize(); 1480 1481 }); 1482 </script> 1483 </body> 1484 </html>
浏览器里的效果图如下: