@flexem/fc-gui 3.0.0-alpha.19 → 3.0.0-alpha.190
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.
- package/CHANGELOG.md +413 -0
- package/assets/img/black_first_page.png +0 -0
- package/assets/img/black_last_page.png +0 -0
- package/assets/img/black_next_page.png +0 -0
- package/assets/img/black_previous_page.png +0 -0
- package/assets/img/exclamation-outline-circle.png +0 -0
- package/bundles/@flexem/fc-gui.umd.js +37817 -32408
- package/bundles/@flexem/fc-gui.umd.js.map +1 -1
- package/bundles/@flexem/fc-gui.umd.min.js +5 -5
- package/bundles/@flexem/fc-gui.umd.min.js.map +1 -1
- package/communication/variable/variable-communicator.d.ts +5 -0
- package/communication/variable/variable-state-enum.d.ts +1 -0
- package/communication/variable/variable-state-enum.js +1 -0
- package/communication/variable/variable-state-enum.metadata.json +1 -1
- package/communication/variable/variable-value.d.ts +4 -1
- package/communication/variable/variable-value.js +4 -1
- package/communication/variable/variable-value.metadata.json +1 -1
- package/config/alarm/alarm.store.d.ts +6 -0
- package/config/alarm/alarm.store.js +0 -0
- package/config/alarm/alarm.store.metadata.json +1 -0
- package/config/alarm/get-alarms-args.d.ts +12 -0
- package/config/alarm/get-alarms-args.js +13 -0
- package/config/alarm/get-alarms-args.metadata.json +1 -0
- package/config/alarm/index.d.ts +2 -0
- package/config/alarm/index.js +1 -0
- package/config/alarm/index.metadata.json +1 -0
- package/config/config-store.d.ts +2 -0
- package/config/gui-feature-config.d.ts +3 -0
- package/config/gui-feature-config.js +3 -0
- package/config/gui-feature-config.metadata.json +1 -0
- package/config/index.d.ts +2 -0
- package/config/index.js +2 -0
- package/config/index.metadata.json +1 -1
- package/config/variable/variable-store.d.ts +5 -0
- package/elements/air-quality/air-quality-element.js +3 -4
- package/elements/alarm/alarm-element.d.ts +106 -0
- package/elements/alarm/alarm-element.js +836 -0
- package/elements/alarm/alarm-element.metadata.json +1 -0
- package/elements/alarm/alarm-table-scrollbar.d.ts +35 -0
- package/elements/alarm/alarm-table-scrollbar.js +170 -0
- package/elements/alarm/alarm-table-scrollbar.metadata.json +1 -0
- package/elements/bar-graph-element.d.ts +10 -2
- package/elements/bar-graph-element.js +135 -5
- package/elements/bar-graph-element.metadata.json +1 -1
- package/elements/base/readable-element.d.ts +6 -1
- package/elements/base/readable-element.js +69 -2
- package/elements/base/readable-element.metadata.json +1 -1
- package/elements/base/state-control-element.d.ts +13 -1
- package/elements/base/state-control-element.js +42 -8
- package/elements/base/state-control-element.metadata.json +1 -1
- package/elements/character-display/character-display-element.d.ts +12 -2
- package/elements/character-display/character-display-element.js +35 -7
- package/elements/character-display/character-display-element.metadata.json +1 -1
- package/elements/datetime-display/datetime-display-element.d.ts +1 -0
- package/elements/datetime-display/datetime-display-element.js +10 -2
- package/elements/datetime-display/datetime-display-element.metadata.json +1 -1
- package/elements/datetime-display/time-zone-select-json.d.ts +8 -0
- package/elements/datetime-display/time-zone-select-json.js +558 -0
- package/elements/historical-curve/historical-curve.element.d.ts +45 -3
- package/elements/historical-curve/historical-curve.element.js +707 -45
- package/elements/historical-curve/historical-curve.element.metadata.json +1 -1
- package/elements/main-element.d.ts +1 -0
- package/elements/main-element.js +72 -10
- package/elements/main-element.metadata.json +1 -1
- package/elements/meter-element.d.ts +7 -1
- package/elements/meter-element.js +76 -7
- package/elements/meter-element.metadata.json +1 -1
- package/elements/numerical-display/numerical-display-element.d.ts +31 -3
- package/elements/numerical-display/numerical-display-element.js +166 -15
- package/elements/numerical-display/numerical-display-element.metadata.json +1 -1
- package/elements/per-view-variable-communicator.d.ts +4 -0
- package/elements/per-view-variable-communicator.js +21 -1
- package/elements/per-view-variable-communicator.metadata.json +1 -1
- package/elements/ring-graph/ring-graph-element.d.ts +13 -1
- package/elements/ring-graph/ring-graph-element.js +164 -3
- package/elements/ring-graph/ring-graph-element.metadata.json +1 -1
- package/elements/scroll-alarm/scroll-alarm-element.d.ts +79 -0
- package/elements/scroll-alarm/scroll-alarm-element.js +845 -0
- package/elements/scroll-alarm/scroll-alarm-element.metadata.json +1 -0
- package/elements/shared/graph/graph-state-element.d.ts +1 -0
- package/elements/shared/graph/graph-state-element.js +30 -1
- package/elements/shared/graph/graph-state-element.metadata.json +1 -1
- package/elements/shared/text/text-element.d.ts +14 -0
- package/elements/shared/text/text-element.js +38 -6
- package/elements/shared/text/text-element.metadata.json +1 -1
- package/elements/shared/text/text-state-element.d.ts +39 -2
- package/elements/shared/text/text-state-element.js +242 -67
- package/elements/shared/text/text-state-element.metadata.json +1 -1
- package/elements/static-elements/hyperlink-element.d.ts +28 -2
- package/elements/static-elements/hyperlink-element.js +153 -4
- package/elements/static-elements/hyperlink-element.metadata.json +1 -1
- package/elements/static-elements/text-element.d.ts +28 -2
- package/elements/static-elements/text-element.js +162 -12
- package/elements/static-elements/text-element.metadata.json +1 -1
- package/elements/switch-indicator-light/bit-indicator-light-operator.d.ts +1 -0
- package/elements/switch-indicator-light/bit-indicator-light-operator.js +10 -3
- package/elements/switch-indicator-light/bit-indicator-light-operator.metadata.json +1 -1
- package/elements/switch-indicator-light/switch-indicator-light-element.d.ts +20 -2
- package/elements/switch-indicator-light/switch-indicator-light-element.js +92 -15
- package/elements/switch-indicator-light/switch-indicator-light-element.metadata.json +1 -1
- package/elements/switch-indicator-light/word-indicator-light-operator.d.ts +1 -0
- package/elements/switch-indicator-light/word-indicator-light-operator.js +10 -3
- package/elements/switch-indicator-light/word-indicator-light-operator.metadata.json +1 -1
- package/elements/video/video-element.d.ts +10 -0
- package/elements/video/video-element.js +139 -21
- package/elements/video/video-element.metadata.json +1 -1
- package/elements/view-operation/view-operation.element.d.ts +28 -2
- package/elements/view-operation/view-operation.element.js +172 -8
- package/elements/view-operation/view-operation.element.metadata.json +1 -1
- package/elements/weather/weater-element.js +0 -1
- package/gui/gui-context.d.ts +25 -2
- package/gui/gui-host.d.ts +1 -1
- package/gui/gui-view.d.ts +19 -2
- package/gui/gui-view.js +147 -12
- package/gui/gui-view.metadata.json +1 -1
- package/gui/gui.component.d.ts +3 -0
- package/gui/gui.component.js +15 -2
- package/gui/gui.component.metadata.json +1 -1
- package/localization/localization.service.d.ts +11 -0
- package/localization/localization.service.js +16 -5
- package/localization/localization.service.metadata.json +1 -1
- package/localization/localization.service.zh_CN.js +12 -1
- package/localization/localization.service.zh_CN.metadata.json +1 -1
- package/modal/alert/alert-modal.component.js +1 -1
- package/modal/alert/alert-modal.component.less +13 -5
- package/modal/alert/alert-modal.component.metadata.json +1 -1
- package/modal/confirm-operation/confirm-operation-modal.component.html +2 -2
- package/modal/confirm-operation/confirm-operation-modal.component.js +2 -2
- package/modal/confirm-operation/confirm-operation-modal.component.less +10 -7
- package/modal/confirm-operation/confirm-operation-modal.component.metadata.json +1 -1
- package/modal/verify-password/verify-password-modal.component.d.ts +6 -2
- package/modal/verify-password/verify-password-modal.component.html +2 -2
- package/modal/verify-password/verify-password-modal.component.js +11 -5
- package/modal/verify-password/verify-password-modal.component.less +9 -0
- package/modal/verify-password/verify-password-modal.component.metadata.json +1 -1
- package/modal/write-character/write-character-modal-args.d.ts +3 -1
- package/modal/write-character/write-character-modal-args.js +2 -1
- package/modal/write-character/write-character-modal-args.metadata.json +1 -1
- package/modal/write-character/write-character-modal.component.d.ts +4 -2
- package/modal/write-character/write-character-modal.component.html +4 -4
- package/modal/write-character/write-character-modal.component.js +11 -5
- package/modal/write-character/write-character-modal.component.less +15 -0
- package/modal/write-character/write-character-modal.component.metadata.json +1 -1
- package/modal/write-value/write-value-modal-args.d.ts +5 -1
- package/modal/write-value/write-value-modal-args.js +3 -1
- package/modal/write-value/write-value-modal-args.metadata.json +1 -1
- package/modal/write-value/write-value-modal.component.d.ts +16 -8
- package/modal/write-value/write-value-modal.component.html +10 -5
- package/modal/write-value/write-value-modal.component.js +103 -21
- package/modal/write-value/write-value-modal.component.less +16 -1
- package/modal/write-value/write-value-modal.component.metadata.json +1 -1
- package/model/alarm/alarm.model.d.ts +14 -0
- package/model/alarm/alarm.model.js +0 -0
- package/model/alarm/alarm.model.metadata.json +1 -0
- package/model/bar-graph/bar-graph.d.ts +4 -0
- package/model/base/font-setting-model.d.ts +22 -1
- package/model/base/font-setting-model.metadata.json +1 -1
- package/model/base/readable-model.d.ts +4 -0
- package/model/character-display/character-display.d.ts +1 -0
- package/model/datetime-display/datetime-display.d.ts +1 -0
- package/model/historical-curve/historical-curve-axis-settings.d.ts +11 -0
- package/model/historical-curve/historical-curve-axis-settings.js +5 -0
- package/model/historical-curve/historical-curve-axis-settings.metadata.json +1 -1
- package/model/historical-curve/historical-curve-chanel.model.d.ts +8 -0
- package/model/historical-curve/historical-curve.display-settings.d.ts +1 -0
- package/model/meter/meter.d.ts +4 -0
- package/model/numerical-display/numerical-display.d.ts +1 -0
- package/model/numerical-display/numerical-operation.d.ts +10 -2
- package/model/ring-graph/ring-graph.model.d.ts +8 -0
- package/model/scroll-alarm/scroll-alarm.model.d.ts +21 -0
- package/model/scroll-alarm/scroll-alarm.model.js +0 -0
- package/model/scroll-alarm/scroll-alarm.model.metadata.json +1 -0
- package/model/settings/popup/view-popup-location-type.d.ts +2 -1
- package/model/settings/popup/view-popup-location-type.js +1 -0
- package/model/settings/popup/view-popup-location-type.metadata.json +1 -1
- package/model/shared/state/state.d.ts +1 -0
- package/model/shared/state/state.js +1 -0
- package/model/shared/state/state.metadata.json +1 -1
- package/model/shared/text/font.d.ts +1 -1
- package/model/shared/text/text.d.ts +3 -0
- package/model/switch-indicator-light/switch-indicator-light.d.ts +3 -0
- package/model/view-operation/view-operation-element.model.d.ts +7 -1
- package/package.json +1 -1
- package/public_api.js +1 -0
- package/remote/communication/variable/remote-variable-communicator.d.ts +30 -0
- package/remote/communication/variable/remote-variable-communicator.js +193 -3
- package/remote/communication/variable/remote-variable-communicator.metadata.json +1 -1
- package/remote/communication/variable/remote-variable-protocol.d.ts +8 -0
- package/service/index.d.ts +4 -0
- package/service/index.js +1 -0
- package/service/index.metadata.json +1 -1
- package/service/language.service.d.ts +37 -0
- package/service/language.service.js +0 -0
- package/service/language.service.metadata.json +1 -0
- package/service/released-variable/index.d.ts +1 -0
- package/service/released-variable/index.js +0 -0
- package/service/released-variable/index.metadata.json +1 -0
- package/service/released-variable/released-variable.service.d.ts +4 -0
- package/service/released-variable/released-variable.service.js +0 -0
- package/service/released-variable/released-variable.service.metadata.json +1 -0
- package/service/system-text-library.service.d.ts +77 -0
- package/service/system-text-library.service.js +29 -0
- package/service/system-text-library.service.metadata.json +1 -0
- package/service/text-library.service.d.ts +49 -0
- package/service/text-library.service.js +0 -0
- package/service/text-library.service.metadata.json +1 -0
- package/shared/gui-consts.d.ts +2 -0
- package/shared/gui-consts.js +3 -1
- package/shared/gui-consts.metadata.json +1 -1
- package/utils/data-type/fbox-data-type.service.js +40 -0
- package/utils/numerical-operation.service.js +28 -4
- package/view/popup-view.service.d.ts +1 -1
|
@@ -2,6 +2,7 @@ import { LOGGER_SERVICE_TOKEN } from '../../logger';
|
|
|
2
2
|
import * as d3 from 'd3-selection';
|
|
3
3
|
import { drag } from 'd3-drag';
|
|
4
4
|
import { ViewOperationType } from '../../model';
|
|
5
|
+
import { TextElementModal } from '../shared/text/text-element';
|
|
5
6
|
import { ConditionalEnableElement } from '../base/conditional-enable-element';
|
|
6
7
|
import { GraphStateElement } from '../shared/graph/graph-state-element';
|
|
7
8
|
import { AlertModalComponent } from '../../modal/alert/alert-modal.component';
|
|
@@ -9,7 +10,7 @@ import { ConfirmOperationModalComponent } from '../../modal/confirm-operation/co
|
|
|
9
10
|
import { VerifyPasswordModalComponent } from '../../modal/verify-password/verify-password-modal.component';
|
|
10
11
|
import { ViewService } from '../../view/view.service';
|
|
11
12
|
export class ViewOperationElement extends ConditionalEnableElement {
|
|
12
|
-
constructor(element, injector, modalService, variableCommunicator, variableStore, graphStore, permissionChecker, operationRecordService, securityChecker, localization, popupViewService, hostContainerId, el, signalRAppId) {
|
|
13
|
+
constructor(element, injector, modalService, variableCommunicator, variableStore, graphStore, permissionChecker, operationRecordService, securityChecker, localization, popupViewService, hostContainerId, el, signalRAppId, textLibraryService, languageService, guiContext) {
|
|
13
14
|
super(element, permissionChecker, variableCommunicator, variableStore, localization, signalRAppId);
|
|
14
15
|
this.modalService = modalService;
|
|
15
16
|
this.graphStore = graphStore;
|
|
@@ -18,20 +19,27 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
18
19
|
this.popupViewService = popupViewService;
|
|
19
20
|
this.hostContainerId = hostContainerId;
|
|
20
21
|
this.el = el;
|
|
22
|
+
this.textLibraryService = textLibraryService;
|
|
23
|
+
this.languageService = languageService;
|
|
24
|
+
this.guiContext = guiContext;
|
|
21
25
|
this.touchedScreenX = 0;
|
|
22
26
|
this.touchedScreenY = 0;
|
|
23
27
|
this.logger = injector.get(LOGGER_SERVICE_TOKEN);
|
|
24
28
|
this.viewService = injector.get(ViewService);
|
|
25
29
|
this.initElement();
|
|
30
|
+
this.subscribeLanguageChange();
|
|
26
31
|
}
|
|
27
32
|
initElement() {
|
|
28
33
|
const width = this.model.size.width;
|
|
29
34
|
const height = this.model.size.height;
|
|
35
|
+
// 在 initGraph 之前移除模板原有的 SVG <text>(改用 foreignObject 渲染,支持换行居中)
|
|
36
|
+
// 必须在 initGraph 前执行,否则会误删图形状态内部后续添加的文字节点
|
|
37
|
+
this.$element.find('text').remove();
|
|
30
38
|
this.initGraph(width, height);
|
|
31
39
|
this.rootElement.append('rect').attr('id', 'StateFrame').attr('width', width).attr('height', height).attr('fill', 'transparent');
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
const font = this.getFontStyle();
|
|
41
|
+
this.textElementModal = new TextElementModal(this.getDisplayText(), font, width, height);
|
|
42
|
+
this.$element.append(this.textElementModal.Element);
|
|
35
43
|
this.switchToState(0);
|
|
36
44
|
this.rootElement.on('mousedown', () => {
|
|
37
45
|
if (!this.isInitialized) {
|
|
@@ -41,7 +49,7 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
41
49
|
d3.event.preventDefault();
|
|
42
50
|
if (!this.isEnable) {
|
|
43
51
|
const message = !this.isGrantedPermission ? this.localization.permissiontip : this.localization.conditionIsNotMetTip;
|
|
44
|
-
const modalRef = this.modalService.show(AlertModalComponent, { initialState: { alermMessage: message }, backdrop: 'static', class: 'gui-modal-dialog-position', animated: false });
|
|
52
|
+
const modalRef = this.modalService.show(AlertModalComponent, { initialState: { alermMessage: message }, backdrop: 'static', class: 'gui-modal-dialog-position gui-modal-dialog-tip', animated: false });
|
|
45
53
|
const onClosedSub = modalRef.content.onClosed.subscribe(() => {
|
|
46
54
|
modalRef.hide();
|
|
47
55
|
onClosedSub.unsubscribe();
|
|
@@ -57,7 +65,7 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
57
65
|
}
|
|
58
66
|
if (this.model.useSecurity) {
|
|
59
67
|
const modalRef = this.modalService.show(ConfirmOperationModalComponent, {
|
|
60
|
-
backdrop: 'static', class: 'gui-modal-dialog-position', animated: false
|
|
68
|
+
backdrop: 'static', class: 'gui-modal-dialog-position gui-modal-dialog-tip', animated: false
|
|
61
69
|
});
|
|
62
70
|
const onClosedSub = modalRef.content.onClosed.subscribe(result => {
|
|
63
71
|
modalRef.hide();
|
|
@@ -124,8 +132,12 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
checkElementPassword() {
|
|
135
|
+
var _a;
|
|
127
136
|
if (this.model.enablePassword) {
|
|
128
|
-
const modalRef = this.modalService.show(VerifyPasswordModalComponent, {
|
|
137
|
+
const modalRef = this.modalService.show(VerifyPasswordModalComponent, {
|
|
138
|
+
initialState: { securityChecker: this.securityChecker, releasedVariableService: (_a = this.guiContext) === null || _a === void 0 ? void 0 : _a.releasedVariableService },
|
|
139
|
+
backdrop: 'static', class: 'gui-modal-dialog-position', animated: false
|
|
140
|
+
});
|
|
129
141
|
const onClosedSub = modalRef.content.onClosed.subscribe(result => {
|
|
130
142
|
modalRef.hide();
|
|
131
143
|
onClosedSub.unsubscribe();
|
|
@@ -169,7 +181,11 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
169
181
|
popView() {
|
|
170
182
|
const viewIndex = this.model.viewIndex;
|
|
171
183
|
if (null != viewIndex) {
|
|
172
|
-
|
|
184
|
+
const triggerRect = this.$element[0].getBoundingClientRect();
|
|
185
|
+
this.popupViewService.popView(viewIndex, this.hostContainerId, this.el, triggerRect).subscribe(() => this.recordViewOperation(), error => this.logger.error(`ToggleView(${viewIndex}) failed. ${error}`));
|
|
186
|
+
$(`#${this.hostContainerId} video`)
|
|
187
|
+
.addClass('video-hidden')
|
|
188
|
+
.css('visibility', 'hidden');
|
|
173
189
|
}
|
|
174
190
|
else {
|
|
175
191
|
this.logger.error('[GUI]Toggle View:invalid view index');
|
|
@@ -177,6 +193,11 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
177
193
|
}
|
|
178
194
|
closeView() {
|
|
179
195
|
this.popupViewService.closeView();
|
|
196
|
+
if ($(`#${this.hostContainerId} > svg > svg`).length === 0) {
|
|
197
|
+
$(`#${this.hostContainerId} .video-hidden`)
|
|
198
|
+
.removeClass('video-hidden')
|
|
199
|
+
.css('visibility', 'visible');
|
|
200
|
+
}
|
|
180
201
|
this.recordViewOperation();
|
|
181
202
|
}
|
|
182
203
|
moveView(movementX, movementY) {
|
|
@@ -191,4 +212,147 @@ export class ViewOperationElement extends ConditionalEnableElement {
|
|
|
191
212
|
this.operationRecordService.record({ operationDescription: this.model.operationDescription });
|
|
192
213
|
}
|
|
193
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* 订阅语种变化事件
|
|
217
|
+
*/
|
|
218
|
+
subscribeLanguageChange() {
|
|
219
|
+
if (this.guiContext && this.guiContext.languageChanged$) {
|
|
220
|
+
this.languageChangeSubscription = this.guiContext.languageChanged$.subscribe(() => {
|
|
221
|
+
this.updateTextContent();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 获取当前语种的字体样式
|
|
227
|
+
*/
|
|
228
|
+
getFontStyle() {
|
|
229
|
+
const getTargetLanguage = () => {
|
|
230
|
+
var _a, _b, _c, _d, _e, _f;
|
|
231
|
+
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;
|
|
232
|
+
const defaultLanguage = ((_d = this.languageService) === null || _d === void 0 ? void 0 : _d.getDefaultLanguage()) || 'zh-CN';
|
|
233
|
+
if (currentLanguageId === null || currentLanguageId === undefined) {
|
|
234
|
+
return defaultLanguage;
|
|
235
|
+
}
|
|
236
|
+
return ((_f = (_e = this.guiContext) === null || _e === void 0 ? void 0 : _e.getLanguageCultureById) === null || _f === void 0 ? void 0 : _f.call(_e, currentLanguageId)) || defaultLanguage;
|
|
237
|
+
};
|
|
238
|
+
const targetLanguage = getTargetLanguage();
|
|
239
|
+
if (this.model.fontStyleCultures && this.model.fontStyleCultures[targetLanguage]) {
|
|
240
|
+
const style = this.model.fontStyleCultures[targetLanguage];
|
|
241
|
+
const fs = style.fontStyle || '';
|
|
242
|
+
const isBold = fs.indexOf('bold') !== -1;
|
|
243
|
+
const isItalic = fs.indexOf('Italic') !== -1;
|
|
244
|
+
const parts = fs.split(' ');
|
|
245
|
+
const familyIndex = (isBold ? 1 : 0) + (isItalic ? 1 : 0) + 1;
|
|
246
|
+
const fontFamily = parts.length > familyIndex ? parts[familyIndex] : null;
|
|
247
|
+
return {
|
|
248
|
+
isUnderline: style.isUnderline,
|
|
249
|
+
isBold,
|
|
250
|
+
isItalic,
|
|
251
|
+
fontSize: style.fontSize,
|
|
252
|
+
fontFamily: fontFamily || 'msyh',
|
|
253
|
+
color: style.color
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return Object.assign(Object.assign({}, this.model.label.font), { fontFamily: this.model.label.font.fontFamily || 'msyh' });
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* 更新文本内容(支持文本库和语种切换)
|
|
260
|
+
*/
|
|
261
|
+
updateTextContent() {
|
|
262
|
+
if (this.textElementModal) {
|
|
263
|
+
const font = this.getFontStyle();
|
|
264
|
+
this.textElementModal.updateFont(font);
|
|
265
|
+
this.textElementModal.updateText(this.getDisplayText());
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 获取显示文本
|
|
270
|
+
*/
|
|
271
|
+
getDisplayText() {
|
|
272
|
+
var _a, _b, _c, _d, _e, _f;
|
|
273
|
+
// 检查是否使用文本库
|
|
274
|
+
if (this.model.textLibrary && this.model.textLibrary.labelType === 'textLibrary') {
|
|
275
|
+
const textLibraryId = this.model.textLibrary.selectedTextLibraryItem;
|
|
276
|
+
const textItem = this.model.textLibrary.selectedTextItem;
|
|
277
|
+
if (textLibraryId && textItem && this.textLibraryService) {
|
|
278
|
+
// 获取文本库数据
|
|
279
|
+
const textLibraryData = this.textLibraryService.getTextLibraryById(typeof textLibraryId === 'string' ? parseInt(textLibraryId, 10) : textLibraryId);
|
|
280
|
+
if (textLibraryData && textLibraryData.data) {
|
|
281
|
+
// 查找对应的文本条目
|
|
282
|
+
const textEntry = textLibraryData.data.find(entry => entry.key === textItem.toString());
|
|
283
|
+
if (textEntry) {
|
|
284
|
+
// 获取当前语种ID
|
|
285
|
+
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;
|
|
286
|
+
// 确定要使用的语种代码(culture)
|
|
287
|
+
let targetLanguage;
|
|
288
|
+
const defaultLanguage = ((_d = this.languageService) === null || _d === void 0 ? void 0 : _d.getDefaultLanguage()) || 'zh-CN';
|
|
289
|
+
if (currentLanguageId === null || currentLanguageId === undefined) {
|
|
290
|
+
// 设备未设置当前语种,使用默认语种
|
|
291
|
+
targetLanguage = defaultLanguage;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
// 设备已设置当前语种,获取对应的语种代码
|
|
295
|
+
const currentLanguage = (_f = (_e = this.guiContext) === null || _e === void 0 ? void 0 : _e.getLanguageCultureById) === null || _f === void 0 ? void 0 : _f.call(_e, currentLanguageId);
|
|
296
|
+
if (currentLanguage) {
|
|
297
|
+
targetLanguage = currentLanguage;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// 无法获取语种代码,使用默认语种
|
|
301
|
+
targetLanguage = defaultLanguage;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// 返回对应语种的文本
|
|
305
|
+
if (textEntry.cultures && textEntry.cultures[targetLanguage]) {
|
|
306
|
+
return textEntry.cultures[targetLanguage];
|
|
307
|
+
}
|
|
308
|
+
// 如果没有对应语种,返回空字符串
|
|
309
|
+
return '';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// 文本库配置但未找到数据,返回空字符串
|
|
314
|
+
return '';
|
|
315
|
+
}
|
|
316
|
+
// 处理自定义文本(支持多语种)
|
|
317
|
+
if (this.model.label) {
|
|
318
|
+
// 获取目标语种(公共逻辑)
|
|
319
|
+
const getTargetLanguage = () => {
|
|
320
|
+
var _a, _b, _c, _d, _e, _f;
|
|
321
|
+
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;
|
|
322
|
+
const defaultLanguage = ((_d = this.languageService) === null || _d === void 0 ? void 0 : _d.getDefaultLanguage()) || 'zh-CN';
|
|
323
|
+
if (currentLanguageId === null || currentLanguageId === undefined) {
|
|
324
|
+
return defaultLanguage;
|
|
325
|
+
}
|
|
326
|
+
return ((_f = (_e = this.guiContext) === null || _e === void 0 ? void 0 : _e.getLanguageCultureById) === null || _f === void 0 ? void 0 : _f.call(_e, currentLanguageId)) || defaultLanguage;
|
|
327
|
+
};
|
|
328
|
+
// 新格式:cultures 在 label 顶层
|
|
329
|
+
if (this.model.label.cultures && typeof this.model.label.cultures === 'object') {
|
|
330
|
+
return this.model.label.cultures[getTargetLanguage()] || '';
|
|
331
|
+
}
|
|
332
|
+
if (this.model.label.content) {
|
|
333
|
+
// 中间格式(兼容):content 为 { cultures: {...} }
|
|
334
|
+
if (typeof this.model.label.content === 'object' && this.model.label.content.cultures) {
|
|
335
|
+
return this.model.label.content.cultures[getTargetLanguage()] || '';
|
|
336
|
+
}
|
|
337
|
+
// 旧格式:字符串
|
|
338
|
+
if (typeof this.model.label.content === 'string') {
|
|
339
|
+
return this.model.label.content;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return '';
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 释放资源
|
|
347
|
+
*/
|
|
348
|
+
dispose() {
|
|
349
|
+
// 取消语种变化订阅
|
|
350
|
+
if (this.languageChangeSubscription) {
|
|
351
|
+
this.languageChangeSubscription.unsubscribe();
|
|
352
|
+
this.languageChangeSubscription = undefined;
|
|
353
|
+
}
|
|
354
|
+
if (this.$element) {
|
|
355
|
+
this.$element.remove();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
194
358
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"__symbolic":"module","version":4,"metadata":{"ViewOperationElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-enable-element","name":"ConditionalEnableElement","line":
|
|
1
|
+
[{"__symbolic":"module","version":4,"metadata":{"ViewOperationElement":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"../base/conditional-enable-element","name":"ConditionalEnableElement","line":23,"character":42},"members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"error","message":"Could not resolve type","line":36,"character":25,"context":{"typeName":"HTMLElement"}},{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":36,"character":48},{"__symbolic":"reference","module":"ngx-bootstrap/modal","name":"BsModalService","line":37,"character":39},{"__symbolic":"reference","module":"../../communication","name":"VariableCommunicator","line":38,"character":30},{"__symbolic":"reference","module":"../../config","name":"VariableStore","line":39,"character":23},{"__symbolic":"reference","module":"../../config","name":"GraphStore","line":40,"character":37},{"__symbolic":"reference","module":"../../service","name":"PermissionChecker","line":41,"character":27},{"__symbolic":"reference","module":"../../service","name":"OperationRecordService","line":42,"character":49},{"__symbolic":"reference","module":"../../security","name":"SecurityChecker","line":43,"character":42},{"__symbolic":"reference","module":"../../localization","name":"Localization","line":44,"character":22},{"__symbolic":"reference","module":"../../view/popup-view.service","name":"PopupViewService","line":45,"character":43},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"@angular/core","name":"ElementRef","line":47,"character":29},{"__symbolic":"reference","name":"string"},{"__symbolic":"reference","module":"../../service","name":"TextLibraryService","line":49,"character":46},{"__symbolic":"reference","module":"../../service","name":"LanguageService","line":50,"character":43},{"__symbolic":"reference","module":"../../gui/gui-context","name":"GuiContext","line":51,"character":38}]}],"initElement":[{"__symbolic":"method"}],"switchToState":[{"__symbolic":"method"}],"initGraph":[{"__symbolic":"method"}],"checkElementPassword":[{"__symbolic":"method"}],"executeViewOperation":[{"__symbolic":"method"}],"toggleView":[{"__symbolic":"method"}],"popView":[{"__symbolic":"method"}],"closeView":[{"__symbolic":"method"}],"moveView":[{"__symbolic":"method"}],"recordViewOperation":[{"__symbolic":"method"}],"subscribeLanguageChange":[{"__symbolic":"method"}],"getFontStyle":[{"__symbolic":"method"}],"updateTextContent":[{"__symbolic":"method"}],"getDisplayText":[{"__symbolic":"method"}],"dispose":[{"__symbolic":"method"}]}}}}]
|
|
@@ -45,7 +45,6 @@ export class WeatherElement extends ConditionalDisplayElement {
|
|
|
45
45
|
}
|
|
46
46
|
handleGettingAddress() {
|
|
47
47
|
this.handleUnusualCommon(this.localization.weatherNotAvailable);
|
|
48
|
-
setTimeout(() => this.initWeatherInfo(), 1000 * 60);
|
|
49
48
|
}
|
|
50
49
|
handleNoAddress() {
|
|
51
50
|
this.handleUnusualCommon(this.localization.weatherNotAddress);
|
package/gui/gui-context.d.ts
CHANGED
|
@@ -1,16 +1,39 @@
|
|
|
1
1
|
import { ConfigStore } from '../config';
|
|
2
2
|
import { VariableCommunicator } from '../communication';
|
|
3
3
|
import { SecurityChecker } from '../security';
|
|
4
|
-
import { VideoService, WeatherService, PermissionChecker, OperationRecordService } from '../service';
|
|
4
|
+
import { VideoService, WeatherService, PermissionChecker, OperationRecordService, ReleasedVariableService, TextLibraryService, LanguageService, SystemTextLibraryService } from '../service';
|
|
5
|
+
import { Subject } from 'rxjs';
|
|
5
6
|
export interface GuiContext {
|
|
6
7
|
readonly configStore: ConfigStore;
|
|
7
8
|
readonly variableCommunicator: VariableCommunicator;
|
|
8
9
|
readonly permissionChecker: PermissionChecker;
|
|
9
10
|
readonly operationRecordService: OperationRecordService;
|
|
11
|
+
readonly releasedVariableService: ReleasedVariableService;
|
|
10
12
|
readonly securityChecker: SecurityChecker;
|
|
11
13
|
readonly videoService: VideoService;
|
|
12
14
|
readonly weatherService: WeatherService;
|
|
15
|
+
readonly textLibraryService?: TextLibraryService;
|
|
16
|
+
readonly languageService?: LanguageService;
|
|
17
|
+
readonly systemTextLibraryService?: SystemTextLibraryService;
|
|
13
18
|
readonly configureViewId?: number;
|
|
14
19
|
readonly configureViewCode?: string;
|
|
15
|
-
|
|
20
|
+
/**
|
|
21
|
+
* 模拟模式下由后端 GetVariablesForDesigner 查询出的"有效变量"名集合
|
|
22
|
+
* (IncludeCalculationVariable=true, HasRefVariable=true,即绑定了 manager 的变量)。
|
|
23
|
+
* 运行模式不填充,元件会回退到 variableStore.getAllVariableNames()。
|
|
24
|
+
*/
|
|
25
|
+
readonly validVariableNames?: string[];
|
|
26
|
+
readonly languageChanged$?: Subject<number | null>;
|
|
27
|
+
getCurrentLanguageId?(): number | null;
|
|
28
|
+
getDefaultLanguageId?(): number;
|
|
29
|
+
getLanguageCultureById?(languageId: number | null): string | null;
|
|
30
|
+
updateCurrentLanguageId?(languageId: number | null): Promise<void>;
|
|
31
|
+
showCustomTimeRangeModal?(config: {
|
|
32
|
+
startTime?: Date;
|
|
33
|
+
endTime?: Date;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
startTime: Date;
|
|
36
|
+
endTime: Date;
|
|
37
|
+
} | null>;
|
|
38
|
+
dispose(): void;
|
|
16
39
|
}
|
package/gui/gui-host.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare class GuiHost implements Disposable {
|
|
|
13
13
|
readonly el: ElementRef;
|
|
14
14
|
private isLoaded;
|
|
15
15
|
private currentView;
|
|
16
|
-
|
|
16
|
+
currentViewIndex: number;
|
|
17
17
|
private containerEl;
|
|
18
18
|
private currentViewSize;
|
|
19
19
|
private currentViewReiszeMode;
|
package/gui/gui-view.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare class GuiView implements PopupViewService {
|
|
|
12
12
|
private readonly bsModalService;
|
|
13
13
|
private readonly context;
|
|
14
14
|
private readonly parentView?;
|
|
15
|
+
/** showShadow() 给阴影留白,内容整体右下偏移的量,"按钮附近"定位需要据此修正才能让可见内容与按钮对齐 */
|
|
16
|
+
private static readonly SHADOW_INSET;
|
|
15
17
|
get element(): HTMLElement;
|
|
16
18
|
get rawWidth(): number;
|
|
17
19
|
get rawHeight(): number;
|
|
@@ -21,7 +23,10 @@ export declare class GuiView implements PopupViewService {
|
|
|
21
23
|
private mainElement;
|
|
22
24
|
private muskElement;
|
|
23
25
|
private openVariableStatesSubscription;
|
|
24
|
-
private
|
|
26
|
+
private requestVirtualDeviceState;
|
|
27
|
+
private virtualDeviceStatesChanged;
|
|
28
|
+
private openVariablesSubscriptions;
|
|
29
|
+
private openedVariableNamesForValues;
|
|
25
30
|
private openedVariableNames;
|
|
26
31
|
private perViewVariableCommunicator;
|
|
27
32
|
private svgRootClass;
|
|
@@ -31,7 +36,19 @@ export declare class GuiView implements PopupViewService {
|
|
|
31
36
|
resize(size: Size, resizeMode: ViewResizeMode, svgRootClass: string): void;
|
|
32
37
|
load(config: ViewModel, guiOptions: GuiOptions): void;
|
|
33
38
|
loadElementState(): void;
|
|
34
|
-
popView(viewIndex: number, hostContainerId: string, el: ElementRef): Observable<void>;
|
|
39
|
+
popView(viewIndex: number, hostContainerId: string, el: ElementRef, triggerRect?: DOMRect): Observable<void>;
|
|
40
|
+
/**
|
|
41
|
+
* 把屏幕像素矩形(如按钮的 getBoundingClientRect)换算为当前画面 SVG 坐标系下的矩形。
|
|
42
|
+
* 用 getScreenCTM 而不是简单的宽高比换算,因为画面容器宽高比与 rawWidth/rawHeight 不一致时
|
|
43
|
+
* SVG 会按 preserveAspectRatio 留白(letterbox),线性比例换算会有偏差。
|
|
44
|
+
*/
|
|
45
|
+
private toLocalRect;
|
|
46
|
+
/**
|
|
47
|
+
* 按钮附近:按钮与弹出画面之间留缝隙,两侧都放得下时默认放右侧,放不下时放在放得下的一侧;
|
|
48
|
+
* 竖直方向默认与按钮顶部对齐,下方放不下时改为与按钮底部对齐;
|
|
49
|
+
* 左右两侧都放不下(弹窗比画面还宽)时直接退化为画面居中,不再跟随按钮
|
|
50
|
+
*/
|
|
51
|
+
private getNearButtonPosition;
|
|
35
52
|
closeView(): void;
|
|
36
53
|
moveView(movementX: number, movementY: number): void;
|
|
37
54
|
showMusk(): void;
|
package/gui/gui-view.js
CHANGED
|
@@ -2,13 +2,14 @@ import { LOGGER_SERVICE_TOKEN } from '../logger';
|
|
|
2
2
|
import * as d3 from 'd3-selection';
|
|
3
3
|
import { each, forEach } from 'lodash';
|
|
4
4
|
import { map } from 'rxjs/operators';
|
|
5
|
-
import { VariableStateEnum } from '../communication';
|
|
5
|
+
import { VariableStateEnum, VariableState } from '../communication';
|
|
6
6
|
import { MainElement } from '../elements/main-element';
|
|
7
7
|
import { PerViewVariableCommunicator } from '../elements/per-view-variable-communicator';
|
|
8
8
|
import { ViewPopupBackdropType, ViewPopupLocationType } from '../model';
|
|
9
9
|
import { ViewResizeMode, DisplayMode, GlobalSettings } from '../settings';
|
|
10
10
|
import { ConfigIsEmptyError } from '../shared';
|
|
11
11
|
import { Guid } from '../utils/guid';
|
|
12
|
+
import { GuiFeatureConfig } from '../config';
|
|
12
13
|
export class GuiView {
|
|
13
14
|
constructor(injector, bsModalService, context, parentView) {
|
|
14
15
|
this.injector = injector;
|
|
@@ -16,6 +17,8 @@ export class GuiView {
|
|
|
16
17
|
this.context = context;
|
|
17
18
|
this.parentView = parentView;
|
|
18
19
|
this.viewId = Guid.newGuid().toString('n');
|
|
20
|
+
this.openVariablesSubscriptions = [];
|
|
21
|
+
this.openedVariableNamesForValues = new Set();
|
|
19
22
|
this.logger = injector.get(LOGGER_SERVICE_TOKEN);
|
|
20
23
|
this.childViews = new Array();
|
|
21
24
|
if (parentView) {
|
|
@@ -61,6 +64,7 @@ export class GuiView {
|
|
|
61
64
|
this.logger.debug('[GUI] View loaded.');
|
|
62
65
|
}
|
|
63
66
|
loadElementState() {
|
|
67
|
+
var _a, _b, _c;
|
|
64
68
|
if (!this.mainElement) {
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
@@ -71,16 +75,64 @@ export class GuiView {
|
|
|
71
75
|
if (this.openVariableStatesSubscription) {
|
|
72
76
|
this.openVariableStatesSubscription.unsubscribe();
|
|
73
77
|
}
|
|
78
|
+
this.openVariablesSubscriptions.forEach(s => s.unsubscribe());
|
|
79
|
+
this.openVariablesSubscriptions = [];
|
|
80
|
+
this.openedVariableNamesForValues.clear();
|
|
74
81
|
this.openedVariableNames = this.mainElement.getVariableNames();
|
|
75
|
-
|
|
82
|
+
// 分离系统变量和普通变量
|
|
83
|
+
const systemVariables = [];
|
|
84
|
+
const normalVariablesForState = [];
|
|
85
|
+
this.openedVariableNames.forEach(variableName => {
|
|
86
|
+
if (variableName === '当前语种ID') {
|
|
87
|
+
// 系统变量直接订阅,不需要等待状态
|
|
88
|
+
systemVariables.push(variableName);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
normalVariablesForState.push(variableName);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// 立即订阅系统变量
|
|
95
|
+
if (systemVariables.length > 0) {
|
|
96
|
+
this.normalOpenedVariableNames(systemVariables);
|
|
97
|
+
}
|
|
98
|
+
if (normalVariablesForState.indexOf('设备状态') !== -1) {
|
|
99
|
+
if (this.requestVirtualDeviceState) {
|
|
100
|
+
this.requestVirtualDeviceState.unsubscribe();
|
|
101
|
+
}
|
|
102
|
+
if (this.requestVirtualDeviceState) {
|
|
103
|
+
this.requestVirtualDeviceState.unsubscribe();
|
|
104
|
+
}
|
|
105
|
+
this.normalOpenedVariableNames(['设备状态']);
|
|
106
|
+
this.requestVirtualDeviceState =
|
|
107
|
+
this.perViewVariableCommunicator.requestVirtualDeviceState().subscribe({
|
|
108
|
+
complete: () => { }
|
|
109
|
+
});
|
|
110
|
+
normalVariablesForState.splice(normalVariablesForState.indexOf('设备状态'), 1);
|
|
111
|
+
}
|
|
112
|
+
const validVariableNames = (_a = this.context.validVariableNames) !== null && _a !== void 0 ? _a : (_c = (_b = this.context.configStore.variableStore).getAllVariableNames) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
113
|
+
if (validVariableNames) {
|
|
114
|
+
const invalidVariables = normalVariablesForState.filter(name => !validVariableNames.includes(name));
|
|
115
|
+
if (invalidVariables.length > 0) {
|
|
116
|
+
const unbindStates = invalidVariables.map(name => new VariableState(name, VariableStateEnum.InvalidMonitor));
|
|
117
|
+
this.mainElement.reportVariableStates(unbindStates);
|
|
118
|
+
invalidVariables.forEach(name => {
|
|
119
|
+
const idx = normalVariablesForState.indexOf(name);
|
|
120
|
+
if (idx !== -1) {
|
|
121
|
+
normalVariablesForState.splice(idx, 1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (normalVariablesForState.length === 0) {
|
|
76
127
|
return;
|
|
77
128
|
}
|
|
78
129
|
this.openVariableStatesSubscription =
|
|
79
|
-
this.perViewVariableCommunicator.subscribeVariableStates(
|
|
130
|
+
this.perViewVariableCommunicator.subscribeVariableStates(normalVariablesForState).subscribe((states) => {
|
|
80
131
|
this.mainElement.reportVariableStates(states);
|
|
81
132
|
const normalVariableNames = [];
|
|
82
133
|
each(states, v => {
|
|
83
|
-
if (v.state === VariableStateEnum.Normal &&
|
|
134
|
+
if ((v.state === VariableStateEnum.Normal || (GuiFeatureConfig.isOfflineDisplayEnabled && v.state === VariableStateEnum.Offline))
|
|
135
|
+
&& normalVariableNames.indexOf(v.variableName) === -1) {
|
|
84
136
|
normalVariableNames.push(v.variableName);
|
|
85
137
|
}
|
|
86
138
|
});
|
|
@@ -89,7 +141,7 @@ export class GuiView {
|
|
|
89
141
|
}
|
|
90
142
|
});
|
|
91
143
|
}
|
|
92
|
-
popView(viewIndex, hostContainerId, el) {
|
|
144
|
+
popView(viewIndex, hostContainerId, el, triggerRect) {
|
|
93
145
|
return this.context.configStore.viewStore.getViewConfig(viewIndex).pipe(map(view => {
|
|
94
146
|
if (!view.config || !view.config.length) {
|
|
95
147
|
throw new ConfigIsEmptyError();
|
|
@@ -110,6 +162,10 @@ export class GuiView {
|
|
|
110
162
|
x = settings.popup.location.x;
|
|
111
163
|
y = settings.popup.location.y;
|
|
112
164
|
}
|
|
165
|
+
else if (settings.popup.locationType === ViewPopupLocationType.NearButton && triggerRect) {
|
|
166
|
+
const hasShadow = settings.popup.backdropType === ViewPopupBackdropType.Shadow && !this.isMobileMode;
|
|
167
|
+
({ x, y } = this.getNearButtonPosition(popView, triggerRect, hasShadow));
|
|
168
|
+
}
|
|
113
169
|
else {
|
|
114
170
|
x = (this.rawWidth - popView.rawWidth) / 2;
|
|
115
171
|
y = (this.rawHeight - popView.rawHeight) / 2;
|
|
@@ -129,6 +185,73 @@ export class GuiView {
|
|
|
129
185
|
this.element.appendChild(popView.element);
|
|
130
186
|
}));
|
|
131
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* 把屏幕像素矩形(如按钮的 getBoundingClientRect)换算为当前画面 SVG 坐标系下的矩形。
|
|
190
|
+
* 用 getScreenCTM 而不是简单的宽高比换算,因为画面容器宽高比与 rawWidth/rawHeight 不一致时
|
|
191
|
+
* SVG 会按 preserveAspectRatio 留白(letterbox),线性比例换算会有偏差。
|
|
192
|
+
*/
|
|
193
|
+
toLocalRect(rect) {
|
|
194
|
+
const svgRoot = this.element;
|
|
195
|
+
const ctm = svgRoot.getScreenCTM && svgRoot.getScreenCTM();
|
|
196
|
+
if (ctm) {
|
|
197
|
+
const inverse = ctm.inverse();
|
|
198
|
+
const toLocal = (clientX, clientY) => {
|
|
199
|
+
const pt = svgRoot.createSVGPoint();
|
|
200
|
+
pt.x = clientX;
|
|
201
|
+
pt.y = clientY;
|
|
202
|
+
return pt.matrixTransform(inverse);
|
|
203
|
+
};
|
|
204
|
+
const topLeft = toLocal(rect.left, rect.top);
|
|
205
|
+
const bottomRight = toLocal(rect.right, rect.bottom);
|
|
206
|
+
return {
|
|
207
|
+
x: topLeft.x,
|
|
208
|
+
y: topLeft.y,
|
|
209
|
+
width: bottomRight.x - topLeft.x,
|
|
210
|
+
height: bottomRight.y - topLeft.y
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const viewRect = this.element.getBoundingClientRect();
|
|
214
|
+
const scaleX = viewRect.width ? this.rawWidth / viewRect.width : 1;
|
|
215
|
+
const scaleY = viewRect.height ? this.rawHeight / viewRect.height : 1;
|
|
216
|
+
return {
|
|
217
|
+
x: (rect.left - viewRect.left) * scaleX,
|
|
218
|
+
y: (rect.top - viewRect.top) * scaleY,
|
|
219
|
+
width: rect.width * scaleX,
|
|
220
|
+
height: rect.height * scaleY
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 按钮附近:按钮与弹出画面之间留缝隙,两侧都放得下时默认放右侧,放不下时放在放得下的一侧;
|
|
225
|
+
* 竖直方向默认与按钮顶部对齐,下方放不下时改为与按钮底部对齐;
|
|
226
|
+
* 左右两侧都放不下(弹窗比画面还宽)时直接退化为画面居中,不再跟随按钮
|
|
227
|
+
*/
|
|
228
|
+
getNearButtonPosition(popView, triggerRect, hasShadow) {
|
|
229
|
+
const gap = 20;
|
|
230
|
+
const { x: buttonX, y: buttonY, width: buttonWidth, height: buttonHeight } = this.toLocalRect(triggerRect);
|
|
231
|
+
const rightX = buttonX + buttonWidth + gap;
|
|
232
|
+
const leftX = buttonX - gap - popView.rawWidth;
|
|
233
|
+
const fitsRight = rightX + popView.rawWidth <= this.rawWidth;
|
|
234
|
+
const fitsLeft = leftX >= 0;
|
|
235
|
+
let x;
|
|
236
|
+
let y;
|
|
237
|
+
if (!fitsRight && !fitsLeft) {
|
|
238
|
+
x = (this.rawWidth - popView.rawWidth) / 2;
|
|
239
|
+
y = (this.rawHeight - popView.rawHeight) / 2;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
x = fitsRight ? rightX : leftX;
|
|
243
|
+
const fitsBelow = buttonY + popView.rawHeight <= this.rawHeight;
|
|
244
|
+
y = fitsBelow ? buttonY : buttonY + buttonHeight - popView.rawHeight;
|
|
245
|
+
if (hasShadow) {
|
|
246
|
+
// 阴影会把可见内容整体右下偏移 SHADOW_INSET,这里反向补偏移让可见内容而不是容器本身对齐按钮
|
|
247
|
+
x -= GuiView.SHADOW_INSET;
|
|
248
|
+
y -= GuiView.SHADOW_INSET;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
x = Math.max(0, Math.min(x, this.rawWidth - popView.rawWidth));
|
|
252
|
+
y = Math.max(0, Math.min(y, this.rawHeight - popView.rawHeight));
|
|
253
|
+
return { x, y };
|
|
254
|
+
}
|
|
132
255
|
closeView() {
|
|
133
256
|
if (!this.parentView) {
|
|
134
257
|
return;
|
|
@@ -166,10 +289,11 @@ export class GuiView {
|
|
|
166
289
|
if (!container) {
|
|
167
290
|
return;
|
|
168
291
|
}
|
|
292
|
+
const inset = GuiView.SHADOW_INSET;
|
|
169
293
|
container.setAttribute('filter', `url(#${filterId})`);
|
|
170
|
-
container.setAttribute('x',
|
|
171
|
-
container.setAttribute('y',
|
|
172
|
-
$(this.element).children('g').attr('transform',
|
|
294
|
+
container.setAttribute('x', String(inset));
|
|
295
|
+
container.setAttribute('y', String(inset));
|
|
296
|
+
$(this.element).children('g').attr('transform', `matrix(1, 0, 0, 1, ${inset}, ${inset})`);
|
|
173
297
|
}
|
|
174
298
|
hideMusk() {
|
|
175
299
|
if (this.muskElement) {
|
|
@@ -181,8 +305,14 @@ export class GuiView {
|
|
|
181
305
|
if (this.openVariableStatesSubscription) {
|
|
182
306
|
this.openVariableStatesSubscription.unsubscribe();
|
|
183
307
|
}
|
|
184
|
-
if (this.
|
|
185
|
-
this.
|
|
308
|
+
if (this.requestVirtualDeviceState) {
|
|
309
|
+
this.requestVirtualDeviceState.unsubscribe();
|
|
310
|
+
}
|
|
311
|
+
if (this.virtualDeviceStatesChanged) {
|
|
312
|
+
this.virtualDeviceStatesChanged.unsubscribe();
|
|
313
|
+
}
|
|
314
|
+
if (this.openVariablesSubscriptions) {
|
|
315
|
+
this.openVariablesSubscriptions.forEach(s => s.unsubscribe());
|
|
186
316
|
}
|
|
187
317
|
if (this.perViewVariableCommunicator) {
|
|
188
318
|
this.perViewVariableCommunicator.dispose();
|
|
@@ -196,9 +326,14 @@ export class GuiView {
|
|
|
196
326
|
this.logger.debug('[GUI] View disposed.');
|
|
197
327
|
}
|
|
198
328
|
normalOpenedVariableNames(normalVariableNames) {
|
|
199
|
-
|
|
200
|
-
|
|
329
|
+
const newVariables = normalVariableNames.filter(v => !this.openedVariableNamesForValues.has(v));
|
|
330
|
+
if (newVariables.length > 0) {
|
|
331
|
+
newVariables.forEach(v => this.openedVariableNamesForValues.add(v));
|
|
332
|
+
const sub = this.perViewVariableCommunicator.openVariables(newVariables, this.signalRAppId)
|
|
201
333
|
.subscribe(variableValues => this.mainElement.reportVariableValues(variableValues));
|
|
334
|
+
this.openVariablesSubscriptions.push(sub);
|
|
202
335
|
}
|
|
203
336
|
}
|
|
204
337
|
}
|
|
338
|
+
/** showShadow() 给阴影留白,内容整体右下偏移的量,"按钮附近"定位需要据此修正才能让可见内容与按钮对齐 */
|
|
339
|
+
GuiView.SHADOW_INSET = 5;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"__symbolic":"module","version":4,"metadata":{"GuiView":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":
|
|
1
|
+
[{"__symbolic":"module","version":4,"metadata":{"GuiView":{"__symbolic":"class","members":{"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/core","name":"Injector","line":54,"character":43},{"__symbolic":"reference","module":"ngx-bootstrap/modal","name":"BsModalService","line":55,"character":41},{"__symbolic":"reference","module":"./gui-context","name":"GuiContext","line":56,"character":34},{"__symbolic":"reference","name":"GuiView"}]}],"resize":[{"__symbolic":"method"}],"load":[{"__symbolic":"method"}],"loadElementState":[{"__symbolic":"method"}],"popView":[{"__symbolic":"method"}],"toLocalRect":[{"__symbolic":"method"}],"getNearButtonPosition":[{"__symbolic":"method"}],"closeView":[{"__symbolic":"method"}],"moveView":[{"__symbolic":"method"}],"showMusk":[{"__symbolic":"method"}],"showShadow":[{"__symbolic":"method"}],"hideMusk":[{"__symbolic":"method"}],"dispose":[{"__symbolic":"method"}],"normalOpenedVariableNames":[{"__symbolic":"method"}]},"statics":{"SHADOW_INSET":5}}}}]
|
package/gui/gui.component.d.ts
CHANGED
|
@@ -24,10 +24,12 @@ export declare class GuiComponent implements OnChanges, OnDestroy {
|
|
|
24
24
|
initHeight: number;
|
|
25
25
|
lastScale: number;
|
|
26
26
|
isMobileMode: boolean;
|
|
27
|
+
hostContainerId: string;
|
|
27
28
|
private $svg;
|
|
28
29
|
private host;
|
|
29
30
|
private get viewResizeMode();
|
|
30
31
|
private readonly globalSettings;
|
|
32
|
+
private readonly viewService;
|
|
31
33
|
constructor(el: ElementRef, localization: Localization, injector: Injector, bsModalService: BsModalService);
|
|
32
34
|
ngOnChanges(changes: {
|
|
33
35
|
[propKey: string]: SimpleChange;
|
|
@@ -39,4 +41,5 @@ export declare class GuiComponent implements OnChanges, OnDestroy {
|
|
|
39
41
|
private onSizeChanged;
|
|
40
42
|
private onResizeModeChanged;
|
|
41
43
|
private onDisplayModeChanged;
|
|
44
|
+
refreshCurrentPage(): boolean;
|
|
42
45
|
}
|