TB-63: Alarms widget implementation
This commit is contained in:
parent
531f42450f
commit
eda21c94ef
@ -23,11 +23,14 @@ import './alarm-details-dialog.scss';
|
||||
const js_beautify = beautify.js;
|
||||
|
||||
/*@ngInject*/
|
||||
export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) {
|
||||
export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types,
|
||||
alarmService, alarmId, allowAcknowledgment, allowClear, showingCallback) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.alarmId = alarmId;
|
||||
vm.allowAcknowledgment = allowAcknowledgment;
|
||||
vm.allowClear = allowClear;
|
||||
vm.types = types;
|
||||
vm.alarm = null;
|
||||
|
||||
|
||||
@ -84,16 +84,16 @@
|
||||
</div>
|
||||
</md-dialog-content>
|
||||
<md-dialog-actions layout="row">
|
||||
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack ||
|
||||
vm.alarm.status==vm.types.alarmStatus.clearedUnack"
|
||||
<md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack ||
|
||||
vm.alarm.status==vm.types.alarmStatus.clearedUnack)"
|
||||
class="md-raised md-primary"
|
||||
ng-disabled="loading"
|
||||
ng-click="vm.acknowledge()"
|
||||
style="margin-right:20px;">{{ 'alarm.acknowledge' |
|
||||
translate }}
|
||||
</md-button>
|
||||
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck ||
|
||||
vm.alarm.status==vm.types.alarmStatus.activeUnack"
|
||||
<md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck ||
|
||||
vm.alarm.status==vm.types.alarmStatus.activeUnack)"
|
||||
class="md-raised md-primary"
|
||||
ng-disabled="loading"
|
||||
ng-click="vm.clear()">{{ 'alarm.clear' |
|
||||
|
||||
@ -40,7 +40,12 @@ export default function AlarmRowDirective($compile, $templateCache, types, $mdDi
|
||||
controller: 'AlarmDetailsDialogController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: alarmDetailsDialogTemplate,
|
||||
locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback},
|
||||
locals: {
|
||||
alarmId: scope.alarm.id.id,
|
||||
allowAcknowledgment: true,
|
||||
allowClear: true,
|
||||
showingCallback: onShowingCallback
|
||||
},
|
||||
parent: angular.element($document[0].body),
|
||||
targetEvent: $event,
|
||||
fullscreen: true,
|
||||
|
||||
@ -252,12 +252,12 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
|
||||
$timeout(function() {
|
||||
alarmSourceListener.alarmsUpdated([simulatedAlarm], false);
|
||||
});
|
||||
} else {
|
||||
var pollingInterval = 5000; //TODO:
|
||||
} else if (alarmSource.entityType && alarmSource.entityId) {
|
||||
var pollingInterval = alarmSourceListener.alarmsPollingInterval;
|
||||
alarmSourceListener.alarmsQuery = {
|
||||
entityType: alarmSource.entityType,
|
||||
entityId: alarmSource.entityId,
|
||||
alarmSearchStatus: null, //TODO:
|
||||
alarmSearchStatus: alarmSourceListener.alarmSearchStatus,
|
||||
alarmStatus: null
|
||||
}
|
||||
var originatorKeys = $filter('filter')(alarmSource.dataKeys, {name: 'originator'});
|
||||
|
||||
@ -70,6 +70,12 @@ export default class Subscription {
|
||||
this.callbacks.dataLoading = this.callbacks.dataLoading || function(){};
|
||||
this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || function(){};
|
||||
this.alarmSource = options.alarmSource;
|
||||
|
||||
this.alarmSearchStatus = angular.isDefined(options.alarmSearchStatus) ?
|
||||
options.alarmSearchStatus : this.ctx.types.alarmSearchStatus.any;
|
||||
this.alarmsPollingInterval = angular.isDefined(options.alarmsPollingInterval) ?
|
||||
options.alarmsPollingInterval : 5000;
|
||||
|
||||
this.alarmSourceListener = null;
|
||||
this.alarms = [];
|
||||
|
||||
@ -193,8 +199,7 @@ export default class Subscription {
|
||||
registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) {
|
||||
if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) {
|
||||
subscription.timeWindowConfig = angular.copy(newDashboardTimewindow);
|
||||
subscription.unsubscribe();
|
||||
subscription.subscribe();
|
||||
subscription.update();
|
||||
}
|
||||
});
|
||||
this.registrations.push(registration);
|
||||
@ -281,8 +286,7 @@ export default class Subscription {
|
||||
registration = this.ctx.$scope.$on('dashboardTimewindowChanged', function (event, newDashboardTimewindow) {
|
||||
if (!angular.equals(subscription.timeWindowConfig, newDashboardTimewindow) && newDashboardTimewindow) {
|
||||
subscription.timeWindowConfig = angular.copy(newDashboardTimewindow);
|
||||
subscription.unsubscribe();
|
||||
subscription.subscribe();
|
||||
subscription.update();
|
||||
}
|
||||
});
|
||||
this.registrations.push(registration);
|
||||
@ -298,8 +302,7 @@ export default class Subscription {
|
||||
return subscription.timeWindowConfig;
|
||||
}, function (newTimewindow, prevTimewindow) {
|
||||
if (!angular.equals(newTimewindow, prevTimewindow)) {
|
||||
subscription.unsubscribe();
|
||||
subscription.subscribe();
|
||||
subscription.update();
|
||||
}
|
||||
}, true);
|
||||
this.registrations.push(this.timeWindowWatchRegistration);
|
||||
@ -502,8 +505,7 @@ export default class Subscription {
|
||||
this.timeWindowConfig = angular.copy(this.originalTimewindow);
|
||||
this.originalTimewindow = null;
|
||||
this.callbacks.timeWindowUpdated(this, this.timeWindowConfig);
|
||||
this.unsubscribe();
|
||||
this.subscribe();
|
||||
this.update();
|
||||
this.startWatchingTimewindow();
|
||||
}
|
||||
}
|
||||
@ -519,8 +521,7 @@ export default class Subscription {
|
||||
}
|
||||
this.timeWindowConfig = this.ctx.timeService.toHistoryTimewindow(this.timeWindowConfig, startTimeMs, endTimeMs);
|
||||
this.callbacks.timeWindowUpdated(this, this.timeWindowConfig);
|
||||
this.unsubscribe();
|
||||
this.subscribe();
|
||||
this.update();
|
||||
this.startWatchingTimewindow();
|
||||
}
|
||||
}
|
||||
@ -618,6 +619,11 @@ export default class Subscription {
|
||||
this.callbacks.legendDataUpdated(this, apply !== false);
|
||||
}
|
||||
|
||||
update() {
|
||||
this.unsubscribe();
|
||||
this.subscribe();
|
||||
}
|
||||
|
||||
subscribe() {
|
||||
if (this.type === this.ctx.types.widgetType.rpc.value) {
|
||||
return;
|
||||
@ -688,6 +694,8 @@ export default class Subscription {
|
||||
this.alarmSourceListener = {
|
||||
subscriptionTimewindow: this.subscriptionTimewindow,
|
||||
alarmSource: this.alarmSource,
|
||||
alarmSearchStatus: this.alarmSearchStatus,
|
||||
alarmsPollingInterval: this.alarmsPollingInterval,
|
||||
alarmsUpdated: function(alarms, apply) {
|
||||
subscription.alarmsUpdated(alarms, apply);
|
||||
}
|
||||
|
||||
@ -394,7 +394,8 @@ export default angular.module('thingsboard.types', [])
|
||||
cards: "cards"
|
||||
},
|
||||
translate: {
|
||||
dashboardStatePrefix: "dashboardState.state."
|
||||
dashboardStatePrefix: "dashboardState.state.",
|
||||
keyLabelPrefix: "key.label."
|
||||
}
|
||||
}
|
||||
).name;
|
||||
|
||||
@ -63,6 +63,12 @@ function DatakeyConfig($compile, $templateCache, $q, types) {
|
||||
element.html(template);
|
||||
|
||||
scope.types = types;
|
||||
|
||||
scope.alarmFields = [];
|
||||
for (var alarmField in types.alarmFields) {
|
||||
scope.alarmFields.push(alarmField);
|
||||
}
|
||||
|
||||
scope.selectedKey = null;
|
||||
scope.keySearchText = null;
|
||||
scope.usePostProcessing = false;
|
||||
@ -112,9 +118,19 @@ function DatakeyConfig($compile, $templateCache, $q, types) {
|
||||
}, true);
|
||||
|
||||
scope.keysSearch = function (searchText) {
|
||||
if (scope.model.type === types.dataKeyType.alarm) {
|
||||
var dataKeys = searchText ? scope.alarmFields.filter(
|
||||
scope.createFilterForDataKey(searchText)) : scope.alarmFields;
|
||||
dataKeys.push(searchText);
|
||||
return dataKeys;
|
||||
} else {
|
||||
if (scope.entityAlias) {
|
||||
var deferred = $q.defer();
|
||||
scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: scope.model.type})
|
||||
scope.fetchEntityKeys({
|
||||
entityAliasId: scope.entityAlias.id,
|
||||
query: searchText,
|
||||
type: scope.model.type
|
||||
})
|
||||
.then(function (keys) {
|
||||
keys.push(searchText);
|
||||
deferred.resolve(keys);
|
||||
@ -125,6 +141,14 @@ function DatakeyConfig($compile, $templateCache, $q, types) {
|
||||
} else {
|
||||
return $q.when([]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.createFilterForDataKey = function (query) {
|
||||
var lowercaseQuery = angular.lowercase(query);
|
||||
return function filterFn(dataKey) {
|
||||
return (angular.lowercase(dataKey).indexOf(lowercaseQuery) === 0);
|
||||
};
|
||||
};
|
||||
|
||||
$compile(element.contents())(scope);
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
|
||||
-->
|
||||
<md-content class="md-padding" layout="column">
|
||||
<md-autocomplete ng-if="model.type === types.dataKeyType.timeseries || model.type === types.dataKeyType.attribute"
|
||||
<md-autocomplete ng-if="model.type === types.dataKeyType.timeseries ||
|
||||
model.type === types.dataKeyType.attribute ||
|
||||
model.type === types.dataKeyType.alarm"
|
||||
style="padding-bottom: 8px;"
|
||||
ng-required="true"
|
||||
md-no-cache="true"
|
||||
@ -27,8 +29,8 @@
|
||||
md-items="item in keysSearch(keySearchText)"
|
||||
md-item-text="item"
|
||||
md-min-length="0"
|
||||
placeholder="Key name"
|
||||
md-floating-label="Key">
|
||||
placeholder="{{ 'entity.key-name' | translate }}"
|
||||
md-floating-label="{{ 'entity.key' | translate }}">
|
||||
<span md-highlight-text="keySearchText" md-highlight-flags="^i">{{item}}</span>
|
||||
</md-autocomplete>
|
||||
<div layout="row" layout-align="start center">
|
||||
@ -48,7 +50,7 @@
|
||||
md-color-history="false">
|
||||
</div>
|
||||
</div>
|
||||
<div layout="row" layout-align="start center">
|
||||
<div layout="row" layout-align="start center" ng-if="model.type !== types.dataKeyType.alarm">
|
||||
<md-input-container flex>
|
||||
<label translate>datakey.units</label>
|
||||
<input name="units" ng-model="model.units">
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
@import '../../scss/constants';
|
||||
|
||||
.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete {
|
||||
.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
|
||||
.tb-not-found {
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
|
||||
@ -133,7 +133,7 @@
|
||||
ng-required="true"
|
||||
ng-model="alarmDataKeys" md-autocomplete-snap
|
||||
md-transform-chip="transformAlarmDataKeyChip($chip)"
|
||||
md-require-match="true">
|
||||
md-require-match="false">
|
||||
<md-autocomplete
|
||||
md-no-cache="true"
|
||||
id="alarm_datakey"
|
||||
@ -152,6 +152,9 @@
|
||||
</div>
|
||||
<div ng-if="textIsNotEmpty(alarmDataKeySearchText)">
|
||||
<span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span>
|
||||
<span>
|
||||
<a translate ng-click="createKey($event, '#alarm_datakey_chips')">entity.create-new-key</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</md-not-found>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tb-func-datakey-autocomplete {
|
||||
.tb-func-datakey-autocomplete, .tb-alarm-datakey-autocomplete {
|
||||
.tb-not-found {
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
|
||||
@ -48,9 +48,9 @@
|
||||
<span translate>device.no-keys-found</span>
|
||||
</div>
|
||||
<div ng-if="textIsNotEmpty(dataKeySearchText)">
|
||||
<span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span>
|
||||
<span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span>
|
||||
<span>
|
||||
<a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a>
|
||||
<a translate ng-click="createKey($event, '#function_datakey_chips')">entity.create-new-key</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,7 +81,7 @@
|
||||
ng-required="true"
|
||||
ng-model="alarmDataKeys" md-autocomplete-snap
|
||||
md-transform-chip="transformAlarmDataKeyChip($chip)"
|
||||
md-require-match="true">
|
||||
md-require-match="false">
|
||||
<md-autocomplete
|
||||
md-no-cache="true"
|
||||
id="alarm_datakey"
|
||||
@ -100,6 +100,9 @@
|
||||
</div>
|
||||
<div ng-if="textIsNotEmpty(alarmDataKeySearchText)">
|
||||
<span translate translate-values='{ key: "{{alarmDataKeySearchText | truncate:true:6:'...'}}" }'>entity.no-key-matching</span>
|
||||
<span>
|
||||
<a translate ng-click="createKey($event, '#alarm_datakey_chips')">entity.create-new-key</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</md-not-found>
|
||||
|
||||
@ -144,6 +144,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
|
||||
scope.targetDeviceAlias.value = null;
|
||||
}
|
||||
} else if (scope.widgetType === types.widgetType.alarm.value && scope.isDataEnabled) {
|
||||
scope.alarmSearchStatus = angular.isDefined(config.alarmSearchStatus) ?
|
||||
config.alarmSearchStatus : types.alarmSearchStatus.any;
|
||||
scope.alarmsPollingInterval = angular.isDefined(config.alarmsPollingInterval) ?
|
||||
config.alarmsPollingInterval : 5;
|
||||
if (config.alarmSource) {
|
||||
scope.alarmSource.value = config.alarmSource;
|
||||
} else {
|
||||
@ -205,7 +209,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
|
||||
};
|
||||
|
||||
scope.$watch('title + showTitle + dropShadow + enableFullscreen + backgroundColor + color + ' +
|
||||
'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + showLegend', function () {
|
||||
'padding + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' +
|
||||
'alarmSearchStatus + alarmsPollingInterval + showLegend', function () {
|
||||
if (ngModelCtrl.$viewValue) {
|
||||
var value = ngModelCtrl.$viewValue;
|
||||
if (value.config) {
|
||||
@ -225,6 +230,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
|
||||
config.units = scope.units;
|
||||
config.decimals = scope.decimals;
|
||||
config.useDashboardTimewindow = scope.useDashboardTimewindow;
|
||||
config.alarmSearchStatus = scope.alarmSearchStatus;
|
||||
config.alarmsPollingInterval = scope.alarmsPollingInterval;
|
||||
config.showLegend = scope.showLegend;
|
||||
}
|
||||
if (value.layout) {
|
||||
|
||||
@ -31,6 +31,30 @@
|
||||
flex ng-model="timewindow"></tb-timewindow>
|
||||
</section>
|
||||
</div>
|
||||
<div ng-show="widgetType === types.widgetType.alarm.value" layout='column' layout-align="center"
|
||||
layout-gt-sm='row' layout-align-gt-sm="start center">
|
||||
<md-input-container class="md-block" flex>
|
||||
<label translate>alarm.alarm-status</label>
|
||||
<md-select ng-model="alarmSearchStatus" style="padding-bottom: 24px;">
|
||||
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
|
||||
{{ ('alarm.search-status.' + searchStatus) | translate }}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container flex class="md-block">
|
||||
<label translate>alarm.polling-interval</label>
|
||||
<input ng-required="widgetType === types.widgetType.alarm.value"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
name="alarmsPollingInterval"
|
||||
ng-model="alarmsPollingInterval"/>
|
||||
<div ng-messages="theForm.alarmsPollingInterval.$error" multiple md-auto-hide="false">
|
||||
<div ng-message="required" translate>alarm.polling-interval-required</div>
|
||||
<div ng-message="min" translate>alarm.min-polling-interval-message</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default"
|
||||
ng-show="widgetType !== types.widgetType.rpc.value
|
||||
&& widgetType !== types.widgetType.alarm.value
|
||||
|
||||
@ -289,6 +289,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
|
||||
}
|
||||
if (widget.type == types.widgetType.alarm.value) {
|
||||
options.alarmSource = angular.copy(widget.config.alarmSource);
|
||||
options.alarmSearchStatus = angular.isDefined(widget.config.alarmSearchStatus) ?
|
||||
widget.config.alarmSearchStatus : types.alarmSearchStatus.any;
|
||||
options.alarmsPollingInterval = angular.isDefined(widget.config.alarmsPollingInterval) ?
|
||||
widget.config.alarmsPollingInterval * 1000 : 5000;
|
||||
} else {
|
||||
options.datasources = angular.copy(widget.config.datasources)
|
||||
}
|
||||
|
||||
@ -243,13 +243,11 @@ export default function EntityStateController($scope, $location, $state, $stateP
|
||||
}
|
||||
|
||||
function gotoState(stateId, update, openRightLayout) {
|
||||
if (vm.dashboardCtrl.dashboardCtx.state != stateId) {
|
||||
vm.dashboardCtrl.openDashboardState(stateId, openRightLayout);
|
||||
if (update) {
|
||||
updateLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateLocation() {
|
||||
if (vm.stateObject[vm.stateObject.length-1].id) {
|
||||
|
||||
@ -148,7 +148,14 @@ export default angular.module('thingsboard.locale', [])
|
||||
"clear": "Clear",
|
||||
"search": "Search alarms",
|
||||
"selected-alarms": "{ count, select, 1 {1 alarm} other {# alarms} } selected",
|
||||
"no-data": "No data to display"
|
||||
"no-data": "No data to display",
|
||||
"polling-interval": "Alarms polling interval (sec)",
|
||||
"polling-interval-required": "Alarms polling interval is required.",
|
||||
"min-polling-interval-message": "At least 1 sec polling interval is allowed.",
|
||||
"aknowledge-alarms-title": "Acknowledge { count, select, 1 {1 alarm} other {# alarms} }",
|
||||
"aknowledge-alarms-text": "Are you sure you want to acknowledge { count, select, 1 {1 alarm} other {# alarms} }?",
|
||||
"clear-alarms-title": "Clear { count, select, 1 {1 alarm} other {# alarms} }",
|
||||
"clear-alarms-text": "Are you sure you want to clear { count, select, 1 {1 alarm} other {# alarms} }?"
|
||||
},
|
||||
"alias": {
|
||||
"add": "Add alias",
|
||||
@ -643,6 +650,8 @@ export default angular.module('thingsboard.locale', [])
|
||||
"no-aliases-found": "No aliases found.",
|
||||
"no-alias-matching": "'{{alias}}' not found.",
|
||||
"create-new-alias": "Create a new one!",
|
||||
"key": "Key",
|
||||
"key-name": "Key name",
|
||||
"no-keys-found": "No keys found.",
|
||||
"no-key-matching": "'{{key}}' not found.",
|
||||
"create-new-key": "Create a new one!",
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
export default function ThingsboardMissingTranslateHandler($log, types) {
|
||||
|
||||
return function (translationId) {
|
||||
if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix)) {
|
||||
if (translationId && !translationId.startsWith(types.translate.dashboardStatePrefix) &&
|
||||
!translationId.startsWith(types.translate.keyLabelPrefix)) {
|
||||
$log.warn('Translation for ' + translationId + ' doesn\'t exist');
|
||||
}
|
||||
};
|
||||
|
||||
@ -19,6 +19,7 @@ import './alarms-table-widget.scss';
|
||||
/* eslint-disable import/no-unresolved, import/default */
|
||||
|
||||
import alarmsTableWidgetTemplate from './alarms-table-widget.tpl.html';
|
||||
import alarmDetailsDialogTemplate from '../../alarm/alarm-details-dialog.tpl.html';
|
||||
|
||||
/* eslint-enable import/no-unresolved, import/default */
|
||||
|
||||
@ -46,11 +47,12 @@ function AlarmsTableWidget() {
|
||||
}
|
||||
|
||||
/*@ngInject*/
|
||||
function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUtil, $translate, utils, types) {
|
||||
function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDialog, $document, $translate, $q, alarmService, utils, types) {
|
||||
var vm = this;
|
||||
|
||||
vm.stylesInfo = {};
|
||||
vm.contentsInfo = {};
|
||||
vm.columnWidth = {};
|
||||
|
||||
vm.showData = true;
|
||||
vm.hasData = false;
|
||||
@ -64,19 +66,32 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
|
||||
vm.currentAlarm = null;
|
||||
|
||||
vm.alarmsTitle = $translate.instant('alarm.alarms');
|
||||
vm.enableSelection = true;
|
||||
vm.enableSearch = true;
|
||||
vm.displayDetails = true;
|
||||
vm.allowAcknowledgment = true;
|
||||
vm.allowClear = true;
|
||||
vm.displayPagination = true;
|
||||
vm.defaultPageSize = 10;
|
||||
vm.defaultSortOrder = '-'+types.alarmFields.createdTime.value;
|
||||
|
||||
vm.query = {
|
||||
order: '-'+types.alarmFields.createdTime.value,
|
||||
limit: 10,
|
||||
order: vm.defaultSortOrder,
|
||||
limit: vm.defaultPageSize,
|
||||
page: 1,
|
||||
search: null
|
||||
};
|
||||
|
||||
vm.alarmsTitle = $translate.instant('alarm.alarms');
|
||||
|
||||
vm.enterFilterMode = enterFilterMode;
|
||||
vm.exitFilterMode = exitFilterMode;
|
||||
vm.onReorder = onReorder;
|
||||
vm.onPaginate = onPaginate;
|
||||
vm.onRowClick = onRowClick;
|
||||
vm.isCurrent = isCurrent;
|
||||
vm.openAlarmDetails = openAlarmDetails;
|
||||
vm.ackAlarms = ackAlarms;
|
||||
vm.clearAlarms = clearAlarms;
|
||||
|
||||
vm.cellStyle = cellStyle;
|
||||
vm.cellContent = cellContent;
|
||||
@ -119,7 +134,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
$scope.$watch(function() { return $mdMedia('gt-md'); }, function(isGtMd) {
|
||||
vm.isGtMd = isGtMd;
|
||||
if (vm.isGtMd) {
|
||||
vm.limitOptions = [5, 10, 15];
|
||||
vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3];
|
||||
} else {
|
||||
vm.limitOptions = null;
|
||||
}
|
||||
@ -130,7 +145,33 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
if (vm.settings.alarmsTitle && vm.settings.alarmsTitle.length) {
|
||||
vm.alarmsTitle = vm.settings.alarmsTitle;
|
||||
}
|
||||
//TODO:
|
||||
vm.enableSelection = angular.isDefined(vm.settings.enableSelection) ? vm.settings.enableSelection : true;
|
||||
vm.enableSearch = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true;
|
||||
vm.displayDetails = angular.isDefined(vm.settings.displayDetails) ? vm.settings.displayDetails : true;
|
||||
vm.allowAcknowledgment = angular.isDefined(vm.settings.allowAcknowledgment) ? vm.settings.allowAcknowledgment : true;
|
||||
vm.allowClear = angular.isDefined(vm.settings.allowClear) ? vm.settings.allowClear : true;
|
||||
if (!vm.allowAcknowledgment && !vm.allowClear) {
|
||||
vm.enableSelection = false;
|
||||
}
|
||||
|
||||
vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true;
|
||||
|
||||
var pageSize = vm.settings.defaultPageSize;
|
||||
if (angular.isDefined(pageSize) && Number.isInteger(pageSize) && pageSize > 0) {
|
||||
vm.defaultPageSize = pageSize;
|
||||
}
|
||||
|
||||
if (vm.settings.defaultSortOrder && vm.settings.defaultSortOrder.length) {
|
||||
vm.defaultSortOrder = vm.settings.defaultSortOrder;
|
||||
}
|
||||
|
||||
vm.query.order = vm.defaultSortOrder;
|
||||
vm.query.limit = vm.defaultPageSize;
|
||||
if (vm.isGtMd) {
|
||||
vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3];
|
||||
} else {
|
||||
vm.limitOptions = null;
|
||||
}
|
||||
|
||||
var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)';
|
||||
var defaultColor = tinycolor(origColor);
|
||||
@ -207,6 +248,115 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
updateAlarms();
|
||||
}
|
||||
|
||||
function onRowClick($event, alarm) {
|
||||
if (vm.currentAlarm != alarm) {
|
||||
vm.currentAlarm = alarm;
|
||||
}
|
||||
}
|
||||
|
||||
function isCurrent(alarm) {
|
||||
return (vm.currentAlarm && alarm && vm.currentAlarm.id && alarm.id) &&
|
||||
(vm.currentAlarm.id.id === alarm.id.id);
|
||||
}
|
||||
|
||||
function openAlarmDetails($event, alarm) {
|
||||
if (alarm && alarm.id) {
|
||||
var onShowingCallback = {
|
||||
onShowing: function(){}
|
||||
}
|
||||
$mdDialog.show({
|
||||
controller: 'AlarmDetailsDialogController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: alarmDetailsDialogTemplate,
|
||||
locals: {
|
||||
alarmId: alarm.id.id,
|
||||
allowAcknowledgment: vm.allowAcknowledgment,
|
||||
allowClear: vm.allowClear,
|
||||
showingCallback: onShowingCallback
|
||||
},
|
||||
parent: angular.element($document[0].body),
|
||||
targetEvent: $event,
|
||||
fullscreen: true,
|
||||
skipHide: true,
|
||||
onShowing: function(scope, element) {
|
||||
onShowingCallback.onShowing(scope, element);
|
||||
}
|
||||
}).then(function (alarm) {
|
||||
if (alarm) {
|
||||
vm.subscription.update();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function ackAlarms($event) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
if (vm.selectedAlarms && vm.selectedAlarms.length > 0) {
|
||||
var title = $translate.instant('alarm.aknowledge-alarms-title', {count: vm.selectedAlarms.length}, 'messageformat');
|
||||
var content = $translate.instant('alarm.aknowledge-alarms-text', {count: vm.selectedAlarms.length}, 'messageformat');
|
||||
var confirm = $mdDialog.confirm()
|
||||
.targetEvent($event)
|
||||
.title(title)
|
||||
.htmlContent(content)
|
||||
.ariaLabel(title)
|
||||
.cancel($translate.instant('action.no'))
|
||||
.ok($translate.instant('action.yes'));
|
||||
$mdDialog.show(confirm).then(function () {
|
||||
var tasks = [];
|
||||
for (var i=0;i<vm.selectedAlarms.length;i++) {
|
||||
var alarm = vm.selectedAlarms[i];
|
||||
if (alarm.id) {
|
||||
tasks.push(alarmService.ackAlarm(alarm.id.id));
|
||||
}
|
||||
}
|
||||
if (tasks.length) {
|
||||
$q.all(tasks).then(function () {
|
||||
vm.selectedAlarms = [];
|
||||
vm.subscription.update();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function clearAlarms($event) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
if (vm.selectedAlarms && vm.selectedAlarms.length > 0) {
|
||||
var title = $translate.instant('alarm.clear-alarms-title', {count: vm.selectedAlarms.length}, 'messageformat');
|
||||
var content = $translate.instant('alarm.clear-alarms-text', {count: vm.selectedAlarms.length}, 'messageformat');
|
||||
var confirm = $mdDialog.confirm()
|
||||
.targetEvent($event)
|
||||
.title(title)
|
||||
.htmlContent(content)
|
||||
.ariaLabel(title)
|
||||
.cancel($translate.instant('action.no'))
|
||||
.ok($translate.instant('action.yes'));
|
||||
$mdDialog.show(confirm).then(function () {
|
||||
var tasks = [];
|
||||
for (var i=0;i<vm.selectedAlarms.length;i++) {
|
||||
var alarm = vm.selectedAlarms[i];
|
||||
if (alarm.id) {
|
||||
tasks.push(alarmService.clearAlarm(alarm.id.id));
|
||||
}
|
||||
}
|
||||
if (tasks.length) {
|
||||
$q.all(tasks).then(function () {
|
||||
vm.selectedAlarms = [];
|
||||
vm.subscription.update();
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateAlarms(preserveSelections) {
|
||||
if (!preserveSelections) {
|
||||
vm.selectedAlarms = [];
|
||||
@ -216,8 +366,14 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
result = $filter('filter')(result, {$: vm.query.search});
|
||||
}
|
||||
vm.alarmsCount = result.length;
|
||||
|
||||
if (vm.displayPagination) {
|
||||
var startIndex = vm.query.limit * (vm.query.page - 1);
|
||||
vm.alarms = result.slice(startIndex, startIndex + vm.query.limit);
|
||||
} else {
|
||||
vm.alarms = result;
|
||||
}
|
||||
|
||||
if (preserveSelections) {
|
||||
var newSelectedAlarms = [];
|
||||
if (vm.selectedAlarms && vm.selectedAlarms.length) {
|
||||
@ -251,6 +407,10 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
style = defaultStyle(key, value);
|
||||
}
|
||||
}
|
||||
if (!style.width) {
|
||||
var columnWidth = vm.columnWidth[key.label];
|
||||
style.width = columnWidth;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
@ -295,7 +455,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
@ -313,6 +473,8 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
@ -340,9 +502,19 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
|
||||
vm.stylesInfo = {};
|
||||
vm.contentsInfo = {};
|
||||
vm.columnWidth = {};
|
||||
|
||||
for (var d = 0; d < vm.alarmSource.dataKeys.length; d++ ) {
|
||||
var dataKey = vm.alarmSource.dataKeys[d];
|
||||
|
||||
var translationId = types.translate.keyLabelPrefix + dataKey.label;
|
||||
var translation = $translate.instant(translationId);
|
||||
if (translation != translationId) {
|
||||
dataKey.title = translation;
|
||||
} else {
|
||||
dataKey.title = dataKey.label;
|
||||
}
|
||||
|
||||
var keySettings = dataKey.settings;
|
||||
|
||||
var cellStyleFunction = null;
|
||||
@ -384,6 +556,9 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdUti
|
||||
useCellContentFunction: useCellContentFunction,
|
||||
cellContentFunction: cellContentFunction
|
||||
};
|
||||
|
||||
var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px';
|
||||
vm.columnWidth[dataKey.label] = columnWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,14 +17,14 @@
|
||||
.tb-alarms-table {
|
||||
margin-top: 15px;
|
||||
&.tb-data-table {
|
||||
table.md-table {
|
||||
table.md-table, table.md-table.md-row-select {
|
||||
tbody {
|
||||
tr {
|
||||
td {
|
||||
&.ag-action-cell {
|
||||
min-width: 40px;
|
||||
max-width: 40px;
|
||||
width: 40px;
|
||||
&.tb-action-cell {
|
||||
min-width: 36px;
|
||||
max-width: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<div class="md-toolbar-tools">
|
||||
<span>{{ vm.alarmsTitle }}</span>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="vm.enterFilterMode()">
|
||||
<md-button ng-if="vm.enableSearch" class="md-icon-button" ng-click="vm.enterFilterMode()">
|
||||
<md-icon>search</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'action.search' | translate }}
|
||||
@ -57,13 +57,13 @@
|
||||
translate-values="{count: vm.selectedAlarms.length}"
|
||||
translate-interpolation="messageformat"></span>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="vm.ackAlarms($event)">
|
||||
<md-button ng-if="vm.allowAcknowledgment" class="md-icon-button" ng-click="vm.ackAlarms($event)">
|
||||
<md-icon>done</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'alarm.acknowledge' | translate }}
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
<md-button class="md-icon-button" ng-click="vm.clearAlarms($event)">
|
||||
<md-button ng-if="vm.allowClear" class="md-icon-button" ng-click="vm.clearAlarms($event)">
|
||||
<md-icon>clear</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'alarm.clear' | translate }}
|
||||
@ -72,22 +72,22 @@
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-table-container flex>
|
||||
<table md-table md-row-select multiple="" ng-model="vm.selectedAlarms">
|
||||
<table md-table md-row-select="vm.enableSelection" multiple="" ng-model="vm.selectedAlarms">
|
||||
<thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
|
||||
<tr md-row>
|
||||
<th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.label }}</span></th>
|
||||
<th md-column><span> </span></th>
|
||||
<th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.alarmSource.dataKeys"><span>{{ key.title }}</span></th>
|
||||
<th md-column ng-if="vm.displayDetails"><span> </span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr ng-show="vm.alarms.length" md-row md-select="alarm"
|
||||
md-select-id="id.id" md-auto-select="false" ng-repeat="alarm in vm.alarms"
|
||||
ng-click="vm.onRowClick($event, alarm)" ng-class="{'tb-current': vm.isCurrent(alarm)}">
|
||||
<td md-cell ng-repeat="key in vm.alarmSource.dataKeys"
|
||||
<td md-cell flex ng-repeat="key in vm.alarmSource.dataKeys"
|
||||
ng-style="vm.cellStyle(alarm, key)"
|
||||
ng-bind-html="vm.cellContent(alarm, key)">
|
||||
</td>
|
||||
<td md-cell class="tb-action-cell">
|
||||
<td md-cell ng-if="vm.displayDetails" class="tb-action-cell">
|
||||
<md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}"
|
||||
ng-click="vm.openAlarmDetails($event, alarm)">
|
||||
<md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon>
|
||||
@ -104,7 +104,7 @@
|
||||
layout-align="center center"
|
||||
class="no-data-found" translate>alarm.no-alarms-prompt</span>
|
||||
</md-table-container>
|
||||
<md-table-pagination md-boundary-links md-limit="vm.query.limit" md-limit-options="vm.limitOptions"
|
||||
<md-table-pagination ng-if="vm.displayPagination" md-boundary-links md-limit="vm.query.limit" md-limit-options="vm.limitOptions"
|
||||
md-page="vm.query.page" md-total="{{vm.alarmsCount}}"
|
||||
md-on-paginate="vm.onPaginate" md-page-select="vm.isGtMd">
|
||||
</md-table-pagination>
|
||||
|
||||
@ -281,7 +281,55 @@ pre.tb-highlight {
|
||||
display: flex;
|
||||
}
|
||||
table.md-table {
|
||||
&.md-row-select td.md-cell,
|
||||
&.md-row-select th.md-column {
|
||||
&:first-child {
|
||||
width: 20px;
|
||||
padding: 0 0 0 12px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
&:nth-child(n+3):nth-last-child(n+2) {
|
||||
padding: 0 28px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.md-row-select) td.md-cell,
|
||||
&:not(.md-row-select) th.md-column {
|
||||
&:first-child {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
&:nth-child(n+2):nth-last-child(n+2) {
|
||||
padding: 0 28px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
td.md-cell,
|
||||
th.md-column {
|
||||
|
||||
&:last-child {
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
table.md-table, table.md-table.md-row-select {
|
||||
tbody {
|
||||
&.md-body {
|
||||
tr {
|
||||
&.md-row:not([disabled]) {
|
||||
outline: none;
|
||||
&.tb-current, &.tb-current:hover{
|
||||
background-color: #dddddd !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td {
|
||||
&.tb-action-cell {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user