vue-wiring-diagram 1.0.15 → 1.0.17

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.
@@ -64,7 +64,7 @@
64
64
  </template>
65
65
 
66
66
  <script setup>
67
- import {onMounted, reactive, ref, shallowRef} from "vue";
67
+ import {defineComponent, onMounted, reactive, ref, shallowRef} from "vue";
68
68
  import moment from 'moment'
69
69
  import {ElMessage, ElTooltip} from "element-plus";
70
70
  import {getData, showPorts} from "../common.js";
@@ -75,22 +75,26 @@ import edgeControl from "../edge-control/index.vue";
75
75
  import Shortcuts from "../Shortcuts.vue";
76
76
  import {Graph} from "@antv/x6";
77
77
  import {Stencil} from '@antv/x6-plugin-stencil'
78
- import {Transform} from "@antv/x6-plugin-transform";
79
- import {Snapline} from "@antv/x6-plugin-snapline";
80
- import {Clipboard} from '@antv/x6-plugin-clipboard'
81
- import {Keyboard} from "@antv/x6-plugin-keyboard";
82
- import {Selection} from '@antv/x6-plugin-selection'
83
- import {History} from '@antv/x6-plugin-history'
84
78
  import {Export} from "@antv/x6-plugin-export";
85
79
  import imageControl from "../image-control/index.vue";
86
- import {imageOptions} from "../image-control/imageOptions.js";
87
80
  import {baseShape} from "../baseShape.js";
88
81
  import {portsOptions} from "../portsOptions.js";
89
82
  import {Document, View, ArrowDown} from "@element-plus/icons-vue";
90
83
  import WiringDiagramPreview from "../preview/index.vue";
91
84
  import ImageManagement from "../image-control/image-management.vue";
92
85
  import {get} from "packages/http.js";
93
- import towerPoles from "packages/assets/image/tower-poles.svg";
86
+ import {
87
+ clipboard,
88
+ keyboard,
89
+ snapLine,
90
+ transform,
91
+ history,
92
+ selection,
93
+ setMathRandom,
94
+ deleteCell,
95
+ copyCell,
96
+ pasteCell, undoCell, redoCell, addLineTools, isLineOrPipe
97
+ } from "packages/components/tools.js";
94
98
 
