Commit bb746b68 authored by 陈龙's avatar 陈龙

feat: 多图表支持最大值最小值标签

parent dfa489ca
...@@ -37,7 +37,7 @@ path: / ...@@ -37,7 +37,7 @@ path: /
[//]: # (## 多图表) [//]: # (## 多图表)
[//]: # (<code src="./demos/GridDemo.js"></code>) <code src="./demos/GridDemo.js"></code>
## API ## API
......
...@@ -20,6 +20,227 @@ const ChartTitle = ({prefixCls, title, unit}) => { ...@@ -20,6 +20,227 @@ const ChartTitle = ({prefixCls, title, unit}) => {
</div> </div>
); );
}; };
const ChartWidthRef = (props) => {
const ref = useRef(null);
const timerRef = useRef(null);
const minMaxMarkPoint = (dataSource, chart, isInit) => {
// 只有一个数据曲线时显示markline
if (dataSource.length !== 1) return {};
// 1. 找出最大、最小的值
let pointArr = dataSource[0].dataModel;
let valueArr = pointArr.map(item => item.pv);
let maxValue = Math.max(...valueArr);
let minValue = Math.min(...valueArr);
// 2. 找出点的索引和实际的点
let maxValueIndex = valueArr.findIndex(val => val === maxValue);
let minValueIndex = valueArr.findIndex(val => val === minValue);
let maxPoint = pointArr[maxValueIndex];
let minPoint = pointArr[minValueIndex];
if (!maxPoint || !minPoint) return {}
// 3. 通过最大值、最小值,数组的首值尾值以及图表宽度来确认markpoint的位置
let _opts = chart.getOption();
let zoom = _opts.dataZoom[0];
let startPoint = pointArr[0];
let endPoint = pointArr[pointArr.length - 1];
let timePeriod = isInit ? moment(endPoint.pt) - moment(startPoint.pt) : zoom.endValue - zoom.startValue;
let chartWidth = chart.getWidth();
// 需要考虑是否为0的情况
// 4. 计算最大最小值的标签宽度
let maxLength = 70 + String(maxValue).length * 5;
let minLength = 70 + String(minValue).length * 5;
// 5. 确定是否超出边界,确定超出边界是哪一边;
// 用首尾时间评分
let gapOfYAxisToEdge = 62;
let startTime = isInit ? moment(startPoint.pt) : zoom.startValue;
let maxPointPosition = ((chartWidth - gapOfYAxisToEdge) / timePeriod) * (moment(maxPoint.pt).valueOf() - startTime) + gapOfYAxisToEdge;
let minPointPosition = ((chartWidth - gapOfYAxisToEdge) / timePeriod) * (moment(minPoint.pt).valueOf() - startTime) + gapOfYAxisToEdge;
let maxTagLeft = maxPointPosition - maxLength / 2;
let maxTagRight = maxPointPosition + maxLength / 2;
let minTagLeft = minPointPosition - minLength / 2;
let minTagRight = minPointPosition + minLength / 2;
let maxOutEdge = false, minOutEdge = false;
let maxOutSide = '', minOutSide = '';
// 在实际使用中,我们认为不存在一个tag同时超出边界的情况。
if (maxTagLeft < 0) {
maxOutEdge = true;
maxOutSide = 'left';
}
if (maxTagRight > chartWidth) {
maxOutEdge = true;
maxOutSide = 'right';
}
if (minTagLeft < 0) {
minOutEdge = true;
minOutSide = 'left';
}
if (minTagRight > chartWidth) {
minOutEdge = true;
minOutSide = 'right';
}
// 6. 确定使用的图形
// 默认图形,居中
let maxIconPath = `path://M1233.329493 195.75466633h-1112.064c-34.128213 0-61.781333 27.661653-61.781333 61.781334v473.658026c0 34.117973 27.65312 61.781333 61.781333 61.781334h472.80128l83.23072 144.155306 83.23072-144.155306h472.80128c34.128213 0 61.781333-27.66336 61.781334-61.781334V257.53600033c0-34.117973-27.65312-61.781333-61.781334-61.781334z`;
let maxSymbolOffset = [0, -20];
if (maxOutEdge) {
if (maxOutSide === 'left') {
// 左边界超出,使用朝右的图表
maxSymbolOffset = ['50%', -20];
maxIconPath = 'path://M48.677,6.151v10c0,1.7-1.3,3-3,3h-35.5l-6.7,4c0,0,0.2-5.3,0.2-7v-10c0-1.7,1.3-3,3-3h39C47.277,3.151,48.677,4.551,48.677,6.151z';
} else if (maxOutSide === 'right') {
// 右边界超出,使用朝左的图表
maxSymbolOffset = ['-50%', -20];
maxIconPath = 'path://M6.477,3.151h39c1.7,0,3,1.3,3,3v10c0,1.7,0.2,7,0.2,7l-6.7-4h-35.5c-1.7,0-3-1.3-3-3v-10C3.477,4.551,4.877,3.151,6.477,3.151z'
}
}
// 默认图形
let minIconPath = 'path://M131.999 849.579h1112.064c34.128 0 61.781-27.662 61.781-61.781v-473.658c0-34.118-27.653-61.781-61.781-61.781h-472.801l-83.231-144.155-83.231 144.155h-472.801c-34.128 0-61.781 27.663-61.781 61.781v473.658c0 34.118 27.653 61.781 61.781 61.781z';
let minSymbolOffset = [0, 20];
if (minOutEdge) {
if (minOutSide === 'left') {
// 左边界超出,使用朝右的图表
minSymbolOffset = ['50%', 20];
minIconPath = 'path://M45.677,23.151h-39c-1.7,0-3-1.3-3-3v-10c0-1.7-0.2-7-0.2-7l6.7,4h35.5c1.7,0,3,1.3,3,3v10C48.677,21.751,47.277,23.151,45.677,23.151z';
} else if (minOutSide === 'right') {
// 右边界超出,使用朝左的图表
minSymbolOffset = ['-50%', 20];
minIconPath = 'path://M3.477,20.151v-10c0-1.7,1.3-3,3-3h35.5l6.7-4c0,0-0.2,5.3-0.2,7v10c0,1.7-1.3,3-3,3h-39C4.877,23.151,3.477,21.751,3.477,20.151z'
}
}
const data = [
{
type: 'min',
color: 'rgba(255,255,255,1)',
name: '最小: ',
symbolOffset: minSymbolOffset,
symbol: minIconPath,
symbolSize: (e) => {
let str = ![undefined, null].includes(e) ? String(e) : '';
let length = 60 + str.length * 6
return [length, 32]
},
label: {
show: true,
color: '#fff',
formatter: '最小: {c}',
fontSize: 14,
fontWeight: 'bold',
verticalAlign: 'top',
offset: [0, -2]
},
itemStyle: {
// color: "#21c8c3",
}
},
{
type: 'max',
name: '最大: ',
position: [20, 200],
symbol: maxIconPath,
symbolOffset: maxSymbolOffset,
symbolSize: (e) => {
let str = ![undefined, null].includes(e) ? String(e) : '';
let length = 60 + str.length * 6
return [length, 32]
},
itemStyle: {
// color: "#1980ff",
},
label: {
color: '#fff',
fontSize: 14,
fontWeight: 'bold',
show: true,
formatter: '最大: {c}',
offset: [0, -2]
}
},
{
name: '',
type: 'max',
symbol: 'emptyCircle',
label: {show: false},
symbolSize: 6,
},
{
name: '',
type: 'min',
symbol: 'emptyCircle',
label: {show: false},
symbolSize: 6,
}
];
return {
symbol: 'circle',
symbolSize: 20,
animation: false,
silent: true,
label: {
show: false,
},
data,
};
};
const renderMarkPoint = (isInit) => {
if (timerRef.current) clearTimeout(timerRef.current);
const chart = ref.current?.getEchartsInstance?.();
timerRef.current = setTimeout(() => {
chart.setOption({
series: {markPoint: minMaxMarkPoint(props.data.list, chart, isInit)}
})
}, 200)
};
useEffect(() => {
if (props.data.list?.length !== 1) return;
const chart = ref.current?.getEchartsInstance?.();
chart.setOption({
series: {
markLine: {
silent: false,
symbol: 'none',
data: [{
name: '平均线',
type: 'average',
lineStyle: {
color: '#00b8b1',
type: 'solid',
},
label: {
position: 'insideEndTop',
color: '#00b8b1',
formatter: function (param) {
return `平均值:${param.value}`;
},
},
}]
}
}
})
function dataZoomFn() {
renderMarkPoint(false)
}
chart.on('legendselectchanged', renderMarkPoint);
chart.on('datazoom', dataZoomFn);
return () => {
chart.off('legendselectchanged', renderMarkPoint);
chart.off('datazoom', dataZoomFn);
}
}, [props.option]);
useEffect(() => {
if (props.data.list?.length !== 1) return;
renderMarkPoint(false)
}, [props.data.list]);
return <BasicChart
ref={ref}
{...props}
/>
}
const GridChart = memo((props) => { const GridChart = memo((props) => {
const { const {
dataSource, dataSource,
...@@ -232,11 +453,12 @@ const GridChart = memo((props) => { ...@@ -232,11 +453,12 @@ const GridChart = memo((props) => {
}, },
}, },
}; };
const option = optionGenerator(list, cusOption, contrast, contrastOption, smooth, { const option = optionGenerator(list, cusOption, null, contrastOption, smooth, {
curveCenter, curveCenter,
nameWithSensor: false, nameWithSensor: false,
showGridLine: true, showGridLine: true,
isMultiple: gridData.length > 1 isMultiple: gridData.length > 1,
chartType: 'lineChart'
}); });
// 无数据时,图表需要显示默认图形 2024年3月14日 // 无数据时,图表需要显示默认图形 2024年3月14日
// 1. x轴 // 1. x轴
...@@ -261,6 +483,8 @@ const GridChart = memo((props) => { ...@@ -261,6 +483,8 @@ const GridChart = memo((props) => {
}); });
option.tooltip = false; option.tooltip = false;
} }
delete option.xAxis.max
delete option.xAxis.min
return { return {
key, key,
option: option, option: option,
...@@ -293,8 +517,8 @@ const GridChart = memo((props) => { ...@@ -293,8 +517,8 @@ const GridChart = memo((props) => {
{isEmpty ? ( {isEmpty ? (
isInit ? '' : <PandaEmpty/> isInit ? '' : <PandaEmpty/>
) : ( ) : (
<BasicChart <ChartWidthRef
// ref={chartRef[index]} data={gridData[index]}
style={{width: '100%', height: '100%'}} style={{width: '100%', height: '100%'}}
option={item.option} option={item.option}
notMerge notMerge
......
...@@ -95,30 +95,27 @@ import HistoryView from '../index'; ...@@ -95,30 +95,27 @@ import HistoryView from '../index';
];*/ ];*/
const deviceParams = [ const deviceParams = [
{ {
"deviceCode": "EGJZ00000318", "deviceCode": "DYSC00000001",
"sensors": "进水压力,出水瞬时流量", // "sensors": "出水压力,出水瞬时流量,今日供水量,出厂水浊度",
"deviceType": "二供机组" "sensors": "出水压力,出水瞬时流量",
}, "deviceType": "大邑生产数据"
{
"deviceCode": "EGBF00000184",
"sensors": "进水压力,出水瞬时流量,今日用电量",
"deviceType": "二供泵房"
}, },
{ /* {
"deviceCode": "EGBF00000081", "deviceCode": "DYSC00000006",
"sensors": "进水压力,出水瞬时流量,今日用电量", "sensors": "出水压力,出水瞬时流量,今日供水量,出厂水浊度",
"deviceType": "二供泵房" "deviceType": "大邑生产数据"
}, },
{ {
"deviceCode": "EGBF00000135", "deviceCode": "DYSC00000007",
"sensors": "进水压力,出水瞬时流量,今日用电量", "sensors": "出水压力,出水瞬时流量,今日供水量,出厂水浊度",
"deviceType": "二供泵房" "deviceType": "大邑生产数据"
} }*/
] ]
const Demo = () => { const Demo = () => {
return <div> return <div>
<div style={{height: 700}}> <div style={{height: 700}}>
<HistoryView deviceParams={deviceParams} grid defaultDateRange={['2022-12-12 12:12:12','2022-12-31 23:23:23']}/> {/*<HistoryView deviceParams={deviceParams} grid defaultDateRange={['2022-12-12 12:12:12','2022-12-31 23:23:23']}/>*/}
<HistoryView deviceParams={deviceParams} grid />
</div> </div>
</div>; </div>;
}; };
......
...@@ -3,9 +3,8 @@ import HistoryView from '../index'; ...@@ -3,9 +3,8 @@ import HistoryView from '../index';
const deviceParams = [ const deviceParams = [
{ {
"deviceCode": "EGBF00000245", "deviceCode": "EGBF00000023",
"sensors": "进水压力,余氯", "sensors": "出水瞬时流量",
// "sensors": "进水压力",
"deviceType": "二供泵房" "deviceType": "二供泵房"
} }
] ]
...@@ -14,7 +13,8 @@ const Demo = () => { ...@@ -14,7 +13,8 @@ const Demo = () => {
<> <>
<div> <div>
<div style={{height: 700}}> <div style={{height: 700}}>
<HistoryView deviceParams={deviceParams} defaultModel="curve" defaultDateRange={['2022-12-12 12:12:12','2022-12-31 23:23:23']}/> {/*<HistoryView deviceParams={deviceParams} defaultModel="curve" defaultDateRange={['2022-12-12 12:12:12','2022-12-31 23:23:23']}/>*/}
<HistoryView deviceParams={deviceParams} defaultModel="curve" />
{/*<HistoryView theme={'BI'} deviceParams={deviceParams} defaultModel="curve"/>*/} {/*<HistoryView theme={'BI'} deviceParams={deviceParams} defaultModel="curve"/>*/}
</div> </div>
</div> </div>
......
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