service.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { inject, injectable } from 'inversify';
  6. import { DisposableCollection } from '@flowgram.ai/utils';
  7. import type { PositionSchema } from '@flowgram.ai/utils';
  8. import {
  9. WorkflowDocument,
  10. WorkflowDragService,
  11. WorkflowLinesManager,
  12. WorkflowNodeEntity,
  13. } from '@flowgram.ai/free-layout-core';
  14. import { WorkflowSelectService } from '@flowgram.ai/free-layout-core';
  15. import { WorkflowNodeJSON } from '@flowgram.ai/free-layout-core';
  16. import { HistoryService } from '@flowgram.ai/free-history-plugin';
  17. import { PlaygroundConfigEntity } from '@flowgram.ai/core';
  18. import { WorkflowNodePanelUtils } from './utils';
  19. import type {
  20. CallNodePanel,
  21. CallNodePanelParams,
  22. NodePanelCallParams,
  23. NodePanelResult,
  24. } from './type';
  25. /**
  26. * 添加节点面板服务
  27. */
  28. @injectable()
  29. export class WorkflowNodePanelService {
  30. @inject(WorkflowDocument) private readonly document: WorkflowDocument;
  31. @inject(WorkflowDragService)
  32. private readonly dragService: WorkflowDragService;
  33. @inject(WorkflowSelectService)
  34. private readonly selectService: WorkflowSelectService;
  35. @inject(WorkflowLinesManager)
  36. private readonly linesManager: WorkflowLinesManager;
  37. @inject(PlaygroundConfigEntity)
  38. private readonly playgroundConfig: PlaygroundConfigEntity;
  39. @inject(HistoryService) private readonly historyService: HistoryService;
  40. private readonly toDispose = new DisposableCollection();
  41. public callNodePanel: CallNodePanel = async () => undefined;
  42. /** 销毁 */
  43. public dispose(): void {
  44. this.toDispose.dispose();
  45. }
  46. public setCallNodePanel(callNodePanel: CallNodePanel) {
  47. this.callNodePanel = callNodePanel;
  48. }
  49. /** 唤起节点面板 */
  50. public async call(
  51. callParams: NodePanelCallParams
  52. ): Promise<WorkflowNodeEntity | WorkflowNodeEntity[] | undefined> {
  53. const {
  54. panelPosition,
  55. fromPort,
  56. enableMultiAdd = false,
  57. panelProps = {},
  58. containerNode,
  59. afterAddNode,
  60. } = callParams;
  61. if (!panelPosition || this.playgroundConfig.readonly) {
  62. return;
  63. }
  64. const nodes: WorkflowNodeEntity[] = [];
  65. return new Promise((resolve) => {
  66. this.callNodePanel({
  67. position: panelPosition,
  68. enableMultiAdd,
  69. panelProps,
  70. containerNode: WorkflowNodePanelUtils.getContainerNode({
  71. fromPort,
  72. containerNode,
  73. }),
  74. onSelect: async (panelParams?: NodePanelResult) => {
  75. const node = await this.addNode(callParams, panelParams);
  76. afterAddNode?.(node);
  77. if (!enableMultiAdd) {
  78. resolve(node);
  79. } else if (node) {
  80. nodes.push(node);
  81. }
  82. },
  83. onClose: () => {
  84. resolve(enableMultiAdd ? nodes : undefined);
  85. },
  86. });
  87. });
  88. }
  89. /**
  90. * 唤起单选面板
  91. */
  92. public async singleSelectNodePanel(
  93. params: Omit<CallNodePanelParams, 'onSelect' | 'onClose' | 'enableMultiAdd'>
  94. ): Promise<NodePanelResult | undefined> {
  95. return new Promise((resolve) => {
  96. this.callNodePanel({
  97. ...params,
  98. enableMultiAdd: false,
  99. onSelect: async (panelParams?: NodePanelResult) => {
  100. resolve(panelParams);
  101. },
  102. onClose: () => {
  103. resolve(undefined);
  104. },
  105. });
  106. });
  107. }
  108. /** 添加节点 */
  109. private async addNode(
  110. callParams: NodePanelCallParams,
  111. panelParams: NodePanelResult
  112. ): Promise<WorkflowNodeEntity | undefined> {
  113. const {
  114. panelPosition,
  115. fromPort,
  116. toPort,
  117. canAddNode,
  118. autoOffsetPadding = {
  119. x: 100,
  120. y: 100,
  121. },
  122. enableBuildLine = false,
  123. enableSelectPosition = false,
  124. enableAutoOffset = false,
  125. enableDragNode = false,
  126. } = callParams;
  127. if (!panelPosition || !panelParams) {
  128. return;
  129. }
  130. const { nodeType, selectEvent, nodeJSON } = panelParams;
  131. const containerNode = WorkflowNodePanelUtils.getContainerNode({
  132. fromPort,
  133. containerNode: callParams.containerNode,
  134. });
  135. // 判断是否可以添加节点
  136. if (canAddNode) {
  137. const canAdd = canAddNode({ nodeType, containerNode });
  138. if (!canAdd) {
  139. return;
  140. }
  141. }
  142. // 鼠标选择坐标
  143. const selectPosition = this.playgroundConfig.getPosFromMouseEvent(selectEvent);
  144. // 自定义坐标
  145. const nodePosition: PositionSchema = callParams.customPosition
  146. ? callParams.customPosition({ nodeType, selectPosition })
  147. : WorkflowNodePanelUtils.adjustNodePosition({
  148. nodeType,
  149. position: enableSelectPosition ? selectPosition : panelPosition,
  150. fromPort,
  151. toPort,
  152. containerNode,
  153. document: this.document,
  154. dragService: this.dragService,
  155. });
  156. // 创建节点
  157. const node: WorkflowNodeEntity = this.document.createWorkflowNodeByType(
  158. nodeType,
  159. nodePosition,
  160. nodeJSON ?? ({} as WorkflowNodeJSON),
  161. containerNode?.id
  162. );
  163. if (!node) {
  164. return;
  165. }
  166. // 后续节点偏移
  167. if (enableAutoOffset && fromPort && toPort) {
  168. WorkflowNodePanelUtils.subNodesAutoOffset({
  169. node,
  170. fromPort,
  171. toPort,
  172. padding: autoOffsetPadding,
  173. containerNode,
  174. historyService: this.historyService,
  175. dragService: this.dragService,
  176. linesManager: this.linesManager,
  177. });
  178. }
  179. if (!enableBuildLine && !enableDragNode) {
  180. return node;
  181. }
  182. // 等待节点渲染
  183. await WorkflowNodePanelUtils.waitNodeRender();
  184. // 重建连线(需先让端口完成渲染)
  185. if (enableBuildLine) {
  186. WorkflowNodePanelUtils.buildLine({
  187. fromPort,
  188. node,
  189. toPort,
  190. linesManager: this.linesManager,
  191. });
  192. }
  193. // 开始拖拽节点
  194. if (enableDragNode) {
  195. this.selectService.selectNode(node);
  196. this.dragService.startDragSelectedNodes(selectEvent);
  197. }
  198. return node;
  199. }
  200. }