cnhis-design-vue 2.1.21 → 2.1.22

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.
Files changed (122) hide show
  1. package/CHANGELOG.md +2270 -2270
  2. package/es/affix/index.js +8 -8
  3. package/es/age/index.js +10 -10
  4. package/es/alert/index.js +8 -8
  5. package/es/anchor/index.js +8 -8
  6. package/es/auto-complete/index.js +8 -8
  7. package/es/avatar/index.js +8 -8
  8. package/es/back-top/index.js +8 -8
  9. package/es/badge/index.js +8 -8
  10. package/es/base/index.js +8 -8
  11. package/es/big-table/index.js +205 -195
  12. package/es/big-table/style.css +1 -1
  13. package/es/breadcrumb/index.js +8 -8
  14. package/es/button/index.js +31 -31
  15. package/es/calendar/index.js +8 -8
  16. package/es/captcha/index.js +3 -3
  17. package/es/card/index.js +8 -8
  18. package/es/carousel/index.js +8 -8
  19. package/es/cascader/index.js +8 -8
  20. package/es/checkbox/index.js +9 -9
  21. package/es/col/index.js +8 -8
  22. package/es/collapse/index.js +8 -8
  23. package/es/color-picker/index.js +1 -1
  24. package/es/comment/index.js +8 -8
  25. package/es/config-provider/index.js +8 -8
  26. package/es/date-picker/index.js +8 -8
  27. package/es/descriptions/index.js +8 -8
  28. package/es/divider/index.js +8 -8
  29. package/es/drag-layout/index.js +3 -3
  30. package/es/drawer/index.js +8 -8
  31. package/es/dropdown/index.js +8 -8
  32. package/es/editor/index.js +1 -1
  33. package/es/empty/index.js +8 -8
  34. package/es/fabric-chart/index.js +138 -100
  35. package/es/form/index.js +8 -8
  36. package/es/form-model/index.js +8 -8
  37. package/es/form-table/index.js +62 -62
  38. package/es/index/index.js +787 -739
  39. package/es/index/style.css +1 -1
  40. package/es/input/index.js +9 -9
  41. package/es/input-number/index.js +8 -8
  42. package/es/layout/index.js +8 -8
  43. package/es/list/index.js +8 -8
  44. package/es/locale-provider/index.js +8 -8
  45. package/es/map/index.js +9 -9
  46. package/es/mentions/index.js +8 -8
  47. package/es/menu/index.js +8 -8
  48. package/es/message/index.js +8 -8
  49. package/es/multi-chat/index.js +92 -92
  50. package/es/multi-chat-client/index.js +86 -86
  51. package/es/multi-chat-history/index.js +4 -4
  52. package/es/multi-chat-record/index.js +14 -14
  53. package/es/multi-chat-setting/index.js +27 -27
  54. package/es/multi-chat-sip/index.js +1 -1
  55. package/es/notification/index.js +8 -8
  56. package/es/page-header/index.js +8 -8
  57. package/es/pagination/index.js +8 -8
  58. package/es/popconfirm/index.js +8 -8
  59. package/es/popover/index.js +8 -8
  60. package/es/progress/index.js +8 -8
  61. package/es/radio/index.js +9 -9
  62. package/es/rate/index.js +8 -8
  63. package/es/result/index.js +8 -8
  64. package/es/row/index.js +8 -8
  65. package/es/scale-view/index.js +33 -33
  66. package/es/select/index.js +11 -11
  67. package/es/select-label/index.js +11 -11
  68. package/es/select-person/index.js +20 -20
  69. package/es/skeleton/index.js +8 -8
  70. package/es/slider/index.js +8 -8
  71. package/es/space/index.js +8 -8
  72. package/es/spin/index.js +8 -8
  73. package/es/statistic/index.js +8 -8
  74. package/es/steps/index.js +8 -8
  75. package/es/switch/index.js +8 -8
  76. package/es/table-filter/index.js +142 -142
  77. package/es/tabs/index.js +8 -8
  78. package/es/tag/index.js +9 -9
  79. package/es/time-picker/index.js +8 -8
  80. package/es/timeline/index.js +8 -8
  81. package/es/tooltip/index.js +8 -8
  82. package/es/transfer/index.js +8 -8
  83. package/es/tree/index.js +8 -8
  84. package/es/tree-select/index.js +8 -8
  85. package/es/upload/index.js +8 -8
  86. package/es/verification-code/index.js +2 -2
  87. package/lib/cui.common.js +736 -688
  88. package/lib/cui.umd.js +736 -688
  89. package/lib/cui.umd.min.js +14 -14
  90. package/package.json +107 -107
  91. package/packages/big-table/src/BigTable.vue +3044 -3039
  92. package/packages/big-table/src/assets/style/table-base.less +370 -370
  93. package/packages/big-table/src/components/AutoLayoutButton.vue +270 -270
  94. package/packages/big-table/src/utils/batchEditing.js +610 -610
  95. package/packages/big-table/src/utils/bigTableProps.js +95 -95
  96. package/packages/button/src/ButtonPrint/components/IdentityVerification.vue +181 -181
  97. package/packages/button/src/ButtonPrint/index.vue +728 -728
  98. package/packages/fabric-chart/src/components/TimeScaleValue.vue +113 -113
  99. package/packages/fabric-chart/src/const/defaultVaule.js +59 -59
  100. package/packages/fabric-chart/src/fabric-chart/FabricPolylines.vue +1066 -1055
  101. package/packages/fabric-chart/src/fabric-chart/FabricScaleValue.vue +135 -135
  102. package/packages/fabric-chart/src/fabric-chart/FabricTextGroup.vue +558 -558
  103. package/packages/fabric-chart/src/fabric-chart2/FabricTop.vue +172 -172
  104. package/packages/multi-chat/chat/chatFooter.vue +1594 -1594
  105. package/packages/multi-chat/chat/chatMain.vue +1466 -1466
  106. package/packages/multi-chat/chat/quickReply.vue +439 -439
  107. package/packages/multi-chat/chat/scrollList.vue +1232 -1232
  108. package/packages/multi-chat/setting/baseInfo/index.vue +1316 -1316
  109. package/packages/multi-chat/store/actions.js +448 -448
  110. package/packages/multi-chat/store/state.js +112 -112
  111. package/packages/scale-view/formitem/r-choice.vue +714 -714
  112. package/packages/scale-view/scaleView.vue +2010 -2010
  113. package/packages/select-person/select-person.vue +1658 -1658
  114. package/packages/table-filter/src/base-search-com/BaseSearch.vue +2462 -2462
  115. package/packages/table-filter/src/components/c-tree-select/tree-select.vue +336 -336
  116. package/packages/table-filter/src/components/multi-select/multi-select.vue +219 -219
  117. package/packages/table-filter/src/components/out-quick-search/out-quick-search.vue +340 -340
  118. package/packages/table-filter/src/components/search-condition/SearchCondition.vue +1825 -1825
  119. package/packages/table-filter/src/const/dataOptions.js +43 -43
  120. package/packages/table-filter/src/mixins/mixins.js +695 -695
  121. package/packages/table-filter/src/quick-search/QuickSearch.vue +2109 -2109
  122. package/src/directive/preventReClick.js +12 -12
