diff --git a/application/src/main/data/json/system/widget_types/state_chart.json b/application/src/main/data/json/system/widget_types/state_chart.json index 25a3130d48..a78fc06f64 100644 --- a/application/src/main/data/json/system/widget_types/state_chart.json +++ b/application/src/main/data/json/system/widget_types/state_chart.json @@ -19,7 +19,7 @@ "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", "hasBasicMode": true, "basicModeDirective": "tb-flot-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"waterfall_chart\",\"iconColor\":\"#1F6BDD\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"waterfall_chart\",\"iconColor\":\"#1F6BDD\"}" }, "externalId": null, "tags": null diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 9dcda4ef8a..6b53e4d0cb 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -236,6 +236,15 @@ export class WidgetService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } + public addWidgetFqnToWidgetBundle(widgetsBundleId: string, fqn: string, config?: RequestConfig) { + return this.getBundleWidgetTypeFqns(widgetsBundleId, config).pipe( + mergeMap(widgetsBundleFqn => { + widgetsBundleFqn.push(fqn); + return this.updateWidgetsBundleWidgetFqns(widgetsBundleId, widgetsBundleFqn, config); + }) + ); + } + public getWidgetTemplate(widgetTypeParam: widgetType, config?: RequestConfig): Observable { const templateWidgetType = widgetTypesData.get(widgetTypeParam); diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index 1678eac615..0a072b5377 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -110,6 +110,7 @@ {{ 'widgets-bundle.current' | translate }} { + public importWidgetType(): Observable { return this.openImportDialog('widget.import', 'widget-type.widget-file').pipe( mergeMap((widgetTypeDetails: WidgetTypeDetails) => { if (!this.validateImportedWidgetTypeDetails(widgetTypeDetails)) { @@ -309,9 +309,7 @@ export class ImportExportService { return this.widgetService.saveImportedWidgetTypeDetails(widgetTypeDetails); } }), - catchError((err) => { - return of(null); - }) + catchError(() => of(null)) ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index 86046df6d7..874d6ca1b4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -116,7 +116,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit ngOnInit(): void { this.ctx.$scope.aggregatedValueCardWidget = this; this.settings = {...aggregatedValueCardDefaultSettings, ...this.ctx.settings}; - this.showSubtitle = this.settings.showSubtitle; + this.showSubtitle = this.settings.showSubtitle && this.ctx.datasources?.length > 0; const subtitle = this.settings.subtitle; this.subtitle$ = this.ctx.registerLabelPattern(subtitle, this.subtitle$); this.subtitleStyle = textStyle(this.settings.subtitleFont); @@ -156,7 +156,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit } ngAfterViewInit(): void { - if (this.showChart) { + if (this.showChart && this.ctx.datasources?.length) { const settings = { shadowSize: 0, enableSelection: false, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts index 9272295a27..1494e987ce 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts @@ -60,7 +60,9 @@ export class FlotWidgetComponent implements OnInit { this.settings = this.ctx.settings; this.chartType = this.chartType || 'line'; this.configureLegend(); - this.flot = new TbFlot(this.ctx, this.chartType, $(this.flotElement.nativeElement)); + if (this.ctx.datasources?.length) { + this.flot = new TbFlot(this.ctx, this.chartType, $(this.flotElement.nativeElement)); + } } private configureLegend(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html index ee332924f0..279b48fb3a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html @@ -21,7 +21,7 @@
date.format
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss index ec6a762966..7ca3237b5d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss @@ -48,10 +48,12 @@ } .mat-mdc-form-field.tb-date-format-input { .mat-mdc-text-field-wrapper.mdc-text-field--outlined { - .mat-mdc-form-field-icon-suffix { - display: flex; - align-items: center; - line-height: normal; + .mat-mdc-form-field-flex { + .mat-mdc-form-field-icon-suffix { + display: flex; + align-items: center; + line-height: normal; + } } } } diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index fbfa520fb3..f49a5454f1 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -479,7 +479,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { backgroundColor: this.backgroundColor, padding: this.padding, margin: this.margin, - borderRadius: this.borderRadius}; + borderRadius: this.borderRadius || 'unset' }; if (this.widget.config.widgetStyle) { this.style = {...this.style, ...this.widget.config.widgetStyle}; } diff --git a/ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html b/ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html index 7588d1d8f5..f7937193d4 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+

widget.save-widget-as

@@ -29,16 +29,18 @@
-
- widget.save-widget-as-text - - widget.title - - - {{ 'widget.title-required' | translate }} - - -
+ widget.save-widget-as-text + + widget.title + + + {{ 'widget.title-required' | translate }} + + + +
{{ widgetsBundle.title }}: {{ 'widget.widgets' | translate }} + + + + +
add
-
+
add -
widget.add
+
widget.add-existing-widget
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts index 77d7cb6c2e..cc54c53901 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts @@ -24,13 +24,17 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; -import { WidgetTypeInfo } from '@shared/models/widget.models'; +import { widgetType as WidgetDataType, WidgetTypeInfo } from '@shared/models/widget.models'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { WidgetService } from '@core/http/widget.service'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { isDefinedAndNotNull } from '@core/utils'; import { FormControl, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; + +type WidgetTypeBundle = WithOptional; @Component({ selector: 'tb-widgets-bundle-widget', @@ -47,7 +51,7 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn isDirty = false; widgetsBundle: WidgetsBundle; - widgets: Array; + widgets: Array; excludeWidgetTypeIds: Array; addWidgetFormControl = new FormControl(null, [Validators.required]); @@ -58,7 +62,8 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn private widgetsService: WidgetService, private importExport: ImportExportService, private sanitizer: DomSanitizer, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private dialog: MatDialog) { super(store); this.authUser = getCurrentAuthUser(this.store); this.widgetsBundle = this.route.snapshot.data.widgetsBundle; @@ -88,7 +93,7 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn return '/assets/widget-preview-empty.svg'; } - trackByWidget(index: number, widget: WidgetTypeInfo): any { + trackByWidget(index: number, widget: WidgetTypeBundle): any { return widget; } @@ -109,27 +114,27 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn this.addMode = false; } - private addWidget(newWidget: WidgetTypeInfo) { + private addWidget(newWidget: WidgetTypeBundle) { this.widgets.push(newWidget); this.isDirty = true; this.addMode = false; } - openWidgetEditor($event: Event, widgetType: WidgetTypeInfo) { + openWidgetEditor($event: Event, widgetType: WidgetTypeBundle) { if ($event) { $event.stopPropagation(); } this.router.navigate([widgetType.id.id], {relativeTo: this.route}).then(()=> {}); } - exportWidgetType($event: Event, widgetType: WidgetTypeInfo) { + exportWidgetType($event: Event, widgetType: WidgetTypeBundle) { if ($event) { $event.stopPropagation(); } this.importExport.exportWidgetType(widgetType.id.id); } - removeWidgetType($event: Event, widgetType: WidgetTypeInfo) { + removeWidgetType($event: Event, widgetType: WidgetTypeBundle) { if ($event) { $event.stopPropagation(); } @@ -176,4 +181,44 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn }); } + addWidgetType($event: Event): void { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(SelectWidgetTypeDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe( + (type) => { + if (type) { + this.router.navigate([type], {relativeTo: this.route}).then(() => {}); + } + } + ); + } + + importWidgetType() { + this.importExport.importWidgetType().subscribe( + (widgetType) => { + if (widgetType) { + const isExistWidget = this.widgets.some(widget => widget.id.id === widgetType.id.id); + if (isExistWidget) { + this.widgets = this.widgets.map(widget => widget.id.id !== widgetType.id.id ? widget : widgetType); + } + if (this.editMode) { + this.widgets.push(widgetType); + this.isDirty = true; + this.cd.markForCheck(); + } else if (!isExistWidget) { + this.widgetsService.addWidgetFqnToWidgetBundle(this.widgetsBundle.id.id, widgetType.fqn).subscribe(() => { + this.widgets.push(widgetType); + this.cd.markForCheck(); + }); + } + } + } + ); + } + } diff --git a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html index bffcbc9c37..7642debf37 100644 --- a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html +++ b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html @@ -19,9 +19,9 @@
@@ -29,6 +29,8 @@ widgets-bundle.system
+ +
{{widgetsBundle.title}} diff --git a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts index 28d15b45a2..271db6d26a 100644 --- a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts +++ b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts @@ -20,12 +20,12 @@ import { Observable } from 'rxjs'; import { map, share, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { WidgetService } from '@core/http/widget.service'; import { isDefined } from '@core/utils'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-widgets-bundle-select', @@ -44,19 +44,15 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni bundlesScope: 'system' | 'tenant'; @Input() + @coerceBoolean() selectFirstBundle: boolean; @Input() selectBundleAlias: string; - private requiredValue: boolean; - get required(): boolean { - return this.requiredValue; - } @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } + @coerceBoolean() + required: boolean; @Input() disabled: boolean; @@ -70,7 +66,8 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni widgetsBundle: WidgetsBundle | null; - private propagateChange = (v: any) => { }; + onTouched = () => {}; + private propagateChange: (value: any) => void = () => {}; constructor(private store: Store, private widgetService: WidgetService) { @@ -81,6 +78,7 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni } registerOnTouched(fn: any): void { + this.onTouched = fn; } ngOnInit() { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f5ea9e79ec..f5c09ce75f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3748,7 +3748,7 @@ "ui-resources-load-error": "Failed to load configuration ui resources.", "invalid-target-rulechain": "Unable to resolve target rule chain!", "test-script-function": "Test script function", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "Message", "message-type": "Message type", @@ -4388,6 +4388,7 @@ "volt": "Volt", "kilovolts": "Kilovolts", "dbmV": "dBmV", + "dbm": "dBm", "volt-meter": "Volt-Meter", "kilovolt-meter": "Kilovolt-Meter", "megavolt-meter": "Megavolt-Meter", @@ -4759,7 +4760,7 @@ "no-widgets-text": "No widgets found", "management": "Widget management", "editor": "Widget Editor", - "confirm-to-exit-editor-html": "You have unsaved default widget settings.
Are you sure you want to leave this page?", + "confirm-to-exit-editor-html": "You have unsaved widget settings.
Are you sure you want to leave this page?", "widget-type-not-found": "Problem loading widget configuration.
Probably associated\n widget type was removed.", "widget-type-load-error": "Widget wasn't loaded due to the following errors:", "remove": "Remove widget", @@ -4827,6 +4828,8 @@ "details": "Details", "widget-details": "Widget details", "add": "Add Widget", + "add-existing-widget": "Add existing widget", + "add-new-widget": "Add new widget", "search-widgets": "Search widgets", "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } selected", "undo": "Undo widget changes", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index f6c4b24e8a..b9a386071a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3735,7 +3735,7 @@ "ui-resources-load-error": "Error al cargar los recursos de configuración UI.", "invalid-target-rulechain": "No se puede resolver la cadena de reglas objetivo!", "test-script-function": "Probar Script de función", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "Mensaje", "message-type": "Tipo de mensaje", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 5df0238594..2c241a1c44 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -3366,7 +3366,7 @@ "ui-resources-load-error": "加载配置UI资源失败。", "invalid-target-rulechain": "无法解析目标规则链!", "test-script-function": "测试脚本功能", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "消息", "message-type": "消息类型", diff --git a/ui-ngx/src/assets/metadata/units.json b/ui-ngx/src/assets/metadata/units.json index 988c3f34ce..5d680c6e2c 100644 --- a/ui-ngx/src/assets/metadata/units.json +++ b/ui-ngx/src/assets/metadata/units.json @@ -938,6 +938,11 @@ "symbol": "dBmV", "tags": ["decibels millivolt","voltage level","signal","dBmV"] }, +{ + "name": "unit.dbm", + "symbol": "dBm", + "tags": ["decibel milliwatts","output power","signal","dBm"] +}, { "name": "unit.volt-meter", "symbol": "V·m", diff --git a/ui-ngx/src/typings/utils.d.ts b/ui-ngx/src/typings/utils.d.ts index 7557501bea..a9e250bad0 100644 --- a/ui-ngx/src/typings/utils.d.ts +++ b/ui-ngx/src/typings/utils.d.ts @@ -19,3 +19,9 @@ type NestedKeyOf = ? `${Key}` | `${Key}.${NestedKeyOf extends infer U extends string ? U : never}` : `${Key}` }[keyof ObjectType & (string | number)]; + +type AllKeyOf = T extends never ? never : keyof T; + +type Optional = { [P in Extract]?: T[P] }; + +type WithOptional> = T extends never ? never : Omit & Optional;