index.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. // @ts-nocheck
  2. import { every, find, forEach, some } from 'min-dash';
  3. import { is, getBusinessObject } from 'bpmn-js/lib/util/ModelUtil';
  4. import { getParent, isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
  5. import { isLabel } from 'bpmn-js/lib/util/LabelUtil';
  6. import {
  7. isExpanded,
  8. isEventSubProcess,
  9. isInterrupting,
  10. hasErrorEventDefinition,
  11. hasEscalationEventDefinition,
  12. hasCompensateEventDefinition,
  13. } from 'bpmn-js/lib/util/DiUtil';
  14. import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider';
  15. import { getBoundaryAttachment as isBoundaryAttachment } from 'bpmn-js/lib/features/snapping/BpmnSnappingUtil';
  16. import {
  17. bpmnEnd,
  18. bpmnExecute,
  19. bpmnGroup,
  20. bpmnProcessing,
  21. bpmnSubFlow,
  22. bpmnTask,
  23. bpmnTrigger,
  24. typeEnd,
  25. typeExecute,
  26. typeOutside,
  27. typeProcessing,
  28. typeSubFlow,
  29. typeTask,
  30. typeTrigger,
  31. typeStart
  32. } from '../config/variableName';
  33. import { changeTypeByTaskShape, changeTypeByTrigger, triggerTypeChange } from '../config';
  34. function inherits(e, t) {
  35. t && ((e.super_ = t), (e.prototype = Object.create(t.prototype, { constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 } })));
  36. }
  37. /**
  38. * BPMN specific modeling rule
  39. */
  40. export default function BpmnRules(eventBus, type, injector) {
  41. RuleProvider.call(this, eventBus, type, injector);
  42. this.type = type;
  43. this._injector = injector;
  44. }
  45. inherits(BpmnRules, RuleProvider);
  46. BpmnRules.$inject = ['eventBus', 'config.type', 'injector'];
  47. BpmnRules.prototype.init = function () {
  48. // 自定义删除规则
  49. this.addRule('connection.start', context => {
  50. var source = context.source;
  51. return canStartConnection(source);
  52. });
  53. this.addRule('connection.create', context => {
  54. const { source, target, hints = {} } = context;
  55. const targetParent = hints.targetParent;
  56. const targetAttach = hints.targetAttach;
  57. let isSameGroupId = false
  58. if (changeTypeByTaskShape[target.wnType] && source.wnType === typeTrigger) {
  59. if (source.businessObject.$attrs?.customGroupId === target.businessObject.$attrs?.customGroupId) {
  60. isSameGroupId = true
  61. }
  62. }
  63. if (!isSameGroupId) {
  64. if (!canConnectNodes(source, target, this)) return false;
  65. if (!canConnectTaskNodes(source, target, this)) return false;
  66. if (!canConnectTriggerElements(source, target, this)) return false;
  67. }
  68. // 如果目标需要附加或指定父节点
  69. if (targetAttach) return false;
  70. if (targetParent) target.parent = targetParent;
  71. try {
  72. return canConnect(source, target);
  73. } finally {
  74. if (targetParent) target.parent = null;
  75. }
  76. });
  77. this.addRule('connection.reconnect', context => {
  78. const { connection, source, target } = context;
  79. if (!canConnectNodes(source, target, this, connection)) return false;
  80. if (!canConnectTaskNodes(source, target, this)) return false;
  81. if (!canConnectTriggerElements(source, target, this)) return false;
  82. return canConnect(source, target, connection);
  83. });
  84. this.addRule('connection.updateWaypoints', context => {
  85. return { type: context.connection.type };
  86. });
  87. this.addRule('shape.resize', context => {
  88. var shape = context.shape,
  89. newBounds = context.newBounds;
  90. return canResize(shape, newBounds);
  91. });
  92. // 元素创建
  93. this.addRule('elements.create', context => {
  94. var elements = context.elements,
  95. position = context.position,
  96. target = context.target;
  97. if (isConnection(target) && !canInsert(elements, target, position)) return false;
  98. return every(elements, element => {
  99. if (isConnection(element)) return canConnect(element.source, element.target, element);
  100. if (element.host) return canAttach(element, element.host, null, position);
  101. return canCreate(element, target, null, position);
  102. });
  103. });
  104. // 移动规则
  105. this.addRule('elements.move', context => {
  106. var target = context.target,
  107. shapes = context.shapes,
  108. position = context.position;
  109. return (
  110. canAttach(shapes, target, null, position) ||
  111. canReplace(shapes, target, position) ||
  112. canMove(shapes, target, position) ||
  113. canInsert(shapes, target, position)
  114. );
  115. });
  116. // 创建规则 发起节点只允许存在一个
  117. this.addRule('shape.create', context => {
  118. let startEventCount = 0;
  119. if (context?.target?.children?.length) {
  120. context.target.children.map((children: any) => {
  121. if (children.type === 'bpmn:StartEvent') return startEventCount++;
  122. });
  123. }
  124. if (context.shape.type === 'bpmn:StartEvent' && startEventCount > 0) return false;
  125. return canCreate(context.shape, context.target, context.source, context.position);
  126. });
  127. this.addRule('shape.attach', context => {
  128. return canAttach(context.shape, context.target, null, context.position);
  129. });
  130. this.addRule('element.copy', context => {
  131. var element = context.element,
  132. elements = context.elements;
  133. const hasStartElement = elements.some(o => o.type === 'bpmn:StartEvent');
  134. return !hasStartElement ? canCopy(elements, element, this) : false;
  135. });
  136. };
  137. BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
  138. BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
  139. BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
  140. BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
  141. BpmnRules.prototype.canMove = canMove;
  142. BpmnRules.prototype.canAttach = canAttach;
  143. BpmnRules.prototype.canReplace = canReplace;
  144. BpmnRules.prototype.canDrop = canDrop;
  145. BpmnRules.prototype.canInsert = canInsert;
  146. BpmnRules.prototype.canCreate = canCreate;
  147. BpmnRules.prototype.canConnect = canConnect;
  148. BpmnRules.prototype.canResize = canResize;
  149. BpmnRules.prototype.canCopy = canCopy;
  150. BpmnRules.prototype.canConnectNodes = canConnectNodes;
  151. BpmnRules.prototype.canConnectTaskNodes = canConnectTaskNodes;
  152. BpmnRules.prototype.canConnectTriggerElements = canConnectTriggerElements;
  153. /**
  154. * Utility functions for rule checking
  155. */
  156. /**
  157. * Checks if given element can be used for starting connection.
  158. *
  159. * @param {Element} source
  160. * @return {boolean}
  161. */
  162. function canStartConnection(element) {
  163. if (nonExistingOrLabel(element)) return null;
  164. return isAny(element, ['bpmn:FlowNode', 'bpmn:InteractionNode', 'bpmn:DataObjectReference', 'bpmn:DataStoreReference', 'bpmn:Group', 'bpmn:TextAnnotation']);
  165. }
  166. function nonExistingOrLabel(element) {
  167. return !element || isLabel(element);
  168. }
  169. function isSame(a, b) {
  170. return a === b;
  171. }
  172. function getOrganizationalParent(element) {
  173. do {
  174. if (is(element, 'bpmn:Process')) {
  175. return getBusinessObject(element);
  176. }
  177. if (is(element, 'bpmn:Participant')) {
  178. return getBusinessObject(element).processRef || getBusinessObject(element);
  179. }
  180. } while ((element = element.parent));
  181. }
  182. function isTextAnnotation(element) {
  183. return is(element, 'bpmn:TextAnnotation');
  184. }
  185. function isGroup(element) {
  186. return is(element, 'bpmn:Group') && !element.labelTarget;
  187. }
  188. function isCompensationBoundary(element) {
  189. return is(element, 'bpmn:BoundaryEvent') && hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
  190. }
  191. function isForCompensation(e) {
  192. return getBusinessObject(e).isForCompensation;
  193. }
  194. function isSameOrganization(a, b) {
  195. var parentA = getOrganizationalParent(a),
  196. parentB = getOrganizationalParent(b);
  197. return parentA === parentB;
  198. }
  199. function isMessageFlowSource(element) {
  200. return (
  201. is(element, 'bpmn:InteractionNode') &&
  202. !is(element, 'bpmn:BoundaryEvent') &&
  203. (!is(element, 'bpmn:Event') || (is(element, 'bpmn:ThrowEvent') && hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')))
  204. );
  205. }
  206. function isMessageFlowTarget(element) {
  207. return (
  208. is(element, 'bpmn:InteractionNode') &&
  209. !isForCompensation(element) &&
  210. (!is(element, 'bpmn:Event') || (is(element, 'bpmn:CatchEvent') && hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition'))) &&
  211. !(is(element, 'bpmn:BoundaryEvent') && !hasEventDefinition(element, 'bpmn:MessageEventDefinition'))
  212. );
  213. }
  214. function getScopeParent(element) {
  215. var parent = element;
  216. while ((parent = parent.parent)) {
  217. if (is(parent, 'bpmn:FlowElementsContainer')) {
  218. return getBusinessObject(parent);
  219. }
  220. if (is(parent, 'bpmn:Participant')) {
  221. return getBusinessObject(parent).processRef;
  222. }
  223. }
  224. return null;
  225. }
  226. function isSameScope(a, b) {
  227. var scopeParentA = getScopeParent(a),
  228. scopeParentB = getScopeParent(b);
  229. return scopeParentA === scopeParentB;
  230. }
  231. function hasEventDefinition(element, eventDefinition) {
  232. var bo = getBusinessObject(element);
  233. return !!find(bo.eventDefinitions || [], function (definition) {
  234. return is(definition, eventDefinition);
  235. });
  236. }
  237. function hasEventDefinitionOrNone(element, eventDefinition) {
  238. var bo = getBusinessObject(element);
  239. return (bo.eventDefinitions || []).every(function (definition) {
  240. return is(definition, eventDefinition);
  241. });
  242. }
  243. function isSequenceFlowSource(element) {
  244. return (
  245. is(element, 'bpmn:FlowNode') &&
  246. !is(element, 'bpmn:EndEvent') &&
  247. !isEventSubProcess(element) &&
  248. !(is(element, 'bpmn:IntermediateThrowEvent') && hasEventDefinition(element, 'bpmn:LinkEventDefinition')) &&
  249. !isCompensationBoundary(element) &&
  250. !isForCompensation(element)
  251. );
  252. }
  253. function isSequenceFlowTarget(element) {
  254. return (
  255. is(element, 'bpmn:FlowNode') &&
  256. !is(element, 'bpmn:StartEvent') &&
  257. !is(element, 'bpmn:BoundaryEvent') &&
  258. !isEventSubProcess(element) &&
  259. !(is(element, 'bpmn:IntermediateCatchEvent') && hasEventDefinition(element, 'bpmn:LinkEventDefinition')) &&
  260. !isForCompensation(element)
  261. );
  262. }
  263. function isEventBasedTarget(element) {
  264. return (
  265. is(element, 'bpmn:ReceiveTask') ||
  266. (is(element, 'bpmn:IntermediateCatchEvent') &&
  267. (hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
  268. hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
  269. hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
  270. hasEventDefinition(element, 'bpmn:SignalEventDefinition')))
  271. );
  272. }
  273. function isConnection(element) {
  274. return element.waypoints;
  275. }
  276. function getParents(element) {
  277. var parents = [];
  278. while (element) {
  279. element = element.parent;
  280. if (element) parents.push(element);
  281. }
  282. return parents;
  283. }
  284. function isParent(possibleParent, element) {
  285. var allParents = getParents(element);
  286. return allParents.indexOf(possibleParent) !== -1;
  287. }
  288. function canConnect(source, target, connection) {
  289. if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) return null;
  290. if (!is(connection, 'bpmn:DataAssociation')) {
  291. if (canConnectMessageFlow(source, target)) return { type: 'bpmn:MessageFlow' };
  292. if (canConnectSequenceFlow(source, target)) return { type: 'bpmn:SequenceFlow' };
  293. }
  294. var connectDataAssociation = canConnectDataAssociation(source, target);
  295. if (connectDataAssociation) return connectDataAssociation;
  296. if (isCompensationBoundary(source) && isForCompensation(target)) {
  297. return {
  298. type: 'bpmn:Association',
  299. associationDirection: 'One',
  300. };
  301. }
  302. if (canConnectAssociation(source, target)) {
  303. return {
  304. type: 'bpmn:Association',
  305. };
  306. }
  307. return false;
  308. }
  309. /**
  310. * Can an element be dropped into the target element
  311. *
  312. * @return {boolean}
  313. */
  314. function canDrop(element, target, position) {
  315. // can move labels and groups everywhere
  316. if (isLabel(element) || isGroup(element)) return true;
  317. // disallow to create elements on collapsed pools
  318. if (is(target, 'bpmn:Participant') && !isExpanded(target)) return false;
  319. // allow to create new participants on
  320. // existing collaboration and process diagrams
  321. if (is(element, 'bpmn:Participant')) return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration');
  322. // allow moving DataInput / DataOutput within its original container only
  323. if (isAny(element, ['bpmn:DataInput', 'bpmn:DataOutput'])) {
  324. if (element.parent) return target === element.parent;
  325. }
  326. // allow creating lanes on participants and other lanes only
  327. if (is(element, 'bpmn:Lane')) return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane');
  328. // disallow dropping boundary events which cannot replace with intermediate event
  329. if (is(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) return false;
  330. // drop flow elements onto flow element containers
  331. // and participants
  332. if (is(element, 'bpmn:FlowElement') && !is(element, 'bpmn:DataStoreReference')) {
  333. if (is(target, 'bpmn:FlowElementsContainer')) return isExpanded(target);
  334. return isAny(target, ['bpmn:Participant', 'bpmn:Lane']);
  335. }
  336. // disallow dropping data store reference if there is no process to append to
  337. if (is(element, 'bpmn:DataStoreReference') && is(target, 'bpmn:Collaboration')) {
  338. return some(getBusinessObject(target).get('participants'), function (participant) {
  339. return !!participant.get('processRef');
  340. });
  341. }
  342. // account for the fact that data associations are always
  343. // rendered and moved to top (Process or Collaboration level)
  344. //
  345. // artifacts may be placed wherever, too
  346. if (isAny(element, ['bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference'])) {
  347. return isAny(target, ['bpmn:Collaboration', 'bpmn:Lane', 'bpmn:Participant', 'bpmn:Process', 'bpmn:SubProcess']);
  348. }
  349. if (is(element, 'bpmn:MessageFlow')) {
  350. return is(target, 'bpmn:Collaboration') || element.source.parent == target || element.target.parent == target;
  351. }
  352. return false;
  353. }
  354. function isDroppableBoundaryEvent(event) {
  355. return getBusinessObject(event).cancelActivity && (hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event));
  356. }
  357. function isBoundaryEvent(element) {
  358. return !isLabel(element) && is(element, 'bpmn:BoundaryEvent');
  359. }
  360. function isLane(element) {
  361. return is(element, 'bpmn:Lane');
  362. }
  363. /**
  364. * We treat IntermediateThrowEvents as boundary events during create,
  365. * this must be reflected in the rules.
  366. */
  367. function isBoundaryCandidate(element) {
  368. if (isBoundaryEvent(element)) return true;
  369. if (is(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) return true;
  370. return is(element, 'bpmn:IntermediateCatchEvent') && hasCommonBoundaryIntermediateEventDefinition(element);
  371. }
  372. function hasNoEventDefinition(element) {
  373. var bo = getBusinessObject(element);
  374. return bo && !(bo.eventDefinitions && bo.eventDefinitions.length);
  375. }
  376. function hasCommonBoundaryIntermediateEventDefinition(element) {
  377. return hasOneOfEventDefinitions(element, [
  378. 'bpmn:MessageEventDefinition',
  379. 'bpmn:TimerEventDefinition',
  380. 'bpmn:SignalEventDefinition',
  381. 'bpmn:ConditionalEventDefinition',
  382. ]);
  383. }
  384. function hasOneOfEventDefinitions(element, eventDefinitions) {
  385. return eventDefinitions.some(function (definition) {
  386. return hasEventDefinition(element, definition);
  387. });
  388. }
  389. function isReceiveTaskAfterEventBasedGateway(element) {
  390. return (
  391. is(element, 'bpmn:ReceiveTask') &&
  392. find(element.incoming, function (incoming) {
  393. return is(incoming.source, 'bpmn:EventBasedGateway');
  394. })
  395. );
  396. }
  397. function canAttach(elements, target, source, position) {
  398. if (!Array.isArray(elements)) elements = [elements];
  399. // only (re-)attach one element at a time
  400. if (elements.length !== 1) return false;
  401. var element = elements[0];
  402. // do not attach labels
  403. if (isLabel(element)) return false;
  404. // only handle boundary events
  405. if (!isBoundaryCandidate(element)) return false;
  406. // disallow drop on event sub processes
  407. if (isEventSubProcess(target)) return false;
  408. // only allow drop on non compensation activities
  409. if (!is(target, 'bpmn:Activity') || isForCompensation(target)) return false;
  410. // only attach to subprocess border
  411. if (position && !isBoundaryAttachment(position, target)) return false;
  412. // do not attach on receive tasks after event based gateways
  413. if (isReceiveTaskAfterEventBasedGateway(target)) return false;
  414. return 'attach';
  415. }
  416. /**
  417. * Defines how to replace elements for a given target.
  418. *
  419. * Returns an array containing all elements which will be replaced.
  420. *
  421. * @example
  422. *
  423. * [{ id: 'IntermediateEvent_2',
  424. * type: 'bpmn:StartEvent'
  425. * },
  426. * { id: 'IntermediateEvent_5',
  427. * type: 'bpmn:EndEvent'
  428. * }]
  429. *
  430. * @param {Array} elements
  431. * @param {Object} target
  432. *
  433. * @return {Object} an object containing all elements which have to be replaced
  434. */
  435. function canReplace(elements, target, position) {
  436. if (!target) return false;
  437. var canExecute: any = {
  438. replacements: [],
  439. };
  440. forEach(elements, function (element) {
  441. if (!isEventSubProcess(target)) {
  442. if (is(element, 'bpmn:StartEvent') && element.type !== 'label' && canDrop(element, target)) {
  443. // replace a non-interrupting start event by a blank interrupting start event
  444. // when the target is not an event sub process
  445. if (!isInterrupting(element)) {
  446. canExecute.replacements.push({
  447. oldElementId: element.id,
  448. newElementType: 'bpmn:StartEvent',
  449. });
  450. }
  451. // replace an error/escalation/compensate start event by a blank interrupting start event
  452. // when the target is not an event sub process
  453. if (hasErrorEventDefinition(element) || hasEscalationEventDefinition(element) || hasCompensateEventDefinition(element)) {
  454. canExecute.replacements.push({
  455. oldElementId: element.id,
  456. newElementType: 'bpmn:StartEvent',
  457. });
  458. }
  459. // replace a typed start event by a blank interrupting start event
  460. // when the target is a sub process but not an event sub process
  461. if (
  462. hasOneOfEventDefinitions(element, [
  463. 'bpmn:MessageEventDefinition',
  464. 'bpmn:TimerEventDefinition',
  465. 'bpmn:SignalEventDefinition',
  466. 'bpmn:ConditionalEventDefinition',
  467. ]) &&
  468. is(target, 'bpmn:SubProcess')
  469. ) {
  470. canExecute.replacements.push({
  471. oldElementId: element.id,
  472. newElementType: 'bpmn:StartEvent',
  473. });
  474. }
  475. }
  476. }
  477. if (!is(target, 'bpmn:Transaction')) {
  478. if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') && element.type !== 'label') {
  479. if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) {
  480. canExecute.replacements.push({
  481. oldElementId: element.id,
  482. newElementType: 'bpmn:EndEvent',
  483. });
  484. }
  485. if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
  486. canExecute.replacements.push({
  487. oldElementId: element.id,
  488. newElementType: 'bpmn:BoundaryEvent',
  489. });
  490. }
  491. }
  492. }
  493. });
  494. return canExecute.replacements.length ? canExecute : false;
  495. }
  496. function canMove(elements, target) {
  497. // do not move selection containing lanes
  498. if (some(elements, isLane) || some(elements, isGroup) || some(elements, isLabel)) return false;
  499. // allow default move check to start move operation
  500. if (!target) return true;
  501. return elements.every(function (element) {
  502. return canDrop(element, target);
  503. });
  504. }
  505. function canCreate(shape, target, source, position) {
  506. if (!target) return false;
  507. if (isLabel(shape) || isGroup(shape)) return true;
  508. if (isSame(source, target)) return false;
  509. // ensure we do not drop the element
  510. // into source
  511. if (source && isParent(source, target)) return false;
  512. return canDrop(shape, target, position) || canInsert(shape, target, position);
  513. }
  514. function canResize(shape, newBounds) {
  515. if (is(shape, 'bpmn:SubProcess')) {
  516. return isExpanded(shape) && (!newBounds || (newBounds.width >= 100 && newBounds.height >= 80));
  517. }
  518. if (is(shape, 'bpmn:Lane')) {
  519. return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
  520. }
  521. if (is(shape, 'bpmn:Participant')) {
  522. return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
  523. }
  524. if (isTextAnnotation(shape)) return true;
  525. if (isGroup(shape)) return true;
  526. return false;
  527. }
  528. /**
  529. * Check, whether one side of the relationship
  530. * is a text annotation.
  531. */
  532. function isOneTextAnnotation(source, target) {
  533. var sourceTextAnnotation = isTextAnnotation(source),
  534. targetTextAnnotation = isTextAnnotation(target);
  535. return (sourceTextAnnotation || targetTextAnnotation) && sourceTextAnnotation !== targetTextAnnotation;
  536. }
  537. function canConnectAssociation(source, target) {
  538. // compensation boundary events are exception
  539. if (isCompensationBoundary(source) && isForCompensation(target)) return true;
  540. // don't connect parent <-> child
  541. if (isParent(target, source) || isParent(source, target)) return false;
  542. // allow connection of associations between <!TextAnnotation> and <TextAnnotation>
  543. if (isOneTextAnnotation(source, target)) return true;
  544. // can connect associations where we can connect
  545. // data associations, too (!)
  546. return !!canConnectDataAssociation(source, target);
  547. }
  548. function canConnectMessageFlow(source, target) {
  549. // during connect user might move mouse out of canvas
  550. // https://github.com/bpmn-io/bpmn-js/issues/1033
  551. if (getRootElement(source) && !getRootElement(target)) return false;
  552. return isMessageFlowSource(source) && isMessageFlowTarget(target) && !isSameOrganization(source, target);
  553. }
  554. function canConnectSequenceFlow(source, target) {
  555. if (
  556. isEventBasedTarget(target) &&
  557. target.incoming.length > 0 &&
  558. areOutgoingEventBasedGatewayConnections(target.incoming) &&
  559. !is(source, 'bpmn:EventBasedGateway')
  560. ) {
  561. return false;
  562. }
  563. return (
  564. isSequenceFlowSource(source) &&
  565. isSequenceFlowTarget(target) &&
  566. isSameScope(source, target) &&
  567. !(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target))
  568. );
  569. }
  570. function canConnectDataAssociation(source, target) {
  571. if (isAny(source, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference']) && isAny(target, ['bpmn:Activity', 'bpmn:ThrowEvent'])) {
  572. return { type: 'bpmn:DataInputAssociation' };
  573. }
  574. if (isAny(target, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference']) && isAny(source, ['bpmn:Activity', 'bpmn:CatchEvent'])) {
  575. return { type: 'bpmn:DataOutputAssociation' };
  576. }
  577. return false;
  578. }
  579. function canInsert(shape, flow, position) {
  580. if (!flow) return false;
  581. if (Array.isArray(shape)) {
  582. if (shape.length !== 1) return false;
  583. shape = shape[0];
  584. }
  585. if (flow.source === shape || flow.target === shape) return false;
  586. // return true if we can drop on the
  587. // underlying flow parent
  588. // at this point we are not really able to talk
  589. // about connection rules (yet)
  590. return (
  591. isAny(flow, ['bpmn:SequenceFlow', 'bpmn:MessageFlow']) &&
  592. !isLabel(flow) &&
  593. is(shape, 'bpmn:FlowNode') &&
  594. !is(shape, 'bpmn:BoundaryEvent') &&
  595. canDrop(shape, flow.parent, position)
  596. );
  597. }
  598. function includes(elements, element) {
  599. return elements && element && elements.indexOf(element) !== -1;
  600. }
  601. function canCopy(elements, element, context) {
  602. if (context.type === 1) return false;
  603. if (isLabel(element)) return true;
  604. if (is(element, 'bpmn:Lane') && !includes(elements, element.parent)) return false;
  605. if (element.wnType === typeTrigger || changeTypeByTaskShape[element.wnType]) return false;
  606. return true;
  607. }
  608. function isOutgoingEventBasedGatewayConnection(connection) {
  609. if (connection && connection.source) {
  610. return is(connection.source, 'bpmn:EventBasedGateway');
  611. }
  612. }
  613. function areOutgoingEventBasedGatewayConnections(connections) {
  614. connections = connections || [];
  615. return connections.some(isOutgoingEventBasedGatewayConnection);
  616. }
  617. function getRootElement(element) {
  618. return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
  619. }
  620. function getElementsUpToElement(element) {
  621. const result = new Set();
  622. const findIncoming = el => {
  623. const incoming = el.incoming || [];
  624. incoming.map(connection => {
  625. result.add(connection.source?.id);
  626. const sourceElement = connection.source; // 直接访问源元素
  627. // 检查源元素的wntype是否为trigger
  628. if (sourceElement.wnType === typeTrigger) return true; // 找到满足条件的元素
  629. if (sourceElement) {
  630. const found = findIncoming(sourceElement);
  631. if (found) return true;
  632. }
  633. });
  634. return false;
  635. };
  636. const foundTrigger = findIncoming(element);
  637. return result; // 如果找到满足条件的元素,则返回结果,否则返回空数组
  638. }
  639. function canConnectNodes(source, target, context, connection?) {
  640. if (source.id === target.id) return false; // 自连接检查
  641. if (source.type === 'bpmn:StartEvent' && ([bpmnEnd].includes(target.type) || target.wnType === typeTrigger || changeTypeByTaskShape[target.wnType])) return false; // 开始节点不能连接结束节点
  642. if ((changeTypeByTrigger[source.wnType] || [typeOutside].includes(source.wnType)) && target.type === 'bpmn:EndEvent') return false; //
  643. if ((source.wnType === typeSubFlow && target.wnType === typeTrigger) || (source.wnType === typeTrigger && target.wnType === typeSubFlow)) return false; // 子流程不能连接触发节点
  644. if (!connection && source.outgoing && source.outgoing.some(o => o.target.id === target.id)) return false; // 检查重复连接
  645. if (connection && !(connection.target.id === target.id)) return false;
  646. if (source.wnType === typeTrigger && (target.wnType === typeTask || target.wnType === typeEnd)) return false; // 触发节点连接审批节点及结束节点
  647. if (source.wnType === typeOutside && ([typeSubFlow, typeOutside].includes(target.wnType))) return false; // 外部节点不能连接子流程外部节点
  648. if (source.wnType === typeOutside) {
  649. // 获取该线条的data 判断是否包含条件 有条件的话禁用重新连接
  650. if(context._injector?.get('jnpfData')?.getValue(connection?.id)?.conditions?.length > 0) return false
  651. }
  652. return true;
  653. }
  654. // 检查任务节点之间的连接规则
  655. function canConnectTaskNodes(source, target, context) {
  656. const sourceType = changeTypeByTaskShape[source.wnType];
  657. const targetType = changeTypeByTaskShape[target.wnType];
  658. const isTaskToOtherType = (sourceType && !targetType) || (!sourceType && source.wnType !== bpmnTrigger && targetType);
  659. if (isTaskToOtherType) {
  660. if (!(context.type === 2 && target.wnType === typeEnd) && !(context.type === 2 && !changeTypeByTrigger[target.wnType])) {
  661. return false;
  662. }
  663. }
  664. // 检查任务节点是否在相同分组中
  665. if (sourceType && targetType) {
  666. if (source.businessObject.$attrs?.customGroupId !== target.businessObject.$attrs?.customGroupId) return false;
  667. const groupSet = getElementsUpToElement(source);
  668. if (groupSet.has(target.id)) return false;
  669. }
  670. return true;
  671. }
  672. // 检查触发元素的连接规则
  673. function canConnectTriggerElements(source, target, context) {
  674. const isSourceTrigger = source.wnType === typeTrigger;
  675. const isTargetTrigger = target.wnType === typeTrigger;
  676. if (isSourceTrigger && isTargetTrigger) return false;
  677. // 不同分组检查
  678. if (isSourceTrigger || isTargetTrigger) {
  679. const elementRegistry = context._injector.get('elementRegistry');
  680. const element = isTargetTrigger ? elementRegistry.get(target.id) : elementRegistry.get(source.id);
  681. if (element.incoming.length > 0) {
  682. let wnType = element.incoming[0].source.wnType;
  683. if (source.wnType != wnType) return false;
  684. }
  685. if (source.businessObject.$attrs?.customGroupId !== target.businessObject.$attrs?.customGroupId) {
  686. if (isSourceTrigger && !isTargetTrigger && !(element.wnType === typeTask || element.wnType === typeProcessing)) {
  687. return false;
  688. }
  689. }
  690. }
  691. return true;
  692. }