import React, { useContext, useEffect, useRef, useState } from 'react'; import { ConfigProvider, Radio, Slider, InputNumber, Input } from 'antd'; import classNames from 'classnames'; import moment from 'moment'; import { BasicChart } from '@wisdom-components/basicchart'; import { getHistoryInfo, getDateList } from '../../apis'; import { timeArr } from '../utils'; import './index.less'; const HistoryTrend = (props) => { const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('history-trend'); const { deviceCode, sensors, deviceType, changeSpin } = props; const chartRef = useRef(null); const infoRef = useRef({ decimalPoint: 2, unit: '', }); const [sensitive, setSensitive] = useState(10); // 敏感度 const [timeType, setTimeType] = useState('近7日'); // 时间 const [timeData, setTimeData] = useState([]); // 时间 const [sensorData, setSensorData] = useState([]); // 所有数据 const [ruleData, setRuleData] = useState([]); // 预测数据 const [options, setOptions] = useState({}); // 日期处理 const dateMethod = async () => { const arr = new Array(7).fill('list'); let data = arr.map((item, index) => { return moment() .subtract(index + 1, 'day') .format('YYYY-MM-DD'); }); switch (timeType) { case '7工作日': const dateList1 = await getDateList({ n: 7, isHoliday: false, date: moment().subtract(1, 'day').format('YYYY-MM-DD'), }); setTimeData(dateList1?.data || []); break; case '7节假日': const dateList2 = await getDateList({ n: 7, isHoliday: false, date: moment().subtract(1, 'day').format('YYYY-MM-DD'), }); setTimeData(dateList2?.data || []); break; default: setTimeData([data.join(',')]); break; } }; // 获取历史数据 const getSensorsData = () => { changeSpin(true); if (!timeData.length) return setOptions({}); const reqs = []; timeData.forEach((item) => { const list = item.split(','); const params = { isDilute: true, zoom: '5', unit: 'min', dateFrom: moment(list[list.length - 1]).format('YYYY-MM-DD 00:00:00'), dateTo: moment(list[0]).format('YYYY-MM-DD 23:59:59'), acrossTables: [{ deviceCode: deviceCode, sensors: sensors, deviceType: deviceType }], isBoxPlots: true, ignoreOutliers: true, }; const req = getHistoryInfo(params); reqs.push(req); }); Promise.all(reqs).then((results) => { changeSpin(false); let historyData = []; results.forEach((result) => { const _historyData = result?.data?.[0]?.dataModel || []; const info = result?.data?.[0] || {}; infoRef.current = { decimalPoint: info?.decimalPoint || 2, unit: info?.unit || '', }; historyData = historyData.concat([..._historyData]); }); // console.log(historyData); setSensorData(() => { dataMonthod(historyData); return historyData; }); }); }; // 数据处理 const dataMonthod = (data) => { let timeDatas = []; const chartData = []; const strTime = moment().format('YYYY-MM-DD'); timeData.forEach((item) => { const list = item.split(','); timeDatas = timeDatas.concat([...list]); }); timeDatas.forEach((item) => { const seriesData = data.filter((list) => { return list.pt.indexOf(item) > -1; }); const series = { type: 'line', name: item, smooth: true, areaStyle: {}, data: seriesData.map((list) => { const pv = new Date(moment(list['pt']).format(strTime + ' HH:mm:ss')).getTime(); return [pv, list['pv']]; }), }; chartData.push(series); }); renderChart(chartData); }; // 渲染图表 const renderChart = (chartData) => { const option = { xAxis: { type: 'time', axisTick: { alignWithLabel: true, }, axisLabel: { formatter: (value) => { return moment(value).format('HH:mm:ss'); }, }, boundaryGap: false, }, yAxis: { type: 'value', name: infoRef.current?.unit || '', position: 'left', alignTicks: true, axisLabel: { formatter: '{value}', }, }, tooltip: { formatter: function (params) { const title = moment(params[0].axisValue).format('HH:mm:ss'); let html = `<div style="border-bottom: 1px solid #F0F0F0;color: #808080;margin-bottom: 5px;padding-bottom: 5px;">${title}</div><div>`; params.forEach((item) => { html += `<span style="display: inline-block;margin: 0 7px 2px 0;border-radius: 5px;width: 5px;height: 5px;background: ${ item.color };"></span>${item.seriesName}:<span style="color: ${item.color}">${ item.data[1] || item.data[1] === 0 ? item.data[1] : '--' } ${infoRef.current?.unit}</span><br />`; }); return html; }, }, series: chartData, }; setOptions(option); }; // 限值处理 const limitMethod = () => { const pvArr = sensorData.map((item) => { return item.pv; }); let max = Math.max(...pvArr); let min = Math.min(...pvArr); // console.log(max, min); const decimalPoint = infoRef.current.decimalPoint || 2; const data = [ (min * (1 - sensitive / 100)).toFixed(decimalPoint) * 1, (min * (1 + sensitive / 100)).toFixed(decimalPoint) * 1, (max * (1 - sensitive / 100)).toFixed(decimalPoint) * 1, (max * (1 + sensitive / 100)).toFixed(decimalPoint) * 1, ]; const color = ['#CB2D2D', '#0087F7']; const name = ['低低限', '低限', '高限', '高高限']; const mark = name.map((item, index) => { return { name: item, yAxis: data[index], lineStyle: { color: color[index % 2], type: 'dashed', }, label: { color: color[index % 2], }, }; }); let option = { ...options }; option.series[0].markLine = { data: mark, }; setOptions(option); setRuleData(data); props.backData(data); }; useEffect(() => { getSensorsData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [timeData]); useEffect(() => { dateMethod(); }, [timeType]); useEffect(() => { if (options?.series?.length && sensorData.length) limitMethod(); }, [sensitive, sensorData]); return ( <div className={classNames(`${prefixCls}`)}> <div className={classNames(`${prefixCls}-header`)}> <div className={classNames(`${prefixCls}-list`)}> <span>参考依据:</span> <Radio.Group options={timeArr} optionType={'button'} value={timeType} onChange={(e) => { setTimeType(e.target.value); }} /> </div> <div className={classNames(`${prefixCls}-list`)}> <span>建议取值:</span> <div className={classNames(`${prefixCls}-read`)}> <div className={classNames(`${prefixCls}-value`)}> <Input style={{ width: '150px', }} value={ruleData[0]} addonBefore="低低限" disabled /> </div> <div className={classNames(`${prefixCls}-value`)}> <Input style={{ width: '150px', }} value={ruleData[1]} addonBefore="低限" disabled /> </div> <div className={classNames(`${prefixCls}-value`)}> <Input style={{ width: '150px', }} value={ruleData[2]} addonBefore="高限" disabled /> </div> <div className={classNames(`${prefixCls}-value`)}> <Input style={{ width: '150px', }} value={ruleData[3]} addonBefore="高高限" disabled /> </div> </div> <div className={classNames(`${prefixCls}-range`)}> 允许浮动范围: <Slider min={0} max={100} style={{ width: '100px' }} onChange={(value) => { setSensitive(value); }} value={typeof sensitive === 'number' ? sensitive : 0} /> <InputNumber min={1} max={100} style={{ margin: '0 16px', width: '100px', }} formatter={(value) => `${value}%`} value={sensitive} onChange={(value) => { setSensitive(value); }} /> </div> </div> </div> <div className={classNames(`${prefixCls}-chart`)}> <BasicChart ref={chartRef} option={options} notMerge style={{ width: '100%', height: '100%' }} /> </div> </div> ); }; export default HistoryTrend;