@flexem/fc-gui 3.0.0-alpha.180 → 3.0.0-alpha.181

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 (37) hide show
  1. package/bundles/@flexem/fc-gui.umd.js +618 -223
  2. package/bundles/@flexem/fc-gui.umd.js.map +1 -1
  3. package/bundles/@flexem/fc-gui.umd.min.js +4 -4
  4. package/bundles/@flexem/fc-gui.umd.min.js.map +1 -1
  5. package/elements/alarm/alarm-element.d.ts +20 -0
  6. package/elements/alarm/alarm-element.js +312 -70
  7. package/elements/alarm/alarm-element.metadata.json +1 -1
  8. package/elements/base/state-control-element.d.ts +10 -0
  9. package/elements/base/state-control-element.js +21 -3
  10. package/elements/base/state-control-element.metadata.json +1 -1
  11. package/elements/character-display/character-display-element.d.ts +9 -0
  12. package/elements/character-display/character-display-element.js +15 -0
  13. package/elements/character-display/character-display-element.metadata.json +1 -1
  14. package/elements/historical-curve/historical-curve.element.d.ts +1 -0
  15. package/elements/historical-curve/historical-curve.element.js +35 -22
  16. package/elements/historical-curve/historical-curve.element.metadata.json +1 -1
  17. package/elements/main-element.js +2 -1
  18. package/elements/numerical-display/numerical-display-element.d.ts +9 -0
  19. package/elements/numerical-display/numerical-display-element.js +15 -0
  20. package/elements/numerical-display/numerical-display-element.metadata.json +1 -1
  21. package/elements/switch-indicator-light/switch-indicator-light-element.d.ts +9 -0
  22. package/elements/switch-indicator-light/switch-indicator-light-element.js +15 -0
  23. package/elements/switch-indicator-light/switch-indicator-light-element.metadata.json +1 -1
  24. package/elements/view-operation/view-operation.element.js +2 -1
  25. package/gui/gui-view.d.ts +15 -1
  26. package/gui/gui-view.js +78 -4
  27. package/gui/gui-view.metadata.json +1 -1
  28. package/model/alarm/alarm.model.d.ts +1 -0
  29. package/model/character-display/character-display.d.ts +1 -0
  30. package/model/historical-curve/historical-curve.display-settings.d.ts +1 -0
  31. package/model/numerical-display/numerical-display.d.ts +1 -0
  32. package/model/settings/popup/view-popup-location-type.d.ts +2 -1
  33. package/model/settings/popup/view-popup-location-type.js +1 -0
  34. package/model/settings/popup/view-popup-location-type.metadata.json +1 -1
  35. package/model/switch-indicator-light/switch-indicator-light.d.ts +1 -0
  36. package/package.json +1 -1
  37. package/view/popup-view.service.d.ts +1 -1
