Commit 4b15851b authored by 陈龙's avatar 陈龙

feat: 新增预测曲线功能;新增统计曲线功能

parent a2ebc0ae
...@@ -22,7 +22,7 @@ path: / ...@@ -22,7 +22,7 @@ path: /
## 单图表 ## 单图表
<code src="./demos/index.js"></code> [//]: # (<code src="./demos/index.js"></code>)
## 单图表-状态 ## 单图表-状态
...@@ -37,7 +37,7 @@ path: / ...@@ -37,7 +37,7 @@ path: /
## 多图表 ## 多图表
[//]: # (<code src="./demos/GridDemo.js"></code>) <code src="./demos/GridDemo.js"></code>
## API ## API
......
import React, {memo, useMemo} from 'react'; import React, {memo, useEffect, useMemo, useState} from 'react';
import _ from 'lodash'; import _, {cloneDeep} from 'lodash';
import {BasicChart} from '@wisdom-components/basicchart'; import {BasicChart} from '@wisdom-components/basicchart';
import PandaEmpty from '@wisdom-components/empty'; import PandaEmpty from '@wisdom-components/empty';
import optionGenerator from './utils'; import optionGenerator from './utils';
import {getPointAddress, getPointAddressEntry, getSensorsRealName, getSensorType, getStatisticsInfo} from "./apis";
import moment from "moment";
import {isString} from "util";
const ChartTitle = ({prefixCls, title, unit}) => { const ChartTitle = ({prefixCls, title, unit}) => {
const cls = `${prefixCls}-grid-item-title`; const cls = `${prefixCls}-grid-item-title`;
...@@ -20,29 +23,178 @@ const GridChart = memo((props) => { ...@@ -20,29 +23,178 @@ const GridChart = memo((props) => {
contrastOption = 'day', contrastOption = 'day',
smooth = true, smooth = true,
curveCenter, curveCenter,
allPointAddress,
allSensorType,
dateRange
} = props; } = props;
const {prefixCls} = props; const {prefixCls} = props;
const [gridData, setGridData] = useState([]);
const [pointAddressData, setPointAddressData] = useState(null);
const [pointAddressEntryData, setPointAddressEntryData] = useState(null);
const [sensorType, setSensorType] = useState(null);
// 新增逻辑:需要区分出哪些是统计值
const gridData = useMemo(() => { /**
const grids = dataSource.reduce((pre, item, index) => { * @param {array} dataSource
const {sensorName, deviceType} = item; */
const key = `${deviceType}_${sensorName}`; // 同设备类型同指标才在同一组 const handleDataSource = async (dataSource) => {
let grid = pre.find((g) => g.key === key); props.setLoading(true);
if (!grid) { // 1. 统计设备
const restProp = _.pick(item, ['equipmentName', 'sensorName', 'stationCode', 'unit']); let _deviceTypes = [];
grid = { let _deviceCodes = dataSource.reduce((final, cur) => {
key: key, if (!final.includes(cur.stationCode) && !_deviceTypes.includes(cur.deviceType)) {
list: [], final.push(cur.stationCode);
...restProp, _deviceTypes.push(cur.deviceType);
};
pre.push(grid);
} }
grid.list.push(item); return final;
return pre;
}, []); }, []);
return grids; // 2. 获取对应的版本id
}, [dataSource]); let _ids = [];
if (pointAddressData !== null) {
_ids = pointAddressData;
} else {
let _idRequest = await getPointAddress({code: _deviceCodes.join(',')});
_ids = _idRequest?.data ?? [];
setPointAddressData(_ids)
}
// 3. 获取对应的点表
let _map = {};
for await (let item of _ids) {
let _index = _deviceCodes.findIndex(code => code === item.code);
if (pointAddressEntryData && pointAddressEntryData[item.id]) {
_map[_deviceTypes[_index]] = pointAddressEntryData[item.id];
} else {
let _entry = await getPointAddressEntry({versionId: item.id});
_map[_deviceTypes[_index]] = _entry?.data ?? [];
setPointAddressEntryData({...pointAddressEntryData, [item.id]: _entry?.data})
}
}
// 4. 获取点类型
let _sensorType = []
if (sensorType) {
_sensorType = sensorType;
} else {
_sensorType = (await getSensorType())?.data ?? [];
}
//5. 找出统计值,合并
let _dataSource = cloneDeep(dataSource);
let _nameListMap = {};
let _indexArr = [];
let _tempValue = {};
let _finalData = {};
_dataSource.forEach((item, index) => {
let _sensorTypeId = _map[item.deviceType].find(sensor => sensor.name === item.sensorName)?.sensorTypeID || 0;
let _type = _sensorType.find(sensor => sensor.id === _sensorTypeId)?.type ?? '';
if (_type === '统计值') {
// 移除掉,并存储
_tempValue[`needToReplace_${item.stationCode}_${item.sensorName}`] = _dataSource.splice(index, 1, `needToReplace_${item.stationCode}_${item.sensorName}`)?.[0];
if (!_nameListMap[item.stationCode]) {
_nameListMap[item.stationCode] = {
code: item.stationCode,
deviceType: item.deviceType,
sensors: [item.sensorName]
}
} else {
_nameListMap[item.stationCode].sensors.push(item.sensorName)
}
}
})
//6. 请求数据并替换数据。grid模式下,请求的时间是一致的。
let baseParam = {
pageIndex: 1,
pageSize: 999,
dateFrom: dateRange[0].dateFrom,
dateTo: dateRange[0].dateTo,
}
let _arr = Object.values(_nameListMap)
for await (let item of _arr) {
let _params = {
...baseParam,
accountName: item.deviceType,
deviceCode: item.code,
nameTypeList: item.sensors.map(sensor => ({
name: sensor,
type: 'Sub'
})),
/* nameTypeList: ['今日用电量', '今日供水量'].map(sensor => ({
name: sensor,
type: 'Sub'
})),*/
dateType: returnDateType(dateRange[0])
};
// 虚拟点需要查出实际点后,进行查找
let _realSensors = {};
let _realSensorsMap = {};
// 统计类的如果是虚拟点,那么需要查出实际数据来源的点,查出映射关系
(await getSensorsRealName(_params))?.data?.forEach(sensor => {
// name 虚拟点 staticName实际的点
_realSensors[sensor.staticName] = sensor.name;
_realSensorsMap[sensor.name] = sensor.staticName;
});
// 请求统计数据时,需要使用实际点去获取
_params.nameTypeList.forEach(sensor => {
sensor.name = _realSensors[sensor.name]
});
// 获取数据后,将原始数据中的dataModel这部分替换掉
((await getStatisticsInfo(_params))?.data?.list?.[0].dNameDataList ?? [])?.forEach(obj => {
let _v = _tempValue[`needToReplace_${item.code}_${_realSensorsMap[obj.dName]}`];
_v.dataModel = obj.nameDate.map(d => {
return {
pt: moment(d.time),
pv: d.value,
maxPV: d.value, minPV: d.value, firstPV: d.value, lastPV: d.value,
}
});
_finalData[`needToReplace_${item.code}_${_realSensorsMap[obj.dName]}`] = _v;
});
// 替换数据
_dataSource.forEach((d, index) => {
if (_.isString(d) && d.includes('needToReplace') && _finalData[d]) {
_dataSource[index] = _finalData[d];
}
})
// 有不存在数据的,将原始数据替换回来
_dataSource.forEach((d, index) => {
if (_.isString(d) && d.includes('needToReplace')) {
_dataSource[index] = dataSource[index];
}
})
}
props.setLoading(false);
return _dataSource
};
const returnDateType = (date) => {
let {dateFrom, dateTo} = date;
let _duration = moment.duration(moment(dateTo) - moment(dateFrom), 'ms').days();
if (_duration >= 7) return 'month';
if (_duration >= 30) return 'year';
return 'day';
};
useEffect(() => {
async function handle() {
let _data = await handleDataSource(dataSource);
const grids = _data.reduce((pre, item, index) => {
const {sensorName, deviceType} = item;
const key = `${deviceType}_${sensorName}`; // 同设备类型同指标才在同一组
let grid = pre.find((g) => g.key === key);
if (!grid) {
const restProp = _.pick(item, ['equipmentName', 'sensorName', 'stationCode', 'unit']);
grid = {
key: key,
list: [],
...restProp,
};
pre.push(grid);
}
grid.list.push(item);
return pre;
}, []);
setGridData(grids);
}
handle();
}, [dataSource])
const options = useMemo(() => { const options = useMemo(() => {
let _options = gridData.map((item) => { let _options = gridData.map((item) => {
const {key, list, equipmentName, sensorName, stationCode, unit} = item; const {key, list, equipmentName, sensorName, stationCode, unit} = item;
......
import React, { memo, useEffect, useMemo, useRef } from 'react'; import React, {memo, useEffect, useMemo, useRef} from 'react';
import { BasicChart } from '@wisdom-components/basicchart'; import {BasicChart} from '@wisdom-components/basicchart';
import PandaEmpty from '@wisdom-components/empty'; import PandaEmpty from '@wisdom-components/empty';
import optionGenerator, { import optionGenerator, {
alarmMarkLine, alarmMarkLine,
...@@ -8,7 +8,7 @@ import optionGenerator, { ...@@ -8,7 +8,7 @@ import optionGenerator, {
specialTypeChartOptionGenerator, specialTypeChartOptionGenerator,
statusChartOptionGenerator statusChartOptionGenerator
} from './utils'; } from './utils';
import { isArray, cloneDeep } from 'lodash'; import {isArray, cloneDeep} from 'lodash';
const SingleChart = memo((props) => { const SingleChart = memo((props) => {
const { const {
...@@ -22,7 +22,8 @@ const SingleChart = memo((props) => { ...@@ -22,7 +22,8 @@ const SingleChart = memo((props) => {
chartType, chartType,
lineDataType, lineDataType,
showBoxOption, showBoxOption,
special special,
predicateData
} = props; } = props;
const chartRef = useRef(); const chartRef = useRef();
const SpecialType = ['状态值', '开关值']; // 横向柱状条 const SpecialType = ['状态值', '开关值']; // 横向柱状条
...@@ -51,16 +52,16 @@ const SingleChart = memo((props) => { ...@@ -51,16 +52,16 @@ const SingleChart = memo((props) => {
if (dataSource.length === 1 && SpecialType.includes(_allPointAddress[dataSource[0].sensorName])) { if (dataSource.length === 1 && SpecialType.includes(_allPointAddress[dataSource[0].sensorName])) {
config.sensorType = _allPointAddress[dataSource[0].sensorName]; config.sensorType = _allPointAddress[dataSource[0].sensorName];
config.special.allValDesc = allValDesc; config.special.allValDesc = allValDesc;
return specialTypeChartOptionGenerator({ dataSource, config }); return specialTypeChartOptionGenerator({dataSource, config});
} }
return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config, lineDataType); return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config, lineDataType, predicateData);
}, [dataSource, smooth, curveCenter, chartType]); }, [dataSource, smooth, curveCenter, chartType, predicateData]);
useEffect(() => { useEffect(() => {
chartRef.current?.resize?.(); chartRef.current?.resize?.();
const chart = chartRef.current?.getEchartsInstance?.(); const chart = chartRef.current?.getEchartsInstance?.();
function hander(params) { function hander(params) {
const { selected } = params; const {selected} = params;
const count = Object.values(selected || {}).filter((item) => item).length; const count = Object.values(selected || {}).filter((item) => item).length;
const option = cloneDeep(chart.getOption()); const option = cloneDeep(chart.getOption());
const needMarkLine = count === 1; const needMarkLine = count === 1;
...@@ -126,7 +127,7 @@ const SingleChart = memo((props) => { ...@@ -126,7 +127,7 @@ const SingleChart = memo((props) => {
}; };
let yAxis = axisConfig; let yAxis = axisConfig;
if (isArray(option.yAxis)) { if (isArray(option.yAxis)) {
yAxis = option.yAxis.map((item) => ({ ...axisConfig })); yAxis = option.yAxis.map((item) => ({...axisConfig}));
} }
let xAxis = axisConfig; let xAxis = axisConfig;
chart.setOption({ chart.setOption({
...@@ -145,9 +146,9 @@ const SingleChart = memo((props) => { ...@@ -145,9 +146,9 @@ const SingleChart = memo((props) => {
[dataSource], [dataSource],
); );
return isEmpty ? ( return isEmpty ? (
<PandaEmpty /> <PandaEmpty/>
) : ( ) : (
<BasicChart ref={chartRef} option={option} notMerge style={{ width: '100%', height: '100%' }} /> <BasicChart ref={chartRef} option={option} notMerge style={{width: '100%', height: '100%'}}/>
); );
}); });
......
...@@ -10,7 +10,7 @@ const monitorDeviceUrl = `${baseUrl}/PandaMonitor/Monitor/Device`; ...@@ -10,7 +10,7 @@ const monitorDeviceUrl = `${baseUrl}/PandaMonitor/Monitor/Device`;
// 获取单个设备的配置信息 // 获取单个设备的配置信息
export function getPointAddress(params) { export function getPointAddress(params) {
return request({ return request({
url: `/PandaMonitor/Monitor/PointAddress/GetPointAddress`, url: `${baseUrl}/PandaMonitor/Monitor/PointAddress/GetPointAddress`,
method: REQUEST_METHOD_GET, method: REQUEST_METHOD_GET,
params params
}); });
...@@ -19,7 +19,7 @@ export function getPointAddress(params) { ...@@ -19,7 +19,7 @@ export function getPointAddress(params) {
// 获取点表信息 // 获取点表信息
export function getPointAddressEntry(params) { export function getPointAddressEntry(params) {
return request({ return request({
url: `/PandaMonitor/Monitor/PointAddress/GetPointAddressEntry`, url: `${baseUrl}/PandaMonitor/Monitor/PointAddress/GetPointAddressEntry`,
method: REQUEST_METHOD_GET, method: REQUEST_METHOD_GET,
params params
}); });
...@@ -62,7 +62,32 @@ export function getDictionaryInfoAll(params) { ...@@ -62,7 +62,32 @@ export function getDictionaryInfoAll(params) {
export function getSensorType() { export function getSensorType() {
return request({ return request({
url: '/PandaMonitor/Monitor/Sensor/GetSensorType', url: `${baseUrl}/PandaMonitor/Monitor/Sensor/GetSensorType`,
method: REQUEST_METHOD_GET, method: REQUEST_METHOD_GET,
}) })
}
export function getPredicateSensor(params) {
return request({
url: `${baseUrl}/PandaWater/CityWater/PiZhou/GetPredicateSensor`,
method: REQUEST_METHOD_GET,
params
})
}
// 获取统计数据
export function getStatisticsInfo(data) {
return request({
url: `${baseUrl}/PandaMonitor/Monitor/Device/EquipmentDataReport`,
method: REQUEST_METHOD_POST,
data,
});
}
export function getSensorsRealName (data) {
return request({
url:`${baseUrl}/PandaMonitor/Monitor/Device/GetStaticRealName`,
method: REQUEST_METHOD_POST,
data,
})
} }
\ No newline at end of file
import React from 'react'; import React from 'react';
import HistoryView from '../index'; import HistoryView from '../index';
const deviceParams = [ /*const deviceParams = [
{ {
deviceCode: 'EGBF00000136', deviceCode: 'EGBF00000136',
sensors: '进水压力,出水瞬时流量,出水累计流量', sensors: '进水压力,出水瞬时流量,出水累计流量',
deviceType: '二供泵房', deviceType: '二供泵房',
pointAddressID: 4, pointAddressID: 4,
}, },*/
/* { /* {
deviceCode: 'EGBF00000137', deviceCode: 'EGBF00000137',
sensors: '进水压力,出水瞬时流量,出水累计流量', sensors: '进水压力,出水瞬时流量,出水累计流量',
...@@ -21,7 +21,7 @@ const deviceParams = [ ...@@ -21,7 +21,7 @@ const deviceParams = [
deviceType: '二供泵房', deviceType: '二供泵房',
pointAddressID: 4, pointAddressID: 4,
},*/ },*/
]; // ];
/*const deviceParams = [ /*const deviceParams = [
{ {
"deviceCode": "LLJ00000055", "deviceCode": "LLJ00000055",
...@@ -58,10 +58,39 @@ const deviceParams = [ ...@@ -58,10 +58,39 @@ const deviceParams = [
"deviceType": "二供泵房" "deviceType": "二供泵房"
} }
]*/ ]*/
/*const deviceParams = [
{
"deviceCode": "EGBF00000244",
"sensors": "进水压力,出水瞬时流量,今日用电量",
"deviceType": "二供泵房"
},
{
"deviceCode": "EGBF00000081",
"sensors": "进水压力,出水瞬时流量,今日用电量",
"deviceType": "二供泵房"
}
]*/
const deviceParams = [
{
"deviceCode": "EGBF00000244",
"sensors": "进水压力,出水瞬时流量,今日用电量,今日供水量",
"deviceType": "二供泵房"
},
{
"deviceCode": "EGJZ00000317",
"sensors": "进水压力,出水瞬时流量",
"deviceType": "二供机组"
},
{
"deviceCode": "EGBF00000184",
"sensors": "进水压力,出水瞬时流量,今日用电量,今日供水量",
"deviceType": "二供泵房"
}
];
const Demo = () => { const Demo = () => {
return <div style={{ height: 700 }}> return <div style={{height: 700}}>
<HistoryView deviceParams={deviceParams} grid /> <HistoryView deviceParams={deviceParams} grid/>
</div>; </div>;
}; };
export default Demo; export default Demo;
...@@ -178,10 +178,16 @@ const deviceParams = [ ...@@ -178,10 +178,16 @@ const deviceParams = [
"sensors": "PH,浑浊度,氨氮,总氮,总磷,溶解氧,水温,电导率,叶绿素a,藻密度,高锰酸盐指数" "sensors": "PH,浑浊度,氨氮,总氮,总磷,溶解氧,水温,电导率,叶绿素a,藻密度,高锰酸盐指数"
}*/ }*/
/*邳州*/ /*邳州*/
{ /* {
"deviceCode": "TLC00000001", "deviceCode": "TLC00000001",
"sensors": "1号碳滤池浊度", "sensors": "1号碳滤池浊度",
"deviceType": "碳滤池" "deviceType": "碳滤池"
}*/
/*邳州*/
{
"deviceCode": "SSBF00000001",
"sensors": "出水压力",
"deviceType": "送水泵房"
} }
]; ];
......
...@@ -35,7 +35,7 @@ import { ...@@ -35,7 +35,7 @@ import {
getExportDeviceHistoryUrl, getExportDeviceHistoryUrl,
getDictionaryInfoAll, getDictionaryInfoAll,
getPointAddress, getPointAddress,
getPointAddressEntry, getPointAddressEntry, getPredicateSensor,
} from './apis'; } from './apis';
import SingleChart from './SingleChart'; import SingleChart from './SingleChart';
import GridChart from './GridChart'; import GridChart from './GridChart';
...@@ -337,6 +337,8 @@ const HistoryView = (props) => { ...@@ -337,6 +337,8 @@ const HistoryView = (props) => {
//查询所有sensorType //查询所有sensorType
const [allSensorType, setAllSensorType] = useState([]); const [allSensorType, setAllSensorType] = useState([]);
const [isSingleStatusSensor, setIsSingleStatusSensor] = useState(false); const [isSingleStatusSensor, setIsSingleStatusSensor] = useState(false);
const [predicateDevice, setPredicateDevice] = useState(null);
const [predicateData, setPredicateData] = useState(null);
// 历史数据相关的特征描述 // 历史数据相关的特征描述
const deviceConfig = useRef({ const deviceConfig = useRef({
oneDevice: deviceParams.length === 1, //单设备 oneDevice: deviceParams.length === 1, //单设备
...@@ -1079,11 +1081,6 @@ const HistoryView = (props) => { ...@@ -1079,11 +1081,6 @@ const HistoryView = (props) => {
deviceParams deviceParams
.map((item) => { .map((item) => {
let _item = {...item}; let _item = {...item};
// 历史曲线中,是否在线暂时去除,不显示 要显示是否在线解开这里即可 2023-09-15
/* _item.sensors =
item.sensors && !item.sensors.includes('是否在线')
? item.sensors + ',是否在线'
: item.sensors;*/
_item.sensors = item.sensors; _item.sensors = item.sensors;
// special 业务 // special 业务
if (special1) { if (special1) {
...@@ -1092,7 +1089,7 @@ const HistoryView = (props) => { ...@@ -1092,7 +1089,7 @@ const HistoryView = (props) => {
return _item; return _item;
}) })
.forEach((i) => { .forEach((i) => {
if (i.sensors && i.deviceCode && i.deviceCode) if (i.sensors && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID'])); acrossTables.push(_.omit(i, ['pointAddressID']));
}); });
if (!acrossTables?.length || !dateRange.length) { if (!acrossTables?.length || !dateRange.length) {
...@@ -1127,6 +1124,11 @@ const HistoryView = (props) => { ...@@ -1127,6 +1124,11 @@ const HistoryView = (props) => {
let _isNaN = isNaN(_num); let _isNaN = isNaN(_num);
if (!_isNaN && _num >= 12) _finalParams.isInterpolation = false; if (!_isNaN && _num >= 12) _finalParams.isInterpolation = false;
} }
// 2024年1月23日 增加预测曲线,单设备单曲线
// 同期对比不允许、多设备的不允许
if (dateRange.length === 1 && predicateDevice) {
_finalParams.acrossTables.push(predicateDevice);
}
requestArr.push(getHistoryInfo(_finalParams)); requestArr.push(getHistoryInfo(_finalParams));
}); });
setLoading(true); setLoading(true);
...@@ -1135,6 +1137,7 @@ const HistoryView = (props) => { ...@@ -1135,6 +1137,7 @@ const HistoryView = (props) => {
setLoading(false); setLoading(false);
if (results.length) { if (results.length) {
let data = []; let data = [];
let _predicateData = [];
results.forEach((res, index) => { results.forEach((res, index) => {
const {dateFrom, dateTo} = dateRange?.[index] ?? {}; const {dateFrom, dateTo} = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) { if (res.code === 0 && res.data.length) {
...@@ -1165,7 +1168,8 @@ const HistoryView = (props) => { ...@@ -1165,7 +1168,8 @@ const HistoryView = (props) => {
}; };
}); });
}); });
deviceParams.forEach((p) => { // 加入预测
(predicateDevice ? deviceParams.concat(predicateDevice) : deviceParams).forEach((p) => {
// 返回数据按查询指标顺序排序 // 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? []; const sensors = p.sensors?.split(',') ?? [];
if (sensors?.length) { if (sensors?.length) {
...@@ -1174,27 +1178,29 @@ const HistoryView = (props) => { ...@@ -1174,27 +1178,29 @@ const HistoryView = (props) => {
sensors.push(special1.name); sensors.push(special1.name);
} }
} }
const list = sensors const list = sensors.map((s) => {
.map((s) => { const dataItem = res.data.find(
const dataItem = res.data.find( (d) => d.stationCode === p.deviceCode && d.sensorName === s,
(d) => d.stationCode === p.deviceCode && d.sensorName === s, );
); if (dataItem) {
if (dataItem) { dataItem.dateFrom = dateFrom || '';
dataItem.dateFrom = dateFrom || ''; dataItem.dateTo = dateTo || '';
dataItem.dateTo = dateTo || ''; dataItem.deviceType = p.deviceType;
return dataItem; return dataItem;
} else { } else {
return {}; return {};
} }
}) }).filter((item) => item.sensorName);
.filter((item) => item.sensorName); // 预测的
data = data.concat(list); data = data.concat(list.filter(item => item.deviceType !== '预测'));
_predicateData = _predicateData.concat(list.filter(item => item.deviceType === '预测'));
}); });
} }
}); });
setLoading(false); setLoading(false);
handleTableData(data); handleTableData(data)
setChartDataSource(data); setChartDataSource(data);
setPredicateData(_predicateData);
} }
}) })
.catch((err) => { .catch((err) => {
...@@ -1322,6 +1328,11 @@ const HistoryView = (props) => { ...@@ -1322,6 +1328,11 @@ const HistoryView = (props) => {
contrast={timeValue === 'contrast'} contrast={timeValue === 'contrast'}
contrastOption={contrastOption} contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes} deviceAlarmSchemes={deviceAlarmSchemes}
dateRange={dateRange}
allPointAddress={allPointAddress}
allSensorType={allSensorType}
loading={loading}
setLoading={setLoading}
/> />
) : ( ) : (
<SingleChart <SingleChart
...@@ -1331,6 +1342,7 @@ const HistoryView = (props) => { ...@@ -1331,6 +1342,7 @@ const HistoryView = (props) => {
showGridLine={chartGrid} showGridLine={chartGrid}
prefixCls={prefixCls} prefixCls={prefixCls}
dataSource={chartDataSource} dataSource={chartDataSource}
predicateData={predicateData}
chartType={isBoxPlots ? chartType : null} chartType={isBoxPlots ? chartType : null}
contrast={timeValue === 'contrast'} contrast={timeValue === 'contrast'}
contrastOption={contrastOption} contrastOption={contrastOption}
...@@ -1374,11 +1386,13 @@ const HistoryView = (props) => { ...@@ -1374,11 +1386,13 @@ const HistoryView = (props) => {
// 以下请求为处理状态值、开关值的图表,只允许单曲线单指标情况下展示 // 以下请求为处理状态值、开关值的图表,只允许单曲线单指标情况下展示
let _request1 = getPointAddressEntry(_params); let _request1 = getPointAddressEntry(_params);
let _request2 = getSensorType(); let _request2 = getSensorType();
await Promise.all([_request0, _request1, _request2]).then((result) => { let _request3 = getPredicateSensor({deviceCode, sensors});
await Promise.all([_request0, _request1, _request2, _request3]).then((result) => {
if (result) { if (result) {
let _res0 = result[0]; let _res0 = result[0];
let _res1 = result[1]; let _res1 = result[1];
let _res2 = result[2]; let _res2 = result[2];
let _res3 = result[3];
// 查字典配置 // 查字典配置
if (_res0.code === 0) { if (_res0.code === 0) {
let _opt = _res0.data.reduce((final, cur) => { let _opt = _res0.data.reduce((final, cur) => {
...@@ -1421,6 +1435,10 @@ const HistoryView = (props) => { ...@@ -1421,6 +1435,10 @@ const HistoryView = (props) => {
let _isStatusSensor = ['状态值', '开关值'].includes(_sensor); let _isStatusSensor = ['状态值', '开关值'].includes(_sensor);
setIsSingleStatusSensor(_isStatusSensor); setIsSingleStatusSensor(_isStatusSensor);
} }
// 单设备单曲线的设定加载
if (_res3.code === 0) {
setPredicateDevice({..._res3.data, deviceType: '预测'});
}
} }
}); });
setCompleteInit(true); setCompleteInit(true);
...@@ -1497,11 +1515,6 @@ const HistoryView = (props) => { ...@@ -1497,11 +1515,6 @@ const HistoryView = (props) => {
<DownloadOutlined/> <DownloadOutlined/>
下载 下载
</Button> </Button>
{/* 保留此处代码,当项目需要表格定制时需要使用 */}
{/*<Button type="link" onClick={exportExcelBtn}>
<DownloadOutlined/>
原始数据
</Button> */}
</> </>
)} )}
</div> </div>
......
...@@ -71,7 +71,7 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION; ...@@ -71,7 +71,7 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
*/ */
const nameFormatter = (data, contrast, contrastOption, nameWithSensor, isSingle) => { const nameFormatter = (data, contrast, contrastOption, nameWithSensor, isSingle) => {
const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data; const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data;
let name = nameWithSensor ? (isSingle?`${sensorName}`:`${equipmentName}-${sensorName}`) : equipmentName; let name = nameWithSensor ? (isSingle ? `${sensorName}` : `${equipmentName}-${sensorName}`) : equipmentName;
if (contrast) { if (contrast) {
const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, ''); const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, '');
name = `${name}-${time}`; name = `${name}-${time}`;
...@@ -182,7 +182,20 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => { ...@@ -182,7 +182,20 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
data, data,
}; };
}; };
export const minMaxMarkPointForPredicateDevice = (dataItem, index, dateSource) => {
return {
symbol: 'circle',
animation: false,
label: {
show: false
},
symbolSize: 10,
data: [
{type: 'max', itemStyle: {color: '#dc5a5a'}},
{type: 'min', itemStyle: {color: '#62b659'}}
]
};
}
export const minMaxMarkPoint = (dataItem, index, dataSource) => { export const minMaxMarkPoint = (dataItem, index, dataSource) => {
const _isMobile = isMobile(); const _isMobile = isMobile();
// 只有一个数据曲线时显示markline // 只有一个数据曲线时显示markline
...@@ -501,6 +514,8 @@ const returnXAxis = ({ ...@@ -501,6 +514,8 @@ const returnXAxis = ({
smooth, smooth,
special, special,
yAxis, yAxis,
predicateData,
chartType
}) => { }) => {
// 根据"指标名称"分类yAxis // 根据"指标名称"分类yAxis
const yAxisInterator = (() => { const yAxisInterator = (() => {
...@@ -522,7 +537,8 @@ const returnXAxis = ({ ...@@ -522,7 +537,8 @@ const returnXAxis = ({
if (!final.includes(cur.stationCode)) final.push(cur.stationCode); if (!final.includes(cur.stationCode)) final.push(cur.stationCode);
return final return final
}, [])?.length; }, [])?.length;
let series = dataSource // 线图 且 有预测数据情况下,才合并预测数据
let series = (predicateData && chartType === 'lineChart' ? dataSource.concat(predicateData) : dataSource)
.filter((item) => { .filter((item) => {
if (item.sensorName === '是否在线') { if (item.sensorName === '是否在线') {
_offlineData.push(item); _offlineData.push(item);
...@@ -537,20 +553,22 @@ const returnXAxis = ({ ...@@ -537,20 +553,22 @@ const returnXAxis = ({
const areaStyle = areaStyleFormatter(item); const areaStyle = areaStyleFormatter(item);
const _index = yAxis.findIndex((item) => item.name === unit); const _index = yAxis.findIndex((item) => item.name === unit);
const yAxisIndex = _index > -1 ? _index : 0; const yAxisIndex = _index > -1 ? _index : 0;
const markLine = showMarkLine let markLine = showMarkLine
? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes) ? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes)
: {}; : {};
const markPoint = showPoint ? minMaxMarkPoint(item, index, dataSource) : {}; let markPoint = showPoint ? minMaxMarkPoint(item, index, dataSource) : {};
// let markArea = null; let _lineStyle = {};
// 需求变更:设备离线改用“是否在线”的数据,离线的状态标记的数据用该部分的数据。 2023年4月25日09:36:55 if (item.deviceType === '预测' && chartType === 'lineChart') {
// 暂时注释,离线逻辑需要再确认 2023-09-15 // markPoint = minMaxMarkPointForPredicateDevice(item, index, dataSource);
/* let _offlineAreasData = _offlineData markPoint = null;
.find((offline) => offline.stationCode === item.stationCode); markLine = null;
let offlineAreas = offlineArea(_offlineAreasData); _lineStyle = {
if (offlineAreas.data?.length) { lineStyle: {
restOption.markArea = null; type: 'dashed',
markArea = offlineAreas; color: '#07a49a'
}*/ }
}
}
// 需求新增:增加频率业务 // 需求新增:增加频率业务
return { return {
notMerge: true, notMerge: true,
...@@ -564,6 +582,7 @@ const returnXAxis = ({ ...@@ -564,6 +582,7 @@ const returnXAxis = ({
markLine, markLine,
markPoint, markPoint,
markArea, markArea,
..._lineStyle,
}; };
}); });
// 由于series更新后,没有的数据曲线仍然停留在图表区上,导致图表可视区范围有问题 // 由于series更新后,没有的数据曲线仍然停留在图表区上,导致图表可视区范围有问题
...@@ -967,6 +986,8 @@ const renderStatusItem = (params, api) => { ...@@ -967,6 +986,8 @@ const renderStatusItem = (params, api) => {
* @param {any} contrastOption 同期对比周期配置, day|month * @param {any} contrastOption 同期对比周期配置, day|month
* @param {any} smooth Ture/false, 曲线/折线 * @param {any} smooth Ture/false, 曲线/折线
* @param {any} config 额外配置信息 * @param {any} config 额外配置信息
* @param lineDataType
* @param predicateData
*/ */
const optionGenerator = ( const optionGenerator = (
...@@ -977,6 +998,7 @@ const optionGenerator = ( ...@@ -977,6 +998,7 @@ const optionGenerator = (
smooth, smooth,
config, config,
lineDataType = '', lineDataType = '',
predicateData
) => { ) => {
// 1. 处理配置,配置分配默认值; // 1. 处理配置,配置分配默认值;
const { const {
...@@ -1010,6 +1032,8 @@ const optionGenerator = ( ...@@ -1010,6 +1032,8 @@ const optionGenerator = (
restOption, restOption,
special, special,
yAxis, yAxis,
predicateData,
chartType
}); });
// 3. 判断是否开启网格; // 3. 判断是否开启网格;
const grid = handleGrid(dataSource, needUnit, leftNum, rightNum, chartType); const grid = handleGrid(dataSource, needUnit, leftNum, rightNum, chartType);
...@@ -1110,6 +1134,7 @@ const optionGenerator = ( ...@@ -1110,6 +1134,7 @@ const optionGenerator = (
symbol: 'none', symbol: 'none',
}); });
}); });
// 加入预测逻辑
tooltip = { tooltip = {
trigger: 'axis', trigger: 'axis',
formatter: (e) => { formatter: (e) => {
...@@ -1156,6 +1181,62 @@ const optionGenerator = ( ...@@ -1156,6 +1181,62 @@ const optionGenerator = (
</div>`; </div>`;
}, },
}; };
if (predicateData) {
tooltip = {
trigger: 'axis',
formatter: (e) => {
return `<div>
${headTemplate(e[0])}
<div>
<div style="display: flex; align-items: center;">
<span style="${
isMobile()
? 'width: ' +
handlePx(90, 'px') +
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
: ''
}">${
e[0].seriesName
}</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
5,
'px',
)} 0 auto;">${e[0]?.value?.[1] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: ${predicateData && chartType === 'lineChart' && e.length >= 2 ? 'flex' : 'none'}">
<span>${e?.[1]?.seriesName}</span>
<span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
5,
'px',
)} 0 auto;">${e[1]?.value?.[1] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: ${
lineDataType === '特征曲线' ? 'flex' : 'none'
}; align-items: center;">
<span>周期最小值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
5,
'px',
)} 0 auto;">${e?.[2]?.value?.[1] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: ${
lineDataType === '特征曲线' ? 'flex' : 'none'
}; align-items: center;">
<span>周期最大值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
5,
'px',
)} 0 auto;">${_maxValues[e?.[3]?.dataIndex] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
</div>
</div>`;
},
};
}
} }
// 单曲线需要标记最大值、最小值的情况下,需要增加自定义的series,将最大最小值显示在图表上 // 单曲线需要标记最大值、最小值的情况下,需要增加自定义的series,将最大最小值显示在图表上
if (dataSource?.[0]?.dataModel?.length && chartType === 'lineChart') { if (dataSource?.[0]?.dataModel?.length && chartType === 'lineChart') {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment