Commit 89f89959 authored by 陈龙's avatar 陈龙

feat: 升级历史曲线

parent ff4ddada
import React, { memo, useEffect, useMemo, useRef } from 'react';
import { BasicChart } from '@wisdom-components/basicchart';
import React, {memo, useEffect, useMemo, useRef} from 'react';
import {BasicChart} from '@wisdom-components/basicchart';
import PandaEmpty from '@wisdom-components/empty';
import optionGenerator, { alarmMarkLine, minMaxMarkPoint, offlineArea } from './utils';
import { isArray, cloneDeep } from 'lodash';
import optionGenerator, {alarmMarkLine, minMaxMarkPoint, offlineArea} from './utils';
import {isArray, cloneDeep} from 'lodash';
const SingleChart = memo((props) => {
const {
dataSource,
contrast = false,
contrastOption = 'day',
smooth = true,
curveCenter,
showGridLine = true,
deviceAlarmSchemes,
chartType,
lineDataType,
showBoxOption,
} = props;
const chartRef = useRef();
const {
dataSource,
contrast = false,
contrastOption = 'day',
smooth = true,
curveCenter,
showGridLine = true,
deviceAlarmSchemes,
chartType,
lineDataType,
showBoxOption,
special
} = props;
const chartRef = useRef();
const option = useMemo(() => {
const config = {
needUnit: true,
curveCenter,
showGridLine,
deviceAlarmSchemes,
showMarkLine: true,
showPoint: true,
chartType,
// justLine,
showBoxOption,
};
return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config,lineDataType);
}, [dataSource, smooth, curveCenter,chartType]);
useEffect(() => {
chartRef.current?.resize?.();
const chart = chartRef.current?.getEchartsInstance?.();
function hander(params) {
const { selected } = params;
const count = Object.values(selected || {}).filter((item) => item).length;
const option = cloneDeep(chart.getOption());
const needMarkLine = count === 1;
// 需求变更:设备离线改用“是否在线”的数据,离线的状态标记的数据用该部分的数据。 2023年4月25日09:36:55
let _tempDataArray = dataSource.filter((item) => item.sensorName === '是否在线');
option.series.forEach((item, index) => {
let _data = _tempDataArray.find((offline) => offline.stationCode === item.stationCode);
let offlineAreas = offlineArea(_data);
if (offlineAreas.data?.length) {
option.markArea = null;
item.markArea = offlineAreas;
}
if (needMarkLine && selected[item.name]) {
item.markLine = alarmMarkLine(
dataSource[index],
index,
[dataSource[index]],
const option = useMemo(() => {
const config = {
needUnit: true,
curveCenter,
showGridLine,
deviceAlarmSchemes,
);
item.markPoint = minMaxMarkPoint(dataSource[index], index, [dataSource[index]]);
} else {
item.markLine = {};
item.markPoint = {};
showMarkLine: true,
showPoint: true,
chartType,
// justLine,
showBoxOption,
special
};
return optionGenerator(dataSource, null, contrast, contrastOption, smooth, config, lineDataType);
}, [dataSource, smooth, curveCenter, chartType]);
useEffect(() => {
chartRef.current?.resize?.();
const chart = chartRef.current?.getEchartsInstance?.();
function hander(params) {
const {selected} = params;
const count = Object.values(selected || {}).filter((item) => item).length;
const option = cloneDeep(chart.getOption());
const needMarkLine = count === 1;
// 需求变更:设备离线改用“是否在线”的数据,离线的状态标记的数据用该部分的数据。 2023年4月25日09:36:55
let _tempDataArray = dataSource.filter((item) => item.sensorName === '是否在线');
option.series.forEach((item, index) => {
let _data = _tempDataArray.find((offline) => offline.stationCode === item.stationCode);
let offlineAreas = offlineArea(_data);
if (offlineAreas.data?.length) {
option.markArea = null;
item.markArea = offlineAreas;
}
if (needMarkLine && selected[item.name]) {
item.markLine = alarmMarkLine(
dataSource[index],
index,
[dataSource[index]],
deviceAlarmSchemes,
);
item.markPoint = minMaxMarkPoint(dataSource[index], index, [dataSource[index]]);
} else {
item.markLine = {};
item.markPoint = {};
}
});
chart.setOption(option, true);
}
});
chart.setOption(option, true);
}
if (!chart) return;
chart.on('legendselectchanged', hander);
return () => {
chart.off('legendselectchanged', hander);
};
}, [dataSource, deviceAlarmSchemes]);
// 网格开关,不更新整个图表
useEffect(() => {
const chart = chartRef.current?.getEchartsInstance?.();
if (!chart) return;
const option = chart.getOption();
// 交互指针
const tooltip = {
trigger: 'axis',
axisPointer: {
type: showGridLine ? 'cross' : 'line',
},
};
// 网格线
const axisConfig = {
minorTick: {
show: showGridLine,
splitNumber: 2,
},
minorSplitLine: {
show: showGridLine,
lineStyle: {
type: 'dashed',
},
},
splitLine: {
show: showGridLine,
},
};
let yAxis = axisConfig;
if (isArray(option.yAxis)) {
yAxis = option.yAxis.map((item) => ({ ...axisConfig }));
}
let xAxis = axisConfig;
chart.setOption({
xAxis,
yAxis,
tooltip,
});
}, [showGridLine]);
if (!chart) return;
chart.on('legendselectchanged', hander);
return () => {
chart.off('legendselectchanged', hander);
};
}, [dataSource, deviceAlarmSchemes]);
// 网格开关,不更新整个图表
useEffect(() => {
const chart = chartRef.current?.getEchartsInstance?.();
if (!chart) return;
const option = chart.getOption();
// 交互指针
const tooltip = {
trigger: 'axis',
axisPointer: {
type: showGridLine ? 'cross' : 'line',
},
};
// 网格线
const axisConfig = {
minorTick: {
show: showGridLine,
splitNumber: 2,
},
minorSplitLine: {
show: showGridLine,
lineStyle: {
type: 'dashed',
},
},
splitLine: {
show: showGridLine,
},
};
let yAxis = axisConfig;
if (isArray(option.yAxis)) {
yAxis = option.yAxis.map((item) => ({...axisConfig}));
}
let xAxis = axisConfig;
chart.setOption({
xAxis,
yAxis,
tooltip,
});
}, [showGridLine]);
// 数据都为空显示缺省页
const isEmpty = useMemo(
() =>
!dataSource ||
!dataSource.length ||
!dataSource.find((e) => e.dataModel && e.dataModel.length > 0),
[dataSource],
);
return isEmpty ? (
<PandaEmpty />
) : (
<BasicChart ref={chartRef} option={option} notMerge style={{ width: '100%', height: '100%' }} />
);
// 数据都为空显示缺省页
const isEmpty = useMemo(
() =>
!dataSource ||
!dataSource.length ||
!dataSource.find((e) => e.dataModel && e.dataModel.length > 0),
[dataSource],
);
return isEmpty ? (
<PandaEmpty/>
) : (
<BasicChart ref={chartRef} option={option} notMerge style={{width: '100%', height: '100%'}}/>
);
});
export default SingleChart;
......@@ -7,6 +7,22 @@ const baseUrl = typeof DUMI_TYPE !== 'undefined' && DUMI_TYPE === 'dumi' ? '/api
const monitorDeviceUrl = `${baseUrl}/PandaMonitor/Monitor/Device`;
// 获取单个设备的配置信息
export function getPointAddress (params) {
return request({
url: `/PandaMonitor/Monitor/PointAddress/GetPointAddress`,
method: REQUEST_METHOD_GET,
params
});
}
// 获取点表信息
export function getPointAddressEntry(params) {
return request({
url: `/PandaMonitor/Monitor/PointAddress/GetPointAddressEntry`,
method: REQUEST_METHOD_GET,
params
});
}
// 获取历史数据
export function getHistoryInfo(data) {
......
......@@ -3,13 +3,21 @@ import HistoryView from '../index';
import { MobileHistoryChart } from "../mobile";
const deviceParams = [
/*10.182*/
/* {
deviceCode: 'EGBF00000141',
// sensors: '进水压力,出水瞬时流量,出水累计流量',
sensors: '进水压力',
deviceType: '二供泵房',
deviceType: '熊猫二供泵房',
pointAddressID: 208,
}, */
},*/
/* {
deviceCode: 'EGJZ00000197',
// sensors: '进水压力,出水瞬时流量,出水累计流量',
sensors: '1#变频器运行频率',
deviceType: '二供机组',
// pointAddressID: 208,
}*/
/* {
"deviceCode": "SYJ00000008",
"sensors": "瞬时流量",
......@@ -73,6 +81,21 @@ const deviceParams = [
"sensors": "瞬时流量",
"deviceType": "流量计"
}
/*邳州*/
/* {
deviceCode: 'EGJZ00000027',
sensors: '2#变频器运行频率',
deviceType: '二供机组',
}*/
/*确山*/
/*泵3状态*/
/* {
// EGJZ00000003
deviceCode: 'EGJZ00000003',
// sensors: '3#频率',
sensors: '出1累计流量',
deviceType: '二供机组',
}*/
];
const Demo = () => {
return (
......@@ -81,7 +104,6 @@ const Demo = () => {
<HistoryView deviceParams={deviceParams} defaultModel="curve" />
</div>
</div>
);
};
......
......@@ -24,11 +24,18 @@ import _ from 'lodash';
import TimeRangePicker from '@wisdom-components/timerangepicker';
import PandaEmpty from '@wisdom-components/empty';
import BasicTable from '@wisdom-components/basictable';
import {getHistoryInfo, getDeviceAlarmScheme, getExportDeviceHistoryUrl, getDictionaryInfoAll} from './apis';
import {
getHistoryInfo,
getDeviceAlarmScheme,
getExportDeviceHistoryUrl,
getDictionaryInfoAll,
getPointAddress, getPointAddressEntry
} from './apis';
import SingleChart from './SingleChart';
import GridChart from './GridChart';
import './index.less';
import {globalConfig} from 'antd/lib/config-provider';
const {RangePicker} = DatePicker;
const {Option} = Select;
......@@ -108,12 +115,12 @@ const CheckboxData = [
},
{
key: 'ignoreOutliers',
label: '曲线降噪',
label: '去除异常值',
type: 'updateIgnoreOutliers',
checked: false,
showInCurve: true,
showInTable: true,
tooltip: '本算法采用递推平均滤波法(滑动平均滤波法)对采样数据进行均值化平滑处理。',
tooltip: '采用递推平均滤波法(滑动平均滤波法)对采样数据中的异常离群值进行识别与去除。',
hasSub: true
},
{
......@@ -302,6 +309,8 @@ const HistoryView = (props) => {
const [shortcutsValue, setShortcutsValue] = useState('');
const [shortcutsDatePickerArr, setShortcutsDatePickerArr] = useState([]);
const [percent, setPercent] = useState(0);
// 频率指标特殊业务
const [special1, setSpecial1] = useState(null);
// 选择的时间范围值
const dateRange = useMemo(() => {
if (timeValue === 'customer') {
......@@ -565,7 +574,8 @@ const HistoryView = (props) => {
<div key={child.key} className={classNames(`${prefixCls}-contrast-list`)}>
<div className={classNames(`${prefixCls}-contrast-wrap`)}>
<DatePicker
picker={contrastOption}
key={child.key}
picker={contrastOption === 'day' ? undefined : contrastOption}
value={child.value}
onChange={(date, dateString) => onContrastPickerChange(date, dateString, child)}
style={{width: 130, border: !shortcutsValue ? '1px solid #1890ff' : ''}}
......@@ -922,10 +932,16 @@ const HistoryView = (props) => {
deviceParams
.map((item) => {
let _item = {...item};
_item.sensors =
item.sensors && !item.sensors.includes('是否在线')
? item.sensors + ',是否在线'
: item.sensors;
// 历史曲线中,是否在线暂时去除,不显示 要显示是否在线解开这里即可 2023-09-15
/* _item.sensors =
item.sensors && !item.sensors.includes('是否在线')
? item.sensors + ',是否在线'
: item.sensors;*/
_item.sensors = item.sensors;
// special 业务
if (special1) {
_item.sensors += `,${special1.name}`;
}
return _item;
})
.forEach((i) => {
......@@ -972,7 +988,12 @@ const HistoryView = (props) => {
deviceParams.forEach((p) => {
// 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? [];
if (sensors?.length) sensors.push('是否在线');
if (sensors?.length) {
sensors.push('是否在线');
if (special1) {
sensors.push(special1.name);
}
}
const list = sensors.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
......@@ -1117,6 +1138,9 @@ const HistoryView = (props) => {
contrast={timeValue === 'contrast'}
contrastOption={contrastOption}
deviceAlarmSchemes={deviceAlarmSchemes}
special={{
special1, // 频率业务
}}
/>
)}
</div>
......@@ -1132,26 +1156,57 @@ const HistoryView = (props) => {
// 非单曲线、单指标不执行
if (deviceParams?.length !== 1 || (deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',')?.length > 1)) return setCompleteInit(true);
setLoading(true);
await getDictionaryInfoAll({
const {deviceCode, deviceType, sensors} = deviceParams[0];
let _id = (await getPointAddress({
code: deviceCode
}))?.data?.[0]?.id;
let _params = {
deviceType: deviceType
};
if (_id) _params.versionId = _id;
let _request0 = getDictionaryInfoAll({
level: '组件_ec_historyview'
}).then(res => {
if (res.code === 0) {
let _opt = res.data.reduce((final, cur) => {
final[cur.fieldName] = cur.fieldValue
return final
}, {});
let _checkboxData = [...checkboxData].map(item => {
let _item = {...item};
if (_opt[item.label] !== undefined) {
_item.checked = _opt[item.label] === 'true'
});
let _request1 = getPointAddressEntry(_params);
await Promise.all([_request0, _request1]).then(result => {
if (result) {
let _res0 = result[0];
let _res1 = result[1];
// 查字典配置
if (_res0.code === 0) {
let _opt = _res0.data.reduce((final, cur) => {
final[cur.fieldName] = cur.fieldValue
return final
}, {});
let _checkboxData = [...checkboxData].map(item => {
let _item = {...item};
if (_opt[item.label] !== undefined) {
_item.checked = _opt[item.label] === 'true'
}
return _item;
});
setCheckboxData(_checkboxData);
}
// 查点表配置
if (_res1.code === 0) {
let _sensorConfig = _res1.data.find(item => item.name.trim() === sensors.trim());
let _statusName = _sensorConfig.statusName;
if (_statusName) {
let _statusConfig = _res1.data.find(item => item.name.trim() === _statusName.trim());
let _valDesc = _statusConfig.valDesc;
setSpecial1({
name: _statusName,
valDesc: _valDesc.split(';').reduce((final, cur) => {
let _arr = cur.split(':');
final[_arr[0]] = _arr[1];
return final
}, {})
});
}
return _item;
});
setCheckboxData(_checkboxData);
}
}
}).catch(err=>{
setLoading(false);
})
});
setCompleteInit(true);
};
useEffect(() => {
......@@ -1184,10 +1239,11 @@ const HistoryView = (props) => {
(loading || percent !== 0) ? <div className={classNames(`${prefixCls}-progressWrapper`)}>
<div className={classNames(`${prefixCls}-contentWrapper`)}>
{
lineDataType === '原始曲线' || lineDataType === '特征曲线'&& moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >= 30? <><Progress percent={percent}
steps={20}
className={classNames(`${prefixCls}-progress`, `${prefixCls}-blink-2`)}
showInfo={false}/>
lineDataType === '原始曲线' || lineDataType === '特征曲线' && moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >= 30 ? <>
<Progress percent={percent}
steps={20}
className={classNames(`${prefixCls}-progress`, `${prefixCls}-blink-2`)}
showInfo={false}/>
<div className={classNames(`${prefixCls}-tip`)}>加载中...</div>
</> : <Spin spinning={loading || false}/>
}
......
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