@mx-sose-front/mx-sose-graph 1.2.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +94440 -1420
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/src/components/InteractionLayer.vue.d.ts.map +1 -1
- package/dist/src/components/Matrix/Matrix.vue.d.ts.map +1 -1
- package/dist/src/hooks/useConnectionPreview.d.ts +500 -0
- package/dist/src/hooks/useConnectionPreview.d.ts.map +1 -0
- package/dist/src/store/graphStore.d.ts +5 -1
- package/dist/src/store/graphStore.d.ts.map +1 -1
- package/dist/src/utils/autoLayout.d.ts +44 -0
- package/dist/src/utils/autoLayout.d.ts.map +1 -0
- package/dist/src/utils/edgeUtils.d.ts +2 -2
- package/dist/src/utils/edgeUtils.d.ts.map +1 -1
- package/dist/src/utils/pinUtils.d.ts +2 -0
- package/dist/src/utils/pinUtils.d.ts.map +1 -1
- package/dist/src/utils/shapeInitialBounds.d.ts +10 -0
- package/dist/src/utils/shapeInitialBounds.d.ts.map +1 -0
- package/dist/src/view/graph.vue.d.ts +4 -4
- package/dist/src/view/graph.vue.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/components/InteractionLayer.vue +34 -406
- package/src/components/Matrix/Matrix.vue +71 -5
- package/src/hooks/useConnectionPreview.ts +405 -0
- package/src/store/graphStore.ts +24 -4
- package/src/utils/autoLayout.ts +388 -0
- package/src/utils/edgeUtils.ts +77 -63
- package/src/utils/pinUtils.ts +5 -0
- package/src/utils/shapeInitialBounds.ts +42 -0
- package/src/view/graph.vue +4 -2
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { ref, computed, watch, type Ref, type ComputedRef } from "vue";
|
|
2
|
+
import type { Shape } from "../types";
|
|
3
|
+
import type { InteractionLayerProps } from "../types/interactionLayer";
|
|
4
|
+
import type { IHighlightUtils } from "./useHighlight";
|
|
5
|
+
import { EdgeUtils } from "../utils/edgeUtils";
|
|
6
|
+
import { getLayerStyle } from "../utils/diagram";
|
|
7
|
+
import { useGraphStore } from "../store/graphStore";
|
|
8
|
+
import { toLocalPoint, getBounds, inBounds } from "../utils/geom";
|
|
9
|
+
import { pickTarget } from "../utils/hittest";
|
|
10
|
+
import { eventBus } from "../store";
|
|
11
|
+
import { createRAFThrottle } from "../utils/rafThrottle";
|
|
12
|
+
|
|
13
|
+
export interface UseConnectionPreviewOptions {
|
|
14
|
+
props: InteractionLayerProps;
|
|
15
|
+
graphStore: ReturnType<typeof useGraphStore>;
|
|
16
|
+
highlightUtils: IHighlightUtils;
|
|
17
|
+
connectMode: Ref<string>;
|
|
18
|
+
layerRef: Ref<HTMLDivElement | null>;
|
|
19
|
+
highlightedShape: ComputedRef<Shape | null>;
|
|
20
|
+
pendingActionButton: Ref<boolean>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 连线预览层:连接点状态、预览线样式、初始化/取消连接、悬停校验与 document 鼠标移动。
|
|
25
|
+
*/
|
|
26
|
+
export function useConnectionPreview(options: UseConnectionPreviewOptions) {
|
|
27
|
+
const {
|
|
28
|
+
props,
|
|
29
|
+
graphStore,
|
|
30
|
+
highlightUtils,
|
|
31
|
+
connectMode,
|
|
32
|
+
layerRef,
|
|
33
|
+
highlightedShape,
|
|
34
|
+
pendingActionButton,
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
const { highlightShape, clearHighlightTimeout } = highlightUtils;
|
|
38
|
+
|
|
39
|
+
const mousePosition = ref({ x: 0, y: 0 });
|
|
40
|
+
const showLine = ref(false);
|
|
41
|
+
const currentConnectPoint = ref({ x: 0, y: 0 });
|
|
42
|
+
const isConnecting = ref(false);
|
|
43
|
+
const targetConnectPoint = ref({ x: 0, y: 0 });
|
|
44
|
+
const targetShape = ref<Shape | null>(null);
|
|
45
|
+
const recordClickPoint = ref({ x: 0, y: 0 });
|
|
46
|
+
const isConnectAllowed = ref(false);
|
|
47
|
+
|
|
48
|
+
const getCurrentTargetModels = () => {
|
|
49
|
+
if (connectMode.value === "action") {
|
|
50
|
+
return (
|
|
51
|
+
props.connectShapeData?.scenarioMenus?.find(
|
|
52
|
+
(menu) => menu.code === props.connectShapeData?.shapeKey
|
|
53
|
+
)?.targetModels ?? props.connectShapeData?.targetModels
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return props.connectShapeData?.targetModels;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const resolveConnectTargetAtPoint = (pt: { x: number; y: number }) => {
|
|
61
|
+
const hit = pickTarget(graphStore.shapes, pt);
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
["shape", "pin"].includes(hit.kind) &&
|
|
65
|
+
hit.shape?.id &&
|
|
66
|
+
hit.shape.id !== props.connectShapeData?.sourceId
|
|
67
|
+
) {
|
|
68
|
+
return hit.shape;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const hoveredShape = highlightedShape.value;
|
|
72
|
+
if (
|
|
73
|
+
!hoveredShape?.id ||
|
|
74
|
+
hoveredShape.id === props.connectShapeData?.sourceId
|
|
75
|
+
) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const bounds = getBounds(hoveredShape);
|
|
80
|
+
const minSide = Math.min(bounds.width, bounds.height);
|
|
81
|
+
const padding = minSide <= 30 ? 8 : minSide <= 40 ? 6 : 0;
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
inBounds(pt, {
|
|
85
|
+
x: bounds.x - padding,
|
|
86
|
+
y: bounds.y - padding,
|
|
87
|
+
width: bounds.width + padding * 2,
|
|
88
|
+
height: bounds.height + padding * 2,
|
|
89
|
+
})
|
|
90
|
+
) {
|
|
91
|
+
return hoveredShape;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const initializeConnectPoint = () => {
|
|
98
|
+
if (props.connectShapeData?.sourceId && props.diagramBounds) {
|
|
99
|
+
const sourceShape = props.connectShapeData?.sourceId
|
|
100
|
+
? graphStore.shapeMap.get(props.connectShapeData.sourceId)
|
|
101
|
+
: undefined;
|
|
102
|
+
|
|
103
|
+
const initialPoint = EdgeUtils.initializeConnectPoint(
|
|
104
|
+
sourceShape,
|
|
105
|
+
recordClickPoint.value,
|
|
106
|
+
props.diagramBounds
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (initialPoint && sourceShape) {
|
|
110
|
+
currentConnectPoint.value = initialPoint;
|
|
111
|
+
|
|
112
|
+
if (!isConnecting.value) {
|
|
113
|
+
graphStore.clearSelection();
|
|
114
|
+
isConnecting.value = true;
|
|
115
|
+
showLine.value = true;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
isConnecting.value = false;
|
|
119
|
+
showLine.value = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const dotStyle = computed(() => {
|
|
125
|
+
if (!props.connectShapeData || !props.diagramBounds) {
|
|
126
|
+
return { display: "none" };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const { x, y } = currentConnectPoint.value;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
position: "absolute" as const,
|
|
133
|
+
left: `${x}px`,
|
|
134
|
+
top: `${y}px`,
|
|
135
|
+
width: "8px",
|
|
136
|
+
height: "8px",
|
|
137
|
+
pointerEvents: "none" as const,
|
|
138
|
+
zIndex: 1001,
|
|
139
|
+
transform: "translate(-50%, -50%)",
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const svgStyle = computed(() => {
|
|
144
|
+
if (
|
|
145
|
+
!props.connectShapeData ||
|
|
146
|
+
!props.connectShapeData.bounds ||
|
|
147
|
+
!props.diagramBounds
|
|
148
|
+
) {
|
|
149
|
+
return { display: "none" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
position: "absolute" as const,
|
|
154
|
+
left: "0px",
|
|
155
|
+
top: "0px",
|
|
156
|
+
width: "100%",
|
|
157
|
+
height: "100%",
|
|
158
|
+
pointerEvents: "none" as const,
|
|
159
|
+
zIndex: 1000,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const linePoints = computed(() => {
|
|
164
|
+
if (
|
|
165
|
+
!isConnecting.value ||
|
|
166
|
+
!showLine.value ||
|
|
167
|
+
!props.connectShapeData ||
|
|
168
|
+
!props.diagramBounds ||
|
|
169
|
+
currentConnectPoint.value.x === 0 ||
|
|
170
|
+
currentConnectPoint.value.y === 0
|
|
171
|
+
) {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const { x: startX, y: startY } = currentConnectPoint.value;
|
|
176
|
+
|
|
177
|
+
let endX: number;
|
|
178
|
+
let endY: number;
|
|
179
|
+
if (isConnecting.value) {
|
|
180
|
+
endX = mousePosition.value.x;
|
|
181
|
+
endY = mousePosition.value.y;
|
|
182
|
+
} else if (targetConnectPoint.value) {
|
|
183
|
+
endX = targetConnectPoint.value.x;
|
|
184
|
+
endY = targetConnectPoint.value.y;
|
|
185
|
+
} else {
|
|
186
|
+
return "";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return EdgeUtils.calculateLinePoints(
|
|
190
|
+
{ x: startX, y: startY },
|
|
191
|
+
{ x: endX, y: endY }
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const layerStyle = computed(() => getLayerStyle(props.diagramBounds));
|
|
196
|
+
|
|
197
|
+
const cancelConnection = () => {
|
|
198
|
+
EdgeUtils.cancelConnection(
|
|
199
|
+
{
|
|
200
|
+
isConnecting,
|
|
201
|
+
currentConnectPoint,
|
|
202
|
+
mousePosition,
|
|
203
|
+
targetConnectPoint,
|
|
204
|
+
targetShape,
|
|
205
|
+
showLine,
|
|
206
|
+
},
|
|
207
|
+
highlightUtils
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const connectMouseMoveThrottle = createRAFThrottle();
|
|
212
|
+
|
|
213
|
+
let lastHoverShapeId: string | null = null;
|
|
214
|
+
let edgeCheckDebounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
215
|
+
const EDGE_CHECK_DEBOUNCE_DELAY = 150;
|
|
216
|
+
|
|
217
|
+
const checkHoverTarget = (x: number, y: number) => {
|
|
218
|
+
if (!props.diagramBounds) return;
|
|
219
|
+
|
|
220
|
+
const hoverShape = EdgeUtils.checkHoverTarget(
|
|
221
|
+
x,
|
|
222
|
+
y,
|
|
223
|
+
graphStore.shapes,
|
|
224
|
+
props.diagramBounds,
|
|
225
|
+
props.connectShapeData?.sourceId
|
|
226
|
+
);
|
|
227
|
+
clearHighlightTimeout();
|
|
228
|
+
|
|
229
|
+
if (hoverShape) {
|
|
230
|
+
const targetModels = getCurrentTargetModels();
|
|
231
|
+
let isAllowed = true;
|
|
232
|
+
if (
|
|
233
|
+
targetModels &&
|
|
234
|
+
Array.isArray(targetModels) &&
|
|
235
|
+
targetModels.length > 0
|
|
236
|
+
) {
|
|
237
|
+
const hoverShapeType = hoverShape.shapeKey || "";
|
|
238
|
+
isAllowed = targetModels.includes(hoverShapeType);
|
|
239
|
+
}
|
|
240
|
+
if (props.connectShapeData?.sourceId) {
|
|
241
|
+
const sourceShape = graphStore.shapeMap.get(
|
|
242
|
+
props.connectShapeData.sourceId
|
|
243
|
+
);
|
|
244
|
+
if (
|
|
245
|
+
sourceShape &&
|
|
246
|
+
sourceShape.parenShapeId !== hoverShape.parenShapeId &&
|
|
247
|
+
hoverShape.shapeType !== "pin" &&
|
|
248
|
+
sourceShape.shapeType !== "pin"
|
|
249
|
+
) {
|
|
250
|
+
isAllowed = false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (lastHoverShapeId !== hoverShape.id) {
|
|
255
|
+
if (edgeCheckDebounceTimer) {
|
|
256
|
+
clearTimeout(edgeCheckDebounceTimer);
|
|
257
|
+
edgeCheckDebounceTimer = null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
highlightShape(null, false);
|
|
261
|
+
isConnectAllowed.value = false;
|
|
262
|
+
|
|
263
|
+
lastHoverShapeId = hoverShape.id;
|
|
264
|
+
|
|
265
|
+
if (props.connectShapeData) {
|
|
266
|
+
edgeCheckDebounceTimer = setTimeout(() => {
|
|
267
|
+
let sourceModelId;
|
|
268
|
+
if (
|
|
269
|
+
props.connectShapeData?.modelId &&
|
|
270
|
+
props.connectShapeData.modelId.toString().trim() !== ""
|
|
271
|
+
) {
|
|
272
|
+
sourceModelId = props.connectShapeData.modelId;
|
|
273
|
+
} else if (
|
|
274
|
+
props.connectShapeData?.sourceModelId &&
|
|
275
|
+
props.connectShapeData.sourceModelId.toString().trim() !== ""
|
|
276
|
+
) {
|
|
277
|
+
sourceModelId = props.connectShapeData.sourceModelId;
|
|
278
|
+
}
|
|
279
|
+
if (sourceModelId) {
|
|
280
|
+
eventBus.emit("edge-check", {
|
|
281
|
+
sourceModelId: sourceModelId,
|
|
282
|
+
targetModelId: hoverShape.modelId,
|
|
283
|
+
isAllowed: isAllowed,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
highlightShape(hoverShape, true, isAllowed);
|
|
288
|
+
isConnectAllowed.value = isAllowed;
|
|
289
|
+
}, EDGE_CHECK_DEBOUNCE_DELAY);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
if (edgeCheckDebounceTimer) {
|
|
294
|
+
clearTimeout(edgeCheckDebounceTimer);
|
|
295
|
+
edgeCheckDebounceTimer = null;
|
|
296
|
+
}
|
|
297
|
+
lastHoverShapeId = null;
|
|
298
|
+
clearHighlightTimeout();
|
|
299
|
+
highlightShape(null, false);
|
|
300
|
+
isConnectAllowed.value = false;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const updateConnectPointToNearest = (mouseX: number, mouseY: number) => {
|
|
305
|
+
if (!props.connectShapeData?.sourceId || !props.diagramBounds) return;
|
|
306
|
+
|
|
307
|
+
const sourceShape = props.connectShapeData?.sourceId
|
|
308
|
+
? graphStore.shapeMap.get(props.connectShapeData.sourceId)
|
|
309
|
+
: undefined;
|
|
310
|
+
|
|
311
|
+
const nearestPoint = EdgeUtils.findNearestConnectPoint(
|
|
312
|
+
{ x: mouseX, y: mouseY },
|
|
313
|
+
sourceShape
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
if (
|
|
317
|
+
nearestPoint &&
|
|
318
|
+
nearestPoint.x !== undefined &&
|
|
319
|
+
nearestPoint.y !== undefined
|
|
320
|
+
) {
|
|
321
|
+
currentConnectPoint.value = {
|
|
322
|
+
x: nearestPoint.x,
|
|
323
|
+
y: nearestPoint.y,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const handleMouseMove = (event: MouseEvent) => {
|
|
329
|
+
connectMouseMoveThrottle.throttle((evt: MouseEvent) => {
|
|
330
|
+
const { clientX, clientY } = evt;
|
|
331
|
+
const localPoint = toLocalPoint(evt, layerRef.value);
|
|
332
|
+
if (EdgeUtils.isWithinCanvas(clientX, clientY)) {
|
|
333
|
+
mousePosition.value = { x: localPoint.x, y: localPoint.y };
|
|
334
|
+
|
|
335
|
+
if (isConnecting.value && !pendingActionButton.value) {
|
|
336
|
+
showLine.value = true;
|
|
337
|
+
updateConnectPointToNearest(localPoint.x, localPoint.y);
|
|
338
|
+
checkHoverTarget(localPoint.x, localPoint.y);
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
if (isConnecting.value) {
|
|
342
|
+
showLine.value = false;
|
|
343
|
+
}
|
|
344
|
+
highlightShape(null, false);
|
|
345
|
+
}
|
|
346
|
+
})(event);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const handleMouseLeave = () => {
|
|
350
|
+
if (isConnecting.value) {
|
|
351
|
+
showLine.value = false;
|
|
352
|
+
}
|
|
353
|
+
highlightShape(null, false);
|
|
354
|
+
clearHighlightTimeout();
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
watch(
|
|
358
|
+
() => isConnecting.value,
|
|
359
|
+
(newVal) => {
|
|
360
|
+
if (!newVal) {
|
|
361
|
+
if (edgeCheckDebounceTimer) {
|
|
362
|
+
clearTimeout(edgeCheckDebounceTimer);
|
|
363
|
+
edgeCheckDebounceTimer = null;
|
|
364
|
+
}
|
|
365
|
+
lastHoverShapeId = null;
|
|
366
|
+
isConnectAllowed.value = false;
|
|
367
|
+
highlightShape(null, false);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
watch(
|
|
373
|
+
() => props.edgeCheck,
|
|
374
|
+
(newEdgeCheck) => {
|
|
375
|
+
if (
|
|
376
|
+
isConnecting.value &&
|
|
377
|
+
highlightedShape.value &&
|
|
378
|
+
newEdgeCheck !== undefined
|
|
379
|
+
) {
|
|
380
|
+
highlightShape(highlightedShape.value, true, newEdgeCheck);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
mousePosition,
|
|
387
|
+
showLine,
|
|
388
|
+
currentConnectPoint,
|
|
389
|
+
isConnecting,
|
|
390
|
+
targetConnectPoint,
|
|
391
|
+
targetShape,
|
|
392
|
+
recordClickPoint,
|
|
393
|
+
isConnectAllowed,
|
|
394
|
+
dotStyle,
|
|
395
|
+
svgStyle,
|
|
396
|
+
linePoints,
|
|
397
|
+
layerStyle,
|
|
398
|
+
cancelConnection,
|
|
399
|
+
initializeConnectPoint,
|
|
400
|
+
getCurrentTargetModels,
|
|
401
|
+
resolveConnectTargetAtPoint,
|
|
402
|
+
handleMouseMove,
|
|
403
|
+
handleMouseLeave,
|
|
404
|
+
};
|
|
405
|
+
}
|
package/src/store/graphStore.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { batchAutoExpandParents } from '../utils/batchAutoExpand'
|
|
|
27
27
|
import { adjustCanvasToFitAllShapes } from '../utils/diagram'
|
|
28
28
|
import { applyReparentAndClone, autoExpandMovedCompartmentsAfterDrag, buildDragEndPayloads, buildPrevParentMap, collectAffectedShapeIds, createOnNestDoneCallback } from '../utils/graphDragService'
|
|
29
29
|
import { createShapeOperator, type ShapeOp, type ShapeId } from "../utils/shapeOps/shapeOps"
|
|
30
|
+
import { autoLayout, type AutoLayoutOptions, type LayoutResult } from '../utils/autoLayout'
|
|
30
31
|
type Rect = { x: number; y: number; width: number; height: number };
|
|
31
32
|
|
|
32
33
|
const GRAPH_SELECTION_STORAGE_KEY = 'mx-sose-graph:selected-state'
|
|
@@ -798,12 +799,10 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
798
799
|
}
|
|
799
800
|
}
|
|
800
801
|
}
|
|
801
|
-
console.log('1. endDragShape start')
|
|
802
802
|
// 更新相关 edge 的 waypoints
|
|
803
803
|
await EdgeUtils.updateRelatedEdgesAsync(shapes.value, changedIds, (shape) => {
|
|
804
804
|
updateShape(shape.id, shape)
|
|
805
805
|
}, edgesByNodeId.value, shapeMap.value)
|
|
806
|
-
console.log('2. updateRelatedEdgesAsync done')
|
|
807
806
|
//对“本次直接拖动的隔间”做一次自动扩容检查
|
|
808
807
|
autoExpandMovedCompartmentsAfterDrag(shapes.value, changedIds, updateShape)
|
|
809
808
|
// 把 clone 也并入“需要同步 comparents 的变更集合”
|
|
@@ -861,10 +860,8 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
861
860
|
}
|
|
862
861
|
//对外只暴露一个事件:shape-drag-end
|
|
863
862
|
eventBus.emit('shape-drag-end', payloads, ownerPayload, onNestDone)
|
|
864
|
-
console.log('3. emit done')
|
|
865
863
|
// 清理拖拽状态
|
|
866
864
|
cleanupAfterDrag()
|
|
867
|
-
console.log('4. cleanupAfterDrag done')
|
|
868
865
|
} catch (e) {
|
|
869
866
|
console.error('endDragShape 执行失败:', e)
|
|
870
867
|
} finally {
|
|
@@ -1056,6 +1053,28 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
1056
1053
|
// eventBus.emit('shape-size-update', payload)
|
|
1057
1054
|
}
|
|
1058
1055
|
|
|
1056
|
+
/**
|
|
1057
|
+
* 自动布局画布上的所有图元
|
|
1058
|
+
* 基于 ELK 分层算法,支持横向/纵向布局
|
|
1059
|
+
*/
|
|
1060
|
+
const applyAutoLayout = async (options?: AutoLayoutOptions): Promise<LayoutResult> => {
|
|
1061
|
+
const result = await autoLayout(shapes.value, options)
|
|
1062
|
+
|
|
1063
|
+
for (const [id, bounds] of result.nodeUpdates) {
|
|
1064
|
+
updateShape(id, { bounds })
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
await EdgeUtils.updateRelatedEdgesAsync(
|
|
1068
|
+
shapes.value,
|
|
1069
|
+
result.affectedNodeIds,
|
|
1070
|
+
(shape) => updateShape(shape.id, shape),
|
|
1071
|
+
edgesByNodeId.value,
|
|
1072
|
+
shapeMap.value
|
|
1073
|
+
)
|
|
1074
|
+
|
|
1075
|
+
return result
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1059
1078
|
// 设置鼠标是否在图元区域
|
|
1060
1079
|
const setIsMouseInGraphView = (isIn: boolean) => {
|
|
1061
1080
|
museInGraphView.value = isIn
|
|
@@ -1159,5 +1178,6 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
1159
1178
|
clearCutShapeIds,
|
|
1160
1179
|
setCopiedShapesCount,
|
|
1161
1180
|
setMatrixData,
|
|
1181
|
+
applyAutoLayout,
|
|
1162
1182
|
}
|
|
1163
1183
|
})
|