@zhangqingcq/vgce 0.0.20 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. package/README.md +1 -1
  2. package/dist/style.css +2 -2
  3. package/dist/vgce.js +15858 -15276
  4. package/dist/vgce.umd.cjs +82 -129
  5. package/package.json +13 -15
  6. package/src/assets/base.less +79 -31
  7. package/src/assets/icons/delete-gray.svg +1 -0
  8. package/src/assets/icons/delete.svg +1 -12
  9. package/src/assets/icons/export.svg +1 -1
  10. package/src/assets/icons/import.svg +1 -1
  11. package/src/assets/icons/line-active.svg +1 -0
  12. package/src/assets/icons/line.svg +1 -0
  13. package/src/assets/icons/menu-fold.svg +1 -9
  14. package/src/assets/icons/menu-unfold.svg +1 -9
  15. package/src/assets/icons/preview.svg +1 -6
  16. package/src/assets/icons/question.svg +1 -0
  17. package/src/assets/icons/redo-gray.svg +1 -0
  18. package/src/assets/icons/redo.svg +1 -8
  19. package/src/assets/icons/return.svg +1 -8
  20. package/src/assets/icons/rotate.svg +1 -1
  21. package/src/assets/icons/save.svg +1 -9
  22. package/src/assets/icons/undo-gray.svg +1 -0
  23. package/src/assets/icons/undo.svg +1 -7
  24. package/src/assets/svgs/sheet-border.svg +1 -0
  25. package/src/assets/svgs/svg-text.svg +1 -5
  26. package/src/components/svg-analysis/index.vue +1 -1
  27. package/src/components/svg-editor/center-panel/index.vue +508 -176
  28. package/src/components/svg-editor/component-tree/index.vue +4 -0
  29. package/src/components/svg-editor/connection-line/index.vue +12 -8
  30. package/src/components/svg-editor/connection-panel/index.vue +121 -181
  31. package/src/components/svg-editor/handle-panel/index.vue +32 -24
  32. package/src/components/svg-editor/index.vue +24 -25
  33. package/src/components/svg-editor/left-panel/index.vue +2 -2
  34. package/src/components/svg-editor/right-panel/bind-anchor.vue +124 -0
  35. package/src/components/svg-editor/right-panel/code-edit-modal.vue +1 -1
  36. package/src/components/svg-editor/right-panel/dynamic-el-form-item.vue +8 -1
  37. package/src/components/svg-editor/right-panel/index.vue +117 -56
  38. package/src/components/svg-editor/top-panel/index.vue +109 -25
  39. package/src/components/svg-viewer/index.vue +31 -21
  40. package/src/components/vue3-ruler-tool/index.vue +329 -372
  41. package/src/config/files/clock-a.vue +64 -64
  42. package/src/config/svg/animation/index.ts +1 -1
  43. package/src/config/svg/custom/svg-text.ts +2 -2
  44. package/src/config/svg/stateful/index.ts +1 -1
  45. package/src/config/svg/stateless/index.ts +3 -2
  46. package/src/config/svg/stateless/sheet-border.ts +22 -0
  47. package/src/config/types.ts +1 -0
  48. package/src/stores/config/index.ts +1 -8
  49. package/src/stores/config/types.ts +0 -8
  50. package/src/stores/global/index.ts +0 -10
  51. package/src/stores/global/types.ts +33 -10
  52. package/src/stores/svg-edit-layout/index.ts +7 -0
  53. package/src/stores/svg-edit-layout/types.ts +1 -0
  54. package/src/utils/index.ts +227 -103
  55. package/src/utils/scale-core.ts +1 -0
  56. package/src/views/EditorS.vue +1 -1
@@ -1,18 +1,22 @@
1
1
  <script setup lang="ts">
2
- import { pinia } from '@/hooks'
2
+ import { pinia, useHistoryRecord } from '@/hooks'
3
3
  import { useConfigStore } from '@/stores/config'
4
4
  import { useGlobalStore } from '@/stores/global'
5
- import { EGlobalStoreIntention, EMouseInfoState, EScaleInfoType } from '@/stores/global/types'
6
5
  import type { IDoneJson } from '@/stores/global/types'
6
+ import { EGlobalStoreIntention, EMouseInfoState, EScaleInfoType } from '@/stores/global/types'
7
7
  import { useSvgEditLayoutStore } from '@/stores/svg-edit-layout'
8
8
  import {
9
+ createLine,
9
10
  getCenterPoint,
10
- randomString,
11
+ getCommonClass,
11
12
  getSvgNowPosition,
12
- setSvgActualInfo,
13
- prosToVBind,
13
+ moveAnchors,
14
+ moveHandlePoint,
14
15
  objectDeepClone,
15
- getCommonClass
16
+ prosToVBind,
17
+ randomString,
18
+ resetHandlePointOld,
19
+ setSvgActualInfo
16
20
  } from '@/utils'
17
21
  import {
18
22
  calculateBottom,
@@ -26,14 +30,14 @@
26
30
  } from '@/utils/scale-core'
27
31
  import HandlePanel from '@/components/svg-editor/handle-panel/index.vue'
28
32
  import ConnectionPanel from '@/components/svg-editor/connection-panel/index.vue'
29
- import { EDoneJsonType } from '@/config/types'
30
33
  import type { IConfigItem } from '@/config/types'
34
+ import { EDoneJsonType } from '@/config/types'
31
35
  import ConnectionLine from '@/components/svg-editor/connection-line/index.vue'
32
36
  import type { IVisibleInfo } from './types'
33
37
  import { vueComp } from '@/config'
34
38
  import { useContextMenuStore, useEditPrivateStore } from '@/stores/system'
35
39
  import { EContextMenuInfoType } from '@/stores/system/types'
36
- import { useHistoryRecord } from '@/hooks'
40
+ import { computed } from 'vue'
37
41
 
38
42
  //注册所有组件
39
43
  const instance = getCurrentInstance()
@@ -50,13 +54,18 @@
50
54
  const contextMenuStore = useContextMenuStore(pinia)
51
55
  const contextMenuRef = ref<HTMLElement>()
52
56
  const canvasRef = ref<HTMLElement>()
