cnhis-design-vue 2.1.24 → 2.1.25

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