立即试用 商务报价
专业版
文档 > 仪表板 > 部件 > 部件操作

本页目录

部件操作

介绍

操作功能可以实现仪表板状态之间或不同仪表板之间的导航切换允许快速地配置到状态转换、移动到其他仪表板或更新当前仪表板,部件不同操作源有所不同,但是可以选择的操作类型对于所有部件都是相同的。

所有操作在部件的编辑模式下操作。

了解如何使用操作必须向部件添加一个状态具体操作请参见此处文档。

操作类型

有六种操作类型对所有部件都是相同的如何使用实体卡片部件示例中的配置操作类型。

类型配置

所有部件的操作类型都相同因此在本教程中将使用顶部按钮click操作源解释所有操作类型并将使用实体卡片部件示例进行演示。

导航到新的仪表板状态

查看不同设备、资产的详细信息使用导航到新的仪表板状态进行切换必须选择创建好的状态至于如何添加到仪表板请阅读此处

添加状态后可以配置导航到新的仪表板状态操作类型:

  1. 进入编辑模式选择“操作”
  2. 单击“+”图标添加新操作
  3. 选择“Widget header button”操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择“类型”中的导航到新的仪表板状态操作类型
  6. 选择“目标仪表板状态”
  7. 选择状态并单击“添加操作”
  8. 完成操作配置检查操作源、图标和操作类型
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

保存更改后可以在部件的右上角看到一个图标按钮单击此图标将带进入先前选择的状态。

如果要在单独的窗口中打开所选状态请选中“在单独的对话框中打开”。

可以在对话框窗口中隐藏仪表板工具栏请选择“在对话框中隐藏仪表板工具栏”。

更新当前仪表板状态

查看指定设备/资产的详细信息使用更新当前仪表板状态此操作类型最常见的用途是通过图表部件查看详细信息,对于初学者添加图表部件到仪表板配置更新当前仪表板状态操作类型:

  1. 进入编辑模式“选择部件”菜单中选择图表:时间序列折线图
  2. 输入数据源
  3. 完成图表部件添加但是没有数据显示
  4. 进入部件编辑模式
  5. 添加部件操作
  6. 单击“+”图标添加操作
  7. 选择“Widget header button”操作源
  8. 输入操作名称并选择按钮的图标
  9. 选择更新当前仪表板状态操作类型
  10. 选择操作类型单击“添加操作”窗口的“添加”按钮
  11. 完成操作配置检查操作源、图标和操作类型
  12. 单击窗口右上角应用更改然后关闭窗口
  13. 单击屏幕右下角“应用更改”按钮

保存更改后将出现按钮图标单击图标将更新实体详细信息并将显示在当前仪表板上的图表部件中。

导航到其他仪表板

如果需要快速导航到另一个指定的仪表板通过这种类型的操作实现。

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择“Widget header button”操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择导航到其他仪表板操作类型
  6. 选择“目标仪表板”
  7. 选择操作类型单击“添加操作”窗口的“添加”按钮
  8. 完成操作配置检查操作源、图标和操作类型
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

保存更改后将出现按钮图标单击图标将导航到指定的仪表板(或选中的仪表板中状态)。

自定义操作

自义定操作用于向部件配置单个操作功能(例如删除设备/资产)。

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择“Widget header button”操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择导航到自定义操作类型
  6. 选择自定义操作类型
  7. 输入自定义函数代码
  8. 单击“添加操作”窗口右下角的“保存”按钮应用更改
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

保存更改后将出现按钮图标单击“垃圾箱”图标执行操作。

设备删除功能的示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let $injector = widgetContext.$scope.$injector;
let dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));

openDeleteDeviceDialog();

function openDeleteDeviceDialog() {
    let title = "Are you sure you want to delete the device " + entityName + "?";
    let content = "Be careful, after the confirmation, the device and all related data will become unrecoverable!";
    dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(
        function(result) {
            if (result) {
                deleteDevice();
            }
        }
    );
}

function deleteDevice() {
    deviceService.deleteDevice(entityId.id).subscribe(
        function() {
            widgetContext.updateAliases();
        }
    );
}
自定义操作(HTML模板)

HTML模板的自定义操作允许在现有HTML模板中手动输入代码并实现功能(例如:通过对话窗口创建或编辑设备/资产)。

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择“Widget header button”操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择自定义操作类型

    资源 用于部件自定义操作使用外部JavaScript/CSS;

    样式 用于部件自定义操作CSS;

    HTML 用于部件自定义操作HTML代码;

    JavaScript 用于部件自定义操作JavaScript代码。

  6. 输入操作JavaScript代码
  7. 输入操作HTML代码
  8. 单击“添加操作”窗口右下角的“保存”按钮应用更改
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

