@flexem/fc-gui 3.0.0-alpha.13 → 3.0.0-alpha.130

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 (184) hide show
  1. package/CHANGELOG.md +439 -1
  2. package/assets/img/black_first_page.png +0 -0
  3. package/assets/img/black_last_page.png +0 -0
  4. package/assets/img/black_next_page.png +0 -0
  5. package/assets/img/black_previous_page.png +0 -0
  6. package/bundles/@flexem/fc-gui.umd.js +23330 -19502
  7. package/bundles/@flexem/fc-gui.umd.js.map +1 -1
  8. package/bundles/@flexem/fc-gui.umd.min.js +5 -5
  9. package/bundles/@flexem/fc-gui.umd.min.js.map +1 -1
  10. package/communication/variable/variable-communicator.d.ts +4 -0
  11. package/communication/variable/variable-value.d.ts +4 -1
  12. package/communication/variable/variable-value.js +4 -1
  13. package/communication/variable/variable-value.metadata.json +1 -1
  14. package/config/alarm/alarm.store.d.ts +6 -0
  15. package/config/alarm/alarm.store.js +0 -0
  16. package/config/alarm/alarm.store.metadata.json +1 -0
  17. package/config/alarm/get-alarms-args.d.ts +12 -0
  18. package/config/alarm/get-alarms-args.js +13 -0
  19. package/config/alarm/get-alarms-args.metadata.json +1 -0
  20. package/config/alarm/index.d.ts +2 -0
  21. package/config/alarm/index.js +1 -0
  22. package/config/alarm/index.metadata.json +1 -0
  23. package/config/config-store.d.ts +2 -0
  24. package/config/history-data/get-history-data-args.d.ts +14 -3
  25. package/config/history-data/get-history-data-args.js +5 -3
  26. package/config/history-data/get-history-data-args.metadata.json +1 -1
  27. package/config/history-data/history-data.model.d.ts +7 -1
  28. package/config/history-data/history-data.model.js +9 -1
  29. package/config/history-data/history-data.model.metadata.json +1 -1
  30. package/config/history-data/index.d.ts +1 -1
  31. package/config/history-data/index.js +1 -1
  32. package/config/history-data/index.metadata.json +1 -1
  33. package/config/index.d.ts +1 -0
  34. package/config/index.js +1 -0
  35. package/config/index.metadata.json +1 -1
  36. package/elements/air-quality/air-quality-element.d.ts +31 -0
  37. package/elements/air-quality/air-quality-element.js +194 -0
  38. package/elements/air-quality/air-quality-element.metadata.json +1 -0
  39. package/elements/alarm/alarm-element.d.ts +69 -0
  40. package/elements/alarm/alarm-element.js +497 -0
  41. package/elements/alarm/alarm-element.metadata.json +1 -0
  42. package/elements/bar-graph-element.d.ts +10 -2
  43. package/elements/bar-graph-element.js +135 -5
  44. package/elements/bar-graph-element.metadata.json +1 -1
  45. package/elements/base/readable-element.d.ts +6 -1
  46. package/elements/base/readable-element.js +64 -2
  47. package/elements/base/readable-element.metadata.json +1 -1
  48. package/elements/base/state-control-element.d.ts +3 -1
  49. package/elements/base/state-control-element.js +3 -0
  50. package/elements/datetime-display/datetime-display-element.d.ts +1 -0
  51. package/elements/datetime-display/datetime-display-element.js +10 -2
  52. package/elements/datetime-display/datetime-display-element.metadata.json +1 -1
  53. package/elements/datetime-display/time-zone-select-json.d.ts +8 -0
  54. package/elements/datetime-display/time-zone-select-json.js +558 -0
  55. package/elements/historical-curve/historical-curve.element.d.ts +39 -3
  56. package/elements/historical-curve/historical-curve.element.js +499 -39
  57. package/elements/historical-curve/historical-curve.element.metadata.json +1 -1
  58. package/elements/main-element.d.ts +1 -0
  59. package/elements/main-element.js +59 -9
  60. package/elements/main-element.metadata.json +1 -1
  61. package/elements/meter-element.d.ts +7 -1
  62. package/elements/meter-element.js +76 -7
  63. package/elements/meter-element.metadata.json +1 -1
  64. package/elements/numerical-display/numerical-display-element.d.ts +16 -3
  65. package/elements/numerical-display/numerical-display-element.js +83 -11
  66. package/elements/numerical-display/numerical-display-element.metadata.json +1 -1
  67. package/elements/per-view-variable-communicator.d.ts +3 -0
  68. package/elements/per-view-variable-communicator.js +11 -0
  69. package/elements/per-view-variable-communicator.metadata.json +1 -1
  70. package/elements/ring-graph/ring-graph-element.d.ts +13 -1
  71. package/elements/ring-graph/ring-graph-element.js +164 -3
  72. package/elements/ring-graph/ring-graph-element.metadata.json +1 -1
  73. package/elements/scroll-alarm/scroll-alarm-element.d.ts +54 -0
  74. package/elements/scroll-alarm/scroll-alarm-element.js +517 -0
  75. package/elements/scroll-alarm/scroll-alarm-element.metadata.json +1 -0
  76. package/elements/shared/graph/graph-state-element.js +0 -3
  77. package/elements/shared/text/text-element.d.ts +9 -0
  78. package/elements/shared/text/text-element.js +33 -2
  79. package/elements/shared/text/text-element.metadata.json +1 -1
  80. package/elements/shared/text/text-state-element.d.ts +25 -2
  81. package/elements/shared/text/text-state-element.js +138 -63
  82. package/elements/shared/text/text-state-element.metadata.json +1 -1
  83. package/elements/static-elements/hyperlink-element.d.ts +24 -2
  84. package/elements/static-elements/hyperlink-element.js +101 -3
  85. package/elements/static-elements/hyperlink-element.metadata.json +1 -1
  86. package/elements/static-elements/text-element.d.ts +23 -2
  87. package/elements/static-elements/text-element.js +96 -3
  88. package/elements/static-elements/text-element.metadata.json +1 -1
  89. package/elements/switch-indicator-light/bit-switch-operator.d.ts +1 -0
  90. package/elements/switch-indicator-light/bit-switch-operator.js +19 -0
  91. package/elements/switch-indicator-light/bit-switch-operator.metadata.json +1 -1
  92. package/elements/switch-indicator-light/switch-indicator-light-element.d.ts +18 -2
  93. package/elements/switch-indicator-light/switch-indicator-light-element.js +120 -25
  94. package/elements/switch-indicator-light/switch-indicator-light-element.metadata.json +1 -1
  95. package/elements/switch-indicator-light/switch-operator.d.ts +1 -0
  96. package/elements/switch-indicator-light/word-switch-operator.d.ts +1 -0
  97. package/elements/switch-indicator-light/word-switch-operator.js +6 -0
  98. package/elements/switch-indicator-light/word-switch-operator.metadata.json +1 -1
  99. package/elements/video/video-element.d.ts +4 -0
  100. package/elements/video/video-element.js +81 -21
  101. package/elements/video/video-element.metadata.json +1 -1
  102. package/elements/view-operation/view-operation.element.d.ts +23 -2
  103. package/elements/view-operation/view-operation.element.js +103 -1
  104. package/elements/view-operation/view-operation.element.metadata.json +1 -1
  105. package/elements/weather/weater-element.js +0 -1
  106. package/gui/gui-context.d.ts +12 -2
  107. package/gui/gui-host.d.ts +1 -1
  108. package/gui/gui-view.d.ts +2 -0
  109. package/gui/gui-view.js +38 -2
  110. package/gui/gui-view.metadata.json +1 -1
  111. package/gui/gui.component.d.ts +3 -0
  112. package/gui/gui.component.js +15 -2
  113. package/gui/gui.component.metadata.json +1 -1
  114. package/localization/localization.service.d.ts +7 -0
  115. package/localization/localization.service.js +10 -3
  116. package/localization/localization.service.metadata.json +1 -1
  117. package/localization/localization.service.zh_CN.js +8 -1
  118. package/localization/localization.service.zh_CN.metadata.json +1 -1
  119. package/modal/write-value/write-value-modal-args.d.ts +5 -1
  120. package/modal/write-value/write-value-modal-args.js +3 -1
  121. package/modal/write-value/write-value-modal-args.metadata.json +1 -1
  122. package/modal/write-value/write-value-modal.component.d.ts +12 -7
  123. package/modal/write-value/write-value-modal.component.html +9 -4
  124. package/modal/write-value/write-value-modal.component.js +73 -15
  125. package/modal/write-value/write-value-modal.component.metadata.json +1 -1
  126. package/model/air-quality/air-quality-info.d.ts +23 -0
  127. package/model/air-quality/air-quality-info.js +4 -0
  128. package/model/air-quality/air-quality-info.metadata.json +1 -0
  129. package/model/air-quality/air-quality.model.d.ts +7 -0
  130. package/model/air-quality/air-quality.model.js +0 -0
  131. package/model/air-quality/air-quality.model.metadata.json +1 -0
  132. package/model/alarm/alarm.model.d.ts +13 -0
  133. package/model/alarm/alarm.model.js +0 -0
  134. package/model/alarm/alarm.model.metadata.json +1 -0
  135. package/model/bar-graph/bar-graph.d.ts +4 -0
  136. package/model/base/font-setting-model.d.ts +6 -0
  137. package/model/base/font-setting-model.metadata.json +1 -1
  138. package/model/base/readable-model.d.ts +4 -0
  139. package/model/datetime-display/datetime-display.d.ts +1 -0
  140. package/model/historical-curve/historical-curve-axis-settings.d.ts +11 -0
  141. package/model/historical-curve/historical-curve-axis-settings.js +5 -0
  142. package/model/historical-curve/historical-curve-axis-settings.metadata.json +1 -1
  143. package/model/historical-curve/historical-curve-chanel.model.d.ts +8 -0
  144. package/model/historical-curve/historical-curve.data-settings.d.ts +18 -1
  145. package/model/historical-curve/historical-curve.data-settings.metadata.json +1 -1
  146. package/model/meter/meter.d.ts +4 -0
  147. package/model/ring-graph/ring-graph.model.d.ts +8 -0
  148. package/model/scroll-alarm/scroll-alarm.model.d.ts +21 -0
  149. package/model/scroll-alarm/scroll-alarm.model.js +0 -0
  150. package/model/scroll-alarm/scroll-alarm.model.metadata.json +1 -0
  151. package/model/switch-indicator-light/bit-switch-operation.d.ts +2 -1
  152. package/model/switch-indicator-light/bit-switch-operation.js +1 -0
  153. package/model/switch-indicator-light/bit-switch-operation.metadata.json +1 -1
  154. package/model/switch-indicator-light/switch-indicator-light.d.ts +2 -0
  155. package/model/view-operation/view-operation-element.model.d.ts +7 -1
  156. package/package.json +1 -1
  157. package/public_api.js +1 -0
  158. package/remote/communication/variable/remote-variable-communicator.d.ts +22 -0
  159. package/remote/communication/variable/remote-variable-communicator.js +122 -2
  160. package/remote/communication/variable/remote-variable-communicator.metadata.json +1 -1
  161. package/remote/communication/variable/remote-variable-protocol.d.ts +5 -0
  162. package/service/index.d.ts +4 -0
  163. package/service/index.js +1 -0
  164. package/service/index.metadata.json +1 -1
  165. package/service/language.service.d.ts +37 -0
  166. package/service/language.service.js +0 -0
  167. package/service/language.service.metadata.json +1 -0
  168. package/service/released-variable/index.d.ts +1 -0
  169. package/service/released-variable/index.js +0 -0
  170. package/service/released-variable/index.metadata.json +1 -0
  171. package/service/released-variable/released-variable.service.d.ts +4 -0
  172. package/service/released-variable/released-variable.service.js +0 -0
  173. package/service/released-variable/released-variable.service.metadata.json +1 -0
  174. package/service/system-text-library.service.d.ts +76 -0
  175. package/service/system-text-library.service.js +28 -0
  176. package/service/system-text-library.service.metadata.json +1 -0
  177. package/service/text-library.service.d.ts +49 -0
  178. package/service/text-library.service.js +0 -0
  179. package/service/text-library.service.metadata.json +1 -0
  180. package/service/weather.service.d.ts +1 -0
  181. package/shared/gui-consts.d.ts +3 -0
  182. package/shared/gui-consts.js +3 -0
  183. package/shared/gui-consts.metadata.json +1 -1
  184. package/utils/data-type/fbox-data-type.service.js +40 -0
