// import { getNewShapePosition } from 'bpmn-js/lib/features/auto-place/BpmnAutoPlaceUtil'; import { is } from 'bpmn-js/lib/util/ModelUtil'; import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; import { getMid, asTRBL, getOrientation } from 'diagram-js/lib/layout/LayoutUtil'; import { findFreePosition, generateGetNextPosition, getConnectedDistance } from 'diagram-js/lib/features/auto-place/AutoPlaceUtil'; var HIGH_PRIORITY = 2500; export default function AutoPlaceBehavior(eventBus, gridSnapping, toolManager, selection, modeling, bpmnFactory, elementRegistry) { eventBus.on('connection.added', HIGH_PRIORITY, function (context, element) { let source = context.element.source; let target = context.element.target; let outgoing = source.outgoing; // 分支 同步条件节点 条件同步 let gateWay = ["bpmn:ExclusiveGateway", "bpmn:ParallelGateway", "bpmn:InclusiveGateway"]; if (gateWay.includes(context.element.source.type) && context.element.waypoints && context.element.waypoints.length == 3) { let mid = Math.ceil(outgoing.length / 2); // 奇数 if (outgoing.length % 2 != 0) { // 设置中间点的线和任务节点的位置 } // 设置左边和右边的线和任务的位置 // context.element.waypoints[0] = { x: context.element.waypoints[0].x + source.width / 2, y: context.element.waypoints[0].y - source.height / 2 } // context.element.waypoints[2] = { x: context.element.waypoints[2].x + target.width / 2, y: context.element.waypoints[2].y - target.height / 2 } // context.element.waypoints[1] = { x: context.element.waypoints[2].x, y: context.element.waypoints[0].y } } }); eventBus.on('autoPlace', HIGH_PRIORITY, function (context) { var source = context.source, sourceMid = getMid(source), shape = context.shape; var position = getNewShapePosition(source, shape); ['x', 'y'].forEach(function (axis) { var options = {}; // do not snap if x/y equal if (position[axis] === sourceMid[axis]) { return; } if (position[axis] > sourceMid[axis]) { options.min = position[axis]; } else { options.max = position[axis]; } if (is(shape, 'bpmn:TextAnnotation')) { if (isHorizontal(axis)) { options.offset = -shape.width / 2; } else { options.offset = -shape.height / 2; } } position[axis] = gridSnapping.snapValue(position[axis], options); }); // must be returned to be considered by auto place return position; }); // 消除移动到边界移动的bug eventBus.on('drag.move', HIGH_PRIORITY, function (context) { }); // 移除原来的监听器 eventBus._listeners["drag.move"].next = null; } AutoPlaceBehavior.$inject = [ 'eventBus', 'gridSnapping', 'toolManager', 'selection', 'modeling', 'bpmnFactory', 'elementRegistry' ]; // helpers ////////// function isHorizontal(axis) { return axis === 'x'; } // BpmnAutoPlaceUtil.js /** * Find the new position for the target element to * connect to source. * * @param {djs.model.Shape} source * @param {djs.model.Shape} element * * @return {Point} */ function getNewShapePosition(source, element) { if (is(element, 'bpmn:TextAnnotation')) { return getTextAnnotationPosition(source, element); } if (isAny(element, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { return getDataElementPosition(source, element); } if (is(element, 'bpmn:FlowNode')) { return getFlowNodePosition(source, element); } } /** * Always try to place element bottom of source; * compute actual distance from previous nodes in flow. */ function getFlowNodePosition(source, element) { var sourceTrbl = asTRBL(source); var sourceMid = getMid(source); var horizontalDistance = getConnectedDistance(source, { filter: function (connection) { return is(connection, 'bpmn:SequenceFlow'); } }); var margin = 30, minDistance = 80, orientation = 'left'; if (is(source, 'bpmn:BoundaryEvent')) { orientation = getOrientation(source, source.host, -25); if (orientation.indexOf('top') !== -1) { margin *= -1; } } // 向右 // var position = { // x: sourceTrbl.right + horizontalDistance + element.width / 2, // y: sourceMid.y + getVerticalDistance(orientation, minDistance) // }; // 向下 var position = { x: sourceMid.x + getVerticalDistance(orientation, minDistance), y: sourceTrbl.bottom + horizontalDistance + element.width / 2, }; var nextPositionDirection = { y: { margin: margin, minDistance: minDistance } }; // 如果是已经有分支的话 下一个位置 x坐标偏移 if ("bpmn:ExclusiveGateway" == source.type) { // position = { // x: sourceTrbl.right + horizontalDistance + element.width / 2, // y: sourceMid.y + getVerticalDistance(orientation, minDistance) // }; nextPositionDirection = { x: { margin: margin * 2, minDistance: minDistance } }; } return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); } function getVerticalDistance(orientation, minDistance) { if (orientation.indexOf('top') != -1) { return -1 * minDistance; } else if (orientation.indexOf('bottom') != -1) { return minDistance; } else { return 0; } } /** * Always try to place text annotations top right of source. */ function getTextAnnotationPosition(source, element) { var sourceTrbl = asTRBL(source); var position = { x: sourceTrbl.right + element.width / 2, y: sourceTrbl.top - 50 - element.height / 2 }; var nextPositionDirection = { y: { margin: -30, minDistance: 20 } }; return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); } /** * Always put element bottom right of source. */ function getDataElementPosition(source, element) { var sourceTrbl = asTRBL(source); var position = { x: sourceTrbl.right - 10 + element.width / 2, y: sourceTrbl.bottom + 40 + element.width / 2 }; var nextPositionDirection = { x: { margin: 30, minDistance: 30 } }; return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); }