@@ -5,9 +5,50 @@ import { MsgSuccess, MsgError, MsgConfirm } from '@/utils/message'
55import { WorkflowType } from '@/enums/application'
66import { t } from '@/locales'
77import { copyClick } from '@/utils/clipboard'
8+ import { randomId } from '@/utils/common'
89import { getMenuNodes , workflowModelDict } from './data'
10+ let activeCanvasId : string | null = null
11+ type Point = { x : number ; y : number }
12+ const lastMouse = {
13+ x : 0 ,
14+ y : 0 ,
15+ hasValue : false ,
16+ }
917let selected : any | null = null
18+ const bindMousePosition = ( lf : any ) => {
19+ const updateMouse = ( e : MouseEvent ) => {
20+ lastMouse . x = e . clientX
21+ lastMouse . y = e . clientY
22+ lastMouse . hasValue = true
23+ }
24+
25+ // 推荐直接监听容器,这样鼠标在节点上移动也能拿到
26+ lf . container . addEventListener ( 'mousemove' , updateMouse )
27+
28+ return ( ) => {
29+ lf . container . removeEventListener ( 'mousemove' , updateMouse )
30+ }
31+ }
32+ const bindCanvasActive = ( lf : any ) => {
33+ const container = lf . container as HTMLElement
34+ if ( ! container ) return
1035
36+ // 让容器可聚焦
37+ container . tabIndex = 0
38+
39+ const activate = ( ) => {
40+ activeCanvasId = lf . graphModel . flowId
41+ container . focus ( )
42+ }
43+
44+ container . addEventListener ( 'mousedown' , activate )
45+ container . addEventListener ( 'focus' , activate )
46+
47+ return ( ) => {
48+ container . removeEventListener ( 'mousedown' , activate )
49+ container . removeEventListener ( 'focus' , activate )
50+ }
51+ }
1152function translationNodeData ( nodeData : any , distance : any ) {
1253 nodeData . x += distance
1354 nodeData . y += distance
@@ -44,6 +85,8 @@ const TRANSLATION_DISTANCE = 40
4485let CHILDREN_TRANSLATION_DISTANCE = 40
4586
4687export function initDefaultShortcut ( lf : LogicFlow , graph : GraphModel ) {
88+ bindMousePosition ( lf )
89+ bindCanvasActive ( lf )
4790 const { keyboard } = lf
4891 const {
4992 options : { keyboard : keyboardOptions } ,
@@ -72,18 +115,124 @@ export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) {
72115 copyClick ( JSON . stringify ( selected ) )
73116 return false
74117 }
118+ // 3. 求节点包围盒
119+ const getBounds = ( nodes : any [ ] ) => {
120+ if ( ! nodes . length ) {
121+ return { minX : 0 , maxX : 0 , minY : 0 , maxY : 0 }
122+ }
123+
124+ let minX = nodes [ 0 ] . x
125+ let maxX = nodes [ 0 ] . x
126+ let minY = nodes [ 0 ] . y
127+ let maxY = nodes [ 0 ] . y
128+
129+ for ( const node of nodes ) {
130+ if ( node . x < minX ) minX = node . x
131+ if ( node . x > maxX ) maxX = node . x
132+ if ( node . y < minY ) minY = node . y
133+ if ( node . y > maxY ) maxY = node . y
134+ }
135+
136+ return { minX, maxX, minY, maxY }
137+ }
138+
139+ // 4. 整体平移
140+ const moveData = ( data : any , dx : number , dy : number ) => {
141+ for ( const node of data . nodes ?? [ ] ) {
142+ node . x += dx
143+ node . y += dy
144+ }
145+
146+ for ( const edge of data . edges ?? [ ] ) {
147+ if ( edge . startPoint ) {
148+ edge . startPoint . x += dx
149+ edge . startPoint . y += dy
150+ }
151+ if ( edge . endPoint ) {
152+ edge . endPoint . x += dx
153+ edge . endPoint . y += dy
154+ }
155+ if ( edge . text && typeof edge . text . x === 'number' && typeof edge . text . y === 'number' ) {
156+ edge . text . x += dx
157+ edge . text . y += dy
158+ }
159+ if ( Array . isArray ( edge . pointsList ) ) {
160+ edge . pointsList = edge . pointsList . map ( ( p : Point ) => ( {
161+ ...p ,
162+ x : p . x + dx ,
163+ y : p . y + dy ,
164+ } ) )
165+ }
166+ }
167+ }
168+ const resetData = ( data : any ) => {
169+ const idMap = new Map < string , string > ( )
170+
171+ const getOrCreateId = ( oldId : string ) => {
172+ let newId = idMap . get ( oldId )
173+ if ( ! newId ) {
174+ newId = randomId ( )
175+ idMap . set ( oldId , newId )
176+ }
177+ return newId
178+ }
179+
180+ for ( const node of data . nodes ) {
181+ node . id = getOrCreateId ( node . id )
182+ }
183+
184+ for ( const edge of data . edges ) {
185+ const oldEdgeId = edge . id
186+ const oldSourceNodeId = edge . sourceNodeId
187+ const oldTargetNodeId = edge . targetNodeId
188+
189+ edge . id = getOrCreateId ( oldEdgeId )
190+ edge . sourceNodeId = getOrCreateId ( oldSourceNodeId )
191+ edge . targetNodeId = getOrCreateId ( oldTargetNodeId )
192+
193+ if ( typeof edge . sourceAnchorId === 'string' ) {
194+ edge . sourceAnchorId = edge . sourceAnchorId . replace ( oldSourceNodeId , edge . sourceNodeId )
195+ }
196+
197+ if ( typeof edge . targetAnchorId === 'string' ) {
198+ edge . targetAnchorId = edge . targetAnchorId . replace ( oldTargetNodeId , edge . targetNodeId )
199+ }
200+ }
201+
202+ return data
203+ }
75204
76205 const paste_node = async ( e : ClipboardEvent ) => {
206+ if ( lf . graphModel . flowId !== activeCanvasId ) {
207+ return true
208+ }
77209 if ( ! keyboardOptions ?. enabled ) return true
78210 if ( graph . textEditElement ) return true
79211 const text = e . clipboardData ?. getData ( 'text/plain' ) || ''
80212 const data = parseAndValidate ( text )
81- selected = data
213+ selected = resetData ( data )
82214 const workflowMode = lf . graphModel . get_provide ( null , null ) . workflowMode
83215 const menus = getMenuNodes ( workflowMode )
84216 const nodes = menus ?. flatMap ( ( m : any ) => m . list ) . map ( ( n ) => n . type )
85217
86218 if ( selected && ( selected . nodes || selected . edges ) ) {
219+ if ( ! lastMouse . hasValue ) {
220+ moveData ( data , 40 , 40 )
221+ } else {
222+ // LogicFlow 文档里 getPointByClient 会把页面坐标转成画布坐标
223+ const point = lf . graphModel . getPointByClient ( {
224+ x : lastMouse . x ,
225+ y : lastMouse . y ,
226+ } )
227+ const mouseCanvasX = point . canvasOverlayPosition . x
228+ const mouseCanvasY = point . canvasOverlayPosition . y
229+
230+ const { minX, maxX, minY, maxY } = getBounds ( selected . nodes )
231+ const centerX = ( minX + maxX ) / 2
232+ const centerY = ( minY + maxY ) / 2
233+ moveData ( data , mouseCanvasX - centerX , mouseCanvasY - centerY )
234+ }
235+
87236 selected . nodes = selected . nodes . filter (
88237 ( n : any ) => nodes ?. includes ( n . type ) || workflowModelDict [ workflowMode ] ( n ) ,
89238 )
0 commit comments