@@ -5,15 +5,20 @@ import * as nv from 'nvd3';
5
5
  import { GetHistoryDataArgs } from '../../config';
6
6
  import { HistoricalCurveTimeRange } from '../../config/history-data/historical-curve.time-range';
7
7
  import { LOCALIZATION } from '../../localization';
8
+ import { SYSTEM_TEXT_LIBRARY_TYPES, TIME_PERIOD_KEYS } from '../../service';
8
9
  import { ConditionalDisplayElement } from '../base/conditional-display-element';
9
10
  import { HistoricalCurveElementStatus } from './historical-curve-element-status';
10
11
  import { LOGGER_SERVICE_TOKEN } from '../../logger';
11
12
  import { GlobalSettings, DisplayMode } from '../../settings';
12
13
  import { CurveType } from '../../model/historical-curve/curve-type';
14
+ import { AxisRangeType } from '../../model/historical-curve/historical-curve-axis-settings';
13
15
  export class HistoricalCurveElement extends ConditionalDisplayElement {
14
- constructor(element, injector, permissionChecker, variableCommunicator, variableStore, historyDataStore, signalRAppId) {
16
+ constructor(element, injector, permissionChecker, variableCommunicator, variableStore, historyDataStore, signalRAppId, systemTextLibraryService, languageService, guiContext) {
15
17
  super(element, permissionChecker, variableCommunicator, variableStore, signalRAppId);
16
18
  this.historyDataStore = historyDataStore;
19
+ this.systemTextLibraryService = systemTextLibraryService;
20
+ this.languageService = languageService;
21
+ this.guiContext = guiContext;
17
22
  this.displayOption = {
18
23
  dataLimit: 500,
19
24
  dataZoomHeight: 32,
@@ -21,16 +26,21 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
21
26
  marginRight: 20,
22
27
  mobileMinWidth: 450,
23
28
  operationAreaHeight: 32,
24
- operationAreaMarginTop: 10,
29
+ operationAreaMarginTop: 25,
25
30
  operationSelectFontSize: '16px',
26
31
  operationButtonWidth: 24,
27
32
  operationButtonHeight: 24,
28
33
  operationButtonMargin: 4
29
34
  };
30
35
  this.elementStatus = HistoricalCurveElementStatus.Loading;
36
+ this.data = [];
37
+ this.needResize = true;
38
+ this.setNeedResize = () => {
39
+ this.needResize = false;
40
+ setTimeout(() => this.needResize = true, 500);
41
+ };
31
42
  this.logger = injector.get(LOGGER_SERVICE_TOKEN);
32
43
  this.localization = injector.get(LOCALIZATION);
33
- this.timePeriods = this.getValidTimePeriods();
34
44
  this.updateTimeRange(this.model.displaySetting.displayTimePeriod);
35
45
  this.refreshIntervalId = setInterval(() => {
36
46
  this.loadFirstPage();
@@ -38,7 +48,7 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
38
48
  }, this.model.displaySetting.refreshInterval * 1000);
39
49
  this.isMobileMode = DisplayMode.Mobile === injector.get(GlobalSettings).displayMode;
40
50
  if (this.isMobileMode) {
41
- this.displayOption.operationAreaMarginTop = 20;
51
+ this.displayOption.operationAreaMarginTop = 35;
42
52
  if (this.model.displaySetting.size.width >= this.displayOption.mobileMinWidth) {
43
53
  this.displayOption.operationAreaHeight = 68;
44
54
  this.displayOption.operationSelectFontSize = '24px';
@@ -48,23 +58,115 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
48
58
  }
49
59
  }
50
60
  this.loadFirstPage();
61
+ this.initKeyboardListener();
62
+ // 订阅语种变化事件
63
+ this.subscribeLanguageChange();
51
64
  }
52
65
  dispose() {
53
66
  clearInterval(this.refreshIntervalId);
54
67
  if (this.chartElement) {
55
68
  this.chartElement.tooltip.hidden(true);
56
69
  }
70
+ if (this.resizeEventListener) {
71
+ this.resizeEventListener.clear();
72
+ }
73
+ if (this.isAndroid) {
74
+ window.removeEventListener('native.keyboardshow', this.setNeedResize);
75
+ window.removeEventListener('native.keyboardhide', this.setNeedResize);
76
+ }
77
+ // 取消语种变化订阅
78
+ if (this.languageChangeSubscription) {
79
+ this.languageChangeSubscription.unsubscribe();
80
+ this.languageChangeSubscription = undefined;
81
+ }
57
82
  this.logger.debug(`[GUI]Dispose Histoical Curve Refresh Interval:${d3.time.format('%x %X')(new Date())}`);
58
83
  }
84
+ initKeyboardListener() {
85
+ this.isAndroid = !!navigator.userAgent.match(/(Android)/i);
86
+ if (this.isAndroid) {
87
+ window.addEventListener('native.keyboardshow', this.setNeedResize);
88
+ window.addEventListener('native.keyboardhide', this.setNeedResize);
89
+ }
90
+ }
91
+ /**
92
+ * 订阅语种变化事件
93
+ */
94
+ subscribeLanguageChange() {
95
+ if (this.guiContext && this.guiContext.languageChanged$) {
96
+ this.languageChangeSubscription = this.guiContext.languageChanged$.subscribe(() => {
97
+ // 只更新时间段选择器的文案,不重新查询数据
98
+ this.updateLanguageTexts();
99
+ });
100
+ }
101
+ }
102
+ /**
103
+ * 更新语种相关的文案(时间段选择器)
104
+ */
105
+ updateLanguageTexts() {
106
+ const selectElement = this.rootElement.select('select');
107
+ if (!selectElement.empty()) {
108
+ // 重新生成时间段数据
109
+ const updatedTimePeriods = this.getValidTimePeriods();
110
+ this.timePeriods = updatedTimePeriods;
111
+ // 更新选项文本
112
+ const options = selectElement.selectAll('option');
113
+ options.each(function (_d, i) {
114
+ if (i < updatedTimePeriods.length) {
115
+ d3.select(this).text(updatedTimePeriods[i].name);
116
+ }
117
+ });
118
+ }
119
+ }
120
+ /**
121
+ * 获取当前语种的 culture 代码
122
+ */
123
+ getCurrentCulture() {
124
+ var _a, _b, _c, _e, _f, _g;
125
+ // 获取当前语种ID
126
+ const currentLanguageId = (_c = (_b = (_a = this.guiContext) === null || _a === void 0 ? void 0 : _a.getCurrentLanguageId) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : null;
127
+ // 确定要使用的语种代码(culture)
128
+ const defaultLanguage = ((_e = this.languageService) === null || _e === void 0 ? void 0 : _e.getDefaultLanguage()) || 'zh-CN';
129
+ if (currentLanguageId === null || currentLanguageId === undefined) {
130
+ // 设备未设置当前语种,使用默认语种
131
+ return defaultLanguage;
132
+ }
133
+ else {
134
+ // 设备已设置当前语种,获取对应的语种代码
135
+ const currentLanguage = (_g = (_f = this.guiContext) === null || _f === void 0 ? void 0 : _f.getLanguageCultureById) === null || _g === void 0 ? void 0 : _g.call(_f, currentLanguageId);
136
+ if (currentLanguage) {
137
+ return currentLanguage;
138
+ }
139
+ else {
140
+ // 无法获取语种代码,使用默认语种
141
+ return defaultLanguage;
142
+ }
143
+ }
144
+ }
59
145
  getValidTimePeriods() {
60
146
  const timePeriods = new Array();
61
- timePeriods.push({ key: 1, name: this.localization.lastOneHour });
62
- timePeriods.push({ key: 2, name: this.localization.lastTwentyFourHours });
63
- timePeriods.push({ key: 3, name: this.localization.lastSevenDays });
64
- timePeriods.push({ key: 4, name: this.localization.lastThirtyDays });
65
- timePeriods.push({ key: 5, name: this.localization.lastOneYear });
147
+ timePeriods.push({ key: 6, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_THIRTY_MINUTES) });
148
+ timePeriods.push({ key: 1, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_ONE_HOUR) });
149
+ timePeriods.push({ key: 7, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_EIGHT_HOURS) });
150
+ timePeriods.push({ key: 2, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_TWENTY_FOUR_HOURS) });
151
+ timePeriods.push({ key: 3, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_SEVEN_DAYS) });
152
+ timePeriods.push({ key: 4, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_THIRTY_DAYS) });
153
+ timePeriods.push({ key: 5, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_ONE_YEAR) });
66
154
  return timePeriods;