53
- const cursor_style = computed(() =>
54
- globalStore.intention == EGlobalStoreIntention.MoveCanvas
55
- ? 'grab'
56
- : globalStore.intention == EGlobalStoreIntention.Rotate
57
- ? "url('/src/assets/icons/rotate.svg') 12 12, auto"
58
- : 'default'
59
- )
57
+ const ct: Record<string, any> = {
58
+ MoveCanvas: 'grab',
59
+ Rotate: "url('/src/assets/icons/rotate.svg'), auto",
60
+ Connection: 'crosshair'
61
+ }
62
+ const cursor_style = computed(() => {
63
+ if (Object.keys(ct).indexOf(globalStore.intention) > -1) {
64
+ return ct[globalStore.intention]
65
+ } else {
66
+ return 'default'
67
+ }
68
+ })
60
69
  const visible_info: IVisibleInfo = reactive({
61
70
  handle_panel: computed(
62
71
  () =>
@@ -71,7 +80,47 @@
71
80
  index: -1
72
81
  }
73
82
  })
74
- const dropEvent = (e: DragEvent) => {
83
+
84
+ const area = ref<Record<string, any>>({
85
+ a: {
86
+ x: 0,
87
+ y: 0
88
+ },
89
+ b: {
90
+ x: 0,
91
+ y: 0
92
+ }
93
+ })
94
+ const selectRect = computed(() => {
95
+ if (globalStore.intention === EGlobalStoreIntention.SelectArea && area.value.b.x && area.value.b.y) {
96
+ return {
97
+ with: Math.abs(area.value.b.x - area.value.a.x),
98
+ height: Math.abs(area.value.b.y - area.value.a.y),
99
+ x: area.value.a.x < area.value.b.x ? area.value.a.x : area.value.b.x,
100
+ y: area.value.a.y < area.value.b.y ? area.value.a.y : area.value.b.y
101
+ }
102
+ }
103
+ return {
104
+ with: 0,
105
+ height: 0,
106
+ x: 0,
107
+ y: 0
108
+ }
109
+ })
110
+
111
+ const isGroup = computed(() => {
112
+ let t = 0
113
+ for (let e of globalStore.done_json) {
114
+ if (e.selected) {
115
+ t++
116
+ }
117
+ }
118
+ return t > 0
119
+ })
120
+
121
+ let groupMoved = false
122
+
123
+ const dropEvent = (e: Record<string, any>) => {
75
124
  if (globalStore.intention == EGlobalStoreIntention.None) {
76
125
  return
77
126
  } else if (globalStore.intention == EGlobalStoreIntention.Create) {
@@ -80,8 +129,8 @@
80
129
  return
81
130
  }
82
131
 
83
- const _x = Math.floor(e.clientX - svgEditLayoutStore.center_offset.x)
84
- const _y = Math.floor(e.clientY - svgEditLayoutStore.center_offset.y)
132
+ const _x = Math.floor(e.offsetX / configStore.svg.scale - svgEditLayoutStore.center_offset.x)
133
+ const _y = Math.floor(e.offsetY / configStore.svg.scale - svgEditLayoutStore.center_offset.y)
85
134
 
86
135
  const done_item_json: IDoneJson = {
87
136
  id: randomString(),
@@ -100,6 +149,10 @@
100
149
  width: 0,
101
150
  height: 0
102
151
  },
152
+ center_position: {
153
+ x: 0,
154
+ y: 0
155
+ },
103
156
  point_coordinate: {
104
157
  tl: {
105
158
  x: 0,
@@ -134,10 +187,11 @@
134
187
  y: 0
135
188
  }
136
189
  },
190
+ selected: false,
137
191
  ...objectDeepClone<IConfigItem>(globalStore.create_svg_info)
138
192
  }
139
- globalStore.setHandleSvgInfo(done_item_json, globalStore.done_json.length)
140
193
  globalStore.setDoneJson(done_item_json)
194
+ globalStore.setHandleSvgInfo(done_item_json, globalStore.done_json.length)
141
195
  globalStore.intention = EGlobalStoreIntention.None
142
196
  }
143
197
  canvasRef.value?.focus()
@@ -157,18 +211,80 @@
157
211
  }
158
212
  e.preventDefault()
159
213
  e.stopPropagation()
