src/app/business/business.module.ts
New file @@ -0,0 +1,20 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; // Statics import 'rxjs/add/observable/throw'; // Operators import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/debounceTime'; import 'rxjs/add/operator/distinctUntilChanged'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/toPromise'; import 'rxjs/add/operator/filter'; @NgModule({ imports: [ CommonModule ] }) export class BusinessModule { } src/app/business/entity/data.ts
@@ -1,4 +1,5 @@ import { Column } from '@business/entity/grid'; import { AlarmStyle } from '@business/enum/types.enum'; export interface AreaNames { @@ -61,3 +62,33 @@ areaNames?: AreaNames|any ; organization?: Organization; } // 报警配置 export interface AlarmConfig { id?: number; organizationId?: number; createTime?: number; updateTime?: number; value: AlarmConfigValue; } export interface AlarmConfigValue { alarmMode: AlarmMode; alarmLevels:{[key: string]:AlarmSensorLevel}; } export interface AlarmSensorLevel { enable: boolean|any []; increment: number []|any[]; degression: number []|any []; } export interface AlarmMode { enable: boolean|any[]; level1: AlarmStyle[]|any[]; level2: AlarmStyle[]|any[]; level3: AlarmStyle[]|any[]; } src/app/business/entity/grid.ts
@@ -111,3 +111,8 @@ return arr; } } export interface ResultBean<T>{ code?: number; data?: T; message?: string; } src/app/business/enum/patterns.enum.ts
New file @@ -0,0 +1,3 @@ export enum patterns{ num = '\\d+(\\.\\d+)?' } src/app/business/enum/types.enum.ts
@@ -1,3 +1,7 @@ export enum Types { Date, Json } export enum AlarmStyle { //微信,邮件,短信,语音 weixin='weixin',email='email',sms='sms',voice='voice' } src/app/business/services/http/alarm-config.service.ts
New file @@ -0,0 +1,54 @@ import { _HttpClient } from '@delon/theme'; import { Injectable } from '@angular/core'; import { environment } from '@env/environment'; import { Observable } from 'rxjs/Observable'; import { ResultBean } from '@business/entity/grid'; import { AlarmConfig, AlarmConfigValue,AlarmSensorLevel, AlarmMode } from '@business/entity/data'; @Injectable() export class AlarmConfigService { private urls = { getByOid: environment.SERVER_BASH_URL + 'alarm-config/get-by-oid', save: environment.SERVER_BASH_URL + '/alarm-config/add-or-modify', }; constructor( private http: _HttpClient) { } public generateAlarmConfig(sensors: {key:string} [],alarmConfig?: AlarmConfig): AlarmConfig{ const _alarmConfig : AlarmConfig = alarmConfig==null || alarmConfig.value == null ? {value:{ alarmLevels:null, alarmMode:null }}: alarmConfig; let alarmLevels = _alarmConfig.value.alarmLevels; alarmLevels = alarmLevels ==null ?{} :alarmLevels; sensors.forEach( sensor => { const key = sensor.key; alarmLevels[key] = alarmLevels[key] == null ? { enable: false, increment: [0,0,0], degression: [0,0,0] } : alarmLevels[key]; } ); _alarmConfig.value.alarmLevels = alarmLevels; //报警方式 let alarmMode = _alarmConfig.value.alarmMode; alarmMode = alarmMode == null ? { enable: false, level1: null, level2: null, level3: null } : alarmMode; _alarmConfig.value.alarmMode = alarmMode; return _alarmConfig; } public getByOid(oid:number):Observable<ResultBean<AlarmConfig>>{ return this.http.get(this.urls.getByOid,{organizationId:oid}); } public save(data: any): Observable<ResultBean<any>> { return this.http.post(this.urls.save, data); } } src/app/business/services/http/areacode.service.ts
@@ -1,10 +1,5 @@ import { Injectable } from '@angular/core'; import { _HttpClient } from '@delon/theme/services/http/http.client'; // Statics import 'rxjs/add/observable/throw'; // Operators import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/map'; import { Observable } from 'rxjs/Observable'; import { environment } from 'environments/environment'; src/app/business/services/http/organization.service.ts
@@ -1,4 +1,4 @@ import { Organization } from '@business/entity/data'; import { Organization, AlarmConfig } from '@business/entity/data'; import { ExampleService } from '@business/services/util/example.service'; import { _HttpClient } from '@delon/theme'; import { environment } from 'environments/environment'; @@ -6,13 +6,13 @@ import { Injectable } from '@angular/core'; import { equal } from 'assert'; import { Observable } from 'rxjs/Observable'; import { PageBean } from '@business/entity/grid'; import { PageBean, ResultBean } from '@business/entity/grid'; @Injectable() export class OrganizationService { handle: 'list'|'config' = 'list'; config: {pageBean: PageBean,resultBean: ResultBean<AlarmConfig>}; data: Organization; title: '组织列表'|'组织配置' = '组织列表'; private urls = { src/app/business/services/http/sensors.service.ts
@@ -10,7 +10,7 @@ @Injectable() export class SensorsService { private urls = { edit: environment.SERVER_BASH_URL + '/sensor/page-list', list: environment.SERVER_BASH_URL + '/sensor/page-list', save: environment.SERVER_BASH_URL + '/sensor/add-or-modify', delete: environment.SERVER_BASH_URL + '/sensor/delete-by-ids' }; @@ -27,7 +27,7 @@ } const param: PageBean = {pageSize: page.pageSize, pageIndex: page.pageIndex, queryParams: example.getSqlParam(), orderByClause: orderByClause}; return this.http.get(this.urls.edit, param); return this.http.get(this.urls.list, param); } public save(data: any): Observable<any> { return this.http.post(this.urls.save, data); src/app/routes/devices/version/version-sensor-config/version-sensor-config.component.html
@@ -2,7 +2,7 @@ <div class="modal-title">配置传感器</div> </div> <nz-table #nzTable [nzDataSource]="grid.data" [nzPageSize]="8" [nzLoading]="grid.loading" [nzShowTotal]="true" > [nzLoading]="grid.loading" [nzShowTotal]="true"> <thead nz-thead> <tr> <th nz-th [nzCheckbox]="true"> src/app/routes/routes.module.ts
@@ -29,7 +29,7 @@ DashboardV1Component, DashboardAnalysisComponent, DashboardMonitorComponent, DashboardWorkplaceComponent, DashboardWorkplaceComponent ], providers: [ _HttpClient, src/app/routes/systems/organization/organization-config/organization-config.component.html
@@ -1,67 +1,104 @@ <form> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <p>111111111111111111111111111<br/> 222222222222222222222222222<br/> 333333333333333333333333333<br/> </p> <footer-toolbar errorCollect> <form nz-form [formGroup]="validateForm" (ngSubmit)="save($event,validateForm.value,validateForm.valid)" [nzLayout]="'vertical'" #f="ngForm"> <nz-card [nzBordered]="false" nzTitle="报警阀值(注意,只有部分选项有 反向三级 数值,没有可不填)"> <nz-table #nzTable [nzDataSource]="grid.data" [nzPageSize]="10" [nzLoading]="grid.loading" [nzShowTotal]="true"> <thead nz-thead> <tr> <th nz-th [nzCheckbox]="true"> <label nz-checkbox formControlName="_allChecked" [nzIndeterminate]="indeterminate" (ngModelChange)="checkAll($event)"></label> </th> <th nz-th> 传感器名称(单位) </th> <th nz-th> 一级 </th> <th nz-th> 二级 </th> <th nz-th> 三级 </th> <th nz-th> 反向一级 </th> <th nz-th> 反向二级 </th> <th nz-th> 反向三级 </th> </tr> </thead> <tbody formGroupName="alarmLevels" nz-tbody> <tr nz-tbody-tr *ngFor="let row of nzTable.data" formGroupName="{{row.key}}"> <td nz-td [nzCheckbox]="true"> <label nz-checkbox formControlName="enable"></label> </td> <td> <span> {{ row.name }}({{ row.unit }}) </span> </td> <td formArrayName="increment" *ngFor="let in of validateForm.get('alarmLevels.'+row.key+'.increment').controls; index as i"> <div nz-form-item> <div nz-form-control nzHasFeedback> <nz-input [formControlName]="i" maxlength="20" nzDisabled="{{ !f.value['alarmLevels'][row.key]['enable'] }}"></nz-input> </div> </div> </td> <td formArrayName="degression" *ngFor="let in of validateForm.get('alarmLevels.'+row.key+'.degression').controls; index as i"> <div nz-form-item> <div nz-form-control nzHasFeedback> <nz-input [formControlName]="i" maxlength="20" nzDisabled="{{ !f.value['alarmLevels'][row.key]['enable'] }}"></nz-input> </div> </div> </td> </tr> </tbody> </nz-table> </nz-card> <div [ngStyle]="{'background-color':'#f5f7fa','width':'110%','left':'-5%','height':'20px','position':'relative'}"></div> <nz-card [nzBordered]="false" nzTitle="报警方式" formGroupName="alarmMode"> <label nz-checkbox formControlName="enable"> <span>启用报警</span> </label> <div [ngStyle]="{'margin-left': '20%'}"> <br/> <br/> <br/> <div *ngFor="let i of [1,2,3]" nz-form-item nz-row class="mb-sm"> <div nz-form-label nz-col [nzSm]="3" [nzXs]="24"> <span [ngSwitch]="i"> <label *ngSwitchCase="1">一级报警方式:</label> <label *ngSwitchCase="2">二级报警方式:</label> <label *ngSwitchCase="3">三级报警方式:</label> </span> </div> <div nz-form-control nz-col [nzSpan]="12" nzHasFeedback> <nz-select formControlName="{{ 'level'+i }}" [nzMode]="'multiple'" [nzPlaceHolder]="'选择 报警方式'" [nzNotFoundContent]="'无法找到'" nzDisabled="{{ !f.value['alarmMode']['enable'] }}"> <nz-option *ngFor="let option of alarmModes" [nzLabel]="option.label" [nzValue]="option.value" [nzDisabled]="option.disabled"> </nz-option> </nz-select> </div> <br/> <br/> <br/> </div> </div> </nz-card> <footer-toolbar errorCollect> <span [ngStyle]="{'color':'red','width':'300px','margin-right':'40px'}">{{ errorMessage }}</span> <label>组织名称:</label> <span [ngStyle]="{'font-size': '16px','font-weight': 'bold','margin-right':'30px'}">{{ organization.name }}</span> <button nz-button type="button" (click)="backToList()">返回</button> <button nz-button [nzType]="'primary'" [nzLoading]="isSaving"> <span> 保存 <span *ngIf="isSaving">中</span> </span> <span> 保存 <span *ngIf="isSaving">中</span> </span> </button> </footer-toolbar> </footer-toolbar> </form> src/app/routes/systems/organization/organization-config/organization-config.component.ts
@@ -1,24 +1,265 @@ import { Organization } from '@business/entity/data'; import { Component, OnInit } from '@angular/core'; import { NzMessageService } from 'ng-zorro-antd'; import { filter } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { FormGroup, FormBuilder, FormControl, Validators, FormArray } from '@angular/forms'; import { SensorsService } from '@business/services/http/sensors.service'; import { Grid, PageBean } from '@business/entity/grid'; import { Organization, AlarmConfig, AlarmSensorLevel, AlarmConfigValue } from '@business/entity/data'; import { Component, OnInit, OnDestroy } from '@angular/core'; import { OrganizationService } from '@business/services/http/organization.service'; import { _HttpClient } from '@delon/theme'; import { AlarmConfigService } from '@business/services/http/alarm-config.service'; import { AlarmStyle } from '@business/enum/types.enum'; import { patterns } from '@business/enum/patterns.enum'; @Component({ selector: 'app-organization-config', templateUrl: './organization-config.component.html', styles: [] styles: [], providers: [AlarmConfigService] }) export class OrganizationConfigComponent implements OnInit { organization: Organization; export class OrganizationConfigComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.backToList(); } private organization: Organization; grid: Grid<object> = new Grid<object>(null); validateForm: FormGroup; constructor( private organizationService: OrganizationService ) { console.log(this.organizationService.data); private organizationService: OrganizationService, private sensorsService: SensorsService, private alarmConfigService: AlarmConfigService, private formBuilder: FormBuilder, private http: _HttpClient, public msgSrv: NzMessageService ) { this.organization = this.organizationService.data; } ngOnInit() { this.load(); } load(reload: boolean = false) { if (reload) { this.grid.pageIndex = 1; } // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { this.grid.loading = true; }, 1); let pageBean = this.organizationService.config.pageBean; let resultBean = this.organizationService.config.resultBean; resultBean = resultBean == null ? {} : resultBean; if (pageBean != null && pageBean.data != null) { this.grid.initData(pageBean); let alarmConfig: AlarmConfig = null; if (resultBean == null || resultBean.data == null) { alarmConfig = this.alarmConfigService.generateAlarmConfig(pageBean.data); } else { // 防止 新增 传感器 alarmConfig = this.alarmConfigService.generateAlarmConfig(pageBean.data, resultBean.data); } const alarmConfigValue = alarmConfig.value; const alarmLevels = alarmConfigValue.alarmLevels; let alarmLevelsGroupsConfig = {}; Object.keys(alarmLevels).forEach(key => { let increment = alarmLevels[key].increment; increment = increment == null || increment.length != 3 ? [0, 0, 0] : increment; const incrementArray = this.formBuilder.array([ [increment[0], Validators.pattern(patterns.num)], [increment[1], Validators.pattern(patterns.num)], [increment[2], Validators.pattern(patterns.num)] ]); let degression = alarmLevels[key].degression; degression = degression == null || degression.length != 3 ? [0, 0, 0] : degression; const degressionArray = this.formBuilder.array([ [degression[0], Validators.pattern(patterns.num)], [degression[1], Validators.pattern(patterns.num)], [degression[2], Validators.pattern(patterns.num)] ]); const alarmSensorGroup = this.formBuilder.group( { enable: [alarmLevels[key].enable], increment: incrementArray, degression: degressionArray, }, { validator: this.alarmLevelValidator } ); alarmSensorGroup.get('enable').valueChanges.subscribe( (value: any) => { if (this._allCheckTriggers <= 0) { this.refreshIndeterminate(); } else { this._allCheckTriggers--; } } ); alarmLevelsGroupsConfig[key] = alarmSensorGroup; }); this.alarmModes.push( { label: '邮件', value: AlarmStyle.email, disabled: false }, { label: '短信', value: AlarmStyle.sms, disabled: false }, { label: '语音', value: AlarmStyle.voice, disabled: false }, { label: '微信', value: AlarmStyle.weixin, disabled: false } ); const alarmMode = alarmConfigValue.alarmMode; this.validateForm = this.formBuilder.group({ alarmLevels: this.formBuilder.group(alarmLevelsGroupsConfig), alarmMode: this.formBuilder.group( { enable: [alarmMode.enable], level1: [alarmMode.level1], level2: [alarmMode.level2], level3: [alarmMode.level3] } ), '_id':alarmConfig.id, '_allChecked': [] }); this.validateForm.statusChanges.subscribe( item => { this.setErrorMessage(); } ) this.refreshIndeterminate(); } // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { this.grid.loading = false; }, 1); } alarmModes: { label: string, value: AlarmStyle, disabled: boolean }[] = []; backToList() { this.organizationService.handle = 'list'; this.organizationService.title = '组织列表'; } indeterminate: boolean; checkAll(param) { const keys = this.grid.data.map( item => { return item['key']; } ); this._allCheckTriggers = 0; keys.forEach( (key: string) => { this._allCheckTriggers++; this.validateForm.get('alarmLevels.' + key + '.enable').setValue(param); } ); this.refreshIndeterminate(); } private _allCheckTriggers: number = 0; refreshIndeterminate() { const keys = this.grid.data.map( item => { return item['key']; } ); const allChecked = keys.every(key => this.validateForm.get('alarmLevels.' + key + '.enable').value); const allUnChecked = keys.every(key => !this.validateForm.get('alarmLevels.' + key + '.enable').value); this.indeterminate = (!allChecked) && (!allUnChecked); } save($event, value, valid) { $event.preventDefault(); if (valid) { const data:AlarmConfig = { id:value._id, organizationId:this.organization.id, value:value } this.alarmConfigService.save(data).subscribe( result => { if(result!=null&&result.code==1){ this.msgSrv.success(this.organization.name+' 配置成功!'); this.backToList(); } } ); } } setErrorMessage(){ this.errorMessage = ''; const errObj = this.getLastError(this.validateForm); if(errObj!=null&&Object.keys(errObj).length>0){ if(errObj['increment']){ this.errorMessage = '一级 二级 三级 依次递增'; } else if(errObj['degression']){ this.errorMessage = '反向一级 反向二级 反向三级 依次递减'; } else if(errObj['pattern']!=null&&errObj['pattern']['requiredPattern'] == '^'+patterns.num.toString()+'$'){ this.errorMessage = '阀值只能为数字'; } } } errorMessage:string = ''; private alarmLevelValidator = (control: FormControl): { [s: string]: boolean } => { let result = {}; const i0 = control.get("increment.0"); const i1 = control.get("increment.1"); const i2 = control.get("increment.2"); if ((i0.value != 0 || i1.value != 0 || i2.value != 0)&&(i0.value >= i1.value||i1.value >= i2.value)) { i0.setErrors({ increment: true }); i0.markAsDirty(); i1.setErrors({ increment: true }); i1.markAsDirty(); i2.setErrors({ increment: true }); i2.markAsDirty(); result["increment"] = true; }else{ if(i0.hasError('increment')&&Object.keys(i0.errors).length==1){ i0.setErrors(null); } if(i1.hasError('increment')&&Object.keys(i1.errors).length==1){ i1.setErrors(null); } if(i2.hasError('increment')&&Object.keys(i2.errors).length==1){ i2.setErrors(null); } } const d0 = control.get("degression.0"); const d1 = control.get("degression.1"); const d2 = control.get("degression.2"); if ((d0.value != 0 || d1.value != 0 || d2.value != 0)&&(d1.value >= d0.value||d2.value >= d1.value)) { d0.setErrors({ degression: true }); d0.markAsDirty(); d1.setErrors({ degression: true }); d1.markAsDirty(); d2.setErrors({ degression: true }); d2.markAsDirty(); result["degression"] = true; }else{ if(d0.hasError('degression')&&Object.keys(d0.errors).length==1){ d0.setErrors(null); } if(d1.hasError('degression')&&Object.keys(d1.errors).length==1){ d1.setErrors(null); } if(d2.hasError('degression')&&Object.keys(d2.errors).length==1){ d2.setErrors(null); } } return Object.keys(result).length == 0?null:result; }; getLastError(control:FormGroup|FormArray){ if(control.errors!=null){ return control.errors; }else{ const controls = Object.values(control.controls); for(let i = controls.length-1;i>=0;i--){ const c = controls[i]; if(c.errors!=null){ return c.errors; }else if(c instanceof FormArray||c instanceof FormGroup){ const result = this.getLastError(c); if(result != null) { return result; } } } } } } src/app/routes/systems/organization/organization-list/organization-list.component.ts
@@ -1,3 +1,5 @@ import { AlarmConfigService } from '@business/services/http/alarm-config.service'; import { SensorsService } from '@business/services/http/sensors.service'; import { Router } from '@angular/router'; import { ModalHelper } from '@delon/theme'; import { NzModalService, NzMessageService } from 'ng-zorro-antd'; @@ -8,11 +10,13 @@ import { Subject } from 'rxjs/Subject'; import { Types } from '@business/enum/types.enum'; import { OrganizationEditComponent } from 'app/routes/systems/organization/organization-edit/organization-edit.component'; import { zip } from 'rxjs/observable/zip'; @Component({ selector: 'app-organization-list', templateUrl: './organization-list.component.html', styles: [] styles: [], providers: [AlarmConfigService] }) export class OrganizationListComponent implements OnInit { private organization: Organization; @@ -89,6 +93,8 @@ public msgSrv: NzMessageService, private modalHelper: ModalHelper, private router: Router, private sensorsService:SensorsService, private alarmConfigService:AlarmConfigService, ) {} ngOnInit() { @@ -205,8 +211,20 @@ this.load(); } config(row) { // 延时加载避免ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { this.grid.loading = true; }, 1); zip( this.sensorsService.getPagingList({pageIndex:0,pageSize:0}, null), this.alarmConfigService.getByOid( row.id ) ).subscribe(([pageBean, resultBean]) => { console.log(pageBean); this.grid.loading = false; this.organizationService.handle = 'config' ; this.organizationService.data = row; this.organizationService.config = {pageBean,resultBean}; this.organizationService.title = '组织配置'; }); } } src/app/routes/systems/organization/organization.component.html
@@ -1,4 +1,6 @@ <pro-header [title]="organizationService.title"></pro-header> <pro-header [title]="organizationService.title"> </pro-header> <nz-card [nzBordered]="false" [nzNoHovering]="true"> <ng-template #body> <app-organization-list *ngIf="organizationService.handle=='list'"></app-organization-list> src/app/routes/systems/organization/organization.component.ts
@@ -10,7 +10,6 @@ }) export class OrganizationComponent implements OnInit { ngOnInit(): void { } constructor( private organizationService: OrganizationService src/app/routes/systems/systems.module.ts
@@ -15,6 +15,8 @@ import { AreacodeService } from '@business/services/http/areacode.service'; import { OrganizationConfigComponent } from './organization/organization-config/organization-config.component'; import { OrganizationListComponent } from './organization/organization-list/organization-list.component'; import { SensorsService } from '@business/services/http/sensors.service'; import { BusinessModule } from '@business/business.module'; const routes: Routes = [ { @@ -34,7 +36,8 @@ PipeModule, CommonModule, SharedModule, RouterModule.forChild(routes) RouterModule.forChild(routes), BusinessModule ], declarations: [ ...COMPONENTS_NOROUNT, @@ -44,7 +47,7 @@ OrganizationConfigComponent, OrganizationListComponent ], providers: [ToolsService, OrganizationService, _HttpClient, FormBuilder, AreacodeService], providers: [ToolsService, SensorsService,OrganizationService, _HttpClient, FormBuilder, AreacodeService], entryComponents: COMPONENTS_NOROUNT }) export class SystemsModule { }