@@ -1,1055 +1,1066 @@
1
- <template>
2
- <div class="poly-line">
3
- <MouseRightClick v-if="isRightVisible" :getContainer="getContainer" :nodeList="rightClickNode" :rightPos="rightPos" @nodeClick="handleRightClick" @closeRight="isRightVisible = false" />
4
-
5
- <DropPopup v-show="isDropVisible" :val="dropVal" :dropPos="dropPos" />
6
- </div>
7
- </template>
8
-
9
- <script>
10
- import FabricCommon from '../mixins/fabricCommon';
11
- import draw from '../mixins/draw';
12
- import eventCommon from '../mixins/eventCommon';
13
- import MouseRightClick from '../components/MouseRightClick';
14
- import DropPopup from '../components/DropPopup';
15
- import defaultVaule from '../const/defaultVaule';
16
-
17
- const isEffectiveNode = node => {
18
- return node?.time && (node?.value || node?.value === 0);
19
- };
20
- const rightClickNode = [
21
- { name: '新增节点', type: 'add' },
22
- { name: '检测项设置', type: 'set' },
23
- { name: '删除节点', type: 'delete' }
24
- ];
25
- const rightClickNodeConnect = [
26
- { name: '新增节点连接右侧节点', type: 'add' },
27
- { name: '新增节点连接左侧节点', type: 'add' }
28
- ];
29
- export default {
30
- name: 'fabric-polylines',
31
- mixins: [FabricCommon, draw, eventCommon],
32
- inject: ['$propItems'],
33
- props: {
34
- polyline: {
35
- type: Array,
36
- required: true
37
- },
38
- other: {
39
- type: Object,
40
- default: () => {}
41
- }
42
- },
43
- components: {
44
- MouseRightClick,
45
- DropPopup
46
- },
47
- data() {
48
- return {
49
- isDropVisible: false,
50
- dropVal: {},
51
- dropPos: { clientX: 0, clientY: 0 },
52
- isRightVisible: false,
53
- rightPos: { clientX: 0, clientY: 0 },
54
- rightClickNode: [],
55
- activeEvent: null, // 当前可右键活动的对象
56
- addPointList: [], // 快速新增的节点list
57
- isSelectArea: false, // 是否正在选区
58
- selectArea: null, // 选区矩形对象
59
- currentDelPoint: null, // 缓存当前可批量删除的项目线段上的点
60
- polylinePointList: [] // 缓存折线项目节点
61
- };
62
- },
63
- computed: {
64
- eventStyle() {
65
- return this.propItems.eventStyle;
66
- }
67
- },
68
-
69
- watch: {},
70
-
71
- mounted() {
72
- this.$nextTick(() => {
73
- this.drawPolylineTitle();
74
- this.polylinePointList = [];
75
- this.polyline.forEach((polylineType, polylineTypeId) => {
76
- this.createPolyline(polylineType, polylineTypeId);
77
- });
78
-
79
- this.eventStyle.evented && this.createEvent();
80
- });
81
- },
82
-
83
- methods: {
84
- createEvent() {
85
- this.canvas.on('mouse:up', event => {
86
- if (event.button === 3) {
87
- const { x, y } = event.pointer;
88
- const { originX, originY, endX, endY } = this.$propItems();
89
- if (!(x < originX || x > endX || y < originY || y > endY)) {
90
- this.activeEvent = event;
91
- document.querySelector('.upper-canvas').addEventListener('contextmenu', this.addEventListenerContextmenu, true);
92
- }
93
- // this.willUpdateLine = line;
94
- }
95
- // 左键松开鼠标-批量删除
96
- this.isSelectArea = false;
97
- if (event.button === 1 && !event.target && this.selectArea) {
98
- const points = this.selectArea.ponits;
99
- const delList = this.getAreaPonits(points);
100
- delList.length === 0 && this.removeSelectArea();
101
- if (delList.length > 0 && this.selectArea) {
102
- this.$emit('pointOperation', 'delete', delList);
103
- this.currentDelPoint = null;
104
- }
105
- }
106
- });
107
- let selectAreaOrigin = { left: 0, top: 0 };
108
- this.canvas.on('mouse:down', event => {
109
- if (event.button === 1 && !event.target) {
110
- this.isSelectArea = true;
111
- selectAreaOrigin.left = event.pointer.x;
112
- selectAreaOrigin.top = event.pointer.y;
113
- }
114
- });
115
- this.canvas.on('mouse:move', event => {
116
- this.isSelectArea && this.createSelectArea(event, selectAreaOrigin);
117
- });
118
- },
119
- // 获取选区内包含的节点
120
- getAreaPonits(points) {
121
- const delList = [];
122
- const [x1, y1, x2, y2] = points;
123
- this.canvas.forEachObject(obj => {
124
- const { id, left, top } = obj;
125
- const isArea = left >= x1 && left <= x2 && top >= y1 && top <= y2;
126
- if (id && /_polyline(Point)_/.test(id) && !id.includes('isTitle') && isArea) {
127
- if (this.currentDelPoint) {
128
- const { polylineIndex } = this.currentDelPoint;
129
- if (polylineIndex == obj.polylineIndex) {
130
- delList.push(this.getDataById(id));
131
- }
132
- } else {
133
- delList.push(this.getDataById(id));
134
- }
135
- }
136
- });
137
- return delList;
138
- },
139
- // 创建选区
140
- createSelectArea(event, selectAreaOrigin) {
141
- this.selectArea && this.canvas.remove(this.selectArea);
142
- this.selectArea = new this.fabric.Rect({
143
- ...defaultVaule.style,
144
- fill: '#CAF982',
145
- opacity: 0.4,
146
- left: selectAreaOrigin.left,
147
- top: selectAreaOrigin.top,
148
- width: event.pointer.x - selectAreaOrigin.left,
149
- height: event.pointer.y - selectAreaOrigin.top,
150
- ponits: [selectAreaOrigin.left, selectAreaOrigin.top, event.pointer.x, event.pointer.y]
151
- });
152
- this.canvas.add(this.selectArea);
153
- this.canvas.renderAll();
154
- },
155
- // 每一个折线类别
156
- createPolyline(polylineType, polylineTypeId) {
157
- polylineType.dataList.forEach((polylineItem, polylineIndex) => {
158
- this.drawPolyline(polylineItem, polylineIndex, polylineType, polylineTypeId);
159
- });
160
- },
161
- // 绘制左侧折线标题
162
- drawPolylineTitle() {
163
- const { canvasHeight, treeTableminCellWidth, originX, xScaleList, xScaleCellList, endX } = this.propItems;
164
- const leftYScalevalue = this.polyline.find(v => v.position === 'left');
165
- const lableMargin = leftYScalevalue.lableMargin || [5, 30];
166
- const lableLineHeight = leftYScalevalue.lableLineHeight || 20;
167
- const style = leftYScalevalue.style || { fontSize: 12, fill: '#000' };
168
-
169
- const leftTitleList = [];
170
-
171
- const minTime = Math.min(...xScaleList);
172
- const maxTime = Math.max(...xScaleList);
173
- this.polyline.forEach((item, index) => {
174
- item.dataList.forEach((v, i) => {
175
- // 配置是否可拖动标题图标批量增加-
176
- const isAdd = v.list.length
177
- ? !v.list.some(k => {
178
- return isEffectiveNode(k) && new Date(k.time).getTime() >= minTime && new Date(k.time).getTime() < xScaleCellList[1].time;
179
- })
180
- : true;
181
- let rightLimit = endX;
182
- const point = v.list.find(k => isEffectiveNode(k) && new Date(k.time).getTime() >= xScaleCellList[1].time && new Date(k.time).getTime() <= maxTime);
183
- const limitObj =
184
- point &&
185
- JSON.parse(JSON.stringify(xScaleCellList))
186
- .reverse()
187
- .find(k => new Date(point.time).getTime() > k.time);
188
- limitObj && (rightLimit = limitObj.x);
189
- point && !limitObj && new Date(point.time).getTime() === maxTime && (rightLimit = xScaleCellList[xScaleCellList.length - 1].x);
190
- leftTitleList.push({
191
- title: v.title,
192
- type: v.type,
193
- pointAttr: v.pointAttr,
194
- lineAttr: v.lineAttr,
195
- polylineTypeId: index,
196
- polylineIndex: i,
197
- isAdd,
198
- rightLimit
199
- });
200
- });
201
- });
202
-
203
- // 配置左侧title图标(但不是折线项目)
204
- if (this.other?.list && Array.isArray(this.other.list)) {
205
- const otherList = this.other.list.map(v => {
206
- return {
207
- title: v.name,
208
- type: v.type,
209
- pointAttr: v,
210
- isAdd: false,
211
- polylineTypeId: -1,
212
- polylineIndex: -1
213
- };
214
- });
215
- if (this.other.position && this.other.position === 'top') {
216
- leftTitleList.push(...otherList);
217
- } else {
218
- leftTitleList.unshift(...otherList);
219
- }
220
- }
221
-
222
- const eventStyle = { selectable: false, evented: false };
223
- const types = ['arrow', '+', 'shjs', 'shks', 'cg', 'jxtq'];
224
- leftTitleList.forEach(async (v, i) => {
225
- const baseTop = canvasHeight - lableMargin[1];
226
- const top = baseTop - i * lableLineHeight;
227
- const left = lableMargin[0] + treeTableminCellWidth;
228
- const pointId = `${v.polylineTypeId}_${v.polylineIndex}_-1_polylinePoint_'${new Date().getTime()}_isTitle`;
229
- const { fill: fillColor = '#000', stroke } = v.pointAttr;
230
- let text = new this.fabric.Text(String(v.title), {
231
- fontSize: style.fontSize,
232
- fill: fillColor && stroke ? stroke : fillColor,
233
- left,
234
- top,
235
- originY: 'center',
236
- ...defaultVaule.style,
237
- id: pointId
238
- });
239
- const isOriginXCenter = types.includes(v.type);
240
- const iconLeft = isOriginXCenter ? text.width + text.left + 10 : text.width + text.left + 5;
241
- const option = {
242
- left: iconLeft,
243
- top,
244
- ...v.pointAttr,
245
- originY: 'center',
246
- originX: isOriginXCenter ? 'center' : 'left',
247
- lineAttr: v.lineAttr || '',
248
- pointAttr: v.pointAttr,
249
- id: pointId,
250
- polylineTypeId: v.polylineTypeId,
251
- polylineIndex: v.polylineIndex,
252
- ...(v.isAdd && this.eventStyle.evented ? {} : eventStyle),
253
- rightLimit: v.rightLimit || ''
254
- };
255
- let ele;
256
- if (v.type === 'img' && v.pointAttr?.iconClassName) {
257
- ele = await this.createImage(v.pointAttr.iconClassName, v.pointAttr);
258
- }
259
- const icon = await this.createPoint(v.type, {
260
- ele: ele || '',
261
- ...option
262
- });
263
- icon.hasControls = icon.hasBorders = false;
264
- let originTop = top;
265
- let cloneIconId;
266
- icon.on('moving', () => {
267
- // 优化左侧图标太小不容易选中的问题
268
- if (!icon.url && icon.scaleX == 1.8 && icon.scaleY == 1.8) {
269
- icon.set({
270
- scaleX: 1,
271
- scaleY: 1
272
- });
273
- this.canvas.renderAll();
274
- }
275
-
276
- if (!cloneIconId) {
277
- cloneIconId = '_polylinePoint_temp_' + new Date().getTime();
278
- icon.clone(clonedObj => {
279
- clonedObj.set({
280
- id: cloneIconId // 此id必须,用于remove节点的时候
281
- });
282
- clonedObj.bringForward();
283
- this.canvas.add(clonedObj);
284
- this.canvas.renderAll();
285
- });
286
- }
287
-
288
- icon.set({ originX: 'center' });
289
- this.moveLimit(icon);
290
- icon.left >= originX && icon.left < originX + 5 && (originTop = icon.top);
291
- if (icon.left >= originX) {
292
- // this.addPoint(icon, originX, originTop);
293
- this.showDrapPopup(icon);
294
- } else {
295
- this.isDropVisible = false;
296
- }
297
- this.canvas.renderAll();
298
- });
299
- // 标题折线点停止拖拽后 更新
300
- icon.on('moved', () => {
301
- icon.set({
302
- id: icon.id.replace('_isTitle', '')
303
- });
304
- this.canvas.renderAll();
305
- this.removeTitle();
306
- this.removePolyline(cloneIconId);
307
-
308
- if (icon.id.includes('_polylinePoint_') && this.isDropVisible) {
309
- this.isDropVisible = false;
310
- if (icon.left >= originX) {
311
- // this.setEmitFunction(icon);
312
- this.setAddPointList(icon);
313
- this.$emit('pointOperation', 'increasePointBatch', this.addPointList);
314
- this.addPointList = [];
315
- }
316
- }
317
- this.removePolyline(icon.id);
318
- // 重绘折线标题
319
- this.drawPolylineTitle();
320
- });
321
-
322
- // 优化左侧图标太小不容易选中的问题
323
- if (icon.evented && !icon.url) {
324
- const maxVal = Math.max(icon.width * icon.scale, icon.height * icon.scale);
325
- if (maxVal < 7) {
326
- icon.set({
327
- scaleX: 1.8,
328
- scaleY: 1.8
329
- });
330
- }
331
- }
332
-
333
- this.canvas.add(text, icon);
334
- this.canvas.requestRenderAll();
335
- });
336
- },
337
- // 点移动限制
338
- moveLimit(point) {
339
- point.setCoords();
340
- const leftLimit = this.propItems.treeTableminCellWidth;
341
- const rightLimit = point.rightLimit;
342
- if (point.top < this.propItems.originY) {
343
- point.set('top', this.propItems.originY);
344
- }
345
- if (point.top > this.propItems.endY) {
346
- point.set('top', this.propItems.endY);
347
- }
348
- if (point.left < leftLimit) {
349
- point.set('left', leftLimit);
350
- }
351
- if (point.left > rightLimit) {
352
- point.set('left', rightLimit);
353
- }
354
- },
355
- removeTitle() {
356
- this.canvas.forEachObject(obj => {
357
- if (obj.id && obj.id.includes('isTitle')) {
358
- this.canvas.remove(obj);
359
- }
360
- });
361
- },
362
- isLimit(time) {
363
- const minMinute = Math.min(...this.propItems.xScaleList);
364
- const maxMinute = Math.max(...this.propItems.xScaleList);
365
- const getTime = new Date(time).getTime();
366
- return getTime >= minMinute && getTime <= maxMinute;
367
- },
368
- // 创建危急值和差异值
369
- createAttrVaule(attr, polyline, polylineIndex, polylineType, polylineTypeId) {
370
- const { list } = polyline;
371
- const valueObj = polyline[attr] || {};
372
- if (!valueObj.max && !valueObj.min) return;
373
-
374
- const { originX, endX } = this.propItems;
375
- const isMax = valueObj.max?.show && valueObj.max?.value && list.some(v => +v.value >= +valueObj.max.value && this.isLimit(v.time));
376
- const isMin = valueObj.min?.show && valueObj.min?.value && list.some(v => +v.value <= +valueObj.min.value && this.isLimit(v.time));
377
- const lines = [];
378
-
379
- const drawLine = ({ value, lineStyle }) => {
380
- const top = this.cumputedY(polylineType, +value);
381
- return this.drawLine([originX, top, endX, top], { polylineIndex, lineIndex: 0, polylineTypeId, ...(lineStyle || defaultVaule.criticalStyle) });
382
- };
383
- const drawText = ({ name, nameStyle }, top) => {
384
- return new this.fabric.Text(String(name || ''), {
385
- ...defaultVaule.style,
386
- ...defaultVaule.textStyle,
387
- ...nameStyle,
388
- originY: 'bottom',
389
- left: originX + 5,
390
- top: top - 5
391
- });
392
- };
393
-
394
- if (isMax) {
395
- const line = drawLine(valueObj.max);
396
- const text = drawText(valueObj.max, line.top);
397
- line.text = text;
398
- lines.push(line, text);
399
- }
400
- if (isMin) {
401
- const line = drawLine(valueObj.min);
402
- const text = drawText(valueObj.min, line.top);
403
- line.text = text;
404
- lines.push(line, text);
405
- }
406
- this.canvas.add(...lines);
407
- this.canvas.renderAll();
408
- },
409
- /**
410
- * 绘制一条折线
411
- * @param {polyline} 折线属性
412
- * @param {polyline.type} 折线点形状 circle: 圆 | triangle: 三角
413
- * @param {polyline.pointAttr} 参数 fill: 填充色 | stroke: 描边色 | strokeWidth: 宽度 | radius: 半径 | width: 宽度 | height: 高度
414
- * @param {polyline.list} 折线点坐标数组
415
- * @param {polylineIndex} 当前折线 index
416
- */
417
- drawPolyline(polyline, polylineIndex, polylineType, polylineTypeId) {
418
- const { originY, endY } = this.propItems;
419
- const { pointAttr, lineAttr } = polyline;
420
- this._iconClassName = polyline.type === 'img' && pointAttr.iconClassName ? pointAttr.iconClassName : '';
421
-
422
- const list = polyline.list;
423
- const pointList = [];
424
- let lineList = [];
425
- const _this = this;
426
- function getPointer(point) {
427
- let isInit = false;
428
- let points = null;
429
- if (isEffectiveNode(point) && _this.isLimit(point.time)) {
430
- const x = _this.cumputedX(point.time);
431
- const y = _this.cumputedY(polylineType, point.value);
432
- isInit = y < originY || y > endY;
433
- points = [x, y < originY ? originY : y > endY ? endY : y];
434
- }
435
- return { isInit, points };
436
- }
437
- list.forEach((linePoints, index) => {
438
- // 当前点
439
- let { points, isInit } = getPointer(linePoints);
440
-
441
- // 下一个点
442
- const nextPoints = list[index + 1];
443
- let { points: nextPoint } = getPointer(nextPoints);
444
-
445
- let line;
446
- let point;
447
- let previousLine;
448
- line = points && nextPoint ? this.drawLine([...points, ...nextPoint], { polylineIndex, polylineTypeId, lineIndex: index, ...lineAttr }) : null;
449
- previousLine = lineList[index - 1];
450
- const pointOthers = { polylineTypeId, polylineIndex, pointIndex: index, ...pointAttr, lineAttr };
451
- if (points) {
452
- pointOthers.time = linePoints.time;
453
- point = previousLine ? this.drawPoint(...points, previousLine, line, polyline.type, pointOthers, isInit) : this.drawPoint(...points, null, line, polyline.type, pointOthers, isInit);
454
- }
455
- lineList.push(line);
456
- point && pointList.push(point);
457
- });
458
-
459
- Promise.all(pointList).then(res => {
460
- const polylineObj = this.polylinePointList.find(v => v.polylineTypeId == polylineTypeId && v.polylineIndex == polylineIndex);
461
- const pointerList = res.map(v => v.left);
462
- if (polylineObj) {
463
- Object.assign(polylineObj, {
464
- pointerList,
465
- pointList: res
466
- });
467
- polylineObj.pointerList = pointerList;
468
- } else {
469
- this.polylinePointList.push({
470
- polylineTypeId,
471
- polylineIndex,
472
- pointerList,
473
- pointStyle: {
474
- fill: res[0].fill,
475
- stroke: res[0].stroke
476
- },
477
- lineStyle: {
478
- stroke: lineAttr.stroke
479
- },
480
- pointList: res
481
- });
482
- }
483
- lineList = lineList.filter(v => v);
484
- let prevPoint = null;
485
- res = res.filter(v => {
486
- if (v && prevPoint) {
487
- prevPoint.nextPoint = v; // 记录下一个点 id
488
- v.prevPoint = prevPoint;
489
- }
490
- prevPoint = v || prevPoint;
491
- return v;
492
- });
493
- this.canvas.add(...lineList, ...res);
494
- this.canvas.requestRenderAll();
495
- });
496
-
497
- this.createAttrVaule('critical', polyline, polylineIndex, polylineType, polylineTypeId);
498
- this.createAttrVaule('diffValue', polyline, polylineIndex, polylineType, polylineTypeId);
499
- },
500
- // 计算y轴坐标
501
- cumputedY(polylineType, linePointY) {
502
- const { position = 'left', list = [] } = polylineType;
503
- const key = position === 'left' ? 'Left' : 'Right';
504
- const yScaleCell = this.propItems[`yScaleCell${key}`];
505
- const yCellUnit = linePointY - Math.min(...list);
506
- return this.propItems.endY - yScaleCell * yCellUnit;
507
- },
508
- /**
509
- * 绘制折线点
510
- * @param {left, top} 折线点坐标
511
- * @param {line1, line2} 与折线点关联的线
512
- * @param {type} 折线点类型
513
- * @param {others} 参数 polylineIndex | pointIndex | ...polyline.pointAttr
514
- */
515
- async drawPoint(left, top, line1, line2, type, others, isInit) {
516
- const pointId = `${others.polylineTypeId}_${others.polylineIndex}_${others.pointIndex}_polylinePoint_${new Date().getTime()}`;
517
- const ele = this._iconClassName && (await this.createImage(this._iconClassName, others));
518
- let point = await this.createPoint(type, {
519
- id: pointId,
520
- left: left,
521
- top: top,
522
- ele: ele || '',
523
- ...defaultVaule.pointStyle,
524
- ...others,
525
- ...(!this.eventStyle.evented
526
- ? {
527
- lockMovementX: true,
528
- lockMovementY: true
529
- }
530
- : {})
531
- });
532
- point.hasControls = point.hasBorders = false;
533
- line1 && (point.line1 = line1);
534
- line2 && (point.line2 = line2);
535
- point.on('mouseover', () => {
536
- this.showDrapPopup(point);
537
- this.pointToFront(point);
538
- });
539
- point.on('mouseout', () => {
540
- this.isDropVisible = false;
541
- });
542
- // 折线点移动中 实时更新相关联的线的坐标
543
- if (this.eventStyle.evented) {
544
- point.on('moving', () => {
545
- this.pointMoveLimit(point, false);
546
- this.pointMoveUpdateLine(point);
547
- this.addPoint(point, left, top);
548
- this.canvas.renderAll();
549
- this.showDrapPopup(point);
550
- });
551
- // 折线点停止拖拽后 更新polyline中对应的坐标
552
- point.on('moved', () => {
553
- if (point && point.id.includes('_polylinePoint_')) {
554
- this.isDropVisible = false;
555
- this.setEmitFunction(point);
556
- }
557
- });
558
- point.on('mouseup', event => {
559
- const { button, isClick } = event;
560
- if (button == 1 && isClick) {
561
- // 可配置批量删除当前项目上的点了
562
- this.currentDelPoint = point;
563
- }
564
- });
565
- }
566
-
567
- // 超出表格最高或最低值则需要触发pointChange事件并且不能更新视图,否则连接节点判断不会成功
568
- isInit && this.setEmitFunction(point, isInit);
569
-
570
- return point;
571
- },
572
- // 拖动停止后 设置回调触发方法
573
- setEmitFunction(point, isInit = false) {
574
- const { data, x, y } = this.getValue(point);
575
- Object.assign(data, {
576
- value: { time: x, value: y },
577
- isInit
578
- });
579
- if (this.addPointList.length === 0) {
580
- this.$emit('pointChange', data);
581
- } else {
582
- const lastPoint = this.addPointList.at(-1);
583
- const position = this.polyline[point.polylineTypeId].position;
584
- // 如果是重合/连线节点
585
- if (point.get('scaleX') !== point.scale) {
586
- this.addPointList.splice(-1, 1, {
587
- ...lastPoint,
588
- value: {
589
- time: this.getXValue(this._concatPoint?.left),
590
- value: this.getYValue(position, this._concatPoint?.top)
591
- }
592
- });
593
- this.addPointList.forEach(v => Object.assign(v, { isConcat: true, concatIndex: this._concatIndex }));
594
- } else if (point.line2 || (point.nextPoint && lastPoint.left >= point.nextPoint.left)) {
595
- // 1、存在右连线 2、无右侧连线,并且存在下一个节点的情况
596
- this.addPointList = [];
597
- this.repaintPolyline(position, point.polylineIndex);
598
- return;
599
- }
600
- !point.line2 && this.addPointList.splice(0, 1);
601
- !point.id.includes('isTitle') && this.removePolyline(point.id);
602
- if (this.addPointList.length > 0) {
603
- this.$emit('pointOperation', 'increasePointBatch', this.addPointList);
604
- this.addPointList = [];
605
- } else {
606
- this.repaintPolyline(position, point.polylineIndex);
607
- }
608
- }
609
- },
610
- // 设置批量新增数据
611
- setAddPointList(point) {
612
- const { data, x, y } = this.getValue(point);
613
- data.value = { time: x, value: y };
614
- data.left = point.left;
615
- data.top = top;
616
- this.addPointList.push(data);
617
- },
618
- /**
619
- * @description: 拖动批量新增节点
620
- * @param {*} point 拖动的最后一个节点
621
- * @param {*} originLeft 拖动的源节点的left值
622
- * @param {*} originTop 拖动的源节点的top值
623
- * @return {*}
624
- */
625
- async addPoint(point, originLeft, originTop) {
626
- const { xCellWidth, table, originX } = this.propItems;
627
- // const addPointSpaceGridNumbers = table.addPointSpaceGridNumbers || [];
628
- let addPointSpaceGridNumber = table.addPointSpaceGridNumber || 1;
629
-
630
- let { left, top } = point;
631
- const currentLeft = left;
632
-
633
- const startLength = this.addPointList.length;
634
- const n = 6; // 拖动范围,在需要增加节点的刻度左右吸入的范围值
635
- const spaceWidth = xCellWidth * addPointSpaceGridNumber;
636
- const residue = (left - originLeft) % spaceWidth;
637
-
638
- const condition = residue > spaceWidth - n || residue < n;
639
- const conditionNoLine2 = !point.line2 && left > originLeft;
640
- const conditionHasLine2 = point.line2 && left > originLeft + spaceWidth;
641
-
642
- // if (!point.line2 && left > originLeft && condition) {
643
- if (condition && (conditionNoLine2 || conditionHasLine2)) {
644
- if (residue > spaceWidth - n) left = left - residue + spaceWidth;
645
- if (residue < n) left = left - residue;
646
- point.set({
647
- left
648
- });
649
-
650
- const checkPoints = () => {
651
- const PointLens = Math.floor((left - originLeft) / spaceWidth);
652
- if (PointLens > 1 && this.addPointList?.length < PointLens) {
653
- for (let k = 0; k < PointLens; k++) {
654
- if (k > 0) {
655
- const curLeft = originLeft + spaceWidth * k;
656
- // 判断this.addPointList中的left是否包含curLeft,不包含就说明是漏了
657
- const isNoLack = this.addPointList.some(v => v.left === curLeft);
658
- if (!isNoLack) {
659
- let index;
660
- const pointObj = this.addPointList.find((v, i) => {
661
- if (v.left > curLeft) {
662
- index = i;
663
- }
664
- return v.left > curLeft;
665
- });
666
- if (pointObj) {
667
- let cloneObj = JSON.parse(JSON.stringify(pointObj));
668
- cloneObj.value.time = this.getXValue(curLeft);
669
- cloneObj.left = curLeft;
670
- this.addPointList.splice(index, 0, cloneObj);
671
- }
672
- }
673
- }
674
- }
675
- }
676
- };
677
-
678
- // const i = Math.floor((left - originLeft) / spaceWidth);
679
- // 复制点和线
680
- if (conditionNoLine2 || conditionHasLine2) {
681
- // point.line1 && this.removePolyline(point.line1.id);
682
- point.line1?.set({
683
- stroke: 'transparent'
684
- });
685
- conditionHasLine2 &&
686
- point.line2?.set({
687
- stroke: 'transparent'
688
- });
689
- // if (i === 1) {
690
- this.addPointList.length == 0 && (await this.clonePoint(point, [point.line1 ? point.line1.x1 : originLeft, point.line1 ? point.line1.y1 : originTop, originLeft, originTop]));
691
- // }
692
- if (this.addPointList.every(v => v.left !== left)) {
693
- // const points1 = i === 1 ? [originLeft, originTop] : point.prePoints;
694
- const points1 = this.addPointList.length == 0 ? [originLeft, originTop] : point.prePoints;
695
- points1 && (await this.clonePoint(point, [...points1, left, top]));
696
- // this.setAddPointList(point);
697
- // 调用上面方法就会导致线段绘制失败,目前还不知道为啥子
698
- const { data, x, y } = this.getValue(point);
699
- data.value = { time: x, value: y };
700
- data.left = point.left;
701
- data.top = top;
702
- this.addPointList.push(data);
703
- }
704
- checkPoints();
705
- }
706
- }
707
-
708
- // 断点相连
709
- function concatPoint(target) {
710
- const { left: targetLeft, top: targetTop } = target;
711
- point.setCoords();
712
- if (left >= targetLeft - n && left <= targetLeft + n && top <= targetTop + n && top >= targetTop - n) {
713
- point.set({
714
- left: targetLeft,
715
- top: targetTop,
716
- scaleX: point.scale === 1 ? 2.5 : 0.1,
717
- scaleY: point.scale === 1 ? 2.5 : 0.1
718
- });
719
- } else {
720
- point.set({
721
- scaleX: point.scale,
722
- scaleY: point.scale
723
- });
724
- }
725
- }
726
-
727
- // 检查是否显示重合连线放大节点的标识
728
- // if (conditionHasLine2) {
729
- if (point.nextPoint) {
730
- const { polylineTypeId, polylineIndex } = point;
731
- const polylineObj = this.polylinePointList.find(v => v.polylineTypeId == polylineTypeId && v.polylineIndex == polylineIndex);
732
- this._concatIndex = polylineObj?.pointerList.findIndex(v => v + n > left && v > originLeft);
733
- if (!~this._concatIndex) {
734
- point.set({
735
- scaleX: point.scale,
736
- scaleY: point.scale
737
- });
738
- } else {
739
- concatPoint(polylineObj?.pointList[this._concatIndex]);
740
- }
741
- this._concatPoint = polylineObj?.pointList?.[this._concatIndex] || null;
742
- polylineObj?.pointerList.forEach((v, i) => {
743
- const obj = polylineObj?.pointList[i];
744
- if (v > originLeft && v < left) {
745
- const { fill, stroke } = polylineObj.pointStyle;
746
- const setColor = color => {
747
- return color && (color !== '#fff' || color !== 'transparent') ? '#999' : color;
748
- };
749
- // 此处需要递归遍历group节点的所有子节点,然后更改其颜色
750
- // 待完善
751
- obj.set({ fill: setColor(fill), stroke: setColor(stroke) });
752
- // obj.line2?.set({ fill: setColor(fill), stroke: setColor(stroke) });
753
- } else {
754
- obj.set(polylineObj.pointStyle);
755
- // obj.line2?.set(polylineObj.lineStyle);
756
- }
757
- });
758
- }
759
-
760
- // } else {
761
- // point.nextPoint && concatPoint(point.nextPoint);
762
- // }
763
-
764
- // 如果往回拖动则删除经过的已存在的节点
765
- if (startLength > 0 && currentLeft <= this.addPointList[startLength - 1].left) {
766
- this.removePolyline('increasePointBatch', left);
767
- this.addPointList = this.addPointList.filter(v => v.left < left);
768
- const endLength = this.addPointList.length;
769
- if (endLength === 0) {
770
- point.line1?.set({
771
- stroke: point.stroke
772
- });
773
- conditionHasLine2 &&
774
- point.line2?.set({
775
- stroke: point.stroke
776
- });
777
- this.removePolyline('increasePointBatch', originLeft);
778
- }
779
- if (endLength > 0) {
780
- point.set({
781
- prePoints: [this.addPointList[endLength - 1].left, this.addPointList[endLength - 1].top]
782
- });
783
- }
784
- }
785
- },
786
- clonePoint(point, points) {
787
- let evented, selectable, lineIndex, polylineIndex, lineAttr, polylineTypeId;
788
- evented = selectable = false;
789
- lineAttr = { ...point.lineAttr };
790
- lineIndex = point.pointIndex;
791
- polylineIndex = point.polylineIndex;
792
- polylineTypeId = point.polylineTypeId;
793
-
794
- return new Promise((resolve, reject) => {
795
- point.clone(clonedObj => {
796
- clonedObj.set({
797
- left: points[2],
798
- top: points[3],
799
- idClone: 'increasePointBatch',
800
- id: '_polylinePoint_' + new Date().getTime(), // 此id必须,用于remove节点的时候
801
- polylineTypeId,
802
- polylineIndex: point.polylineIndex,
803
- scaleX: point.scale,
804
- scaleY: point.scale
805
- });
806
- clonedObj.hasControls = clonedObj.hasBorders = false;
807
- point.set({
808
- prePoints: [point.left, point.top]
809
- });
810
- const line = this.drawLine([...points], { evented, selectable, ...lineAttr, polylineIndex, polylineTypeId, lineIndex });
811
- clonedObj.line1 = line;
812
- this.canvas.sendBackwards(line);
813
- clonedObj.bringForward();
814
- this.canvas.add(clonedObj);
815
- this.canvas.renderAll();
816
- resolve(clonedObj);
817
- });
818
- });
819
- },
820
- getValue(point) {
821
- const x = this.getXValue(point.left);
822
- if (point.id) {
823
- const data = this.getDataById(point.id);
824
- const position = data.position;
825
- const y = this.getYValue(position, point.top);
826
- return { data, x, y };
827
- } else {
828
- const left = this.getYValue('left', point.top);
829
- const right = this.getYValue('right', point.top);
830
- return { x, left, right };
831
- }
832
- },
833
- getYValue(position, top) {
834
- const key = position == 'left' ? 'Left' : 'Right';
835
- const list = this.polyline.find(item => item.position == position)?.list || [];
836
- const v = list.length ? Math.min(...list) : 0;
837
- const y = (this.propItems.endY - top) / this.propItems[`yScaleCell${key}`] + v;
838
- return y;
839
- },
840
- /**
841
- * 绘制折线点
842
- * @param {lines} 线坐标 [x1, y1, x2, y2]
843
- * @param {others} 参数 polylineIndex | pointIndex | ...polyline.lineAttr
844
- */
845
- drawLine(lines, others) {
846
- const lineId = `${others.lineIndex}_${others.polylineIndex}_polylineLine_${new Date().getTime()}`;
847
- const line = new this.fabric.Line(lines, {
848
- id: lineId,
849
- hoverCursor: 'default',
850
- // objectCaching: false,
851
- selectable: false,
852
- evented: false,
853
- ...others
854
- });
855
- // line.hasControls = line.hasBorders = false;
856
- // line.on('mouseover', () => {
857
- // // 将当条线段以及点层级置顶
858
- // // 此处有问题,线段定位是斜角形成的整个矩形,会挡住其他点或线段的mouseover事件
859
- // });
860
-
861
- return line;
862
- },
863
- showDrapPopup(point) {
864
- this.isDropVisible = true;
865
- this.dropPos = {
866
- left: point.left,
867
- top: point.top,
868
- margin: { top: this.propItems.yCellHeight }
869
- };
870
- let { x, y, data } = this.getValue(point);
871
- const getY = y => {
872
- if (data?.position == 'right') {
873
- if (y.toString().includes('.')) {
874
- const [m, n] = y.toString().split('.');
875
- return (parseFloat(`${m}.${n.slice(0, 1)}`) * 10) / 10;
876
- }
877
- return y;
878
- } else {
879
- return Math.round(y);
880
- }
881
- };
882
- this.dropVal = {
883
- title: data.title,
884
- list: [
885
- { id: '11', name: '时间', value: x },
886
- { id: '22', name: '值', value: getY(y) }
887
- ]
888
- };
889
- },
890
- // 折线点移动时 setCoords()方法手动更新相关联的线坐标
891
- pointMoveUpdateLine(point) {
892
- if (point.line1) {
893
- point.line1.setCoords();
894
- point.line1.set({ x2: point.left, y2: point.top });
895
- }
896
- if (point.line2) {
897
- point.line2.setCoords();
898
- point.line2.set({ x1: point.left, y1: point.top });
899
- }
900
- },
901
- // 打开右键菜单
902
- openRightModal(event) {
903
- this.rightPos = {
904
- clientX: event.e.clientX || event.e.pageX,
905
- clientY: event.e.clientY || event.e.pageY
906
- };
907
- this.isRightVisible = false;
908
- const target = event.target;
909
- const id = target ? target.id : '';
910
- this.$nextTick(() => {
911
- if (id && id.includes('_polylinePoint_')) {
912
- this._currentPoint = target;
913
- let nodeConnect = [];
914
- if (this.propItems.operable.connect) {
915
- if (!target.line2 && target.nextPoint) {
916
- nodeConnect = rightClickNodeConnect.slice(0, 1);
917
- }
918
- if (!target.line1 && target.prevPoint) {
919
- nodeConnect = rightClickNodeConnect.slice(1);
920
- }
921
- if (!target.line1 && !target.line2 && target.prevPoint && target.nextPoint) {
922
- nodeConnect = rightClickNodeConnect.slice();
923
- }
924
- }
925
- this.rightClickNode = Object.freeze(
926
- rightClickNode
927
- .slice(0, 1)
928
- .concat(nodeConnect)
929
- .concat(rightClickNode.slice(-1))
930
- );
931
- this.isRightVisible = true;
932
- } else if (!id) {
933
- const { operable } = this.propItems;
934
- this._currentPoint = null;
935
- this.rightClickNode = Object.freeze(operable.set ? rightClickNode.slice(0, 2) : rightClickNode.slice(0, 1));
936
- this.isRightVisible = true;
937
- }
938
- });
939
- },
940
- // 关闭右键菜单,打开添加节点弹窗表单
941
- handleRightClick({ type, name }) {
942
- this.isRightVisible = false;
943
- const id = this._currentPoint?.id;
944
- const { left, top } = this._active;
945
- let data = id ? this.getDataById(id) : this.getValue({ left, top });
946
- if (type == 'add') {
947
- if (name.includes('左')) {
948
- data.range = {
949
- time: [this._currentPoint.prevPoint.time, this._currentPoint.time],
950
- direction: 'left'
951
- };
952
- }
953
- if (name.includes('右')) {
954
- data.range = {
955
- time: [this._currentPoint.time, this._currentPoint.nextPoint.time],
956
- direction: 'right'
957
- };
958
- }
959
- }
960
- this.$emit('pointOperation', type, data);
961
- this._currentPoint = null;
962
- },
963
- getDataById(id) {
964
- const arr = id.replace(/_?[a-zA-Z]+.+$/, '').split('_');
965
- const [typeIndex, lineIndex, pointIndex] = arr;
966
- const data = this.polyline[typeIndex];
967
- const value = data?.dataList?.[lineIndex]?.list?.[pointIndex]?.data || '';
968
- return {
969
- title: data?.dataList?.[lineIndex].title || '',
970
- position: data.position,
971
- dataIndex: lineIndex,
972
- pointIndex: pointIndex,
973
- data: value
974
- };
975
- },
976
- isOnePolyLine(obj, polylineTypeId, polylineIndex) {
977
- // 排除左侧标题
978
- const isPolyLine = obj.polylineTypeId === polylineTypeId && obj.polylineIndex === polylineIndex;
979
- if (obj.id && /_polyline(Point|Line)_/.test(obj.id) && !obj.id.includes('isTitle') && isPolyLine) return true;
980
- return;
981
- },
982
- // 将当条线段以及点层级置顶
983
- // 线段不能先置顶,会挡住其他线段上的点
984
- pointToFront(point) {
985
- this.canvas.forEachObject(obj => {
986
- if (this.isOnePolyLine(obj, point.polylineTypeId, point.polylineIndex)) {
987
- this.canvas.bringToFront(obj);
988
- }
989
- });
990
- },
991
- removePolyline(id, left, position, polylineIndex) {
992
- // 根据id或者idClone删除
993
- if (id) {
994
- const pointId = left ? 'idClone' : 'id';
995
- const point = this.canvas.getObjects().filter(item => item[pointId] === id);
996
- if (point && point.length > 0) {
997
- point.forEach(v => {
998
- if (left && v.left >= left) {
999
- this.canvas.remove(v);
1000
- v.line1 && this.canvas.remove(v.line1);
1001
- } else if (!left) {
1002
- this.canvas.remove(v);
1003
- v.line1 && this.canvas.remove(v.line1);
1004
- }
1005
- });
1006
- }
1007
- return;
1008
- }
1009
- // 删除一条线
1010
- if (position && (polylineIndex === 0 || polylineIndex)) {
1011
- const polylineTypeId = this.polyline.findIndex(v => v.position === position);
1012
- this.canvas.forEachObject(obj => {
1013
- if (this.isOnePolyLine(obj, polylineTypeId, polylineIndex)) {
1014
- obj.text && this.canvas.remove(obj.text);
1015
- obj.line1 && this.canvas.remove(obj.line1);
1016
- obj.line2 && this.canvas.remove(obj.line2);
1017
- this.canvas.remove(obj);
1018
- }
1019
- });
1020
- return;
1021
- }
1022
- // 删除折线图上的所有点和线
1023
- this.canvas.forEachObject(obj => {
1024
- if (obj.id && /_polyline(Point|Line)_/.test(obj.id) && !obj.id.includes('isTitle')) {
1025
- obj.text && this.canvas.remove(obj.text);
1026
- this.canvas.remove(obj);
1027
- }
1028
- });
1029
- },
1030
- removeSelectArea() {
1031
- if (this.selectArea) {
1032
- this.canvas.remove(this.selectArea);
1033
- this.selectArea = null;
1034
- }
1035
- },
1036
- repaintPolyline(position, dadaIndex) {
1037
- if (arguments.length < 2) {
1038
- if (this.polyline.length) {
1039
- this.removePolyline();
1040
- }
1041
- this.polylinePointList = [];
1042
- this.polyline.forEach((polylineType, polylineTypeId) => {
1043
- this.createPolyline(polylineType, polylineTypeId);
1044
- });
1045
- this.removeTitle();
1046
- this.drawPolylineTitle();
1047
- } else {
1048
- const polylineTypeId = this.polyline.findIndex(v => v.position === position);
1049
- this.removePolyline(null, null, position, parseInt(dadaIndex));
1050
- this.drawPolyline(this.polyline[polylineTypeId].dataList[parseInt(dadaIndex)], parseInt(dadaIndex), this.polyline[polylineTypeId], polylineTypeId);
1051
- }
1052
- }
1053
- }
1054
- };
1055
- </script>
1
+ <template>
2
+ <div class="poly-line">
3
+ <MouseRightClick v-if="isRightVisible" :getContainer="getContainer" :nodeList="rightClickNode" :rightPos="rightPos" @nodeClick="handleRightClick" @closeRight="isRightVisible = false" />
4
+
5
+ <DropPopup v-show="isDropVisible" :val="dropVal" :dropPos="dropPos" />
6
+ </div>
7
+ </template>
8
+
9
+ <script>
10
+ import FabricCommon from '../mixins/fabricCommon';
11
+ import draw from '../mixins/draw';
12
+ import eventCommon from '../mixins/eventCommon';
13
+ import MouseRightClick from '../components/MouseRightClick';
14
+ import DropPopup from '../components/DropPopup';
15
+ import defaultVaule from '../const/defaultVaule';
16
+
17
+ /**
18
+ * @description: 递归遍历节点,设置颜色属性
19
+ * @param {fabric.Object[]} pointArr
20
+ * @param {string}} color
21
+ * @return {*}
22
+ */
23
+
24
+ function resetPointColor(pointArr, color) {
25
+ pointArr.forEach(point => {
26
+ const { fill, stroke } = point;
27
+ if (fill && !fill.includes('#fff') && fill !== 'transparent') {
28
+ point.set({ fill: color });
29
+ }
30
+ if (stroke && !stroke.includes('#fff') && stroke !== 'transparent') {
31
+ point.set({ stroke: color });
32
+ }
33
+ if (point._objects?.length) {
34
+ resetPointColor(point._objects, color);
35
+ }
36
+ });
37
+ }
38
+ const setPointLineColor = (point, color, conditionHasLine2) => {
39
+ point.line1?.set({ stroke: color });
40
+ conditionHasLine2 && point.line2?.set({ stroke: color });
41
+ };
42
+
43
+ const isEffectiveNode = node => {
44
+ return node?.time && (node?.value || node?.value === 0);
45
+ };
46
+ const rightClickNode = [
47
+ { name: '新增节点', type: 'add' },
48
+ { name: '检测项设置', type: 'set' },
49
+ { name: '删除节点', type: 'delete' }
50
+ ];
51
+ const rightClickNodeConnect = [
52
+ { name: '新增节点连接右侧节点', type: 'add' },
53
+ { name: '新增节点连接左侧节点', type: 'add' }
54
+ ];
55
+ export default {
56
+ name: 'fabric-polylines',
57
+ mixins: [FabricCommon, draw, eventCommon],
58
+ inject: ['$propItems'],
59
+ props: {
60
+ polyline: {
61
+ type: Array,
62
+ required: true
63
+ },
64
+ other: {
65
+ type: Object,
66
+ default: () => {}
67
+ }
68
+ },
69
+ components: {
70
+ MouseRightClick,
71
+ DropPopup
72
+ },
73
+ data() {
74
+ return {
75
+ isDropVisible: false,
76
+ dropVal: {},
77
+ dropPos: { clientX: 0, clientY: 0 },
78
+ isRightVisible: false,
79
+ rightPos: { clientX: 0, clientY: 0 },
80
+ rightClickNode: [],
81
+ activeEvent: null, // 当前可右键活动的对象
82
+ addPointList: [], // 快速新增的节点list
83
+ isSelectArea: false, // 是否正在选区
84
+ selectArea: null, // 选区矩形对象
85
+ currentDelPoint: null, // 缓存当前可批量删除的项目线段上的点
86
+ polylinePointList: [] // 缓存折线项目节点
87
+ };
88
+ },
89
+ computed: {
90
+ eventStyle() {
91
+ return this.propItems.eventStyle;
92
+ }
93
+ },
94
+
95
+ watch: {},
96
+
97
+ mounted() {
98
+ this.$nextTick(() => {
99
+ this.drawPolylineTitle();
100
+ this.polylinePointList = [];
101
+ this.polyline.forEach((polylineType, polylineTypeId) => {
102
+ this.createPolyline(polylineType, polylineTypeId);
103
+ });
104
+
105
+ this.eventStyle.evented && this.createEvent();
106
+ });
107
+ },
108
+
109
+ methods: {
110
+ createEvent() {
111
+ this.canvas.on('mouse:up', event => {
112
+ if (event.button === 3) {
113
+ const { x, y } = event.pointer;
114
+ const { originX, originY, endX, endY } = this.$propItems();
115
+ if (!(x < originX || x > endX || y < originY || y > endY)) {
116
+ this.activeEvent = event;
117
+ document.querySelector('.upper-canvas').addEventListener('contextmenu', this.addEventListenerContextmenu, true);
118
+ }
119
+ // this.willUpdateLine = line;
120
+ }
121
+ // 左键松开鼠标-批量删除
122
+ this.isSelectArea = false;
123
+ if (event.button === 1 && !event.target && this.selectArea) {
124
+ const points = this.selectArea.ponits;
125
+ const delList = this.getAreaPonits(points);
126
+ delList.length === 0 && this.removeSelectArea();
127
+ if (delList.length > 0 && this.selectArea) {
128
+ this.$emit('pointOperation', 'delete', delList);
129
+ this.currentDelPoint = null;
130
+ }
131
+ }
132
+ });
133
+ let selectAreaOrigin = { left: 0, top: 0 };
134
+ this.canvas.on('mouse:down', event => {
135
+ if (event.button === 1 && !event.target) {
136
+ this.isSelectArea = true;
137
+ selectAreaOrigin.left = event.pointer.x;
138
+ selectAreaOrigin.top = event.pointer.y;
139
+ }
140
+ });
141
+ this.canvas.on('mouse:move', event => {
142
+ this.isSelectArea && this.createSelectArea(event, selectAreaOrigin);
143
+ });
144
+ },
145
+ // 获取选区内包含的节点
146
+ getAreaPonits(points) {
147
+ const delList = [];
148
+ const [x1, y1, x2, y2] = points;
149
+ this.canvas.forEachObject(obj => {
150
+ const { id, left, top } = obj;
151
+ const isArea = left >= x1 && left <= x2 && top >= y1 && top <= y2;
152
+ if (id && /_polyline(Point)_/.test(id) && !id.includes('isTitle') && isArea) {
153
+ if (this.currentDelPoint) {
154
+ const { polylineIndex } = this.currentDelPoint;
155
+ if (polylineIndex == obj.polylineIndex) {
156
+ delList.push(this.getDataById(id));
157
+ }
158
+ } else {
159
+ delList.push(this.getDataById(id));
160
+ }
161
+ }
162
+ });
163
+ return delList;
164
+ },
165
+ // 创建选区
166
+ createSelectArea(event, selectAreaOrigin) {
167
+ this.selectArea && this.canvas.remove(this.selectArea);
168
+ this.selectArea = new this.fabric.Rect({
169
+ ...defaultVaule.style,
170
+ fill: '#CAF982',
171
+ opacity: 0.4,
172
+ left: selectAreaOrigin.left,
173
+ top: selectAreaOrigin.top,
174
+ width: event.pointer.x - selectAreaOrigin.left,
175
+ height: event.pointer.y - selectAreaOrigin.top,
176
+ ponits: [selectAreaOrigin.left, selectAreaOrigin.top, event.pointer.x, event.pointer.y]
177
+ });
178
+ this.canvas.add(this.selectArea);
179
+ this.canvas.renderAll();
180
+ },
181
+ // 每一个折线类别
182
+ createPolyline(polylineType, polylineTypeId) {
183
+ polylineType.dataList.forEach((polylineItem, polylineIndex) => {
184
+ this.drawPolyline(polylineItem, polylineIndex, polylineType, polylineTypeId);
185
+ });
186
+ },
187
+ // 绘制左侧折线标题
188
+ drawPolylineTitle() {
189
+ const { canvasHeight, treeTableminCellWidth, originX, xScaleList, xScaleCellList, endX } = this.propItems;
190
+ const leftYScalevalue = this.polyline.find(v => v.position === 'left');
191
+ const lableMargin = leftYScalevalue.lableMargin || [5, 30];
192
+ const lableLineHeight = leftYScalevalue.lableLineHeight || 20;
193
+ const style = leftYScalevalue.style || { fontSize: 12, fill: '#000' };
194
+
195
+ const leftTitleList = [];
196
+
197
+ const minTime = Math.min(...xScaleList);
198
+ const maxTime = Math.max(...xScaleList);
199
+ this.polyline.forEach((item, index) => {
200
+ item.dataList.forEach((v, i) => {
201
+ // 配置是否可拖动标题图标批量增加-
202
+ const isAdd = v.list.length
203
+ ? !v.list.some(k => {
204
+ return isEffectiveNode(k) && new Date(k.time).getTime() >= minTime && new Date(k.time).getTime() < xScaleCellList[1].time;
205
+ })
206
+ : true;
207
+ let rightLimit = endX;
208
+ const point = v.list.find(k => isEffectiveNode(k) && new Date(k.time).getTime() >= xScaleCellList[1].time && new Date(k.time).getTime() <= maxTime);
209
+ const limitObj =
210
+ point &&
211
+ JSON.parse(JSON.stringify(xScaleCellList))
212
+ .reverse()
213
+ .find(k => new Date(point.time).getTime() > k.time);
214
+ limitObj && (rightLimit = limitObj.x);
215
+ point && !limitObj && new Date(point.time).getTime() === maxTime && (rightLimit = xScaleCellList[xScaleCellList.length - 1].x);
216
+ leftTitleList.push({
217
+ title: v.title,
218
+ type: v.type,
219
+ pointAttr: v.pointAttr,
220
+ lineAttr: v.lineAttr,
221
+ polylineTypeId: index,
222
+ polylineIndex: i,
223
+ isAdd,
224
+ rightLimit
225
+ });
226
+ });
227
+ });
228
+
229
+ // 配置左侧title图标(但不是折线项目)
230
+ if (this.other?.list && Array.isArray(this.other.list)) {
231
+ const otherList = this.other.list.map(v => {
232
+ return {
233
+ title: v.name,
234
+ type: v.type,
235
+ pointAttr: v,
236
+ isAdd: false,
237
+ polylineTypeId: -1,
238
+ polylineIndex: -1
239
+ };
240
+ });
241
+ if (this.other.position && this.other.position === 'top') {
242
+ leftTitleList.push(...otherList);
243
+ } else {
244
+ leftTitleList.unshift(...otherList);
245
+ }
246
+ }
247
+
248
+ const eventStyle = { selectable: false, evented: false };
249
+ const types = ['arrow', '+', 'shjs', 'shks', 'cg', 'jxtq'];
250
+ leftTitleList.forEach(async (v, i) => {
251
+ const baseTop = canvasHeight - lableMargin[1];
252
+ const top = baseTop - i * lableLineHeight;
253
+ const left = lableMargin[0] + treeTableminCellWidth;
254
+ const pointId = `${v.polylineTypeId}_${v.polylineIndex}_-1_polylinePoint_'${new Date().getTime()}_isTitle`;
255
+ const { fill: fillColor = '#000', stroke } = v.pointAttr;
256
+ let text = new this.fabric.Text(String(v.title), {
257
+ fontSize: style.fontSize,
258
+ fill: fillColor && stroke ? stroke : fillColor,
259
+ left,
260
+ top,
261
+ originY: 'center',
262
+ ...defaultVaule.style,
263
+ id: pointId
264
+ });
265
+ const isOriginXCenter = types.includes(v.type);
266
+ const iconLeft = isOriginXCenter ? text.width + text.left + 10 : text.width + text.left + 5;
267
+ const option = {
268
+ left: iconLeft,
269
+ top,
270
+ ...v.pointAttr,
271
+ originY: 'center',
272
+ originX: isOriginXCenter ? 'center' : 'left',
273
+ lineAttr: v.lineAttr || '',
274
+ pointAttr: v.pointAttr,
275
+ id: pointId,
276
+ polylineTypeId: v.polylineTypeId,
277
+ polylineIndex: v.polylineIndex,
278
+ ...(v.isAdd && this.eventStyle.evented ? {} : eventStyle),
279
+ rightLimit: v.rightLimit || ''
280
+ };
281
+ let ele;
282
+ if (v.type === 'img' && v.pointAttr?.iconClassName) {
283
+ ele = await this.createImage(v.pointAttr.iconClassName, v.pointAttr);
284
+ }
285
+ const icon = await this.createPoint(v.type, {
286
+ ele: ele || '',
287
+ ...option
288
+ });
289
+ icon.hasControls = icon.hasBorders = false;
290
+ let originTop = top;
291
+ let cloneIconId;
292
+ icon.on('moving', () => {
293
+ // 优化左侧图标太小不容易选中的问题
294
+ if (!icon.url && icon.scaleX == 1.8 && icon.scaleY == 1.8) {
295
+ icon.set({
296
+ scaleX: 1,
297
+ scaleY: 1
298
+ });
299
+ this.canvas.renderAll();
300
+ }
301
+
302
+ if (!cloneIconId) {
303
+ cloneIconId = '_polylinePoint_temp_' + new Date().getTime();
304
+ icon.clone(clonedObj => {
305
+ clonedObj.set({
306
+ id: cloneIconId // 此id必须,用于remove节点的时候
307
+ });
308
+ clonedObj.bringForward();
309
+ this.canvas.add(clonedObj);
310
+ this.canvas.renderAll();
311
+ });
312
+ }
313
+
314
+ icon.set({ originX: 'center' });
315
+ this.moveLimit(icon);
316
+ icon.left >= originX && icon.left < originX + 5 && (originTop = icon.top);
317
+ if (icon.left >= originX) {
318
+ // this.addPoint(icon, originX, originTop);
319
+ this.showDrapPopup(icon);
320
+ } else {
321
+ this.isDropVisible = false;
322
+ }
323
+ this.canvas.renderAll();
324
+ });
325
+ // 标题折线点停止拖拽后 更新
326
+ icon.on('moved', () => {
327
+ icon.set({
328
+ id: icon.id.replace('_isTitle', '')
329
+ });
330
+ this.canvas.renderAll();
331
+ this.removeTitle();
332
+ this.removePolyline(cloneIconId);
333
+
334
+ if (icon.id.includes('_polylinePoint_') && this.isDropVisible) {
335
+ this.isDropVisible = false;
336
+ if (icon.left >= originX) {
337
+ // this.setEmitFunction(icon);
338
+ this.setAddPointList(icon);
339
+ this.$emit('pointOperation', 'increasePointBatch', this.addPointList);
340
+ this.addPointList = [];
341
+ }
342
+ }
343
+ this.removePolyline(icon.id);
344
+ // 重绘折线标题
345
+ this.drawPolylineTitle();
346
+ });
347
+
348
+ // 优化左侧图标太小不容易选中的问题
349
+ if (icon.evented && !icon.url) {
350
+ const maxVal = Math.max(icon.width * icon.scale, icon.height * icon.scale);
351
+ if (maxVal < 7) {
352
+ icon.set({
353
+ scaleX: 1.8,
354
+ scaleY: 1.8
355
+ });
356
+ }
357
+ }
358
+
359
+ this.canvas.add(text, icon);
360
+ this.canvas.requestRenderAll();
361
+ });
362
+ },
363
+ // 点移动限制
364
+ moveLimit(point) {
365
+ point.setCoords();
366
+ const leftLimit = this.propItems.treeTableminCellWidth;
367
+ const rightLimit = point.rightLimit;
368
+ if (point.top < this.propItems.originY) {
369
+ point.set('top', this.propItems.originY);
370
+ }
371
+ if (point.top > this.propItems.endY) {
372
+ point.set('top', this.propItems.endY);
373
+ }
374
+ if (point.left < leftLimit) {
375
+ point.set('left', leftLimit);
376
+ }
377
+ if (point.left > rightLimit) {
378
+ point.set('left', rightLimit);
379
+ }
380
+ },
381
+ removeTitle() {
382
+ this.canvas.forEachObject(obj => {
383
+ if (obj.id && obj.id.includes('isTitle')) {
384
+ this.canvas.remove(obj);
385
+ }
386
+ });
387
+ },
388
+ isLimit(time) {
389
+ const minMinute = Math.min(...this.propItems.xScaleList);
390
+ const maxMinute = Math.max(...this.propItems.xScaleList);
391
+ const getTime = new Date(time).getTime();
392
+ return getTime >= minMinute && getTime <= maxMinute;
393
+ },
394
+ // 创建危急值和差异值
395
+ createAttrVaule(attr, polyline, polylineIndex, polylineType, polylineTypeId) {
396
+ const { list } = polyline;
397
+ const valueObj = polyline[attr] || {};
398
+ if (!valueObj.max && !valueObj.min) return;
399
+
400
+ const { originX, endX } = this.propItems;
401
+ const isMax = valueObj.max?.show && valueObj.max?.value && list.some(v => +v.value >= +valueObj.max.value && this.isLimit(v.time));
402
+ const isMin = valueObj.min?.show && valueObj.min?.value && list.some(v => +v.value <= +valueObj.min.value && this.isLimit(v.time));
403
+ const lines = [];
404
+
405
+ const drawLine = ({ value, lineStyle }) => {
406
+ const top = this.cumputedY(polylineType, +value);
407
+ return this.drawLine([originX, top, endX, top], { polylineIndex, lineIndex: 0, polylineTypeId, ...(lineStyle || defaultVaule.criticalStyle) });
408
+ };
409
+ const drawText = ({ name, nameStyle }, top) => {
410
+ return new this.fabric.Text(String(name || ''), {
411
+ ...defaultVaule.style,
412
+ ...defaultVaule.textStyle,
413
+ ...nameStyle,
414
+ originY: 'bottom',
415
+ left: originX + 5,
416
+ top: top - 5
417
+ });
418
+ };
419
+
420
+ if (isMax) {
421
+ const line = drawLine(valueObj.max);
422
+ const text = drawText(valueObj.max, line.top);
423
+ line.text = text;
424
+ lines.push(line, text);
425
+ }
426
+ if (isMin) {
427
+ const line = drawLine(valueObj.min);
428
+ const text = drawText(valueObj.min, line.top);
429
+ line.text = text;
430
+ lines.push(line, text);
431
+ }
432
+ this.canvas.add(...lines);
433
+ this.canvas.renderAll();
434
+ },
435
+ /**
436
+ * 绘制一条折线
437
+ * @param {polyline} 折线属性
438
+ * @param {polyline.type} 折线点形状 circle: 圆 | triangle: 三角
439
+ * @param {polyline.pointAttr} 参数 fill: 填充色 | stroke: 描边色 | strokeWidth: 宽度 | radius: 半径 | width: 宽度 | height: 高度
440
+ * @param {polyline.list} 折线点坐标数组
441
+ * @param {polylineIndex} 当前折线 index
442
+ */
443
+ drawPolyline(polyline, polylineIndex, polylineType, polylineTypeId) {
444
+ const { originY, endY } = this.propItems;
445
+ const { pointAttr, lineAttr } = polyline;
446
+ this._iconClassName = polyline.type === 'img' && pointAttr.iconClassName ? pointAttr.iconClassName : '';
447
+
448
+ const list = polyline.list;
449
+ const pointList = [];
450
+ let lineList = [];
451
+ const _this = this;
452
+ function getPointer(point) {
453
+ let isInit = false;
454
+ let points = null;
455
+ if (isEffectiveNode(point) && _this.isLimit(point.time)) {
456
+ const x = _this.cumputedX(point.time);
457
+ const y = _this.cumputedY(polylineType, point.value);
458
+ isInit = y < originY || y > endY;
459
+ points = [x, y < originY ? originY : y > endY ? endY : y];
460
+ }
461
+ return { isInit, points };
462
+ }
463
+ list.forEach((linePoints, index) => {
464
+ // 当前点
465
+ let { points, isInit } = getPointer(linePoints);
466
+
467
+ // 下一个点
468
+ const nextPoints = list[index + 1];
469
+ let { points: nextPoint } = getPointer(nextPoints);
470
+
471
+ let line;
472
+ let point;
473
+ let previousLine;
474
+ line = points && nextPoint ? this.drawLine([...points, ...nextPoint], { polylineIndex, polylineTypeId, lineIndex: index, ...lineAttr }) : null;
475
+ previousLine = lineList[index - 1];
476
+ const pointOthers = { polylineTypeId, polylineIndex, pointIndex: index, ...pointAttr, lineAttr };
477
+ if (points) {
478
+ pointOthers.time = linePoints.time;
479
+ point = previousLine ? this.drawPoint(...points, previousLine, line, polyline.type, pointOthers, isInit) : this.drawPoint(...points, null, line, polyline.type, pointOthers, isInit);
480
+ }
481
+ lineList.push(line);
482
+ point && pointList.push(point);
483
+ });
484
+
485
+ Promise.all(pointList).then(res => {
486
+ const polylineObj = this.polylinePointList.find(v => v.polylineTypeId == polylineTypeId && v.polylineIndex == polylineIndex);
487
+ const pointerList = res.map(v => v.left);
488
+ if (polylineObj) {
489
+ Object.assign(polylineObj, {
490
+ pointerList,
491
+ pointList: res
492
+ });
493
+ polylineObj.pointerList = pointerList;
494
+ } else {
495
+ this.polylinePointList.push({
496
+ polylineTypeId,
497
+ polylineIndex,
498
+ pointerList,
499
+ color: lineAttr.stroke,
500
+ pointList: res
501
+ });
502
+ }
503
+ lineList = lineList.filter(v => v);
504
+ let prevPoint = null;
505
+ res = res.filter(v => {
506
+ if (v && prevPoint) {
507
+ prevPoint.nextPoint = v; // 记录下一个点 id
508
+ v.prevPoint = prevPoint;
509
+ }
510
+ prevPoint = v || prevPoint;
511
+ return v;
512
+ });
513
+ this.canvas.add(...lineList, ...res);
514
+ this.canvas.requestRenderAll();
515
+ });
516
+
517
+ this.createAttrVaule('critical', polyline, polylineIndex, polylineType, polylineTypeId);
518
+ this.createAttrVaule('diffValue', polyline, polylineIndex, polylineType, polylineTypeId);
519
+ },
520
+ // 计算y轴坐标
521
+ cumputedY(polylineType, linePointY) {
522
+ const { position = 'left', list = [] } = polylineType;
523
+ const key = position === 'left' ? 'Left' : 'Right';
524
+ const yScaleCell = this.propItems[`yScaleCell${key}`];
525
+ const yCellUnit = linePointY - Math.min(...list);
526
+ return this.propItems.endY - yScaleCell * yCellUnit;
527
+ },
528
+ /**
529
+ * 绘制折线点
530
+ * @param {left, top} 折线点坐标
531
+ * @param {line1, line2} 与折线点关联的线
532
+ * @param {type} 折线点类型
533
+ * @param {others} 参数 polylineIndex | pointIndex | ...polyline.pointAttr
534
+ */
535
+ async drawPoint(left, top, line1, line2, type, others, isInit) {
536
+ const pointId = `${others.polylineTypeId}_${others.polylineIndex}_${others.pointIndex}_polylinePoint_${new Date().getTime()}`;
537
+ const ele = this._iconClassName && (await this.createImage(this._iconClassName, others));
538
+ let point = await this.createPoint(type, {
539
+ id: pointId,
540
+ left: left,
541
+ top: top,
542
+ ele: ele || '',
543
+ ...defaultVaule.pointStyle,
544
+ ...others,
545
+ ...(!this.eventStyle.evented
546
+ ? {
547
+ lockMovementX: true,
548
+ lockMovementY: true
549
+ }
550
+ : {})
551
+ });
552
+ point.hasControls = point.hasBorders = false;
553
+ line1 && (point.line1 = line1);
554
+ line2 && (point.line2 = line2);
555
+ point.on('mouseover', () => {
556
+ this.showDrapPopup(point);
557
+ this.pointToFront(point);
558
+ });
559
+ point.on('mouseout', () => {
560
+ this.isDropVisible = false;
561
+ });
562
+ // 折线点移动中 实时更新相关联的线的坐标
563
+ if (this.eventStyle.evented) {
564
+ point.on('moving', () => {
565
+ this.pointMoveLimit(point, false);
566
+ this.pointMoveUpdateLine(point);
567
+ this.addPoint(point, left, top);
568
+ this.canvas.renderAll();
569
+ this.showDrapPopup(point);
570
+ });
571
+ // 折线点停止拖拽后 更新polyline中对应的坐标
572
+ point.on('moved', () => {
573
+ if (point && point.id.includes('_polylinePoint_')) {
574
+ this.isDropVisible = false;
575
+ this.setEmitFunction(point);
576
+ }
577
+ });
578
+ point.on('mouseup', event => {
579
+ const { button, isClick } = event;
580
+ if (button == 1 && isClick) {
581
+ // 可配置批量删除当前项目上的点了
582
+ this.currentDelPoint = point;
583
+ }
584
+ });
585
+ }
586
+
587
+ // 超出表格最高或最低值则需要触发pointChange事件并且不能更新视图,否则连接节点判断不会成功
588
+ isInit && this.setEmitFunction(point, isInit);
589
+
590
+ return point;
591
+ },
592
+ // 拖动停止后 设置回调触发方法
593
+ setEmitFunction(point, isInit = false) {
594
+ const { data, x, y } = this.getValue(point);
595
+ Object.assign(data, {
596
+ value: { time: x, value: y },
597
+ isInit
598
+ });
599
+ if (this.addPointList.length === 0) {
600
+ this.$emit('pointChange', data);
601
+ } else {
602
+ const lastPoint = this.addPointList.at(-1);
603
+ const position = this.polyline[point.polylineTypeId].position;
604
+ // 如果是重合/连线节点
605
+ if (point.get('scaleX') !== point.scale) {
606
+ const addOjb = {
607
+ ...lastPoint,
608
+ value: {
609
+ time: this.getXValue(this._concatPoint?.left),
610
+ value: this.getYValue(position, this._concatPoint?.top)
611
+ }
612
+ };
613
+ if (lastPoint.value.time == addOjb.value.time) {
614
+ this.addPointList.splice(-1, 1, addOjb);
615
+ } else {
616
+ this.addPointList.push(addOjb);
617
+ }
618
+ this.addPointList.forEach(v => Object.assign(v, { isConcat: true, concatIndex: this._concatIndex }));
619
+ } else if (point.line2 || (point.nextPoint && lastPoint.left >= point.nextPoint.left)) {
620
+ // 1、存在右连线 2、无右侧连线,并且存在下一个节点的情况
621
+ this.addPointList = [];
622
+ this.repaintPolyline(position, point.polylineIndex);
623
+ return;
624
+ }
625
+ !point.line2 && this.addPointList.splice(0, 1);
626
+ !point.id.includes('isTitle') && this.removePolyline(point.id);
627
+ if (this.addPointList.length > 0) {
628
+ this.$emit('pointOperation', 'increasePointBatch', this.addPointList);
629
+ this.addPointList = [];
630
+ } else {
631
+ this.repaintPolyline(position, point.polylineIndex);
632
+ }
633
+ }
634
+ },
635
+ // 设置批量新增数据
636
+ setAddPointList(point) {
637
+ const { data, x, y } = this.getValue(point);
638
+ data.value = { time: x, value: y };
639
+ data.left = point.left;
640
+ data.top = top;
641
+ this.addPointList.push(data);
642
+ },
643
+ /**
644
+ * @description: 拖动批量新增节点
645
+ * @param {*} point 拖动的最后一个节点
646
+ * @param {*} originLeft 拖动的源节点的left值
647
+ * @param {*} originTop 拖动的源节点的top值
648
+ * @return {*}
649
+ */
650
+ async addPoint(point, originLeft, originTop) {
651
+ const { xCellWidth, table, originX } = this.propItems;
652
+ // const addPointSpaceGridNumbers = table.addPointSpaceGridNumbers || [];
653
+ let addPointSpaceGridNumber = table.addPointSpaceGridNumber || 1;
654
+
655
+ let { left, top } = point;
656
+ const currentLeft = left;
657
+
658
+ const startLength = this.addPointList.length;
659
+ const n = 6; // 拖动范围,在需要增加节点的刻度左右吸入的范围值
660
+ const spaceWidth = xCellWidth * addPointSpaceGridNumber;
661
+ const residue = (left - originLeft) % spaceWidth;
662
+
663
+ const condition = residue > spaceWidth - n || residue < n;
664
+ const conditionNoLine2 = !point.line2 && left > originLeft;
665
+ const conditionHasLine2 = point.line2 && left > originLeft + spaceWidth;
666
+
667
+ // if (!point.line2 && left > originLeft && condition) {
668
+ if (condition && (conditionNoLine2 || conditionHasLine2)) {
669
+ if (residue > spaceWidth - n) left = left - residue + spaceWidth;
670
+ if (residue < n) left = left - residue;
671
+ point.set({
672
+ left
673
+ });
674
+
675
+ const checkPoints = () => {
676
+ const PointLens = Math.floor((left - originLeft) / spaceWidth);
677
+ if (PointLens > 1 && this.addPointList?.length < PointLens) {
678
+ for (let k = 0; k < PointLens; k++) {
679
+ if (k > 0) {
680
+ const curLeft = originLeft + spaceWidth * k;
681
+ // 判断this.addPointList中的left是否包含curLeft,不包含就说明是漏了
682
+ const isNoLack = this.addPointList.some(v => v.left === curLeft);
683
+ if (!isNoLack) {
684
+ let index;
685
+ const pointObj = this.addPointList.find((v, i) => {
686
+ if (v.left > curLeft) {
687
+ index = i;
688
+ }
689
+ return v.left > curLeft;
690
+ });
691
+ if (pointObj) {
692
+ let cloneObj = JSON.parse(JSON.stringify(pointObj));
693
+ cloneObj.value.time = this.getXValue(curLeft);
694
+ cloneObj.left = curLeft;
695
+ this.addPointList.splice(index, 0, cloneObj);
696
+ }
697
+ }
698
+ }
699
+ }
700
+ }
701
+ };
702
+
703
+ // const i = Math.floor((left - originLeft) / spaceWidth);
704
+ // 复制点和线
705
+ if (conditionNoLine2 || conditionHasLine2) {
706
+ // point.line1 && this.removePolyline(point.line1.id);
707
+ setPointLineColor(point, 'transparent', conditionHasLine2);
708
+ // if (i === 1) {
709
+ this.addPointList.length == 0 && (await this.clonePoint(point, [point.line1 ? point.line1.x1 : originLeft, point.line1 ? point.line1.y1 : originTop, originLeft, originTop]));
710
+ // }
711
+ if (this.addPointList.every(v => v.left !== left)) {
712
+ // const points1 = i === 1 ? [originLeft, originTop] : point.prePoints;
713
+ const points1 = this.addPointList.length == 0 ? [originLeft, originTop] : point.prePoints;
714
+ points1 && (await this.clonePoint(point, [...points1, left, top]));
715
+ // this.setAddPointList(point);
716
+ // 调用上面方法就会导致线段绘制失败,目前还不知道为啥子
717
+ const { data, x, y } = this.getValue(point);
718
+ data.value = { time: x, value: y };
719
+ data.left = point.left;
720
+ data.top = top;
721
+ this.addPointList.push(data);
722
+ }
723
+ checkPoints();
724
+ }
725
+ }
726
+
727
+ // 断点相连
728
+ function concatPoint(target) {
729
+ const { left: targetLeft, top: targetTop } = target;
730
+ point.setCoords();
731
+ if (left >= targetLeft - n && left <= targetLeft + n && top <= targetTop + n && top >= targetTop - n) {
732
+ point.set({
733
+ left: targetLeft,
734
+ top: targetTop,
735
+ scaleX: point.scale === 1 ? 2.5 : 0.1,
736
+ scaleY: point.scale === 1 ? 2.5 : 0.1
737
+ });
738
+ } else {
739
+ point.set({
740
+ scaleX: point.scale,
741
+ scaleY: point.scale
742
+ });
743
+ }
744
+ }
745
+
746
+ // 检查是否显示重合连线放大节点的标识
747
+ const { polylineTypeId, polylineIndex } = point;
748
+ const polylineObj = this.polylinePointList.find(v => v.polylineTypeId == polylineTypeId && v.polylineIndex == polylineIndex);
749
+ polylineObj?.pointList.forEach(v => v.bringToFront());
750
+ if (point.nextPoint) {
751
+ this._concatIndex = polylineObj?.pointerList.findIndex(v => v + n > left && v > originLeft);
752
+ if (!~this._concatIndex) {
753
+ point.set({
754
+ scaleX: point.scale,
755
+ scaleY: point.scale
756
+ });
757
+ } else {
758
+ concatPoint(polylineObj?.pointList[this._concatIndex]);
759
+ }
760
+ this._concatPoint = polylineObj?.pointList?.[this._concatIndex] || null;
761
+
762
+ // 被覆盖节点置灰配置
763
+ polylineObj?.pointerList.forEach((v, i) => {
764
+ const obj = polylineObj?.pointList[i];
765
+ if (v > originLeft && v < left) {
766
+ // 此处需要递归遍历group节点的所有子节点,然后更改其颜色
767
+ resetPointColor([obj], '#999');
768
+ obj.line2?.set({ stroke: '#999' });
769
+ } else {
770
+ resetPointColor([obj], polylineObj.color);
771
+ obj.line2?.set({ stroke: polylineObj.color });
772
+ }
773
+ });
774
+ if ((point.line1 || point.line2) && left < originLeft + spaceWidth) {
775
+ setPointLineColor(point, polylineObj.color, true);
776
+ } else {
777
+ setPointLineColor(point, 'transparent', true);
778
+ }
779
+ }
780
+
781
+ // 如果往回拖动则删除经过的已存在的节点
782
+ if (startLength > 0 && currentLeft <= this.addPointList[startLength - 1].left) {
783
+ this.removePolyline('increasePointBatch', left);
784
+ this.addPointList = this.addPointList.filter(v => v.left < left);
785
+ const endLength = this.addPointList.length;
786
+ if (endLength === 0) {
787
+ setPointLineColor(point, polylineObj?.color, conditionHasLine2);
788
+ this.removePolyline('increasePointBatch', originLeft);
789
+ }
790
+ if (endLength > 0) {
791
+ point.set({
792
+ prePoints: [this.addPointList[endLength - 1].left, this.addPointList[endLength - 1].top]
793
+ });
794
+ }
795
+ }
796
+ },
797
+ clonePoint(point, points) {
798
+ let evented, selectable, lineIndex, polylineIndex, lineAttr, polylineTypeId;
799
+ evented = selectable = false;
800
+ lineAttr = { ...point.lineAttr };
801
+ lineIndex = point.pointIndex;
802
+ polylineIndex = point.polylineIndex;
803
+ polylineTypeId = point.polylineTypeId;
804
+
805
+ return new Promise((resolve, reject) => {
806
+ point.clone(clonedObj => {
807
+ clonedObj.set({
808
+ left: points[2],
809
+ top: points[3],
810
+ idClone: 'increasePointBatch',
811
+ id: '_polylinePoint_' + new Date().getTime(), // 此id必须,用于remove节点的时候
812
+ polylineTypeId,
813
+ polylineIndex: point.polylineIndex,
814
+ scaleX: point.scale,
815
+ scaleY: point.scale
816
+ });
817
+ clonedObj.hasControls = clonedObj.hasBorders = false;
818
+ point.set({
819
+ prePoints: [point.left, point.top]
820
+ });
821
+ const line = this.drawLine([...points], { evented, selectable, ...lineAttr, polylineIndex, polylineTypeId, lineIndex });
822
+ clonedObj.line1 = line;
823
+ this.canvas.sendBackwards(line);
824
+ clonedObj.bringForward();
825
+ this.canvas.add(clonedObj);
826
+ this.canvas.renderAll();
827
+ resolve(clonedObj);
828
+ });
829
+ });
830
+ },
831
+ getValue(point) {
832
+ const x = this.getXValue(point.left);
833
+ if (point.id) {
834
+ const data = this.getDataById(point.id);
835
+ const position = data.position;
836
+ const y = this.getYValue(position, point.top);
837
+ return { data, x, y };
838
+ } else {
839
+ const left = this.getYValue('left', point.top);
840
+ const right = this.getYValue('right', point.top);
841
+ return { x, left, right };
842
+ }
843
+ },
844
+ getYValue(position, top) {
845
+ const key = position == 'left' ? 'Left' : 'Right';
846
+ const list = this.polyline.find(item => item.position == position)?.list || [];
847
+ const v = list.length ? Math.min(...list) : 0;
848
+ const y = (this.propItems.endY - top) / this.propItems[`yScaleCell${key}`] + v;
849
+ return y;
850
+ },
851
+ /**
852
+ * 绘制折线点
853
+ * @param {lines} 线坐标 [x1, y1, x2, y2]
854
+ * @param {others} 参数 polylineIndex | pointIndex | ...polyline.lineAttr
855
+ */
856
+ drawLine(lines, others) {
857
+ const lineId = `${others.lineIndex}_${others.polylineIndex}_polylineLine_${new Date().getTime()}`;
858
+ const line = new this.fabric.Line(lines, {
859
+ id: lineId,
860
+ hoverCursor: 'default',
861
+ // objectCaching: false,
862
+ selectable: false,
863
+ evented: false,
864
+ ...others
865
+ });
866
+ // line.hasControls = line.hasBorders = false;
867
+ // line.on('mouseover', () => {
868
+ // // 将当条线段以及点层级置顶
869
+ // // 此处有问题,线段定位是斜角形成的整个矩形,会挡住其他点或线段的mouseover事件
870
+ // });
871
+
872
+ return line;
873
+ },
874
+ showDrapPopup(point) {
875
+ this.isDropVisible = true;
876
+ this.dropPos = {
877
+ left: point.left,
878
+ top: point.top,
879
+ margin: { top: this.propItems.yCellHeight }
880
+ };
881
+ let { x, y, data } = this.getValue(point);
882
+ const getY = y => {
883
+ if (data?.position == 'right') {
884
+ if (y.toString().includes('.')) {
885
+ const [m, n] = y.toString().split('.');
886
+ return (parseFloat(`${m}.${n.slice(0, 1)}`) * 10) / 10;
887
+ }
888
+ return y;
889
+ } else {
890
+ return Math.round(y);
891
+ }
892
+ };
893
+ this.dropVal = {
894
+ title: data.title,
895
+ list: [
896
+ { id: '11', name: '时间', value: x },
897
+ { id: '22', name: '值', value: getY(y) }
898
+ ]
899
+ };
900
+ },
901
+ // 折线点移动时 setCoords()方法手动更新相关联的线坐标
902
+ pointMoveUpdateLine(point) {
903
+ if (point.line1) {
904
+ point.line1.setCoords();
905
+ point.line1.set({ x2: point.left, y2: point.top });
906
+ }
907
+ if (point.line2) {
908
+ point.line2.setCoords();
909
+ point.line2.set({ x1: point.left, y1: point.top });
910
+ }
911
+ },
912
+ // 打开右键菜单
913
+ openRightModal(event) {
914
+ this.rightPos = {
915
+ clientX: event.e.clientX || event.e.pageX,
916
+ clientY: event.e.clientY || event.e.pageY
917
+ };
918
+ this.isRightVisible = false;
919
+ const target = event.target;
920
+ const id = target ? target.id : '';
921
+ this.$nextTick(() => {
922
+ if (id && id.includes('_polylinePoint_')) {
923
+ this._currentPoint = target;
924
+ let nodeConnect = [];
925
+ if (this.propItems.operable.connect) {
926
+ if (!target.line2 && target.nextPoint) {
927
+ nodeConnect = rightClickNodeConnect.slice(0, 1);
928
+ }
929
+ if (!target.line1 && target.prevPoint) {
930
+ nodeConnect = rightClickNodeConnect.slice(1);
931
+ }
932
+ if (!target.line1 && !target.line2 && target.prevPoint && target.nextPoint) {
933
+ nodeConnect = rightClickNodeConnect.slice();
934
+ }
935
+ }
936
+ this.rightClickNode = Object.freeze(
937
+ rightClickNode
938
+ .slice(0, 1)
939
+ .concat(nodeConnect)
940
+ .concat(rightClickNode.slice(-1))
941
+ );
942
+ this.isRightVisible = true;
943
+ } else if (!id) {
944
+ const { operable } = this.propItems;
945
+ this._currentPoint = null;
946
+ this.rightClickNode = Object.freeze(operable.set ? rightClickNode.slice(0, 2) : rightClickNode.slice(0, 1));
947
+ this.isRightVisible = true;
948
+ }
949
+ });
950
+ },
951
+ // 关闭右键菜单,打开添加节点弹窗表单
952
+ handleRightClick({ type, name }) {
953
+ this.isRightVisible = false;
954
+ const id = this._currentPoint?.id;
955
+ const { left, top } = this._active;
956
+ let data = id ? this.getDataById(id) : this.getValue({ left, top });
957
+ if (type == 'add') {
958
+ if (name.includes('左')) {
959
+ data.range = {
960
+ time: [this._currentPoint.prevPoint.time, this._currentPoint.time],
961
+ direction: 'left'
962
+ };
963
+ }
964
+ if (name.includes('')) {
965
+ data.range = {
966
+ time: [this._currentPoint.time, this._currentPoint.nextPoint.time],
967
+ direction: 'right'
968
+ };
969
+ }
970
+ }
971
+ this.$emit('pointOperation', type, data);
972
+ this._currentPoint = null;
973
+ },
974
+ getDataById(id) {
975
+ const arr = id.replace(/_?[a-zA-Z]+.+$/, '').split('_');
976
+ const [typeIndex, lineIndex, pointIndex] = arr;
977
+ const data = this.polyline[typeIndex];
978
+ const value = data?.dataList?.[lineIndex]?.list?.[pointIndex]?.data || '';
979
+ return {
980
+ title: data?.dataList?.[lineIndex].title || '',
981
+ position: data.position,
982
+ dataIndex: lineIndex,
983
+ pointIndex: pointIndex,
984
+ data: value
985
+ };
986
+ },
987
+ isOnePolyLine(obj, polylineTypeId, polylineIndex) {
988
+ // 排除左侧标题
989
+ const isPolyLine = obj.polylineTypeId === polylineTypeId && obj.polylineIndex === polylineIndex;
990
+ if (obj.id && /_polyline(Point|Line)_/.test(obj.id) && !obj.id.includes('isTitle') && isPolyLine) return true;
991
+ return;
992
+ },
993
+ // 将当条线段以及点层级置顶
994
+ // 线段不能先置顶,会挡住其他线段上的点
995
+ pointToFront(point) {
996
+ this.canvas.forEachObject(obj => {
997
+ if (this.isOnePolyLine(obj, point.polylineTypeId, point.polylineIndex)) {
998
+ this.canvas.bringToFront(obj);
999
+ }
1000
+ });
1001
+ },
1002
+ removePolyline(id, left, position, polylineIndex) {
1003
+ // 根据id或者idClone删除
1004
+ if (id) {
1005
+ const pointId = left ? 'idClone' : 'id';
1006
+ const point = this.canvas.getObjects().filter(item => item[pointId] === id);
1007
+ if (point && point.length > 0) {
1008
+ point.forEach(v => {
1009
+ if (left && v.left >= left) {
1010
+ this.canvas.remove(v);
1011
+ v.line1 && this.canvas.remove(v.line1);
1012
+ } else if (!left) {
1013
+ this.canvas.remove(v);
1014
+ v.line1 && this.canvas.remove(v.line1);
1015
+ }
1016
+ });
1017
+ }
1018
+ return;
1019
+ }
1020
+ // 删除一条线
1021
+ if (position && (polylineIndex === 0 || polylineIndex)) {
1022
+ const polylineTypeId = this.polyline.findIndex(v => v.position === position);
1023
+ this.canvas.forEachObject(obj => {
1024
+ if (this.isOnePolyLine(obj, polylineTypeId, polylineIndex)) {
1025
+ obj.text && this.canvas.remove(obj.text);
1026
+ obj.line1 && this.canvas.remove(obj.line1);
1027
+ obj.line2 && this.canvas.remove(obj.line2);
1028
+ this.canvas.remove(obj);
1029
+ }
1030
+ });
1031
+ return;
1032
+ }
1033
+ // 删除折线图上的所有点和线
1034
+ this.canvas.forEachObject(obj => {
1035
+ if (obj.id && /_polyline(Point|Line)_/.test(obj.id) && !obj.id.includes('isTitle')) {
1036
+ obj.text && this.canvas.remove(obj.text);
1037
+ this.canvas.remove(obj);
1038
+ }
1039
+ });
1040
+ },
1041
+ removeSelectArea() {
1042
+ if (this.selectArea) {
1043
+ this.canvas.remove(this.selectArea);
1044
+ this.selectArea = null;
1045
+ }
1046
+ },
1047
+ repaintPolyline(position, dadaIndex) {
1048
+ if (arguments.length < 2) {
1049
+ if (this.polyline.length) {
1050
+ this.removePolyline();
1051
+ }
1052
+ this.polylinePointList = [];
1053
+ this.polyline.forEach((polylineType, polylineTypeId) => {
1054
+ this.createPolyline(polylineType, polylineTypeId);
1055
+ });
1056
+ this.removeTitle();
1057
+ this.drawPolylineTitle();
1058
+ } else {
1059
+ const polylineTypeId = this.polyline.findIndex(v => v.position === position);
1060
+ this.removePolyline(null, null, position, parseInt(dadaIndex));
1061
+ this.drawPolyline(this.polyline[polylineTypeId].dataList[parseInt(dadaIndex)], parseInt(dadaIndex), this.polyline[polylineTypeId], polylineTypeId);
1062
+ }
1063
+ }
1064
+ }
1065
+ };
1066
+ </script>