95
99
  defineOptions({
96
100
  name: 'editor'
@@ -106,7 +110,7 @@ const props = defineProps({
106
110
  const graph = ref() // 画布实例
107
111
  // 控制器
108
112
  const controls = reactive({
109
- label: null, // 控制器
113
+ label: '', // 控制器
110
114
  component: shallowRef(null), // 控制器组件
111
115
  })
112
116
  const controlsKey = ref(null) // 控制器key
@@ -117,14 +121,15 @@ const payload = ref(null)
117
121
  */
118
122
  const initGraph = () => {
119
123
  const container = document.getElementById('drawing-board')
120
- window.__x6_instances__ = []
121
124
  graph.value = new Graph({
122
125
  container: container,
123
126
  autoResize: true,
124
127
  panning: true,
125
128
  mousewheel: true,
126
129
  connecting: {
130
+ snap: true,
127
131
  allowEdge: true,
132
+ highlight: true,
128
133
  router: 'orth',
129
134
  connectionPoint: {
130
135
  name: 'anchor',
@@ -133,97 +138,88 @@ const initGraph = () => {
133
138
  createEdge() {
134
139
  return graph.value.createEdge({
135
140
  inherit: 'edge',
136
- attrs: {
137
- ...lineOptions,
141
+ ...lineOptions,
142
+ data: {
143
+ type: isLineOrPipe(),
138
144
  },
139
145
  zIndex: 0,
140
146
  })
141
147
  }
142
- }
148
+ },
149
+ })
150
+ graph.value.use(transform())
151
+ graph.value.use(keyboard())
152
+ graph.value.use(snapLine())
153
+ graph.value.use(clipboard())
154
+ graph.value.use(history())
155
+ graph.value.use(selection())
156
+ graph.value.bindKey(['delete', 'backspace'], () => {
157
+ deleteCell(graph.value)
158
+ })
159
+ graph.value.bindKey(['ctrl+c', 'cmd + c'], () => {
160
+ copyCell(graph.value)
161
+ })
162
+ graph.value.bindKey(['ctrl+v', 'cmd + v'], () => {
163
+ pasteCell(graph.value)
164
+ })
165
+ graph.value.bindKey(['ctrl+z', 'cmd + z'], () => {
166
+ undoCell(graph.value)
167
+ })
168
+ graph.value.bindKey(['ctrl+y', 'cmd + y'], () => {
169
+ redoCell(graph.value)
143
170
  })
144
- window.__x6_instances__.push(graph.value)
145
-
146
- graph.value.use(
147
- new Transform({
148
- resizing: {
149
- enabled: true,
150
- preserveAspectRatio: false
151
- },
152
- rotating: true,
153
- })
154
- )
155
171
 
156
- graph.value.use(new Keyboard())
157
- graph.value.use(new Snapline())
158
- graph.value.use(new Clipboard({
159
- format(key) {
160
- return key.replace(/\s/g, '').replace('cmd', 'command')
161
- },
162
- }))
163
- graph.value.use(new History())
164
- graph.value.use(new Selection({
165
- enabled: true,
166
- multiple: true, //多选
167
- rubberband: true, //框选节点
168
- modifiers: ['alt'],
169
- showNodeSelectionBox: true,
170
- }))
172
+ let setZIndexTimer = null
171
173
 
172
- graph.value.bindKey(['delete', 'backspace'], () => {
173
- const cells = graph.value.getSelectedCells()
174
- if (cells.length) {
175
- graph.value.removeCells(cells)
176
- }
177
- return false
174
+ graph.value.on('edge:click', ({cell}) => {
175
+ payload.value = cell
176
+ controls.component = edgeControl
177
+ controls.label = '线段属性'
178
+ controlsKey.value = setMathRandom()
178
179
  })
179
180
 
180
- graph.value.bindKey(['ctrl+c', 'cmd + c'], () => {
181
- const cells = graph.value.getSelectedCells()
182
- if (cells.length) {
183
- graph.value.copy(cells)
184
- ElMessage.success('复制成功')
185
- }
186
- return false
181
+ //给线添加拐点
182
+ graph.value.on('edge:mouseenter', ({cell}) => {
183
+ clearTimeout(setZIndexTimer)
184
+ cell.toFront()
185
+ addLineTools(cell)
187
186
  })
188
187
 
189
- graph.value.bindKey(['ctrl+v', 'cmd + v'], () => {
190
- if (!graph.value.isClipboardEmpty()) {
191
- const cells = graph.value.paste({offset: 32})
192
- graph.value.cleanSelection()
193
- graph.value.select(cells)
194
- ElMessage.success('粘贴成功')
195
- }
196
- return false
188
+ // 隐藏线段的拐点
189
+ graph.value.on('edge:mouseleave', ({cell}) => {
190
+ setZIndexTimer = setTimeout(() => {
191
+ cell.toBack()
192
+ cell.removeTools()
193
+ }, 300)
194
+
197
195
  })
198
196
 
199
- graph.value.bindKey(['ctrl+z', 'cmd + z'], () => {
200
- if (graph.value.canUndo()) {
201
- graph.value.undo()
202
- }
203
- return false
197
+ //连接桩显示
198
+ graph.value.on('node:mouseenter', () => {
199
+ showPorts('#drawing-board', true)
204
200
  })
205
201
 
206
- graph.value.bindKey(['ctrl+y', 'cmd + y'], () => {
207
- if (graph.value.canRedo()) {
208
- graph.value.redo()
209
- }
210
- return false
202
+ //连接桩隐藏
203
+ graph.value.on('node:mouseleave', () => {
204
+ showPorts("#drawing-board", false)
211
205
  })
212
206
 
207
+ // 导出插件
208
+ graph.value.use(new Export())
209
+
213
210
  //选中画布
214
211
  graph.value.on('blank:click', ({e, x, y}) => {
215
212
  payload.value = graph
216
213
  controls.component = GraphControl
217
214
  controls.label = '画布属性'
218
- controlsKey.value = moment().valueOf()
215
+ controlsKey.value = setMathRandom()
219
216
  })
220
217
 
221
218
 
222
219
  //选中节点
223
220
  graph.value.on('cell:click', ({cell}) => {
224
- console.log('cell:click', cell, cell?.data?.type ?? cell?.shape)
225
221
  payload.value = cell
226
- controlsKey.value = moment().valueOf()
222
+ controlsKey.value = setMathRandom()
227
223
  if (cell.data?.type === 'text') {
228
224
  controls.component = textControl
229
225
  controls.label = '文本属性'
@@ -247,43 +243,6 @@ const initGraph = () => {
247
243
  controls.component = null
248
244
  controls.label = null
249
245
  })
250
-
251
- //给线添加拐点
252
- graph.value.on('edge:mouseenter', ({cell}) => {
253
- cell.addTools([
254
- {
255
- name: 'vertices',
256
- args: {
257
- snapRadius: 3,
258
- attrs: {
259
- r: 3,
260
- fill: '#239edd',
261
- cursor: 'move',
262
- 'stroke-width': 0
263
- },
264
- modifiers: ['alt']
265
- },
266
- },
267
- ]);
268
- })
269
-
270
- // 隐藏线段的拐点
271
- graph.value.on('edge:mouseleave', ({cell}) => {
272
- cell.removeTools()
273
- })
274
-
275
- //连接桩显示
276
- graph.value.on('node:mouseenter', () => {
277
- showPorts('#drawing-board', true)
278
- })
279
-
280
- //连接桩隐藏
281
- graph.value.on('node:mouseleave', () => {
282
- showPorts("#drawing-board", false)
283
- })
284
-
285
- // 导出插件
286
- graph.value.use(new Export())
287
246
  }
288
247
 
289
248
  const stencil = ref()
@@ -344,7 +303,6 @@ const initStencil = () => {
344
303
  document.getElementById('stencil').appendChild(stencil.value.container)
345
304
  })
346
305
  }).finally(() => {
347
- // 添加 hover 事件监听器
348
306
  setTimeout(() => {
349
307
  const nodes = stencil.value.container.querySelectorAll('.x6-node');
350
308
  nodes.forEach((node, index) => {
@@ -359,7 +317,6 @@ const initStencil = () => {
359
317
  text.style.fill = '#ffffff';
360
318
  }
361
319
  }
362
- // 添加 hover 事件监听器
363
320
  node.addEventListener('mouseenter', (e) => {
364
321
  if (index > baseShape.length) {
365
322
  const text = node.querySelector('text');
@@ -377,7 +334,7 @@ const initStencil = () => {
377
334
  }
378
335
  })
379
336
  });
380
- }, 20)
337
+ }, 200)
381
338
  })
382
339
  }
383
340
 
@@ -442,9 +399,9 @@ const drawLine = () => {
442
399
  edge.value = graph.value.addEdge({
443
400
  source: {x: x, y: y},
444
401
  target: {x: x, y: y},
445
- attrs: lineOptions,
402
+ ...lineOptions,
446
403
  data: {
447
- type: 'edge',
404
+ type: isLineOrPipe(),
448
405
  isConnector: false,
449
406
  },
450
407
  zIndex: 0,
@@ -470,7 +427,6 @@ const drawLine = () => {
470
427
  */
471
428
  const adaptive = () => {
472
429
  graph.value.zoomToFit({maxScale: 1, padding: 20})
473
- // ElMessage.success('自适应成功')
474
430
  }
475
431
 
476
432
  const dialog = reactive({show: false}) // 弹窗
@@ -582,3 +538,5 @@ onMounted(() => {
582
538
  <style lang="scss" scoped>
583
539
  @use "../../styles/editor";
584
540
  </style>
541
+
542
+ <style></style>
@@ -0,0 +1,60 @@
1
+ // 线类型
2
+ export const typeList = [{
3
+ label: '直线',
4
+ value: 'line',
5
+ }, {
6
+ label: '管道',
7
+ value: 'pipe',
8
+ }]
9
+
10
+ // 箭头类型
11
+ export const arrowList = [
12
+ {
13
+ label: 'Block',
14
+ value: 'block'
15
+ },
16
+ {
17
+ label: 'Classic',
18
+ value: 'classic'
19
+ },
20
+ {
21
+ label: 'Diamond',
22
+ value: 'diamond'
23
+ },
24
+ {
25
+ label: 'Cross',
26
+ value: 'cross'
27
+ },
28
+ {
29
+ label: 'Async',
30
+ value: 'async'
31
+ },
32
+ {
33
+ label: 'Circle',
34
+ value: 'circle'
35
+ },
36
+ {
37
+ label: 'CirclePlus',
38
+ value: 'circlePlus'
39
+ },
40
+ {
41
+ label: 'Ellipse',
42
+ value: 'ellipse'
43
+ },
44
+ ]
45
+
46
+ export const STOP = 'stop'
47
+ export const FORWARD_PIPE = 'forward-pipe'
48
+ export const REVERSE_PIPE = 'reverse-pipe'
49
+
50
+ // 管道方向
51
+ export const directionList = [{
52
+ label: '停止',
53
+ value: STOP,
54
+ }, {
55
+ label: '正向',
56
+ value: FORWARD_PIPE ,
57
+ }, {
58
+ label: '反向',
59
+ value: REVERSE_PIPE,
60
+ }]
@@ -76,7 +76,6 @@ import {ref} from "vue";
76
76
  const props = defineProps({
77
77
  payload: Object
78
78
  })
79
- console.log(props.payload.value.options.grid)
80
79
  const color = ref(props.payload.value.options.background?.color || null) // 画布背景颜色
81
80
  const visible = ref(props.payload.value.options?.grid?.visible || null) // 网格是否显示
82
81
  const size = ref(props.payload.value.options?.grid?.size || 10) // 网格大小
@@ -12,6 +12,7 @@ import {onMounted, onUnmounted, ref, shallowRef} from "vue";
12
12
  import {Graph} from "@antv/x6";
13
13
  import {getData, getRealData, showPorts} from "../common.js";
14
14
  import {ElMessage} from "element-plus";
15
+ import {defaultMatch, defaultText, setPipeDirection} from "packages/components/tools.js";
15
16
 
16
17
  defineOptions({
17
18
  name: 'wiring-diagram-preview'
@@ -42,9 +43,6 @@ const props = defineProps({
42
43
  }
43
44
  })
44
45
 
45
- // 所有字段
46
- const allFields = ref([])
47
-
48
46
  // 画布
49
47
  const graph = ref()
50
48
 
@@ -94,7 +92,7 @@ const renderData = async () => {
94
92
  if (props.id) {
95
93
  const {background: bg, grid: gd, data: dt} = await getData(props.id);
96
94
  background = bg;
97
- grid = {};
95
+ grid = gd;
98
96
  data = dt;
99
97
  } else {
100
98
  background = props.background;
@@ -110,30 +108,27 @@ const renderData = async () => {
110
108
 
111
109
  // 定时器
112
110
  const timer = ref(null)
113
- // 获取所有节点中 data.type 为 text 的节点
114
- const textNodes = ref([])
111
+ // 节点
112
+ const cells = ref([])
113
+ // 参数
114
+ const allFields = ref([])
115
115
 
116
116
  /**
117
117
  * 刷新数据
118
118
  */
119
119
  const refreshData = () => {
120
- textNodes.value = getNodes()
121
- const regex = /\$\{(.*?)\}/g;
120
+ cells.value = graph.value.getCells().filter(cell => cell?.data?.type === 'text' || cell?.data?.type === 'pipe')
121
+ cells.value.forEach(item => {
122
+ if (item?.data?.fields?.length) {
123
+ item.data.fields.forEach(field => {
124
+ if (field.field) {
125
+ allFields.value.push({id: item?.id, fields: item?.data.fields})
126
+ }
127
+ })
122
128
 
123
- textNodes.value.forEach(node => {
124
- // 确保 node.data 和 node.data.field 存在
125
- if (!node.data || !node.data.field) return;
126
-
127
- const matches = node.data.content.match(regex);
128
- allFields.value.push(...node.data.field);
129
-
130
- if (matches) {
131
- node.matches = matches;
132
129
  }
133
- });
134
- if (!textNodes) {
135
- return
136
- }
130
+ })
131
+ console.log('所有字段', allFields.value)
137
132
  setFieldData()
138
133
  }
139
134
 
@@ -160,61 +155,69 @@ const setFieldData = async () => {
160
155
 
161
156
  isAllowRefresh.value = false
162
157
 
163
- const realData = await getRealData(allFields.value)
158
+ let realData = await getRealData(allFields.value).catch(() => {
159
+ isAllowRefresh.value = true
160
+ })
164
161
 
165
162
  isAllowRefresh.value = true
166
163
 
167
- if (!Array.isArray(realData) || !realData.length) {
168
- return
169
- }
170
-
171
- let length = 0
172
- textNodes.value.forEach((node, index) => {
173
- // 确保 node.data 和 node.data.field 存在
174
- if (!node.data || !node.data.field) return;
175
-
176
- const matches = node.matches;
177
- if (matches) {
178
- const start = index === 0 ? 0 : length || 0;
179
- const end = length = start + node.data.field.length;
180
- node.label = replaceAllTemplateLiteralsWithArray(node.data.content, realData.slice(start, end));
164
+ realData?.forEach(item => {
165
+ const cell = graph.value.getCellById(item.id)
166
+ if (cell.data.type === 'text') {
167
+ cell.label = replaceAllTemplateLiteralsWithArray(cell.data.content, item.fields)
181
168
  }
182
- });
169
+ if (cell.data.type === 'pipe') {
170
+ console.log('管道详情', cell, item.fields?.[0].value)
171
+ setPipe(cell, item.fields?.[0].value)
172
+ // TODO: 暂时不支持管道
173
+ }
174
+ })
183
175
  timer.value = setTimeout(() => {
184
176
  setFieldData()
185
177
  }, 10000)
186
178
  }
187
179
 
188
-
189
180
  /**
190
- * 获取所有节点中 data.type 为 text 的节点
181
+ * 替换模板字符串
182
+ * @param str 模板字符串
183
+ * @param fields 替换数组
184
+ * @returns {*}
191
185
  */
192
- const getNodes = () => {
193
- const nodes = graph.value.getNodes()
194
- return nodes.filter(node => node.data?.type === 'text')
186
+ const replaceAllTemplateLiteralsWithArray = (str, fields) => {
187
+ let index = 0
188
+ return str.replace(defaultMatch, (match, p1) => {
189
+ const field = fields.find(item => item.index === index)
190
+ index++
191
+ if (field) {
192
+ return field.value
193
+ } else {
194
+ return defaultText
195
+ }
196
+ })
195
197
  }
196
198
 
197
199
  /**
198
- * 替换模板字符串
199
- * @param str 模板字符串
200
- * @param replacements 替换数组
201
- * @returns {*}
200
+ * 设置管道
201
+ * @param cell
202
+ * @param value
202
203
  */
203
- const replaceAllTemplateLiteralsWithArray = (str, replacements) => {
204
- let index = 0;
205
- return str.replace(/\$\{.*?\}/g, () => {
206
- if (index < replacements.length) {
207
- return replacements[index++];
204
+ const setPipe = (cell, value) => {
205
+ if(!cell.data.isCondition) {
206
+ return false
207
+ }
208
+ for (let i = 0; i < cell.data.condition.length; i++) {
209
+ if (value > cell.data.condition[i].min && value <= cell.data.condition[i].max) {
210
+ cell.attr('line2/style/animation', setPipeDirection(cell.data.condition[i].direction))
211
+ return false
208
212
  }
209
- return '--'; // 如果替换数组长度不够,返回空字符串或其他默认值
210
- });
213
+ }
211
214
  }
212
215
 
213
216
  /**
214
217
  * \@description 画布自适应
215
218
  */
216
219
  const zoomToFit = () => {
217
- graph.value.zoomToFit({maxScale: 10, padding: 20})
220
+ graph.value.zoomToFit({maxScale: 1.4, padding: 20})
218
221
  }
219
222
 
220
223
  const previewRef = shallowRef()
@@ -33,6 +33,7 @@ export const stencilOptions = {
33
33
  },
34
34
  }
35
35
 
36
+ // 连接桩配置
36
37
  export const portOption = {
37
38
  position: 'absolute',
38
39
  args: {
@@ -81,7 +82,7 @@ export const textOptions = {
81
82
  data: {
82
83
  type: 'text',
83
84
  content: '文本',
84
- field: []
85
+ fields: []
85
86
  },
86
87
  ports: {
87
88
  groups: {
@@ -93,33 +94,51 @@ export const textOptions = {
93
94
  }
94
95
 
95
96
 
96
- // 基础线配置
97
+ // 线配置
97
98
  export const lineOptions = {
98
- line: {
99
- stroke: '#ffffff',
100
- strokeWidth: 1,
101
- sourceMarker: {
102
- args: {
103
- width: 10,
104
- height: 10,
105
- offset: 0,
106
- },
107
- name: null
108
- }, //取消箭头
109
- targetMarker: {
110
- args: {
111
- width: 10,
112
- height: 10,
113
- offset: 0,
99
+ markup: [
100
+ {
101
+ tagName: 'path',
102
+ selector: 'line1',
103
+ groupSelector: 'lines'
104
+ },
105
+ {
106
+ tagName: 'path',
107
+ selector: 'line2',
108
+ groupSelector: 'lines'
109
+ },
110
+ ],
111
+ attrs: {
112
+ lines: {
113
+ connection: true,
114
+ strokeLinejoin: 'round',
115
+ fill: 'none',
116
+ strokeWidth: 2,
117
+ },
118
+ line1: {
119
+ stroke: '#2364DD',
120
+ strokeWidth: 1,
121
+ },
122
+ line2: {
123
+ stroke: '#ffffff',
124
+ strokeWidth: 1,
125
+ strokeDasharray: 0,
126
+ sourceMarker: {
127
+ args: {
128
+ width: 10,
129
+ height: 10,
130
+ offset: 0,
131
+ },
132
+ name: null
114
133
  },
115
- name: null
116
- }, //取消箭头
117
- },
118
- ports: {
119
- groups: {
120
- ports: {
121
- portOption
134
+ targetMarker: {
135
+ args: {
136
+ width: 10,
137
+ height: 10,
138
+ offset: 0,
139
+ },
140
+ name: null
122
141
  },
123
- }
124
- },
142
+ },
143
+ }
125
144
  }