67
155
  }
156
+ /**
157
+ * 获取时间段文案(从系统文本库获取多语种翻译)
158
+ */
159
+ getTimePeriodText(textKey) {
160
+ const currentCulture = this.getCurrentCulture();
161
+ const systemType = SYSTEM_TEXT_LIBRARY_TYPES.COMPONENT_BUILTIN;
162
+ if (this.systemTextLibraryService) {
163
+ const translation = this.systemTextLibraryService.getSystemTextValue(systemType, textKey, currentCulture);
164
+ if (translation) {
165
+ return translation;
166
+ }
167
+ }
168
+ return '';
169
+ }
68
170
  updateTimeRange(timePeriodType) {
69
171
  this.currentTimePeriod = +timePeriodType;
70
172
  this.updateQueryTimeRange();
@@ -84,6 +186,12 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
84
186
  case 5:
85
187
  this.startTime = moment().subtract(1, 'years');
86
188
  break;
189
+ case 6:
190
+ this.startTime = moment().subtract(30, 'minutes');
191
+ break;
192
+ case 7:
193
+ this.startTime = moment().subtract(8, 'hours');
194
+ break;
87
195
  default:
88
196
  this.startTime = moment().subtract(1, 'days');
89
197
  }
@@ -97,53 +205,256 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
97
205
  if (!this.model.dataSetting) {
98
206
  return;
99
207
  }