通过单击图标执行操作。

添加设备或资产的JavaScript函数和HTML代码示例:

JavaScript代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
let $injector = widgetContext.$scope.$injector;
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
let entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));

openAddEntityDialog();

function openAddEntityDialog() {
customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();
}

function AddEntityDialogController(instance) {
let vm = instance;

    vm.allowedEntityTypes = ['ASSET', 'DEVICE'];
    vm.entitySearchDirection = {
        from: "FROM",
        to: "TO"
    }

    vm.addEntityFormGroup = vm.fb.group({
     entityName: ['', [vm.validators.required]],
     entityType: ['DEVICE'],
     entityLabel: [null],
     type: ['', [vm.validators.required]],
     attributes: vm.fb.group({
         latitude: [null],
         longitude: [null],
         address: [null],
         owner: [null],
         number: [null, [vm.validators.pattern(/^-?[0-9]+$/)]],
         booleanValue: [null]
     }),
     relations: vm.fb.array([])
    });

    vm.cancel = function() {
        vm.dialogRef.close(null);
    };

    vm.relations = function() {
        return vm.addEntityFormGroup.get('relations');
    };

    vm.addRelation = function() {
        vm.relations().push(vm.fb.group({
         relatedEntity: [null, [vm.validators.required]],
         relationType: [null, [vm.validators.required]],
         direction: [null, [vm.validators.required]]
        }));
    };

    vm.removeRelation = function(index) {
        vm.relations().removeAt(index);
        vm.relations().markAsDirty();
    };

    vm.save = function() {
        vm.addEntityFormGroup.markAsPristine();
        saveEntityObservable().subscribe(
            function (entity) {
                widgetContext.rxjs.forkJoin([
                    saveAttributes(entity.id),
                    saveRelations(entity.id)
                ]).subscribe(
                    function () {
                        widgetContext.updateAliases();
                        vm.dialogRef.close(null);
                    }
                );
            }
        );
    };

    function saveEntityObservable() {
        const formValues = vm.addEntityFormGroup.value;
        let entity = {
            name: formValues.entityName,
            type: formValues.type,
            label: formValues.entityLabel
        };
        if (formValues.entityType == 'ASSET') {
            return assetService.saveAsset(entity);
        } else if (formValues.entityType == 'DEVICE') {
            return deviceService.saveDevice(entity);
        }
    }

    function saveAttributes(entityId) {
        let attributes = vm.addEntityFormGroup.get('attributes').value;
        let attributesArray = [];
        for (let key in attributes) {
            if(attributes[key] !== null) {
                attributesArray.push({key: key, value: attributes[key]});
            }
        }
        if (attributesArray.length > 0) {
            return attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributesArray);
        }
        return widgetContext.rxjs.of([]);
    }

    function saveRelations(entityId) {
        let relations = vm.addEntityFormGroup.get('relations').value;
        let tasks = [];
        for(let i=0; i < relations.length; i++) {
            let relation = {
                type: relations[i].relationType,
                typeGroup: 'COMMON'
            };
            if (relations[i].direction == 'FROM') {
                relation.to = relations[i].relatedEntity;
                relation.from = entityId;
            } else {
                relation.to = entityId;
                relation.from = relations[i].relatedEntity;
            }
            tasks.push(entityRelationService.saveRelation(relation));
        }
        if (tasks.length > 0) {
            return widgetContext.rxjs.forkJoin(tasks);
        }
        return widgetContext.rxjs.of([]);
    }
}


