import type { EChartsOption } from 'echarts';
|
|
export interface WaterTrendItem {
|
id: string;
|
name: string;
|
trend_enabled: boolean;
|
unit: string;
|
}
|
|
export interface WaterTrendRecord {
|
ceshi_jieguo: null | number;
|
jianyan_xiang_id: string;
|
jianyan_riqi: string;
|
jianyan_xiangmu_id: string;
|
quyangdian_id: string;
|
quyangdian_mingcheng: string;
|
renwu_jianyan_xiang_kuaizhao_id: string;
|
}
|
|
export interface WaterTrendStandard {
|
biaozhun_banben?: string;
|
biaozhun_bianhao?: string;
|
biaozhun_shangxian?: null | number;
|
biaozhun_xiaxian?: null | number;
|
jianyan_xiangmu_id: string;
|
renwu_jianyan_xiang_kuaizhao_id: string;
|
trend_enabled: boolean;
|
unit: string;
|
}
|
|
export interface WaterTrendSeries {
|
data: Array<null | number>;
|
id: string;
|
kind: 'limit' | 'record';
|
name: string;
|
}
|
|
export type WaterTrendChartType = 'bar' | 'line';
|
|
export interface WaterTrendModel {
|
categories: string[];
|
item: WaterTrendItem;
|
key: string;
|
series: WaterTrendSeries[];
|
standard: WaterTrendStandard;
|
unit: string;
|
}
|
|
interface BuildWaterTrendModelsOptions {
|
items: WaterTrendItem[];
|
records: WaterTrendRecord[];
|
standards: WaterTrendStandard[];
|
}
|
|
export function buildWaterTrendModels({ items, records, standards }: BuildWaterTrendModelsOptions): WaterTrendModel[] {
|
const standardsBySnapshotId = new Map(
|
standards
|
.filter((standard) => standard.trend_enabled && standard.renwu_jianyan_xiang_kuaizhao_id)
|
.map((standard) => [standard.renwu_jianyan_xiang_kuaizhao_id, standard]),
|
);
|
|
return items.flatMap((item) => {
|
if (!item.trend_enabled) {
|
return [];
|
}
|
|
const recordsByStandard = new Map<string, { records: WaterTrendRecord[]; standard: WaterTrendStandard }>();
|
for (const record of records) {
|
if (record.jianyan_xiangmu_id !== item.id || !record.jianyan_riqi) {
|
continue;
|
}
|
const standard = standardsBySnapshotId.get(record.renwu_jianyan_xiang_kuaizhao_id);
|
if (!standard || standard.jianyan_xiangmu_id !== item.id) {
|
continue;
|
}
|
const signature = buildStandardSignature(standard);
|
const group = recordsByStandard.get(signature);
|
if (group) {
|
group.records.push(record);
|
} else {
|
recordsByStandard.set(signature, { records: [record], standard });
|
}
|
}
|
|
return [...recordsByStandard.entries()].map(([signature, group]) => buildWaterTrendModel(item, signature, group.standard, group.records));
|
});
|
}
|
|
function buildStandardSignature(standard: WaterTrendStandard) {
|
return [
|
standard.biaozhun_bianhao || '',
|
standard.biaozhun_banben || '',
|
standard.biaozhun_xiaxian ?? '',
|
standard.biaozhun_shangxian ?? '',
|
standard.unit || '',
|
].join('|');
|
}
|
|
function buildWaterTrendModel(
|
item: WaterTrendItem,
|
standardSignature: string,
|
standard: WaterTrendStandard,
|
records: WaterTrendRecord[],
|
): WaterTrendModel {
|
const orderedRecords = [...records].sort(
|
(left, right) =>
|
left.jianyan_riqi.localeCompare(right.jianyan_riqi) || left.jianyan_xiang_id.localeCompare(right.jianyan_xiang_id),
|
);
|
const categories = orderedRecords.map((record) => record.jianyan_riqi);
|
const series: WaterTrendSeries[] = [];
|
|
const addLimitSeries = (value: null | number | undefined, id: string, name: string) => {
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
series.push({ data: categories.map(() => value), id, kind: 'limit', name });
|
}
|
};
|
|
addLimitSeries(standard.biaozhun_xiaxian, 'lower-limit', '最低限');
|
addLimitSeries(standard.biaozhun_shangxian, 'upper-limit', '最高限');
|
|
const recordsByPoint = new Map<string, Map<string, WaterTrendRecord>>();
|
for (const record of orderedRecords) {
|
const pointRecords = recordsByPoint.get(record.quyangdian_id) || new Map<string, WaterTrendRecord>();
|
pointRecords.set(record.jianyan_xiang_id, record);
|
recordsByPoint.set(record.quyangdian_id, pointRecords);
|
}
|
|
for (const [pointId, pointRecords] of recordsByPoint) {
|
series.push({
|
data: orderedRecords.map((record) => {
|
const value = pointRecords.get(record.jianyan_xiang_id)?.ceshi_jieguo;
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
}),
|
id: `point-${pointId}`,
|
kind: 'record',
|
name: pointRecords.values().next().value?.quyangdian_mingcheng || pointId,
|
});
|
}
|
|
return {
|
categories,
|
item,
|
key: `${item.id}:${standardSignature}`,
|
series,
|
standard,
|
unit: standard.unit || item.unit,
|
};
|
}
|
|
export function createWaterTrendOption(model: WaterTrendModel, title: string, chartType: WaterTrendChartType = 'line'): EChartsOption {
|
const recordNameCounts = model.series.reduce((counts, series) => {
|
if (series.kind === 'record') {
|
counts.set(series.name, (counts.get(series.name) || 0) + 1);
|
}
|
return counts;
|
}, new Map<string, number>());
|
const chartSeries = model.series.map((series) => ({
|
...series,
|
name: series.kind === 'record' && (recordNameCounts.get(series.name) || 0) > 1 ? `${series.name}(${series.id.replace('point-', '')})` : series.name,
|
}));
|
|
return {
|
color: ['#d94a4a', '#356fc5', '#12a594', '#ed9b23', '#7658ab', '#5c7080'],
|
graphic: {
|
bottom: 4,
|
left: 'center',
|
style: {
|
fill: '#666',
|
font: '13px sans-serif',
|
text: `标准编号:${model.standard.biaozhun_bianhao || '-'};版本:${model.standard.biaozhun_banben || '-'}`,
|
},
|
type: 'text',
|
},
|
grid: { bottom: 104, containLabel: true, left: 70, right: 40, top: 86 },
|
legend: {
|
bottom: 34,
|
data: chartSeries.map((series) => series.name),
|
type: 'scroll',
|
},
|
series: chartSeries.map((series) => {
|
const type = series.kind === 'limit' ? 'line' : chartType;
|
return {
|
...(type === 'bar' ? { barMaxWidth: 36 } : {}),
|
data: series.data,
|
id: series.id,
|
lineStyle: series.kind === 'limit' ? { type: 'dashed', width: 2 } : { width: 2 },
|
name: series.name,
|
symbol: series.kind === 'limit' ? 'none' : 'circle',
|
type,
|
};
|
}),
|
title: {
|
left: 'center',
|
text: title,
|
textStyle: { fontSize: 20, fontWeight: 700 },
|
},
|
toolbox: { feature: { saveAsImage: {} }, right: 16, top: 14 },
|
tooltip: { trigger: 'axis' },
|
xAxis: {
|
axisLabel: {
|
interval: 0,
|
rotate: model.categories.length > 8 ? 35 : 0,
|
},
|
data: model.categories,
|
name: '时间',
|
type: 'category',
|
},
|
yAxis: {
|
name: model.unit || '检测值',
|
nameGap: 18,
|
type: 'value',
|
},
|
};
|
}
|