@@ -29,6 +29,12 @@ export declare class AlarmElement extends ConditionalDisplayElement {
29
29
  private totalCount;
30
30
  private pageSize;
31
31
  private getAlarmDataId;
32
+ private readonly maxVisibleRows;
33
+ private scrollIntervalId;
34
+ private scrollContainer;
35
+ private hoverTarget;
36
+ private rowHeight;
37
+ private isDisposed;
32
38
  constructor(element: HTMLElement, injector: Injector, permissionChecker: PermissionChecker, variableCommunicator: VariableCommunicator, variableStore: VariableStore, alarmsStore: AlarmsStore, signalRAppId: string, systemTextLibraryService?: SystemTextLibraryService, languageService?: LanguageService, guiContext?: GuiContext);
33
39
  dispose(): void;
34
40
  /**
@@ -54,7 +60,21 @@ export declare class AlarmElement extends ConditionalDisplayElement {
54
60
  */
55
61
  private getTimePeriodText;
56
62
  private getAlarmData;
63
+ private refreshCurrentPageData;
64
+ private updateTableContent;
65
+ private getIconScale;
57
66
  private renderElement;
67
+ private buildHeaderRow;
68
+ private buildDataRow;
69
+ private renderPlainTable;
70
+ private renderScrollableTable;
71
+ private bindScrollHoverEvents;
72
+ private unbindScrollHoverEvents;
73
+ private readonly handleScrollMouseEnter;
74
+ private readonly handleScrollMouseLeave;
75
+ private startAutoScroll;
76
+ private scrollOneRow;
77
+ private autoNextPage;
58
78
  private renderOperationArea;
59
79
  private updateTimeRange;
60
80
  private updateQueryTimeRange;
@@ -37,6 +37,15 @@ export class AlarmElement extends ConditionalDisplayElement {
37
37
  this.page = 0;
38
38
  this.totalCount = 0;
39
39
  this.pageSize = 5;
40
+ this.maxVisibleRows = 15;
41
+ this.rowHeight = 0;
42
+ this.isDisposed = false;
43
+ this.handleScrollMouseEnter = () => {
44
+ clearInterval(this.scrollIntervalId);
45
+ };
46
+ this.handleScrollMouseLeave = () => {
47
+ this.startAutoScroll();
48
+ };
40
49
  this.rootElement.selectAll('*').remove();
41
50
  this.setStatusAsLoading();
42
51
  this.logger = injector.get(LOGGER_SERVICE_TOKEN);
@@ -51,7 +60,14 @@ export class AlarmElement extends ConditionalDisplayElement {
51
60
  this.updateQueryTimeRange();
52
61
  if (this.model.filterSetting.alarmType) {
53
62
  this.refreshIntervalId = setInterval(() => {
54
- this.loadFirstPage();
63
+ if (this.model.generalSetting.autoScroll) {
64
+ // 开启滚动时,仅更新数据不重渲染,保持滚动进度
65
+ this.refreshCurrentPageData();
66
+ }
67
+ else {
68
+ // 未开启滚动时,返回第一页
69
+ this.loadFirstPage();
70
+ }
55
71
  this.logger.debug(`[GUI]Refresh Alarm Table:${d3.time.format('%x %X')(new Date())}`);
56
72
  }, this.model.filterSetting.refreshCycle * 60 * 1000);
57
73
  }
@@ -59,7 +75,14 @@ export class AlarmElement extends ConditionalDisplayElement {
59
75
  variableCommunicator.subscribeUserDeviceAlarms(signalRAppId).subscribe(alarms => {
60
76
  alarms.forEach(alarm => {
61
77
  if (this.alarmNames.indexOf(alarm.name) !== -1) {
62
- this.getAlarmData();
78
+ if (this.model.generalSetting.autoScroll) {
79
+ // 开启滚动时,刷新当前页并保持滚动进度
80
+ this.refreshCurrentPageData();
81
+ }
82
+ else {
83
+ // 未开启滚动时,刷新当前页(原有逻辑)
84
+ this.getAlarmData();
85
+ }
63
86
  return;
64
87
  }
65
88
  });
@@ -74,10 +97,11 @@ export class AlarmElement extends ConditionalDisplayElement {
74
97
  }
75
98
  }
76
99
  dispose() {
100
+ this.isDisposed = true;
77
101
  clearInterval(this.refreshIntervalId);
78
- if (this.element) {
79
- this.element.tooltip.hidden(true);
80
- }
102
+ clearInterval(this.scrollIntervalId);
103
+ clearTimeout(this.getAlarmDataId);
104
+ this.unbindScrollHoverEvents();
81
105
  // 取消语种变化订阅
82
106
  if (this.languageChangeSubscription) {
83
107
  this.languageChangeSubscription.unsubscribe();
@@ -199,15 +223,21 @@ export class AlarmElement extends ConditionalDisplayElement {
199
223
  return '';
200
224
  }
201
225
  getAlarmData() {
226
+ clearInterval(this.scrollIntervalId);
227
+ this.unbindScrollHoverEvents();
202
228
  this.rootElement.selectAll('*').remove();
203
229
  this.setStatusAsLoading();
204
230
  clearTimeout(this.getAlarmDataId);
205
231
  this.getAlarmDataId = setTimeout(() => {
232
+ if (this.isDisposed)
233
+ return;
206
234
  this.timePeriods = this.getValidTimePeriods();
207
235
  if (this.model.filterSetting.alarmType) {
208
236
  this.updateQueryTimeRange();
209
237
  const input = new GetAlarmsArgs(this.alarmNames, this.startTime, this.endTime, this.pageSize, this.page * this.pageSize);
210
238
  this.alarmsStore.getHistoryAlarms(input).subscribe(result => {
239
+ if (this.isDisposed)
240
+ return;
211
241
  if (!result.error) {
212
242
  this.totalCount = result.totalCount;
213
243
  this.alarmList = result.items.map(item => [
@@ -223,6 +253,8 @@ export class AlarmElement extends ConditionalDisplayElement {
223
253
  else {
224
254
  const input = new GetAlarmsArgs(this.alarmNames, undefined, undefined, this.pageSize, this.page * this.pageSize);
225
255
  this.alarmsStore.getCurrentAlarms(input).subscribe(result => {
256
+ if (this.isDisposed)
257
+ return;
226
258
  if (!result.error) {
227
259
  this.totalCount = result.totalCount;
228
260
  this.currentAlarmList = result.items;
@@ -243,16 +275,124 @@ export class AlarmElement extends ConditionalDisplayElement {
243
275
  }
244
276
  }, 500);
245
277
  }
278
+ refreshCurrentPageData() {
279
+ var _a;
280
+ if (this.isDisposed)
281
+ return;
282
+ const currentScrollTop = ((_a = this.scrollContainer) === null || _a === void 0 ? void 0 : _a.scrollTop) || 0;
283
+ if (this.model.filterSetting.alarmType) {
284
+ this.updateQueryTimeRange();
285
+ const input = new GetAlarmsArgs(this.alarmNames, this.startTime, this.endTime, this.pageSize, this.page * this.pageSize);
286
+ this.alarmsStore.getHistoryAlarms(input).subscribe(result => {
287
+ if (this.isDisposed)
288
+ return;
289
+ if (!result.error) {
290
+ this.totalCount = result.totalCount;
291
+ this.alarmList = result.items.map(item => [
292
+ item.name,
293
+ item.message,
294
+ item.alarmLevel,
295
+ item.triggeredTime.format('YYYY-MM-DD HH:mm:ss')
296
+ ]);
297
+ this.updateTableContent();
298
+ if (this.scrollContainer) {
299
+ this.scrollContainer.scrollTop = currentScrollTop;
300
+ }
301
+ }
302
+ });
303
+ }
304
+ else {
305
+ const input = new GetAlarmsArgs(this.alarmNames, undefined, undefined, this.pageSize, this.page * this.pageSize);
306
+ this.alarmsStore.getCurrentAlarms(input).subscribe(result => {
307
+ if (this.isDisposed)
308
+ return;
309
+ if (!result.error) {
310
+ this.totalCount = result.totalCount;
311
+ this.currentAlarmList = result.items;
312
+ if (!result.items.length && result.totalCount) {
313
+ this.page = 0;
314
+ this.refreshCurrentPageData();
315
+ return;
316
+ }
317
+ this.alarmList = this.currentAlarmList.map(item => [
318
+ item.name,
319
+ item.message,
320
+ item.alarmLevel,
321
+ item.triggeredTime.format('YYYY-MM-DD HH:mm:ss')
322
+ ]);
323
+ this.updateTableContent();
324
+ if (this.scrollContainer) {
325
+ this.scrollContainer.scrollTop = currentScrollTop;
326
+ }
327
+ }
328
+ });
329
+ }
330
+ }
331
+ updateTableContent() {
332
+ if (!this.element)
333
+ return;
334
+ const useScroll = this.pageSize > this.maxVisibleRows;
335
+ const dataRowHeight = this.model.heights[1] || this.model.heights[0];
336
+ // 查找表格主体
337
+ let tbody;
338
+ if (useScroll) {
339
+ const bodyContainer = this.element.querySelector('div > div:nth-child(2)');
340
+ if (!bodyContainer)
341
+ return;
342
+ const bodyTable = bodyContainer.querySelector('table');
343
+ if (!bodyTable)
344
+ return;
345
+ tbody = bodyTable;
346
+ }
347
+ else {
348
+ const table = this.element.querySelector('table');
349
+ if (!table)
350
+ return;
351
+ tbody = table;
352
+ }
353
+ // 清除现有数据行(保留表头)
354
+ const rows = Array.from(tbody.querySelectorAll('tr'));
355
+ rows.forEach((row, index) => {
356
+ if (index > 0 || useScroll) { // 对于 scrollable 模式,表头在另一个 table,所以这里全部清除
357
+ row.remove();
358
+ }
359
+ });
360
+ // 重新生成数据行
361
+ for (let row = 0; row < this.pageSize; row++) {
362
+ const rowHeight = useScroll ? dataRowHeight : this.model.heights[row + 1];
363
+ tbody.appendChild(this.buildDataRow(row, rowHeight));
364
+ }
365
+ }
366
+ getIconScale() {
367
+ const initSize = this.model.initSize;
368
+ if (!initSize || !initSize.width || !initSize.height) {
369
+ return 1;
370
+ }
371
+ const scaleX = this.model.size.width / initSize.width;
372
+ const scaleY = this.model.size.height / initSize.height;
373
+ return Math.min(scaleX, scaleY);
374
+ }
246
375
  renderElement() {
247
376
  const elementHeight = this.model.size.height;
248
377
  this.element = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
249
378
  this.element.setAttribute('width', this.model.size.width.toString());
250
379
  this.element.setAttribute('height', elementHeight.toString());
251
380
  this.$element.append(this.element);
252
- const table = document.createElement('table');
253
- table.style.cssText = `border: 2px solid ${this.model.generalSetting.stroke};
254
- height: ${elementHeight - 39}`;
255
- let tr = document.createElement('tr');
381
+ const scale = this.getIconScale();
382
+ // 表格与底部操作区之间的预留高度,需要与操作区渲染时使用的同一个 scale 保持一致,
383
+ // 否则表格按未缩放高度占满后,缩放后的操作区会超出元件整体高度
384
+ const operationAreaReservedHeight = 39 * scale;
385
+ const useScroll = this.pageSize > this.maxVisibleRows;
386
+ if (useScroll) {
387
+ this.renderScrollableTable(elementHeight, operationAreaReservedHeight);
388
+ }
389
+ else {
390
+ this.renderPlainTable(elementHeight, operationAreaReservedHeight);
391
+ }
392
+ this.renderOperationArea(this.model.size.width, elementHeight - operationAreaReservedHeight, scale);
393
+ }
394
+ buildHeaderRow() {
395
+ const tr = document.createElement('tr');
256
396
  let fontString = '';
257
397
  if (this.model.generalSetting.headerFont.isItalic) {
258
398
  fontString += 'italic ';
@@ -277,56 +417,34 @@ export class AlarmElement extends ConditionalDisplayElement {
277
417
  border-left-width: ${!col || this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;`;
278
418
  tr.appendChild(th);
279
419
  }
280
- table.appendChild(tr);
420
+ return tr;
421
+ }
422
+ buildDataRow(row, rowHeight) {
281
423
  const levelColors = ['#3695FF', '#FFE900', '#FFA800', '#E84F4F'];
282
- for (let row = 0; row < this.pageSize; row++) {
283
- tr = document.createElement('tr');
284
- tr.style.cssText = `height: ${this.model.heights[row + 1]}px;
285
- background: ${this.model.generalSetting.tableFillColor};
286
- font: ${this.model.generalSetting.fontStyle};
287
- color: ${this.model.generalSetting.fontColor};
288
- text-align: ${this.model.generalSetting.textAlign};
289
- vertical-align: middle;`;
290
- for (let col = 0; col < 4; col++) {
291
- const td = document.createElement('td');
292
- td.style.cssText = `border-style: solid;
293
- border-color: ${this.model.generalSetting.splitLine};
294
- border-top-width: ${this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;
295
- border-left-width: ${!col || this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;`;
296
- if (this.alarmList[row]) {
297
- if (col !== 2) {
298
- td.innerHTML = this.alarmList[row][col];
299
- td.style.cssText += `${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
300
- }
301
- else {
302
- const alarmLevel = document.createElement('div');
303
- let alarmHeight = parseInt(this.model.generalSetting.fontSize) + 15;
304
- if (alarmHeight > this.model.heights[row + 1])
305
- alarmHeight = this.model.heights[row + 1];
306
- let margin = '0 15px';
307
- if (this.model.generalSetting.textAlign === 'left') {
308
- margin = '0 30px 0 0';
309
- }
310
- else if (this.model.generalSetting.textAlign === 'right') {
311
- margin = '0 0 0 30px';
312
- }
313
- alarmLevel.style.cssText = `margin: ${margin};
314
- border-radius: 5px;
315
- background: ${levelColors[this.alarmList[row][col]]};
316
- color: #ffffff;
317
- width: ${this.model.widths[col] - 30}px;
318
- height: ${alarmHeight}px;
319
- max-height: ${this.model.heights[row + 1]}px;
320
- display: flex;
321
- justify-content: center;
322
- align-items: center;
323
- ${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
324
- alarmLevel.innerHTML = this.model.levelText[this.alarmList[row][col]];
325
- td.appendChild(alarmLevel);
326
- }
424
+ const tr = document.createElement('tr');
425
+ tr.style.cssText = `height: ${rowHeight}px;
426
+ background: ${this.model.generalSetting.tableFillColor};
427
+ font: ${this.model.generalSetting.fontStyle};
428
+ color: ${this.model.generalSetting.fontColor};
429
+ text-align: ${this.model.generalSetting.textAlign};
430
+ vertical-align: middle;`;
431
+ for (let col = 0; col < 4; col++) {
432
+ const td = document.createElement('td');
433
+ td.style.cssText = `width: ${this.model.widths[col] / this.model.size.width * 100}%;
434
+ border-style: solid;
435
+ border-color: ${this.model.generalSetting.splitLine};
436
+ border-top-width: ${this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;
437
+ border-left-width: ${!col || this.model.generalSetting.isHiddenColumnSplitLine ? 0 : 1}px;`;
438
+ if (this.alarmList[row]) {
439
+ if (col !== 2) {
440
+ td.innerHTML = this.alarmList[row][col];
441
+ td.style.cssText += `${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
327
442
  }
328
- else if (col === 2) {
443
+ else {
329
444
  const alarmLevel = document.createElement('div');
445
+ let alarmHeight = parseInt(this.model.generalSetting.fontSize) + 15;
446
+ if (alarmHeight > rowHeight)
447
+ alarmHeight = rowHeight;
330
448
  let margin = '0 15px';
331
449
  if (this.model.generalSetting.textAlign === 'left') {
332
450
  margin = '0 30px 0 0';
@@ -335,24 +453,144 @@ export class AlarmElement extends ConditionalDisplayElement {
335
453
  margin = '0 0 0 30px';
336
454
  }
337
455
  alarmLevel.style.cssText = `margin: ${margin};
338
- width: ${this.model.widths[col] - 30}px;`;
456
+ border-radius: 5px;
457
+ background: ${levelColors[this.alarmList[row][col]]};
458
+ color: #ffffff;
459
+ width: ${this.model.widths[col] - 30}px;
460
+ height: ${alarmHeight}px;
461
+ max-height: ${rowHeight}px;
462
+ display: flex;
463
+ justify-content: center;
464
+ align-items: center;
465
+ ${this.model.generalSetting.isUnderline ? 'text-decoration: underline' : ''};`;
466
+ alarmLevel.innerHTML = this.model.levelText[this.alarmList[row][col]];
339
467
  td.appendChild(alarmLevel);
340
468
  }
341
- tr.appendChild(td);
342
469
  }
343
- table.appendChild(tr);
470
+ else if (col === 2) {
471
+ const alarmLevel = document.createElement('div');
472
+ let margin = '0 15px';
473
+ if (this.model.generalSetting.textAlign === 'left') {
474
+ margin = '0 30px 0 0';
475
+ }
476
+ else if (this.model.generalSetting.textAlign === 'right') {
477
+ margin = '0 0 0 30px';
478
+ }
479
+ alarmLevel.style.cssText = `margin: ${margin};
480
+ width: ${this.model.widths[col] - 30}px;`;
481
+ td.appendChild(alarmLevel);
482
+ }
483
+ tr.appendChild(td);
484
+ }
485
+ return tr;
486
+ }
487
+ renderPlainTable(elementHeight, operationAreaReservedHeight) {
488
+ this.scrollContainer = null;
489
+ const table = document.createElement('table');
490
+ table.style.cssText = `border: 2px solid ${this.model.generalSetting.stroke};
491
+ height: ${elementHeight - operationAreaReservedHeight}px;`;
492
+ table.appendChild(this.buildHeaderRow());
493
+ for (let row = 0; row < this.pageSize; row++) {
494
+ table.appendChild(this.buildDataRow(row, this.model.heights[row + 1]));
344
495
  }
345
496
  this.element.appendChild(table);
346
- this.renderOperationArea(this.model.size.width, elementHeight - 39);
497
+ this.hoverTarget = table;
498
+ this.bindScrollHoverEvents();
499
+ this.startAutoScroll();
500
+ }
501
+ renderScrollableTable(elementHeight, operationAreaReservedHeight) {
502
+ const dataRowHeight = this.model.heights[1] || this.model.heights[0];
503
+ this.rowHeight = dataRowHeight;
504
+ const wrapper = document.createElement('div');
505
+ wrapper.style.cssText = `display: flex;
506
+ flex-direction: column;
507
+ box-sizing: border-box;
508
+ border: 2px solid ${this.model.generalSetting.stroke};
509
+ height: ${elementHeight - operationAreaReservedHeight}px;
510
+ overflow: hidden;`;
511
+ const headerWrapper = document.createElement('div');
512
+ headerWrapper.style.cssText = 'flex: none; box-sizing: border-box; overflow: hidden;';
513
+ const headerTable = document.createElement('table');
514
+ headerTable.style.cssText = 'width: 100%; table-layout: fixed;';
515
+ headerTable.appendChild(this.buildHeaderRow());
516
+ headerWrapper.appendChild(headerTable);
517
+ const bodyContainer = document.createElement('div');
518
+ bodyContainer.style.cssText = `flex: none;
519
+ overflow-y: auto;
520
+ overflow-x: hidden;
521
+ height: ${dataRowHeight * this.maxVisibleRows}px;`;
522
+ const bodyTable = document.createElement('table');
523
+ bodyTable.style.cssText = 'width: 100%; table-layout: fixed;';
524
+ for (let row = 0; row < this.pageSize; row++) {
525
+ bodyTable.appendChild(this.buildDataRow(row, dataRowHeight));
526
+ }
527
+ bodyContainer.appendChild(bodyTable);
528
+ wrapper.appendChild(headerWrapper);
529
+ wrapper.appendChild(bodyContainer);
530
+ this.element.appendChild(wrapper);
531
+ this.scrollContainer = bodyContainer;
532
+ requestAnimationFrame(() => {
533
+ const scrollbarWidth = bodyContainer.offsetWidth - bodyContainer.clientWidth;
534
+ if (scrollbarWidth > 0) {
535
+ headerWrapper.style.paddingRight = scrollbarWidth + 'px';
536
+ }
537
+ });
538
+ this.hoverTarget = bodyContainer;
539
+ this.bindScrollHoverEvents();
540
+ this.startAutoScroll();
541
+ }
542
+ bindScrollHoverEvents() {
543
+ if (!this.hoverTarget)
544
+ return;
545
+ this.hoverTarget.addEventListener('mouseenter', this.handleScrollMouseEnter);
546
+ this.hoverTarget.addEventListener('mouseleave', this.handleScrollMouseLeave);
547
+ }
548
+ unbindScrollHoverEvents() {
549
+ if (!this.hoverTarget)
550
+ return;
551
+ this.hoverTarget.removeEventListener('mouseenter', this.handleScrollMouseEnter);
552
+ this.hoverTarget.removeEventListener('mouseleave', this.handleScrollMouseLeave);
553
+ this.hoverTarget = null;
554
+ }
555
+ startAutoScroll() {
556
+ clearInterval(this.scrollIntervalId);
557
+ if (!this.model.generalSetting.autoScroll)
558
+ return;
559
+ const totalPages = Math.max(1, Math.ceil(this.totalCount / this.pageSize));
560
+ if (totalPages <= 1 && !this.scrollContainer)
561
+ return;
562
+ const duration = (this.model.generalSetting.scrollDuration || 10) * 1000;
563
+ this.scrollIntervalId = setInterval(() => this.scrollOneRow(), duration);
564
+ }
565
+ scrollOneRow() {
566
+ const el = this.scrollContainer;
567
+ if (!el) {
568
+ this.autoNextPage();
569
+ return;
570
+ }
571
+ const maxScrollTop = el.scrollHeight - el.clientHeight;
572
+ if (maxScrollTop <= 1 || el.scrollTop >= maxScrollTop - 1) {
573
+ this.autoNextPage();
574
+ return;
575
+ }
576
+ el.scrollTop = Math.min(el.scrollTop + this.rowHeight, maxScrollTop);
577
+ }
578
+ autoNextPage() {
579
+ const totalPages = Math.max(1, Math.ceil(this.totalCount / this.pageSize));
580
+ if (totalPages <= 1)
581
+ return;
582
+ const lastPage = totalPages - 1;
583
+ this.page = this.page < lastPage ? this.page + 1 : 0;
584
+ this.getAlarmData();
347
585
  }
348
- renderOperationArea(chartWidth, chartHeight) {
586
+ renderOperationArea(chartWidth, chartHeight, scale = 1) {
349
587
  var _a;
350
- const operationArea = this.rootElement.append('g').attr('transform', `translate(0,${chartHeight + this.displayOption.operationAreaMarginTop})`)
351
- .append('foreignObject').attr('width', chartWidth).attr('height', this.displayOption.operationAreaHeight).attr('fill', 'none')
352
- .append('xhtml:div').style('height', (this.displayOption.operationAreaHeight - 4) + 'px').style('overflow', 'hidden').style('margin-top', '4px');
588
+ const operationArea = this.rootElement.append('g').attr('transform', `translate(0,${chartHeight + this.displayOption.operationAreaMarginTop * scale})`)
589
+ .append('foreignObject').attr('width', chartWidth).attr('height', this.displayOption.operationAreaHeight * scale).attr('fill', 'none')
590
+ .append('xhtml:div').style('height', (this.displayOption.operationAreaHeight * scale - 4) + 'px').style('overflow', 'hidden').style('margin-top', '4px');
353
591
  if ((_a = this.model.filterSetting) === null || _a === void 0 ? void 0 : _a.alarmType) {
354
- const selectElement = operationArea.append('select').style('margin-left', this.displayOption.marginLeft + 'px')
355
- .style('font-size', this.displayOption.operationSelectFontSize).on('change', () => {
592
+ const selectElement = operationArea.append('select').style('margin-left', (this.displayOption.marginLeft * scale) + 'px')
593
+ .style('font-size', (parseFloat(this.displayOption.operationSelectFontSize) * scale) + 'px').on('change', () => {
356
594
  const displayTimePeriod = this.rootElement.select('select').property('value');
357
595
  this.updateTimeRange(displayTimePeriod);
358
596
  this.getAlarmData();
@@ -360,24 +598,28 @@ export class AlarmElement extends ConditionalDisplayElement {
360
598
  const options = selectElement.selectAll('option').data(this.timePeriods).enter().append('option');
361
599
  options.text(d => d.name).attr('value', d => d.key).property('selected', d => d.key === Number(this.currentTimePeriod));
362
600
  }
363
- const buttonWidth = this.displayOption.operationButtonWidth + 'px', buttonHeight = this.displayOption.operationButtonHeight + 'px';
601
+ const buttonWidth = (this.displayOption.operationButtonWidth * scale) + 'px', buttonHeight = (this.displayOption.operationButtonHeight * scale) + 'px';
364
602
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
365
603
  .style('float', 'right').style('background-image', 'url(assets/img/last_page.svg)')
604
+ .style('background-size', 'contain').style('background-repeat', 'no-repeat').style('background-position', 'center')
366
605
  .style('background-color', 'inherit')
367
606
  .on('click', () => { this.loadLastPage(); });
368
607
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
369
608
  .style('float', 'right').style('background-image', 'url(assets/img/next_page.svg)')
370
- .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
609
+ .style('background-size', 'contain').style('background-repeat', 'no-repeat').style('background-position', 'center')
610
+ .style('margin', `0 ${this.displayOption.operationButtonMargin * scale}px 0 0`)
371
611
  .style('background-color', 'inherit')
372
612
  .on('click', () => { this.loadNextPage(); });
373
613
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
374
614
  .style('float', 'right').style('background-image', 'url(assets/img/previous_page.svg)')
375
- .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
615
+ .style('background-size', 'contain').style('background-repeat', 'no-repeat').style('background-position', 'center')
616
+ .style('margin', `0 ${this.displayOption.operationButtonMargin * scale}px 0 0`)
376
617
  .style('background-color', 'inherit')
377
618
  .on('click', () => { this.loadPreviousPage(); });
378
619
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
379
620
  .style('float', 'right').style('background-image', 'url(assets/img/first_page.svg)')
380
- .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
621
+ .style('background-size', 'contain').style('background-repeat', 'no-repeat').style('background-position', 'center')
622
+ .style('margin', `0 ${this.displayOption.operationButtonMargin * scale}px 0 0`)
381
623
  .style('background-color', 'inherit')
382
624
  .on('click', () => { this.loadFirstPage(); });
383
625
  this.setStatusAsNormal();
@@ -1 +1 @@
1
- [{"__symbolic":"module","version":4,"metadata":{"AlarmElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-display-element","name":"ConditionalDisplayElement","line":21,"character":34},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":55,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":56,"character":18},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":57,"character":27},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":58,"character":30},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":59,"character":23},{"__symbolic":"reference","module":"../../config","name":"AlarmsStore","line":60,"character":38},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"../../service","name":"SystemTextLibraryService","line":62,"character":52},{"__symbolic":"reference","module":"../../service","name":"LanguageService","line":63,"character":43},{"__symbolic":"reference","module":"../../gui/gui-context","name":"GuiContext","line":64,"character":38}]}],"dispose":[{"__symbolic":"method"}],"subscribeLanguageChange":[{"__symbolic":"method"}],"updateLanguageTexts":[{"__symbolic":"method"}],"getCurrentCulture":[{"__symbolic":"method"}],"getHeaderTextByColumn":[{"__symbolic":"method"}],"getValidTimePeriods":[{"__symbolic":"method"}],"getTimePeriodText":[{"__symbolic":"method"}],"getAlarmData":[{"__symbolic":"method"}],"renderElement":[{"__symbolic":"method"}],"renderOperationArea":[{"__symbolic":"method"}],"updateTimeRange":[{"__symbolic":"method"}],"updateQueryTimeRange":[{"__symbolic":"method"}],"loadFirstPage":[{"__symbolic":"method"}],"loadNextPage":[{"__symbolic":"method"}],"loadPreviousPage":[{"__symbolic":"method"}],"loadLastPage":[{"__symbolic":"method"}],"setStatusAsNormal":[{"__symbolic":"method"}],"setStatusAsLoading":[{"__symbolic":"method"}],"renderStatus":[{"__symbolic":"method"}],"clearStatus":[{"__symbolic":"method"}]}}}}]
1
+ [{"__symbolic":"module","version":4,"metadata":{"AlarmElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-display-element","name":"ConditionalDisplayElement","line":21,"character":34},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":61,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":62,"character":18},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":63,"character":27},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":64,"character":30},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":65,"character":23},{"__symbolic":"reference","module":"../../config","name":"AlarmsStore","line":66,"character":38},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"../../service","name":"SystemTextLibraryService","line":68,"character":52},{"__symbolic":"reference","module":"../../service","name":"LanguageService","line":69,"character":43},{"__symbolic":"reference","module":"../../gui/gui-context","name":"GuiContext","line":70,"character":38}]}],"dispose":[{"__symbolic":"method"}],"subscribeLanguageChange":[{"__symbolic":"method"}],"updateLanguageTexts":[{"__symbolic":"method"}],"getCurrentCulture":[{"__symbolic":"method"}],"getHeaderTextByColumn":[{"__symbolic":"method"}],"getValidTimePeriods":[{"__symbolic":"method"}],"getTimePeriodText":[{"__symbolic":"method"}],"getAlarmData":[{"__symbolic":"method"}],"refreshCurrentPageData":[{"__symbolic":"method"}],"updateTableContent":[{"__symbolic":"method"}],"getIconScale":[{"__symbolic":"method"}],"renderElement":[{"__symbolic":"method"}],"buildHeaderRow":[{"__symbolic":"method"}],"buildDataRow":[{"__symbolic":"method"}],"renderPlainTable":[{"__symbolic":"method"}],"renderScrollableTable":[{"__symbolic":"method"}],"bindScrollHoverEvents":[{"__symbolic":"method"}],"unbindScrollHoverEvents":[{"__symbolic":"method"}],"startAutoScroll":[{"__symbolic":"method"}],"scrollOneRow":[{"__symbolic":"method"}],"autoNextPage":[{"__symbolic":"method"}],"renderOperationArea":[{"__symbolic":"method"}],"updateTimeRange":[{"__symbolic":"method"}],"updateQueryTimeRange":[{"__symbolic":"method"}],"loadFirstPage":[{"__symbolic":"method"}],"loadNextPage":[{"__symbolic":"method"}],"loadPreviousPage":[{"__symbolic":"method"}],"loadLastPage":[{"__symbolic":"method"}],"setStatusAsNormal":[{"__symbolic":"method"}],"setStatusAsLoading":[{"__symbolic":"method"}],"renderStatus":[{"__symbolic":"method"}],"clearStatus":[{"__symbolic":"method"}]}}}}]
@@ -14,6 +14,16 @@ export declare abstract class StateControlElement extends ConditionalDynamicDisp
14
14
  currentRect: JQuery;
15
15
  preState: State;
16
16
  constructor(element: HTMLElement, permissionChecker: PermissionChecker, variableCommunicator: VariableCommunicator, variableStore: VariableStore, localization: Localization, signalRAppId: string);
17
+ /**
18
+ * 子类可覆盖此方法提供元件当前大小和初始大小,用于状态图标缩放计算
19
+ * @returns {{ width: number, height: number, initWidth?: number, initHeight?: number }}
20
+ */
21
+ protected getElementSizeForIconScale(): {
22
+ width: number;
23
+ height: number;
24
+ initWidth?: number;
25
+ initHeight?: number;
26
+ } | null;
17
27
  initState(): void;
18
28
  updateElementStates(newElementStates: VariableState[]): void;
19
29
  addElementState(newElementState: VariableState): void;
@@ -9,6 +9,13 @@ export class StateControlElement extends ConditionalDynamicDisplayElement {
9
9
  this.isEnable = true;
10
10
  this.elementStates = [];
11
11
  }
12
+ /**
13
+ * 子类可覆盖此方法提供元件当前大小和初始大小,用于状态图标缩放计算
14
+ * @returns {{ width: number, height: number, initWidth?: number, initHeight?: number }}
15
+ */
16
+ getElementSizeForIconScale() {
17
+ return null;
18
+ }
12
19
  initState() {
13
20
  if (!this.elementStates.length) {
14
21
  return;
@@ -159,7 +166,18 @@ export class StateControlElement extends ConditionalDynamicDisplayElement {
159
166
  const imgObj = document.createElementNS('http://www.w3.org/2000/svg', 'image');
160
167
  if (imgObj) {
161
168
  const currentRectElement = this.currentRect[0];
162
- let x = Number(currentRectElement.getAttribute('width')) - 20;
169
+ // 计算图标缩放比例
170
+ const baseIconSize = 20;
171
+ let iconSize = baseIconSize;
172
+ const sizeInfo = this.getElementSizeForIconScale();
173
+ if (sizeInfo && sizeInfo.initWidth && sizeInfo.initHeight) {
174
+ const scaleX = sizeInfo.width / sizeInfo.initWidth;
175
+ const scaleY = sizeInfo.height / sizeInfo.initHeight;
176
+ // 取较小值,避免图标超出元件边界
177
+ const scale = Math.min(scaleX, scaleY);
178
+ iconSize = baseIconSize * scale;
179
+ }
180
+ let x = Number(currentRectElement.getAttribute('width')) - iconSize;
163
181
  const currentRectX = currentRectElement.getAttribute('x');
164
182
  if (currentRectX !== null) {
165
183
  x += Number(currentRectX);
@@ -170,8 +188,8 @@ export class StateControlElement extends ConditionalDynamicDisplayElement {
170
188
  imgObj.setAttributeNS(null, 'id', 'StateImage');
171
189
  imgObj.setAttributeNS(null, 'x', x.toString());
172
190
  imgObj.setAttributeNS(null, 'y', '0');
173
- imgObj.setAttributeNS(null, 'height', '20px');
174
- imgObj.setAttributeNS(null, 'width', '20px');
191
+ imgObj.setAttributeNS(null, 'height', iconSize + 'px');
192
+ imgObj.setAttributeNS(null, 'width', iconSize + 'px');
175
193
  const titleElement = document.createElementNS('http://www.w3.org/2000/svg', 'title');
176
194
  const text = document.createTextNode(title);
177
195
  titleElement.appendChild(text);
@@ -1 +1 @@
1
- [{"__symbolic":"module","version":4,"metadata":{"StateControlElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"./conditional-dynamic-display-element","name":"ConditionalDynamicDisplayElement","line":8,"character":50},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":16,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":16,"character":57},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":16,"character":98},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":17,"character":23},{"__symbolic":"reference","module":"../../localization","name":"Localization","line":17,"character":71},{"__symbolic":"reference","name":"string"}]}],"initState":[{"__symbolic":"method"}],"updateElementStates":[{"__symbolic":"method"}],"addElementState":[{"__symbolic":"method"}],"changeStates":[{"__symbolic":"method"}],"getState":[{"__symbolic":"method"}],"changeState":[{"__symbolic":"method"}],"loadStateData":[{"__symbolic":"method"}],"setState":[{"__symbolic":"method"}],"clearTip":[{"__symbolic":"method"}]}}}}]
1
+ [{"__symbolic":"module","version":4,"metadata":{"StateControlElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"./conditional-dynamic-display-element","name":"ConditionalDynamicDisplayElement","line":8,"character":50},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":16,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":16,"character":57},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":16,"character":98},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":17,"character":23},{"__symbolic":"reference","module":"../../localization","name":"Localization","line":17,"character":71},{"__symbolic":"reference","name":"string"}]}],"getElementSizeForIconScale":[{"__symbolic":"method"}],"initState":[{"__symbolic":"method"}],"updateElementStates":[{"__symbolic":"method"}],"addElementState":[{"__symbolic":"method"}],"changeStates":[{"__symbolic":"method"}],"getState":[{"__symbolic":"method"}],"changeState":[{"__symbolic":"method"}],"loadStateData":[{"__symbolic":"method"}],"setState":[{"__symbolic":"method"}],"clearTip":[{"__symbolic":"method"}]}}}}]
@@ -32,4 +32,13 @@ export declare class CharacterDisplayElement extends ReadableElement {
32
32
  private doWriteCharacterValue;
33
33
  private recordOperation;
34
34
  private changeGraph;
35
+ /**
36
+ * 提供元件尺寸信息用于状态图标缩放
37
+ */
38
+ protected getElementSizeForIconScale(): {
39
+ width: number;
40
+ height: number;
41
+ initWidth?: number;
42
+ initHeight?: number;
43
+ } | null;
35
44
  }