HTML代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup"

      (ngSubmit)="save()" class="add-entity-form">
    <mat-toolbar fxLayout="row" color="primary">
        <h2>Add entity</h2>
        <span fxFlex></span>
        <button mat-icon-button (click)="cancel()" type="button">
            <mat-icon class="material-icons">close</mat-icon>
        </button>
    </mat-toolbar>
    <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
    </mat-progress-bar>
    <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
    <div mat-dialog-content fxLayout="column">
        <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Entity Name</mat-label>
                <input matInput formControlName="entityName" required>
                <mat-error *ngIf="addEntityFormGroup.get('entityName').hasError('required')">
                    Entity name is required.
                </mat-error>
            </mat-form-field>
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Entity Label</mat-label>
                <input matInput formControlName="entityLabel" >
            </mat-form-field>
        </div>
        <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
            <tb-entity-type-select
                    class="mat-block"
                    formControlName="entityType"
                    [showLabel]="true"
                    [allowedEntityTypes]="allowedEntityTypes"
            ></tb-entity-type-select>
            <tb-entity-subtype-autocomplete
                    fxFlex *ngIf="addEntityFormGroup.get('entityType').value == 'ASSET'"
                    class="mat-block"
                    formControlName="type"
                    [required]="true"
                    [entityType]="'ASSET'"
            ></tb-entity-subtype-autocomplete>
            <tb-entity-subtype-autocomplete
                    fxFlex *ngIf="addEntityFormGroup.get('entityType').value != 'ASSET'"
                    class="mat-block"
                    formControlName="type"
                    [required]="true"
                    [entityType]="'DEVICE'"
            ></tb-entity-subtype-autocomplete>
        </div>
        <div formGroupName="attributes" fxLayout="column">
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Latitude</mat-label>
                    <input type="number" step="any" matInput formControlName="latitude">
                </mat-form-field>
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Longitude</mat-label>
                    <input type="number" step="any" matInput formControlName="longitude">
                </mat-form-field>
            </div>
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Address</mat-label>
                    <input matInput formControlName="address">
                </mat-form-field>
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Owner</mat-label>
                    <input matInput formControlName="owner">
                </mat-form-field>
            </div>
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Integer Value</mat-label>
                    <input type="number" step="1" matInput formControlName="number">
                    <mat-error *ngIf="addEntityFormGroup.get('attributes.number').hasError('pattern')">
                        Invalid integer value.
                    </mat-error>
                </mat-form-field>
                <div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
                    <label class="checkbox-label">Boolean Value</label>
                    <mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
                        
                    </mat-checkbox>
                </div>
            </div>
        </div>
        <div class="relations-list">
            <div class="mat-body-1" style="padding-bottom: 10px; color: rgba(0,0,0,0.57);">Relations</div>
            <div class="body" [fxShow]="relations().length">
                <div class="row" fxLayout="row" fxLayoutAlign="start center" formArrayName="relations" *ngFor="let relation of relations().controls; let i = index;">
                    <div [formGroupName]="i" class="mat-elevation-z2" fxFlex fxLayout="row" style="padding: 5px 0 5px 5px;">
                        <div fxFlex fxLayout="column">
                            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                                <mat-form-field class="mat-block" style="min-width: 100px;">
                                    <mat-label>Direction</mat-label>
                                    <mat-select formControlName="direction" name="direction">
                                        <mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
                                            
                                        </mat-option>
                                    </mat-select>
                                    <mat-error *ngIf="relation.get('direction').hasError('required')">
                                        Relation direction is required.
                                    </mat-error>
                                </mat-form-field>
                                <tb-relation-type-autocomplete
                                        fxFlex class="mat-block"
                                        formControlName="relationType"
                                        [required]="true">
                                </tb-relation-type-autocomplete>
                            </div>
                            <div fxLayout="row" fxLayout.xs="column">
                                <tb-entity-select
                                        fxFlex class="mat-block"
                                        [required]="true"
                                        formControlName="relatedEntity">
                                </tb-entity-select>
                            </div>
                        </div>
                        <div fxLayout="column" fxLayoutAlign="center center">
                            <button mat-icon-button color="primary"
                                    aria-label="Remove"
                                    type="button"
                                    (click)="removeRelation(i)"
                                    matTooltip="Remove relation"
                                    matTooltipPosition="above">
                                <mat-icon>close</mat-icon>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <div>
                <button mat-raised-button color="primary"
                        type="button"
                        (click)="addRelation()"
                        matTooltip="Add Relation"
                        matTooltipPosition="above">
                    Add
                </button>
            </div>
        </div>
    </div>
    <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
        <button mat-button color="primary"
                type="button"
                [disabled]="(isLoading$ | async)"
                (click)="cancel()" cdkFocusInitial>
            Cancel
        </button>
        <button mat-button mat-raised-button color="primary"
                type="submit"
                [disabled]="(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty">
            Create
        </button>
    </div>
</form>


编辑设备或资产的JavaScript函数和HTML代码示例:

JavaScript代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
let $injector = widgetContext.$scope.$injector;
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
let entityService = $injector.get(widgetContext.servicesMap.get('entityService'));
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
let deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));
let entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));

openEditEntityDialog();

function openEditEntityDialog() {
customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();
}

