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 } 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 { environment } from '@env/environment';
|
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 { zip } from 'rxjs/observable/zip';
|
import { Subject } from 'rxjs/Subject';
|
|
@Component({
|
selector: 'app-query',
|
templateUrl: './query.component.html',
|
styleUrls: ['./query.component.less']
|
})
|
export class QueryComponent 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 };
|
|
public monitorPointOptions: MonitorPoint[] = [];
|
|
get actualYear(): number {
|
return this.actualTime.getFullYear();
|
}
|
set actualYear(year) {
|
this.actualTime.setFullYear(Number(year));
|
}
|
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.monitorPointOptions = res.data;
|
}
|
}
|
);
|
}
|
devicesChange(text?: string) {
|
if (!!this.monitorPoint) {
|
const example = new ExampleService();
|
if (!!text) {
|
example.or().andEqualTo({ name: 'monitorPointId', value: this.monitorPoint.id })
|
.andLike({name: 'text', value: text});
|
} else {
|
example.or().andEqualTo({ name: 'monitorPointId', value: this.monitorPoint.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;
|
}
|
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.actualYearOptions = this.newArray(this.actualYear - 9, 10).map(item => Number(item)).reverse();
|
// 省市区 初始值
|
this.http.get(environment.SERVER_BASH_URL + '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);
|
}
|
}
|
);
|
}
|
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.monitorPoint = null;
|
this.monitorPointsChange();
|
}
|
}
|
|
// 第二步 设置 监控站点, 值变 改变设备选项,值为null 置空设备选项和设备值
|
public _monitorPoint: MonitorPoint;
|
get monitorPoint(): MonitorPoint {
|
return this._monitorPoint;
|
}
|
set monitorPoint(value) {
|
if (!!value) {
|
// 值变 改变设备选项
|
if (this._monitorPoint !== value) {
|
this._monitorPoint = value; // 此处不能提前也不能放后,设备改变要调用
|
this._device = null; // 设备值清空
|
this.devicesChange();
|
}
|
// 值为null 置空设备选项和设备值
|
} else {
|
this._monitorPoint = 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 mptValue: number = !!this._monitorPoint ? this._monitorPoint.id : null;
|
const devValue: number = !!this._device ? this._device.id : null;
|
const areasData = [ devValue, mptValue, Number(this._areas.slice(-1).pop().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: this.dataCondition.areaRange = AreaRange.MONITORPOINT; break;
|
case 2:
|
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;
|
}
|
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(environment.SERVER_BASH_URL + '/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 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._monitorPoint.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;
|
}
|
names[2] = moment(this.actualTime).format(this._timeType.dateFormat);
|
if ( this.grid.data.length > this.chartSelectedIndex) {
|
names[3] = 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);
|
}
|
}
|