Commit 365230d8 authored by 陈龙's avatar 陈龙

fix: 修复历史曲线频率、状态的图表显示异常

parent 4ea22fe6
import React, {useContext, useEffect, useMemo, useState, useCallback, useRef} from 'react';
import React, { useContext, useEffect, useMemo, useState, useCallback, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
......@@ -11,7 +11,8 @@ import {
Tabs,
Tooltip,
Button,
message, Progress,
message,
Progress,
} from 'antd';
import {
CloseCircleFilled,
......@@ -29,17 +30,18 @@ import {
getDeviceAlarmScheme,
getExportDeviceHistoryUrl,
getDictionaryInfoAll,
getPointAddress, getPointAddressEntry
getPointAddress,
getPointAddressEntry,
} from './apis';
import SingleChart from './SingleChart';
import GridChart from './GridChart';
import './index.less';
import {globalConfig} from 'antd/lib/config-provider';
import {getSensorType} from "./apis/index";
import {ExportExcel} from '@wisdom-components/exportexcel';
import { globalConfig } from 'antd/lib/config-provider';
import { getSensorType } from './apis/index';
import { ExportExcel } from '@wisdom-components/exportexcel';
const {RangePicker} = DatePicker;
const {Option} = Select;
const { RangePicker } = DatePicker;
const { Option } = Select;
const startFormat = 'YYYY-MM-DD 00:00:00';
const endFormat = 'YYYY-MM-DD 23:59:59';
......@@ -57,7 +59,7 @@ const timeList = [
},
{
key: 'yesterday',
name: '昨日'
name: '昨日',
},
{
key: 'oneWeek',
......@@ -73,11 +75,11 @@ const timeList = [
const shortcutsForDay = [
{
label: '近3天',
value: '近3天'
value: '近3天',
},
{
label: '近7天',
value: '近7天'
value: '近7天',
},
/* {
label: '去年同期',
......@@ -88,17 +90,17 @@ const shortcutsForDay = [
const shortcutsForMonth = [
{
label: '近3月',
value: '近3月'
value: '近3月',
},
{
label: '近6月',
value: '近6月'
value: '近6月',
},
/* {
label: '去年同期',
value: '去年同期',
}*/
]
];
const CheckboxData = [
{
......@@ -123,7 +125,7 @@ const CheckboxData = [
showInCurve: true,
showInTable: true,
tooltip: '采用递推平均滤波法(滑动平均滤波法)对采样数据中的异常离群值进行识别与去除。',
hasSub: true
hasSub: true,
},
{
key: 'dataThin',
......@@ -136,6 +138,12 @@ const CheckboxData = [
];
const timeIntervalList = [
{
key: '1min',
zoom: '1',
unit: 'min',
name: '1分钟',
},
{
key: '5',
zoom: '5',
......@@ -265,7 +273,7 @@ const OriginMaxDays = 31; // 原始曲线请求数据的最大天数
const CharacteristicMaxDays = null; // 特征曲线或者其他曲线的最大天数
const HistoryView = (props) => {
const [completeInit, setCompleteInit] = useState(false);
const {getPrefixCls} = useContext(ConfigProvider.ConfigContext);
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('history-view');
const {
title,
......@@ -295,7 +303,9 @@ const HistoryView = (props) => {
const [datePickerArr, setDatePickerArr] = useState(DefaultDatePicker(defaultDate)); // 对比时间段配置值
const [checkboxData, setCheckboxData] = useState(() => [...CheckboxData]); // 曲线设置项
const [dataThinKey, setDataThinKey] = useState(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([]);
......@@ -319,11 +329,16 @@ const HistoryView = (props) => {
const [isSingleStatusSensor, setIsSingleStatusSensor] = useState(false);
// 历史数据相关的特征描述
const deviceConfig = useRef({
oneDevice: deviceParams.length === 1,//单设备
oneSensor: [...new Set(deviceParams.reduce((final, cur) => {
oneDevice: deviceParams.length === 1, //单设备
oneSensor:
[
...new Set(
deviceParams.reduce((final, cur) => {
let _sensors = cur.sensors.split(',');
return final.concat(_sensors);
}, []))].length === 1, // 单指标
}, []),
),
].length === 1, // 单指标
});
// 选择的时间范围值
const dateRange = useMemo(() => {
......@@ -334,7 +349,12 @@ const HistoryView = (props) => {
return handleBatchTime(_dateArr, contrastOption);
}
}, [contrastOption, customerChecked, customerTime, datePickerArr, timeValue, shortcutsValue]);
useEffect(() => {
let _diffDays = moment(dateRange[0].dateTo).diff(dateRange[0].dateFrom, 'days');
if (_diffDays > 7 && dataThinKey === '1min') {
setDataThinKey(timeIntervalList[1].key);
}
}, [dateRange]);
const [dates, setDates] = useState(null);
const configDependence = checkboxData
......@@ -400,7 +420,8 @@ const HistoryView = (props) => {
// 同期对比模式: 选择(日/月)
const onContrastChange = (value) => {
if (value === 'month') {
if (lineDataType === '原始曲线') message.info('月模式数据量较大,不支持原始曲线模式,已切换为特征曲线')
if (lineDataType === '原始曲线')
message.info('月模式数据量较大,不支持原始曲线模式,已切换为特征曲线');
setLineDataType('特征曲线');
}
setShortcutsValue('');
......@@ -458,13 +479,13 @@ const HistoryView = (props) => {
onContrastChange(contrastOption);
setShowBoxOption(false);
setChartType('lineChart');
onCheckboxChange({target: {value: false}}, 'chartType');
onCheckboxChange({target: {value: false}}, 'ignoreOutliers');
onCheckboxChange({ target: { value: false } }, 'chartType');
onCheckboxChange({ target: { value: false } }, 'ignoreOutliers');
} else {
// 自定义
// 不需要处理
setShowBoxOption(true);
onCheckboxChange({target: {value: true}}, 'chartType');
onCheckboxChange({ target: { value: true } }, 'chartType');
}
};
const onShortcutsChange = (e) => {
......@@ -474,38 +495,38 @@ const HistoryView = (props) => {
switch (_val) {
case '近3天':
_arr = [
{key: 1, value: moment()},
{key: 2, value: moment().subtract(1, 'days')},
{key: 3, value: moment().subtract(2, 'days')},
]
{ 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')},
]
{ 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')},
]
{ 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')},
]
{ 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);
......@@ -516,9 +537,7 @@ const HistoryView = (props) => {
<div className={classNames(`${prefixCls}-label`)}>时间选择</div>
<Radio.Group value={timeValue} onChange={onTimeSetChange}>
<Radio.Button value="customer">自定义</Radio.Button>
{
!grid ? <Radio.Button value="contrast">同期对比</Radio.Button> : ''
}
{!grid ? <Radio.Button value="contrast">同期对比</Radio.Button> : ''}
</Radio.Group>
{timeValue === 'customer' && ( // 自定义
<>
......@@ -560,30 +579,29 @@ const HistoryView = (props) => {
}}
showTime={{
format: 'YYYY-MM-DD HH:mm',
minuteStep: 10
minuteStep: 10,
}}
/>
</>
)}
{timeValue === 'contrast' && ( // 同期对比
<>
<Select value={contrastOption} style={{width: 60}} onChange={onContrastChange}>
<Select value={contrastOption} style={{ width: 60 }} onChange={onContrastChange}>
<Option value="day"></Option>
<Option value="month" disabled={lineDataType === '原始曲线'}></Option>
<Option value="month" disabled={lineDataType === '原始曲线'}>
</Option>
</Select>
{/*增加快捷日期*/}
{
deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1 ?
{deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1 ? (
<Radio.Group value={shortcutsValue} onChange={onShortcutsChange}>
{
(contrastOption === 'day' ? shortcutsForDay : shortcutsForMonth).map(item => {
return <Radio.Button value={item.value}>
{item.label}
</Radio.Button>
})
}
</Radio.Group> : ''
}
{(contrastOption === 'day' ? shortcutsForDay : shortcutsForMonth).map((item) => {
return <Radio.Button value={item.value}>{item.label}</Radio.Button>;
})}
</Radio.Group>
) : (
''
)}
{datePickerArr.map((child, index) => (
<div key={child.key} className={classNames(`${prefixCls}-contrast-list`)}>
<div className={classNames(`${prefixCls}-contrast-wrap`)}>
......@@ -592,14 +610,14 @@ const HistoryView = (props) => {
picker={contrastOption === 'day' ? undefined : contrastOption}
value={child.value}
onChange={(date, dateString) => onContrastPickerChange(date, dateString, child)}
style={{width: 130, border: !shortcutsValue ? '1px solid #1890ff' : ''}}
style={{ width: 130, border: !shortcutsValue ? '1px solid #1890ff' : '' }}
/>
{datePickerArr.length > 2 && (
<div
className={classNames(`${prefixCls}-contrast-delete`)}
onClick={() => handleDeleteDatePicker(index)}
>
<CloseCircleFilled/>
<CloseCircleFilled />
</div>
)}
</div>
......@@ -608,12 +626,21 @@ const HistoryView = (props) => {
)}
</div>
))}
{datePickerArr.length < 4 && <PlusCircleOutlined onClick={handleAddDatePicker}/>}
{datePickerArr.length < 4 && <PlusCircleOutlined onClick={handleAddDatePicker} />}
</>
)}
</div>
);
}, [timeValue, customerChecked, lineDataType, datePickerArr, deviceParams, dates, customerTime, chartDataSource]);
}, [
timeValue,
customerChecked,
lineDataType,
datePickerArr,
deviceParams,
dates,
customerTime,
chartDataSource,
]);
// 曲线设置项选择/取消
const onCheckboxChange = (e, key, showJustLine) => {
......@@ -651,7 +678,7 @@ const HistoryView = (props) => {
};
// 切换数据类型
const switchLineDataType = (e) => {
let _val = e.target.value
let _val = e.target.value;
let _startDate = dateRange[0]?.dateFrom;
let _endDate = dateRange[0]?.dateTo;
let diffDays = moment(_endDate).diff(moment(_startDate), 'days');
......@@ -662,7 +689,7 @@ const HistoryView = (props) => {
if (_val === '原始曲线') {
setContrastOption('day');
}
setLineDataType(_val)
setLineDataType(_val);
};
const renderCheckbox = (child, showJustLine) => {
const curveAccess = activeTabKey === 'curve' && child.showInCurve;
......@@ -678,18 +705,22 @@ const HistoryView = (props) => {
</Checkbox>
{child.tooltip && (
<Tooltip title={child.tooltip}>
<QuestionCircleFilled className={`${prefixCls}-question`}/>
<QuestionCircleFilled className={`${prefixCls}-question`} />
</Tooltip>
)}
{
child.hasSub && child.checked && false ?
<Select style={{width: 80, marginLeft: 10}} value={algorithmValue}
onChange={(e) => setAlgorithmValue(e)}>
{child.hasSub && child.checked && false ? (
<Select
style={{ width: 80, marginLeft: 10 }}
value={algorithmValue}
onChange={(e) => setAlgorithmValue(e)}
>
<Option value={1}></Option>
<Option value={5}></Option>
<Option value={10}></Option>
</Select> : ''
}
</Select>
) : (
''
)}
</>
)
);
......@@ -699,51 +730,56 @@ const HistoryView = (props) => {
return (
<div
className={classNames(`${prefixCls}-cover`)}
style={isChart && isSingle ? {width: '100%'} : {}}
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}
>
<Radio.Group value={lineDataType} onChange={switchLineDataType}>
<Radio.Button value={'特征曲线'}>特征曲线</Radio.Button>
<Radio.Button value={'原始曲线'}>原始曲线</Radio.Button>
</Radio.Group>
{/*<Segmented value={lineDataType} options={['特征曲线', '原始曲线']} onChange={switchLineDataType}/>*/}
<Tooltip title={'原始曲线数据量较大,单次查询最多展示1万条数据'}>
<QuestionCircleFilled style={{marginLeft: 6}} className={`${prefixCls}-question`}/>
<QuestionCircleFilled
style={{ marginLeft: 6 }}
className={`${prefixCls}-question`}
/>
</Tooltip>
</div>
</> : ''
}
</>
) : (
''
)}
{isChart && isSingle && showBoxOption && !isStatus ? (
<>
{
lineDataType !== '原始曲线' ? <>
<div style={{marginLeft: 7}} className={classNames(`${prefixCls}-label`)}>曲线形态</div>
{lineDataType !== '原始曲线' ? (
<>
<div style={{ marginLeft: 7 }} className={classNames(`${prefixCls}-label`)}>
曲线形态
</div>
<Radio.Group
value={chartType}
style={{marginRight: 16}}
style={{ marginRight: 16 }}
onChange={(e) => {
let _value = e.target.value;
setChartType(_value);
onCheckboxChange({target: {value: _value !== 'boxChart'}}, 'chartType');
onCheckboxChange({ target: { value: _value !== 'boxChart' } }, 'chartType');
}}
>
<Radio.Button value={'lineChart'}>线形图</Radio.Button>
<Radio.Button value={'boxChart'}>箱线图</Radio.Button>
</Radio.Group>
</> : ''
}
</>
) : (
''
)}
{
!isStatus ?
</>
) : (
''
)}
{!isStatus ? (
<>
<div className={classNames(`${prefixCls}-label`)}>
{activeTabKey !== 'table' ? '曲线设置' : '表格设置'}
......@@ -760,19 +796,28 @@ const HistoryView = (props) => {
{activeTabKey === 'table' && (
<Select
value={dataThinKey}
style={{width: 90}}
style={{ width: 90 }}
onChange={onTimeIntervalChange}
disabled={!dataConfig.dataThin}
>
{timeIntervalList.map((child) => (
{timeIntervalList
.filter((item) => {
let _diffDays = moment(dateRange[0].dateTo).diff(dateRange[0].dateFrom, 'days');
return !(_diffDays > 7 && item.key === '1min');
})
.map((child) => {
return (
<Option key={child.key} unit={child.unit} value={child.key}>
{child.name}
</Option>
))}
);
})}
</Select>
)}
</> : ''
}
</>
) : (
''
)}
</div>
);
};
......@@ -785,7 +830,10 @@ const HistoryView = (props) => {
let fileName = `数据报表-${i.deviceType}-${i.deviceCode}-${moment(timeFrom).format(
dateFormat,
)}${moment(timeTo).format(dateFormat)}`;
let _quotas = i.sensors.split(',').filter(item => item !== '是否在线').join(',')
let _quotas = i.sensors
.split(',')
.filter((item) => item !== '是否在线')
.join(',');
getExportDeviceHistoryUrl({
deviceType: i.deviceType,
deviceCode: i.deviceCode,
......@@ -802,15 +850,14 @@ const HistoryView = (props) => {
aDom.click();
aDom.remove();
})
.catch((err) => {
});
.catch((err) => {});
});
};
const exportFeatureBtn = () => {
message.info('报表生成中,请稍后~');
let _dataSource = tableData.sort((a, b) => {
let _a = a.time;
let _b = b.time
let _b = b.time;
if (timeValue === 'contrast') {
if (contrastOption === 'day') {
_a = `2000-01-01 ${a.time}:00`;
......@@ -822,37 +869,40 @@ const HistoryView = (props) => {
}
}
return timeOrder === 'ascend' ? moment(_a) - moment(_b) : moment(_b) - moment(_a);
})
});
let _columns = [...columns];
let timeFrom = dateRange?.[0]?.dateFrom || moment().format(startFormat);
let timeTo = dateRange?.[0]?.dateTo || moment().format(timeFormat);
let fileName = `特征数据-${moment(timeFrom).format(
let fileName = `特征数据-${moment(timeFrom).format(dateFormat)}${moment(timeTo).format(
dateFormat,
)}${moment(timeTo).format(dateFormat)}`;
)}`;
let _dataIndex = [];
let _titleWidth = [];
let _title = _columns.map(item => {
let _title = _columns.map((item) => {
_dataIndex.push(item.dataIndex);
let _titleStr = [item.name, item.title].filter(item => item).join('-');
let _titleStr = [item.name, item.title].filter((item) => item).join('-');
_titleWidth.push(_titleStr.length * 1);
return _titleStr;
});
ExportExcel({
name: fileName,
content: [{
content: [
{
sheetData: _dataSource,
sheetFilter: _dataIndex,
sheetHeader: _title,
columnWidths: _titleWidth
}]
})
columnWidths: _titleWidth,
},
],
});
};
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;
const dataIndexAccess = (dataItem, index) => {
const {stationCode, sensorName} = dataItem;
const { stationCode, sensorName } = dataItem;
return `${stationCode}-${sensorName}-${index}`;
};
......@@ -863,13 +913,13 @@ const HistoryView = (props) => {
// 判断是否是单设备,单设备则不显示设备名称
// 处理表头数据
const columnsData = data.map((item, index) => {
const {stationCode, equipmentName, sensorName, unit, dataModel} = item;
const { stationCode, equipmentName, sensorName, unit, dataModel } = item;
const dataIndex = dataIndexAccess(item, index);
let _title = '';
if (deviceConfig.current.oneDevice) {
_title = `${sensorName}${unit ? `(${unit})` : ''}`;
} else {
_title = `${equipmentName}-${sensorName}${unit ? `(${unit})` : ''}`
_title = `${equipmentName}-${sensorName}${unit ? `(${unit})` : ''}`;
}
let col = {
title: _title,
......@@ -878,7 +928,7 @@ const HistoryView = (props) => {
ellipsis: true,
align: 'center',
width: 200,
name: equipmentName
name: equipmentName,
};
// 同期对比
if (timeValue === 'contrast' && dataModel[0]) {
......@@ -893,7 +943,7 @@ const HistoryView = (props) => {
// 格式化时间对齐数据, 生成行数
const timeData = {};
const buildDefaultData = (time) => {
const obj = {key: time, time: time};
const obj = { key: time, time: time };
data.forEach((item, index) => {
const dataIndex = dataIndexAccess(item, index);
obj[dataIndex] = '';
......@@ -902,7 +952,7 @@ const HistoryView = (props) => {
return obj;
};
data.forEach((item, index) => {
const {stationCode, sensorName, dataModel} = item;
const { stationCode, sensorName, dataModel } = item;
dataModel &&
dataModel.forEach((data) => {
const formatTime = moment(data.pt).format(format);
......@@ -917,7 +967,7 @@ const HistoryView = (props) => {
});
// 处理表格数据
data.forEach((child, index) => {
const {dataModel} = child;
const { dataModel } = child;
const dataIndex = dataIndexAccess(child, index);
dataModel &&
dataModel.forEach((value, j) => {
......@@ -941,7 +991,9 @@ 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 = {}) => {
......@@ -966,40 +1018,45 @@ const HistoryView = (props) => {
};
const handleDataThinKey = (diffYears, diffDays, diffHours, lineDataType) => {
if (lineDataType === '原始曲线') {
return {unit: '', zoom: ''}
return { unit: '', zoom: '' };
}
// edit by zy 根据选择的时长控制抽稀频度
if (diffYears > 0) {
if (diffYears === 1) return {unit: 'h', zoom: '24'}
return {unit: 'h', zoom: '48'}
if (diffYears === 1) return { unit: 'h', zoom: '24' };
return { unit: 'h', zoom: '48' };
} else if (diffYears === 0 && diffDays > 0) {
if (diffDays > 90) return {unit: 'h', zoom: '24'}
if (diffDays > 30) return {unit: 'h', zoom: '4'}
if (diffDays > 15) return {unit: 'h', zoom: '2'}
if (diffDays > 7) return {unit: 'h', zoom: '1'}
if (diffDays > 3) return {unit: 'min', zoom: '20'}
if (diffDays > 1) return {unit: 'min', zoom: '15'}
if (diffDays === 1) return {unit: 'min', zoom: '5'}
if (diffDays > 90) return { unit: 'h', zoom: '24' };
if (diffDays > 30) return { unit: 'h', zoom: '4' };
if (diffDays > 15) return { unit: 'h', zoom: '2' };
if (diffDays > 7) return { unit: 'h', zoom: '1' };
if (diffDays > 3) return { unit: 'min', zoom: '20' };
if (diffDays > 1) return { unit: 'min', zoom: '15' };
if (diffDays === 1) return { unit: 'min', zoom: '5' };
} else if (diffYears === 0 && diffDays === 0 && diffHours > 0) {
if (diffHours > 12) return {unit: 'min', zoom: '5'}
if (diffHours > 4) return {unit: 'min', zoom: '1'}
if (diffHours > 1) return {unit: 's', zoom: '30'}
if (diffHours > 0) return {unit: 's', zoom: '5'}
return {unit: 's', zoom: '5'}
if (diffHours > 12) return { unit: 'min', zoom: '5' };
if (diffHours > 4) return { unit: 'min', zoom: '1' };
if (diffHours > 1) return { unit: 's', zoom: '30' };
if (diffHours > 0) return { unit: 's', zoom: '5' };
return { unit: 's', zoom: '5' };
} else {
return {unit: '', zoom: ''}
return { unit: '', zoom: '' };
}
};
// 处理接口服务参数的变化
const onChangeParams = (value = {}) => {
const {dateRange, isDilute, ignoreOutliers, zoom, unit} = value;
const { dateRange, isDilute, ignoreOutliers, zoom, unit } = value;
let _diffDays = moment(dateRange[0].dateTo).diff(dateRange[0].dateFrom, 'days');
// 查询时段大于7天时,不提供1分钟的抽稀选项。
if (_diffDays > 7 && zoom === '1' && unit === 'min') {
return false;
}
const requestArr = [];
const acrossTables = [];
const zoomArray = [];
deviceParams
.map((item) => {
let _item = {...item};
let _item = { ...item };
// 历史曲线中,是否在线暂时去除,不显示 要显示是否在线解开这里即可 2023-09-15
/* _item.sensors =
item.sensors && !item.sensors.includes('是否在线')
......@@ -1037,17 +1094,21 @@ 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) : {};
zoomArray.push(zoomParam);
requestArr.push(getHistoryInfo({...param, ...zoomParam}));
let zoomParam =
activeTabKey === 'curve'
? handleDataThinKey(diffYears, diffDays, diffHours, lineDataType)
: {};
requestArr.push(getHistoryInfo({ ...param, ...zoomParam }));
});
setLoading(true);
Promise.all(requestArr).then((results) => {
Promise.all(requestArr)
.then((results) => {
setLoading(false);
if (results.length) {
let data = [];
results.forEach((res, index) => {
const {dateFrom, dateTo} = dateRange?.[index] ?? {};
const { dateFrom, dateTo } = dateRange?.[index] ?? {};
if (res.code === 0 && res.data.length) {
res.data.forEach((d) => {
d.dateFrom = dateFrom || '';
......@@ -1062,7 +1123,8 @@ const HistoryView = (props) => {
sensors.push(special1.name);
}
}
const list = sensors.map((s) => {
const list = sensors
.map((s) => {
const dataItem = res.data.find(
(d) => d.stationCode === p.deviceCode && d.sensorName === s,
);
......@@ -1073,7 +1135,8 @@ const HistoryView = (props) => {
} else {
return {};
}
}).filter(item => item.sensorName);
})
.filter((item) => item.sensorName);
data = data.concat(list);
});
}
......@@ -1082,15 +1145,16 @@ const HistoryView = (props) => {
handleTableData(data);
setChartDataSource(data);
}
}).catch(err => {
})
.catch((err) => {
message.info('未查询到数据,请重试~');
setLoading(false)
setLoading(false);
});
};
useEffect(() => {
if (!completeInit) return;
const {dataThin, ignoreOutliers, zoom, unit} = dataConfig;
const { dataThin, ignoreOutliers, zoom, unit } = dataConfig;
beforChangeParams().finally(() => {
onChangeParams({
isDilute: dataThin,
......@@ -1104,11 +1168,12 @@ const HistoryView = (props) => {
}, [dateRange, dataConfig, deviceParams, chartType, lineDataType, completeInit, algorithmValue]);
const handleChange = (pagination, filter, sort) => {
if (sort.field === 'time') {
setTimeOrder(sort.order)
setTimeOrder(sort.order);
}
};
const tableMemo = useMemo(() => {
return <>
return (
<>
<div className={`${prefixCls}-options`}>
{renderTimeOption}
{renderCurveOption()}
......@@ -1118,7 +1183,7 @@ const HistoryView = (props) => {
<BasicTable
dataSource={tableData.sort((a, b) => {
let _a = a.time;
let _b = b.time
let _b = b.time;
if (timeValue === 'contrast') {
if (contrastOption === 'day') {
_a = `2000-01-01 ${a.time}:00`;
......@@ -1135,32 +1200,33 @@ const HistoryView = (props) => {
{...tableProps}
pagination={false}
onChange={handleChange}
scroll={{x: 'max-content', y: 'calc(100% - 40px)'}}
scroll={{ x: 'max-content', y: 'calc(100% - 40px)' }}
/>
) : (
<PandaEmpty/>
<PandaEmpty />
)}
</div>
</>
}, [timeOrder, chartDataSource, columns, tableProps, tableData, isSingleStatusSensor])
);
}, [timeOrder, chartDataSource, columns, tableProps, tableData, isSingleStatusSensor, dateRange]);
const returnLongestPeriod = (data) => {
let _earliest = ''
let _earliest = '';
let _latest = '';
data.forEach(item => {
data.forEach((item) => {
let _length = item.dataModel.length;
let _tempFirst = item.dataModel[0].pt;
let _tempLast = item.dataModel[_length - 1].pt;
if (_earliest) {
_earliest = moment(_earliest) > moment(_tempFirst) ? _tempFirst : _earliest
_earliest = moment(_earliest) > moment(_tempFirst) ? _tempFirst : _earliest;
} else {
_earliest = _tempFirst
_earliest = _tempFirst;
}
if (_latest) {
_latest = moment(_latest) < moment(_tempLast) ? _tempLast : _latest
_latest = moment(_latest) < moment(_tempLast) ? _tempLast : _latest;
} else {
_latest = _tempLast;
}
})
});
return `${_earliest} - ${_latest}`;
};
const renderPanel = (model) => {
......@@ -1172,16 +1238,17 @@ const HistoryView = (props) => {
{renderCurveOption(
true,
deviceParams?.length === 1 && deviceParams?.[0]?.sensors?.split(',').length === 1,
isSingleStatusSensor
isSingleStatusSensor,
)}
</div>
{
lineDataType === '原始曲线' && false ?
<div style={{marginTop: 10}}>展示区间:{returnLongestPeriod(chartDataSource)}</div> : ''
}
{lineDataType === '原始曲线' && false ? (
<div style={{ marginTop: 10 }}>展示区间:{returnLongestPeriod(chartDataSource)}</div>
) : (
''
)}
<div className={`${prefixCls}-content`}>
{!chartDataSource.length ? (
<PandaEmpty/>
<PandaEmpty />
) : grid === true ? (
<GridChart
curveCenter={curveCenter}
......@@ -1221,24 +1288,28 @@ const HistoryView = (props) => {
// 获取字段配置
const getDefaultOptions = async () => {
// 非单曲线、单指标不执行
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({
code: deviceCode
}))?.data?.[0]?.id;
let _params = {
// deviceType: deviceType
};
const { deviceCode, deviceType, sensors } = deviceParams[0];
let _id = (
await getPointAddress({
code: deviceCode,
})
)?.data?.[0]?.id;
let _params = {};
if (_id) _params.versionId = _id;
// 多曲线的居中,容易导致曲线被截断,故多曲线时,不请求
let _request0 = getDictionaryInfoAll({
level: '组件_ec_historyview'
level: '组件_ec_historyview',
});
// 以下请求为处理状态值、开关值的图表,只允许单曲线单指标情况下展示
let _request1 = getPointAddressEntry(_params);
let _request2 = getSensorType();
await Promise.all([_request0, _request1, _request2]).then(result => {
await Promise.all([_request0, _request1, _request2]).then((result) => {
if (result) {
let _res0 = result[0];
let _res1 = result[1];
......@@ -1246,13 +1317,13 @@ const HistoryView = (props) => {
// 查字典配置
if (_res0.code === 0) {
let _opt = _res0.data.reduce((final, cur) => {
final[cur.fieldName] = cur.fieldValue
return final
final[cur.fieldName] = cur.fieldValue;
return final;
}, {});
let _checkboxData = [...checkboxData].map(item => {
let _item = {...item};
let _checkboxData = [...checkboxData].map((item) => {
let _item = { ...item };
if (_opt[item.label] !== undefined) {
_item.checked = _opt[item.label] === 'true'
_item.checked = _opt[item.label] === 'true';
}
return _item;
});
......@@ -1260,19 +1331,19 @@ const HistoryView = (props) => {
}
// 查点表配置
if (_res1.code === 0) {
let _sensorConfig = _res1.data.find(item => item?.name.trim() === sensors.trim());
let _sensorConfig = _res1.data.find((item) => item?.name.trim() === sensors.trim());
let _statusName = _sensorConfig?.statusName;
setAllPointAddress(_res1.data);
if (_statusName) {
let _statusConfig = _res1.data.find(item => item?.name.trim() === _statusName.trim());
let _statusConfig = _res1.data.find((item) => item?.name.trim() === _statusName.trim());
let _valDesc = _statusConfig?.valDesc || '';
setSpecial1({
name: _statusName,
valDesc: _valDesc.split(';').reduce((final, cur) => {
let _arr = cur.split(':');
final[_arr[0]] = _arr[1];
return final
}, {})
return final;
}, {}),
});
}
}
......@@ -1280,8 +1351,8 @@ const HistoryView = (props) => {
if (_res2.code === 0) {
setAllSensorType(_res2.data);
let _sensorID = _res1.data?.find(item => item.name === sensors)?.sensorTypeID;
let _sensor = _res2.data?.find(item => item.id === _sensorID)?.type;
let _sensorID = _res1.data?.find((item) => item.name === sensors)?.sensorTypeID;
let _sensor = _res2.data?.find((item) => item.id === _sensorID)?.type;
let _isStatusSensor = ['状态值', '开关值'].includes(_sensor);
setIsSingleStatusSensor(_isStatusSensor);
}
......@@ -1291,10 +1362,11 @@ const HistoryView = (props) => {
};
useEffect(() => {
getDefaultOptions();
}, [deviceParams])
}, [deviceParams]);
let percentTimer = useRef({
timer: null
timer: null,
});
// 加载动画
useEffect(() => {
if (loading === null) return;
if (loading) {
......@@ -1303,34 +1375,45 @@ const HistoryView = (props) => {
_percent += 5;
if (_percent > 95) return clearInterval(percentTimer.current.timer);
setPercent(_percent);
}, 100)
}, 100);
} else {
clearInterval(percentTimer.current.timer);
setPercent(100);
setTimeout(() => {
setTimeout(
() => {
setPercent(0);
}, lineDataType === '原始曲线' ? 500 : 0)
},
lineDataType === '原始曲线' ? 500 : 0,
);
}
}, [loading])
}, [loading]);
return (
<div className={classNames(prefixCls, 'wkt-scroll-light')}>
<div className={classNames(`${prefixCls}-spin`)} style={{position: "relative"}}>
{
(loading || percent !== 0) ? <div className={classNames(`${prefixCls}-progressWrapper`)}>
<div className={classNames(`${prefixCls}-spin`)} style={{ position: 'relative' }}>
{loading || percent !== 0 ? (
<div className={classNames(`${prefixCls}-progressWrapper`)}>
<div className={classNames(`${prefixCls}-contentWrapper`)}>
{
lineDataType === '原始曲线' || lineDataType === '特征曲线' && moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >= 30 ? <>
<Progress percent={percent}
{lineDataType === '原始曲线' ||
(lineDataType === '特征曲线' &&
moment(dateRange?.[0]?.dateTo).diff(moment(dateRange?.[0]?.dateFrom), 'days') >=
30) ? (
<>
<Progress
percent={percent}
steps={20}
className={classNames(`${prefixCls}-progress`, `${prefixCls}-blink-2`)}
showInfo={false}/>
showInfo={false}
/>
<div className={classNames(`${prefixCls}-tip`)}>加载中...</div>
</> : <Spin spinning={loading || false}/>
}
</>
) : (
<Spin spinning={loading || false} />
)}
</div>
</div> : ''
}
</div>
) : (
''
)}
{showModels.length === 1 && (
<div className={`${prefixCls}-single-panel`}>{renderPanel(showModels[0])}</div>
)}
......@@ -1346,11 +1429,11 @@ const HistoryView = (props) => {
{activeTabKey === 'table' && (
<>
<Button type="link" onClick={exportFeatureBtn}>
<DownloadOutlined/>
<DownloadOutlined />
特征数据
</Button>
<Button type="link" onClick={exportExcelBtn}>
<DownloadOutlined/>
<DownloadOutlined />
原始数据
</Button>
</>
......@@ -1360,9 +1443,7 @@ const HistoryView = (props) => {
}}
>
<Tabs.TabPane key="curve" tab="曲线" forceRender={true}>
{
activeTabKey === 'curve' ? renderPanel('curve') : ''
}
{activeTabKey === 'curve' ? renderPanel('curve') : ''}
</Tabs.TabPane>
<Tabs.TabPane key="table" tab="表格">
{renderPanel('table')}
......
import moment from 'moment';
import _, {isArray} from 'lodash';
import _, { isArray } from 'lodash';
import maxIcon from './assets/最大实心.svg';
import minIcon from './assets/最小实心.svg';
import minIconDownArrow from './assets/最小实心箭头朝下.svg';
import lineChart from '@wisdom-components/basicchart/es/LineChart';
import * as echarts from "echarts";
import * as echarts from 'echarts';
/** 轴宽度, 用于计算多轴显示时, 轴线偏移和绘图区域尺寸 */
const AXIS_WIDTH = 40;
......@@ -68,7 +68,7 @@ const currentOption = isMobile() ? MOBILE_OPTION : PC_OPTION;
* @returns
*/
const nameFormatter = (data, contrast, contrastOption, nameWithSensor) => {
const {equipmentName, sensorName, unit, dataModel, dateFrom, dateTo} = data;
const { equipmentName, sensorName, unit, dataModel, dateFrom, dateTo } = data;
let name = nameWithSensor ? `${equipmentName}-${sensorName}` : equipmentName;
if (contrast) {
const time = dateFrom.slice(0, contrastOption === 'day' ? 10 : 7).replace(/-/g, '');
......@@ -103,7 +103,7 @@ const dataAccessor = (data, contrast, contrastOption) => {
* @returns Null/areaStyle, 为null显示曲线图, 为areaStyle对象显示为面积图.
*/
const areaStyleFormatter = (data) => {
const {sensorName} = data;
const { sensorName } = data;
return sensorName && sensorName.indexOf('流量') > -1 ? {} : null;
};
......@@ -114,7 +114,7 @@ const areaStyleFormatter = (data) => {
* @returns
*/
const minMax = (data) => {
const {dataModel} = data;
const { dataModel } = data;
let min = Number.MAX_SAFE_INTEGER;
let max = Number.MIN_SAFE_INTEGER;
dataModel.forEach((item) => {
......@@ -145,7 +145,7 @@ const markLineItem = (name, value, color) => {
export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
// 只有一个数据曲线时显示markline
if (!dataItem || !schemes || dataSource.length !== 1) return {};
const {deviceType, stationCode, sensorName, decimalPoint} = dataItem;
const { deviceType, stationCode, sensorName, decimalPoint } = dataItem;
const curSchemes = schemes.filter(
(item) =>
item.deviceCode === stationCode &&
......@@ -154,7 +154,7 @@ export const alarmMarkLine = (dataItem, index, dataSource, schemes) => {
);
const data = [];
curSchemes.forEach((scheme) => {
const {hLimit, hhLimit, lLimit, llLimit} = scheme;
const { hLimit, hhLimit, lLimit, llLimit } = scheme;
lLimit !== null && lLimit !== void 0 && data.push(markLineItem('低限', lLimit, '#fa8c16'));
hLimit !== null && hLimit !== void 0 && data.push(markLineItem('高限', hLimit, '#fa8c16'));
llLimit !== null && llLimit !== void 0 && data.push(markLineItem('低低限', llLimit, '#FF0000'));
......@@ -246,7 +246,7 @@ export const decorateAxisGridLine = (axis, showGrid) => {
*/
export const offlineArea = (dataItem) => {
if (!dataItem) return {};
const {dataModel} = dataItem;
const { dataModel } = dataItem;
let datas = new Array();
let offlineData = [];
let hasOffline = false;
......@@ -256,7 +256,7 @@ export const offlineArea = (dataItem) => {
{
name: '离线',
xAxis: new Date(item.pt),
label: {show: !datas?.length},
label: { show: !datas?.length },
},
];
hasOffline = true;
......@@ -280,7 +280,7 @@ export const offlineArea = (dataItem) => {
// tooltip 模板
const headTemplate = (param, opt) => {
if (!param) return '';
const {name, axisValueLabel, axisType, axisValue} = param;
const { name, axisValueLabel, axisType, axisValue } = param;
const timeFormat =
opt && opt.contrast
? opt.contrastOption === 'day'
......@@ -296,57 +296,65 @@ const headTemplate = (param, opt) => {
};
const seriesTemplate = (param, unit) => {
if (!param || param.seriesName === '自定义') return '';
const {value, encode} = param;
const { value, encode } = param;
// const val = value[encode.y[0]];
const _unit = unit || '';
const color = '#008CFF';
if (!isArray(value))
return ` <div style="display: flex; align-items: center;">
<span style="${isMobile()
<span style="${
isMobile()
? 'width: ' +
handlePx(90, 'px') +
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
: ''
}">${param.seriesName}</span>
<span style="display:inline-block;">:</span>
<span style="color:${color};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value?.toFixed(3) ?? '-'
<span style="color:${color};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value?.toFixed(3) ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>`;
return param.componentSubType !== 'candlestick'
? `<div style="display: flex; align-items: center;">
<span style="${isMobile()
<span style="${
isMobile()
? 'width: ' +
handlePx(90, 'px') +
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
: ''
}">${param.seriesName}</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value[1] ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>`
: `
<div style="display: flex; align-items: center;">
<span>首值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[1] ?? '-'
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value[1] ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: flex; align-items: center;">
<span>尾值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[2] ?? '-'
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value[2] ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: flex; align-items: center;">
<span>周期最小值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[3] ?? '-'
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value[3] ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: flex; align-items: center;">
<span>周期最大值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${value[4] ?? '-'
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(5, 'px')} 0 auto;">${
value[4] ?? '-'
}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
......@@ -409,7 +417,7 @@ const handleSpecial1 = (special, dataSource) => {
} else {
final.push({
lte: _pt,
gte: _pt,
gte: final[_length - 1].gte,
color: _colorMap[_valDesc[cur.pv]],
value: cur.pv,
text: _valDesc[cur.pv],
......@@ -445,7 +453,7 @@ const handleSpecial1 = (special, dataSource) => {
},
},
name: item.text,
label: {show: true},
label: { show: true },
},
{
xAxis: item.lte,
......@@ -490,18 +498,18 @@ const returnXAxis = ({
restOption,
smooth,
special,
yAxis
}) => {
yAxis,
}) => {
// 根据"指标名称"分类yAxis
const yAxisInterator = (() => {
const map = new Map();
let current = -1;
const get = (name) => (map.has(name) ? map.get(name) : map.set(name, ++current).get(name));
return {get};
return { get };
})();
let _offlineData = [];
// 生成visualMap、markArea
let {visualMap, markArea} = handleSpecial1(special, dataSource);
let { visualMap, markArea } = handleSpecial1(special, dataSource);
let _filterArr = ['是否在线'];
if (special?.special1?.name) {
_filterArr.push(special.special1.name);
......@@ -515,12 +523,12 @@ const returnXAxis = ({
return !_filterArr.includes(item.sensorName);
})
.map((item, index) => {
const {sensorName, unit} = item;
const { sensorName, unit } = item;
const name = nameFormatter(item, contrast, contrastOption, nameWithSensor);
const data = dataAccessor(item, contrast, contrastOption);
const type = 'line';
const areaStyle = areaStyleFormatter(item);
const _index = yAxis.findIndex(item => item.name === unit);
const _index = yAxis.findIndex((item) => item.name === unit);
const yAxisIndex = _index > -1 ? _index : 0;
const markLine = showMarkLine
? alarmMarkLine(item, index, dataSource, deviceAlarmSchemes)
......@@ -616,70 +624,69 @@ const handleMaxValue = (value) => {
if (value <= 1) return value.toFixed(2);
if (value >= 100000) return `${(value / 1000).toFixed(2)}k`;
return value.toFixed(2);
}
};
const reduceYAxis = (arr, dataSource) => {
let _offsetValue = [];
// 1. 合并相同单位的坐标轴
let _arr = arr.reduce((final, cur) => {
let _key = cur.name === null ? 'null' : cur.name
let _key = cur.name === null ? 'null' : cur.name;
if (!final[_key]) {
final[_key] = cur
final[_key] = cur;
}
return final;
}, {});
// 2. 合并相同单位的数据,找出最大值
let _maxValueArr = Object.values(dataSource.reduce((final, cur) => {
let _maxValueArr = Object.values(
dataSource.reduce((final, cur) => {
let _key = cur.sensorName === null ? 'null' : cur.sensorName;
let _maxValue = cur.dataModel.reduce((final, cur) => {
// eslint-disable-next-line no-param-reassign
if (cur.pv > final) final = cur.pv;
return final
return final;
}, 0);
if (final[_key] === undefined) {
final[_key] = _maxValue
final[_key] = _maxValue;
} else {
final[_key] = Math.max([final[_key], _maxValue]);
}
return final
}, {}));
return final;
}, {}),
);
// 3. 合并,生成Y轴配置
return Object.values(_arr).map((item, index) => {
let _key = item.name === null ? 'null' : item.name;
let _lastAxisNumber = _maxValueArr[index - 2];
let _baseOffset = _offsetValue[index - 2] ?? 0;
let _finalOffset = (
_lastAxisNumber !== undefined ? // 没有相邻的轴
(_lastAxisNumber === 0 ? 20 : _lastAxisNumber.toFixed(2).replaceAll('.', '').length) * 12
: 0
) + _baseOffset;
let _finalOffset =
(_lastAxisNumber !== undefined // 没有相邻的轴
? (_lastAxisNumber === 0 ? 20 : _lastAxisNumber.toFixed(2).replaceAll('.', '').length) * 12
: 0) + _baseOffset;
_offsetValue.push(_finalOffset);
return ({
return {
...item,
offset: _finalOffset,
position: index % 2 === 0 ? 'left' : 'right',
nameTextStyle: {
align: index % 2 === 0 ? 'right' : 'left',
},
})
};
});
};
/**
*
* 1. 生成常规的yAxis配置;
* 2. 处理sensorType为状态值的指标,生成yAxis配置
* 1. 生成常规的yAxis配置; 2. 处理sensorType为状态值的指标,生成yAxis配置
*
* @param {array} dataSource 数据源
* @param {boolean} needUnit 是否显示单位。
* @param {boolean} curveCenter 曲线是否居中。
* @param {boolean} showGridLine 是否显示网格线。
* @return {object} 返回左右轴的margin、yAxis的配置。
* */
const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
* @returns {object} 返回左右轴的margin、yAxis的配置。
*/
const handleYAxis = ({ dataSource, needUnit, curveCenter, showGridLine }) => {
const yAxisMap = new Map();
// 1. 找出最大值; 2. 计算出y轴最大宽度动态计算偏移距离;
dataSource.forEach((item, index) => {
const {sensorName, unit} = item;
const { sensorName, unit } = item;
const key = sensorName;
if (!yAxisMap.has(key)) {
const axis = {
......@@ -688,7 +695,7 @@ const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
axisLabel: {
formatter: (value) => {
return handleMaxValue(value);
}
},
},
axisLine: {
show: true,
......@@ -718,25 +725,35 @@ const handleYAxis = ({dataSource, needUnit, curveCenter, showGridLine}) => {
const axis = yAxisMap.get(key);
decorateAxisGridLine(axis, showGridLine);
});
const yAxis = yAxisMap.size > 0 ? reduceYAxis([...yAxisMap.values()], dataSource) : {type: 'value'};
const yAxis =
yAxisMap.size > 0 ? reduceYAxis([...yAxisMap.values()], dataSource) : { type: 'value' };
const leftNum = Math.ceil(yAxisMap.size / 2);
const rightNum = Math.floor(yAxisMap.size / 2);
return {leftNum, rightNum, yAxis};
return { leftNum, rightNum, yAxis };
};
/**
* 1. 最后的配置处理、合并
* dataZoom 缩放
* xAxis.minInterval X轴的最小间隔
* legend legend配置
*
* @param {object} restOption 额外配置
* @param {object} xAxis x轴的配置
* @param {array} legendData legend数组
* @param {object} xAxis X轴的配置
* @param {array} legendData Legend数组
* @param {string} chartType 线型 lineChart|boxChart
* @param {boolean} contrast 是否为同期对比
* @param {string} contrastOption 同期对比周期配置, day|month
* @param {object} config 其他的配置
* */
const assignOptions = (restOption, xAxis, legendData, chartType, contrast, contrastOption, config) => {
*/
const assignOptions = (
restOption,
xAxis,
legendData,
chartType,
contrast,
contrastOption,
config,
) => {
restOption.dataZoom = [
{
show: true,
......@@ -771,13 +788,15 @@ const assignOptions = (restOption, xAxis, legendData, chartType, contrast, contr
restOption.legend = {
...{
show: true,
right:10,
right: 10,
top: 30,
icon: 'rect',
itemWidth: 14,
itemHeight: 8,
itemGap: 20,
}, ...restOption.legend, ...{data: legendData}
},
...restOption.legend,
...{ data: legendData },
};
}
};
......@@ -912,24 +931,24 @@ const renderStatusItem = (params, api) => {
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height
height: height,
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
}
height: params.coordSys.height,
},
);
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style()
style: api.style(),
}
);
}
};
/**
* 图表配置项生成
......@@ -965,13 +984,13 @@ const optionGenerator = (
restOption,
special,
} = handleDefault(config, cusOption);
const {leftNum, rightNum, yAxis} = handleYAxis({
const { leftNum, rightNum, yAxis } = handleYAxis({
dataSource,
needUnit,
curveCenter,
showGridLine,
});
let {xAxis, series, visualMap} = returnXAxis({
let { xAxis, series, visualMap } = returnXAxis({
dataSource,
contrast,
contrastOption,
......@@ -982,7 +1001,7 @@ const optionGenerator = (
smooth,
restOption,
special,
yAxis
yAxis,
});
// 3. 判断是否开启网格;
const grid = handleGrid(dataSource, needUnit, leftNum, rightNum, chartType);
......@@ -998,19 +1017,20 @@ const optionGenerator = (
if (chartType === 'boxChart' && lineDataType === '特征曲线') {
const otherData =
dataSource?.[0]?.dataModel.map((item) => {
const {firstPV, lastPV, maxPV, minPV, pt} = item;
const { firstPV, lastPV, maxPV, minPV, pt } = item;
return [moment(pt).valueOf(), firstPV, lastPV, minPV, maxPV];
}) || []; //当存在othersData的时候,只是单曲线
xAxis = {type: 'time'};
xAxis = { type: 'time' };
decorateAxisGridLine(xAxis, showGridLine);
let unit = [];
series = series.map((item) => {
if (item.unit) unit.push(item.unit);
item.areaStyle = null;
return {...item, showSymbol: false};
return { ...item, showSymbol: false };
});
// 箱线图去除曲线 2023年10月17日
series=[{
series = [
{
type: 'candlestick',
name: '箱线图',
symbol: 'none',
......@@ -1021,7 +1041,8 @@ const optionGenerator = (
borderColor: '#FFA200',
borderColor0: '#44CD00',
},
}];
},
];
tooltip = tooltipAccessor(unit);
}
if (chartType === 'lineChart' || lineDataType === '原始曲线') {
......@@ -1033,13 +1054,9 @@ const optionGenerator = (
? `${_currentYear}-01-01 HH:mm:00`
: `${_currentYear}-01-DD HH:mm:00`; // 用来做同期对比,把日期拉到同一区间
let _maxValues = [];
/**
* 生成泳道图,分两种情况
* 1. 当最大值最小值都是正数时;
* 2. 当最大值小于零时(此时,最小值一定小于零);
*/
/** 生成泳道图,分两种情况 1. 当最大值最小值都是正数时; 2. 当最大值小于零时(此时,最小值一定小于零); */
dataSource?.[0]?.dataModel.forEach((item) => {
const {firstPV, lastPV, maxPV, minPV, pt} = item;
const { firstPV, lastPV, maxPV, minPV, pt } = item;
_maxValues.push(maxPV);
let time = contrast ? moment(pt).format(formatStr) : pt;
_maxData.push([
......@@ -1049,13 +1066,13 @@ const optionGenerator = (
_minData.push([moment(time).valueOf(), maxPV > 0 ? minPV : maxPV]);
}); //当存在othersData的时候,只是单曲线
// xAxis = {type: 'category', data: series[0].data.map(item => moment(item[0]).format('YYYY-MM-DD HH:mm:ss'))};
xAxis = {type: 'time'};
xAxis = { type: 'time' };
decorateAxisGridLine(xAxis, showGridLine);
let _unit = '';
series = series.map((item) => {
_unit = item.unit ?? '';
item.areaStyle = null;
return {...item, showSymbol: false};
return { ...item, showSymbol: false };
});
[[..._minData], [..._maxData]].forEach((item, index) => {
series.push({
......@@ -1084,12 +1101,14 @@ const optionGenerator = (
${headTemplate(e[0])}
<div>
<div style="display: flex; align-items: center;">
<span style="${isMobile()
<span style="${
isMobile()
? 'width: ' +
handlePx(90, 'px') +
';overflow:hidden;text-overflow:ellipsis;white-space:nowrap'
: ''
}">${e[0].seriesName
}">${
e[0].seriesName
}</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.NORMAL};margin: 0 ${handlePx(
5,
......@@ -1097,7 +1116,8 @@ const optionGenerator = (
)} 0 auto;">${e[0]?.value?.[1] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: ${lineDataType === '特征曲线' ? 'flex' : 'none'
<div style="display: ${
lineDataType === '特征曲线' ? 'flex' : 'none'
}; align-items: center;">
<span>周期最小值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
......@@ -1106,7 +1126,8 @@ const optionGenerator = (
)} 0 auto;">${e?.[1]?.value?.[1] ?? '-'}</span>
<span style="font-size: ${handlePx(12, 'px')};">${_unit ?? ''}</span>
</div>
<div style="display: ${lineDataType === '特征曲线' ? 'flex' : 'none'
<div style="display: ${
lineDataType === '特征曲线' ? 'flex' : 'none'
}; align-items: center;">
<span>周期最大值</span><span style="display:inline-block;">:</span>
<span style="color: ${COLOR.AVG};margin: 0 ${handlePx(
......@@ -1128,7 +1149,7 @@ const optionGenerator = (
} else {
tooltip = tooltipAccessor(
series.map((item) => item.unit),
{contrastOption, contrast},
{ contrastOption, contrast },
);
}
tooltip.timeFormat = tooltipTimeFormat;
......@@ -1157,7 +1178,7 @@ const handleDataSource = (dataSource) => {
// handleSpecial2()
dataSource[0].dataModel.forEach((item, index) => {
if (index === 0) {
_data.push(item)
_data.push(item);
} else if (index === _dataLength - 1) {
_data.push(item);
} else {
......@@ -1167,7 +1188,7 @@ const handleDataSource = (dataSource) => {
}
_temp = item;
});
return _data
return _data;
};
const returnLegend = (sensorType) => {
const _colorMap = {
......@@ -1195,7 +1216,7 @@ const handleSpecial2 = (special, sensorName, sensorType, data1, data2) => {
let _valDescMap = special.allValDesc[sensorName]?.split(';').reduce((final, cur) => {
let _arr = cur.split(':');
final[_arr[0]] = _arr[1];
return final
return final;
}, {});
name = _valDescMap[data1.pv];
color = _colorMap[name];
......@@ -1205,48 +1226,64 @@ const handleSpecial2 = (special, sensorName, sensorType, data1, data2) => {
if (sensorType === '开关值') {
const _switchColorMap = {
0: '#666666',
1: '#1685ff'
1: '#1685ff',
};
const _switchNameMap = {
0: '关',
1: '开'
}
1: '开',
};
name = _switchNameMap[data1.pv];
color = _switchColorMap[data1.pv];
}
value1 = moment(data1.pt).valueOf();
value2 = moment(data2.pt).valueOf();
return {color, value1, value2, name}
return { color, value1, value2, name };
};
const handleDataToSeries = (special, sensorName, sensorType, data) => {
let _data = [];
let _legend = [];
data.forEach((item, index) => {
if (index === data.length - 1) return;
let {color, value1, value2, name} = handleSpecial2(special, sensorName, sensorType, item, data[index + 1])
let { color, value1, value2, name } = handleSpecial2(
special,
sensorName,
sensorType,
item,
data[index + 1],
);
if (!_legend.includes(name)) _legend.push(name);
_data.push({
itemStyle: {normal: {color}},
itemStyle: { normal: { color } },
name: name,
value: [0, value1, value2, `${item.pt}-${data[index + 1].pt}`]
value: [0, value1, value2, `${item.pt}-${data[index + 1].pt}`],
});
});
return {data: _data, legend: _legend};
return { data: _data, legend: _legend };
};
const specialTypeChartOptionGenerator = ({dataSource, cusOption, contrast, contrastOption, smooth, config}) => {
const {special, sensorType} = config;
const {allSensorType, allPointAddress} = special;
const specialTypeChartOptionGenerator = ({
dataSource,
cusOption,
contrast,
contrastOption,
smooth,
config,
}) => {
const { special, sensorType } = config;
const { allSensorType, allPointAddress } = special;
// 处理原始数据,处理数据为后series数据
const sensorName = dataSource[0].sensorName;
let _data = handleDataSource(dataSource);
let {data, legend} = handleDataToSeries(special, sensorName, sensorType, _data);
let { data, legend } = handleDataToSeries(special, sensorName, sensorType, _data);
// 1. x/y轴
let xAxis = {
type: 'time',
axisLabel: {
formatter: contrast ? (contrastOption === 'month' ? '{dd}日' : '{HH}:{mm}')
formatter: contrast
? contrastOption === 'month'
? '{dd}日'
: '{HH}:{mm}'
: {
year: '{yyyy}',
month: '{MMM}',
......@@ -1259,44 +1296,44 @@ const specialTypeChartOptionGenerator = ({dataSource, cusOption, contrast, contr
},
minorTick: {
lineStyle: {
color: "#e2e2e2"
color: '#e2e2e2',
},
show: true,
splitNumber: 2
splitNumber: 2,
},
minorSplitLine: {
lineStyle: {
color: "#e2e2e2",
type: "dashed"
color: '#e2e2e2',
type: 'dashed',
},
show: true
show: true,
},
splitLine: {
show: true
show: true,
},
minInterval: 3600 * 1000
minInterval: 3600 * 1000,
};
let yAxis = {
data: [dataSource[0].sensorName],
axisLine: {
show: true
show: true,
},
minorTick: {
lineStyle: {
color: "#e2e2e2"
color: '#e2e2e2',
},
show: true,
splitNumber: 2
splitNumber: 2,
},
minorSplitLine: {
lineStyle: {
color: "#e2e2e2",
type: "dashed"
color: '#e2e2e2',
type: 'dashed',
},
show: true
show: true,
},
splitLine: {
show: true
show: true,
},
};
//2. series
......@@ -1305,15 +1342,15 @@ const specialTypeChartOptionGenerator = ({dataSource, cusOption, contrast, contr
type: 'custom',
renderItem: renderStatusItem,
itemStyle: {
opacity: 0.8
opacity: 0.8,
},
encode: {
x: [1, 2],
y: 0
y: 0,
},
data
data,
},
...legend.map(item => {
...legend.map((item) => {
let _map = {
变频: '#1685ff',
工频: '#00d0c7',
......@@ -1321,23 +1358,22 @@ const specialTypeChartOptionGenerator = ({dataSource, cusOption, contrast, contr
故障: '#ff6b37',
停止: '#666666',
: '#666666',
: '#1685ff'
}
: '#1685ff',
};
return {
type: 'custom',
name: item,
color: _map[item],
renderItem: () => {
}
}
})
renderItem: () => {},
};
}),
];
let grid = {
top: 80,
left: 30,
right: 10,
bottom: 60,
containLabel: true
containLabel: true,
};
let legendConfig = {
show: true,
......@@ -1360,23 +1396,21 @@ const specialTypeChartOptionGenerator = ({dataSource, cusOption, contrast, contr
trigger: 'item',
formatter: function (params) {
return params.marker + params.name + ': ' + params?.value?.[3];
}
},
},
dataZoom: [
{
type: 'slider',
filterMode: 'weakFilter',
showDataShadow: false,
labelFormatter: ''
labelFormatter: '',
},
{
type: 'inside',
filterMode: 'weakFilter'
}
filterMode: 'weakFilter',
},
],
}
};
return _option;
};
export {
specialTypeChartOptionGenerator
}
\ No newline at end of file
export { specialTypeChartOptionGenerator };
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