function EditEntityDialogController(instance) {
let vm = instance;

    vm.entityName = entityName;
    vm.entityType = entityId.entityType;
    vm.entitySearchDirection = {
        from: "FROM",
        to: "TO"
    };
    vm.attributes = {};
    vm.oldRelationsData = [];
    vm.relationsToDelete = [];
    vm.entity = {};

    vm.editEntityFormGroup = vm.fb.group({
        entityName: ['', [vm.validators.required]],
        entityType: [null],
        entityLabel: [null],
        type: ['', [vm.validators.required]],
        attributes: vm.fb.group({
            latitude: [null],
            longitude: [null],
            address: [null],
            owner: [null],
            number: [null, [vm.validators.pattern(/^-?[0-9]+$/)]],
            booleanValue: [false]
        }),
        oldRelations: vm.fb.array([]),
        relations: vm.fb.array([])
    });

    getEntityInfo();

    vm.cancel = function() {
        vm.dialogRef.close(null);
    };

    vm.relations = function() {
        return vm.editEntityFormGroup.get('relations');
    };

    vm.oldRelations = function() {
        return vm.editEntityFormGroup.get('oldRelations');
    };

    vm.addRelation = function() {
        vm.relations().push(vm.fb.group({
            relatedEntity: [null, [vm.validators.required]],
            relationType: [null, [vm.validators.required]],
            direction: [null, [vm.validators.required]]
        }));
    };

    function addOldRelation() {
        vm.oldRelations().push(vm.fb.group({
            relatedEntity: [{value: null, disabled: true}, [vm.validators.required]],
            relationType: [{value: null, disabled: true}, [vm.validators.required]],
            direction: [{value: null, disabled: true}, [vm.validators.required]]
        }));
    }

    vm.removeRelation = function(index) {
        vm.relations().removeAt(index);
        vm.relations().markAsDirty();
    };

    vm.removeOldRelation = function(index) {
        vm.oldRelations().removeAt(index);
        vm.relationsToDelete.push(vm.oldRelationsData[index]);
        vm.oldRelations().markAsDirty();
    };

    vm.save = function() {
        vm.editEntityFormGroup.markAsPristine();
        widgetContext.rxjs.forkJoin([
            saveAttributes(entityId),
            saveRelations(entityId),
            saveEntity()
        ]).subscribe(
            function () {
                widgetContext.updateAliases();
                vm.dialogRef.close(null);
            }
        );
    };

    function getEntityAttributes(attributes) {
        for (var i = 0; i < attributes.length; i++) {
            vm.attributes[attributes[i].key] = attributes[i].value;
        }
    }

    function getEntityRelations(relations) {
        let relationsFrom = relations[0];
        let relationsTo = relations[1];
        for (let i=0; i < relationsFrom.length; i++) {
            let relation = {
                direction: 'FROM',
                relationType: relationsFrom[i].type,
                relatedEntity: relationsFrom[i].to
            };
            vm.oldRelationsData.push(relation);
            addOldRelation();
        }
        for (let i=0; i < relationsTo.length; i++) {
            let relation = {
                direction: 'TO',
                relationType: relationsTo[i].type,
                relatedEntity: relationsTo[i].from
            };
            vm.oldRelationsData.push(relation);
            addOldRelation();
        }
    }

    function getEntityInfo() {
        widgetContext.rxjs.forkJoin([
            entityRelationService.findInfoByFrom(entityId),
            entityRelationService.findInfoByTo(entityId),
            attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE'),
            entityService.getEntity(entityId.entityType, entityId.id)
        ]).subscribe(
            function (data) {
                getEntityRelations(data.slice(0,2));
                getEntityAttributes(data[2]);
                vm.entity = data[3];
                vm.editEntityFormGroup.patchValue({
                    entityName: vm.entity.name,
                    entityType: vm.entityType,
                    entityLabel: vm.entity.label,
                    type: vm.entity.type,
                    attributes: vm.attributes,
                    oldRelations: vm.oldRelationsData
                }, {emitEvent: false});
            }
        );
    }

    function saveEntity() {
        const formValues = vm.editEntityFormGroup.value;
        if (vm.entity.label !== formValues.entityLabel){
            vm.entity.label = formValues.entityLabel;
            if (formValues.entityType == 'ASSET') {
                return assetService.saveAsset(vm.entity);
            } else if (formValues.entityType == 'DEVICE') {
                return deviceService.saveDevice(vm.entity);
            }
        }
        return widgetContext.rxjs.of([]);
    }

    function saveAttributes(entityId) {
        let attributes = vm.editEntityFormGroup.get('attributes').value;
        let attributesArray = [];
        for (let key in attributes) {
            if (attributes[key] !== vm.attributes[key]) {
                attributesArray.push({key: key, value: attributes[key]});
            }
        }
        if (attributesArray.length > 0) {
            return attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributesArray);
        }
        return widgetContext.rxjs.of([]);
    }

    function saveRelations(entityId) {
        let relations = vm.editEntityFormGroup.get('relations').value;
        let tasks = [];
        for(let i=0; i < relations.length; i++) {
            let relation = {
                type: relations[i].relationType,
                typeGroup: 'COMMON'
            };
            if (relations[i].direction == 'FROM') {
                relation.to = relations[i].relatedEntity;
                relation.from = entityId;
            } else {
                relation.to = entityId;
                relation.from = relations[i].relatedEntity;
            }
            tasks.push(entityRelationService.saveRelation(relation));
        }
        for (let i=0; i < vm.relationsToDelete.length; i++) {
            let relation = {
                type: vm.relationsToDelete[i].relationType
            };
            if (vm.relationsToDelete[i].direction == 'FROM') {
                relation.to = vm.relationsToDelete[i].relatedEntity;
                relation.from = entityId;
            } else {
                relation.to = entityId;
                relation.from = vm.relationsToDelete[i].relatedEntity;
            }
            tasks.push(entityRelationService.deleteRelation(relation.from, relation.type, relation.to));
        }
        if (tasks.length > 0) {
            return widgetContext.rxjs.forkJoin(tasks);
        }
        return widgetContext.rxjs.of([]);
    }
}