160
- //鼠标在画布上的组件按下记录选中的组件信息和鼠标位置信息等
161
- globalStore.intention = EGlobalStoreIntention.Select
162
- globalStore.setHandleSvgInfo(select_item, index)
163
- globalStore.setMouseInfo({
164
- state: EMouseInfoState.Down,
165
- position_x: e.clientX,
166
- position_y: e.clientY,
167
- now_position_x: select_item.x,
168
- now_position_y: select_item.y,
169
- new_position_x: select_item.x,
170
- new_position_y: select_item.y
171
- })
214
+ if (e.ctrlKey && e.button === 0) {
215
+ //ctrl+鼠标左键 多选组件
216
+ if (globalStore.handle_svg_info?.index) {
217
+ globalStore.done_json[globalStore.handle_svg_info.index].selected = true
218
+ globalStore.setHandleSvgInfo(null)
219
+ select_item.selected = !select_item.selected
220
+ }
221
+ } else if (isGroup.value) {
222
+ //有框选的组件
223
+ globalStore.intention = EGlobalStoreIntention.GroupMove
224
+ globalStore.mouse_info = {
225
+ state: EMouseInfoState.Down,
226
+ position_x: Math.round((e.clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale),
227
+ position_y: Math.round((e.clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale),
228
+ now_position_x: 0,
229
+ now_position_y: 0,
230
+ new_position_x: 0,
231
+ new_position_y: 0
232
+ }
233
+ for (let e of globalStore.done_json) {
234
+ if (e.selected) {
235
+ e.old_position = {
236
+ x: e.x,
237
+ y: e.y
238
+ }
239
+ e.point_coordinate_old = objectDeepClone(e.point_coordinate)
240
+ }
241
+ }
242
+ } else {
243
+ //鼠标在画布上的组件按下,记录选中的组件信息和鼠标位置信息等
244
+ globalStore.intention = EGlobalStoreIntention.Select
245
+ globalStore.setHandleSvgInfo(select_item, index)
246
+ globalStore.mouse_info = {
247
+ state: EMouseInfoState.Down,
248
+ position_x: Math.round((e.clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale),
249
+ position_y: Math.round((e.clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale),
250
+ now_position_x: select_item.x,
251
+ now_position_y: select_item.y,
252
+ new_position_x: 0,
253
+ new_position_y: 0
254
+ }
255
+ select_item.point_coordinate_old = objectDeepClone(select_item.point_coordinate)
256
+ }
257
+ }
258
+
259
+ const onSvgMouseUp = (select_item: IDoneJson, index: number, e: MouseEvent) => {
260
+ if (
261
+ globalStore.intention === EGlobalStoreIntention.Connection ||
262
+ globalStore.mouse_info.state != EMouseInfoState.Down
263
+ ) {
264
+ return
265
+ }
266
+ if (isGroup.value && !groupMoved && ['SelectArea', 'Move', 'Select'].indexOf(globalStore.intention) < 0) {
267
+ //在有框选组件的情况下点击单个组件,鼠标在画布上的组件弹起记录选中的组件信息和鼠标位置信息等
268
+ for (let e of globalStore.done_json) {
269
+ e.selected = false
270
+ e.old_position = {
271
+ x: 0,
272
+ y: 0
273
+ }
274
+ resetHandlePointOld(e)
275
+ }
276
+ globalStore.intention = EGlobalStoreIntention.Select
277
+ globalStore.setHandleSvgInfo(select_item, index)
278
+ globalStore.mouse_info = {
279
+ state: EMouseInfoState.Down,
280
+ position_x: Math.round((e.clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale),
281
+ position_y: Math.round((e.clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale),
282
+ now_position_x: select_item.x,
283
+ now_position_y: select_item.y,
284
+ new_position_x: 0,
285
+ new_position_y: 0
286
+ }
287
+ }
172
288
  }
173
289
  const onSvgMouseEnter = (select_item: IDoneJson, index: number, e: MouseEvent) => {
174
290
  e.preventDefault()
@@ -209,34 +325,110 @@
209
325
  return
210
326
  }
211
327
  const { clientX, clientY } = e
212
- globalStore.mouse_info.new_position_x =
213
- globalStore.mouse_info.now_position_x + clientX - globalStore.mouse_info.position_x
214
- globalStore.mouse_info.new_position_y =
215
- globalStore.mouse_info.now_position_y + clientY - globalStore.mouse_info.position_y
328
+ globalStore.mouse_info.new_position_x = Math.round(
329
+ (clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale
330
+ )
331
+ globalStore.mouse_info.new_position_y = Math.round(
332
+ (clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale
333
+ )
334
+ const x = Math.round(
335
+ globalStore.mouse_info.new_position_x - globalStore.mouse_info.position_x + globalStore.mouse_info.now_position_x
336
+ )
337
+ const y = Math.round(
338
+ globalStore.mouse_info.new_position_y - globalStore.mouse_info.position_y + globalStore.mouse_info.now_position_y
339
+ )
216
340
  if (
217
341
  globalStore.handle_svg_info?.info &&
218
- (globalStore.intention == EGlobalStoreIntention.Select || globalStore.intention == EGlobalStoreIntention.Move)
342
+ (globalStore.intention === EGlobalStoreIntention.Select || globalStore.intention === EGlobalStoreIntention.Move)
219
343
  ) {
220
- //有选中组件 移动组件
221
- globalStore.handle_svg_info.info.x = globalStore.mouse_info.new_position_x
222
- globalStore.handle_svg_info.info.y = globalStore.mouse_info.new_position_y
344
+ //有选中组件 移动单个组件
345
+ globalStore.handle_svg_info.info.x = x
346
+
347
+ globalStore.handle_svg_info.info.y = y
348
+
223
349
  globalStore.handle_svg_info.info.client = {
224
- x: globalStore.mouse_info.new_position_x,
225
- y: globalStore.mouse_info.new_position_y
350
+ x: x,
351
+ y: y
226
352
  }
227
353
  globalStore.intention = EGlobalStoreIntention.Move
228
- } else if (globalStore.intention == EGlobalStoreIntention.MoveCanvas) {
354
+
355
+ moveHandlePoint(globalStore.handle_svg_info.info)
356
+ moveAnchors(globalStore.handle_svg_info.info)
357
+ if (globalStore.handle_svg_info.info.type === EDoneJsonType.ConnectionLine) {
358
+ //移动连线自己绑定过的锚点到绑定位置
359
+ if (globalStore.handle_svg_info.info.bind_anchors?.start) {
360
+ let _done_json: any = null
361
+ for (let t of globalStore.done_json) {
362
+ if (t.id === globalStore.handle_svg_info.info.bind_anchors?.start?.target_id) {
363
+ _done_json = t
364
+ }
365
+ }
366
+ if (_done_json) {
367
+ moveAnchors(_done_json)
368
+ }
369
+ }
370
+ if (globalStore.handle_svg_info.info.bind_anchors?.end) {
371
+ let _done_json: any = null
372
+ for (let t of globalStore.done_json) {
373
+ if (t.id === globalStore.handle_svg_info.info.bind_anchors?.end?.target_id) {
374
+ _done_json = t
375
+ }
376
+ }
377
+ if (_done_json) {
378
+ moveAnchors(_done_json)
379
+ }
380
+ }
381
+ }
382
+ } else if (globalStore.intention === EGlobalStoreIntention.MoveCanvas) {
229
383
  //移动画布
230
- svgEditLayoutStore.center_offset.x = globalStore.mouse_info.new_position_x
231
- svgEditLayoutStore.center_offset.y = globalStore.mouse_info.new_position_y
384
+ svgEditLayoutStore.center_offset.x = x
385
+ svgEditLayoutStore.center_offset.y = y
386
+ } else if (globalStore.intention === EGlobalStoreIntention.SelectArea) {
387
+ //框选
388
+ area.value.b.x = globalStore.mouse_info.new_position_x - svgEditLayoutStore.center_offset.x
389
+ area.value.b.y = globalStore.mouse_info.new_position_y - svgEditLayoutStore.center_offset.y
390
+ } else if (globalStore.intention === EGlobalStoreIntention.GroupMove) {
391
+ //群组移动
392
+ groupMoved = true
393
+ for (let e of globalStore.done_json) {
394
+ if (e.selected && e.old_position) {
395
+ const tx = Math.round(
396
+ globalStore.mouse_info.new_position_x - globalStore.mouse_info.position_x + e.old_position.x
397
+ )
398
+ const ty = Math.round(
399
+ globalStore.mouse_info.new_position_y - globalStore.mouse_info.position_y + e.old_position.y
400
+ )
401
+ e.x = tx
402
+ e.y = ty
403
+ e.client = {
404
+ x: tx,
405
+ y: ty
406
+ }
407
+ moveHandlePoint(e)
408
+ moveAnchors(e)
409
+ if (e.type === EDoneJsonType.ConnectionLine && e.bind_anchors?.end) {
410
+ //移动连线自己绑定过的锚点到绑定位置
411
+ let _done_json: any = null
412
+ for (let t of globalStore.done_json) {
413
+ if (t.id === e.bind_anchors?.end?.target_id) {
414
+ _done_json = t
415
+ }
416
+ }
417
+ if (_done_json) {
418
+ moveAnchors(_done_json)
419
+ }
420
+ }
421
+ }
422
+ }
232
423
  } else if (globalStore.intention === EGlobalStoreIntention.Zoom) {
424
+ //缩放单个组件
233
425
  if (!globalStore.handle_svg_info) {
234
426
  return
235
427
  }
236
- //当前鼠标坐标
428
+ //当前鼠标在画布中的坐标
237
429
  const curPosition = {
238
- x: e.clientX - svgEditLayoutStore.center_offset.x,
239
- y: e.clientY - svgEditLayoutStore.center_offset.y
430
+ x: globalStore.mouse_info.new_position_x - svgEditLayoutStore.center_offset.x,
431
+ y: globalStore.mouse_info.new_position_y - svgEditLayoutStore.center_offset.y
240
432
  }
241
433
  let new_length = {
242
434
  width: 0,
@@ -298,35 +490,13 @@
298
490
  )
299
491
  }
300
492
 
301
- //缩放
302
- // const move_length_x =
303
- // globalStore.scale_info.type === EScaleInfoType.TopLeft ||
304
- // globalStore.scale_info.type === EScaleInfoType.Left ||
305
- // globalStore.scale_info.type === EScaleInfoType.BottomLeft
306
- // ? -(newTopLeftPoint.x - globalStore.mouse_info.now_position_x)
307
- // : globalStore.scale_info.type === EScaleInfoType.TopRight ||
308
- // globalStore.scale_info.type === EScaleInfoType.Right ||
309
- // globalStore.scale_info.type === EScaleInfoType.BottomRight
310
- // ? globalStore.mouse_info.now_position_x - newTopLeftPoint.x
311
- // : 0;
312
- // const move_length_y =
313
- // globalStore.scale_info.type === EScaleInfoType.TopLeft ||
314
- // globalStore.scale_info.type === EScaleInfoType.TopCenter ||
315
- // globalStore.scale_info.type === EScaleInfoType.TopRight
316
- // ? newTopLeftPoint.y - globalStore.mouse_info.now_position_y
317
- // : globalStore.scale_info.type === EScaleInfoType.BottomLeft ||
318
- // globalStore.scale_info.type === EScaleInfoType.BottomCenter ||
319
- // globalStore.scale_info.type === EScaleInfoType.BottomRight
320
- // ? globalStore.mouse_info.now_position_y - newTopLeftPoint.y
321
- // : 0;
322
-
323
493
  //算出缩放倍数
324
494
  if (globalStore.handle_svg_info && new_length.width > 0 && new_length.height > 0) {
325
495
  const scale_x = !new_length.is_old_width
326
- ? new_length.width / globalStore.handle_svg_info.info.actual_bound.width
496
+ ? parseFloat((new_length.width / globalStore.handle_svg_info.info.actual_bound.width).toFixed(3))
327
497
  : 1
328
498
  const scale_y = !new_length.is_old_height
329
- ? new_length.height / globalStore.handle_svg_info.info.actual_bound.height
499
+ ? parseFloat((new_length.height / globalStore.handle_svg_info.info.actual_bound.height).toFixed(3))
330
500
  : 1
331
501
  const newCenterPoint = getCenterPoint(curPosition, globalStore.scale_info.symmetric_point)
332
502
  if (
@@ -335,10 +505,12 @@
335
505
  globalStore.scale_info.type !== EScaleInfoType.BottomCenter
336
506
  ) {
337
507
  globalStore.handle_svg_info.info.scale_x = scale_x
338
- globalStore.handle_svg_info.info.x = getSvgNowPosition(
339
- globalStore.handle_svg_info.info.client.x,
340
- newCenterPoint.x,
341
- globalStore.scale_info.scale_item_info.x
508
+ globalStore.handle_svg_info.info.x = Math.round(
509
+ getSvgNowPosition(
510
+ globalStore.handle_svg_info.info.center_position.x,
511
+ newCenterPoint.x,
512
+ globalStore.handle_svg_info.info.client.x
513
+ )
342
514
  )
343
515
  }
344
516
  if (
@@ -347,10 +519,12 @@
347
519
  globalStore.scale_info.type !== EScaleInfoType.Right
348
520
  ) {
349
521
  globalStore.handle_svg_info.info.scale_y = scale_y
350
- globalStore.handle_svg_info.info.y = getSvgNowPosition(
351
- globalStore.handle_svg_info.info.client.y,
352
- newCenterPoint.y,
353
- globalStore.scale_info.scale_item_info.y
522
+ globalStore.handle_svg_info.info.y = Math.round(
523
+ getSvgNowPosition(
524
+ globalStore.handle_svg_info.info.center_position.y,
525
+ newCenterPoint.y,
526
+ globalStore.handle_svg_info.info.client.y
527
+ )
354
528
  )
355
529
  }
356
530
  }
@@ -360,43 +534,61 @@
360
534
  }
361
535
  const rotateDegreeBefore =
362
536
  Math.atan2(
363
- globalStore.mouse_info.position_y - globalStore.handle_svg_info.info.client.y,
364
- globalStore.mouse_info.position_x - globalStore.handle_svg_info.info.client.x
537
+ globalStore.mouse_info.position_y - globalStore.handle_svg_info.info.y,
538
+ globalStore.mouse_info.position_x - globalStore.handle_svg_info.info.x
365
539
  ) /
366
540
  (Math.PI / 180)
367
541
  const rotateDegreeAfter =
368
542
  Math.atan2(
369
- clientY - svgEditLayoutStore.center_offset.y - globalStore.handle_svg_info.info.client.y,
370
- clientX - svgEditLayoutStore.center_offset.x - globalStore.handle_svg_info.info.client.x
543
+ globalStore.mouse_info.new_position_y -
544
+ svgEditLayoutStore.center_offset.y -
545
+ globalStore.handle_svg_info.info.y,
546
+ globalStore.mouse_info.new_position_x -
547
+ svgEditLayoutStore.center_offset.x -
548
+ globalStore.handle_svg_info.info.x
371
549
  ) /
372
550
  (Math.PI / 180)
373
- globalStore.handle_svg_info.info.rotate = globalStore.rotate_info.angle + rotateDegreeAfter - rotateDegreeBefore
551
+ globalStore.handle_svg_info.info.rotate = parseFloat(
552
+ (globalStore.rotate_info.angle + rotateDegreeAfter - rotateDegreeBefore).toFixed(2)
553
+ )
374
554
  } else if (globalStore.intention === EGlobalStoreIntention.Connection && globalStore.handle_svg_info) {
375
- globalStore.handle_svg_info.info.props.point_position.val[
376
- globalStore.handle_svg_info?.info.props.point_position.val.length - 1
377
- ] = {
378
- x: getSvgNowPosition(
379
- globalStore.mouse_info.position_x,
380
- clientX,
381
- globalStore.handle_svg_info?.info.props.point_position.val[0].x
382
- ),
383
- y: getSvgNowPosition(
384
- globalStore.mouse_info.position_y,
385
- clientY,
386
- globalStore.handle_svg_info?.info.props.point_position.val[0].y
387
- )
555
+ //鼠标移动的实时位置(相对于连线起始点,只在创建第一个点时记录了鼠标原始位置)
556
+ const l = globalStore.handle_svg_info?.info.props.point_position.val.length
557
+ const _x =
558
+ globalStore.mouse_info.new_position_x - globalStore.mouse_info.position_x - svgEditLayoutStore.center_offset.x
559
+ const _y =
560
+ globalStore.mouse_info.new_position_y - globalStore.mouse_info.position_y - svgEditLayoutStore.center_offset.y
561
+ const brotherPoint = globalStore.handle_svg_info.info.props.point_position.val[l - 2]
562
+ let ox = brotherPoint.x < _x ? -5 : brotherPoint.x > _x ? 5 : 0
563
+ let oy = brotherPoint.y < _y ? -5 : brotherPoint.y > _y ? 5 : 0
564
+ if (e.ctrlKey) {
565
+ //画竖线
566
+ globalStore.handle_svg_info.info.props.point_position.val[l - 1] = {
567
+ x: brotherPoint.x,
568
+ y: _y + oy
569
+ }
570
+ } else if (e.shiftKey) {
571
+ //画横线
572
+ globalStore.handle_svg_info.info.props.point_position.val[l - 1] = {
573
+ x: _x + ox,
574
+ y: brotherPoint.y
575
+ }
576
+ } else {
577
+ globalStore.handle_svg_info.info.props.point_position.val[l - 1] = {
578
+ x: _x + ox,
579
+ y: _y + oy
580
+ }
388
581
  }
389
- // console.log('连线', start_x, start_y, end_x, end_y, clientX, clientY);
390
582
  } else if (globalStore.intention === EGlobalStoreIntention.SetConnectionLineNode && globalStore.handle_svg_info) {
391
583
  globalStore.handle_svg_info.info.props.point_position.val[globalStore.connection_line_node_info.point_index] = {
392
584
  x: getSvgNowPosition(
393
585
  globalStore.mouse_info.position_x,
394
- clientX,
586
+ globalStore.mouse_info.new_position_x,
395
587
  globalStore.connection_line_node_info.init_pos.x
396
588
  ),
397
589
  y: getSvgNowPosition(
398
590
  globalStore.mouse_info.position_y,
399
- clientY,
591
+ globalStore.mouse_info.new_position_y,
400
592
  globalStore.connection_line_node_info.init_pos.y
401
593
  )
402
594
  }
@@ -408,14 +600,16 @@
408
600
  return
409
601
  }
410
602
  if (globalStore.handle_svg_info?.info && globalStore.intention == EGlobalStoreIntention.Move) {
411
- globalStore.done_json[globalStore.handle_svg_info.index].x = globalStore.mouse_info.new_position_x
412
- globalStore.done_json[globalStore.handle_svg_info.index].y = globalStore.mouse_info.new_position_y
413
- // globalStore.setDoneJson(globalStore.done_json);
414
603
  setSvgActualInfo(globalStore.done_json[globalStore.handle_svg_info.index])
415
604
  globalStore.intention = EGlobalStoreIntention.None
416
605
  //记录历史记录
417
606
  globalStore.setDoneJson(globalStore.done_json)
418
- // globalStore.setHandleSvgInfo(undefined, 0);
607
+ } else if (globalStore.intention == EGlobalStoreIntention.GroupMove) {
608
+ if (groupMoved) {
609
+ //群组移动了
610
+ groupMoved = false
611
+ globalStore.intention = EGlobalStoreIntention.None
612
+ }
419
613
  } else if (globalStore.handle_svg_info?.info && globalStore.intention == EGlobalStoreIntention.Zoom) {
420
614
  //缩放完成后重置中点 新版本中点就是组件坐标
421
615
  // const newCenterPoint = getCenterPoint(
@@ -434,12 +628,21 @@
434
628
  setSvgActualInfo(globalStore.done_json[globalStore.handle_svg_info.index])
435
629
  //记录历史记录
436
630
  globalStore.setDoneJson(globalStore.done_json)
631
+ globalStore.intention = EGlobalStoreIntention.None
437
632
  } else if (globalStore.intention === EGlobalStoreIntention.Connection) {
438
633
  return
634
+ } else if (globalStore.intention === EGlobalStoreIntention.SelectArea) {
635
+ //框选
636
+ globalStore.setHandleSvgInfo(null)
637
+ for (let e of globalStore.done_json) {
638
+ const t = selectRect.value
639
+ e.selected = e.x > t.x && e.x < t.x + t.with && e.y > t.y && e.y < t.y + t.height
640
+ }
641
+ globalStore.intention = EGlobalStoreIntention.None
439
642
  } else if (globalStore.intention != EGlobalStoreIntention.Select) {
440
643
  globalStore.intention = EGlobalStoreIntention.None
441
644
  }
442
- globalStore.setMouseInfo({
645
+ globalStore.mouse_info = {
443
646
  state: EMouseInfoState.Up,
444
647
  position_x: 0,
445
648
  position_y: 0,
@@ -447,46 +650,102 @@
447
650
  now_position_y: 0,
448
651
  new_position_x: 0,
449
652
  new_position_y: 0
450
- })
653
+ }
451
654
  contextMenuStore.display = false
452
655
  }
453
656
  const onCanvasMouseDown = (e: MouseEvent) => {
657
+ //todo 画横线或垂线
454
658
  const { clientX, clientY } = e
455
- if (globalStore.intention === EGlobalStoreIntention.Connection && globalStore.handle_svg_info) {
456
- if (e.button === 0) {
457
- //鼠标左键创建新线段
458
- globalStore.handle_svg_info.info.props.point_position.val.push({
459
- x: getSvgNowPosition(
460
- globalStore.mouse_info.position_x,
461
- clientX,
462
- globalStore.handle_svg_info?.info.props.point_position.val[0].x
463
- ),
464
- y: getSvgNowPosition(
465
- globalStore.mouse_info.position_y,
466
- clientY,
467
- globalStore.handle_svg_info?.info.props.point_position.val[0].y
468
- )
469
- })
659
+ if (globalStore.intention === EGlobalStoreIntention.Connection) {
660
+ if (!globalStore.handle_svg_info) {
661
+ //空白地方画线
662
+ createLine(e)
470
663
  }
471
- if (e.button === 2) {
472
- //鼠标右键结束线段绘制
473
- globalStore.intention = EGlobalStoreIntention.None
474
- setSvgActualInfo(globalStore.done_json[globalStore.handle_svg_info.index])
664
+ if (globalStore.handle_svg_info) {
665
+ if (e.button === 0) {
666
+ //鼠标左键创建新线段
667
+ if (globalStore.handle_svg_info.info.props.point_position.val.length !== 1) {
668
+ //鼠标移动后的实时位置(相对于起始点,只在创建第一个点时记录了鼠标原始位置)
669
+ globalStore.handle_svg_info.info.props.point_position.val.push({
670
+ x:
671
+ globalStore.mouse_info.new_position_x -
672
+ globalStore.mouse_info.position_x -
673
+ svgEditLayoutStore.center_offset.x,
674
+ y:
675
+ globalStore.mouse_info.new_position_y -
676
+ globalStore.mouse_info.position_y -
677
+ svgEditLayoutStore.center_offset.y
678
+ })
679
+ } else {
680
+ //第二个点,在鼠标没有移动的情况下,就是起始点
681
+ globalStore.handle_svg_info.info.props.point_position.val.push({
682
+ x: 0,
683
+ y: 0
684
+ })
685
+ }
686
+ } else if (e.button === 2) {
687
+ //鼠标右键结束线段绘制
688
+ globalStore.intention = EGlobalStoreIntention.None
689
+ setSvgActualInfo(globalStore.done_json[globalStore.handle_svg_info.index])
690
+ globalStore.setHandleSvgInfo(null)
691
+ }
475
692
  }
476
693
  return
477
694
  }
478
- //点击画布 未选中组件 拖动画布
479
- globalStore.intention = EGlobalStoreIntention.MoveCanvas
480
- globalStore.setMouseInfo({
481
- state: EMouseInfoState.Down,
482
- position_x: clientX,
483
- position_y: clientY,
484
- now_position_x: svgEditLayoutStore.center_offset.x,
485
- now_position_y: svgEditLayoutStore.center_offset.y,
486
- new_position_x: svgEditLayoutStore.center_offset.x,
487
- new_position_y: svgEditLayoutStore.center_offset.y
488
- })
695
+ if (e.button === 0) {
696
+ //左键点击画布 未选中组件 框选
697
+ globalStore.intention = EGlobalStoreIntention.SelectArea
698
+
699
+ globalStore.mouse_info = {
700
+ state: EMouseInfoState.Down,
701
+ position_x: Math.round(
702
+ (clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale - svgEditLayoutStore.center_offset.x
703
+ ),
704
+ position_y: Math.round(
705
+ (clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale - svgEditLayoutStore.center_offset.y
706
+ ),
707
+ now_position_x: 0,
708
+ now_position_y: 0,
709
+ new_position_x: 0,
710
+ new_position_y: 0
711
+ }
712
+
713
+ area.value = {
714
+ a: {
715
+ x: globalStore.mouse_info.position_x,
716
+ y: globalStore.mouse_info.position_y
717
+ },
718
+ b: {
719
+ x: 0,
720
+ y: 0
721
+ }
722
+ }
723
+ } else if (e.button === 2) {
724
+ //右键点击画布 未选中组件 拖动画布
725
+ globalStore.intention = EGlobalStoreIntention.MoveCanvas
726
+
727
+ globalStore.mouse_info = {
728
+ state: EMouseInfoState.Down,
729
+ position_x: Math.round((clientX - svgEditLayoutStore.canvasInfo.left) / configStore.svg.scale),
730
+ position_y: Math.round((clientY - svgEditLayoutStore.canvasInfo.top) / configStore.svg.scale),
731
+ now_position_x: svgEditLayoutStore.center_offset.x,
732
+ now_position_y: svgEditLayoutStore.center_offset.y,
733
+ new_position_x: 0,
734
+ new_position_y: 0
735
+ }
736
+ }
489
737
  }
738
+
739
+ function onMousewheel(e: any) {
740
+ if (e?.wheelDelta) {
741
+ if (e.wheelDelta > 0) {
742
+ configStore.svg.scale = parseFloat((configStore.svg.scale + 0.1).toFixed(1))
743
+ } else {
744
+ configStore.svg.scale = parseFloat((configStore.svg.scale - 0.1).toFixed(1))
745
+ }
746
+ }
747
+ }
748
+
490
749
  /**
491
750
  * 鼠标右键事件
492
751
  * @param e
@@ -497,7 +756,6 @@
497
756
  e.preventDefault() //禁用浏览器右键
498
757
  }
499
758
  const onSvgContextMenuEvent = (select_item: IDoneJson, index: number, e: MouseEvent) => {
500
- console.log(globalStore.intention)
501
759
  if (globalStore.intention === EGlobalStoreIntention.Connection) {
502
760
  return
503
761
  }
@@ -532,26 +790,62 @@
532
790
  scale_y: number
533
791
  ) => {
534
792
  return {
535
- x: actual_bound.x - (actual_bound.width / 2) * scale_x + actual_bound.width / 2,
536
- y: actual_bound.y - (actual_bound.height / 2) * scale_y + actual_bound.height / 2,
537
- width: actual_bound.width * scale_x,
538
- height: actual_bound.height * scale_y
793
+ x: parseFloat((actual_bound.x - (actual_bound.width / 2) * scale_x + actual_bound.width / 2).toFixed(1)),
794
+ y: parseFloat((actual_bound.y - (actual_bound.height / 2) * scale_y + actual_bound.height / 2).toFixed(1)),
795
+ width: parseFloat((actual_bound.width * scale_x).toFixed(1)),
796
+ height: parseFloat((actual_bound.height * scale_y).toFixed(1))
539
797
  }
540
798
  }
541
799
  const onHandleKeyDown = (e: KeyboardEvent) => {
542
- console.log(e, 733)
543
800
  e.preventDefault()
544
- if (globalStore.handle_svg_info && !e.ctrlKey && e.key == 'ArrowUp') {
545
- globalStore.done_json[globalStore.handle_svg_info.index].y -= 1
801
+ let mGroup: number[] = []
802
+ if (!e.ctrlKey && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].indexOf(e.key) > -1) {
803
+ if (isGroup.value) {
804
+ //群体移动
805
+ mGroup = globalStore.done_json
806
+ .map((e, i) => ({
807
+ selected: e.selected,
808
+ i: i
809
+ }))
810
+ .filter((e) => e.selected)
811
+ .map((e) => e.i)
812
+ } else {
813
+ if (globalStore.handle_svg_info) {
814
+ mGroup.push(globalStore.handle_svg_info.index)
815
+ }
816
+ }
817
+ }
818
+ if (!e.ctrlKey && e.key == 'ArrowUp') {
819
+ for (let i of mGroup) {
820
+ globalStore.done_json[i].y -= 1
821
+ globalStore.done_json[i].client.y -= 1
822
+ moveHandlePoint(globalStore.done_json[i], 0, -1)
823
+ moveAnchors(globalStore.done_json[i])
824
+ }
546
825
  useHistoryRecord(globalStore.done_json)
547
- } else if (globalStore.handle_svg_info && !e.ctrlKey && e.key == 'ArrowDown') {
548
- globalStore.handle_svg_info.info.y += 1
826
+ } else if (!e.ctrlKey && e.key == 'ArrowDown') {
827
+ for (let i of mGroup) {
828
+ globalStore.done_json[i].y += 1
829
+ globalStore.done_json[i].client.y += 1
830
+ moveHandlePoint(globalStore.done_json[i], 0, 1)
831
+ moveAnchors(globalStore.done_json[i])
832
+ }
549
833
  useHistoryRecord(globalStore.done_json)
550
- } else if (globalStore.handle_svg_info && !e.ctrlKey && e.key == 'ArrowLeft') {
551
- globalStore.handle_svg_info.info.x -= 1
834
+ } else if (!e.ctrlKey && e.key == 'ArrowLeft') {
835
+ for (let i of mGroup) {
836
+ globalStore.done_json[i].x -= 1
837
+ globalStore.done_json[i].client.x -= 1
838
+ moveHandlePoint(globalStore.done_json[i], -1, 0)
839
+ moveAnchors(globalStore.done_json[i])
840
+ }
552
841
  useHistoryRecord(globalStore.done_json)
553
- } else if (globalStore.handle_svg_info && !e.ctrlKey && e.key == 'ArrowRight') {
554
- globalStore.handle_svg_info.info.x += 1
842
+ } else if (!e.ctrlKey && e.key == 'ArrowRight') {
843
+ for (let i of mGroup) {
844
+ globalStore.done_json[i].x += 1
845
+ globalStore.done_json[i].client.x += 1
846
+ moveHandlePoint(globalStore.done_json[i], 1, 0)
847
+ moveAnchors(globalStore.done_json[i])
848
+ }
555
849
  useHistoryRecord(globalStore.done_json)
556
850
  }
557
851
  //ctrl c
@@ -560,7 +854,16 @@
560
854
  }
561
855
  //deleted
562
856
  else if (!e.ctrlKey && e.key == 'Delete') {
563
- contextMenuStore.onContextMenuClick(EContextMenuInfoType.Delete)
857
+ if (isGroup.value) {
858
+ //有框选组件,批量删除
859
+ globalStore.setDoneJson(globalStore.done_json.filter((e) => !e.selected))
860
+ globalStore.setHandleSvgInfo(
861
+ globalStore.done_json[globalStore.done_json.length - 1],
862
+ globalStore.done_json.length - 1
863
+ )
864
+ } else {
865
+ contextMenuStore.onContextMenuClick(EContextMenuInfoType.Delete)
866
+ }
564
867
  }
565
868
  //上移一层
566
869
  else if (e.ctrlKey && e.key == 'ArrowUp') {
@@ -598,6 +901,15 @@
598
901
 
599
902
  onMounted(() => {
600
903
  canvasRef.value?.focus()
904
+ nextTick(function () {
905
+ const d = canvasRef.value?.getBoundingClientRect()
906
+ svgEditLayoutStore.canvasInfo = {
907
+ height: d?.height || 0,
908
+ with: d?.width || 0,
909
+ top: d?.top || 0,
910
+ left: d?.left || 0
911
+ }
912
+ })
601
913
  })
602
914
  </script>
603
915
 
@@ -614,6 +926,7 @@
614
926
  @mouseup="onCanvasMouseUp"
615
927
  @contextmenu="onCanvasContextMenuEvent"
616
928
  @keydown="onHandleKeyDown"
929
+ @mousewheel="onMousewheel"
617
930
  >
618
931
  <svg
619
932
  xmlns="http://www.w3.org/2000/svg"
@@ -622,14 +935,17 @@
622
935
  height="100%"
623
936
  >
624
937
  <defs>
625
- <pattern id="pattern_grid" patternUnits="userSpaceOnUse" x="0" y="0" width="10" height="10">
626
- <rect width="1" height="1" rx="1" ry="1" fill="#aaaaaa" />
938
+ <pattern id="pattern_grid" patternUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
939
+ <line x1="0" y1="0" x2="0" y2="20" :stroke="configStore.svg.grid_color" />
940
+ <line x1="10" y1="0" x2="10" y2="20" :stroke="configStore.svg.grid_color" />
941
+ <line x1="0" y1="0" x2="20" y2="0" :stroke="configStore.svg.grid_color" />
942
+ <line x1="0" y1="10" x2="20" y2="10" :stroke="configStore.svg.grid_color" />
627
943
  </pattern>
628
944
  </defs>
629
945
  <rect v-if="configStore.svg.grid" width="100%" height="100%" fill="url(#pattern_grid)" />
630
946
  <g
631
- :transform="`translate(${configStore.svg.position_center.x + svgEditLayoutStore.center_offset.x},${
632
- configStore.svg.position_center.y + svgEditLayoutStore.center_offset.y
947
+ :transform="`translate(${svgEditLayoutStore.center_offset.x * configStore.svg.scale},${
948
+ svgEditLayoutStore.center_offset.y * configStore.svg.scale
633
949
  })rotate(${0})scale(${configStore.svg.scale})`"
634
950
  >
635
951
  <g
@@ -647,6 +963,7 @@
647
963
  item.actual_bound.height / 2
648
964
  )})`"
649
965
  @mousedown="onSvgMouseDown(item, index, $event)"
966
+ @mouseup="onSvgMouseUp(item, index, $event)"
650
967
  @mouseenter="onSvgMouseEnter(item, index, $event)"
651
968
  @mouseleave="onSvgMouseLeave(item, index, $event)"
652
969
  @contextmenu="onSvgContextMenuEvent(item, index, $event)"
@@ -669,7 +986,7 @@
669
986
  item.actual_bound.x +
670
987
  item.actual_bound.width / 2
671
988
  )},${-(item.actual_bound.y + item.actual_bound.height / 2)})`"
672
- ></use>
989
+ />
673
990
  <component
674
991
  v-else-if="item.type === EDoneJsonType.CustomSvg"
675
992
  :is="item.tag"
@@ -722,24 +1039,21 @@
722
1039
  fill-opacity="0"
723
1040
  v-bind="getActualBoundScale(item.actual_bound, item.scale_x, item.scale_y)"
724
1041
  style="stroke: none; stroke-width: 2; stroke-miterlimit: 10"
725
- :class="`${
726
- globalStore.intention == EGlobalStoreIntention.None ||
727
- globalStore.intention == EGlobalStoreIntention.Select
728
- ? 'svg-item-none'
729
- : ''
730
- }
731
- ${
732
- globalStore.intention == EGlobalStoreIntention.Move &&
733
- globalStore.handle_svg_info?.info.id == item.id
734
- ? 'svg-item-move'
735
- : ''
736
- } ${
737
- globalStore.intention == EGlobalStoreIntention.Select &&
738
- globalStore.handle_svg_info?.info.id == item.id
739
- ? 'svg-item-select'
740
- : ''
741
- }`"
1042
+ :class="{
1043
+ 'svg-item-none':
1044
+ !item.selected &&
1045
+ (globalStore.intention == EGlobalStoreIntention.None ||
1046
+ globalStore.intention == EGlobalStoreIntention.Select),
1047
+ 'svg-item-move':
1048
+ globalStore.intention == EGlobalStoreIntention.Move &&
1049
+ globalStore.handle_svg_info?.info.id == item.id,
1050
+ 'svg-item-select':
1051
+ globalStore.intention == EGlobalStoreIntention.Select &&
1052
+ globalStore.handle_svg_info?.info.id == item.id,
1053
+ 'svg-item-in-group': item.selected
1054
+ }"
742
1055
  />
1056
+
743
1057
  <handle-panel
744
1058
  v-if="
745
1059
  globalStore.handle_svg_info?.info.id === item.id && visible_info.handle_panel && item.config.can_zoom
@@ -747,10 +1061,13 @@
747
1061
  :item-info="item"
748
1062
  />
749
1063
  <connection-panel
1064
+ @contextmenu="onCanvasContextMenuEvent"
750
1065
  v-if="
1066
+ !item.selected &&
751
1067
  visible_info.select_item.info?.id == item.id &&
752
1068
  visible_info.connection_panel &&
753
1069
  item.config.have_anchor &&
1070
+ globalStore.intention !== EGlobalStoreIntention.SelectArea &&
754
1071
  (globalStore.intention === EGlobalStoreIntention.Select
755
1072
  ? item.id !== globalStore.handle_svg_info?.info.id
756
1073
  : true)
@@ -760,6 +1077,16 @@
760
1077
  </g>
761
1078
  </g>
762
1079
  </g>
1080
+ <!--框选-->
1081
+ <rect
1082
+ :x="selectRect.x"
1083
+ :y="selectRect.y"
1084
+ :height="selectRect.height"
1085
+ :width="selectRect.with"
1086
+ stroke="#107cec"
1087
+ fill="#107cec"
1088
+ fill-opacity=".2"
1089
+ />
763
1090
  </g>
764
1091
  </svg>
765
1092
  <!-- 右键菜单 -->
@@ -793,18 +1120,23 @@
793
1120
  cursor: move;
794
1121
 
795
1122
  &:hover {
796
- outline: 1px solid #0cf;
1123
+ outline: 1px solid #f8d032;
797
1124
  }
798
1125
  }
799
1126
 
800
1127
  .svg-item-move {
801
1128
  cursor: move;
802
- outline: 1px dashed rgb(23, 222, 30);
1129
+ outline: 1px dashed rgb(222, 123, 23);
803
1130
  }
804
1131
 
805
1132
  .svg-item-select {
806
1133
  cursor: move;
807
- outline: 1px solid rgb(23, 222, 30);
1134
+ outline: 1px solid rgb(222, 123, 23);
1135
+ }
1136
+
1137
+ .svg-item-in-group {
1138
+ //outline: 1px solid rgb(222, 123, 23);
1139
+ outline: 1px solid rgb(222, 69, 23);
808
1140
  }
809
1141
 
810
1142
  .contextMenu {