100
- const dataItemName = this.model.dataSetting.dataName;
101
208
  const dataSourceCode = this.model.dataSetting.dataSourceCode;
102
- const channelNames = this.model.dataSetting.channels.map(c => c.name);
103
209
  this.updateElementStatus(HistoricalCurveElementStatus.Loading);
104
- const input = new GetHistoryDataArgs(dataSourceCode, dataItemName, channelNames, startTime, endTime, limit, rangeType);
105
- this.historyDataStore.getHistoryData(input).subscribe(result => {
106
- if (result.error) {
107
- this.updateElementStatus(HistoricalCurveElementStatus.LoadFailed, result.error);
210
+ // 【新格式】如果有多条目配置,使用多条目查询
211
+ if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
212
+ const historyDataItems = this.model.dataSetting.dataItems.map(item => ({
213
+ dataItemName: item.dataName,
214
+ channelNames: item.channels.map(c => c.name)
215
+ }));
216
+ // 使用第一个条目的信息作为兼容参数
217
+ const firstItem = this.model.dataSetting.dataItems[0];
218
+ const input = new GetHistoryDataArgs(dataSourceCode, firstItem.dataName, firstItem.channels.map(c => c.name), startTime, endTime, limit, rangeType, historyDataItems // 传递多条目参数
219
+ );
220
+ this.historyDataStore.getHistoryData(input).subscribe(result => {
221
+ this.handleQueryResult(result);
222
+ });
223
+ }
224
+ // 【旧格式】单条目模式
225
+ else {
226
+ const dataItemName = this.model.dataSetting.dataName;
227
+ const channelNames = this.model.dataSetting.channels.map(c => c.name);
228
+ const input = new GetHistoryDataArgs(dataSourceCode, dataItemName, channelNames, startTime, endTime, limit, rangeType);
229
+ this.historyDataStore.getHistoryData(input).subscribe(result => {
230
+ this.handleQueryResult(result);
231
+ });
232
+ }
233
+ }
234
+ handleQueryResult(result) {
235
+ this.timePeriods = this.getValidTimePeriods();
236
+ // 【新格式】多条目模式下的错误处理
237
+ if (result.historyDataItems && result.historyDataItems.length > 0) {
238
+ // 只要有条目正常返回(即使没有数据),就走正常逻辑
239
+ // 这样可以确保即使部分条目失败,只要有条目成功返回,就显示正常状态
240
+ this.clearStatus();
241
+ if (result.isUnbind) {
242
+ this.updateElementStatus(HistoricalCurveElementStatus.Unbound);
108
243
  }
109
244
  else {
110
- this.clearStatus();
111
- if (result.isUnbind) {
112
- this.updateElementStatus(HistoricalCurveElementStatus.Unbound);
113
- }
114
- else {
115
- this.updateElementStatus(HistoricalCurveElementStatus.Normal);
116
- }
117
- if (result.values.length) {
118
- this.currentStartTime = moment(first(result.values).time);
119
- this.currentEndTime = moment(last(result.values).time);
245
+ this.updateElementStatus(HistoricalCurveElementStatus.Normal);
246
+ }
247
+ // 如果有错误信息,在控制台输出警告
248
+ if (result.error) {
249
+ this.logger.warn(`[历史曲线] 部分条目查询失败: ${result.error}`);
250
+ }
251
+ // 检查是否有任何条目有数据
252
+ const hasAnyData = result.historyDataItems.some(item => item.rows && item.rows.length > 0);
253
+ if (hasAnyData) {
254
+ // 有数据,计算所有条目的最小和最大时间,确保时间轴覆盖所有数据
255
+ let globalMinTime = null;
256
+ let globalMaxTime = null;
257
+ result.historyDataItems.forEach(item => {
258
+ if (item.rows && item.rows.length > 0) {
259
+ const itemMinTime = moment(first(item.rows).time);
260
+ const itemMaxTime = moment(last(item.rows).time);
261
+ if (!globalMinTime || itemMinTime.isBefore(globalMinTime)) {
262
+ globalMinTime = itemMinTime;
263
+ }
264
+ if (!globalMaxTime || itemMaxTime.isAfter(globalMaxTime)) {
265
+ globalMaxTime = itemMaxTime;
266
+ }
267
+ }
268
+ });
269
+ // 设置全局时间范围
270
+ if (globalMinTime && globalMaxTime) {
271
+ this.currentStartTime = globalMinTime;
272
+ this.currentEndTime = globalMaxTime;
120
273
  }
121
- this.chartElement = this.renderChart(result.values);
122
274
  }
275
+ // 无论是否有数据,都渲染曲线(没有数据会显示空曲线)
276
+ this.chartElement = this.renderChartWithMultiItems(result.historyDataItems);
277
+ }
278
+ // 【旧格式】单条目模式的错误处理
279
+ else if (result.error) {
280
+ this.updateElementStatus(HistoricalCurveElementStatus.LoadFailed, result.error);
281
+ }
282
+ else {
283
+ this.clearStatus();
284
+ if (result.isUnbind) {
285
+ this.updateElementStatus(HistoricalCurveElementStatus.Unbound);
286
+ }
287
+ else {
288
+ this.updateElementStatus(HistoricalCurveElementStatus.Normal);
289
+ }
290
+ // 【旧格式】单条目数据,使用原有渲染方式
291
+ if (result.values.length) {
292
+ this.currentStartTime = moment(first(result.values).time);
293
+ this.currentEndTime = moment(last(result.values).time);
294
+ }
295
+ this.chartElement = this.renderChart(result.values);
296
+ }
297
+ }
298
+ setupTooltipAutoHide(chart) {
299
+ const chartContainer = this.rootElement.select('.nv-focus').node();
300
+ if (!chartContainer || !chart)
301
+ return;
302
+ let timeoutId;
303
+ // 鼠标移入图表时显示 tooltip
304
+ chartContainer.addEventListener('mouseover', () => {
305
+ hideTooltipAfterDelay();
123
306
  });
307
+ const clearTooltipTimeout = () => {
308
+ if (timeoutId) {
309
+ clearTimeout(timeoutId);
310
+ timeoutId = null;
311
+ }
312
+ };
313
+ const hideTooltipAfterDelay = () => {
314
+ clearTooltipTimeout();
315
+ timeoutId = setTimeout(() => {
316
+ chart.tooltip.hidden(true);
317
+ }, 2000); // 2秒延迟
318
+ };
124
319
  }
125
320
  renderChart(result) {
126
321
  const chartWidth = this.model.displaySetting.size.width;
127
322
  const chartHeight = this.model.displaySetting.size.height - this.displayOption.operationAreaHeight - this.displayOption.operationAreaMarginTop;
128
323
  const data = new Array();
324
+ // 【旧格式】单条目模式:只显示通道名
129
325
  each(this.model.dataSetting.channels, (channel, key) => {
130
326
  const values = new Array();
131
327
  each(result, v => values.push({ x: moment(v.time).local().toDate().valueOf(), y: v.values[key] }));
132
- data.push({ key: channel.name, area: channel.projectEnabled, values: values });
328
+ const displayName = channel.name;
329
+ data.push({ key: displayName, area: channel.projectEnabled, values: values });
330
+ });
331
+ this.data = data;
332
+ nv.addGraph(() => {
333
+ let chart;
334
+ if (this.model.displaySetting.curveType === CurveType.BarGroup || this.model.displaySetting.curveType === CurveType.BarStack) {
335
+ chart = this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
336
+ }
337
+ else {
338
+ chart = this.getLineChart(chartWidth, chartHeight, data);
339
+ }
340
+ // 设置 tooltip 自动隐藏逻辑
341
+ this.setupTooltipAutoHide(chart);
342
+ return chart;
133
343
  });
344
+ }
345
+ /**
346
+ * 【新格式】多条目独立数据的曲线渲染
347
+ * 每个条目独立保持自己的时间戳,不会出现时间戳混乱的问题
348
+ */
349
+ renderChartWithMultiItems(historyDataItems) {
350
+ const chartWidth = this.model.displaySetting.size.width;
351
+ const chartHeight = this.model.displaySetting.size.height - this.displayOption.operationAreaHeight - this.displayOption.operationAreaMarginTop;
352
+ const data = new Array();
353
+ // 创建一个 Map 用于快速查找条目数据
354
+ const itemDataMap = new Map();
355
+ each(historyDataItems, item => {
356
+ if (item && item.itemName) {
357
+ itemDataMap.set(item.itemName, item.rows || []);
358
+ }
359
+ });
360
+ // 判断是否只有一个条目
361
+ const isSingleItem = this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length === 1;
362
+ // 为每个条目的每个通道创建独立的曲线
363
+ each(this.model.dataSetting.dataItems, (dataItem) => {
364
+ const itemName = dataItem.dataName;
365
+ // 通过 itemName 匹配数据,而不是通过索引
366
+ const itemRows = itemDataMap.get(itemName) || [];
367
+ each(dataItem.channels, (channel, channelIdx) => {
368
+ const values = new Array();
369
+ // 每个条目使用自己的时间戳和数据
370
+ each(itemRows, row => {
371
+ const value = row.values && row.values[channelIdx] !== undefined ? row.values[channelIdx] : null;
372
+ values.push({ x: moment(row.time).local().toDate().valueOf(), y: value });
373
+ });
374
+ // 只有多个条目时才使用"条目名-通道名"格式,单条目时只显示通道名
375
+ const displayName = isSingleItem ? channel.name : `${itemName}-${channel.name}`;
376
+ data.push({ key: displayName, area: channel.projectEnabled, values: values });
377
+ });
378
+ });
379
+ this.data = data;
134
380
  nv.addGraph(() => {
381
+ let chart;
135
382
  if (this.model.displaySetting.curveType === CurveType.BarGroup || this.model.displaySetting.curveType === CurveType.BarStack) {
136
- return this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
383
+ chart = this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
137
384
  }
138
385
  else {
139
- return this.getLineChart(chartWidth, chartHeight, data);
386
+ chart = this.getLineChart(chartWidth, chartHeight, data);
140
387
  }
388
+ // 设置 tooltip 自动隐藏逻辑
389
+ this.setupTooltipAutoHide(chart);
390
+ return chart;
141
391
  });
142
392
  }
393
+ initPoint() {
394
+ try {
395
+ const legendList = this.$element
396
+ .find('.nv-legend')
397
+ .find('.nv-series');
398
+ let hiddenCount = 0;
399
+ // 获取所有通道(支持多条目和单条目模式)
400
+ const allChannels = [];
401
+ if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
402
+ // 【新格式】多条目模式:收集所有条目的所有通道
403
+ each(this.model.dataSetting.dataItems, dataItem => {
404
+ each(dataItem.channels, channel => {
405
+ allChannels.push(channel);
406
+ });
407
+ });
408
+ }
409
+ else {
410
+ // 【旧格式】单条目模式
411
+ allChannels.push(...this.model.dataSetting.channels);
412
+ }
413
+ for (let i = 0; i < this.data.length; i++) {
414
+ const channel = allChannels[i];
415
+ if (legendList.eq(i).children().eq(0).css('fill-opacity') === '1') {
416
+ const pointList = this.$element
417
+ .find('.nv-scatterWrap')
418
+ .find('.nv-series-' + (i - hiddenCount))
419
+ .find('.nv-point');
420
+ if (pointList && pointList.length) {
421
+ for (let j = 0; j < pointList.length; j++) {
422
+ const point = pointList.eq(j);
423
+ const previousPoint = pointList.eq(j - 1);
424
+ if (j && point.attr('transform').split(',')[1] !== previousPoint.attr('transform').split(',')[1]) {
425
+ if (channel.enablePoint && channel.pointColor) {
426
+ const pointStyle = {
427
+ 'stroke-opacity': 1,
428
+ 'stroke-width': '2px',
429
+ 'stroke': channel.pointColor,
430
+ 'fill-opacity': 1,
431
+ 'fill': channel.pointColor
432
+ };
433
+ point.addClass('nv-mark-point');
434
+ point.css(pointStyle);
435
+ previousPoint.addClass('nv-mark-point');
436
+ previousPoint.css(pointStyle);
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ else {
443
+ hiddenCount++;
444
+ }
445
+ }
446
+ }
447
+ catch (e) {
448
+ console.log(e);
449
+ }
450
+ }
143
451
  getLineChart(chartWidth, chartHeight, data) {
144
452
  const chart = nv.models.lineChart().showLegend(true)
145
453
  .margin({ top: 0, bottom: 0, left: this.displayOption.marginLeft, right: this.displayOption.marginRight })
146
454
  .noData(this.localization.chartNoData);
455
+ if (this.model.displaySetting.axisSetting.yAxisRangeType === AxisRangeType.Custom) {
456
+ chart.yDomain([this.model.displaySetting.axisSetting.yAxisMin, this.model.displaySetting.axisSetting.yAxisMax]);
457
+ }
147
458
  if (!this.isMobileMode) {
148
459
  chart.focusEnable(true);
149
460
  chart.focus.margin({ top: 10, right: 0, bottom: 0, left: 0 });
@@ -152,6 +463,19 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
152
463
  }
153
464
  this.renderCommonProperty(chart, chartWidth, chartHeight, data);
154
465
  this.renderOperationArea(chartWidth, chartHeight);
466
+ this.initPoint();
467
+ chart.legend.dispatch.on('legendClick', () => {
468
+ setTimeout(() => {
469
+ this.$element.find('.nv-mark-point').css({
470
+ 'stroke-opacity': 0,
471
+ 'stroke-width': 0,
472
+ 'stroke': 'unset',
473
+ 'fill-opacity': 0,
474
+ 'fill': 'unset'
475
+ }).removeClass('nv-mark-point');
476
+ this.initPoint();
477
+ }, 1);
478
+ });
155
479
  return chart;
156
480
  }
157
481
  getMultiBarWithFocusChart(chartWidth, chartHeight, data) {
@@ -168,6 +492,9 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
168
492
  chart.multibar.stacked(true);
169
493
  chart.multibar2.stacked(true);
170
494
  }
495
+ if (this.model.displaySetting.axisSetting.yAxisRangeType === AxisRangeType.Custom) {
496
+ chart.yDomain([this.model.displaySetting.axisSetting.yAxisMin, this.model.displaySetting.axisSetting.yAxisMax]);
497
+ }
171
498
  if (!this.isMobileMode) {
172
499
  chart.focusEnable(true);
173
500
  chart.focusShowAxisX(false);
@@ -188,9 +515,27 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
188
515
  return chart;
189
516
  }
190
517
  renderCommonProperty(chart, chartWidth, chartHeight, data) {
518
+ var _a, _b;
191
519
  chart.tooltip.headerFormatter(d => this.timeFormat(d, '%x %X'));
192
520
  if (this.model.displaySetting.showAxis) {
193
- chart.xAxis.showMaxMin(true).tickFormat(d => this.timeFormat(d, '%X'));
521
+ chart.xAxis.showMaxMin(true).tickFormat(d => {
522
+ this.$element.find('.nv-mark-point').css({
523
+ 'stroke-opacity': 0,
524
+ 'stroke-width': 0,
525
+ 'stroke': 'unset',
526
+ 'fill-opacity': 0,
527
+ 'fill': 'unset'
528
+ }).removeClass('nv-mark-point');
529
+ clearTimeout(this.timer);
530
+ this.timer = undefined;
531
+ this.timer = setTimeout(() => {
532
+ this.initPoint();
533
+ }, 100);
534
+ if (this.currentTimePeriod === 3 || this.currentTimePeriod === 4 || this.currentTimePeriod === 5) {
535
+ return this.timeFormat(d, '%y-%m-%d');
536
+ }
537
+ return this.timeFormat(Number(d), '%X');
538
+ });
194
539
  if (this.model.displaySetting.axisSetting) {
195
540
  if (this.model.displaySetting.axisSetting.showAxisLabel && this.model.displaySetting.axisSetting.axisLabelFont) {
196
541
  chart.xAxis.fontSize(this.model.displaySetting.axisSetting.axisLabelFont.fontSize);
@@ -208,50 +553,165 @@ export class HistoricalCurveElement extends ConditionalDisplayElement {
208
553
  }
209
554
  chart.width(chartWidth);
210
555
  chart.height(chartHeight);
211
- chart.color(this.model.dataSetting.channels.map(c => c.connectorColor));
556
+ // 获取所有通道的颜色(支持多条目和单条目模式)
557
+ let channelColors = [];
558
+ if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
559
+ // 【新格式】多条目模式:收集所有条目的所有通道颜色
560
+ each(this.model.dataSetting.dataItems, dataItem => {
561
+ each(dataItem.channels, channel => {
562
+ channelColors.push(channel.connectorColor);
563
+ });
564
+ });
565
+ }
566
+ else {
567
+ // 【旧格式】单条目模式
568
+ channelColors = this.model.dataSetting.channels.map(c => c.connectorColor);
569
+ }
570
+ chart.color(channelColors);
212
571
  this.rootElement.append('g').datum(data).call(chart);
213
572
  this.rootElement.selectAll('.nv-noData').attr('x', chartWidth / 2).attr('y', chartHeight / 2 + this.displayOption.operationAreaHeight);
214
- nv.utils.windowResize(() => {
573
+ this.resizeEventListener = nv.utils.windowResize(() => {
574
+ if (!this.needResize)
575
+ return;
215
576
  chart.update();
216
577
  this.rootElement.selectAll('.nv-noData').attr('x', chartWidth / 2).attr('y', chartHeight / 2 + this.displayOption.operationAreaHeight);
217
578
  });
579
+ const fontSize = (_b = (_a = this.model.displaySetting.axisSetting) === null || _a === void 0 ? void 0 : _a.axisLabelFont) === null || _b === void 0 ? void 0 : _b.fontSize;
218
580
  this.rootElement.selectAll('.domain').style('stroke-opacity', 1);
219
581
  if (this.model.displaySetting.showAxis && this.model.displaySetting.axisSetting) {
220
582
  const axisColor = this.model.displaySetting.axisSetting.axisColor;
221
583
  this.rootElement.selectAll('.domain').style('stroke', axisColor);
222
- if (this.model.displaySetting.axisSetting.showAxisLabel) {
223
- const fontSize = this.model.displaySetting.axisSetting.axisLabelFont.fontSize;
584
+ if (fontSize && this.model.displaySetting.axisSetting.showAxisLabel) {
224
585
  this.rootElement.selectAll('.nv-axisMaxMin').select('text').style('font-size', fontSize);
225
586
  }
226
587
  }
588
+ let strokeWidth = 0;
589
+ if (this.model.displaySetting.axisSetting.showTick !== false) {
590
+ strokeWidth = 1;
591
+ }
592
+ this.rootElement
593
+ .selectAll('.nv-x')
594
+ .selectAll('.tick')
595
+ .selectAll('line')
596
+ .attr('style', `stroke:${this.model.displaySetting.axisSetting.xAxisTickColor || 'rgb(127, 147, 159)'};stroke-width:${strokeWidth};`);
597
+ this.rootElement
598
+ .selectAll('.nv-y')
599
+ .selectAll('.tick')
600
+ .selectAll('line')
601
+ .attr('style', `stroke:${this.model.displaySetting.axisSetting.yAxisTickColor || 'rgb(127, 147, 159)'};stroke-width:${strokeWidth};`);
602
+ if (fontSize && this.currentTimePeriod === 3 || this.currentTimePeriod === 4 || this.currentTimePeriod === 5) {
603
+ const self = this;
604
+ this.rootElement
605
+ .selectAll('.nv-x')
606
+ .selectAll('.tick')
607
+ .selectAll('text')
608
+ .data(function (d) {
609
+ return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
610
+ })
611
+ .enter()
612
+ .append('text')
613
+ .attr('class', 'full-date')
614
+ .attr('x', 0)
615
+ .attr('y', 0)
616
+ .attr('dy', '2.3em')
617
+ .style('text-anchor', 'middle')
618
+ .style('font-size', fontSize)
619
+ .text((d) => d);
620
+ this.rootElement
621
+ .selectAll('.nv-axisMaxMin-x')
622
+ .selectAll('text')
623
+ .data(function (d) {
624
+ return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
625
+ })
626
+ .enter()
627
+ .append('text')
628
+ .attr('class', 'full-date')
629
+ .attr('x', 0)
630
+ .attr('y', 0)
631
+ .attr('dy', '2.3em')
632
+ .style('text-anchor', 'middle')
633
+ .style('font-size', fontSize)
634
+ .text((d) => d);
635
+ const focusWrap = this.rootElement.selectAll('.nv-focusWrap');
636
+ if (focusWrap.size()) {
637
+ let h = focusWrap.attr('transform');
638
+ if (h && h.length && h.indexOf(',') !== -1) {
639
+ h = h.slice(0, -1).split(',')[1];
640
+ this.rootElement
641
+ .selectAll('.nv-focusWrap')
642
+ .attr('transform', `translate(0,${Number(h) + 15})`);
643
+ }
644
+ }
645
+ const resizeObserver = new window.MutationObserver(() => {
646
+ this.rootElement
647
+ .selectAll('.nv-x')
648
+ .selectAll('.tick')
649
+ .selectAll('.full-date')
650
+ .remove();
651
+ this.rootElement
652
+ .selectAll('.nv-x')
653
+ .selectAll('.tick')
654
+ .selectAll('text')
655
+ .data(function (d) {
656
+ return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
657
+ })
658
+ .enter()
659
+ .append('text')
660
+ .attr('class', 'full-date')
661
+ .attr('x', 0)
662
+ .attr('y', 0)
663
+ .attr('dy', '2.3em')
664
+ .style('text-anchor', 'middle')
665
+ .style('font-size', fontSize)
666
+ .text((d) => d);
667
+ });
668
+ const extent = document.getElementsByClassName('extent');
669
+ if (extent.length) {
670
+ resizeObserver.observe(extent[0], { attributes: true });
671
+ }
672
+ }
673
+ else {
674
+ this.rootElement
675
+ .selectAll('.full-date')
676
+ .remove();
677
+ }
227
678
  }
228
679
  renderOperationArea(chartWidth, chartHeight) {
680
+ const backgroundColor = this.model.displaySetting.axisSetting.filterBackgroudColor || 'inherit';
229
681
  const operationArea = this.rootElement.append('g').attr('transform', `translate(0,${chartHeight + this.displayOption.operationAreaMarginTop})`)
230
682
  .append('foreignObject').attr('width', chartWidth).attr('height', this.displayOption.operationAreaHeight).attr('fill', 'none')
231
683
  .append('xhtml:div').style('height', (this.displayOption.operationAreaHeight - 4) + 'px').style('overflow', 'hidden').style('margin-top', '4px');
232
684
  const selectElement = operationArea.append('select').style('margin-left', this.displayOption.marginLeft + 'px')
685
+ .style('background-color', backgroundColor)
233
686
  .style('font-size', this.displayOption.operationSelectFontSize).on('change', () => {
234
687
  const displayTimePeriod = this.rootElement.select('select').property('value');
235
688
  this.updateTimeRange(displayTimePeriod);
236
689
  this.reRenderElement(this.startTime, this.endTime, this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginOpenEndOpen);
237
690
  });
691
+ const rect = this.$element.parent().parent().find('rect');
692
+ const fillColor = rect.attr('fill');
238
693
  const options = selectElement.selectAll('option').data(this.timePeriods).enter().append('option');
239
- options.text(d => d.name).attr('value', d => d.key).property('selected', d => d.key === Number(this.currentTimePeriod));
694
+ options.text(d => d.name).attr('value', d => d.key).property('selected', d => d.key === Number(this.currentTimePeriod))
695
+ .style('background-color', this.model.displaySetting.axisSetting.filterBackgroudColor || fillColor);
240
696
  const buttonWidth = this.displayOption.operationButtonWidth + 'px', buttonHeight = this.displayOption.operationButtonHeight + 'px';
241
697
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
242
- .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/last_page.svg)')
698
+ .style('float', 'right').style('background-image', 'url(assets/img/black_last_page.png)')
699
+ .style('background-color', backgroundColor).style('background-size', 'cover')
243
700
  .on('click', () => { this.loadLastPage(); });
244
701
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
245
- .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/next_page.svg)')
702
+ .style('float', 'right').style('background-image', 'url(assets/img/black_next_page.png)')
246
703
  .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
704
+ .style('background-color', backgroundColor).style('background-size', 'cover')
247
705
  .on('click', () => { this.loadNextPage(); });
248
706
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
249
- .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/previous_page.svg)')
707
+ .style('float', 'right').style('background-image', 'url(assets/img/black_previous_page.png)')
250
708
  .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
709
+ .style('background-color', backgroundColor).style('background-size', 'cover')
251
710
  .on('click', () => { this.loadPreviousPage(); });
252
711
  operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
253
- .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/first_page.svg)')
712
+ .style('float', 'right').style('background-image', 'url(assets/img/black_first_page.png)')
254
713
  .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
714
+ .style('background-color', backgroundColor).style('background-size', 'cover')
255
715
  .on('click', () => { this.loadFirstPage(); });
256
716
  }
257
717
  timeFormat(datetime, specifier) {