| New file | 
|  |  |  | 
|---|
|  |  |  | import { Component, OnInit, ViewChild } from '@angular/core'; | 
|---|
|  |  |  | import { _HttpClient } from '@delon/theme'; | 
|---|
|  |  |  | import { Sensor, Device, DataCondition, MonitorPoint, Organization, LineChartCriteria } from '@business/entity/data'; | 
|---|
|  |  |  | import { TimeUnits, AreaRange, ResultCode, DeviceDimension } from '@business/enum/types.enum'; | 
|---|
|  |  |  | import { SensorsService } from '@business/services/http/sensors.service'; | 
|---|
|  |  |  | import { PageBean, ResultBean, Grid } from '@business/entity/grid'; | 
|---|
|  |  |  | import { NzTreeComponent } from 'ng-tree-antd'; | 
|---|
|  |  |  | import * as moment from 'moment'; | 
|---|
|  |  |  | import { ExampleService } from '@business/services/util/example.service'; | 
|---|
|  |  |  | import { DeviceService } from '@business/services/http/device.service'; | 
|---|
|  |  |  | import { CascaderOption } from 'ng-zorro-antd/src/cascader/nz-cascader.component'; | 
|---|
|  |  |  | import { AreacodeService } from '@business/services/http/areacode.service'; | 
|---|
|  |  |  | import { MonitorPointService } from '@business/services/http/monitor-point.service'; | 
|---|
|  |  |  | import { NzMessageService } from 'ng-zorro-antd'; | 
|---|
|  |  |  | import * as $ from 'jquery'; | 
|---|
|  |  |  | import { DateService } from '@business/services/util/date.service'; | 
|---|
|  |  |  | import { Subject } from 'rxjs/Subject'; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @Component({ | 
|---|
|  |  |  | selector: 'app-data-audit', | 
|---|
|  |  |  | templateUrl: './data-audit.component.html', | 
|---|
|  |  |  | styleUrls: ['./data-audit.component.less'], | 
|---|
|  |  |  | }) | 
|---|
|  |  |  | export class DataAuditComponent implements OnInit { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // aqi六项排序 | 
|---|
|  |  |  | private aqiSort = { | 
|---|
|  |  |  | e1: 1, | 
|---|
|  |  |  | e2: 2, | 
|---|
|  |  |  | e10: 3, | 
|---|
|  |  |  | e11: 4, | 
|---|
|  |  |  | e15: 5, | 
|---|
|  |  |  | e16: 6 | 
|---|
|  |  |  | }; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public tableWidth = 2800; | 
|---|
|  |  |  | // private tableWidthOption = { | 
|---|
|  |  |  | //     'MONTH': 1400, | 
|---|
|  |  |  | //     'DAY': 2800, | 
|---|
|  |  |  | //     'HOUR': 2100, | 
|---|
|  |  |  | //     'MINUTE': 5000 | 
|---|
|  |  |  | // }; | 
|---|
|  |  |  | public expandForm: boolean; // 搜索区 展开控制 | 
|---|
|  |  |  | // public sensors: any[] = []; | 
|---|
|  |  |  | public sensorOptions: any[] = []; | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 数据类型,下拉列表数据 | 
|---|
|  |  |  | * 注意,年度类型,对应查询数据单位为月,以此类推 | 
|---|
|  |  |  | * @memberof QueryComponent | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public timeUnitOptions = [ | 
|---|
|  |  |  | {label: '年', value: TimeUnits.MONTH}, | 
|---|
|  |  |  | {label: '月', value: TimeUnits.DAY}, | 
|---|
|  |  |  | {label: '日', value: TimeUnits.HOUR}, | 
|---|
|  |  |  | {label: '时', value: TimeUnits.MINUTE}, | 
|---|
|  |  |  | ]; | 
|---|
|  |  |  | public timeUnit: {label: string, value: TimeUnits} = this.timeUnitOptions[2]; | 
|---|
|  |  |  | // 默认时间为昨天,今天无数据 | 
|---|
|  |  |  | public actualTime: Date = moment().subtract(1, 'days').toDate(); | 
|---|
|  |  |  | public actualYearOptions: number []; | 
|---|
|  |  |  | public isCollapse = false; | 
|---|
|  |  |  | public isChartCollapse = false; | 
|---|
|  |  |  | // 显示使用 | 
|---|
|  |  |  | public _areas: { label: string, value: string }[] = new Array(); | 
|---|
|  |  |  | public  grid: Grid<{sensor: Sensor, data: string [], weight?: number}> = new Grid(); | 
|---|
|  |  |  | // public grid.data: {sensor: Sensor, data: string [], weight?: number} [] = []; | 
|---|
|  |  |  | //  样式控制,设备名称列冻结偏移 | 
|---|
|  |  |  | public sensorNameScrollLeft = 0; | 
|---|
|  |  |  | public sensorNameScrollTop = 0; | 
|---|
|  |  |  | @ViewChild(NzTreeComponent)  private tree: NzTreeComponent; | 
|---|
|  |  |  | private _timeType: {showTime: boolean|{[key: string]: Function|boolean}, mode:  'month' | 'day', dateFormat: string  } = {showTime: false, mode: 'day', dateFormat: 'YYYY年MM月DD日'}; | 
|---|
|  |  |  | // 用key-value方式,暂存监测项目 | 
|---|
|  |  |  | private _sensors: {[key: string]: string} = {}; | 
|---|
|  |  |  | private _sensorNames: string; | 
|---|
|  |  |  | get sensorNames(): string { | 
|---|
|  |  |  | return this._sensorNames; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public toggleCollapse() { | 
|---|
|  |  |  | this.isCollapse = !this.isCollapse; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public toggleChartCollapse() { | 
|---|
|  |  |  | // 打开的时候 | 
|---|
|  |  |  | if (this.isChartCollapse && !!this.echartsIntance) { | 
|---|
|  |  |  | this.reloadChart(); | 
|---|
|  |  |  | this.switchSensor(this.chartSelectedIndex); | 
|---|
|  |  |  | // this.chartLoading = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.isChartCollapse = !this.isChartCollapse; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public deviceOptions: Device[] = []; | 
|---|
|  |  |  | public dataCondition: DataCondition = {areaRangeId: 320583, areaRange: AreaRange.AREA, deviceDimension: DeviceDimension.NONE}; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public dimensionOptions: MonitorPoint[] = []; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | get actualYear(): number { | 
|---|
|  |  |  | return this.actualTime.getFullYear(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | set actualYear(year) { | 
|---|
|  |  |  | this.actualTime.setFullYear(Number(year)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | dimensionsChange(text?: string) { | 
|---|
|  |  |  | switch (this.dimensionItem.value) { | 
|---|
|  |  |  | case DeviceDimension.MONITORPOINT: | 
|---|
|  |  |  | this.monitorPointsChange(text); break; | 
|---|
|  |  |  | case DeviceDimension.PROFESSION: | 
|---|
|  |  |  | this.professionsChange(text); break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | professionsChange(text?: string) { | 
|---|
|  |  |  | this.http.get<ResultBean<any[]>>('profession/getall').subscribe( | 
|---|
|  |  |  | result => { | 
|---|
|  |  |  | if (!!result.code) { | 
|---|
|  |  |  | this.dimensionOptions = result.data; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | monitorPointsChange(text?: string) { | 
|---|
|  |  |  | const pageBean: PageBean = { pageIndex: 0, pageSize: 20 }; | 
|---|
|  |  |  | const example = new ExampleService(); | 
|---|
|  |  |  | let areaName = ''; | 
|---|
|  |  |  | switch (this._areas.length) { | 
|---|
|  |  |  | case 1: | 
|---|
|  |  |  | areaName = 'provinceCode'; break; | 
|---|
|  |  |  | case 2: | 
|---|
|  |  |  | areaName = 'cityCode'; break; | 
|---|
|  |  |  | case 3: | 
|---|
|  |  |  | areaName = 'areaCode'; break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (!!text) { | 
|---|
|  |  |  | example.or().andLike({ name: 'name', value: '%' + text + '%' }) | 
|---|
|  |  |  | .andEqualTo({ name: areaName, value:  Number(this._areas.slice(-1).pop().value) }); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | example.or() | 
|---|
|  |  |  | .andEqualTo({ name: areaName, value:  Number(this._areas.slice(-1).pop().value) }); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.monitorPointService.getPageByExample(pageBean, example).subscribe( | 
|---|
|  |  |  | (res: PageBean) => { | 
|---|
|  |  |  | if (!!res && !!res.data) { | 
|---|
|  |  |  | this.dimensionOptions = res.data; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | devicesChange(text?: string) { | 
|---|
|  |  |  | if (!!this.deviceDimension) { | 
|---|
|  |  |  | const example = new ExampleService(); | 
|---|
|  |  |  | const deviceDimensionProperty = | 
|---|
|  |  |  | this.dimensionItem.value === DeviceDimension.MONITORPOINT ? 'monitorPointId' : 'professionId'; | 
|---|
|  |  |  | if (!!text) { | 
|---|
|  |  |  | example.or().andEqualTo({ name: deviceDimensionProperty, value: this.deviceDimension.id }) | 
|---|
|  |  |  | .andLike({name: 'text', value: text}); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | example.or().andEqualTo({ name: deviceDimensionProperty, value: this.deviceDimension.id }); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.deviceService.getPageByExample(null, example).subscribe( | 
|---|
|  |  |  | (res: PageBean) => { | 
|---|
|  |  |  | if (!!res && !!res.data) { | 
|---|
|  |  |  | this.deviceOptions = res.data; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | this.deviceOptions = []; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 报表属性 | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | public chartLoading: boolean; | 
|---|
|  |  |  | public chartOption; | 
|---|
|  |  |  | public echartsIntance; | 
|---|
|  |  |  | public chartSelectedIndex = 0; | 
|---|
|  |  |  | public onChartInit(e): void { | 
|---|
|  |  |  | this.echartsIntance = e; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 放在 sensor 选项加载完后调用 | 
|---|
|  |  |  | * @param sensorKeys | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private mockSelectSensors(sensorKeys: string[]) { | 
|---|
|  |  |  | // 模拟选择 aqi 6个参数 | 
|---|
|  |  |  | // Object.keys(this.aqiSort) | 
|---|
|  |  |  | sensorKeys.forEach( key => { | 
|---|
|  |  |  | const sensor =  this.sensorOptions[0].children.find( | 
|---|
|  |  |  | sen => sen.sensorKey === key | 
|---|
|  |  |  | ) | 
|---|
|  |  |  | if (!!sensor) { | 
|---|
|  |  |  | sensor['halfChecked'] = false; | 
|---|
|  |  |  | sensor['checked'] = true; | 
|---|
|  |  |  | const event = {node: {data: sensor}}; | 
|---|
|  |  |  | this.onSensorSelect(event); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | constructor( | 
|---|
|  |  |  | private http: _HttpClient, | 
|---|
|  |  |  | private sensorsService: SensorsService, | 
|---|
|  |  |  | private deviceService: DeviceService, | 
|---|
|  |  |  | private areacodeService: AreacodeService, | 
|---|
|  |  |  | private monitorPointService: MonitorPointService, | 
|---|
|  |  |  | private msg: NzMessageService, | 
|---|
|  |  |  | private dateService: DateService, | 
|---|
|  |  |  | ) { } | 
|---|
|  |  |  | ngOnInit() { | 
|---|
|  |  |  | this.initPage(); | 
|---|
|  |  |  | // 监测项目 tree click 事件 | 
|---|
|  |  |  | this.treeClickStream.debounceTime(1).subscribe( event => { | 
|---|
|  |  |  | const data = event.node.data; | 
|---|
|  |  |  | // console.log(data); | 
|---|
|  |  |  | if (data.id === -1) { | 
|---|
|  |  |  | const children = data.children; | 
|---|
|  |  |  | // 全选状态 | 
|---|
|  |  |  | if (!data.halfChecked && data.checked) { | 
|---|
|  |  |  | // 全取消操作 | 
|---|
|  |  |  | data['checked'] = false; | 
|---|
|  |  |  | data['halfChecked'] = false; | 
|---|
|  |  |  | children.forEach(element => { | 
|---|
|  |  |  | element['checked'] = false; | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | this._sensors = {}; | 
|---|
|  |  |  | // 未选 或者 半选状态 | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | // 全选操作 | 
|---|
|  |  |  | children.forEach(element => { | 
|---|
|  |  |  | element['checked'] = true; | 
|---|
|  |  |  | this._sensors [element.id] = element.sensorKey; | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | data['checked'] = true; | 
|---|
|  |  |  | data['halfChecked'] = false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | const parentData = event.node.parent.data; | 
|---|
|  |  |  | data['checked'] = !data['checked']; | 
|---|
|  |  |  | if (data['checked']) { | 
|---|
|  |  |  | this._sensors [data.id] = data.sensorKey; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | delete this._sensors [data.id]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const length = Object.keys(this._sensors).length; | 
|---|
|  |  |  | parentData['halfChecked'] = length > 0 | 
|---|
|  |  |  | && this.sensorOptions[0].children.length > length; | 
|---|
|  |  |  | parentData['checked'] = !parentData['halfChecked'] && length !== 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.reloadSensorNames(); | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private initPage() { | 
|---|
|  |  |  | this.sensorsService.getPagingList(null, null).subscribe( | 
|---|
|  |  |  | (res: PageBean) => { | 
|---|
|  |  |  | this.sensorOptions.push({id: -1, name: '全部', isExpanded: true, children: res.data}); | 
|---|
|  |  |  | this.mockSelectSensors(Object.keys(this.aqiSort)); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | this.actualYearOptions = this.newArray(this.actualYear - 9, 10).map(item => Number(item)).reverse(); | 
|---|
|  |  |  | // 省市区 初始值 | 
|---|
|  |  |  | this.http.get('organization/get-my-org').subscribe( | 
|---|
|  |  |  | (res: ResultBean<Organization>) => { | 
|---|
|  |  |  | if (res.code === ResultCode.SUCCESS) { | 
|---|
|  |  |  | const org = res.data; | 
|---|
|  |  |  | const areas = new Array(3); | 
|---|
|  |  |  | if (!!org.areaNames) { | 
|---|
|  |  |  | const areaNames = org.areaNames; | 
|---|
|  |  |  | Object.keys(areaNames).forEach( | 
|---|
|  |  |  | key => { | 
|---|
|  |  |  | const value = areaNames[key]; | 
|---|
|  |  |  | if ( value != null) { | 
|---|
|  |  |  | switch (key) { | 
|---|
|  |  |  | case 'provinceName' : | 
|---|
|  |  |  | areas[0] = {label : value, value: org.provinceCode }; break; | 
|---|
|  |  |  | case 'cityName' : | 
|---|
|  |  |  | areas[1] = {label : value, value: org.cityCode }; break; | 
|---|
|  |  |  | case 'areaName' : | 
|---|
|  |  |  | areas[2] = {label : value, value: org.areaCode }; break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | for (let i = 0; i < areas.length; i++) { | 
|---|
|  |  |  | switch (i) { | 
|---|
|  |  |  | case  0: | 
|---|
|  |  |  | areas[0] = {label : '江苏省', value: 320000 }; break; | 
|---|
|  |  |  | case  1: | 
|---|
|  |  |  | areas[1] = {label : '苏州市', value: 320500 }; break; | 
|---|
|  |  |  | case  2: | 
|---|
|  |  |  | areas[2] = {label : '昆山市', value: 320583 }; break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.setAreasData(areas); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | this.dimensionItem = this.dimensions[0]; | 
|---|
|  |  |  | // this.dimensionsChange(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public get sensorsLength(): number { | 
|---|
|  |  |  | return this.sensorOptions.length > 0 ? this.sensorOptions[0].children.length : 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public  get  sensorsSelect(): number { | 
|---|
|  |  |  | return Object.keys(this._sensors).length; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public areaLazyLoad(event: { option: CascaderOption, index: number, resolve: (children: CascaderOption[]) => void, reject: () => void }) { | 
|---|
|  |  |  | const index = event['index']; | 
|---|
|  |  |  | const option = event.option; | 
|---|
|  |  |  | switch (index) { | 
|---|
|  |  |  | case -1: | 
|---|
|  |  |  | this.areacodeService.getProvinces().subscribe( | 
|---|
|  |  |  | (res: { label: string, value: string }[]) => { | 
|---|
|  |  |  | event.resolve(res); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | case 0: | 
|---|
|  |  |  | this.areacodeService.getCities(option.value).subscribe( | 
|---|
|  |  |  | (res: { label: string, value: string }[]) => { | 
|---|
|  |  |  | event.resolve(res); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | case 1: | 
|---|
|  |  |  | this.areacodeService.getAreas(option.value).subscribe( | 
|---|
|  |  |  | (res: { label: string, value: string }[]) => { | 
|---|
|  |  |  | event.resolve(res); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 第一步,省市区 赋值变 并 改变监控站点选项 | 
|---|
|  |  |  | public  setAreasData(areas: {label: string, value: string}[] ) { | 
|---|
|  |  |  | let isChanged = false; | 
|---|
|  |  |  | isChanged = areas.some( (item , index: number) => { | 
|---|
|  |  |  | // this._areas[index] 为null 改变 监控站点选项 | 
|---|
|  |  |  | return this._areas.length < areas.length | 
|---|
|  |  |  | || !this._areas[index] | 
|---|
|  |  |  | || this._areas[index].value !== item.value; | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | if ( isChanged ) { | 
|---|
|  |  |  | this._areas = areas; | 
|---|
|  |  |  | this.deviceDimension = null; | 
|---|
|  |  |  | if (this.dimensionItem.value === DeviceDimension.MONITORPOINT) { | 
|---|
|  |  |  | this.dimensionsChange(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 第二步 设置 监控站点, 值变 改变设备选项,值为null 置空设备选项和设备值 | 
|---|
|  |  |  | public dimensionItem: {label: string, value: DeviceDimension}; | 
|---|
|  |  |  | public dimensions: {label: string, value: DeviceDimension}[] = [ | 
|---|
|  |  |  | {label: '监控站点', value: DeviceDimension.MONITORPOINT}, | 
|---|
|  |  |  | {label: '监控行业', value: DeviceDimension.PROFESSION} | 
|---|
|  |  |  | ]; | 
|---|
|  |  |  | public get otherDimensions(): {label: string, value: DeviceDimension}[] { | 
|---|
|  |  |  | return  this.dimensions.filter( | 
|---|
|  |  |  | item => { | 
|---|
|  |  |  | return item.value !== this.dimensionItem.value; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public selectDimension(option: {label: string, value: DeviceDimension}) { | 
|---|
|  |  |  | this.dimensionItem = option; | 
|---|
|  |  |  | this.dimensionsChange(); | 
|---|
|  |  |  | this._deviceDimension = null; | 
|---|
|  |  |  | // 清空设备选项 | 
|---|
|  |  |  | this.device = null; | 
|---|
|  |  |  | this.deviceOptions = null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public _deviceDimension: {id: number, name: string}; | 
|---|
|  |  |  | get deviceDimension(): {id: number, name: string} { | 
|---|
|  |  |  | return this._deviceDimension; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | set deviceDimension(value) { | 
|---|
|  |  |  | if (!!value) { | 
|---|
|  |  |  | // 值变 改变设备选项 | 
|---|
|  |  |  | if (this._deviceDimension !== value) { | 
|---|
|  |  |  | this._deviceDimension = value; // 此处不能提前也不能放后,设备改变要调用 | 
|---|
|  |  |  | this._device = null; // 设备值清空 | 
|---|
|  |  |  | this.devicesChange(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 值为null 置空设备选项和设备值 | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | this._deviceDimension = null; | 
|---|
|  |  |  | this.device = null; | 
|---|
|  |  |  | this.deviceOptions = null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 第三步 设置 监控站点 | 
|---|
|  |  |  | public _device: Device; | 
|---|
|  |  |  | set device(val: Device) { | 
|---|
|  |  |  | this._device = val; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | get device(): Device { | 
|---|
|  |  |  | return this._device; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * 获取 传感器名称 | 
|---|
|  |  |  | * | 
|---|
|  |  |  | * @readonly | 
|---|
|  |  |  | * @type {string} | 
|---|
|  |  |  | * @memberof QueryComponent | 
|---|
|  |  |  | */ | 
|---|
|  |  |  | private treeClickStream: Subject<any> = new Subject<any>(); | 
|---|
|  |  |  | public onTreeClickSelect(event): void { | 
|---|
|  |  |  | this.treeClickStream.next(event); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public onSensorSelect(event): void { | 
|---|
|  |  |  | const data = event.node.data; | 
|---|
|  |  |  | if (data.id === -1 && data.halfChecked === false) { | 
|---|
|  |  |  | if (!!data.checked) { | 
|---|
|  |  |  | this.sensorOptions[0].children.forEach( | 
|---|
|  |  |  | sensor => { | 
|---|
|  |  |  | this._sensors [sensor.id] = sensor.sensorKey; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | this._sensors = {}; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | if (!!data.checked) { | 
|---|
|  |  |  | this._sensors [data.id] = data.sensorKey; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | delete this._sensors[data.id]; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.reloadSensorNames(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private reloadSensorNames(): void { | 
|---|
|  |  |  | // 异步提升展现速度 | 
|---|
|  |  |  | setTimeout(() => { | 
|---|
|  |  |  | this._sensorNames = ''; | 
|---|
|  |  |  | const sensorNameList = Object.keys(this._sensors).map( | 
|---|
|  |  |  | id => { | 
|---|
|  |  |  | const sensor =  this.sensorOptions[0].children.find(item => { | 
|---|
|  |  |  | return Number(id) === Number(item.id)   ; | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | return sensor.name; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | this._sensorNames = sensorNameList.join(', '); | 
|---|
|  |  |  | }, 1); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public setTimeUnit(val: {label: string, value: TimeUnits} ) { | 
|---|
|  |  |  | switch (val.value) { | 
|---|
|  |  |  | // YYYY-MM-DD HH:mm:ss | 
|---|
|  |  |  | case TimeUnits.MONTH: | 
|---|
|  |  |  | this._timeType.dateFormat = 'YYYY年'; | 
|---|
|  |  |  | // this._timeType.dateFormat = 'YYYY-MM'; | 
|---|
|  |  |  | // this._timeType.mode = 'month'; | 
|---|
|  |  |  | // this._timeType.endShowTime = null; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case TimeUnits.DAY: | 
|---|
|  |  |  | this._timeType.dateFormat = 'YYYY年MM月'; | 
|---|
|  |  |  | this._timeType.mode = 'month'; | 
|---|
|  |  |  | this._timeType.showTime = false; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case TimeUnits.HOUR: | 
|---|
|  |  |  | this._timeType.dateFormat = 'YYYY年MM月DD日'; | 
|---|
|  |  |  | this._timeType.mode = 'day'; | 
|---|
|  |  |  | this._timeType.showTime = false; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | case TimeUnits.MINUTE: | 
|---|
|  |  |  | this._timeType.dateFormat = 'YYYY年MM月DD日 HH时'; | 
|---|
|  |  |  | this._timeType.mode = 'day'; | 
|---|
|  |  |  | this._timeType.showTime = { | 
|---|
|  |  |  | nzHideDisabledOptions: true, | 
|---|
|  |  |  | nzDisabledHours: () => { | 
|---|
|  |  |  | return []; | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | nzDisabledMinutes: (h) => { | 
|---|
|  |  |  | return this.newArray(60).slice(1).map( | 
|---|
|  |  |  | item => Number(item) | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | nzDisabledSeconds: () => { | 
|---|
|  |  |  | return this.newArray(60).slice(1).map( | 
|---|
|  |  |  | item => Number(item) | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private newArray = (startOrLen: number, len?: number, prefix?: string, suffix?: string) => { | 
|---|
|  |  |  | const result = []; | 
|---|
|  |  |  | const s = !!len ? startOrLen : 0; | 
|---|
|  |  |  | len = !!len ? len : startOrLen; | 
|---|
|  |  |  | suffix = !!suffix ? suffix : ''; | 
|---|
|  |  |  | prefix = !!prefix ? prefix :  ''; | 
|---|
|  |  |  | for (let i = s; result.length < len; i++) { | 
|---|
|  |  |  | result.push(prefix + i + suffix); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return result; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public loadGrid(): void { | 
|---|
|  |  |  | // 数据检查 | 
|---|
|  |  |  | const sensors = Object.values(this._sensors); | 
|---|
|  |  |  | if ( sensors.length === 0 ) { | 
|---|
|  |  |  | this.msg.error(' 监测项目 不能为空'); | 
|---|
|  |  |  | return ; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.grid.loading = true; | 
|---|
|  |  |  | this.sensorNameScrollLeft = 0; | 
|---|
|  |  |  | this.grid.data = []; | 
|---|
|  |  |  | const start: Date = this.getPeriodDate(this.actualTime , 'start'); | 
|---|
|  |  |  | const end: Date = this.getPeriodDate(this.actualTime , 'end'); | 
|---|
|  |  |  | this.dataCondition['actualTime'] = null; | 
|---|
|  |  |  | this.dataCondition['timeUnits'] = this.timeUnit.value; | 
|---|
|  |  |  | // 设置区域值 | 
|---|
|  |  |  | const dimValue: number = !!this._deviceDimension ? this._deviceDimension.id : null; | 
|---|
|  |  |  | const devValue: number = !!this._device ? this._device.id : null; | 
|---|
|  |  |  | const areasData = [ devValue, dimValue, Number(this._areas.slice(-1).pop().value)]; | 
|---|
|  |  |  | if (!!this.deviceDimension) { | 
|---|
|  |  |  | this.dataCondition.dimensionValue = this._deviceDimension.id; | 
|---|
|  |  |  | this.dataCondition.deviceDimension = this.dimensionItem.value; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | for (let index = 0 ; index < areasData.length ; index++) { | 
|---|
|  |  |  | const item = areasData[index]; | 
|---|
|  |  |  | if ( item !== null ) { | 
|---|
|  |  |  | this.dataCondition.areaRangeId = item; | 
|---|
|  |  |  | switch (index) { | 
|---|
|  |  |  | case 0: this.dataCondition.areaRange = AreaRange.DEVICE; break; | 
|---|
|  |  |  | case 1: | 
|---|
|  |  |  | // 维度为监控点 | 
|---|
|  |  |  | if (this.dimensionItem.value === DeviceDimension.MONITORPOINT) { | 
|---|
|  |  |  | this.dataCondition.areaRange = AreaRange.MONITORPOINT; | 
|---|
|  |  |  | // 维度为行业 | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | // 重新标定区域范围 | 
|---|
|  |  |  | this.switchAreas(); | 
|---|
|  |  |  | // 重新设置区域值 | 
|---|
|  |  |  | this.dataCondition.areaRangeId = areasData.slice(-1).pop(); | 
|---|
|  |  |  | } break; | 
|---|
|  |  |  | case 2: | 
|---|
|  |  |  | this.switchAreas(); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const dataConditions = [this.dataCondition]; | 
|---|
|  |  |  | const lineChartCriteria: LineChartCriteria = { | 
|---|
|  |  |  | sensorKeys: sensors, | 
|---|
|  |  |  | timePeriod: {startTime: start , endTime: end, timeUnits: this.timeUnit.value }, | 
|---|
|  |  |  | dataConditions: dataConditions | 
|---|
|  |  |  | }; | 
|---|
|  |  |  | const timePeriod = lineChartCriteria.timePeriod; | 
|---|
|  |  |  | switch (timePeriod.timeUnits) { | 
|---|
|  |  |  | case TimeUnits.MONTH: | 
|---|
|  |  |  | this.grid.columns = this.newArray(1, 12, null, '月').map( | 
|---|
|  |  |  | item => { | 
|---|
|  |  |  | return {text: item}; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | case TimeUnits.DAY: | 
|---|
|  |  |  | const mo = moment(lineChartCriteria.timePeriod.startTime); | 
|---|
|  |  |  | const days = mo.endOf('month').date(); | 
|---|
|  |  |  | this.grid.columns = this.newArray(1, days, null, '日').map( | 
|---|
|  |  |  | item => { | 
|---|
|  |  |  | return {text: item}; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | case TimeUnits.HOUR: | 
|---|
|  |  |  | this.grid.columns = this.newArray(0, 24, null, '时').map( | 
|---|
|  |  |  | item => { | 
|---|
|  |  |  | return {text: item}; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | case TimeUnits.MINUTE: | 
|---|
|  |  |  | this.grid.columns = this.newArray(0, 60, null, '分').map( | 
|---|
|  |  |  | item => { | 
|---|
|  |  |  | return {text: item}; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 设置表格宽度,要在此处 | 
|---|
|  |  |  | this.tableWidth =  this.grid.columns.length * 80 + 160; | 
|---|
|  |  |  | this.reloadChart(); | 
|---|
|  |  |  | // 重设报表标题 | 
|---|
|  |  |  | this.reloadChartTitle(); | 
|---|
|  |  |  | // 清空数据 | 
|---|
|  |  |  | this.grid.data = []; | 
|---|
|  |  |  | console.log(lineChartCriteria); | 
|---|
|  |  |  | this.http.post('/report/line-chart', lineChartCriteria).subscribe( | 
|---|
|  |  |  | (res: ResultBean<{[key: string]: Array<Array<number>>}>) => { | 
|---|
|  |  |  | if (res.code === 1) { | 
|---|
|  |  |  | const data =  res.data; | 
|---|
|  |  |  | if (!!data) { | 
|---|
|  |  |  | const sensorKeys = Object.keys(data); | 
|---|
|  |  |  | // aqi六项置前,其他是有数据项 向前。 | 
|---|
|  |  |  | sensorKeys.forEach( | 
|---|
|  |  |  | key => { | 
|---|
|  |  |  | const sensor = (<Array<Sensor>>this.sensorOptions[0].children). | 
|---|
|  |  |  | find(item => { | 
|---|
|  |  |  | return item.sensorKey === key; | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | let weight = this.aqiSort[key]; | 
|---|
|  |  |  | weight = !!weight ? weight : 101; | 
|---|
|  |  |  | //    this.gridSensors.push(sensor); | 
|---|
|  |  |  | const sensorData = data[key][0].map( | 
|---|
|  |  |  | value => { | 
|---|
|  |  |  | if (weight > 100) { | 
|---|
|  |  |  | // pair.value 为null,权重后移 | 
|---|
|  |  |  | weight = !!value ? weight : weight + 1; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 四舍五入,保留2位 | 
|---|
|  |  |  | return value != null ? String(Math.round(value * 100) / 100) : '-'; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | this.grid.data.push({sensor: sensor, data: sensorData, weight: weight}); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | this.grid.data.sort( (a, b) =>  a.weight - b.weight ); | 
|---|
|  |  |  | this.grid.loading = false; | 
|---|
|  |  |  | this.switchSensor(0); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | $('.ant-table-body').scroll( | 
|---|
|  |  |  | () => { | 
|---|
|  |  |  | this.sensorNameScrollLeft =  $('.ant-table-body').scrollLeft(); | 
|---|
|  |  |  | this.sensorNameScrollTop =   -$('.ant-table-body').scrollTop(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private switchAreas(): void { | 
|---|
|  |  |  | switch (this._areas.length ) { | 
|---|
|  |  |  | case 1: this.dataCondition.areaRange = AreaRange.PROVINCE; break; | 
|---|
|  |  |  | case 2: this.dataCondition.areaRange = AreaRange.CITY; break; | 
|---|
|  |  |  | case 3: this.dataCondition.areaRange = AreaRange.AREA; break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private reloadChart(): void { | 
|---|
|  |  |  | const  timeList = this.grid.columns.map(item => item.text); | 
|---|
|  |  |  | if (!!this.echartsIntance) { | 
|---|
|  |  |  | this.chartOption = null; | 
|---|
|  |  |  | this.echartsIntance.clear(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | //  let series = null; | 
|---|
|  |  |  | //  if ( this.chartSelectedIndex < this.grid.data.length ) { | 
|---|
|  |  |  | //     series = [{type: 'line', data: this.grid.data[this.chartSelectedIndex]}]; | 
|---|
|  |  |  | //  } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | this.initOpton({ xAxis : [{data : timeList}]}); | 
|---|
|  |  |  | this.chartLoading = true; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private initOpton(opt: {[key: string]: object}) { | 
|---|
|  |  |  | const defaultOption = { | 
|---|
|  |  |  | title: { | 
|---|
|  |  |  | left: 'center' | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | tooltip : { | 
|---|
|  |  |  | trigger: 'axis', | 
|---|
|  |  |  | axisPointer: { | 
|---|
|  |  |  | type: 'cross', | 
|---|
|  |  |  | label: { | 
|---|
|  |  |  | backgroundColor: '#6a7985' | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | legend: { | 
|---|
|  |  |  | data: [] | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | toolbox: { | 
|---|
|  |  |  | feature: { | 
|---|
|  |  |  | saveAsImage: {} | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | grid: { | 
|---|
|  |  |  | left: '3%', | 
|---|
|  |  |  | right: '4%', | 
|---|
|  |  |  | bottom: '3%', | 
|---|
|  |  |  | containLabel: true | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | xAxis : [ | 
|---|
|  |  |  | { | 
|---|
|  |  |  | type : 'category', | 
|---|
|  |  |  | boundaryGap : false | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ], | 
|---|
|  |  |  | yAxis : [ | 
|---|
|  |  |  | { | 
|---|
|  |  |  | type : 'value' | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ], | 
|---|
|  |  |  | series : [ | 
|---|
|  |  |  | ] | 
|---|
|  |  |  | }; | 
|---|
|  |  |  | $.extend(true, defaultOption, opt); | 
|---|
|  |  |  | this.chartOption = defaultOption; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public get sensorUnit() { | 
|---|
|  |  |  | return this.grid.data[this.chartSelectedIndex].sensor.unit; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 报表标题暂存 防止联动 | 
|---|
|  |  |  | public _chartTitleTemp = ''; | 
|---|
|  |  |  | public _tableTitleTemp = ''; | 
|---|
|  |  |  | public reloadChartTitle(): void { | 
|---|
|  |  |  | const names = ['辖区', '地区', '维度', '时间', '项目']; | 
|---|
|  |  |  | switch ( this.dataCondition.areaRange ) { | 
|---|
|  |  |  | case AreaRange.MONITORPOINT : | 
|---|
|  |  |  | names[0] = '监控站点'; | 
|---|
|  |  |  | names[1] = this._deviceDimension.name; break; | 
|---|
|  |  |  | case AreaRange.DEVICE : | 
|---|
|  |  |  | names[0] = '设备'; | 
|---|
|  |  |  | names[1] = this._device.name; break; | 
|---|
|  |  |  | default : | 
|---|
|  |  |  | names[0] = ''; | 
|---|
|  |  |  | names[1] = this._areas.map( item => item.label).join('/'); | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | // 维度命名 | 
|---|
|  |  |  | if (!!this._deviceDimension) { | 
|---|
|  |  |  | switch (this.dataCondition.deviceDimension) { | 
|---|
|  |  |  | case DeviceDimension.PROFESSION : | 
|---|
|  |  |  | names[2] = '[' + this._deviceDimension.name + ']'; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | default : | 
|---|
|  |  |  | names[2] = ''; | 
|---|
|  |  |  | break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | names[2] = ''; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | names[3] = moment(this.actualTime).format(this._timeType.dateFormat); | 
|---|
|  |  |  | if ( this.grid.data.length > this.chartSelectedIndex) { | 
|---|
|  |  |  | names[4] = this.grid.data[this.chartSelectedIndex].sensor.name; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const title = names.join('  ') + ' 报表'; | 
|---|
|  |  |  | if (title.trim() !== this._chartTitleTemp.trim()) { | 
|---|
|  |  |  | this._chartTitleTemp = title; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const tableTile = names.slice(0, -1).join('  ') + ' 各项数据'; | 
|---|
|  |  |  | if (tableTile.trim() !== this._tableTitleTemp.trim()) { | 
|---|
|  |  |  | this._tableTitleTemp = tableTile; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public switchSensor(index: number): void { | 
|---|
|  |  |  | this.chartSelectedIndex = index; | 
|---|
|  |  |  | this.chartLoading = true; | 
|---|
|  |  |  | this.reloadChartTitle(); | 
|---|
|  |  |  | setTimeout(() => { | 
|---|
|  |  |  | const series = [{type: 'line', data: this.grid.data[index].data}]; | 
|---|
|  |  |  | this.echartsIntance.setOption({ | 
|---|
|  |  |  | title: { | 
|---|
|  |  |  | text: this._chartTitleTemp | 
|---|
|  |  |  | }, | 
|---|
|  |  |  | yAxis : [ | 
|---|
|  |  |  | { | 
|---|
|  |  |  | name : '单位:' + this.sensorUnit | 
|---|
|  |  |  | } | 
|---|
|  |  |  | ], | 
|---|
|  |  |  | series: series | 
|---|
|  |  |  | }); | 
|---|
|  |  |  | this.chartLoading = false; | 
|---|
|  |  |  | }, 600); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | private getPeriodDate(value: Date , type?: 'start'|'end' ): Date { | 
|---|
|  |  |  | let month = 0; | 
|---|
|  |  |  | let day = 1; | 
|---|
|  |  |  | let hour = 0; | 
|---|
|  |  |  | let minute = 0; | 
|---|
|  |  |  | let second = 0; | 
|---|
|  |  |  | let millisecond = 0; | 
|---|
|  |  |  | if ('end' === type) { | 
|---|
|  |  |  | month = 11; | 
|---|
|  |  |  | day = 31; | 
|---|
|  |  |  | hour = 23; | 
|---|
|  |  |  | minute = 59; | 
|---|
|  |  |  | second = 59; | 
|---|
|  |  |  | millisecond = 999; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | const mo = moment(value); | 
|---|
|  |  |  | switch ( this.timeUnit.value ) { | 
|---|
|  |  |  | case TimeUnits.MONTH: | 
|---|
|  |  |  | mo.month(month).date(day).hour(hour).minute(minute).second(second).millisecond(millisecond); break; | 
|---|
|  |  |  | case TimeUnits.DAY: | 
|---|
|  |  |  | mo.date(day).hour(hour).minute(minute).second(second).millisecond(millisecond); break; | 
|---|
|  |  |  | case TimeUnits.HOUR: | 
|---|
|  |  |  | mo.hour(hour).minute(minute).second(second).millisecond(millisecond); break; | 
|---|
|  |  |  | case TimeUnits.MINUTE: | 
|---|
|  |  |  | mo.minute(minute).second(second).millisecond(millisecond); break; | 
|---|
|  |  |  | // case TimeUnits.MINUTE: | 
|---|
|  |  |  | // mo.second(second).millisecond(millisecond); break; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return mo.toDate(); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public sensorTableFocusIndex = -1; | 
|---|
|  |  |  | public sensorTableFocus(index) { | 
|---|
|  |  |  | this.sensorTableFocusIndex = index; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public sensorTableBlur(index) { | 
|---|
|  |  |  | this.sensorTableFocusIndex = -1; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public sensorSelectVisible = false; | 
|---|
|  |  |  | public treeMouseOverOccur = false; | 
|---|
|  |  |  | public onTreeMouseOver(event) { | 
|---|
|  |  |  | this.treeMouseOverOccur = true; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | public onTreeMouseOut(event) { | 
|---|
|  |  |  | this.treeMouseOverOccur = false; | 
|---|
|  |  |  | setTimeout(() => { | 
|---|
|  |  |  | if (!this.treeMouseOverOccur) { | 
|---|
|  |  |  | this.sensorSelectVisible =  false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | }, 900); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|