Commit 70d93ba9 authored by 陈龙's avatar 陈龙

feat: 新增移动版本

parent 977a05fc
--- ---
title: EC_HistoryView - 历史曲线 title: EC_HistoryView - 历史曲线
nav: nav:
title: 业务组件 title: 业务组件
path: /extend-components path: /extend-components
group: group:
path: / path: /
--- ---
# HistoryView 历史数据查看 # HistoryView 历史数据查看
...@@ -12,9 +12,9 @@ group: ...@@ -12,9 +12,9 @@ group:
基础业务组件 基础业务组件
- 曲线模式 - 曲线模式
- 数据强制自动抽稀 - 数据强制自动抽稀
- 表格模式 - 表格模式
- 默认开启抽稀模式,可选择抽稀 - 默认开启抽稀模式,可选择抽稀
## 何时使用 ## 何时使用
...@@ -45,11 +45,31 @@ group: ...@@ -45,11 +45,31 @@ group:
```javascript ```javascript
[ [
{ {
deviceCode: 'EGBF00000146', // 设备编码 deviceCode: 'EGBF00000146', // 设备编码
sensors: '进水压力,出水瞬时流量', // 设备查询指标 sensors: '进水压力,出水瞬时流量', // 设备查询指标
deviceCode: '二供泵房', // 设备类型 deviceCode: '二供泵房', // 设备类型
}, },
... ...
] ]
``` ```
移动端参数
```javascript
{
// date
date:{
dateFrom: '2022-02-02 00:00:00',
dateTo: '2022-02-02 23:59:59',
}
// deviceParams
deviceParams:[{
deviceCode: 'EGBF00000146', // 设备编码
sensors: '进水压力,出水瞬时流量', // 设备查询指标
deviceCode: '二供泵房', // 设备类型
},...],
// chartType
chartType: 'lineChart' || 'boxChart'
}
```
import React from 'react'; import React from 'react';
import HistoryView from '../index'; import HistoryView from '../index';
import {MobileHistoryChart} from "../mobile";
const deviceParams = [ const deviceParams = [
// { // {
// deviceCode: 'EGBF00000146', // deviceCode: 'EGBF00000146',
// sensors: '进水压力,出水瞬时流量,出水累计流量', // sensors: '进水压力,出水瞬时流量,出水累计流量',
// deviceType: '二供泵房', // deviceType: '二供泵房',
// pointAddressID: 4, // pointAddressID: 4,
// }, // },
{ {
// deviceCode: 'EGBF00000002', // deviceCode: 'EGBF00000002',
// deviceCode: 'EGBF00000018', // deviceCode: 'EGBF00000018',
deviceCode: 'XMYL00000345', deviceCode: 'XMYL00000345',
// deviceCode: 'EGBF00000014', // deviceCode: 'EGBF00000014',
// sensors: '今日供水量,今日用电量,1#水箱液位,是否在线', // sensors: '今日供水量,今日用电量,1#水箱液位,是否在线',
sensors: '进水压力', sensors: '进水压力',
deviceType: '熊猫压力表', deviceType: '熊猫压力表',
pointAddressID: 4, pointAddressID: 4,
}, },
// { // {
// deviceCode: 'EGJZ00001113', // deviceCode: 'EGJZ00001113',
// sensors: '出水压力', // sensors: '出水压力',
// deviceType: '二供机组', // deviceType: '二供机组',
// pointAddressID: 4, // pointAddressID: 4,
// }, // },
]; ];
const Demo = () => { const Demo = () => {
return ( return (
<div style={{ height: 700 }}> <div>
<HistoryView deviceParams={deviceParams} defaultModel="table" /> <div style={{height: 700}}>
</div> <HistoryView deviceParams={deviceParams} defaultModel="curve"/>
); </div>
<div style={{height: 300}}>
<MobileHistoryChart deviceParams={deviceParams} chartType={'lineChart'}/>
</div>
</div>
);
}; };
export default Demo; export default Demo;
import React, { useContext, useEffect, useMemo, useState } from 'react'; import React, {useContext, useEffect, useMemo, useState} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
Checkbox, Checkbox,
ConfigProvider, ConfigProvider,
DatePicker, DatePicker,
Radio, Radio,
Select, Select,
Spin, Spin,
Tabs, Tabs,
Tooltip, Tooltip,
Button, Button,
} from 'antd'; } from 'antd';
import { import {
CloseCircleFilled, CloseCircleFilled,
PlusCircleOutlined, PlusCircleOutlined,
QuestionCircleFilled, QuestionCircleFilled,
DownloadOutlined, DownloadOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import _ from 'lodash'; import _ from 'lodash';
import TimeRangePicker from '@wisdom-components/timerangepicker'; import TimeRangePicker from '@wisdom-components/timerangepicker';
import PandaEmpty from '@wisdom-components/empty'; import PandaEmpty from '@wisdom-components/empty';
import BasicTable from '@wisdom-components/basictable'; import BasicTable from '@wisdom-components/basictable';
import { getHistoryInfo, getDeviceAlarmScheme, getExportDeviceHistoryUrl } from './apis'; import {getHistoryInfo, getDeviceAlarmScheme, getExportDeviceHistoryUrl} from './apis';
import SimgleChart from './SingleChart'; import SimgleChart from './SingleChart';
import GridChart from './GridChart'; import GridChart from './GridChart';
import './index.less'; import './index.less';
import { globalConfig } from 'antd/lib/config-provider'; import {globalConfig} from 'antd/lib/config-provider';
const { RangePicker } = DatePicker; const {RangePicker} = DatePicker;
const { Option } = Select; const {Option} = Select;
const startFormat = 'YYYY-MM-DD 00:00:00'; const startFormat = 'YYYY-MM-DD 00:00:00';
const endFormat = 'YYYY-MM-DD 23:59:59'; const endFormat = 'YYYY-MM-DD 23:59:59';
...@@ -38,887 +38,887 @@ const timeFormat = 'YYYY-MM-DD HH:mm:ss'; ...@@ -38,887 +38,887 @@ const timeFormat = 'YYYY-MM-DD HH:mm:ss';
const dateFormat = 'YYYYMMDD'; const dateFormat = 'YYYYMMDD';
const timeList = [ const timeList = [
{ {
key: 'twelveHours', key: 'twelveHours',
name: '近12小时', name: '近12小时',
}, },
{ {
key: 'roundClock', key: 'roundClock',
name: '近24小时', name: '近24小时',
}, },
{ {
key: 'oneWeek', key: 'oneWeek',
name: '近1周', name: '近1周',
}, },
{ {
key: 'oneMonth', key: 'oneMonth',
name: '近1月', name: '近1月',
}, },
]; ];
const CheckboxData = [ const CheckboxData = [
{ {
key: 'curveCenter', key: 'curveCenter',
label: '曲线居中', label: '曲线居中',
checked: false,
showInCurve: true,
showInTable: false,
},
{
key: 'chartGrid',
label: '图表网格',
checked: true,
showInCurve: true,
showInTable: false,
},
{
key: 'ignoreOutliers',
label: '数据滤波',
type: 'updateIgnoreOutliers',
checked: false,
showInCurve: true,
showInTable: true,
tooltip: '本算法采用递推平均滤波法(滑动平均滤波法)对采样数据进行均值化平滑处理。',
},
// 需求变更,剔除
/* {
key: 'justLine',
label: '仅查看曲线',
type: '',
checked: false, checked: false,
showInCurve: false, showInCurve: true,
showInTable: false, showInTable: false,
},*/ },
{ {
key: 'dataThin', key: 'chartGrid',
label: '数据抽稀', label: '图表网格',
type: 'updateDataThin', checked: true,
checked: true, showInCurve: true,
showInCurve: false, showInTable: false,
showInTable: true, },
}, {
key: 'ignoreOutliers',
label: '数据滤波',
type: 'updateIgnoreOutliers',
checked: false,
showInCurve: true,
showInTable: true,
tooltip: '本算法采用递推平均滤波法(滑动平均滤波法)对采样数据进行均值化平滑处理。',
},
// 需求变更,剔除
/* {
key: 'justLine',
label: '仅查看曲线',
type: '',
checked: false,
showInCurve: false,
showInTable: false,
},*/
{
key: 'dataThin',
label: '数据抽稀',
type: 'updateDataThin',
checked: true,
showInCurve: false,
showInTable: true,
},
]; ];
const timeIntervalList = [ const timeIntervalList = [
{ {
key: '5', key: '5',
zoom: '5', zoom: '5',
unit: 'min', unit: 'min',
name: '5分钟', name: '5分钟',
}, },
{ {
key: '10', key: '10',
zoom: '10', zoom: '10',
unit: 'min', unit: 'min',
name: '10分钟', name: '10分钟',
}, },
{ {
key: '30', key: '30',
zoom: '30', zoom: '30',
unit: 'min', unit: 'min',
name: '30分钟', name: '30分钟',
}, },
{ {
key: '1', key: '1',
zoom: '1', zoom: '1',
unit: 'h', unit: 'h',
name: '1小时', name: '1小时',
}, },
{ {
key: '2', key: '2',
zoom: '2', zoom: '2',
unit: 'h', unit: 'h',
name: '2小时', name: '2小时',
}, },
{ {
key: '4', key: '4',
zoom: '4', zoom: '4',
unit: 'h', unit: 'h',
name: '4小时', name: '4小时',
}, },
{ {
key: '6', key: '6',
zoom: '6', zoom: '6',
unit: 'h', unit: 'h',
name: '6小时', name: '6小时',
}, },
{ {
key: '12', key: '12',
zoom: '12', zoom: '12',
unit: 'h', unit: 'h',
name: '12小时', name: '12小时',
}, },
]; ];
const updateTime = (key) => { const updateTime = (key) => {
let start = ''; let start = '';
let end = ''; let end = '';
if (Array.isArray(key)) { if (Array.isArray(key)) {
start = moment(key[0]).format(timeFormat); start = moment(key[0]).format(timeFormat);
end = moment(key[1]).format(timeFormat); end = moment(key[1]).format(timeFormat);
} else { } else {
switch (key) { switch (key) {
case 'twelveHours': case 'twelveHours':
start = moment().subtract(12, 'hour').format(timeFormat); start = moment().subtract(12, 'hour').format(timeFormat);
end = moment().format(timeFormat); end = moment().format(timeFormat);
break; break;
case 'roundClock': case 'roundClock':
start = moment().subtract(24, 'hour').format(timeFormat); start = moment().subtract(24, 'hour').format(timeFormat);
end = moment().format(timeFormat); end = moment().format(timeFormat);
break; break;
case 'oneWeek': case 'oneWeek':
start = moment().subtract(7, 'day').format(timeFormat); start = moment().subtract(7, 'day').format(timeFormat);
end = moment().format(timeFormat); end = moment().format(timeFormat);
break; break;
case 'oneMonth': case 'oneMonth':
start = moment().subtract(30, 'day').format(timeFormat); start = moment().subtract(30, 'day').format(timeFormat);
end = moment().format(timeFormat); end = moment().format(timeFormat);
break; break;
}
} }
} return [
return [ {
{ dateFrom: start,
dateFrom: start, dateTo: end,
dateTo: end, },
}, ];
];
}; };
const DefaultDatePicker = (value) => [ const DefaultDatePicker = (value) => [
{ {
key: 1, key: 1,
value: moment(), value: moment(),
}, },
{ {
key: 2, key: 2,
value: moment().subtract(1, value), value: moment().subtract(1, value),
}, },
]; ];
const handleBatchTime = (arr, cOption) => { const handleBatchTime = (arr, cOption) => {
let newArr = []; let newArr = [];
arr.forEach((child) => { arr.forEach((child) => {
if (child.value) { if (child.value) {
newArr.push({ newArr.push({
dateFrom: moment(child.value).startOf(cOption).format(startFormat), dateFrom: moment(child.value).startOf(cOption).format(startFormat),
dateTo: moment(child.value).endOf(cOption).format(endFormat), dateTo: moment(child.value).endOf(cOption).format(endFormat),
}); });
} }
}); });
newArr = _.uniqWith(newArr, _.isEqual); // 去掉重复日期时间 newArr = _.uniqWith(newArr, _.isEqual); // 去掉重复日期时间
return newArr; return newArr;
}; };
const timeColumn = { const timeColumn = {
title: '采集时间', title: '采集时间',
dataIndex: 'time', dataIndex: 'time',
key: 'time', key: 'time',
width: 170, width: 170,
fixed: 'left', fixed: 'left',
ellipsis: true, ellipsis: true,
align: 'center', align: 'center',
}; };
const HistoryView = (props) => { const HistoryView = (props) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const {getPrefixCls} = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('history-view'); const prefixCls = getPrefixCls('history-view');
const { const {
title, title,
grid, grid,
defaultChecked, defaultChecked,
tableProps, tableProps,
deviceParams, deviceParams,
defaultModel, defaultModel,
showModels, showModels,
needMarkLine, needMarkLine,
defaultDate, defaultDate,
} = props; } = props;
const isBoxPlots = const isBoxPlots =
deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1; deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1;
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [activeTabKey, setActiveTabKey] = useState(defaultModel); const [activeTabKey, setActiveTabKey] = useState(defaultModel);
// 时间模式: 自定义模式/同期对比模式 // 时间模式: 自定义模式/同期对比模式
const [timeValue, setTimeValue] = useState('customer'); const [timeValue, setTimeValue] = useState('customer');
// 自定义模式 // 自定义模式
const [customerChecked, setCustomerChecked] = useState(defaultChecked); // 时间快速选择类型值 const [customerChecked, setCustomerChecked] = useState(defaultChecked); // 时间快速选择类型值
const [customerTime, setCustomerTime] = useState(); // 自定义时间选择值 const [customerTime, setCustomerTime] = useState(); // 自定义时间选择值
// 同期对比模式 // 同期对比模式
const [contrastOption, setContrastOption] = useState('day'); // 对比时间类型: 日/月 const [contrastOption, setContrastOption] = useState('day'); // 对比时间类型: 日/月
const [datePickerArr, setDatePickerArr] = useState(DefaultDatePicker(defaultDate)); // 对比时间段配置值 const [datePickerArr, setDatePickerArr] = useState(DefaultDatePicker(defaultDate)); // 对比时间段配置值
const [checkboxData, setCheckboxData] = useState(() => [...CheckboxData]); // 曲线设置项 const [checkboxData, setCheckboxData] = useState(() => [...CheckboxData]); // 曲线设置项
const [dataThinKey, setDataThinKey] = useState(timeIntervalList[0].key); // 曲线抽稀时间设置 const [dataThinKey, setDataThinKey] = useState(timeIntervalList[0].key); // 曲线抽稀时间设置
const [columns, setColumns] = useState([]); const [columns, setColumns] = useState([]);
const [tableData, setTableData] = useState([]); const [tableData, setTableData] = useState([]);
const [chartDataSource, setChartDataSource] = useState([]); const [chartDataSource, setChartDataSource] = useState([]);
const [chartType, setChartType] = useState('lineChart'); const [chartType, setChartType] = useState('lineChart');
const [showBoxOption, setShowBoxOption] = useState(true); const [showBoxOption, setShowBoxOption] = useState(true);
// 选择的时间范围值 // 选择的时间范围值
const dateRange = useMemo(() => { const dateRange = useMemo(() => {
if (timeValue === 'customer') { if (timeValue === 'customer') {
return updateTime(customerChecked || customerTime); return updateTime(customerChecked || customerTime);
} else { } else {
return handleBatchTime(datePickerArr, contrastOption); return handleBatchTime(datePickerArr, contrastOption);
} }
}, [contrastOption, customerChecked, customerTime, datePickerArr, timeValue]); }, [contrastOption, customerChecked, customerTime, datePickerArr, timeValue]);
const configDependence = checkboxData const configDependence = checkboxData
.filter((item) => ['curveCenter', 'chartGrid'].indexOf(item.key) === -1) .filter((item) => ['curveCenter', 'chartGrid'].indexOf(item.key) === -1)
.map((item) => item.checked) .map((item) => item.checked)
.join(','); .join(',');
// 数据配置 // 数据配置
const dataConfig = useMemo(() => { const dataConfig = useMemo(() => {
const initial = { const initial = {
ignoreOutliers: false, ignoreOutliers: false,
dataThin: false, dataThin: false,
zoom: '', // 数据抽稀时间 zoom: '', // 数据抽稀时间
unit: '', // 数据抽稀时间单位 unit: '', // 数据抽稀时间单位
};
// 曲线居中,过滤异常值,数据抽稀
const config = checkboxData.reduce(
(pre, item) => (item.key !== 'curveCenter' && (pre[item.key] = item.checked), pre),
initial,
);
// 数据抽稀时间单位
const dataThin = timeIntervalList.find((item) => item.key === dataThinKey);
config.zoom = activeTabKey === 'curve' ? '' : dataThin?.zoom ?? '';
config.unit = activeTabKey === 'curve' ? '' : dataThin?.unit ?? '';
config.dataThin = activeTabKey === 'curve' ? true : config.dataThin; // 曲线强制抽稀
return config;
}, [configDependence, dataThinKey, activeTabKey]);
// 图表居中
const [curveCenter, chartGrid] = useMemo(() => {
const curveCenter = checkboxData.find((item) => item.key === 'curveCenter')?.checked;
const chartGrid = checkboxData.find((item) => item.key === 'chartGrid')?.checked;
return [curveCenter, chartGrid];
}, [checkboxData]);
// 自定义模式: 快速选择
const onCustomerTimeChange = (key) => {
setCustomerChecked(key);
!!customerTime && setCustomerTime(null);
}; };
// 曲线居中,过滤异常值,数据抽稀
const config = checkboxData.reduce(
(pre, item) => (item.key !== 'curveCenter' && (pre[item.key] = item.checked), pre),
initial,
);
// 数据抽稀时间单位
const dataThin = timeIntervalList.find((item) => item.key === dataThinKey);
config.zoom = activeTabKey === 'curve' ? '' : dataThin?.zoom ?? '';
config.unit = activeTabKey === 'curve' ? '' : dataThin?.unit ?? '';
config.dataThin = activeTabKey === 'curve' ? true : config.dataThin; // 曲线强制抽稀
return config;
}, [configDependence, dataThinKey, activeTabKey]);
// 图表居中
const [curveCenter, chartGrid] = useMemo(() => {
const curveCenter = checkboxData.find((item) => item.key === 'curveCenter')?.checked;
const chartGrid = checkboxData.find((item) => item.key === 'chartGrid')?.checked;
return [curveCenter, chartGrid];
}, [checkboxData]);
// 自定义模式: 快速选择
const onCustomerTimeChange = (key) => {
setCustomerChecked(key);
!!customerTime && setCustomerTime(null);
};
// 自定义模式: 自定义时间选择
const onCustomerRangeChange = (value) => {
if (!value) {
// 时间清空,回到默认时间选择
setCustomerChecked(defaultChecked);
setCustomerTime(value);
} else {
setCustomerChecked(null);
setCustomerTime(value);
}
};
// 同期对比模式: 选择(日/月)
const onContrastChange = (value) => {
setContrastOption(value);
// 模式为日时,默认对比时间根据defaultDate判断 是昨天、上月、还是去年
setDatePickerArr([...DefaultDatePicker(value==='day'&&defaultDate?defaultDate:value)]);
};
// 同期对比模式: 时间段选择
const onContrastPickerChange = (date, dateString, item) => {
const arr = [...datePickerArr];
arr.forEach((child) => {
if (child.key === item.key) {
child.value = date;
}
});
setDatePickerArr(arr);
};
// 同期对比模式: 新增日期选择组件
const handleAddDatePicker = () => {
setDatePickerArr([
...datePickerArr,
{
key: datePickerArr[datePickerArr.length - 1].key + 1,
value: '',
},
]);
};
// 同期对比模式: 删除日期选择组件
const handleDeleteDatePicker = (index) => {
const arr = [...datePickerArr];
arr.splice(index, 1);
setDatePickerArr(arr);
};
// 时间设置切换(自定义/同期对比)
const onTimeSetChange = (e) => {
setTimeValue(e.target.value);
if (e.target.value === 'contrast') {
// 同期对比
onContrastChange(contrastOption);
setShowBoxOption(false);
setChartType('lineChart');
onCheckboxChange({ target: { value: false } }, 'chartType');
onCheckboxChange({ target: { value: false } }, 'ignoreOutliers');
} else {
// 自定义
// 不需要处理
setShowBoxOption(true);
onCheckboxChange({ target: { value: true } }, 'chartType');
}
};
const renderTimeOption = () => { // 自定义模式: 自定义时间选择
return ( const onCustomerRangeChange = (value) => {
<div className={classNames(`${prefixCls}-date`)}> if (!value) {
<div className={classNames(`${prefixCls}-label`)}>时间选择</div> // 时间清空,回到默认时间选择
<Radio.Group value={timeValue} onChange={onTimeSetChange}> setCustomerChecked(defaultChecked);
<Radio.Button value="customer">自定义</Radio.Button> setCustomerTime(value);
<Radio.Button value="contrast">同期对比</Radio.Button> } else {
</Radio.Group> setCustomerChecked(null);
{timeValue === 'customer' && ( // 自定义 setCustomerTime(value);
<> }
<TimeRangePicker };
onChange={onCustomerTimeChange}
value={customerChecked}
dataSource={timeList}
/>
<RangePicker
className={classNames(`${prefixCls}-custime-customer`)}
onChange={onCustomerRangeChange}
value={customerTime}
showTime
/>
</>
)}
{timeValue === 'contrast' && ( // 同期对比
<>
<Select value={contrastOption} style={{ width: 60 }} onChange={onContrastChange}>
<Option value="day"></Option>
<Option value="month"></Option>
</Select>
{datePickerArr.map((child, index) => (
<div key={child.key} className={classNames(`${prefixCls}-contrast-list`)}>
<div className={classNames(`${prefixCls}-contrast-wrap`)}>
<DatePicker
picker={contrastOption}
value={child.value}
onChange={(date, dateString) => onContrastPickerChange(date, dateString, child)}
/>
{datePickerArr.length > 2 && (
<div
className={classNames(`${prefixCls}-contrast-delete`)}
onClick={() => handleDeleteDatePicker(index)}
>
<CloseCircleFilled />
</div>
)}
</div>
{index < datePickerArr.length - 1 && (
<div className={classNames(`${prefixCls}-contrast-connect`)}></div>
)}
</div>
))}
{datePickerArr.length < 5 && <PlusCircleOutlined onClick={handleAddDatePicker} />}
</>
)}
</div>
);
};
// 曲线设置项选择/取消
const onCheckboxChange = (e, key, showJustLine) => {
let data = [...checkboxData];
// let _index = data.findIndex(item => item.key === 'justLine'); // 仅查看曲线会在勾选了数据滤波后展示
let _index1 = data.findIndex((item) => item.key === 'ignoreOutliers'); // 仅查看曲线会在勾选了数据滤波后展示
data.forEach((item) => {
if (item.key === key) {
item.checked = e.target.checked;
}
});
if (key === 'ignoreOutliers') {
// 需求变更,仅查看曲线剔除
/* if (showJustLine) {
data[_index].showInCurve = e.target.checked;
data[_index].checked = e.target.checked;
} else {*/
data[_index1].showInCurve = true;
// data[_index1].checked = false;
// }
}
if (key === 'chartType') { // 同期对比模式: 选择(日/月)
data[_index1].showInCurve = e.target.value; const onContrastChange = (value) => {
data[_index1].checked = false; setContrastOption(value);
// data[_index].showInCurve = false; // 模式为日时,默认对比时间根据defaultDate判断 是昨天、上月、还是去年
// data[_index].checked = false; setDatePickerArr([...DefaultDatePicker(value === 'day' && defaultDate ? defaultDate : value)]);
} };
setCheckboxData(data);
};
// 数据抽稀时间间隔 // 同期对比模式: 时间段选择
const onTimeIntervalChange = (value) => { const onContrastPickerChange = (date, dateString, item) => {
setDataThinKey(value); const arr = [...datePickerArr];
}; arr.forEach((child) => {
if (child.key === item.key) {
child.value = date;
}
});
setDatePickerArr(arr);
};
const renderCheckbox = (child, showJustLine) => { // 同期对比模式: 新增日期选择组件
const curveAccess = activeTabKey === 'curve' && child.showInCurve; const handleAddDatePicker = () => {
const tableAccess = activeTabKey === 'table' && child.showInTable; setDatePickerArr([
const gridOptions = ['curveCenter']; ...datePickerArr,
{
key: datePickerArr[datePickerArr.length - 1].key + 1,
value: '',
},
]);
};
if (grid && curveAccess && gridOptions.indexOf(child.key) === -1) return null; // 同期对比模式: 删除日期选择组件
return ( const handleDeleteDatePicker = (index) => {
(curveAccess || tableAccess) && ( const arr = [...datePickerArr];
<> arr.splice(index, 1);
<Checkbox checked={child.checked} onChange={(e) => onCheckboxChange(e, child.key)}> setDatePickerArr(arr);
{child.label} };
</Checkbox>
{child.tooltip && (
<Tooltip title={child.tooltip}>
<QuestionCircleFilled className={`${prefixCls}-question`} />
</Tooltip>
)}
</>
)
);
};
const renderCurveOption = (isChart, isSingle) => { // 时间设置切换(自定义/同期对比)
return ( const onTimeSetChange = (e) => {
<div setTimeValue(e.target.value);
className={classNames(`${prefixCls}-cover`)} if (e.target.value === 'contrast') {
style={isChart && isSingle ? { width: '100%' } : {}} // 同期对比
> onContrastChange(contrastOption);
{isChart && isSingle && showBoxOption ? ( setShowBoxOption(false);
<> setChartType('lineChart');
<div className={classNames(`${prefixCls}-label`)}>曲线形态</div> onCheckboxChange({target: {value: false}}, 'chartType');
<Radio.Group onCheckboxChange({target: {value: false}}, 'ignoreOutliers');
value={chartType} } else {
style={{ marginRight: 16 }} // 自定义
onChange={(e) => { // 不需要处理
let _value = e.target.value; setShowBoxOption(true);
setChartType(_value); onCheckboxChange({target: {value: true}}, 'chartType');
onCheckboxChange({ target: { value: _value !== 'boxChart' } }, 'chartType'); }
}} };
>
<Radio.Button value={'lineChart'}>线形图</Radio.Button> const renderTimeOption = () => {
<Radio.Button value={'boxChart'}>箱线图</Radio.Button> return (
</Radio.Group> <div className={classNames(`${prefixCls}-date`)}>
</> <div className={classNames(`${prefixCls}-label`)}>时间选择</div>
) : ( <Radio.Group value={timeValue} onChange={onTimeSetChange}>
'' <Radio.Button value="customer">自定义</Radio.Button>
)} <Radio.Button value="contrast">同期对比</Radio.Button>
<div className={classNames(`${prefixCls}-label`)}> </Radio.Group>
{activeTabKey !== 'table' ? '曲线设置' : '表格设置'} {timeValue === 'customer' && ( // 自定义
</div> <>
{checkboxData.map((child) => { <TimeRangePicker
const box = renderCheckbox(child, isChart && isSingle); onChange={onCustomerTimeChange}
if (!box) return null; value={customerChecked}
return ( dataSource={timeList}
<div key={child.key} className={`${prefixCls}-cover-item`}> />
{box} <RangePicker
className={classNames(`${prefixCls}-custime-customer`)}
onChange={onCustomerRangeChange}
value={customerTime}
showTime
/>
</>
)}
{timeValue === 'contrast' && ( // 同期对比
<>
<Select value={contrastOption} style={{width: 60}} onChange={onContrastChange}>
<Option value="day"></Option>
<Option value="month"></Option>
</Select>
{datePickerArr.map((child, index) => (
<div key={child.key} className={classNames(`${prefixCls}-contrast-list`)}>
<div className={classNames(`${prefixCls}-contrast-wrap`)}>
<DatePicker
picker={contrastOption}
value={child.value}
onChange={(date, dateString) => onContrastPickerChange(date, dateString, child)}
/>
{datePickerArr.length > 2 && (
<div
className={classNames(`${prefixCls}-contrast-delete`)}
onClick={() => handleDeleteDatePicker(index)}
>
<CloseCircleFilled/>
</div>
)}
</div>
{index < datePickerArr.length - 1 && (
<div className={classNames(`${prefixCls}-contrast-connect`)}></div>
)}
</div>
))}
{datePickerArr.length < 5 && <PlusCircleOutlined onClick={handleAddDatePicker}/>}
</>
)}
</div> </div>
); );
})} };
{activeTabKey === 'table' && (
<Select
value={dataThinKey}
style={{ width: 90 }}
onChange={onTimeIntervalChange}
disabled={!dataConfig.dataThin}
>
{timeIntervalList.map((child) => (
<Option key={child.key} unit={child.unit} value={child.key}>
{child.name}
</Option>
))}
</Select>
)}
</div>
);
};
const exportExcelBtn = () => {
deviceParams.forEach((i, r) => {
let timeFrom = dateRange[r]?.dateFrom || moment().format(startFormat);
let timeTo = dateRange[r]?.dateTo || moment().format(timeFormat);
let fileName = `数据报表-${i.deviceType}-${i.deviceCode}-${moment(timeFrom).format(
dateFormat,
)}${moment(timeTo).format(dateFormat)}`;
getExportDeviceHistoryUrl({
deviceType: i.deviceType,
deviceCode: i.deviceCode,
quotas: i.sensors,
startTime: timeFrom,
endTime: timeTo,
fileName: fileName,
})
.then((res) => {
if (res && res.code === -1) return message.error(res.msg);
const url = `${window.location.origin}/PandaCore/GCK/FileHandleContoller/Download/name?name=${res.data}&_site=${globalConfig?.userInfo?.site}`;
const aDom = document.createElement('a');
aDom.href = url;
aDom.click();
aDom.remove();
})
.catch((err) => {});
});
};
const handleTableData = (data) => { // 曲线设置项选择/取消
const ignoreOutliers = checkboxData.find((item) => item.key === 'ignoreOutliers').checked; const onCheckboxChange = (e, key, showJustLine) => {
const dataIndexAccess = (dataItem, index) => { let data = [...checkboxData];
const { stationCode, sensorName } = dataItem; // let _index = data.findIndex(item => item.key === 'justLine'); // 仅查看曲线会在勾选了数据滤波后展示
return `${stationCode}-${sensorName}-${index}`; let _index1 = data.findIndex((item) => item.key === 'ignoreOutliers'); // 仅查看曲线会在勾选了数据滤波后展示
data.forEach((item) => {
if (item.key === key) {
item.checked = e.target.checked;
}
});
if (key === 'ignoreOutliers') {
// 需求变更,仅查看曲线剔除
/* if (showJustLine) {
data[_index].showInCurve = e.target.checked;
data[_index].checked = e.target.checked;
} else {*/
data[_index1].showInCurve = true;
// data[_index1].checked = false;
// }
}
if (key === 'chartType') {
data[_index1].showInCurve = e.target.value;
data[_index1].checked = false;
// data[_index].showInCurve = false;
// data[_index].checked = false;
}
setCheckboxData(data);
}; };
let format = timeFormat; // 数据抽稀时间间隔
if (timeValue === 'contrast') { const onTimeIntervalChange = (value) => {
format = contrastOption === 'day' ? '2020-01-01 HH:mm:00' : '2020-01-DD HH:mm:00'; setDataThinKey(value);
} };
// 处理表头数据 const renderCheckbox = (child, showJustLine) => {
const columnsData = data.map((item, index) => { const curveAccess = activeTabKey === 'curve' && child.showInCurve;
const { stationCode, equipmentName, sensorName, unit, dataModel } = item; const tableAccess = activeTabKey === 'table' && child.showInTable;
const dataIndex = dataIndexAccess(item, index); const gridOptions = ['curveCenter'];
let col = {
title: `${equipmentName}-${sensorName}${unit ? `(${unit})` : ''}`, if (grid && curveAccess && gridOptions.indexOf(child.key) === -1) return null;
dataIndex: dataIndex, return (
key: dataIndex, (curveAccess || tableAccess) && (
ellipsis: true, <>
align: 'center', <Checkbox checked={child.checked} onChange={(e) => onCheckboxChange(e, child.key)}>
}; {child.label}
// 同期对比 </Checkbox>
if (timeValue === 'contrast' && dataModel[0]) { {child.tooltip && (
const time = item.dataModel[0].pt <Tooltip title={child.tooltip}>
.slice(0, contrastOption === 'day' ? 10 : 7) <QuestionCircleFilled className={`${prefixCls}-question`}/>
.replace(/-/g, ''); </Tooltip>
col.title = `${equipmentName}-${sensorName}-${time}`; )}
} </>
return col; )
}); );
};
// 格式化时间对齐数据, 生成行数 const renderCurveOption = (isChart, isSingle) => {
const timeData = {}; return (
<div
className={classNames(`${prefixCls}-cover`)}
style={isChart && isSingle ? {width: '100%'} : {}}
>
{isChart && isSingle && showBoxOption ? (
<>
<div className={classNames(`${prefixCls}-label`)}>曲线形态</div>
<Radio.Group
value={chartType}
style={{marginRight: 16}}
onChange={(e) => {
let _value = e.target.value;
setChartType(_value);
onCheckboxChange({target: {value: _value !== 'boxChart'}}, 'chartType');
}}
>
<Radio.Button value={'lineChart'}>线形图</Radio.Button>
<Radio.Button value={'boxChart'}>箱线图</Radio.Button>
</Radio.Group>
</>
) : (
''
)}
<div className={classNames(`${prefixCls}-label`)}>
{activeTabKey !== 'table' ? '曲线设置' : '表格设置'}
</div>
{checkboxData.map((child) => {
const box = renderCheckbox(child, isChart && isSingle);
if (!box) return null;
return (
<div key={child.key} className={`${prefixCls}-cover-item`}>
{box}
</div>
);
})}
{activeTabKey === 'table' && (
<Select
value={dataThinKey}
style={{width: 90}}
onChange={onTimeIntervalChange}
disabled={!dataConfig.dataThin}
>
{timeIntervalList.map((child) => (
<Option key={child.key} unit={child.unit} value={child.key}>
{child.name}
</Option>
))}
</Select>
)}
</div>
);
};
const buildDefaultData = (time) => { const exportExcelBtn = () => {
const obj = { key: time, time: time }; deviceParams.forEach((i, r) => {
data.forEach((item, index) => { let timeFrom = dateRange[r]?.dateFrom || moment().format(startFormat);
const dataIndex = dataIndexAccess(item, index); let timeTo = dateRange[r]?.dateTo || moment().format(timeFormat);
obj[dataIndex] = ''; let fileName = `数据报表-${i.deviceType}-${i.deviceCode}-${moment(timeFrom).format(
}); dateFormat,
return obj; )}${moment(timeTo).format(dateFormat)}`;
getExportDeviceHistoryUrl({
deviceType: i.deviceType,
deviceCode: i.deviceCode,
quotas: i.sensors,
startTime: timeFrom,
endTime: timeTo,
fileName: fileName,
})
.then((res) => {
if (res && res.code === -1) return message.error(res.msg);
const url = `${window.location.origin}/PandaCore/GCK/FileHandleContoller/Download/name?name=${res.data}&_site=${globalConfig?.userInfo?.site}`;
const aDom = document.createElement('a');
aDom.href = url;
aDom.click();
aDom.remove();
})
.catch((err) => {
});
});
}; };
data.forEach((item, index) => {
const { stationCode, sensorName, dataModel } = item; const handleTableData = (data) => {
dataModel && const ignoreOutliers = checkboxData.find((item) => item.key === 'ignoreOutliers').checked;
dataModel.forEach((data) => { const dataIndexAccess = (dataItem, index) => {
const formatTime = moment(data.pt).format(format); const {stationCode, sensorName} = dataItem;
return `${stationCode}-${sensorName}-${index}`;
let time = formatTime; };
if (timeValue === 'contrast') {
time = time.slice(contrastOption === 'day' ? 11 : 8, 16); let format = timeFormat;
} if (timeValue === 'contrast') {
format = contrastOption === 'day' ? '2020-01-01 HH:mm:00' : '2020-01-DD HH:mm:00';
timeData[formatTime] = timeData[formatTime] || buildDefaultData(time); }
// 处理表头数据
const columnsData = data.map((item, index) => {
const {stationCode, equipmentName, sensorName, unit, dataModel} = item;
const dataIndex = dataIndexAccess(item, index);
let col = {
title: `${equipmentName}-${sensorName}${unit ? `(${unit})` : ''}`,
dataIndex: dataIndex,
key: dataIndex,
ellipsis: true,
align: 'center',
};
// 同期对比
if (timeValue === 'contrast' && dataModel[0]) {
const time = item.dataModel[0].pt
.slice(0, contrastOption === 'day' ? 10 : 7)
.replace(/-/g, '');
col.title = `${equipmentName}-${sensorName}-${time}`;
}
return col;
}); });
});
// 处理表格数据 // 格式化时间对齐数据, 生成行数
data.forEach((child, index) => { const timeData = {};
const { dataModel } = child;
const dataIndex = dataIndexAccess(child, index); const buildDefaultData = (time) => {
dataModel && const obj = {key: time, time: time};
dataModel.forEach((value, j) => { data.forEach((item, index) => {
const formatTime = moment(value.pt).format(format); const dataIndex = dataIndexAccess(item, index);
const dataRow = timeData[formatTime]; obj[dataIndex] = '';
if (dataRow) { });
dataRow[dataIndex] = value.pv === null || value.pv === undefined ? '' : value.pv; return obj;
} };
data.forEach((item, index) => {
const {stationCode, sensorName, dataModel} = item;
dataModel &&
dataModel.forEach((data) => {
const formatTime = moment(data.pt).format(format);
let time = formatTime;
if (timeValue === 'contrast') {
time = time.slice(contrastOption === 'day' ? 11 : 8, 16);
}
timeData[formatTime] = timeData[formatTime] || buildDefaultData(time);
});
}); });
});
const timeSort = (a, b) => { // 处理表格数据
let aa = a, data.forEach((child, index) => {
bb = b; const {dataModel} = child;
if (timeValue === 'contrast') { const dataIndex = dataIndexAccess(child, index);
aa = a.slice(contrastOption === 'day' ? 11 : 8, 16); dataModel &&
bb = b.slice(contrastOption === 'day' ? 11 : 8, 16); dataModel.forEach((value, j) => {
} const formatTime = moment(value.pt).format(format);
return aa.localeCompare(bb); const dataRow = timeData[formatTime];
if (dataRow) {
dataRow[dataIndex] = value.pv === null || value.pv === undefined ? '' : value.pv;
}
});
});
const timeSort = (a, b) => {
let aa = a,
bb = b;
if (timeValue === 'contrast') {
aa = a.slice(contrastOption === 'day' ? 11 : 8, 16);
bb = b.slice(contrastOption === 'day' ? 11 : 8, 16);
}
return aa.localeCompare(bb);
};
const times = Object.keys(timeData).sort(timeSort);
const tableData = times.map((time) => timeData[time]);
setColumns([timeColumn, ...columnsData]);
setTableData(tableData);
}; };
const times = Object.keys(timeData).sort(timeSort);
const tableData = times.map((time) => timeData[time]); const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState([]);
setColumns([timeColumn, ...columnsData]); const beforChangeParams = (value = {}) => {
setTableData(tableData); if (!needMarkLine) return Promise.resolve();
}; return getDeviceAlarmScheme({
data: deviceParams.map((item) => ({
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState([]); deviceType: item.deviceType,
const beforChangeParams = (value = {}) => { deviceCode: item.deviceCode,
if (!needMarkLine) return Promise.resolve(); pointAddressID: item.pointAddressID,
return getDeviceAlarmScheme({ sensorName: item.sensors,
data: deviceParams.map((item) => ({ })),
deviceType: item.deviceType, })
deviceCode: item.deviceCode, .then((res) => {
pointAddressID: item.pointAddressID, if (res.code === 0) setDeviceAlarmSchemes(res.data || []);
sensorName: item.sensors, else setDeviceAlarmSchemes([]);
})), return Promise.resolve();
}) })
.then((res) => { .catch((err) => {
if (res.code === 0) setDeviceAlarmSchemes(res.data || []); setDeviceAlarmSchemes([]);
else setDeviceAlarmSchemes([]); return Promise.resolve();
return Promise.resolve();
})
.catch((err) => {
setDeviceAlarmSchemes([]);
return Promise.resolve();
});
};
const handleDataThinKey = (diffDays) => {
// edit by zy 根据选择的时长控制抽稀频度
if (diffDays >= 7 && diffDays < 15) {
return { unit: 'h', zoom: '2' };
} else if (diffDays >= 15 && diffDays < 30) {
return { unit: 'h', zoom: '4' };
} else if (diffDays >= 30) {
return { unit: 'h', zoom: '6' };
} else if (diffDays < 7 && diffDays >= 2) {
return { unit: 'min', zoom: '40' };
} else if (diffDays < 2 && diffDays >= 1) {
return { unit: 'min', zoom: '30' };
} else {
return { unit: 'min', zoom: '10' };
}
};
// 处理接口服务参数的变化
const onChangeParams = (value = {}) => {
const { dateRange, isDilute, ignoreOutliers, zoom, unit } = value;
const requestArr = [];
const acrossTables = [];
deviceParams
.map((item) => {
let _item = { ...item };
_item.sensors =
item.sensors && !item.sensors.includes('是否在线')
? item.sensors + ',是否在线'
: item.sensors;
return _item;
})
.forEach((i) => {
if (i.sensors && i.deviceCode && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID']));
});
if (!acrossTables?.length) {
handleTableData([]);
setChartDataSource([]);
return;
}
dateRange.forEach((item) => {
// let _showLine = checkboxData.find(item => item.key === 'justLine');
const param = {
isDilute,
zoom,
unit,
ignoreOutliers,
// isVertical: false, // 是否查询竖表
dateFrom: item.dateFrom,
dateTo: item.dateTo,
acrossTables,
isBoxPlots: isBoxPlots,
};
let diffDays = moment(item.dateTo).diff(moment(item.dateFrom), 'days');
let zoomParam = activeTabKey === 'curve' ? handleDataThinKey(diffDays) : {};
requestArr.push(getHistoryInfo({ ...param, ...zoomParam }));
});
setLoading(true);
Promise.all(requestArr).then((results) => {
if (results.length) {
let data = [];
results.forEach((res, index) => {
const { dateFrom, dateTo } = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) {
res.data.forEach((d) => {
d.dateFrom = dateFrom || '';
d.dateTo = dateTo || '';
}); });
deviceParams.forEach((p) => { };
// 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? []; const handleDataThinKey = (diffDays) => {
const list = sensors.map((s) => { // edit by zy 根据选择的时长控制抽稀频度
const dataItem = res.data.find( if (diffDays >= 7 && diffDays < 15) {
(d) => d.stationCode === p.deviceCode && d.sensorName === s, return {unit: 'h', zoom: '2'};
); } else if (diffDays >= 15 && diffDays < 30) {
if (dataItem) { return {unit: 'h', zoom: '4'};
dataItem.dateFrom = dateFrom || ''; } else if (diffDays >= 30) {
dataItem.dateTo = dateTo || ''; return {unit: 'h', zoom: '6'};
return dataItem; } else if (diffDays < 7 && diffDays >= 2) {
} else { return {unit: 'min', zoom: '40'};
return {}; } else if (diffDays < 2 && diffDays >= 1) {
} return {unit: 'min', zoom: '30'};
}); } else {
data = data.concat(list); return {unit: 'min', zoom: '10'};
}
};
// 处理接口服务参数的变化
const onChangeParams = (value = {}) => {
const {dateRange, isDilute, ignoreOutliers, zoom, unit} = value;
const requestArr = [];
const acrossTables = [];
deviceParams
.map((item) => {
let _item = {...item};
_item.sensors =
item.sensors && !item.sensors.includes('是否在线')
? item.sensors + ',是否在线'
: item.sensors;
return _item;
})
.forEach((i) => {
if (i.sensors && i.deviceCode && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID']));
}); });
} if (!acrossTables?.length) {
handleTableData([]);
setChartDataSource([]);
return;
}
dateRange.forEach((item) => {
const param = {
isDilute,
zoom,
unit,
ignoreOutliers,
dateFrom: item.dateFrom,
dateTo: item.dateTo,
acrossTables,
isBoxPlots: isBoxPlots,
};
let diffDays = moment(item.dateTo).diff(moment(item.dateFrom), 'days');
let zoomParam = activeTabKey === 'curve' ? handleDataThinKey(diffDays) : {};
requestArr.push(getHistoryInfo({...param, ...zoomParam}));
}); });
setLoading(false); setLoading(true);
handleTableData(data); Promise.all(requestArr).then((results) => {
setChartDataSource(data); if (results.length) {
} let data = [];
}); results.forEach((res, index) => {
}; const {dateFrom, dateTo} = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) {
useEffect(() => { res.data.forEach((d) => {
const { dataThin, ignoreOutliers, zoom, unit } = dataConfig; d.dateFrom = dateFrom || '';
beforChangeParams().finally(() => { d.dateTo = dateTo || '';
onChangeParams({ });
isDilute: dataThin, deviceParams.forEach((p) => {
ignoreOutliers, // 返回数据按查询指标顺序排序
zoom, const sensors = p.sensors?.split(',') ?? [];
unit, const list = sensors.map((s) => {
dateRange, const dataItem = res.data.find(
isBoxPlots: isBoxPlots, (d) => d.stationCode === p.deviceCode && d.sensorName === s,
}); );
}); if (dataItem) {
}, [dateRange, dataConfig, deviceParams, chartType]); dataItem.dateFrom = dateFrom || '';
dataItem.dateTo = dateTo || '';
const renderPanel = (model) => { return dataItem;
if (model === 'curve') { } else {
return ( return {};
<> }
<div className={`${prefixCls}-options`}> });
{renderTimeOption()} data = data.concat(list);
{renderCurveOption( });
true, }
deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1, });
)} setLoading(false);
</div> handleTableData(data);
<div className={`${prefixCls}-content`}> setChartDataSource(data);
{!chartDataSource.length ? ( }
<PandaEmpty /> });
) : grid === true ? ( };
<GridChart
curveCenter={curveCenter} useEffect(() => {
prefixCls={prefixCls} const {dataThin, ignoreOutliers, zoom, unit} = dataConfig;
dataSource={chartDataSource} beforChangeParams().finally(() => {
contrast={timeValue === 'contrast'} onChangeParams({
contrastOption={contrastOption} isDilute: dataThin,
deviceAlarmSchemes={deviceAlarmSchemes} ignoreOutliers,
/> zoom,
) : ( unit,
<SimgleChart dateRange,
showBoxOption={showBoxOption} isBoxPlots: isBoxPlots,
curveCenter={curveCenter} });
showGridLine={chartGrid} });
prefixCls={prefixCls} }, [dateRange, dataConfig, deviceParams, chartType]);
dataSource={chartDataSource}
// justLine={!!checkboxData.find(item => item.key === 'justLine' && item.checked)} const renderPanel = (model) => {
chartType={isBoxPlots ? chartType : null} if (model === 'curve') {
contrast={timeValue === 'contrast'} return (
contrastOption={contrastOption} <>
deviceAlarmSchemes={deviceAlarmSchemes} <div className={`${prefixCls}-options`}>
/> {renderTimeOption()}
)} {renderCurveOption(
</div> true,
</> deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1,
); )}
} </div>
if (model === 'table') { <div className={`${prefixCls}-content`}>
return ( {!chartDataSource.length ? (
<> <PandaEmpty/>
<div className={`${prefixCls}-options`}> ) : grid === true ? (
{renderTimeOption()} <GridChart
{renderCurveOption()} curveCenter={curveCenter}
</div> prefixCls={prefixCls}
<div className={`${prefixCls}-content`}> dataSource={chartDataSource}
{chartDataSource.length > 0 ? ( contrast={timeValue === 'contrast'}
<BasicTable contrastOption={contrastOption}
dataSource={tableData} deviceAlarmSchemes={deviceAlarmSchemes}
columns={columns} />
{...tableProps} ) : (
pagination={false} <SimgleChart
onChange={() => {}} showBoxOption={showBoxOption}
/> curveCenter={curveCenter}
) : ( showGridLine={chartGrid}
<PandaEmpty /> prefixCls={prefixCls}
)} dataSource={chartDataSource}
</div> // justLine={!!checkboxData.find(item => item.key === 'justLine' && item.checked)}
</> chartType={isBoxPlots ? chartType : null}
); contrast={timeValue === 'contrast'}
} contrastOption={contrastOption}
}; deviceAlarmSchemes={deviceAlarmSchemes}
/>
return ( )}
<div className={classNames(prefixCls)}> </div>
<Spin spinning={loading} wrapperClassName={classNames(`${prefixCls}-spin`)}> </>
{showModels.length === 1 && ( );
<div className={`${prefixCls}-single-panel`}>{renderPanel(showModels[0])}</div> }
)} if (model === 'table') {
{showModels.length > 1 && ( return (
<Tabs <>
activeKey={activeTabKey} <div className={`${prefixCls}-options`}>
onChange={(key) => setActiveTabKey(key)} {renderTimeOption()}
centered {renderCurveOption()}
tabBarExtraContent={{ </div>
left: <h3>{title}</h3>, <div className={`${prefixCls}-content`}>
right: ( {chartDataSource.length > 0 ? (
<div className={`${prefixCls}-extra-right`}> <BasicTable
{activeTabKey === 'table' && ( dataSource={tableData}
<Button type="link" onClick={exportExcelBtn}> columns={columns}
<DownloadOutlined /> {...tableProps}
下载 pagination={false}
</Button> onChange={() => {
)} }}
</div> />
), ) : (
}} <PandaEmpty/>
> )}
<Tabs.TabPane key="curve" tab="曲线"> </div>
{renderPanel('curve')} </>
</Tabs.TabPane> );
<Tabs.TabPane key="table" tab="表格"> }
{renderPanel('table')} };
</Tabs.TabPane>
</Tabs> return (
)} <div className={classNames(prefixCls)}>
</Spin> <Spin spinning={loading} wrapperClassName={classNames(`${prefixCls}-spin`)}>
</div> {showModels.length === 1 && (
); <div className={`${prefixCls}-single-panel`}>{renderPanel(showModels[0])}</div>
)}
{showModels.length > 1 && (
<Tabs
activeKey={activeTabKey}
onChange={(key) => setActiveTabKey(key)}
centered
tabBarExtraContent={{
left: <h3>{title}</h3>,
right: (
<div className={`${prefixCls}-extra-right`}>
{activeTabKey === 'table' && (
<Button type="link" onClick={exportExcelBtn}>
<DownloadOutlined/>
下载
</Button>
)}
</div>
),
}}
>
<Tabs.TabPane key="curve" tab="曲线">
{renderPanel('curve')}
</Tabs.TabPane>
<Tabs.TabPane key="table" tab="表格">
{renderPanel('table')}
</Tabs.TabPane>
</Tabs>
)}
</Spin>
</div>
);
}; };
HistoryView.propTypes = { HistoryView.propTypes = {
grid: PropTypes.bool, grid: PropTypes.bool,
title: PropTypes.string, title: PropTypes.string,
defaultChecked: PropTypes.oneOf(['twelveHours', 'roundClock', 'oneWeek', 'oneMonth']), defaultChecked: PropTypes.oneOf(['twelveHours', 'roundClock', 'oneWeek', 'oneMonth']),
tableProps: PropTypes.object, tableProps: PropTypes.object,
deviceParams: PropTypes.arrayOf( deviceParams: PropTypes.arrayOf(
PropTypes.objectOf({ PropTypes.objectOf({
deviceCode: PropTypes.string, deviceCode: PropTypes.string,
sensors: PropTypes.string, sensors: PropTypes.string,
deviceType: PropTypes.string, deviceType: PropTypes.string,
pointAddressID: PropTypes.number, // 可选,配置了将会查询相关报警方案配置 pointAddressID: PropTypes.number, // 可选,配置了将会查询相关报警方案配置
}), }),
), ),
defaultModel: PropTypes.oneOf(['curve', 'table']), defaultModel: PropTypes.oneOf(['curve', 'table']),
showModels: PropTypes.arrayOf(PropTypes.oneOf(['curve', 'table'])), showModels: PropTypes.arrayOf(PropTypes.oneOf(['curve', 'table'])),
defaultDate: PropTypes.string, defaultDate: PropTypes.string,
}; };
HistoryView.defaultProps = { HistoryView.defaultProps = {
grid: false, grid: false,
title: '指标曲线', title: '指标曲线',
defaultChecked: 'roundClock', defaultChecked: 'roundClock',
tableProps: {}, tableProps: {},
defaultModel: 'curve', defaultModel: 'curve',
showModels: ['curve', 'table'], showModels: ['curve', 'table'],
needMarkLine: true, needMarkLine: true,
defaultDate: 'day', defaultDate: 'day',
}; };
export default HistoryView; export default HistoryView;
\ No newline at end of file
import React, {useEffect, useMemo, useState, useContext} from 'react';
import GridChart from "./GridChart";
import {ConfigProvider} from "antd";
import moment from "moment";
import {getDeviceAlarmScheme, getHistoryInfo} from "./apis";
import SimgleChart from "./SingleChart";
// deviceAlarmSchemes 用来获取对应的 方案的最大值/最小值 标记状态
// dataSource 获取的报警信息
// deviceParams,
// defaultDate,
// {
// deviceCode: 'XMYL00000345',
// sensors: '进水压力',
// deviceType: '熊猫压力表',
// pointAddressID: 4,
// },
//{
// "isDilute": true, // 抽稀
// "zoom": "30",// 抽稀
// "unit": "min",// 抽稀
// "ignoreOutliers": false, // 滤波
// "dateFrom": "2023-07-09 16:38:32",
// "dateTo": "2023-07-10 16:38:32",
// "acrossTables": [
// {
// "deviceCode": "XMYL00000297",
// "sensors": "进水压力,是否在线",
// "deviceType": "熊猫压力表"
// }
// ],
// "isBoxPlots": true // 箱线图
// }
const DATE_FORMAT = 'YYYY-MM-DD';
const MobileHistoryChart = (
{
date = {
dateFrom: moment().format(`${DATE_FORMAT} 00:00:00`),
dateTo: moment().format(`${DATE_FORMAT} 23:59:59`)
},
deviceParams = [],
ignoreOutliers = true,
isDilute = true,
needMarkLine = true,
showBoxOption = true, //
chartGrid = true,
chartType='lineChart', // lineChart boxChart
}
) => {
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState(null);
const [chartDataSource, setChartDataSource] = useState(null);
const {getPrefixCls} = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('history-view');
const isBoxPlots =
deviceParams?.length === 1 && deviceParams[0]?.sensors?.split(',').length === 1;
const handleDataThinKey = (diffDays) => {
// 移动端缩放的抽稀一倍
if (diffDays >= 7 && diffDays < 15) {
return {unit: 'h', zoom: '4'};
} else if (diffDays >= 15 && diffDays < 30) {
return {unit: 'h', zoom: '8'};
} else if (diffDays >= 30) {
return {unit: 'h', zoom: '12'};
} else if (diffDays < 7 && diffDays >= 2) {
return {unit: 'min', zoom: '80'};
} else if (diffDays < 2 && diffDays >= 1) {
return {unit: 'min', zoom: '60'};
} else {
return {unit: 'min', zoom: '20'};
}
};
const getDataSource = () => {
let diffDays = moment(date.dateTo).diff(moment(date.dateFrom), 'days');
const thinKey = handleDataThinKey(diffDays);
const acrossTables = deviceParams.map(item => {
let _item = {...item};
let _sensorArr = _item.sensors.split(',');
if (!_sensorArr.includes('是否在线')) {
_sensorArr.push('是否在线')
}
return _item;
});
let _params = {
...date,
isBoxPlots,
ignoreOutliers,
isDilute,
...thinKey,
acrossTables
};
getHistoryInfo({..._params}).then(res => {
let data = [];
const {dateFrom, dateTo} = date;
if (res.code === 0 && res.data.length) {
res.data.forEach((d) => {
d.dateFrom = dateFrom || '';
d.dateTo = dateTo || '';
});
deviceParams.forEach((p) => {
// 返回数据按查询指标顺序排序
const sensors = p.sensors?.split(',') ?? [];
const list = sensors.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
);
if (dataItem) {
dataItem.dateFrom = dateFrom || '';
dataItem.dateTo = dateTo || '';
return dataItem;
} else {
return {};
}
});
data = data.concat(list);
});
}
setChartDataSource(data);
})
};
const getScheme = () => {
getDeviceAlarmScheme({
data: deviceParams.map((item) => ({
deviceType: item.deviceType,
deviceCode: item.deviceCode,
pointAddressID: item.pointAddressID,
sensorName: item.sensors,
})),
}).then((res) => {
if (res.code === 0) setDeviceAlarmSchemes(res.data || []);
else setDeviceAlarmSchemes([]);
return Promise.resolve();
}).catch((err) => {
setDeviceAlarmSchemes([]);
return Promise.resolve();
});
}
useEffect(() => {
getDataSource();
getScheme();
}, [])
return deviceAlarmSchemes && chartDataSource ? <SimgleChart
showBoxOption={showBoxOption}
curveCenter={true}
chartGrid={chartGrid}
prefixCls={prefixCls}
dataSource={chartDataSource}
chartType={isBoxPlots ? chartType : null}
deviceAlarmSchemes={deviceAlarmSchemes}
/> : null
}
export {
MobileHistoryChart
}
\ No newline at end of file
import moment from 'moment'; import moment from 'moment';
import _, { isArray } from 'lodash'; import _, {isArray} from 'lodash';
/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */ /** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
const axisWidth = 40; const axisWidth = 40;
...@@ -265,7 +265,6 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -265,7 +265,6 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
const showBoxOption = _.get(config, 'showBoxOption', false); const showBoxOption = _.get(config, 'showBoxOption', false);
// 自定义属性 // 自定义属性
const restOption = _.pick(cusOption, ['title', 'legend']); const restOption = _.pick(cusOption, ['title', 'legend']);
// 一种指标一个y轴 // 一种指标一个y轴
const yAxisMap = new Map(); const yAxisMap = new Map();
dataSource.forEach((item, index) => { dataSource.forEach((item, index) => {
...@@ -336,7 +335,6 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -336,7 +335,6 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
}; };
const seriesTemplate = (param, unit) => { const seriesTemplate = (param, unit) => {
if (!param) return ''; if (!param) return '';
console.log(param);
const { value, encode } = param; const { value, encode } = param;
// const val = value[encode.y[0]]; // const val = value[encode.y[0]];
const _unit = unit || 'Mpa'; const _unit = unit || 'Mpa';
...@@ -535,13 +533,8 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth ...@@ -535,13 +533,8 @@ const optionGenerator = (dataSource, cusOption, contrast, contrastOption, smooth
let _unit = ''; let _unit = '';
series = series.map((item) => { series = series.map((item) => {
_unit = item.unit; _unit = item.unit;
let _item = { ...item, symbol: 'none' }; return {...item, symbol: 'none'};
/* _item.data = _item?.data?.map(d => {
return d[1] || null
}) || [];*/
return _item;
}); });
console.log(series);
[[..._minData], [..._maxData]].forEach((item, index) => { [[..._minData], [..._maxData]].forEach((item, index) => {
series.push({ series.push({
name: index === 0 ? '最小值' : '最大值', name: index === 0 ? '最小值' : '最大值',
......
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