HTML代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"

      (ngSubmit)="save()"  class="edit-entity-form">
    <mat-toolbar fxLayout="row" color="primary">
        <h2>Edit  </h2>
        <span fxFlex></span>
        <button mat-icon-button (click)="cancel()" type="button">
            <mat-icon class="material-icons">close</mat-icon>
        </button>
    </mat-toolbar>
    <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
    </mat-progress-bar>
    <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
    <div mat-dialog-content fxLayout="column">
        <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Entity Name</mat-label>
                <input matInput formControlName="entityName" required readonly="">
            </mat-form-field>
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Entity Label</mat-label>
                <input matInput formControlName="entityLabel">
            </mat-form-field>
        </div>
        <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Entity Type</mat-label>
                <input matInput formControlName="entityType" readonly>
            </mat-form-field>
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Type</mat-label>
                <input matInput formControlName="type" readonly>
            </mat-form-field>
        </div>
        <div formGroupName="attributes" fxLayout="column">
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Latitude</mat-label>
                    <input type="number" step="any" matInput formControlName="latitude">
                </mat-form-field>
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Longitude</mat-label>
                    <input type="number" step="any" matInput formControlName="longitude">
                </mat-form-field>
            </div>
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Address</mat-label>
                    <input matInput formControlName="address">
                </mat-form-field>
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Owner</mat-label>
                    <input matInput formControlName="owner">
                </mat-form-field>
            </div>
            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                <mat-form-field fxFlex class="mat-block">
                    <mat-label>Integer Value</mat-label>
                    <input type="number" step="1" matInput formControlName="number">
                    <mat-error *ngIf="editEntityFormGroup.get('attributes.number').hasError('pattern')">
                        Invalid integer value.
                    </mat-error>
                </mat-form-field>
                <div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
                    <label class="checkbox-label">Boolean Value</label>
                    <mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
                        
                    </mat-checkbox>
                </div>
            </div>
        </div>
        <div class="relations-list old-relations">
            <div class="mat-body-1" style="padding-bottom: 10px; color: rgba(0,0,0,0.57);">Relations</div>
            <div class="body" [fxShow]="oldRelations().length">
                <div class="row" fxLayout="row" fxLayoutAlign="start center" formArrayName="oldRelations" 
                     *ngFor="let relation of oldRelations().controls; let i = index;">
                    <div [formGroupName]="i" class="mat-elevation-z2" fxFlex fxLayout="row" style="padding: 5px 0 5px 5px;">
                        <div fxFlex fxLayout="column">
                            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                                <mat-form-field class="mat-block" style="min-width: 100px;">
                                    <mat-label>Direction</mat-label>
                                    <mat-select formControlName="direction" name="direction">
                                        <mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
                                            
                                        </mat-option>
                                    </mat-select>
                                    <mat-error *ngIf="relation.get('direction').hasError('required')">
                                        Relation direction is required.
                                    </mat-error>
                                </mat-form-field>
                                <tb-relation-type-autocomplete
                                        fxFlex class="mat-block"
                                        formControlName="relationType"
                                        required="true">
                                </tb-relation-type-autocomplete>
                            </div>
                            <div fxLayout="row" fxLayout.xs="column">
                                <tb-entity-select
                                        fxFlex class="mat-block"
                                        required="true"
                                        formControlName="relatedEntity">
                                </tb-entity-select>
                            </div>
                        </div>
                        <div fxLayout="column" fxLayoutAlign="center center">
                            <button mat-icon-button color="primary"
                                    aria-label="Remove"
                                    type="button"
                                    (click)="removeOldRelation(i)"
                                    matTooltip="Remove relation"
                                    matTooltipPosition="above">
                                <mat-icon>close</mat-icon>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="relations-list">
            <div class="mat-body-1" style="padding-bottom: 10px; color: rgba(0,0,0,0.57);">New Relations</div>
            <div class="body" [fxShow]="relations().length">
                <div class="row" fxLayout="row" fxLayoutAlign="start center" formArrayName="relations" *ngFor="let relation of relations().controls; let i = index;">
                    <div [formGroupName]="i" class="mat-elevation-z2" fxFlex fxLayout="row" style="padding: 5px 0 5px 5px;">
                        <div fxFlex fxLayout="column">
                            <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
                                <mat-form-field class="mat-block" style="min-width: 100px;">
                                    <mat-label>Direction</mat-label>
                                    <mat-select formControlName="direction" name="direction">
                                        <mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
                                            
                                        </mat-option>
                                    </mat-select>
                                    <mat-error *ngIf="relation.get('direction').hasError('required')">
                                        Relation direction is required.
                                    </mat-error>
                                </mat-form-field>
                                <tb-relation-type-autocomplete
                                        fxFlex class="mat-block"
                                        formControlName="relationType"
                                        [required]="true">
                                </tb-relation-type-autocomplete>
                            </div>
                            <div fxLayout="row" fxLayout.xs="column">
                                <tb-entity-select
                                        fxFlex class="mat-block"
                                        [required]="true"
                                        formControlName="relatedEntity">
                                </tb-entity-select>
                            </div>
                        </div>
                        <div fxLayout="column" fxLayoutAlign="center center">
                            <button mat-icon-button color="primary"
                                    aria-label="Remove"
                                    type="button"
                                    (click)="removeRelation(i)"
                                    matTooltip="Remove relation"
                                    matTooltipPosition="above">
                                <mat-icon>close</mat-icon>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            <div>
                <button mat-raised-button color="primary"
                        type="button"
                        (click)="addRelation()"
                        matTooltip="Add Relation"
                        matTooltipPosition="above">
                    Add
                </button>
            </div>
        </div>
    </div>
    <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
        <button mat-button color="primary"
                type="button"
                [disabled]="(isLoading$ | async)"
                (click)="cancel()" cdkFocusInitial>
            Cancel
        </button>
        <button mat-button mat-raised-button color="primary"
                type="submit"
                [disabled]="(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty">
            Save
        </button>
    </div>
