@mx-sose-front/mx-sose-graph 1.1.0 → 1.1.2
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.d.ts +103 -10
- package/dist/index.esm.js +6620 -5599
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +7 -7
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/{ContextMenu.vue → ContextMenu/ContextMenu.vue} +243 -70
- package/src/components/DiagramListTooltip/DiagramListTooltip.vue +138 -0
- package/src/components/Edge/Edge.vue +38 -49
- package/src/components/InteractionLayer.vue +432 -838
- package/src/components/Shape/Block.vue +8 -8
- package/src/components/ZoomSlider/ZoomSlider.vue +229 -0
- package/src/constants/index.ts +12 -0
- package/src/statics/icons/childIcons//351/241/271/347/233/256/351/241/272/345/272/217@3x.png +0 -0
- package/src/store/graphStore.ts +98 -21
- package/src/types/index.ts +14 -1
- package/src/types/interactionLayer.ts +35 -0
- package/src/utils/contextMenuUtils.ts +264 -0
- package/src/utils/diagram.ts +93 -4
- package/src/utils/edgeUtils.ts +228 -0
- package/src/utils/geom.ts +34 -0
- package/src/utils/graphDragService.ts +14 -17
- package/src/utils/keyboardUtils.ts +229 -0
- package/src/utils/license-guard.ts +50 -0
- package/src/utils/nameEditUtils.ts +132 -0
- package/src/utils/resizeUtils.ts +463 -0
- package/src/view/graph.vue +102 -136
- package/src/components/Label.vue +0 -0
|
@@ -172,8 +172,8 @@ const shapeStyle = computed<CSSProperties>(() => {
|
|
|
172
172
|
width = Math.max(estimatedTextWidth.value, width);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
// 最小高度限制为
|
|
176
|
-
const minHeight =
|
|
175
|
+
// 最小高度限制为60px
|
|
176
|
+
const minHeight = 60;
|
|
177
177
|
const height = Math.max(bounds.height || 40, minHeight);
|
|
178
178
|
|
|
179
179
|
return {
|
|
@@ -189,8 +189,8 @@ const updateShapeBounds = async () => {
|
|
|
189
189
|
|
|
190
190
|
const requiredWidth = estimatedTextWidth.value;
|
|
191
191
|
const currentWidth = props.shape.bounds.width || 100;
|
|
192
|
-
// 最小高度限制为
|
|
193
|
-
const minHeight =
|
|
192
|
+
// 最小高度限制为60px
|
|
193
|
+
const minHeight = 60;
|
|
194
194
|
const currentHeight = props.shape.bounds.height || 40;
|
|
195
195
|
const requiredHeight = Math.max(currentHeight, minHeight);
|
|
196
196
|
|
|
@@ -249,9 +249,9 @@ const vbW = computed(() => props.shape.bounds.width || 100)
|
|
|
249
249
|
const vbH = computed(() => props.shape.bounds.height || 40)
|
|
250
250
|
// ================== 监听 bounds 宽高变化 ==================
|
|
251
251
|
|
|
252
|
-
// 原始宽高(150 x
|
|
252
|
+
// 原始宽高(150 x 60)
|
|
253
253
|
const DEFAULT_WIDTH = 150
|
|
254
|
-
const DEFAULT_HEIGHT =
|
|
254
|
+
const DEFAULT_HEIGHT = 60
|
|
255
255
|
|
|
256
256
|
// 记录上一次的宽高,用来做去重判断
|
|
257
257
|
const lastBounds = ref({
|
|
@@ -268,7 +268,7 @@ watch(
|
|
|
268
268
|
() => [props.shape.bounds.width, props.shape.bounds.height],
|
|
269
269
|
([newW, newH]) => {
|
|
270
270
|
if (isGhost.value) return
|
|
271
|
-
// 宽高兜底到默认值(150 x
|
|
271
|
+
// 宽高兜底到默认值(150 x 60)
|
|
272
272
|
const width = newW ?? DEFAULT_WIDTH
|
|
273
273
|
const height = newH ?? DEFAULT_HEIGHT
|
|
274
274
|
|
|
@@ -281,7 +281,7 @@ watch(
|
|
|
281
281
|
}
|
|
282
282
|
// 更新“上一次”的记录
|
|
283
283
|
lastBounds.value = { width, height }
|
|
284
|
-
// 只有当宽高不再是原始 150 x
|
|
284
|
+
// 只有当宽高不再是原始 150 x 60 时才认为“真的变化”,再去调用其他方法
|
|
285
285
|
if (
|
|
286
286
|
width !== DEFAULT_WIDTH || height !== DEFAULT_HEIGHT
|
|
287
287
|
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="zoom-bar">
|
|
3
|
+
<button @click="handleZoomOut" class="zoom-button zoom-out">-</button>
|
|
4
|
+
<div class="zoom-slider">
|
|
5
|
+
<div class="slider-wrapper" :style="{ '--progress': `${((modelValue - min) / (max - min)) * 100}%` }">
|
|
6
|
+
<input
|
|
7
|
+
type="range"
|
|
8
|
+
:min="min"
|
|
9
|
+
:max="max"
|
|
10
|
+
:step="step"
|
|
11
|
+
v-model="internalValue"
|
|
12
|
+
@input="handleZoomChange"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<button @click="handleZoomIn" class="zoom-button zoom-in">+</button>
|
|
17
|
+
<div class="zoom-level">{{ Math.round(internalValue * 100) }}%</div>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import { ref, watch } from 'vue'
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
modelValue: number
|
|
26
|
+
min?: number
|
|
27
|
+
max?: number
|
|
28
|
+
step?: number
|
|
29
|
+
onZoomIn?: () => void
|
|
30
|
+
onZoomOut?: () => void
|
|
31
|
+
onScaleChange?: (scale: number) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
35
|
+
min: 0.1,
|
|
36
|
+
max: 3,
|
|
37
|
+
step: 0.1
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const emit = defineEmits<{
|
|
41
|
+
'update:modelValue': [value: number]
|
|
42
|
+
'zoom-in': []
|
|
43
|
+
'zoom-out': []
|
|
44
|
+
'scale-change': [scale: number]
|
|
45
|
+
}>()
|
|
46
|
+
|
|
47
|
+
// 内部值,用于双向绑定滑块
|
|
48
|
+
const internalValue = ref(props.modelValue)
|
|
49
|
+
|
|
50
|
+
// 监听props变化,更新内部值
|
|
51
|
+
watch(() => props.modelValue, (newValue) => {
|
|
52
|
+
internalValue.value = newValue
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// 监听内部值变化,emit事件
|
|
56
|
+
watch(internalValue, (newValue) => {
|
|
57
|
+
emit('update:modelValue', newValue)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// 处理缩放滑块变化
|
|
61
|
+
const handleZoomChange = () => {
|
|
62
|
+
emit('scale-change', internalValue.value)
|
|
63
|
+
emit('update:modelValue', internalValue.value)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 处理放大按钮点击
|
|
67
|
+
const handleZoomIn = () => {
|
|
68
|
+
const newValue = Math.min(internalValue.value + props.step, props.max)
|
|
69
|
+
internalValue.value = newValue
|
|
70
|
+
emit('zoom-in')
|
|
71
|
+
emit('update:modelValue', newValue)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 处理缩小按钮点击
|
|
75
|
+
const handleZoomOut = () => {
|
|
76
|
+
const newValue = Math.max(internalValue.value - props.step, props.min)
|
|
77
|
+
internalValue.value = newValue
|
|
78
|
+
emit('zoom-out')
|
|
79
|
+
emit('update:modelValue', newValue)
|
|
80
|
+
}
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<style scoped>
|
|
84
|
+
.zoom-bar {
|
|
85
|
+
position: absolute;
|
|
86
|
+
bottom: 0;
|
|
87
|
+
left: 0;
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 24px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: flex-end;
|
|
93
|
+
background: white;
|
|
94
|
+
border-top: 1px solid #e0e0e0;
|
|
95
|
+
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
|
96
|
+
z-index: 100;
|
|
97
|
+
gap: 6px;
|
|
98
|
+
padding: 0 10px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.zoom-level {
|
|
102
|
+
font-size: 10px;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
min-width: 35px;
|
|
105
|
+
text-align: center;
|
|
106
|
+
color: #333;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.zoom-button {
|
|
110
|
+
width: 18px;
|
|
111
|
+
height: 18px;
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
border: 1px solid #e0e0e0;
|
|
116
|
+
border-radius: 2px;
|
|
117
|
+
background: white;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
font-size: 10px;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
color: #333;
|
|
122
|
+
transition: all 0.2s;
|
|
123
|
+
user-select: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.zoom-button:hover {
|
|
127
|
+
background: #f5f5f5;
|
|
128
|
+
border-color: #2a7fd9;
|
|
129
|
+
color: #2a7fd9;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.zoom-button:active {
|
|
133
|
+
transform: scale(0.95);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.zoom-slider {
|
|
137
|
+
width: 150px;
|
|
138
|
+
height: 100%;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.slider-wrapper {
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 2px;
|
|
148
|
+
background: #e0e0e0;
|
|
149
|
+
border-radius: 1px;
|
|
150
|
+
position: relative;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.slider-wrapper::before {
|
|
154
|
+
content: '';
|
|
155
|
+
position: absolute;
|
|
156
|
+
top: 50%;
|
|
157
|
+
left: 0;
|
|
158
|
+
width: var(--progress, 0%);
|
|
159
|
+
height: 2px;
|
|
160
|
+
background: #2a7fd9;
|
|
161
|
+
border-radius: 1px;
|
|
162
|
+
z-index: 1;
|
|
163
|
+
pointer-events: none;
|
|
164
|
+
transform: translateY(-50%);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.slider-wrapper input[type="range"] {
|
|
168
|
+
width: 100%;
|
|
169
|
+
height: 12px;
|
|
170
|
+
background: transparent;
|
|
171
|
+
outline: none;
|
|
172
|
+
-webkit-appearance: none;
|
|
173
|
+
margin: 0;
|
|
174
|
+
position: relative;
|
|
175
|
+
z-index: 2;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* WebKit浏览器滑块样式 */
|
|
180
|
+
.slider-wrapper input[type="range"]::-webkit-slider-thumb {
|
|
181
|
+
-webkit-appearance: none;
|
|
182
|
+
appearance: none;
|
|
183
|
+
width: 12px;
|
|
184
|
+
height: 12px;
|
|
185
|
+
border-radius: 50%;
|
|
186
|
+
background: white;
|
|
187
|
+
border: 2px solid #2a7fd9;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
transition: all 0.2s;
|
|
190
|
+
transform: translateY(-6px);
|
|
191
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.slider-wrapper input[type="range"]::-webkit-slider-thumb:hover {
|
|
195
|
+
background: white;
|
|
196
|
+
border-color: #2a7fd9;
|
|
197
|
+
transform: translateY(-6px) scale(1.1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Mozilla浏览器滑块样式 */
|
|
201
|
+
.slider-wrapper input[type="range"]::-moz-range-thumb {
|
|
202
|
+
width: 12px;
|
|
203
|
+
height: 12px;
|
|
204
|
+
border-radius: 50%;
|
|
205
|
+
background: white;
|
|
206
|
+
border: 2px solid #2a7fd9;
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
transition: all 0.2s;
|
|
209
|
+
transform: translateY(-6px);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.slider-wrapper input[type="range"]::-moz-range-thumb:hover {
|
|
213
|
+
background: white;
|
|
214
|
+
border-color: #2a7fd9;
|
|
215
|
+
transform: translateY(-6px) scale(1.1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Firefox滑块轨道样式 */
|
|
219
|
+
.slider-wrapper input[type="range"]::-moz-range-track {
|
|
220
|
+
background: transparent;
|
|
221
|
+
height: 2px;
|
|
222
|
+
border: none;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* 移除聚焦时的默认样式 */
|
|
226
|
+
.slider-wrapper input[type="range"]:focus {
|
|
227
|
+
outline: none;
|
|
228
|
+
}
|
|
229
|
+
</style>
|
package/src/constants/index.ts
CHANGED
|
@@ -201,6 +201,9 @@ export const BlockKeyMap = {
|
|
|
201
201
|
'ActualResponsibility': 'Block',//实际责任
|
|
202
202
|
'System': 'Block',//系统
|
|
203
203
|
'Competence': 'Block',//权限
|
|
204
|
+
'Person': 'Block',//人员
|
|
205
|
+
'SecurityProcess': 'Block',//安全流程
|
|
206
|
+
'ResourceMitigation': 'Block', // 资源缓解措施
|
|
204
207
|
|
|
205
208
|
// 线
|
|
206
209
|
'Association': 'Edge', //双向关联
|
|
@@ -228,6 +231,7 @@ export const BlockKeyMap = {
|
|
|
228
231
|
'FunctionControlFlow':'Edge',//功能控制流
|
|
229
232
|
'ServiceConnector':'Edge',//服务连接器
|
|
230
233
|
'ServiceControlFlow':'Edge',//服务控制流
|
|
234
|
+
"ServiceObjectFlow": 'Edge', // 服务对象流
|
|
231
235
|
'Connector':'Edge',//连接器
|
|
232
236
|
'OperationalConnector':'Edge',//业务连接器
|
|
233
237
|
'IsCapableToPerform':'Edge',//能够胜任
|
|
@@ -321,6 +325,14 @@ export const BlockKeyMap = {
|
|
|
321
325
|
// 导出类型
|
|
322
326
|
export type ShapeKeyValue = typeof BlockKeyMap[keyof typeof BlockKeyMap]
|
|
323
327
|
|
|
328
|
+
// 四个角手柄常量
|
|
329
|
+
export const resizeHandles: { position: "nw" | "ne" | "sw" | "se" }[] = [
|
|
330
|
+
{ position: "nw" },
|
|
331
|
+
{ position: "ne" },
|
|
332
|
+
{ position: "sw" },
|
|
333
|
+
{ position: "se" },
|
|
334
|
+
]
|
|
335
|
+
|
|
324
336
|
// 只包含图类型的映射
|
|
325
337
|
export const DiagramKeyMap = {
|
|
326
338
|
'StrategicTaxonomyDiagram': 'StrategicTaxonomyDiagram', //战略概念图
|
|
Binary file
|
package/src/store/graphStore.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '../utils/compartment'
|
|
21
21
|
import { EdgeUtils } from '../utils/edgeUtils'
|
|
22
22
|
import { expandParentByChild } from '../utils/autoExpandParent'
|
|
23
|
+
import { adjustCanvasToFitAllShapes } from '../utils/diagram'
|
|
23
24
|
import { applyReparentAndClone, autoExpandMovedCompartmentsAfterDrag, buildDragEndPayloads, buildPrevParentMap, collectAffectedShapeIds, createOnNestDoneCallback } from '../utils/graphDragService'
|
|
24
25
|
type Rect = { x: number; y: number; width: number; height: number };
|
|
25
26
|
export const useGraphStore = defineStore('graph', () => {
|
|
@@ -52,6 +53,8 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
52
53
|
const groupBaseBox = ref<Rect | null>(null)
|
|
53
54
|
// 指针相对图元左上角的偏移,用于保持拖动时鼠标与元素的相对位置
|
|
54
55
|
const dragOffset = ref<{ x: number; y: number } | null>(null)
|
|
56
|
+
// 拖拽开始时的画布尺寸快照
|
|
57
|
+
const dragBaseCanvasSize = ref<{ width: number; height: number } | null>(null)
|
|
55
58
|
//当前打开画布的列表
|
|
56
59
|
const diagrams = ref<Shape[]>([])
|
|
57
60
|
//当前打开的画布id
|
|
@@ -71,8 +74,11 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
71
74
|
const packagesTypes = ref<string[]>([]) // 需要展示线的隔间组件的图元类型
|
|
72
75
|
const classMetaTypes = ref<string[]>([]) //可以生成的class类型
|
|
73
76
|
const ownerRequiredShapeKeys = ref<string[]>([])//哪些图元需要自动补 ownerId
|
|
74
|
-
const pinsTypes=ref<string[]>([])//栓类型
|
|
77
|
+
const pinsTypes = ref<string[]>([])//栓类型
|
|
75
78
|
const portsTypes = ref<string[]>([])//端口类型
|
|
79
|
+
const museInGraphView = ref<boolean>(false) //是否鼠标在图元区域
|
|
80
|
+
const canOperate = ref<boolean>(false)//license是否允许操作
|
|
81
|
+
const connectMode = ref<string>('connect')
|
|
76
82
|
// 方法
|
|
77
83
|
/**
|
|
78
84
|
* 添加图元
|
|
@@ -259,6 +265,14 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
259
265
|
}
|
|
260
266
|
// 清空选择
|
|
261
267
|
const clearSelection = () => selectShape(null)
|
|
268
|
+
// 全选图元
|
|
269
|
+
const selectAll = () => {
|
|
270
|
+
// 获取所有非diagram类型的图元
|
|
271
|
+
const allShapeIds = shapes.value
|
|
272
|
+
.filter(shape => shape.shapeType?.toLowerCase() !== 'diagram')
|
|
273
|
+
.map(shape => shape.id)
|
|
274
|
+
selectMany(allShapeIds)
|
|
275
|
+
}
|
|
262
276
|
// 设置图表标题
|
|
263
277
|
const setTitle = (title: string) => {
|
|
264
278
|
diagramTitle.value = title
|
|
@@ -340,6 +354,16 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
340
354
|
for (const k in snap.dragBase) dragGhost[k] = snap.dragBase[k] // 写入新 key
|
|
341
355
|
draggingIds.value = validIds
|
|
342
356
|
dragAnchor.value = { x: pointer.x, y: pointer.y }
|
|
357
|
+
|
|
358
|
+
const currentDiagram = shapes.value.find(s => s.shapeType === 'diagram')
|
|
359
|
+
if (currentDiagram) {
|
|
360
|
+
dragBaseCanvasSize.value = {
|
|
361
|
+
width: currentDiagram.bounds.width ?? 0,
|
|
362
|
+
height: currentDiagram.bounds.height ?? 0
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
dragBaseCanvasSize.value = null
|
|
366
|
+
}
|
|
343
367
|
if (validIds.length === 1) {
|
|
344
368
|
const b = dragBase.value[validIds[0]]
|
|
345
369
|
dragOffset.value = { x: pointer.x - b.x, y: pointer.y - b.y }
|
|
@@ -444,7 +468,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
444
468
|
}
|
|
445
469
|
}
|
|
446
470
|
//拖动过程中
|
|
447
|
-
const moveDraggedShape = (pointer: { x: number; y: number }) => {
|
|
471
|
+
const moveDraggedShape = async (pointer: { x: number; y: number }) => {
|
|
448
472
|
if (!isDragging.value || draggingIds.value.length === 0) return
|
|
449
473
|
|
|
450
474
|
if (draggingIds.value.length === 1) {
|
|
@@ -576,6 +600,23 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
576
600
|
updateShape,
|
|
577
601
|
})
|
|
578
602
|
|
|
603
|
+
// 先同步调整画布大小,确保获取到最新的画布数据
|
|
604
|
+
adjustCanvasToFitAllShapes()
|
|
605
|
+
|
|
606
|
+
// 检查画布宽高是否有变化
|
|
607
|
+
if (dragBaseCanvasSize.value) {
|
|
608
|
+
const currentDiagram = shapes.value.find(s => s.shapeType === 'diagram')
|
|
609
|
+
if (currentDiagram) {
|
|
610
|
+
const currentWidth = currentDiagram.bounds.width
|
|
611
|
+
const currentHeight = currentDiagram.bounds.height
|
|
612
|
+
const { width: baseWidth, height: baseHeight } = dragBaseCanvasSize.value
|
|
613
|
+
if (currentWidth !== baseWidth || currentHeight !== baseHeight) {
|
|
614
|
+
const diagramPayload = _.cloneDeep(currentDiagram)
|
|
615
|
+
payloads.push(diagramPayload)
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
579
620
|
//对外只暴露一个事件:shape-drag-end
|
|
580
621
|
eventBus.emit('shape-drag-end', payloads, ownerPayload, onNestDone)
|
|
581
622
|
|
|
@@ -638,6 +679,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
638
679
|
groupBaseBox.value = null
|
|
639
680
|
dragSelectionSnapshot.value = []
|
|
640
681
|
primaryDragId.value = null
|
|
682
|
+
dragBaseCanvasSize.value = null
|
|
641
683
|
for (const k in dragGhost) delete dragGhost[k]
|
|
642
684
|
}
|
|
643
685
|
//设置当前打开的画布id
|
|
@@ -647,6 +689,14 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
647
689
|
const setActiveDiagramId = (id: string | null) => {
|
|
648
690
|
activeDiagramId.value = id;
|
|
649
691
|
};
|
|
692
|
+
// 设置当前连线的模式
|
|
693
|
+
const setConnectMode = (mode: string) => {
|
|
694
|
+
connectMode.value = mode
|
|
695
|
+
}
|
|
696
|
+
// 获取当前连线的模式
|
|
697
|
+
const getConnectMode = () => {
|
|
698
|
+
return connectMode.value
|
|
699
|
+
}
|
|
650
700
|
// 获取当前缩放比例
|
|
651
701
|
const getScale = (diagramId?: string | null) => {
|
|
652
702
|
const targetId = diagramId || activeDiagramId.value;
|
|
@@ -682,37 +732,56 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
682
732
|
* - 内部调用 expandParentByChild → 得到 expanded / affectedIds
|
|
683
733
|
* - 若有扩父,再组装 sizePayload 并发 shape-size-update 事件
|
|
684
734
|
*/
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
|
|
735
|
+
const collectAncestors = (shapes: Shape[], id: string) => {
|
|
736
|
+
const out: string[] = []
|
|
737
|
+
let cur = shapes.find(s => s.id === id)
|
|
738
|
+
while (cur) {
|
|
739
|
+
out.push(cur.id)
|
|
740
|
+
const pid = cur.parenShapeId
|
|
741
|
+
if (!pid) break
|
|
742
|
+
cur = shapes.find(s => s.id === pid)
|
|
688
743
|
}
|
|
689
|
-
|
|
690
|
-
|
|
744
|
+
return out
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const expandParentAndEmitSizeUpdateByChild = (child: Shape) => {
|
|
748
|
+
if (!child.parenShapeId) return
|
|
749
|
+
if (pinsTypes.value.includes(child.shapeKey)) return
|
|
750
|
+
|
|
751
|
+
const parentId = child.parenShapeId
|
|
752
|
+
const chainIds = collectAncestors(shapes.value, parentId)
|
|
753
|
+
|
|
754
|
+
// before 快照(只对父链)
|
|
755
|
+
const before = new Map(
|
|
756
|
+
chainIds.map(id => [
|
|
757
|
+
id,
|
|
758
|
+
JSON.stringify(shapes.value.find(s => s.id === id)?.bounds ?? {}),
|
|
759
|
+
]),
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
const { expanded } = expandParentByChild({
|
|
691
763
|
shapes: shapes.value,
|
|
692
764
|
child,
|
|
693
765
|
updateShape,
|
|
694
766
|
})
|
|
695
|
-
|
|
696
767
|
if (!expanded) return
|
|
697
768
|
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
// 组装“只更新大小”的 payload:id + JSON.stringify(bounds)
|
|
701
|
-
const sizePayload: ShapeSizeUpdatePayload[] = affectedIds
|
|
769
|
+
const payload: ShapeSizeUpdatePayload[] = chainIds
|
|
770
|
+
.filter(id => id !== child.id)
|
|
702
771
|
.map(id => shapes.value.find(s => s.id === id))
|
|
703
772
|
.filter((s): s is Shape => !!s)
|
|
704
|
-
//
|
|
705
|
-
.
|
|
706
|
-
.map(s => ({
|
|
707
|
-
id: s.id,
|
|
708
|
-
bounds: JSON.stringify(s.bounds ?? {}),
|
|
709
|
-
}))
|
|
773
|
+
.filter(s => before.get(s.id) !== JSON.stringify(s.bounds ?? {})) // diff
|
|
774
|
+
.map(s => ({ id: s.id, bounds: JSON.stringify(s.bounds ?? {}) }))
|
|
710
775
|
|
|
711
|
-
if (!
|
|
776
|
+
if (!payload.length) return
|
|
777
|
+
// eventBus.emit('shape-size-update', payload)
|
|
778
|
+
}
|
|
712
779
|
|
|
713
|
-
|
|
714
|
-
|
|
780
|
+
// 设置鼠标是否在图元区域
|
|
781
|
+
const setIsMouseInGraphView = (isIn: boolean) => {
|
|
782
|
+
museInGraphView.value = isIn
|
|
715
783
|
}
|
|
784
|
+
|
|
716
785
|
return {
|
|
717
786
|
// 状态
|
|
718
787
|
shapes,
|
|
@@ -726,6 +795,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
726
795
|
hoverNestable,
|
|
727
796
|
currentDiagramId,
|
|
728
797
|
canDropOnCanvas,
|
|
798
|
+
canOperate,
|
|
729
799
|
//图元shapeKey
|
|
730
800
|
taggedValueLabels,
|
|
731
801
|
packagesTypes,
|
|
@@ -742,6 +812,9 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
742
812
|
activeDiagramId,
|
|
743
813
|
// 当前活动画布的缩放比例
|
|
744
814
|
currentScale: computed(() => getScale()),
|
|
815
|
+
museInGraphView,
|
|
816
|
+
connectMode,
|
|
817
|
+
|
|
745
818
|
// 方法
|
|
746
819
|
addShape,
|
|
747
820
|
removeShape,
|
|
@@ -759,6 +832,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
759
832
|
setCurrentDiagramId,
|
|
760
833
|
selectMany,
|
|
761
834
|
clearSelection,
|
|
835
|
+
selectAll,
|
|
762
836
|
removeSelected,
|
|
763
837
|
finalCheckCanNest,
|
|
764
838
|
setHoverState,
|
|
@@ -767,9 +841,12 @@ export const useGraphStore = defineStore('graph', () => {
|
|
|
767
841
|
getScale,
|
|
768
842
|
setScale,
|
|
769
843
|
setActiveDiagramId,
|
|
844
|
+
setConnectMode,
|
|
845
|
+
getConnectMode,
|
|
770
846
|
visibleShapes,
|
|
771
847
|
externalCreatingId,
|
|
772
848
|
expandParentAndEmitSizeUpdateByChild,
|
|
849
|
+
setIsMouseInGraphView,
|
|
773
850
|
}
|
|
774
851
|
},
|
|
775
852
|
//配置持久化
|
package/src/types/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface Bounds {
|
|
|
5
5
|
width?: number | undefined
|
|
6
6
|
height?: number | undefined
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
export type Rect = { x: number; y: number; width: number; height: number };
|
|
8
10
|
export type Padding = number | { top: number; right: number; bottom: number; left: number };
|
|
9
11
|
// 样式接口
|
|
10
12
|
export interface Style {
|
|
@@ -197,4 +199,15 @@ export interface CheckNested {
|
|
|
197
199
|
export interface ShapeSizeUpdatePayload {
|
|
198
200
|
id: string
|
|
199
201
|
bounds: string // JSON string
|
|
200
|
-
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 所在图表列表
|
|
205
|
+
export interface locationChart {
|
|
206
|
+
modelCode: string //图表图标名称
|
|
207
|
+
treeId: string
|
|
208
|
+
modelName: string //图表名称
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// InteractionLayer types
|
|
212
|
+
export type { InteractionLayerProps, ExternalCreateDragState } from './interactionLayer';
|
|
213
|
+
export { InteractionLayerEmits } from './interactionLayer';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Shape } from ".";
|
|
2
|
+
|
|
3
|
+
export interface InteractionLayerProps {
|
|
4
|
+
connectShapeData?: Shape;
|
|
5
|
+
diagramBounds?: any;
|
|
6
|
+
resShape: Shape;
|
|
7
|
+
lines?: string[];
|
|
8
|
+
packages?: string[];
|
|
9
|
+
diagram?: string[];
|
|
10
|
+
taggedValueLabels?: string[];
|
|
11
|
+
actionButtonShapeDataId?: string;
|
|
12
|
+
edgeCheck?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const InteractionLayerEmits = [
|
|
16
|
+
"propertyPanel",
|
|
17
|
+
"editName",
|
|
18
|
+
"update-shape",
|
|
19
|
+
"connectEnd",
|
|
20
|
+
"actionButtonClick",
|
|
21
|
+
"diagramDoubleClick",
|
|
22
|
+
"modelTypePropertyIdButtonClick",
|
|
23
|
+
"edge-click",
|
|
24
|
+
"property-panel",
|
|
25
|
+
"actionButtonAdd"
|
|
26
|
+
] as string[];
|
|
27
|
+
|
|
28
|
+
export type InteractionLayerEmitsType = typeof InteractionLayerEmits;
|
|
29
|
+
|
|
30
|
+
export interface ExternalCreateDragState {
|
|
31
|
+
creatingId: string | null;
|
|
32
|
+
pendingShape: Shape | null;
|
|
33
|
+
isDragging: boolean;
|
|
34
|
+
isCheckInFlight: boolean;
|
|
35
|
+
}
|