cnhis-design-vue 2.1.17 → 2.1.18

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