</form>
手机操作

手机操作在移动应用程序配置中进行了说明有关详细信息请参阅手机操作移动操作。

操作源

操作源 是实现目标所需执行的操作(例如单击部件标题按钮、双击行或地图标记)所有部件的操作源都不同通过最常用的操作类型“导航到新仪表板状态”的示例解释每种部件类型的操作源。

所有操作源都将显示主“导航到新仪表板状态”操作类型示例。

配置

通过最常用的部件上的操作进行演示说明。

请在此处了解如何将实体表部件添加到仪表板。

实体表格部件添加操作:

  • 进入仪表板编辑模式;
  • 单击部件右上角的“铅笔”图标;
  • 选择单元格“操作”然后单击窗口右侧的“+”图标。
1. 单元格按钮

配置单元格中的操作按钮通过按钮图标执行操作:

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择单元格按钮操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择单元格click
  6. 选择“目标仪表板状态”
  7. 配置完成单击“添加”按钮
  8. 完成操作配置检查操作源、图标和操作类型
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

每个实体名称右边的操作单元格中的操作按钮单击之后会进入所设置的状态。

2. 部件按钮

每一个部件标题将显示一个按钮负责部件中实体的相关操作而不是单个实体,是一个最常见的操作源对每个部件有效单击图标执行操作:

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择部件按钮操作源
  4. 输入操作名称并选择按钮的图标
  5. 选择顶部按钮click
  6. 选择“目标仪表板状态”
  7. 配置完成单击“添加”按钮
  8. 完成操作配置检查操作源、图标和操作类型
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 单击屏幕右下角“应用更改”按钮

每个部件的标题出现一个按钮图标单击之后进入所设置的状态。

3. 单击

单击“表格”部件的行可执行操作:

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择部件按钮操作源
  4. 输入操作名称
  5. 选择数据行click
  6. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
  7. 选择操作类型“目标仪表板状态”
  8. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  9. 完成操作配置检查操作源、图标和操作类型
  10. 单击窗口右上角应用更改然后关闭窗口
  11. 单击屏幕右下角“应用更改”按钮

单击表格部件中的任何行进入所设置的状态。

4. 双击

双击“表格”部件的行可执行操作:

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择部件按钮操作源
  4. 输入操作名称
  5. 选择双击click
  6. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
  7. 选择操作类型“目标仪表板状态”
  8. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  9. 完成操作配置检查操作源、图标和操作类型
  10. 单击窗口右上角应用更改然后关闭窗口
  11. 单击屏幕右下角“应用更改”按钮

双击表格部件中的任何行进入所设置的状态。

5. 节点选中

