CustomizeContextPad.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. import { changeTypeByTaskShape, changeTypeByTrigger, hasGatewayType, typeConfig } from '../../../config';
  2. import { jnpfConfigBpmnContextPad } from '../../../config/contextPad';
  3. import {
  4. bpmnEnd,
  5. bpmnTask,
  6. typeTask,
  7. typeSubFlow,
  8. typeLabel,
  9. bpmnInclusive,
  10. typeConfluence,
  11. bpmnStart,
  12. typeTrigger,
  13. bpmnTrigger,
  14. typeExecute,
  15. bpmnLabel,
  16. typeStart,
  17. typeCopy,
  18. bpmnCopy,
  19. bpmnPaste,
  20. typePaste,
  21. bpmnChoose,
  22. typeProcessing,
  23. typeOutside,
  24. typeEnd,
  25. typeChoose,
  26. typeCondition,
  27. } from '../../../config/variableName';
  28. import { BPMNTreeBuilder } from '../../../utils/constructTreeUtil';
  29. import { DEFAULT_DISTANCE } from '../../../config/constants';
  30. import { buildBitUUID } from '../../../utils/uuidUtil';
  31. import { NodeUtils } from '../../../utils/nodeUtil';
  32. import bpmnType from '../../../palette/bpmnType';
  33. const CustomizeContextPad = (contextPadProvider: any, element: any) => {
  34. let type = element.type;
  35. let isAction = true;
  36. if (typeConfig[type]) {
  37. const {
  38. _autoPlace: autoPlace,
  39. _create: create,
  40. _elementFactory: elementFactory,
  41. _modeling: modeling,
  42. _connect: connects,
  43. _injector: injector,
  44. _rules: rules,
  45. _eventBus: eventBus,
  46. } = contextPadProvider;
  47. let wnType = element.wnType;
  48. if (wnType === bpmnTrigger) type = bpmnTrigger;
  49. if (changeTypeByTaskShape[wnType]) type = changeTypeByTaskShape[wnType];
  50. const { contextPad, shapeType } = typeConfig[type];
  51. const { del, copy, paste } = jnpfConfigBpmnContextPad;
  52. let customization = contextPad?.customization;
  53. if (type === bpmnLabel) {
  54. let connect = element?.labelTarget;
  55. let target = connect.target;
  56. // 执行节点的线label使用groupCustomization
  57. if (changeTypeByTaskShape[target.wnType]) {
  58. customization = contextPad?.groupCustomization;
  59. }
  60. }
  61. if (type === shapeType) {
  62. if (contextPad) {
  63. let jnpfData = injector.get('jnpfData');
  64. let copyElement = jnpfData.getValue('copyType');
  65. if (element.wnType === typeTrigger && changeTypeByTaskShape[copyElement.type]) customization = contextPad?.otherCustomization;
  66. if (changeTypeByTaskShape[element.wnType] && changeTypeByTaskShape[copyElement.type]) customization = contextPad?.otherCustomization;
  67. if (contextPad.default) return defaultContextPad;
  68. else if (customization) {
  69. let result: any = {};
  70. for (let key of Object.keys(customization)) {
  71. let data = customization[key];
  72. if (data.group === 'model') {
  73. let options: any = {
  74. wnName: typeConfig[key]?.renderer.rendererName,
  75. };
  76. if (element.type === bpmnStart && hasGatewayType.has(key)) {
  77. // 开始节点只有分类节点 因为网关的分流节点和合流节点类型一致 多增加一个字段来表示
  78. options = {
  79. wnName: typeConfig[key]?.renderer.rendererName,
  80. wnGatewayType: key,
  81. wnType: key,
  82. icon: data.icon,
  83. };
  84. }
  85. result[data.name] = appendAction(data.type, data.elementName, data.className, data.title, data.wnType, options);
  86. } else if (data.group === 'connect') {
  87. result[data.name] = {
  88. group: data.group,
  89. className: data.className,
  90. title: data.title,
  91. action: {
  92. click: isAction && startConnect,
  93. },
  94. };
  95. } else if (data.group === 'edit') {
  96. result[data.name] = {
  97. group: data.group,
  98. className: data.className,
  99. title: data.title,
  100. action: {
  101. click: removeElement,
  102. },
  103. };
  104. }
  105. }
  106. return Object.assign(result);
  107. } else return defaultContextPad();
  108. }
  109. // 单个节点删除功能
  110. function removeElement() {
  111. rules.allowed('elements.delete', { elements: [element] });
  112. }
  113. // 开始连线(拖拽)
  114. function startConnect(event: any, element: any) {
  115. connects.start(event, element);
  116. }
  117. // 添加事件
  118. function appendAction(type: any, name: any, className: any, title: any, wnType: any, options?: any) {
  119. const appendStart = (event: any, element: any) => {
  120. let bpmnFactory = elementFactory._bpmnFactory;
  121. if ([typeSubFlow, typeProcessing, typeOutside].includes(type)) type = bpmnTask;
  122. let businessObject = bpmnFactory.create(type);
  123. let shape = elementFactory.createShape(Object.assign({ type, name, wnType, ...options }, businessObject));
  124. create.start(event, shape, { source: element });
  125. };
  126. const autoPlaceAppend = async (_event: any, element: any) => {
  127. let isPaste = false;
  128. if (wnType === typeCopy) {
  129. copyElement(element.wnType);
  130. return;
  131. }
  132. if (wnType === typePaste) {
  133. let jnpfData = injector.get('jnpfData');
  134. let data = jnpfData.getValue(bpmnCopy);
  135. let copyElement = jnpfData.getValue('copyType');
  136. isPaste = true;
  137. if ([typeOutside, typeTrigger, 'default'].includes(copyElement.type)) {
  138. type = data.type;
  139. wnType = data.wnType;
  140. className = data.className;
  141. title = data.title;
  142. options = data.options;
  143. if (element.outgoing.length > 1) {
  144. let connect = element.outgoing.find(connect => connect.target.wnType != typeTrigger);
  145. element = connect.label;
  146. } else {
  147. element = element.outgoing[0]?.label;
  148. }
  149. }
  150. if (copyElement.type === typeTrigger || changeTypeByTaskShape[copyElement.type]) {
  151. type = data.type;
  152. wnType = data.wnType;
  153. className = data.className;
  154. title = data.title;
  155. options = data.options;
  156. name = data.name;
  157. }
  158. }
  159. let hasTaskType = new Set([typeSubFlow, typeProcessing, typeTrigger, typeOutside]);
  160. if (hasTaskType.has(type) || changeTypeByTaskShape[wnType]) type = bpmnTask;
  161. let bpmnFactory = elementFactory._bpmnFactory;
  162. let isChoose = false;
  163. if (type === bpmnChoose) {
  164. type = bpmnInclusive;
  165. isChoose = true;
  166. }
  167. let businessObject = type === bpmnType ? bpmnFactory.create(element.type) : bpmnFactory.create(type);
  168. let shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
  169. if (element.type === typeLabel) {
  170. let businessTaskObject = bpmnFactory.create(bpmnTask);
  171. let businessTask2Object = bpmnFactory.create(bpmnTask);
  172. let businessInclusiveObject = bpmnFactory.create(bpmnInclusive);
  173. let shape: any;
  174. if (hasGatewayType.has(wnType)) {
  175. let id = `Gateway_${buildBitUUID()}_isSimple`;
  176. businessObject.id = id;
  177. shape = elementFactory.createShape(Object.assign({ type, id: id, name: name, businessObject, wnType: wnType }, options));
  178. } else {
  179. shape = elementFactory.createShape(Object.assign({ type, name: name, businessObject, wnType: wnType }, options));
  180. }
  181. updateShapePosition(shape, element, businessTaskObject, businessInclusiveObject, businessTask2Object);
  182. if (isChoose) {
  183. modeling.updateProperties(shape, {
  184. type: bpmnChoose,
  185. });
  186. }
  187. } else {
  188. autoPlace.append(element, shape);
  189. // 自研
  190. if (hasGatewayType.has(element.wnType)) {
  191. // 获取对应的合流网关
  192. const gateway = injector.get('elementRegistry').get(element.id + '_confluence');
  193. modeling.connect(shape, gateway);
  194. }
  195. // 处理执行节点的美化 (获取到最近的合流节点 判断分组与合流坐标距离计算是否需要进行偏移)
  196. if (changeTypeByTaskShape[shape.wnType]) {
  197. modeling.updateProperties(shape, {
  198. customGroupId: element.businessObject.$attrs.customGroupId,
  199. });
  200. let groupShape = injector.get('elementRegistry').get(element.businessObject.$attrs.customGroupId);
  201. let shapeRight = shape.x + shape.width;
  202. let shapeBottom = shape.y + shape.height;
  203. let groupRight = groupShape.x + groupShape.width;
  204. let groupBottom = groupShape.y + groupShape.height;
  205. var newBounds: any = {
  206. x: groupShape.x, // 保持 x 位置不变
  207. y: groupShape.y, // 保持 y 位置不变
  208. width: groupShape.width,
  209. height: groupShape.height,
  210. };
  211. if (shapeRight >= groupRight) newBounds.width = shapeRight + 25 - groupShape.x;
  212. if (shapeBottom >= groupBottom) newBounds.height = shapeBottom + 15 - groupShape.y;
  213. modeling.resizeShape(groupShape, newBounds);
  214. autoExecute(groupShape);
  215. }
  216. }
  217. // 计算需要横向偏移的元素(只有出现分流的情况下需要用到)
  218. if (element?.type === typeLabel || hasGatewayType.has(element.wnType) || element.wnType === typeTrigger || changeTypeByTaskShape[element.wnType])
  219. onAutoPosition();
  220. // 粘贴节点属性(需modeling.create后才能使用modeling.updatePropertie()生效)
  221. if (isPaste) {
  222. let jnpfData = injector.get('jnpfData');
  223. let copyElement = jnpfData.getValue('copyType');
  224. let newData = copyElement.data;
  225. let newShape = injector.get('elementRegistry').get(shape.id);
  226. let customGroupId = "";
  227. if (changeTypeByTrigger[newShape.wnType] || newShape.wnType === typeTrigger) customGroupId = newShape?.businessObject?.$attrs?.customGroupId
  228. else if (changeTypeByTaskShape[newShape.wnType]) customGroupId = element?.businessObject?.$attrs?.customGroupId
  229. jnpfData.setValue(shape.id, { ...newData, nodeId: shape.id, groupId: customGroupId });
  230. if (newShape) {
  231. newShape['elementBodyName'] = newData?.content || '';
  232. newShape['nodeName'] = newData?.nodeName || '';
  233. modeling.updateProperties(newShape, {
  234. customGroupId: customGroupId,
  235. });
  236. }
  237. }
  238. };
  239. var append = autoPlace ? autoPlaceAppend : appendStart;
  240. let disable = isAction;
  241. let lastsource = element?.labelTarget?.source;
  242. let nextTarget = element?.labelTarget?.target;
  243. let targetType = nextTarget?.wnType
  244. let sourceType = lastsource?.wnType
  245. if ([typeConfluence].includes(sourceType) && (hasGatewayType.has(wnType) || wnType === typeTrigger)) disable = false;
  246. if (sourceType === typeSubFlow && ([typeChoose].includes(wnType))) disable = false;
  247. if (sourceType === typeOutside && ([typeChoose, typeTrigger, typeOutside, typeSubFlow].includes(wnType))) disable = false;
  248. if (hasGatewayType.has(nextTarget?.wnType) && hasGatewayType.has(wnType)) disable = false;
  249. if (nextTarget?.wnType === typeTrigger && ([typeSubFlow, typeSubFlow, typeExecute, typeTask].includes(wnType) || hasGatewayType.has(wnType)))
  250. if (changeTypeByTaskShape[nextTarget?.wnType] && hasGatewayType.has(wnType)) disable = false;
  251. if ([typeStart, typeSubFlow].includes(sourceType) && wnType === typeTrigger) disable = false;
  252. if (([typeOutside].includes(wnType) && [typeOutside, typeSubFlow, typeChoose, typeEnd].includes(targetType))) disable = false;
  253. if (([typeSubFlow].includes(wnType) && [typeChoose].includes(targetType))) disable = false;
  254. if (([typeOutside].includes(wnType) && hasGatewayType.has(targetType))) {
  255. if (nextTarget.outgoing.some(o => [typeOutside, typeSubFlow].includes(o.target.wnType))) disable = false;
  256. let jnpfData = injector.get('jnpfData');
  257. if (nextTarget.outgoing.some(o => jnpfData.getValue(o.id)?.conditions?.length > 0)) disable = false; // 线条如果有条件也无法添加外部节点
  258. }
  259. if ([typeOutside].includes(wnType)) {
  260. // 如果外部节点的下一个节点是合流节点,则继续检查合流节点的下一个节点是否为结束节点,如果是则不可添加外部节点
  261. while (nextTarget && targetType === typeConfluence) {
  262. let nextOutgoing = nextTarget.outgoing || [];
  263. // 合流网关后面如果是子流程,外部节点,结束节点,则禁用当前的外部节点
  264. if (nextOutgoing.some(out => out.target && [typeEnd, typeSubFlow, typeOutside].includes(out.target.wnType))) {
  265. disable = false;
  266. break;
  267. }
  268. // 如果有多个分支,取第一个不是合流节点的分支继续判断,否则继续循环
  269. let nextConfluence = nextOutgoing.find(out => out.target && out.target.wnType === typeConfluence);
  270. if (nextConfluence) nextTarget = nextConfluence.target;
  271. else break;
  272. }
  273. while (lastsource && sourceType === typeConfluence) {
  274. let lastIncoming = lastsource.incoming || [];
  275. // 合流网关后面如果是子流程,外部节点,结束节点,则禁用当前的外部节点
  276. if (lastIncoming.some(incoming => incoming.source && [typeOutside].includes(incoming.source.wnType))) {
  277. disable = false;
  278. break;
  279. }
  280. // 如果有多个分支,取第一个不是合流节点的分支继续判断,否则继续循环
  281. let lastConfluence = lastIncoming.find(incoming => incoming.source?.wnType === typeConfluence);
  282. if (lastConfluence) lastsource = lastConfluence.source;
  283. else break;
  284. }
  285. }
  286. return {
  287. group: 'model',
  288. className: className,
  289. title: title,
  290. disable: disable,
  291. action: { click: disable && append },
  292. };
  293. }
  294. // 默认contextPad
  295. function defaultContextPad() {
  296. let obj = Object.assign({
  297. [del.name]: {
  298. group: del.group,
  299. className: del.className,
  300. title: del.title,
  301. action: {
  302. click: removeElement,
  303. },
  304. },
  305. [copy.name]: {
  306. group: copy.group,
  307. className: copy.className,
  308. title: copy.title,
  309. action: {
  310. click: () => copyElement(wnType === typeOutside ? typeOutside : "default"),
  311. },
  312. },
  313. });
  314. let jnpfData = injector.get('jnpfData');
  315. let data = jnpfData.getValue('copyType');
  316. let copyNode = jnpfData.getValue('copy');
  317. let outgoing = element.outgoing?.filter(o => o.target.wnType != typeTrigger)
  318. if (data.type === typeOutside) {
  319. if ([typeTask, typeProcessing, typeSubFlow, typeStart].includes(wnType)) {
  320. if (![typeOutside, typeSubFlow].includes(outgoing[0]?.target?.wnType)) {
  321. obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
  322. }
  323. }
  324. } else if (wnType === typeOutside) {
  325. if (([typeTask, typeProcessing, typeSubFlow, typeEnd].includes(outgoing[0]?.target?.wnType) && [typeTask, typeProcessing].includes(copyNode?.wnType)) || data.type === bpmnTrigger) {
  326. obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
  327. }
  328. } else {
  329. if (data.type === 'default' || data.type === bpmnTrigger) {
  330. if (!(data.type === bpmnTrigger && wnType === typeSubFlow)) {
  331. if (!(outgoing[0]?.target?.wnType === typeOutside && [typeSubFlow].includes(copyNode.wnType)))
  332. obj[paste.name] = appendAction(bpmnPaste, typePaste, paste.className, paste.title, typePaste, { wnName: null });
  333. }
  334. }
  335. }
  336. return obj;
  337. }
  338. // 判断元素与网关之间的垂直距离是否小于某个阈值
  339. function isWithinThreshold(target, source, threshold, processedElements: any) {
  340. // 这里假设网关在上方,即网关的 y 坐标小于当前元素的 y 坐标
  341. let gatewayY = target.y;
  342. let sourceElementY = source.y;
  343. // let elementObj: any;
  344. let map = new Map(
  345. Array.from(processedElements, (item: any) => [item.id, item]), // 使用 id 作为键
  346. );
  347. // 如果当前元素是合流网关 获取分流到合流内所有元素 并且获取y最大值的元素与 当前分流坐标做对比
  348. if (target.wnType === typeConfluence) {
  349. let allElements = injector.get('elementRegistry').getAll();
  350. let treeBuilder = new BPMNTreeBuilder(allElements);
  351. let gatewayElement = injector.get('elementRegistry').get(target.id.replace('_confluence', ''));
  352. treeBuilder.onComputerMaxElementH(injector, target, gatewayElement, [], null, false, map, threshold);
  353. }
  354. if (map.has(source.id)) {
  355. sourceElementY = sourceElementY + threshold;
  356. }
  357. return gatewayY - sourceElementY > threshold && gatewayY > sourceElementY;
  358. }
  359. function moveConnectedElements(connection: any, height: any, useWithinThreshold: boolean = true) {
  360. const stack: any = []; // 用于存储待处理的连接线
  361. const processedElements = new Set(); // 记录已经处理过的目标元素
  362. stack.push(connection); // 从给定的连接线开始
  363. while (stack.length > 0) {
  364. const currentConnection: any = stack.pop();
  365. const target = currentConnection.target;
  366. if (!target) continue; // 如果没有目标元素,跳过
  367. if (processedElements.has(target)) continue; // 如果目标元素已经处理过,跳过
  368. if (useWithinThreshold && isWithinThreshold(target, currentConnection.source, height, processedElements)) continue;
  369. processedElements.add(target); // 标记该元素已经被处理
  370. // 遍历目标元素的所有出线连接,并将它们压入栈
  371. const outgoingConnections: any = target.outgoing || [];
  372. for (const outgoingConnection of outgoingConnections) {
  373. stack.push(outgoingConnection); // 将所有关联的连接线压入栈中
  374. }
  375. }
  376. return Array.from(processedElements);
  377. }
  378. function autoExecute(groupShape: any) {
  379. // 判断groupBottom 和该触发节点对应的任务节点元素与合流节点间的距离
  380. // 1. 获取到触发节点对应的任务节点 后续获取到该元素在哪个合流元素内部 获取到合流网关 判断合流网关和当前的判断groupBottom坐标 如果小于则需要对合流后的所有元素进行统一偏移
  381. let triggerSourceShape: any;
  382. injector.get('elementRegistry').forEach(e => {
  383. if (e.businessObject.$attrs.customGroupId === groupShape.id && e.wnType === typeTrigger) triggerSourceShape = e.incoming[0].source;
  384. });
  385. // 迭代获取最近的合流节点 如果下一个节点时分流节点 则获取到对应的合流节点继续获取下一个节点
  386. function getConfluence(shape: any) {
  387. let targetList = NodeUtils.getNextElementList(shape, injector.get('elementRegistry').getAll());
  388. let confluence: any;
  389. targetList.map((element: any) => {
  390. if (element.wnType === typeConfluence) {
  391. confluence = element;
  392. return;
  393. }
  394. if (element.wnType != typeTrigger) confluence = getConfluence(element);
  395. if (hasGatewayType.has(element.wnType)) confluence = getConfluence(injector.get('elementRegistry').get(element.id + '_confluence'));
  396. });
  397. return confluence;
  398. }
  399. let confluenceShape = getConfluence(triggerSourceShape);
  400. if (confluenceShape) {
  401. if (groupShape && groupShape.y + groupShape.height + 50 >= confluenceShape.y) {
  402. modeling.moveElements(moveConnectedElements(confluenceShape.incoming[0], 0, false), {
  403. x: 0,
  404. y: groupShape.y + groupShape.height + DEFAULT_DISTANCE - 15 - confluenceShape.y,
  405. });
  406. }
  407. }
  408. }
  409. async function updateShapePosition(shape: any, element: any, businessTaskObject, businessInclusiveObject, businessTask2Object) {
  410. let connectElement: any = element?.labelTarget;
  411. let targetElement = connectElement?.target;
  412. let sourceElement = connectElement?.source;
  413. let promises: any = [];
  414. if (shape.wnType === typeTrigger) {
  415. // 生成一个触发节点
  416. let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
  417. let y = labelTargetY - (targetElement.y - sourceElement.y);
  418. if (targetElement.wnType === typeConfluence) {
  419. modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
  420. x: 0,
  421. y: y,
  422. });
  423. }
  424. autoPlace.append(sourceElement, shape);
  425. let groupShapes = modeling.createShape(
  426. {
  427. type: 'bpmn:Group',
  428. },
  429. { x: shape.x - 25, y: shape.y - 15, width: 250, height: 118 },
  430. shape.parent,
  431. );
  432. modeling.updateProperties(shape, {
  433. customGroupId: groupShapes.id,
  434. });
  435. } else if (hasGatewayType.has(shape.wnType)) {
  436. // 生成两个分支 包含分支 → 条件节点*2 → 任务节点*2 → 合流节点*1 最后融入到旧元素内
  437. let shape1 = elementFactory.createShape(
  438. Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTaskObject, wnType: typeTask }, { wnName: '审批节点' }),
  439. );
  440. businessInclusiveObject.id = shape.id + '_confluence';
  441. let gateway = elementFactory.createShape(
  442. Object.assign(
  443. {
  444. id: shape.id + '_confluence',
  445. type: bpmnInclusive,
  446. name: typeConfluence,
  447. businessObject: businessInclusiveObject,
  448. wnType: typeConfluence,
  449. },
  450. { wnName: '合流' },
  451. ),
  452. );
  453. let shape2 = elementFactory.createShape(
  454. Object.assign({ type: bpmnTask, name: typeTask, businessObject: businessTask2Object, wnType: typeTask }, { wnName: '审批节点' }),
  455. );
  456. let labelTargetY =
  457. DEFAULT_DISTANCE * 4 +
  458. typeConfig[bpmnTask].renderer.attr.height +
  459. typeConfig[sourceElement.type].renderer.attr.height +
  460. typeConfig[bpmnInclusive].renderer.attr.height;
  461. promises.push(
  462. modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
  463. x: 0,
  464. y: labelTargetY - (targetElement.y - sourceElement.y),
  465. }),
  466. );
  467. promises.push(modeling.removeConnection(connectElement));
  468. promises.push(autoPlace.append(sourceElement, shape));
  469. promises.push(autoPlace.append(shape, shape1));
  470. promises.push(autoPlace.append(shape, shape2));
  471. promises.push(autoPlace.append(shape1, gateway));
  472. promises.push(modeling.connect(shape2, gateway));
  473. promises.push(modeling.connect(gateway, targetElement));
  474. } else if (targetElement) {
  475. let labelTargetY = (DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height) * 2;
  476. let y = labelTargetY - (targetElement.y - sourceElement.y);
  477. if (targetElement.type === bpmnEnd) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
  478. if (sourceElement.type === bpmnStart) y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
  479. if (sourceElement.wnType === typeConfluence) {
  480. y = DEFAULT_DISTANCE + typeConfig[bpmnTask].renderer.attr.height;
  481. if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height > 0) y = 0;
  482. if (targetElement.y - sourceElement.y - DEFAULT_DISTANCE * 2 - typeConfig[bpmnTask].renderer.attr.height < 0)
  483. y = (sourceElement.y + DEFAULT_DISTANCE * 2 + typeConfig[bpmnTask].renderer.attr.height) - targetElement.y;
  484. }
  485. if (sourceElement?.wnType === typeConfluence && targetElement?.wnType === typeConfluence) {
  486. let maxY = -Infinity;
  487. let id;
  488. targetElement = injector.get('elementRegistry').get(targetElement.id);
  489. targetElement.incoming?.length > 0 &&
  490. targetElement.incoming.map(item => {
  491. let currentElement: any = injector.get('elementRegistry').get(item.id);
  492. if (currentElement.source?.y > maxY) {
  493. id = currentElement.source.id;
  494. maxY = currentElement.source.y;
  495. }
  496. });
  497. if (targetElement.y - maxY <= DEFAULT_DISTANCE && sourceElement.id != id) {
  498. y = DEFAULT_DISTANCE - (targetElement.y - maxY);
  499. }
  500. }
  501. if (changeTypeByTaskShape[shape.wnType]) {
  502. let source = contextPadProvider._injector.get('elementRegistry').get(connectElement.id).source;
  503. // 需要增加customGroupId
  504. modeling.updateProperties(shape, {
  505. customGroupId: source.businessObject.$attrs.customGroupId,
  506. });
  507. }
  508. promises.push(
  509. modeling.moveElements(moveConnectedElements(element.labelTarget, labelTargetY), {
  510. x: 0,
  511. y: y,
  512. }),
  513. );
  514. promises.push(modeling.removeConnection(connectElement));
  515. autoPlace.append(sourceElement, shape);
  516. promises.push(modeling.connect(shape, targetElement));
  517. }
  518. Promise.all(promises);
  519. let elementRegistry = injector.get('elementRegistry');
  520. let treeBuilder = new BPMNTreeBuilder(elementRegistry.getAll()); // 实例化工具类
  521. treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
  522. if (changeTypeByTaskShape[shape.wnType]) {
  523. let groupShape = injector.get('elementRegistry').get(targetElement.businessObject.$attrs.customGroupId);
  524. autoExecute(groupShape);
  525. }
  526. }
  527. async function onAutoPosition() {
  528. let elementRegistry: any = await contextPadProvider._injector.get('elementRegistry');
  529. let allElements = elementRegistry.getAll();
  530. let treeBuilder = new BPMNTreeBuilder(allElements); // 实例化工具类
  531. let bpmnTree = treeBuilder.constructTree(1); // 构建树状数据结构
  532. let visited: any = new Map(); // 用于防止重复访问
  533. let shapeList: any = []; // 修改触发节点旁的连接线坐标
  534. let confluenceMap: any = new Map();
  535. treeBuilder.calculateVirtualWidth(bpmnTree, elementRegistry); // 计算虚拟宽度
  536. treeBuilder.traverseTreeBFS(bpmnTree, node => {
  537. node?.offset && node.x != node.offset.x && visited.set(node.id, node);
  538. if (node?.children?.length > 0) {
  539. let hasTrigger = node.children.some(o => o.wnType === typeTrigger);
  540. let hasConfluence = node.children.some(o => o.wnType === typeConfluence);
  541. let shape = elementRegistry.get(node.id);
  542. let confluence: any;
  543. if (shape.outgoing?.length) {
  544. shape.outgoing.map((connect: any) => {
  545. if (connect.target.wnType === typeTrigger) hasTrigger = true;
  546. if (connect.target.wnType === typeConfluence) {
  547. confluence = connect.target;
  548. hasConfluence = true;
  549. }
  550. });
  551. }
  552. if (hasTrigger && hasConfluence)
  553. shapeList.push({ shape: elementRegistry.get(node.id), treeShape: node, treeConfluence: node.children[0], confluence: confluence });
  554. if (node.wnType === typeConfluence) confluenceMap.set(node.id, node);
  555. }
  556. });
  557. treeBuilder.formatCanvas(Array.from(visited.values()), modeling, elementRegistry);
  558. // 如果某个节点的出线包含合流及触发节点元素 则将连接合流节点的线进行进行重新绘制(根据当前节点的虚拟宽度计算出偏移的xy轴坐标中点)
  559. // 虚拟宽度的最左侧坐标为 当前元素的x + width/2 - 虚拟宽度/2
  560. shapeList.map(({ shape, treeShape, treeConfluence, confluence }) => {
  561. let confluenceElement = confluenceMap.get(treeConfluence.id);
  562. let x = shape.x + shape.width / 2 - treeShape.virtualWidth / 2 + shape.width / 2;
  563. let newWaypoints: any = [];
  564. if (!confluenceElement) {
  565. confluenceElement = confluenceMap.get(confluence.id);
  566. let childrenShape = treeShape.children[treeShape.children.length - 1];
  567. if (childrenShape.wnType === typeConfluence) {
  568. childrenShape = treeShape.children[treeShape.children.length - 2];
  569. }
  570. x = (childrenShape.offset?.x ? childrenShape.offset.x : childrenShape.x) + childrenShape.virtualWidth / 2 + childrenShape.width / 2 + 120;
  571. newWaypoints = [
  572. { x: shape.x + shape.width, y: shape.y + shape.height / 2 },
  573. { x: x, y: shape.y + shape.height / 2 },
  574. { x: x, y: confluenceElement.y },
  575. { x: confluenceElement.x, y: confluenceElement.y },
  576. ];
  577. } else {
  578. newWaypoints = [
  579. { x: shape.x, y: shape.y + shape.height / 2 },
  580. { x: x, y: shape.y + shape.height / 2 },
  581. { x: x, y: confluenceElement.y },
  582. { x: confluenceElement.x, y: confluenceElement.y },
  583. ];
  584. }
  585. let connect = shape.outgoing[0];
  586. if (shape.outgoing?.length) {
  587. connect = shape.outgoing.find(connect => connect.target.wnType != typeTrigger);
  588. }
  589. modeling.updateWaypoints(connect, newWaypoints);
  590. });
  591. treeBuilder.resizeGroupShape(elementRegistry.getAll(), injector);
  592. }
  593. // 复制节点
  594. function copyElement(type) {
  595. let jnpfData = injector.get('jnpfData');
  596. let selection = injector.get('selection');
  597. let copyData = jnpfData.getValue(element.id);
  598. jnpfData.setValue(bpmnCopy, element);
  599. jnpfData.setValue('copyType', { type: type, data: copyData });
  600. eventBus.fire('custom.message', {
  601. context: '复制成功',
  602. messageType: 'success',
  603. });
  604. selection.select(null);
  605. }
  606. }
  607. return undefined;
  608. }
  609. return undefined;
  610. };
  611. export default CustomizeContextPad;