Commit f23d3a78 authored by 陈龙's avatar 陈龙

feat: 优化历史曲线时间选择器;优化数据加载

parent a383e3fa
......@@ -26,16 +26,16 @@ path: /
## 单图表-状态
<code src="./demos/indexForStatus.js"></code>
[//]: # (<code src="./demos/indexForStatus.js"></code>)
## 单图表-频率
<code src="./demos/indexForFrequency.js"></code>
[//]: # (<code src="./demos/indexForFrequency.js"></code>)
[//]: # '## 移动端'
[//]: # '<code src="./demos/mobile.js"></code>'
## 多图表
[//]: # (## 多图表)
[//]: # (<code src="./demos/GridDemo.js"></code>)
......@@ -52,6 +52,19 @@ path: /
| deviceParams | 设备参数信息 | array | - | - |
| needMarkLine | 是否添加报警标识线 | boolean | true | |
## 字典配置
```text
数据字典中,根据需要配置以下条目
组件_ec_historyview
- 曲线居中 true/false
- 数据滤波 true/false
- 日期模式 常规模式/简易模式
- 离散算法设备类型 水厂,二供泵房
曲线居中、数据滤波,默认是否勾选;
日期模式下,常规模式时间格式为 YYYY-MM-DD HH:mm,简易模式时间格式为YYYY-MM-DD;
离散算法设备类型,适用于数据起伏较大的设备类型,根据实际场景,配置设备类型即可
```
### deviceParams
```javascript
......
......@@ -34,90 +34,8 @@ const SingleChart = memo((props) => {
const chartRef = useRef();
const timerRef = useRef();
const SpecialType = ['状态值', '开关值']; // 横向柱状条
const option = useMemo(() => {
const config = {
needUnit: true,
curveCenter,
showGridLine,
deviceAlarmSchemes,
showMarkLine: true,
showPoint: true,
chartType,
showBoxOption,
special
};
let allValDesc = {};
let _allSensorType = special?.allSensorType?.reduce((final, curr) => {
final[curr.id] = curr.type;
return final;
}, {});
let _allPointAddress = special?.allPointAddress?.reduce((final, curr) => {
final[curr.name] = _allSensorType[curr.sensorTypeID];
allValDesc[curr.name] = curr.valDesc;
return final;
}, {});
if (dataSource.length === 1 && SpecialType.includes(_allPointAddress[dataSource[0].sensorName])) {
config.sensorType = _allPointAddress[dataSource[0].sensorName];
config.special.allValDesc = allValDesc;
return specialTypeChartOptionGenerator({dataSource, config});
}
let _option = optionGenerator(dataSource, null, contrast, contrastOption, smooth, config, lineDataType, predicateData, theme);
if (emptyOrError.empty || emptyOrError.error) {
if (isArray(_option.yAxis)) {
_option.yAxis.forEach(item => {
item.max = 100;
item.min = 0;
})
} else {
_option.yAxis.max = 100;
_option.yAxis.min = 0;
}
}
// 加入bi模式
if (theme === 'BI') _option = patchBIOption(_option, BI);
return _option;
}, [dataSource, smooth, curveCenter, chartType, predicateData]);
useEffect(() => {
if (exportCanvas.exportFlag) {
const chart = chartRef.current?.getEchartsInstance?.();
let a = document.createElement('a');
a.href = chart.getDataURL('image/png');
a.download = '曲线图片.png'
a.click();
a.remove();
exportCanvas.setExportFlag(null);
}
}, [exportCanvas.exportFlag]);
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;
option.series.forEach((item, index) => {
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);
}
function renderMarkPoint(e) {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
const minMaxMarkPoint = (dataSource) => {
const minMaxMarkPoint = (dataSource,chart) => {
// 只有一个数据曲线时显示markline
if (dataSource.length !== 1) return {};
// 1. 找出最大、最小的值
......@@ -270,11 +188,98 @@ const SingleChart = memo((props) => {
data,
};
};
// minMaxMarkPoint(dataSource)
chart.setOption({series: {markPoint: minMaxMarkPoint(dataSource)}})
const renderMarkPoint = () => {
if (timerRef.current) clearTimeout(timerRef.current);
const chart = chartRef.current?.getEchartsInstance?.();
timerRef.current = setTimeout(() => {
chart.setOption({series: {markPoint: minMaxMarkPoint(dataSource,chart)}})
}, 200)
}
};
const option = useMemo(() => {
const config = {
needUnit: true,
curveCenter,
showGridLine,
deviceAlarmSchemes,
showMarkLine: true,
showPoint: true,
chartType,
showBoxOption,
special
};
let allValDesc = {};
let _allSensorType = special?.allSensorType?.reduce((final, curr) => {
final[curr.id] = curr.type;
return final;
}, {});
let _allPointAddress = special?.allPointAddress?.reduce((final, curr) => {
final[curr.name] = _allSensorType[curr.sensorTypeID];
allValDesc[curr.name] = curr.valDesc;
return final;
}, {});
if (dataSource.length === 1 && SpecialType.includes(_allPointAddress[dataSource[0].sensorName])) {
config.sensorType = _allPointAddress[dataSource[0].sensorName];
config.special.allValDesc = allValDesc;
return specialTypeChartOptionGenerator({dataSource, config});
}
let _option = optionGenerator(dataSource, null, contrast, contrastOption, smooth, config, lineDataType, predicateData, theme);
// 将markPoint配置加入
const chart = chartRef.current?.getEchartsInstance?.();
if (_option?.series?.[0] && chart) {
_option.series[0].markPoint = minMaxMarkPoint(dataSource,chart);
}
if (emptyOrError.empty || emptyOrError.error) {
if (isArray(_option.yAxis)) {
_option.yAxis.forEach(item => {
item.max = 100;
item.min = 0;
})
} else {
_option.yAxis.max = 100;
_option.yAxis.min = 0;
}
}
// 加入bi模式
if (theme === 'BI') _option = patchBIOption(_option, BI);
return _option;
}, [dataSource, smooth, curveCenter, chartType, predicateData,chartRef]);
useEffect(() => {
if (exportCanvas.exportFlag) {
const chart = chartRef.current?.getEchartsInstance?.();
let a = document.createElement('a');
a.href = chart.getDataURL('image/png');
a.download = '曲线图片.png'
a.click();
a.remove();
exportCanvas.setExportFlag(null);
}
}, [exportCanvas.exportFlag]);
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;
option.series.forEach((item, index) => {
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);
}
if (!chart) return;
renderMarkPoint();
chart.on('legendselectchanged', hander);
......@@ -285,7 +290,7 @@ const SingleChart = memo((props) => {
chart.off('legendselectchanged', renderMarkPoint);
chart.off('datazoom', renderMarkPoint);
};
}, [dataSource, deviceAlarmSchemes]);
}, [JSON.stringify(option)]);
// 网格开关,不更新整个图表
useEffect(() => {
......
......@@ -59,7 +59,20 @@ export function getDictionaryInfoAll(params) {
params
})
}
export function searchDataDictionaryList (params) {
return request({
url: `${baseUrl}/PandaOMS/OMS/DataManger/SearchDataDictionaryList`,
method: REQUEST_METHOD_GET,
params
})
}
export function getDataDictionaryList (params) {
return request({
url: `${baseUrl}/PandaOMS/OMS/DataManger/GetDataDictionaryList`,
method: REQUEST_METHOD_GET,
params
})
}
export function getSensorType() {
return request({
url: `${baseUrl}/PandaMonitor/Monitor/Sensor/GetSensorType`,
......
......@@ -95,14 +95,9 @@ import HistoryView from '../index';
];*/
const deviceParams = [
{
"deviceCode": "EGJZ00000002",
"sensors": "出水实际压力,1#水箱液位",
"deviceType": "二供机组"
},
{
"deviceCode": "EGBF00000004",
"sensors": "今日用电量,今日供水量",
"deviceType": "二供泵房"
"deviceCode": "SZJCY00000001",
"sensors": "温度,压力,浊度,二氧化氯",
"deviceType": "水质监测仪"
}
]
const Demo = () => {
......
......@@ -2,10 +2,15 @@ import React from 'react';
import HistoryView from '../index';
const deviceParams = [
{
/* {
"deviceCode": "EGBF00000022",
"sensors": "出水瞬时流量,今日用电量,余氯",
"deviceType": "二供泵房"
}*/
{
"deviceCode": "SZJCY00000006",
"sensors": "浊度",
"deviceType": "水质监测仪"
}
]
const Demo = () => {
......@@ -13,8 +18,8 @@ const Demo = () => {
<>
<div>
<div style={{height: 700}}>
{/*<HistoryView deviceParams={deviceParams} defaultModel="curve" />*/}
<HistoryView theme={'BI'} deviceParams={deviceParams} defaultModel="curve"/>
<HistoryView deviceParams={deviceParams} defaultModel="curve"/>
{/*<HistoryView theme={'BI'} deviceParams={deviceParams} defaultModel="curve"/>*/}
</div>
</div>
</>
......
......@@ -7,26 +7,13 @@ import React, {useContext, useEffect, useMemo, useState, useCallback, useRef} fr
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
Checkbox,
ConfigProvider,
DatePicker,
Radio,
Select,
Spin,
Tabs,
Tooltip,
Button,
message,
Progress,
Checkbox, ConfigProvider, DatePicker, Radio, Select, Spin, Tabs, Tooltip, Button, message, Progress, Input
} from 'antd';
import {
CloseCircleFilled,
PlusCircleOutlined,
QuestionCircleFilled,
DownloadOutlined,
CloseCircleFilled, PlusCircleOutlined, QuestionCircleFilled, DownloadOutlined, SwapOutlined,
} from '@ant-design/icons';
import moment from 'moment';
import _ from 'lodash';
import _, {cloneDeep} from 'lodash';
import TimeRangePicker from '@wisdom-components/timerangepicker';
import PandaEmpty from '@wisdom-components/empty';
import {
......@@ -35,7 +22,10 @@ import {
getExportDeviceHistoryUrl,
getDictionaryInfoAll,
getPointAddress,
getPointAddressEntry, getPredicateSensor,
getPointAddressEntry,
getPredicateSensor,
searchDataDictionaryList,
getDataDictionaryList,
} from './apis';
import SingleChart from './SingleChart';
import GridChart from './GridChart';
......@@ -54,82 +44,45 @@ const endFormat = 'YYYY-MM-DD 23:59:59';
const timeFormat = 'YYYY-MM-DD HH:mm:ss';
const dateFormat = 'YYYYMMDD';
const timeList = [
{
key: 'twelveHours',
name: '近12小时',
},
{
key: 'roundClock',
name: '近24小时',
},
{
key: 'yesterday',
name: '昨日',
},
{
key: 'oneWeek',
name: '近1周',
},
{
key: 'oneMonth',
name: '近1月',
},
];
const timeList = [{
key: 'twelveHours', name: '近12小时',
}, {
key: 'roundClock', name: '近24小时',
}, {
key: 'yesterday', name: '昨日',
}, {
key: 'oneWeek', name: '近1周',
}, {
key: 'oneMonth', name: '近1月',
},];
const predicateMap = {
twelveHours: 12,
roundClock: 24,
oneWeek: 24,
oneMonth: 24
twelveHours: 12, roundClock: 24, oneWeek: 24, oneMonth: 24
}
// 同期对比 日 快捷按钮
const shortcutsForDay = [
{
label: '近3天',
value: '近3天',
},
{
label: '近7天',
value: '近7天',
},
/* {
const shortcutsForDay = [{
label: '近3天', value: '近3天',
}, {
label: '近7天', value: '近7天',
}, /* {
label: '去年同期',
value: '去年同期',
}*/
];
}*/];
// 同期对比 月 快捷按钮
const shortcutsForMonth = [
{
label: '近3月',
value: '近3月',
},
{
label: '近6月',
value: '近6月',
},
/* {
const shortcutsForMonth = [{
label: '近3月', value: '近3月',
}, {
label: '近6月', value: '近6月',
}, /* {
label: '去年同期',
value: '去年同期',
}*/
];
}*/];
const CheckboxData = [
{
key: 'curveCenter',
label: '曲线居中',
checked: false,
showInCurve: true,
showInTable: false,
},
{
key: 'chartGrid',
label: '图表网格',
checked: true,
showInCurve: true,
showInTable: false,
},
{
const CheckboxData = [{
key: 'curveCenter', label: '曲线居中', checked: false, showInCurve: true, showInTable: false,
}, {
key: 'chartGrid', label: '图表网格', checked: true, showInCurve: true, showInTable: false,
}, {
key: 'ignoreOutliers',
label: '去除异常值',
type: 'updateIgnoreOutliers',
......@@ -138,79 +91,31 @@ const CheckboxData = [
showInTable: true,
tooltip: '采用递推平均滤波法(滑动平均滤波法)对采样数据中的异常离群值进行识别与去除。',
hasSub: true,
},
{
key: 'dataThin',
label: '数据抽稀',
type: 'updateDataThin',
checked: true,
showInCurve: false,
showInTable: true,
}
];
}, {
key: 'dataThin', label: '数据抽稀', type: 'updateDataThin', checked: true, showInCurve: false, showInTable: true,
}];
const timeIntervalList = [
{
key: '1min',
zoom: '1',
unit: 'min',
name: '1分钟',
},
{
key: '5',
zoom: '5',
unit: 'min',
name: '5分钟',
},
{
key: '10',
zoom: '10',
unit: 'min',
name: '10分钟',
},
{
key: '30',
zoom: '30',
unit: 'min',
name: '30分钟',
},
{
key: '1',
zoom: '1',
unit: 'h',
name: '1小时',
},
{
key: '2',
zoom: '2',
unit: 'h',
name: '2小时',
},
{
key: '4',
zoom: '4',
unit: 'h',
name: '4小时',
},
{
key: '6',
zoom: '6',
unit: 'h',
name: '6小时',
},
{
key: '12',
zoom: '12',
unit: 'h',
name: '12小时',
},
{
key: '24',
zoom: '24',
unit: 'h',
name: '24小时',
},
];
const timeIntervalList = [{
key: '1min', zoom: '1', unit: 'min', name: '1分钟',
}, {
key: '5', zoom: '5', unit: 'min', name: '5分钟',
}, {
key: '10', zoom: '10', unit: 'min', name: '10分钟',
}, {
key: '30', zoom: '30', unit: 'min', name: '30分钟',
}, {
key: '1', zoom: '1', unit: 'h', name: '1小时',
}, {
key: '2', zoom: '2', unit: 'h', name: '2小时',
}, {
key: '4', zoom: '4', unit: 'h', name: '4小时',
}, {
key: '6', zoom: '6', unit: 'h', name: '6小时',
}, {
key: '12', zoom: '12', unit: 'h', name: '12小时',
}, {
key: '24', zoom: '24', unit: 'h', name: '24小时',
},];
const handleTimeForPredicate = (key, start, end) => {
let t = predicateMap[key] || 0;
......@@ -251,24 +156,16 @@ const updateTime = (key, predicate) => {
if (predicate) {
end = handleTimeForPredicate(key, end)
}
return [
{
dateFrom: start,
dateTo: end,
},
];
return [{
dateFrom: start, dateTo: end,
},];
};
const DefaultDatePicker = (value) => [
{
key: 1,
value: moment(),
},
{
key: 2,
value: moment().subtract(1, value),
},
];
const DefaultDatePicker = (value) => [{
key: 1, value: moment(),
}, {
key: 2, value: moment().subtract(1, value),
},];
const handleBatchTime = (arr, cOption) => {
let newArr = [];
......@@ -290,10 +187,7 @@ const handleFakeData = (dateRange, deviceParams) => {
let _arr = cur.sensors.split(',');
_arr.forEach(sensor => {
final.push({
dataModel: [
{pt: dateFrom, pv: null},
{pt: dateTo, pv: null}
],
dataModel: [{pt: dateFrom, pv: null}, {pt: dateTo, pv: null}],
dateFrom,
dateTo,
deviceCode: cur.deviceCode,
......@@ -313,8 +207,7 @@ const timeColumn = {
width: 170,
ellipsis: true,
align: 'center',
sorter: true,
// sortOrder:['descend','ascend']
sorter: true, // sortOrder:['descend','ascend']
};
const OriginMaxDays = 31; // 原始曲线请求数据的最大天数
const CharacteristicMaxDays = null; // 特征曲线或者其他曲线的最大天数
......@@ -336,8 +229,7 @@ const HistoryView = (props) => {
} = props;
if (theme === 'Normal') import('./index.less');
if (theme === 'BI') import('./indexForBI.less');
const isBoxPlots =
deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1;
const isBoxPlots = deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1;
const [loading, setLoading] = useState(null);
const [activeTabKey, setActiveTabKey] = useState(defaultModel);
......@@ -352,9 +244,7 @@ const HistoryView = (props) => {
const [contrastOption, setContrastOption] = useState('day'); // 对比时间类型: 日/月
const [datePickerArr, setDatePickerArr] = useState(DefaultDatePicker(defaultDate)); // 对比时间段配置值
const [checkboxData, setCheckboxData] = useState(() => [...CheckboxData]); // 曲线设置项
const [dataThinKey, setDataThinKey] = useState(
timeIntervalList[0].key === '1min' ? timeIntervalList[1].key : timeIntervalList[0].key,
); // 曲线抽稀时间设置
const [dataThinKey, setDataThinKey] = useState(timeIntervalList[0].key === '1min' ? timeIntervalList[1].key : timeIntervalList[0].key,); // 曲线抽稀时间设置
const [algorithmValue, setAlgorithmValue] = useState(1);
const [columns, setColumns] = useState([]);
......@@ -380,23 +270,18 @@ const HistoryView = (props) => {
const [predicateTime, setPredicateTime] = useState(null);
// 需要处理默认数据,确保图表能够一直显示坐标轴。用来存储当前的请求状态。
const emptyOrError = useRef({
empty: true,
error: true
empty: true, error: true
})
// 这部分功能有问题,等待解决后上线 2024年3月13日
const [discreteDeviceType, setDiscreteDeviceType] = useState(['水厂'])
const [discreteDeviceType, setDiscreteDeviceType] = useState(['水厂']);
const [timeFormatter, setTimeFormatter] = useState('常规模式'); // 常规模式 YYYY-MM-DD HH:mm 简易模式 YYYY-MM-DD
// 历史数据相关的特征描述
const deviceConfig = useRef({
oneDevice: deviceParams.length === 1, //单设备
oneSensor:
[
...new Set(
deviceParams.reduce((final, cur) => {
oneSensor: [...new Set(deviceParams.reduce((final, cur) => {
let _sensors = cur.sensors.split(',');
return final.concat(_sensors);
}, []),
),
].length === 1, // 单指标
}, []),),].length === 1, // 单指标
});
// 表格虚拟列表
const tableRef = useRef();
......@@ -424,16 +309,11 @@ const HistoryView = (props) => {
// 数据配置
const dataConfig = useMemo(() => {
const initial = {
ignoreOutliers: false,
dataThin: false,
zoom: '', // 数据抽稀时间
ignoreOutliers: false, dataThin: false, zoom: '', // 数据抽稀时间
unit: '', // 数据抽稀时间单位
};
// 曲线居中,过滤异常值,数据抽稀
const config = checkboxData.reduce(
(pre, item) => (item.key !== 'curveCenter' && (pre[item.key] = item.checked), pre),
initial,
);
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 ?? '';
......@@ -478,8 +358,7 @@ const HistoryView = (props) => {
// 同期对比模式: 选择(日/月)
const onContrastChange = (value) => {
if (value === 'month') {
if (lineDataType === '原始曲线')
message.info('月模式数据量较大,不支持原始曲线模式,已切换为特征曲线');
if (lineDataType === '原始曲线') message.info('月模式数据量较大,不支持原始曲线模式,已切换为特征曲线');
setLineDataType('特征曲线');
}
setShortcutsValue('');
......@@ -505,13 +384,9 @@ const HistoryView = (props) => {
const handleAddDatePicker = () => {
// 操作时间就清除掉快捷键选用状态
setShortcutsValue('');
setDatePickerArr([
...datePickerArr,
{
key: datePickerArr[datePickerArr.length - 1].key + 1,
value: '',
},
]);
setDatePickerArr([...datePickerArr, {
key: datePickerArr[datePickerArr.length - 1].key + 1, value: '',
},]);
};
// 同期对比模式: 删除日期选择组件
......@@ -552,46 +427,40 @@ const HistoryView = (props) => {
let _arr = [];
switch (_val) {
case '近3天':
_arr = [
{key: 1, value: moment()},
{key: 2, value: moment().subtract(1, 'days')},
{key: 3, value: moment().subtract(2, 'days')},
];
_arr = [{key: 1, value: moment()}, {key: 2, value: moment().subtract(1, 'days')}, {
key: 3,
value: moment().subtract(2, 'days')
},];
break;
case '近7天':
_arr = [
{key: 1, value: moment()},
{key: 2, value: moment().subtract(1, 'days')},
{key: 3, value: moment().subtract(2, 'days')},
{key: 4, value: moment().subtract(3, 'days')},
{key: 5, value: moment().subtract(4, 'days')},
{key: 6, value: moment().subtract(5, 'days')},
{key: 7, value: moment().subtract(6, 'days')},
];
_arr = [{key: 1, value: moment()}, {key: 2, value: moment().subtract(1, 'days')}, {
key: 3,
value: moment().subtract(2, 'days')
}, {key: 4, value: moment().subtract(3, 'days')}, {
key: 5,
value: moment().subtract(4, 'days')
}, {key: 6, value: moment().subtract(5, 'days')}, {key: 7, value: moment().subtract(6, 'days')},];
break;
case '近3月':
_arr = [
{key: 1, value: moment()},
{key: 2, value: moment().subtract(1, 'months')},
{key: 3, value: moment().subtract(2, 'months')},
];
_arr = [{key: 1, value: moment()}, {key: 2, value: moment().subtract(1, 'months')}, {
key: 3,
value: moment().subtract(2, 'months')
},];
break;
case '近6月':
_arr = [
{key: 1, value: moment()},
{key: 2, value: moment().subtract(1, 'months')},
{key: 3, value: moment().subtract(2, 'months')},
{key: 4, value: moment().subtract(3, 'months')},
{key: 5, value: moment().subtract(4, 'months')},
{key: 6, value: moment().subtract(5, 'months')},
];
_arr = [{key: 1, value: moment()}, {key: 2, value: moment().subtract(1, 'months')}, {
key: 3,
value: moment().subtract(2, 'months')
}, {key: 4, value: moment().subtract(3, 'months')}, {
key: 5,
value: moment().subtract(4, 'months')
}, {key: 6, value: moment().subtract(5, 'months')},];
break;
}
setShortcutsDatePickerArr(_arr);
};
const renderTimeOption = useMemo(() => {
return (
<div className={classNames(`${prefixCls}-date`)}>
return (<div className={classNames(`${prefixCls}-date`)}>
<div className={classNames(`${prefixCls}-label`)}>时间选择</div>
<Radio.Group value={timeValue} onChange={onTimeSetChange}>
<Radio.Button value="customer">自定义</Radio.Button>
......@@ -605,27 +474,23 @@ const HistoryView = (props) => {
value={customerChecked}
dataSource={timeList}
/>
<Input.Group compact>
<RangePicker
getPopupContainer={trigger => trigger.parentElement}
format={'YYYY-MM-DD HH:mm'}
format={timeFormatter === '简易模式' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm'}
className={classNames(`${prefixCls}-custime-customer`)}
onChange={onCustomerRangeChange}
value={dates || customerTime}
onCalendarChange={(val) => {
setDates(val);
}}
ranges={
dates?.[0]
? {
当前时间: () => {
return [
dates?.[0] || moment(moment().format('YYYY-MM-DD 00:00:00')),
moment(moment()),
];
ranges={{
...(dates?.[0] ? {
[`${timeFormatter === '简易模式' ? '今天' : '当前时间'}`]: () => {
return [dates?.[0] || moment(moment().format('YYYY-MM-DD 00:00:00')), moment(moment()),];
},
}
: {}
}
} : {})
}}
onOpenChange={(open) => {
if (open) {
setDates([null, null]);
......@@ -644,13 +509,23 @@ const HistoryView = (props) => {
const tooEarly = dates[1] && dates[1].diff(current, 'days') > _days;
return !!tooEarly || !!tooLate;
}}
showTime={{
format: 'YYYY-MM-DD HH:mm',
minuteStep: 10,
showTime={timeFormatter === '简易模式' ? false : {
format: 'YYYY-MM-DD HH:mm', minuteStep: 10,
}}
/>
</>
)}
<Button style={{
color: '#1980ff', cursor: 'pointer', textDecoration: 'underline',
}} onClick={() => {
setTimeFormatter(timeFormatter === '常规模式' ? '简易模式' : '常规模式')
}}>
<Tooltip
title={`切换至 ${timeFormatter === '常规模式' ? '【简易模式】:该模式取全天数据' : '【常规模式】:该模式取值时段可精确到分'}`}>
<SwapOutlined/>
</Tooltip>
</Button>
</Input.Group>
</>)}
{timeValue === 'contrast' && ( // 同期对比
<>
<Select value={contrastOption} getPopupContainer={trigger => trigger.parentElement}
......@@ -666,10 +541,7 @@ const HistoryView = (props) => {
{(contrastOption === 'day' ? shortcutsForDay : shortcutsForMonth).map((item) => {
return <Radio.Button value={item.value}>{item.label}</Radio.Button>;
})}
</Radio.Group>
) : (
''
)}
</Radio.Group>) : ('')}
{datePickerArr.map((child, index) => (
<div key={child.key} className={classNames(`${prefixCls}-contrast-list`)}>
<div className={classNames(`${prefixCls}-contrast-wrap`)}>
......@@ -681,35 +553,20 @@ const HistoryView = (props) => {
onChange={(date, dateString) => onContrastPickerChange(date, dateString, child)}
style={{width: 130, border: !shortcutsValue ? '1px solid #1890ff' : ''}}
/>
{datePickerArr.length > 2 && (
<div
{datePickerArr.length > 2 && (<div
className={classNames(`${prefixCls}-contrast-delete`)}
onClick={() => handleDeleteDatePicker(index)}
>
<CloseCircleFilled/>
</div>
)}
</div>)}
</div>
{index < datePickerArr.length - 1 && (
<div className={classNames(`${prefixCls}-contrast-connect`)}></div>
)}
</div>
))}
<div className={classNames(`${prefixCls}-contrast-connect`)}></div>)}
</div>))}
{datePickerArr.length < 4 && <PlusCircleOutlined onClick={handleAddDatePicker}/>}
</>
)}
</div>
);
}, [
timeValue,
customerChecked,
lineDataType,
datePickerArr,
deviceParams,
dates,
customerTime,
chartDataSource,
]);
</>)}
</div>);
}, [timeValue, customerChecked, lineDataType, datePickerArr, deviceParams, dates, customerTime, chartDataSource, timeFormatter]);
// 曲线设置项选择/取消
const onCheckboxChange = (e, key, showJustLine) => {
......@@ -758,19 +615,14 @@ const HistoryView = (props) => {
const gridOptions = ['curveCenter'];
if (grid && curveAccess && gridOptions.indexOf(child.key) === -1) return null;
return (
(curveAccess || tableAccess) && (
<>
return ((curveAccess || tableAccess) && (<>
<Checkbox checked={child.checked} onChange={(e) => onCheckboxChange(e, child.key)}>
{child.label}
</Checkbox>
{child.tooltip && (
<Tooltip title={child.tooltip}>
{child.tooltip && (<Tooltip title={child.tooltip}>
<QuestionCircleFilled className={`${prefixCls}-question`}/>
</Tooltip>
)}
{child.hasSub && child.checked && false ? (
<Select
</Tooltip>)}
{child.hasSub && child.checked && false ? (<Select
style={{width: 80, marginLeft: 10}}
value={algorithmValue}
onChange={(e) => setAlgorithmValue(e)}
......@@ -778,23 +630,16 @@ const HistoryView = (props) => {
<Option value={1}></Option>
<Option value={5}></Option>
<Option value={10}></Option>
</Select>
) : (
''
)}
</>
)
);
</Select>) : ('')}
</>));
};
const renderCurveOption = (isChart, isSingle, isStatus) => {
return (
<div
return (<div
className={classNames(`${prefixCls}-cover`)}
style={isChart && isSingle ? {width: '100%'} : {}}
>
{isChart && !isStatus ? (
<>
{isChart && !isStatus ? (<>
<div className={classNames(`${prefixCls}-label`)}>曲线选择</div>
<div className={`${prefixCls}-cover-item`}>
<Radio.Group value={lineDataType} onChange={switchLineDataType}>
......@@ -808,14 +653,9 @@ const HistoryView = (props) => {
/>
</Tooltip>
</div>
</>
) : (
''
)}
{isChart && isSingle && showBoxOption && !isStatus && !grid ? (
<>
{lineDataType !== '原始曲线' ? (
<>
</>) : ('')}
{isChart && isSingle && showBoxOption && !isStatus && !grid ? (<>
{lineDataType !== '原始曲线' ? (<>
<div style={{marginLeft: 7}} className={classNames(`${prefixCls}-label`)}>
曲线形态
</div>
......@@ -831,30 +671,20 @@ const HistoryView = (props) => {
<Radio.Button value={'lineChart'}>线形图</Radio.Button>
<Radio.Button value={'boxChart'}>箱线图</Radio.Button>
</Radio.Group>
</>
) : (
''
)}
</>
) : (
''
)}
{!isStatus ? (
<>
</>) : ('')}
</>) : ('')}
{!isStatus ? (<>
<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`}>
return (<div key={child.key} className={`${prefixCls}-cover-item`}>
{box}
</div>
);
</div>);
})}
{activeTabKey === 'table' && (
<Select
{activeTabKey === 'table' && (<Select
value={dataThinKey}
style={{width: 90}}
onChange={onTimeIntervalChange}
......@@ -867,20 +697,13 @@ const HistoryView = (props) => {
return !(_diffDays > 7 && item.key === '1min');
})
.map((child) => {
return (
<Option key={child.key} unit={child.unit} value={child.key}>
return (<Option key={child.key} unit={child.unit} value={child.key}>
{child.name}
</Option>
);
</Option>);
})}
</Select>
)}
</>
) : (
''
)}
</div>
);
</Select>)}
</>) : ('')}
</div>);
};
const exportExcelBtn = () => {
......@@ -888,9 +711,7 @@ const HistoryView = (props) => {
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)}`;
let fileName = `数据报表-${i.deviceType}-${i.deviceCode}-${moment(timeFrom).format(dateFormat,)}${moment(timeTo).format(dateFormat)}`;
let _quotas = i.sensors
.split(',')
.filter((item) => item !== '是否在线')
......@@ -935,9 +756,7 @@ const HistoryView = (props) => {
let _columns = [...columns];
let timeFrom = dateRange?.[0]?.dateFrom || moment().format(startFormat);
let timeTo = dateRange?.[0]?.dateTo || moment().format(timeFormat);
let fileName = `特征数据-${moment(timeFrom).format(dateFormat)}${moment(timeTo).format(
dateFormat,
)}`;
let fileName = `特征数据-${moment(timeFrom).format(dateFormat)}${moment(timeTo).format(dateFormat,)}`;
let _dataIndex = [];
let _titleWidth = [];
let _title = _columns.map((item) => {
......@@ -951,22 +770,15 @@ const HistoryView = (props) => {
return _titleStr;
});
ExportExcel({
name: fileName,
content: [
{
sheetData: _dataSource,
sheetFilter: _dataIndex,
sheetHeader: _title,
columnWidths: _titleWidth,
},
],
name: fileName, content: [{
sheetData: _dataSource, sheetFilter: _dataIndex, sheetHeader: _title, columnWidths: _titleWidth,
},],
});
};
const exportCanvas = () => {
setExportFlag(Math.random());
};
const handleTableData = useCallback(
(data) => {
const handleTableData = useCallback((data) => {
// eslint-disable-next-line no-param-reassign
// data = data.filter(item => item.sensorName !== '是否在线');
const ignoreOutliers = checkboxData.find((item) => item.key === 'ignoreOutliers').checked;
......@@ -1022,8 +834,7 @@ const HistoryView = (props) => {
};
data.forEach((item, index) => {
const {stationCode, sensorName, dataModel} = item;
dataModel &&
dataModel.forEach((data) => {
dataModel && dataModel.forEach((data) => {
const formatTime = moment(data.pt).format(format);
let time = formatTime;
......@@ -1038,8 +849,7 @@ const HistoryView = (props) => {
data.forEach((child, index) => {
const {dataModel} = child;
const dataIndex = dataIndexAccess(child, index);
dataModel &&
dataModel.forEach((value, j) => {
dataModel && dataModel.forEach((value, j) => {
const formatTime = moment(value.pt).format(format);
const dataRow = timeData[formatTime];
if (dataRow) {
......@@ -1048,8 +858,7 @@ const HistoryView = (props) => {
});
});
const timeSort = (a, b) => {
let aa = a,
bb = 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);
......@@ -1060,9 +869,7 @@ const HistoryView = (props) => {
const tableData = times.map((time) => timeData[time]);
setColumns([timeColumn, ...columnsData]);
setTableData(tableData);
},
[timeOrder, timeValue, contrastOption],
);
}, [timeOrder, timeValue, contrastOption],);
const [deviceAlarmSchemes, setDeviceAlarmSchemes] = useState([]);
const beforChangeParams = (value = {}) => {
......@@ -1081,8 +888,7 @@ const HistoryView = (props) => {
})),
})
.then((res) => {
if (res.code === 0) setDeviceAlarmSchemes(res.data || []);
else setDeviceAlarmSchemes([]);
if (res.code === 0) setDeviceAlarmSchemes(res.data || []); else setDeviceAlarmSchemes([]);
return Promise.resolve();
})
.catch((err) => {
......@@ -1141,8 +947,7 @@ const HistoryView = (props) => {
return _item;
})
.forEach((i) => {
if (i.sensors && i.deviceCode)
acrossTables.push(_.omit(i, ['pointAddressID']));
if (i.sensors && i.deviceCode) acrossTables.push(_.omit(i, ['pointAddressID']));
// 这部分功能有问题,等待解决后上线 2024年3月13日
if (discreteDeviceType.includes(i.deviceType)) {
hasDiscreteDeviceType = true;
......@@ -1167,12 +972,10 @@ const HistoryView = (props) => {
let diffYears = moment(item.dateTo).diff(moment(item.dateFrom), 'years');
let diffDays = moment(item.dateTo).diff(moment(item.dateFrom), 'days');
let diffHours = moment(item.dateTo).diff(moment(item.dateFrom), 'hours');
let zoomParam =
activeTabKey === 'curve'
? handleDataThinKey(diffYears, diffDays, diffHours, lineDataType)
: !isDilute
? {zoom: '', unit: ''}
: {}; // 表格也支持全数据模式;
let zoomParam = activeTabKey === 'curve' ? handleDataThinKey(diffYears, diffDays, diffHours, lineDataType) : !isDilute ? {
zoom: '',
unit: ''
} : {}; // 表格也支持全数据模式;
let _finalParams = {...param, ...zoomParam};
// 2024年1月8日 抽稀间隔大于等于12小时时,会存在线性插值导致抽稀间隔内数据条数大于预期的问题。需要增加一个额外参数处理该情况。
if (_finalParams.zoom) {
......@@ -1222,11 +1025,7 @@ const HistoryView = (props) => {
minPV = pv;
}
return {
...item,
firstPV,
lastPV,
maxPV,
minPV,
...item, firstPV, lastPV, maxPV, minPV,
};
});
});
......@@ -1241,9 +1040,7 @@ const HistoryView = (props) => {
}
}
const list = sensors.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
);
const dataItem = res.data.find((d) => d.stationCode === p.deviceCode && d.sensorName === s,);
if (dataItem) {
dataItem.dateFrom = dateFrom || '';
dataItem.dateTo = dateTo || '';
......@@ -1282,32 +1079,34 @@ const HistoryView = (props) => {
useEffect(() => {
if (!completeInit) return;
const {dataThin, ignoreOutliers, zoom, unit} = dataConfig;
// 简易模式下,在获取到时间参数后,将时间修改为 00:00:00 - 23:59:59
let _dateRange = cloneDeep(dateRange)
if (_dateRange?.length === 1) {
let _date = dateRange[0];
_dateRange = [{
dateFrom: moment(_date.dateFrom).format('YYYY-MM-DD 00:00:00'),
dateTo: moment(_date.dateTo).format('YYYY-MM-DD 23:59:59'),
}];
}
beforChangeParams().finally(() => {
onChangeParams({
isDilute: dataThin,
ignoreOutliers,
zoom,
unit,
dateRange,
isBoxPlots: isBoxPlots,
isDilute: dataThin, ignoreOutliers, zoom, unit, dateRange: _dateRange, isBoxPlots: isBoxPlots,
});
});
}, [dateRange, dataConfig, deviceParams, chartType, lineDataType, completeInit, algorithmValue]);
}, [dateRange, dataConfig, deviceParams, chartType, lineDataType, completeInit, algorithmValue, timeFormatter]);
const handleChange = (pagination, filter, sort) => {
if (sort.field === 'time') {
setTimeOrder(sort.order);
}
};
const tableMemo = useMemo(() => {
return (
<>
return (<>
<div className={`${prefixCls}-options`}>
{renderTimeOption}
{renderCurveOption()}
</div>
<div className={`${prefixCls}-content`} ref={tableRef}>
{chartDataSource.length > 0 ? (
// <BasicTable
{chartDataSource.length > 0 ? (// <BasicTable
<VirtualTable
className={`${prefixCls}-virtual-table`}
theme={theme}
......@@ -1335,23 +1134,10 @@ const HistoryView = (props) => {
x: 'max-content',
y: tableRef.current ? tableRef.current.getBoundingClientRect().height - 40 : 0,
}}
/>
) : (
<PandaEmpty/>
)}
/>) : (<PandaEmpty/>)}
</div>
</>
);
}, [
timeOrder,
chartDataSource,
columns,
tableProps,
tableData,
isSingleStatusSensor,
dateRange,
tableRef.current,
]);
</>);
}, [timeOrder, chartDataSource, columns, tableProps, tableData, isSingleStatusSensor, dateRange, tableRef.current, timeFormatter]);
const returnLongestPeriod = (data) => {
let _earliest = '';
let _latest = '';
......@@ -1374,24 +1160,15 @@ const HistoryView = (props) => {
};
const renderPanel = (model) => {
if (model === 'curve') {
return (
<>
return (<>
<div className={`${prefixCls}-options`}>
{renderTimeOption}
{renderCurveOption(
true,
deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1,
isSingleStatusSensor,
)}
{renderCurveOption(true, deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1, isSingleStatusSensor,)}
</div>
{lineDataType === '原始曲线' && false ? (
<div style={{marginTop: 10}}>展示区间:{returnLongestPeriod(chartDataSource)}</div>
) : (
''
)}
<div style={{marginTop: 10}}>展示区间:{returnLongestPeriod(chartDataSource)}</div>) : ('')}
<div className={`${prefixCls}-content`}>
{grid === true ? (
<GridChart
{grid === true ? (<GridChart
emptyOrError={emptyOrError.current}
curveCenter={curveCenter}
prefixCls={prefixCls}
......@@ -1404,9 +1181,7 @@ const HistoryView = (props) => {
allSensorType={allSensorType}
loading={loading}
setLoading={setLoading}
/>
) : (
<SingleChart
/>) : (<SingleChart
exportCanvas={{exportFlag, setExportFlag}}
emptyOrError={emptyOrError.current}
dateRange={dateRange}
......@@ -1424,14 +1199,11 @@ const HistoryView = (props) => {
theme={theme}
special={{
special1, // 频率业务
allPointAddress,
allSensorType, // 后续新增的开关量的特殊业务,用来处理开关量业务
allPointAddress, allSensorType, // 后续新增的开关量的特殊业务,用来处理开关量业务
}}
/>
)}
/>)}
</div>
</>
);
</>);
}
if (model === 'table') {
return tableMemo;
......@@ -1439,66 +1211,37 @@ const HistoryView = (props) => {
};
// 获取字段配置
const getDefaultOptions = async () => {
// 特定设备
// 这部分功能有问题,等待解决后上线 2024年3月13日
getDictionaryInfoAll({
level: '离散算法设备类型',
}).then(res => {
if (res.code === 0 && res.data.length) {
let deviceType = res.data.find(item => item.fieldName === '设备类型')?.fieldValue;
setDiscreteDeviceType(deviceType.split(',').filter(item => item))
}
})
let _dictionaryList = await searchDataDictionaryList({key: '组件_ec_historyview', type: 0});
let _item = _dictionaryList?.data?.[0] ?? {}
let _nodeID = _item?.nodeName === '组件_ec_historyview' ? _dictionaryList?.data?.[0]?.nodeID : null;
// 非单曲线、单指标不执行
if (
deviceParams?.length !== 1 ||
(deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',')?.length > 1)
)
return setCompleteInit(true);
if (deviceParams?.length !== 1 || (deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',')?.length > 1)) return setCompleteInit(true);
setLoading(true);
const {deviceCode, deviceType, sensors} = deviceParams[0];
let _id = (
await getPointAddress({
let _id = (await getPointAddress({
code: deviceCode,
})
)?.data?.[0]?.id;
}))?.data?.[0]?.id;
let _params = {};
if (_id) _params.versionId = _id;
// 多曲线的居中,容易导致曲线被截断,故多曲线时,不请求
let _request0 = getDictionaryInfoAll({
level: '组件_ec_historyview',
let _request0 = _nodeID ? getDataDictionaryList({
nodeID: _nodeID
}) : new Promise((resolve, reject) => {
resolve()
});
// 以下请求为处理状态值、开关值的图表,只允许单曲线单指标情况下展示
let _request1 = getPointAddressEntry(_params);
let _request2 = getSensorType();
// let _request3 = getPredicateSensor({deviceCode, sensors});
await Promise.all([_request0, _request1, _request2]).then((result) => {
if (result) {
let _res0 = result[0];
let _res1 = result[1];
let _res2 = result[2];
// let _res3 = result[3];
let _checkboxData = [...checkboxData];
// 单设备单曲线时,查询是否配置为预测点
/* if (_res3.code === 0 && _res3.data) {
// 1. 如果是单曲线,并且配置了预测,那么默认开启预测;
// 2024年3月11日 物联预测功能支撑后,再开发这部分
_checkboxData.push({
key: 'predicate',
label: '数据预测',
checked: true,
showInCurve: true,
showInTable: true,
})
setPredicateDevice({..._res3.data, deviceType: '预测'});
} else {
setPredicateDevice(null);
}*/
// 查字典配置
if (_res0.code === 0) {
if (_res0?.code === 0) {
let _opt = _res0.data.reduce((final, cur) => {
final[cur.fieldName] = cur.fieldValue;
final[cur.nodeName] = cur.nodeValue;
return final;
}, {});
_checkboxData = _checkboxData.map((item) => {
......@@ -1509,6 +1252,13 @@ const HistoryView = (props) => {
return _item;
});
setCheckboxData(_checkboxData);
// 大数据量设备类型,例如:水厂
let deviceType = _res0.data.find(item => item.nodeName === '离散算法设备类型')?.nodeValue ?? '';
setDiscreteDeviceType(deviceType.split(',').filter(item => item));
let _timeFormatter = _res0.data.find(item => item.nodeName === '日期模式');
if (_timeFormatter) {
setTimeFormatter(_timeFormatter.nodeName);
}
}
// 查点表配置
if (_res1.code === 0) {
......@@ -1519,8 +1269,7 @@ const HistoryView = (props) => {
let _statusConfig = _res1.data.find((item) => item?.name.trim() === _statusName.trim());
let _valDesc = _statusConfig?.valDesc || '';
setSpecial1({
name: _statusName,
valDesc: _valDesc.split(';').reduce((final, cur) => {
name: _statusName, valDesc: _valDesc.split(';').reduce((final, cur) => {
let _arr = cur.split(':');
final[_arr[0]] = _arr[1];
return final;
......@@ -1559,25 +1308,17 @@ const HistoryView = (props) => {
} else {
clearInterval(percentTimer.current.timer);
setPercent(100);
setTimeout(
() => {
setTimeout(() => {
setPercent(0);
},
lineDataType === '原始曲线' ? 500 : 0,
);
}, lineDataType === '原始曲线' ? 500 : 0,);
}
}, [loading]);
return (
<div
return (<div
className={classNames(prefixCls, theme === 'BI' ? BIStyles[`${prefixCls}-historyViewComponents`] : '', 'wkt-scroll-light')}
style={{background: theme === 'BI' ? '#282b34' : '#ffffff'}}>
<div className={classNames(`${prefixCls}-spin`)} style={{position: 'relative'}}>
{loading || percent !== 0 ? (
<div className={classNames(`${prefixCls}-progressWrapper`)}>
{lineDataType === '原始曲线' ||
(lineDataType === '特征曲线' &&
moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >=
30) ?
{loading || percent !== 0 ? (<div className={classNames(`${prefixCls}-progressWrapper`)}>
{lineDataType === '原始曲线' || (lineDataType === '特征曲线' && moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >= 30) ?
<div className={classNames(`${prefixCls}-contentWrapper`)}>
<Progress
percent={percent}
......@@ -1586,44 +1327,30 @@ const HistoryView = (props) => {
showInfo={false}
/>
<div className={classNames(`${prefixCls}-tip`)}>加载中...</div>
</div> :
<Spin spinning={loading || false} tip={'数据加载中...'} delay={1000}
style={{background: 'transparent'}}/>
}
</div>
) : (
''
)}
</div> : <Spin spinning={loading || false} tip={'数据加载中...'} delay={1000}
style={{background: 'transparent'}}/>}
</div>) : ('')}
{showModels.length === 1 && (
<div className={`${prefixCls}-single-panel`}>{renderPanel(showModels[0])}</div>
)}
{showModels.length > 1 && (
<Tabs
<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' && (
<>
left: <h3>{title}</h3>, right: (<div className={`${prefixCls}-extra-right`}>
{activeTabKey === 'table' && (<>
<Button type="link" onClick={exportFeatureBtn}>
<DownloadOutlined/>
下载
</Button>
</>
)}
{activeTabKey === 'curve' && (
<>
</>)}
{activeTabKey === 'curve' && (<>
<Button type="link" onClick={exportCanvas}>
<DownloadOutlined/>
下载
</Button>
</>
)}
</div>
),
</>)}
</div>),
}}
>
<Tabs.TabPane key="curve" tab="曲线" forceRender={true}>
......@@ -1632,11 +1359,9 @@ const HistoryView = (props) => {
<Tabs.TabPane key="table" tab="表格">
{renderPanel('table')}
</Tabs.TabPane>
</Tabs>
)}
</div>
</Tabs>)}
</div>
)
</div>)
};
HistoryView.propTypes = {
......@@ -1644,14 +1369,12 @@ HistoryView.propTypes = {
title: PropTypes.string,
defaultChecked: PropTypes.oneOf(['twelveHours', 'roundClock', 'oneWeek', 'oneMonth']),
tableProps: PropTypes.object,
deviceParams: PropTypes.arrayOf(
PropTypes.objectOf({
deviceParams: PropTypes.arrayOf(PropTypes.objectOf({
deviceCode: PropTypes.string,
sensors: PropTypes.string,
deviceType: PropTypes.string,
pointAddressID: PropTypes.number, // 可选,配置了将会查询相关报警方案配置
}),
),
}),),
defaultModel: PropTypes.oneOf(['curve', 'table']),
showModels: PropTypes.arrayOf(PropTypes.oneOf(['curve', 'table'])),
defaultDate: PropTypes.string,
......
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