添加实体树形层次后可以配置节点选中的操作源,请参阅此处)了解更多信息。

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择部件按钮操作源
  4. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
  5. 选择操作类型“目标仪表板状态”
  6. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  7. 完成操作配置检查操作源、图标和操作类型
  8. 单击窗口右上角应用更改然后关闭窗口
  9. 单击屏幕右下角“应用更改”按钮

单击“实体”层次结构中的任何节点执行操作。

6. HTML元素单击

ThingsBoard有两个部件可以通过设置中自定义HTML代码来完全操作执行仅在HTML卡片和标题值部件中使用。

  • HTML Cards

    1. 添加部件操作
    2. 单击“+”图标添加操作
    3. 选择HTML元素click
    4. 输入操作名称方便HTML代码进行使用
    5. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
    6. 选择操作类型“目标仪表板状态”
    7. 单击“添加操作”窗口右下角的“添加”按钮完成配置
    8. 完成操作配置检查操作源、图标和操作类型
    9. 编辑CSS和HTML
    10. 输入操作名称为HTML元素id值
1
<div id='button' class='card'>HTML code here</div>
  • HTML Cards部件中的HTML代码按钮的id是操作的名称

通过单击窗口右上角的橙色勾号图标来应用更改,然后关闭详细信息窗口。
在仪表板的编辑模式下通过单击屏幕右下角的橙色勾号图标“应用更改”来保存应用的更改。

请单击部件中的任意位置执行操作。

  • 标题Value

    1. 添加部件操作
    2. 单击“+”图标添加操作
    3. 选择HTML元素click
    4. 输入操作名称方便HTML代码进行使用
    5. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
    6. 选择操作类型“目标仪表板状态”
    7. 单击“添加操作”窗口右下角的“添加”按钮完成配置
    8. 完成操作配置检查操作源、图标和操作类型
    9. 编辑CSS和HTML
    10. 输入操作名称为HTML元素id值
1
<h1 id='button2'>Value title</h1>
  • 标题HTML代码中的id是操作的名称button2

通过单击窗口右上角的橙色勾号图标来应用更改,然后关闭详细信息窗口。
在仪表板的编辑模式下通过单击屏幕右下角的橙色勾号图标“应用更改”来保存应用的更改。

请单击部件标题位置执行操作。

地图

地图部件有单独的操作源。

进入仪表板编辑模式添加地图部件:

  • 新的空仪表板单击“添加新部件”选择地图部件包之后添加数据源
  • 现有仪表板单击图标选择地图部件包之后添加数据源
  • 添加地图操作

通过地图部件右上角的“铅笔”图标进入编辑模式导航到操作单元格单击“+”图标添加操作。

注意:: 所有操作说明假设是已经将状态添加到部件中如果没有添加请参阅此处了解更多信息。

1. 标记

操作将通过单击红色地图的标记来执行。

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 选择标记click
  4. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
  5. 选择操作类型“目标仪表板状态”
  6. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  7. 完成操作配置检查操作源、图标和操作类型
  8. 单击窗口右上角应用更改然后关闭窗口
  9. 单击屏幕右下角“应用更改”按钮
2. 多边形

多边形是由有限数量的点描述的平面图形通过设备和其它实体指定坐标,使用多边形选项中的polygon参数进行坐标设置:

1
[[1CoordinateLatitude,1CoordinateLatitude],[2CoordinateLatitude,2CoordinateLatitude]...[nCoordinateLatitude,nCoordinateLatitude]]

其中n是多边形参坐标。

在地图部件上配置操作源:

  1. 进入编辑模式高级选项卡
  2. 勾选“显示多边形”复选框
  3. 输入多边形键名
  4. 单击窗口右上角的橙色图标应用更改
  5. 单击右下角的勾号图标保存更改
  6. 单击左则资产菜单
  7. 选择需要添加多边形的资产
  8. 单击属性选项卡
  9. 单击“+”图标向资产添加属性
  10. 输入与多边形设置一样的键名称
  11. 选择“字符串”类型并输入坐标
    1
    
    [[37.758436,  -122.509429],[37.759834, -122.476874],[37.734690, -122.475170],[37.733531, -122.506271]]
    
  12. 单击属性窗口添加按钮
  13. 返回仪表格地图部件
  14. 进入仪表板的编辑模式
  15. 进入部件编辑模式
  16. 输入数据键
  17. 单击应用更改
  18. 单击操作选项卡
  19. 单击“+”图标添加操作
  20. 选择多边形click
  21. 选择下拉菜单中的操作源“导航到新的仪表板状态”进行修改
  22. 选择操作类型“目标仪表板状态”
  23. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  24. 完成操作配置检查操作源、图标和操作类型
  25. 单击窗口右上角应用更改然后关闭窗口
  26. 单击屏幕右下角“应用更改”按钮

单击多边形内的任意位置执行操作。

3. 提示框

单击地图标记时将显示提示框通过操作链接可以执行操作,本文中介绍了提示框的基本用法。
可以不同的设备和资产配置多个链接并在地图部件的高级模式下配置提示框操作。

根据下面步骤配置地图部件的提示框操作:

  1. 添加部件操作
  2. 单击“+”图标添加操作
  3. 勾选“显示多边形”复选框
  4. 输入多边形键名
  5. 选择单元格click
  6. 选择下拉菜单中的操作源“导航到新的仪表板状态”修改状态
  7. 单击“添加操作”窗口右下角的“添加”按钮完成配置
  8. 完成操作配置检查操作源、图标和操作类型
  9. 单击窗口右上角应用更改然后关闭窗口
  10. 选择高级选项卡
  11. 勾选“显示多边形”复选框
  12. 输入以下代码在提示栏中
1
<link-act name='TooltipTag'>Navigate to the Building A</link-act>

其中_TooltipTag_是操作名称导航到时_Building A_将在提示框中显示链接文本。

  1. 单击窗口右上角应用更改然后关闭窗口
  2. 单击屏幕右下角“应用更改”按钮

单击地图的标记显示工具提示单击提示的链接文本执行操作。

特殊设置

左侧布局

如果用户需要在加一个部件上查看更新的详细信息,例如:在一个实体表格上有一个资产列表希望在图表部件上更新详细信息可以在页面添加两个部件选择“更新当前仪表板”操作可以实现这种效果,因此自适应屏幕会在部件下面进行显示信息。

通过布局管理可以实现的功能:

  1. 进入仪表板编辑模式
  2. 单击“管理布局”
  3. 打开“管理布局”窗口中勾选复选框“固定(px)”然后单击保存
  4. 单击右键部件选择“复制”
  5. 单击右键空布选择选择“粘贴”
  6. 单击右键删除布局中的部件
  7. 拖动部件边缘调整大小

通过单击部件中的按钮在另一个部件查看详细信息:

  1. 进入仪表板编辑模式
  2. 单击“+”图标添加新操作
  3. 选择操作源输入名称
  4. 选择操作类型“更新当前仪表板状态”
  5. 勾选复选框“打开右侧布局 (移动端视图)”
  6. 单击窗口右下角“应用更改”按钮
  7. 单击屏幕右下角“应用更改”按钮

在手机模式下通过单击操作按钮将直接过渡到所需的部件可以看到主部件实体的详细信息。

单独对话框

移动到单独的仪表板状态以查看组件的详细信息是不行的但是支持在同一个仪表板页面打开它对于这种情况可以在单独的对话框中打开,该功能允许使用配置的操作在同一页面上打开另一个状态
仪表板上的单独对话框窗口中打开状态步骤:

  1. 进入仪表板的编辑模式
  2. 单击仪表板左上方的“管理仪表板状态”按钮添加状态
  3. 单击“+”图标添加新状态,请参阅此处了解更多信息
  4. 单击“管理仪表板状态”窗口右下角的“保存”按钮
  5. 进入部件编辑模式
  6. 单击“+”图标添加新操作
  7. 选择操操作源并指定名称和图标
  8. 下拉菜单中选择“导航到新仪表板”操作类型和目标状态,即在步骤 3 中创建的状态
  9. 勾选“在单独的对话框中打开”复选框
  10. 勾选“在对话框中隐藏仪表板工具栏”复选框
  11. 选择百分比为单位调整对话框的宽度和高度
  12. 单击“添加操作”窗口右下角的“添加”按钮
  13. 单击窗口右上角应用更改然后关闭窗口

现在将部件添加到另一个状态并执行操作了。

  1. 单击“添加新部件”按钮
  2. 选择部件
  3. 单击“添加”
  4. 拖动部件边缘调整大小
  5. 单击屏幕左下角的橙色勾号保存应用更改

单击实体单元格按钮打开包含状态的对话框窗口。

设置实体

部件设置实体复选框负责将特定实体从部件添加到状态允许通过创建“来自仪表板状态的实体”或其他别名来使用目标仪表板状态中的实体。例如:表格部件中有设备列表并且希望在单击表格行时显示特定的设备详细信息。

如果需要在状态中存储多个实体例如:希望导航到客户列表然后导航到客户的设备最后导航到指定设备详细信息,在这种情况下可能有三种状态:“Main”、“客户设备”和“设备详细信息”可以使用两个不同的状态实体参数来引用“设备详细信息”状态上的当前客户(例如“当前客户”)和当前设备(例如“当前设备”)。