Merge branch 'lsf-dev'
commit
39c270984a
|
|
@ -123,6 +123,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ant-table-cell-fix-left, .ant-table-cell-fix-right{
|
||||
background: transparent !important;
|
||||
}
|
||||
.ant-table-thead > tr > th, .ant-table-tbody > tr > td, .ant-table tfoot > tr > th, .ant-table tfoot > tr > td{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Table Scrollbar Fix (Remove white strip)
|
||||
.ant-table-body, .ant-table-content {
|
||||
&::-webkit-scrollbar {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,304 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Table, Radio } from 'antd';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import * as echarts from 'echarts';
|
||||
// import './ProcessLineChart.less'; // Reuse styles or create new ones
|
||||
|
||||
// Mock Data
|
||||
const mockData = {
|
||||
dates: ['2026-01-26', '2026-01-27', '2026-01-28', '2026-01-29', '2026-01-30', '2026-01-31', '2026-02-01', '2026-02-02', '2026-02-03', '2026-02-04', '2026-02-05', '2026-02-06', '2026-02-07'],
|
||||
reservoirLevel: [704.5, 704.4, 704.6, 704.5, 704.4, 704.5, 704.6, 704.5, 704.39, 704.4, 704.5, 704.6, 704.5],
|
||||
points: {
|
||||
'01': {
|
||||
x: [-89.88, -89.48, -88.77, -89.0, -88.19, -88.5, -88.44, -89.1, -88.75, -89.2, -88.9, -89.0, -88.8],
|
||||
y: [-26.96, -27.22, -28.33, -28.17, -28.48, -29.06, -28.73, -27.9, -28.5, -28.2, -28.1, -28.0, -28.4],
|
||||
z: [10.5, 10.6, 10.4, 10.5, 10.7, 10.6, 10.5, 10.6, 10.5, 10.4, 10.5, 10.6, 10.5]
|
||||
},
|
||||
'02': {
|
||||
x: [-114.67, -114.43, -113.94, -113.45, -113.72, -113.51, -113.9, -114.2, -114.29, -113.8, -114.0, -114.1, -114.3],
|
||||
y: [-24.63, -25.46, -24.91, -25.42, -24.57, -25.23, -25.19, -24.8, -25.0, -25.3, -25.1, -25.2, -25.4],
|
||||
z: [15.2, 15.3, 15.1, 15.2, 15.4, 15.3, 15.2, 15.3, 15.2, 15.1, 15.2, 15.3, 15.2]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getChartOption = (data, mainType, subType) => {
|
||||
const pointNames = Object.keys(data.points);
|
||||
const colors = ['#5470C6', '#91CC75', '#EE6666'];
|
||||
|
||||
// Determine Y-axis name and data key based on types
|
||||
let yAxisName = '';
|
||||
let dataKey = '';
|
||||
|
||||
if (mainType === 'horizontal') {
|
||||
if (subType === 'upDown') {
|
||||
yAxisName = '上下游水平位移(mm)';
|
||||
dataKey = 'x';
|
||||
} else {
|
||||
yAxisName = '左右岸水平位移(mm)';
|
||||
dataKey = 'y';
|
||||
}
|
||||
} else {
|
||||
yAxisName = '垂直位移(mm)';
|
||||
dataKey = 'z';
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: 'transparent',
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross', crossStyle: { color: '#999' } }
|
||||
},
|
||||
legend: {
|
||||
data: ['库水位(m)', ...pointNames],
|
||||
textStyle: { color: '#fff' }
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: data.dates,
|
||||
axisPointer: { type: 'shadow' },
|
||||
axisLine: { lineStyle: { color: '#86909C' } },
|
||||
axisLabel: { color: '#fff' }
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: yAxisName,
|
||||
axisLabel: { formatter: '{value}', color: '#fff' },
|
||||
nameTextStyle: { color: '#fff' },
|
||||
splitLine: { lineStyle: { color: '#4E5969', type: 'dashed' } }
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '库水位(m)',
|
||||
min: 690,
|
||||
max: 715,
|
||||
interval: 5,
|
||||
axisLabel: { formatter: '{value}', color: '#fff' },
|
||||
nameTextStyle: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '库水位(m)',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: data.reservoirLevel,
|
||||
lineStyle: { color: '#EE6666', width: 2 },
|
||||
itemStyle: { color: '#EE6666' },
|
||||
symbol: 'circle',
|
||||
symbolSize: 6
|
||||
},
|
||||
...pointNames.map((name, index) => ({
|
||||
name: name,
|
||||
type: 'line',
|
||||
yAxisIndex: 0,
|
||||
data: data.points[name][dataKey],
|
||||
lineStyle: { color: colors[index] },
|
||||
itemStyle: { color: colors[index] }
|
||||
}))
|
||||
],
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 0, end: 100 },
|
||||
{
|
||||
start: 0, end: 100,
|
||||
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: { color: '#fff', shadowBlur: 3, shadowColor: 'rgba(0, 0, 0, 0.6)', shadowOffsetX: 2, shadowOffsetY: 2 },
|
||||
textStyle: { color: '#fff' }
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
const DeformationPanel = () => {
|
||||
const [chartData] = useState(mockData);
|
||||
const [mainType, setMainType] = useState('horizontal'); // 'horizontal' | 'vertical'
|
||||
const [subType, setSubType] = useState('upDown'); // 'upDown' | 'leftRight'
|
||||
|
||||
// Generate Table Columns
|
||||
const getTableColumns = () => {
|
||||
const pointNames = Object.keys(chartData.points);
|
||||
|
||||
// Fixed columns
|
||||
const columns = [
|
||||
{
|
||||
title: '监测日期',
|
||||
dataIndex: 'date',
|
||||
key: 'date',
|
||||
width: 120,
|
||||
fixed: 'left',
|
||||
}
|
||||
];
|
||||
|
||||
// Dynamic columns for each point
|
||||
if (mainType === 'horizontal') {
|
||||
// For horizontal, show x and y
|
||||
columns.push({
|
||||
title: '水平位移(mm)',
|
||||
children: pointNames.map(name => ({
|
||||
title: name,
|
||||
children: [
|
||||
{ title: 'x', dataIndex: `${name}_x`, key: `${name}_x`, width: 80 },
|
||||
{ title: 'y', dataIndex: `${name}_y`, key: `${name}_y`, width: 80 }
|
||||
]
|
||||
}))
|
||||
});
|
||||
} else {
|
||||
// For vertical, show z
|
||||
columns.push({
|
||||
title: '垂直位移(mm)',
|
||||
children: pointNames.map(name => ({
|
||||
title: name,
|
||||
dataIndex: `${name}_z`,
|
||||
key: `${name}_z`,
|
||||
width: 100
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
// Generate Table Data
|
||||
const getTableData = () => {
|
||||
const dataSource = chartData.dates.map((date, index) => {
|
||||
const row = { key: index, date: date };
|
||||
|
||||
Object.keys(chartData.points).forEach(name => {
|
||||
if (mainType === 'horizontal') {
|
||||
row[`${name}_x`] = chartData.points[name].x[index];
|
||||
row[`${name}_y`] = chartData.points[name].y[index];
|
||||
} else {
|
||||
row[`${name}_z`] = chartData.points[name].z[index];
|
||||
}
|
||||
});
|
||||
|
||||
return row;
|
||||
});
|
||||
|
||||
// Summary (Max, Min, Range)
|
||||
const summary = {
|
||||
max: { date: '最大值', key: 'max' },
|
||||
min: { date: '最小值', key: 'min' },
|
||||
range: { date: '变幅', key: 'range' },
|
||||
};
|
||||
|
||||
const pointNames = Object.keys(chartData.points);
|
||||
const fields = [];
|
||||
pointNames.forEach(name => {
|
||||
if (mainType === 'horizontal') {
|
||||
fields.push(`${name}_x`, `${name}_y`);
|
||||
} else {
|
||||
fields.push(`${name}_z`);
|
||||
}
|
||||
});
|
||||
|
||||
fields.forEach(field => {
|
||||
const [name, key] = field.split('_');
|
||||
const values = chartData.points[name][key];
|
||||
const validValues = values.filter(v => v !== null && v !== undefined);
|
||||
|
||||
if (validValues.length > 0) {
|
||||
const max = Math.max(...validValues);
|
||||
const min = Math.min(...validValues);
|
||||
summary.max[field] = max.toFixed(2);
|
||||
summary.min[field] = min.toFixed(2);
|
||||
summary.range[field] = (max - min).toFixed(2);
|
||||
} else {
|
||||
summary.max[field] = '-';
|
||||
summary.min[field] = '-';
|
||||
summary.range[field] = '-';
|
||||
}
|
||||
});
|
||||
|
||||
return [...dataSource, summary.max, summary.min, summary.range];
|
||||
};
|
||||
|
||||
return (
|
||||
<Row gutter={16} style={{ height: '100%' }}>
|
||||
{/* Left Chart */}
|
||||
<Col span={14} style={{ height: '100%' }}>
|
||||
<ReactECharts
|
||||
option={getChartOption(chartData, mainType, subType)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{/* Right Table */}
|
||||
<Col span={10} style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
<div style={{ marginBottom: 10, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
|
||||
{mainType === 'horizontal' && (
|
||||
<Radio.Group
|
||||
value={subType}
|
||||
onChange={e => setSubType(e.target.value)}
|
||||
buttonStyle="solid"
|
||||
size="small"
|
||||
style={{ marginRight: 12 }}
|
||||
>
|
||||
<Radio.Button value="upDown" style={{
|
||||
borderRadius: '4px 0 0 4px',
|
||||
marginLeft: '-1px',
|
||||
background: subType === 'upDown' ? 'rgba(0, 160, 233, 0.8)' : 'rgba(18, 56, 102, 0.6)',
|
||||
borderColor: '#00a0e9',
|
||||
color: '#fff'
|
||||
}}>上下游</Radio.Button>
|
||||
<Radio.Button value="leftRight" style={{
|
||||
borderRadius: '0 4px 4px 0',
|
||||
marginLeft: '-1px',
|
||||
background: subType === 'leftRight' ? 'rgba(0, 160, 233, 0.8)' : 'rgba(18, 56, 102, 0.6)',
|
||||
borderColor: '#00a0e9',
|
||||
color: '#fff'
|
||||
}}>左右岸</Radio.Button>
|
||||
</Radio.Group>
|
||||
)}
|
||||
|
||||
<Radio.Group
|
||||
value={mainType}
|
||||
onChange={e => {
|
||||
setMainType(e.target.value);
|
||||
if (e.target.value === 'horizontal') {
|
||||
setSubType('upDown'); // Reset subType when switching back
|
||||
}
|
||||
}}
|
||||
buttonStyle="solid"
|
||||
size="small"
|
||||
>
|
||||
<Radio.Button value="horizontal" style={{
|
||||
borderRadius: '4px 0 0 4px',
|
||||
background: mainType === 'horizontal' ? 'rgba(0, 160, 233, 0.8)' : 'rgba(18, 56, 102, 0.6)',
|
||||
borderColor: '#00a0e9',
|
||||
color: '#fff'
|
||||
}}>水平位移</Radio.Button>
|
||||
<Radio.Button value="vertical" style={{
|
||||
borderRadius: '0 4px 4px 0',
|
||||
marginLeft: '-1px',
|
||||
background: mainType === 'vertical' ? 'rgba(0, 160, 233, 0.8)' : 'rgba(18, 56, 102, 0.6)',
|
||||
borderColor: '#00a0e9',
|
||||
color: '#fff'
|
||||
}}>垂直位移</Radio.Button>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={getTableColumns()}
|
||||
dataSource={getTableData()}
|
||||
pagination={false}
|
||||
scroll={{ y: 'calc(100vh - 350px)' }}
|
||||
size="small"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeformationPanel;
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Table } from 'antd';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import * as echarts from 'echarts';
|
||||
// import './ProcessLineChart.less';
|
||||
|
||||
// Mock Data based on the screenshot
|
||||
const mockData = {
|
||||
dates: ['2026-01-26', '2026-01-27', '2026-01-28', '2026-01-29', '2026-01-30', '2026-01-31', '2026-02-01', '2026-02-02', '2026-02-03', '2026-02-04', '2026-02-05', '2026-02-06', '2026-02-07'],
|
||||
rainfall: [0, 0, 0.5, 0.5, 0, 0, 0, 0, 3, 0, 0, 0, 0],
|
||||
reservoirLevel: [null, null, null, null, 1141.7, 1141.69, 1141.65, 1141.52, 1144.41, 1144.34, 1144.34, 1144.33, 1144.3],
|
||||
pipes: {
|
||||
'UPD1': [1142, 1141.96, 1141.8, 1141.71, 1141.7, 1141.69, 1141.65, 1141.52, 1141.39, 1141.42, 1141.64, 1141.55, 1141.73],
|
||||
'UPD2': [1132.42, 1132.43, 1132.43, 1132.4, 1132.42, 1132.43, 1132.43, 1132.43, 1132.43, 1132.41, 1132.4, 1132.4, 1132.41],
|
||||
'UPD3': [1125.47, 1125.47, 1125.46, 1125.41, 1125.48, 1125.51, 1125.51, 1125.52, 1125.48, 1125.42, 1125.41, 1125.46, 1125.46],
|
||||
}
|
||||
};
|
||||
|
||||
const getChartOption = (data) => {
|
||||
const pipeNames = Object.keys(data.pipes);
|
||||
const colors = ['#5470C6', '#91CC75', '#EE6666', '#73C0DE', '#3BA272', '#FC8452', '#9A60B4', '#EA7CCC'];
|
||||
|
||||
return {
|
||||
backgroundColor: 'transparent',
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
crossStyle: {
|
||||
color: '#999'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['雨量', '库水位', ...pipeNames],
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: data.dates,
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#86909C'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '水位(m)',
|
||||
min: 1115,
|
||||
max: 1155,
|
||||
interval: 5,
|
||||
axisLabel: {
|
||||
formatter: '{value}',
|
||||
color: '#fff'
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#4E5969'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '雨量(mm)',
|
||||
min: 0,
|
||||
max: 500,
|
||||
interval: 100,
|
||||
axisLabel: {
|
||||
formatter: '{value}',
|
||||
color: '#fff'
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '雨量',
|
||||
type: 'bar',
|
||||
yAxisIndex: 1,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' mm';
|
||||
}
|
||||
},
|
||||
data: data.rainfall,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '库水位',
|
||||
type: 'line',
|
||||
yAxisIndex: 0,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' m';
|
||||
}
|
||||
},
|
||||
data: data.reservoirLevel,
|
||||
lineStyle: {
|
||||
color: colors[2]
|
||||
}
|
||||
},
|
||||
...pipeNames.map((name, index) => ({
|
||||
name: name,
|
||||
type: 'line',
|
||||
yAxisIndex: 0,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' m';
|
||||
}
|
||||
},
|
||||
data: data.pipes[name],
|
||||
lineStyle: {
|
||||
color: colors[index + 3]
|
||||
}
|
||||
}))
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 100,
|
||||
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: {
|
||||
color: '#fff',
|
||||
shadowBlur: 3,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.6)',
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
const ProcessLineChart = () => {
|
||||
const [chartData] = useState(mockData);
|
||||
|
||||
const tableData = chartData.dates.map((date, index) => {
|
||||
const row = {
|
||||
key: index,
|
||||
date: date,
|
||||
reservoirLevel: chartData.reservoirLevel[index],
|
||||
rainfall: chartData.rainfall[index],
|
||||
};
|
||||
for (const pipeName in chartData.pipes) {
|
||||
row[pipeName] = chartData.pipes[pipeName][index];
|
||||
}
|
||||
return row;
|
||||
});
|
||||
|
||||
const pipeNames = Object.keys(chartData.pipes);
|
||||
const columns = [
|
||||
{ title: '监测日期', dataIndex: 'date', key: 'date', width: 120, fixed: 'left', align: 'center' },
|
||||
{ title: '库水位(m)', dataIndex: 'reservoirLevel', key: 'reservoirLevel', width: 100, align: 'center' },
|
||||
{ title: '雨量(mm)', dataIndex: 'rainfall', key: 'rainfall', width: 100, align: 'center' },
|
||||
{
|
||||
title: '渗压水位(m)',
|
||||
align: 'center',
|
||||
children: pipeNames.map(name => ({
|
||||
title: name,
|
||||
dataIndex: name,
|
||||
key: name,
|
||||
width: 100,
|
||||
align: 'center'
|
||||
})),
|
||||
},
|
||||
];
|
||||
const summaryNode = () => {
|
||||
const summary = {
|
||||
max: { date: '最大值' },
|
||||
min: { date: '最小值' },
|
||||
range: { date: '变幅' },
|
||||
};
|
||||
|
||||
const fields = ['reservoirLevel', 'rainfall', ...pipeNames];
|
||||
fields.forEach(field => {
|
||||
const values = chartData[field] || chartData.pipes[field];
|
||||
let max = -Infinity, min = Infinity, maxDate = '', minDate = '';
|
||||
values.forEach((v, i) => {
|
||||
if (v !== null && v !== undefined) {
|
||||
if (v > max) { max = v; maxDate = chartData.dates[i]; }
|
||||
if (v < min) { min = v; minDate = chartData.dates[i]; }
|
||||
}
|
||||
});
|
||||
|
||||
if (max === -Infinity) {
|
||||
summary.max[field] = 'N/A';
|
||||
summary.min[field] = 'N/A';
|
||||
summary.range[field] = 'N/A';
|
||||
summary.max[`${field}_date`] = '';
|
||||
summary.min[`${field}_date`] = '';
|
||||
} else {
|
||||
summary.max[field] = max.toFixed(2);
|
||||
summary.min[field] = min.toFixed(2);
|
||||
summary.range[field] = (max - min).toFixed(2);
|
||||
summary.max[`${field}_date`] = maxDate;
|
||||
summary.min[`${field}_date`] = minDate;
|
||||
}
|
||||
});
|
||||
|
||||
const cellContentStyle = { whiteSpace: 'pre-line', textAlign: 'center', verticalAlign: 'middle', borderBottom: '1px solid rgba(0, 160, 233, 0.3)' };
|
||||
const rowStyle = { background: 'rgba(0, 33, 64, 0.85)', color: '#fff' };
|
||||
const fixedCellStyle = {
|
||||
...cellContentStyle,
|
||||
background: 'rgba(0, 33, 64, 0.95)',
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
boxShadow: '2px 0 5px rgba(0,0,0,0.3)'
|
||||
};
|
||||
|
||||
return (
|
||||
<Table.Summary fixed>
|
||||
<Table.Summary.Row style={rowStyle}>
|
||||
<Table.Summary.Cell index={0} fixed="left" style={fixedCellStyle}>{summary.max.date}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1} style={cellContentStyle}>{summary.max.reservoirLevel}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2} style={cellContentStyle}>{summary.max.rainfall}</Table.Summary.Cell>
|
||||
{pipeNames.map((name, i) => <Table.Summary.Cell key={`${name}-max`} index={i + 3} style={cellContentStyle}>{summary.max[name]}</Table.Summary.Cell>)}
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row style={rowStyle}>
|
||||
<Table.Summary.Cell index={0} fixed="left" style={fixedCellStyle}>日期</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1} style={cellContentStyle}>{summary.max.reservoirLevel_date}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2} style={cellContentStyle}>{summary.max.rainfall_date}</Table.Summary.Cell>
|
||||
{pipeNames.map((name, i) => <Table.Summary.Cell key={`${name}-max-date`} index={i + 3} style={cellContentStyle}>{summary.max[`${name}_date`]}</Table.Summary.Cell>)}
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row style={rowStyle}>
|
||||
<Table.Summary.Cell index={0} fixed="left" style={fixedCellStyle}>{summary.min.date}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1} style={cellContentStyle}>{summary.min.reservoirLevel}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2} style={cellContentStyle}>{summary.min.rainfall}</Table.Summary.Cell>
|
||||
{pipeNames.map((name, i) => <Table.Summary.Cell key={`${name}-min`} index={i + 3} style={cellContentStyle}>{summary.min[name]}</Table.Summary.Cell>)}
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row style={rowStyle}>
|
||||
<Table.Summary.Cell index={0} fixed="left" style={fixedCellStyle}>日期</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1} style={cellContentStyle}>{summary.min.reservoirLevel_date}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2} style={cellContentStyle}>{summary.min.rainfall_date}</Table.Summary.Cell>
|
||||
{pipeNames.map((name, i) => <Table.Summary.Cell key={`${name}-min-date`} index={i + 3} style={cellContentStyle}>{summary.min[`${name}_date`]}</Table.Summary.Cell>)}
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row style={rowStyle}>
|
||||
<Table.Summary.Cell index={0} fixed="left" style={fixedCellStyle}>{summary.range.date}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={1} style={cellContentStyle}>{summary.range.reservoirLevel}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell index={2} style={cellContentStyle}>{summary.range.rainfall}</Table.Summary.Cell>
|
||||
{pipeNames.map((name, i) => <Table.Summary.Cell key={`${name}-range`} index={i + 3} style={cellContentStyle}>{summary.range[name]}</Table.Summary.Cell>)}
|
||||
</Table.Summary.Row>
|
||||
</Table.Summary>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Row gutter={16} style={{ height: '100%' }}>
|
||||
<Col span={12} style={{ height: '98%' }}>
|
||||
<ReactECharts
|
||||
option={getChartOption(chartData)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12} style={{ height: '100%' }}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={tableData}
|
||||
pagination={false}
|
||||
scroll={{ y: 'calc(100vh - 650px)' }}
|
||||
summary={summaryNode}// Adjust based on your layout
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProcessLineChart;
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
import * as echarts from 'echarts';
|
||||
|
||||
export const getOption = (data = {}) => {
|
||||
const {
|
||||
waterLevel = 113.8,
|
||||
floodLevel = 128.42,
|
||||
pipes = [
|
||||
{ name: 'UPD4', x: 110, top: 175, bottom: 60, level: 103.67 },
|
||||
{ name: 'UPD1', x: 160, top: 175, bottom: 60, level: 103.67 },
|
||||
{ name: 'UPD2', x: 190, top: 150, bottom: 60, level: 102.89 },
|
||||
{ name: 'UPD3', x: 210, top: 130, bottom: 60, level: 101.12 },
|
||||
]
|
||||
} = data;
|
||||
|
||||
// Dam Geometry Configuration
|
||||
const foundationY = 40;
|
||||
const crestY = 180;
|
||||
|
||||
// Left Slope (Upstream)
|
||||
const leftToe = [20, foundationY];
|
||||
const leftCrest = [130, crestY];
|
||||
|
||||
// Right Slope (Downstream) with Berm
|
||||
const rightCrest = [150, crestY];
|
||||
const bermElevation = 120;
|
||||
const bermStart = [190, bermElevation];
|
||||
const bermEnd = [210, bermElevation];
|
||||
const rightToe = [250, foundationY];
|
||||
const distanceFromCrest = 20; // Distance from top of core to dam crest
|
||||
|
||||
const coreBaseLeft = 125;
|
||||
const coreBaseRight = 155;
|
||||
const coreTopLeft = 135;
|
||||
const coreTopRight = 145;
|
||||
const coreBottomY = foundationY;
|
||||
// const coreTopY = crestY - distanceFromCrest;
|
||||
const coreTopY = floodLevel;
|
||||
|
||||
// Dam Toe Weight (Gray Mound)
|
||||
// Shifted left to intersect with the main slope
|
||||
const toeWeightPoints = [
|
||||
[230, foundationY],
|
||||
[240, 55],
|
||||
[245, 55],
|
||||
[255, foundationY]
|
||||
];
|
||||
|
||||
// Colors
|
||||
const damColor = '#Decbb6'; // Light beige for shell
|
||||
const coreColor = '#D2a88d'; // Darker for core
|
||||
const foundationColor = '#8B4513'; // Dark brown
|
||||
const waterColor = 'rgba(0, 150, 136, 0.8)';
|
||||
const saturationLineColor = '#00a0e9'; // Bright blue
|
||||
const floodLineColor = 'red';
|
||||
const toeColor = '#808080'; // Gray for toe
|
||||
|
||||
// Calculations
|
||||
// Upstream Slope Line: y - y1 = m(x - x1)
|
||||
const mUp = (leftCrest[1] - leftToe[1]) / (leftCrest[0] - leftToe[0]);
|
||||
const cUp = leftToe[1] - mUp * leftToe[0];
|
||||
|
||||
// Intersection of Water Level with Upstream Slope
|
||||
// x = (y - c) / m
|
||||
const waterX = (waterLevel - cUp) / mUp;
|
||||
const floodX = (floodLevel - cUp) / mUp;
|
||||
|
||||
// Render Pipe Function
|
||||
const renderPipe = (params, api) => {
|
||||
const x = api.value(0);
|
||||
const top = api.value(1);
|
||||
const bottom = api.value(2);
|
||||
const level = api.value(3);
|
||||
const name = api.value(4);
|
||||
|
||||
const topPos = api.coord([x, top]);
|
||||
const bottomPos = api.coord([x, bottom]);
|
||||
const levelPos = api.coord([x, level]);
|
||||
|
||||
const width = 12;
|
||||
|
||||
return {
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'rect',
|
||||
shape: {
|
||||
x: topPos[0] - width / 2,
|
||||
y: topPos[1],
|
||||
width: width,
|
||||
height: levelPos[1] - topPos[1]
|
||||
},
|
||||
style: {
|
||||
fill: 'rgba(200,200,200,0.2)',
|
||||
stroke: '#999',
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'rect',
|
||||
shape: {
|
||||
x: levelPos[0] - width / 2,
|
||||
y: levelPos[1],
|
||||
width: width,
|
||||
height: bottomPos[1] - levelPos[1]
|
||||
},
|
||||
style: {
|
||||
fill: '#00a0e9',
|
||||
stroke: '#999',
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
style: {
|
||||
text: name,
|
||||
x: topPos[0],
|
||||
y: topPos[1] - 15,
|
||||
textAlign: 'center',
|
||||
fill: '#fff',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
style: {
|
||||
text: level.toFixed(2) + 'm',
|
||||
x: levelPos[0] + width / 2 + 5,
|
||||
y: levelPos[1],
|
||||
textAlign: 'left',
|
||||
textVerticalAlign: 'middle',
|
||||
fill: '#fff',
|
||||
fontSize: 11
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
// Saturation Line Logic
|
||||
const damCenterX = 140;
|
||||
const leftPipes = pipes.filter(p => p.x < damCenterX).sort((a, b) => a.x - b.x);
|
||||
const rightPipes = pipes.filter(p => p.x >= damCenterX).sort((a, b) => a.x - b.x);
|
||||
|
||||
const saturationPoints = [];
|
||||
|
||||
// 1. Start at water surface
|
||||
saturationPoints.push([waterX, waterLevel]);
|
||||
|
||||
// 2. Handle Left Side
|
||||
if (leftPipes.length > 0) {
|
||||
// Connect all left pipes
|
||||
leftPipes.forEach(p => saturationPoints.push([p.x, p.level]));
|
||||
// Extend to center from the last left pipe
|
||||
saturationPoints.push([damCenterX, leftPipes[leftPipes.length - 1].level]);
|
||||
} else {
|
||||
// No left pipes: Extend from water level to center
|
||||
saturationPoints.push([damCenterX, waterLevel]);
|
||||
}
|
||||
|
||||
// 3. Handle Right Side
|
||||
// Connect to the nearest pipe on the right (and subsequent ones)
|
||||
rightPipes.forEach(p => saturationPoints.push([p.x, p.level]));
|
||||
|
||||
// 4. End logic
|
||||
if (rightPipes.length > 0) {
|
||||
const lastPipe = rightPipes[rightPipes.length - 1];
|
||||
if (lastPipe.level < foundationY) {
|
||||
// If last pipe level is below foundation, extend horizontally to x=250
|
||||
saturationPoints.push([250, lastPipe.level]);
|
||||
} else {
|
||||
// Otherwise, connect to toe
|
||||
saturationPoints.push(rightToe);
|
||||
}
|
||||
} else {
|
||||
// No right pipes, connect to toe
|
||||
saturationPoints.push(rightToe);
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: 'transparent',
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' },
|
||||
formatter: () => ''
|
||||
},
|
||||
grid: {
|
||||
left: 60,
|
||||
right: 60,
|
||||
top: 60,
|
||||
bottom: 40
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 280,
|
||||
axisLabel: { color: '#fff' },
|
||||
splitLine: { show: false }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 200,
|
||||
axisLabel: { color: '#fff', formatter: '{value}' },
|
||||
splitLine: {
|
||||
lineStyle: { type: 'dashed', color: 'rgba(255,255,255,0.1)' }
|
||||
},
|
||||
name: '断面高(m)',
|
||||
nameTextStyle: { color: '#fff', padding: [0, 0, 0, 20] }
|
||||
},
|
||||
series: [
|
||||
// 1. Foundation
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [[0, 0], [280, 0], [280, foundationY], [0, foundationY]].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: foundationColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 2. Dam Body Shell (Left)
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [leftToe, leftCrest, [coreTopLeft, coreTopY], [coreBaseLeft, foundationY]].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: damColor, stroke: damColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 3. Dam Body Shell (Right with Berm)
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [
|
||||
[coreBaseRight, foundationY],
|
||||
[coreTopRight, coreTopY],
|
||||
rightCrest,
|
||||
bermStart,
|
||||
bermEnd,
|
||||
rightToe
|
||||
].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: damColor, stroke: damColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 4. Core Wall
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [
|
||||
[coreBaseLeft, coreBottomY],
|
||||
[coreTopLeft, coreTopY],
|
||||
[coreTopRight, coreTopY],
|
||||
[coreBaseRight, coreBottomY]
|
||||
].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: coreColor, stroke: coreColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 5. Dam Crest Filler (Top Part)
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [
|
||||
[coreTopLeft, coreTopY],
|
||||
[coreTopRight, coreTopY],
|
||||
rightCrest,
|
||||
leftCrest
|
||||
].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: damColor, stroke: damColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 6. Dam Toe Weight
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = toeWeightPoints.map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: toeColor, stroke: toeColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 6. Upstream Water Level (Correctly Filled)
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: (params, api) => {
|
||||
const points = [
|
||||
[0, foundationY],
|
||||
leftToe, // Join the slope toe
|
||||
[waterX, waterLevel], // Join the slope at water level
|
||||
[0, waterLevel]
|
||||
].map(p => api.coord(p));
|
||||
return {
|
||||
type: 'polygon',
|
||||
shape: { points },
|
||||
style: { fill: waterColor },
|
||||
silent: true
|
||||
};
|
||||
},
|
||||
data: [[0, 0]]
|
||||
},
|
||||
// 7. Saturation Line
|
||||
{
|
||||
type: 'line',
|
||||
smooth: 0.4,
|
||||
symbol: 'none',
|
||||
lineStyle: { color: saturationLineColor, width: 3 },
|
||||
data: saturationPoints,
|
||||
z: 11
|
||||
},
|
||||
// 8. Flood Level (Dashed Horizontal)
|
||||
{
|
||||
type: 'line',
|
||||
markLine: {
|
||||
symbol: ['none', 'none'],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'middle',
|
||||
formatter: `校核洪水位 ${floodLevel}m`,
|
||||
color: '#fff',
|
||||
padding: [0, 0, 10, 0]
|
||||
},
|
||||
lineStyle: { color: floodLineColor, type: 'dashed', width: 2 },
|
||||
data: [
|
||||
[
|
||||
{ x:70, yAxis: floodLevel },
|
||||
{ xAxis: floodX, yAxis: floodLevel } // To the dam body
|
||||
]
|
||||
]
|
||||
},
|
||||
data: []
|
||||
},
|
||||
// 9. Red Line from Flood Level to Dam Toe
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'none',
|
||||
lineStyle: { color: '#ff0000', width: 2, type: 'solid' }, // Red solid line
|
||||
data: [
|
||||
[floodX, floodLevel],
|
||||
[coreTopLeft, coreTopY],
|
||||
[coreTopRight, coreTopY],
|
||||
[237, 50] // To dam toe top
|
||||
],
|
||||
z: 10 // Ensure it's on top
|
||||
},
|
||||
// 10. Measured Water Level Label
|
||||
{
|
||||
type: 'scatter',
|
||||
symbol: 'triangle',
|
||||
symbolSize: 10,
|
||||
symbolRotate: 180,
|
||||
itemStyle: { color: saturationLineColor },
|
||||
label: {
|
||||
show: true,
|
||||
formatter: `实测水位 ${waterLevel}m`,
|
||||
position: 'top',
|
||||
color: '#fff',
|
||||
fontSize: 12,
|
||||
offset: [0, 0]
|
||||
},
|
||||
data: [[waterX / 2, waterLevel]]
|
||||
},
|
||||
// 10. Pipes
|
||||
{
|
||||
type: 'custom',
|
||||
renderItem: renderPipe,
|
||||
data: pipes.map(p => [p.x, p.top, p.bottom, p.level, p.name]),
|
||||
z: 12
|
||||
},
|
||||
]
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { DatePicker, Select, Button } from 'antd';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import moment from 'moment';
|
||||
import { getOption } from './chartOption';
|
||||
import ProcessLineChart from './ProcessLineChart';
|
||||
import DeformationPanel from './DeformationPanel';
|
||||
import './index.less';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
const { Option } = Select;
|
||||
|
||||
const SafetyPanel = () => {
|
||||
const [activeTab, setActiveTab] = useState('seepage');
|
||||
const [filter, setFilter] = useState({
|
||||
type: 'saturation', // saturation | process
|
||||
section: 'main_0_086',
|
||||
date: [moment().subtract(7, 'days'), moment()]
|
||||
});
|
||||
|
||||
const [chartData, setChartData] = useState({});
|
||||
const [quickDate, setQuickDate] = useState('1m'); // Default to 1 month
|
||||
|
||||
useEffect(() => {
|
||||
// Mock fetching data
|
||||
// In a real scenario, this would depend on the selected section
|
||||
setChartData({
|
||||
waterLevel: 113.8,
|
||||
floodLevel: 128.42,
|
||||
pipes: [
|
||||
{ name: 'UPD4', x: 110, top: 175, bottom: 60, level: 117.67 },
|
||||
{ name: 'UPD1', x: 160, top: 175, bottom: 60, level: 100.67 },
|
||||
{ name: 'UPD2', x: 190, top: 150, bottom: 30, level: 40 },
|
||||
{ name: 'UPD3', x: 210, top: 130, bottom: 20, level: 30 },
|
||||
]
|
||||
});
|
||||
}, [filter]);
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
setActiveTab(key);
|
||||
};
|
||||
|
||||
const handleQuickDate = (type) => {
|
||||
setQuickDate(type);
|
||||
let start = moment();
|
||||
if (type === '1m') start = moment().subtract(1, 'months');
|
||||
if (type === '6m') start = moment().subtract(6, 'months');
|
||||
if (type === '1y') start = moment().subtract(1, 'years');
|
||||
setFilter({ ...filter, date: [start, moment()] });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="safety-panel-container">
|
||||
{/* Sidebar for navigation */}
|
||||
<div className="sidebar">
|
||||
<div
|
||||
className={`sidebar-btn ${activeTab === 'seepage' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('seepage')}
|
||||
>
|
||||
渗压监测
|
||||
</div>
|
||||
<div
|
||||
className={`sidebar-btn ${activeTab === 'deformation' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('deformation')}
|
||||
>
|
||||
变形监测
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main content area */}
|
||||
<div className="main-content-area">
|
||||
{/* Filters bar */}
|
||||
<div className="filters-bar">
|
||||
{activeTab === 'seepage' ? (
|
||||
<>
|
||||
<Select
|
||||
value={filter.type}
|
||||
style={{ width: 120, marginRight: 12 }}
|
||||
onChange={v => setFilter({...filter, type: v})}
|
||||
dropdownStyle={{ background: '#0d2c4d', color: '#fff' }}
|
||||
>
|
||||
<Option value="saturation">浸润线图</Option>
|
||||
<Option value="process">过程线图</Option>
|
||||
</Select>
|
||||
|
||||
<span className="filter-label">断面:</span>
|
||||
<Select
|
||||
value={filter.section}
|
||||
style={{ width: 150, marginRight: 12 }}
|
||||
onChange={v => setFilter({...filter, section: v})}
|
||||
dropdownStyle={{ background: '#0d2c4d', color: '#fff' }}
|
||||
>
|
||||
<Option value="main_0_086">主坝0+086</Option>
|
||||
</Select>
|
||||
|
||||
<span className="filter-label">时间:</span>
|
||||
<RangePicker
|
||||
value={filter.date}
|
||||
onChange={v => setFilter({...filter, date: v})}
|
||||
style={{ width: 260, marginRight: 12 }}
|
||||
dropdownClassName="dark-picker-dropdown"
|
||||
/>
|
||||
|
||||
<Button className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={() => {}}>查询</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Select value="过程线图" style={{ width: 120, marginRight: 12 }} disabled dropdownStyle={{ background: '#0d2c4d', color: '#fff' }}>
|
||||
<Option value="process">过程线图</Option>
|
||||
</Select>
|
||||
|
||||
<RangePicker
|
||||
value={filter.date}
|
||||
onChange={v => {
|
||||
setFilter({...filter, date: v});
|
||||
setQuickDate('');
|
||||
}}
|
||||
style={{ width: 260, marginRight: 12 }}
|
||||
dropdownClassName="dark-picker-dropdown"
|
||||
/>
|
||||
|
||||
<div className="quick-date-group" style={{ display: 'flex', marginRight: 12 }}>
|
||||
<Button type={quickDate === '1m' ? 'primary' : 'default'} onClick={() => handleQuickDate('1m')} style={{ borderRadius: '4px 0 0 4px' }}>近一月</Button>
|
||||
<Button type={quickDate === '6m' ? 'primary' : 'default'} onClick={() => handleQuickDate('6m')} style={{ borderRadius: '0', marginLeft: '-1px' }}>近半年</Button>
|
||||
<Button type={quickDate === '1y' ? 'primary' : 'default'} onClick={() => handleQuickDate('1y')} style={{ borderRadius: '0 4px 4px 0', marginLeft: '-1px' }}>近一年</Button>
|
||||
</div>
|
||||
|
||||
<Button className="ant-btn-ghost-blue" type="primary" onClick={() => {}}>查询</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Chart/Table content */}
|
||||
<div className="chart-content">
|
||||
{activeTab === 'seepage' ? (
|
||||
filter.type === 'saturation' ? (
|
||||
<ReactEcharts
|
||||
option={getOption(chartData)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
notMerge={true}
|
||||
lazyUpdate={true}
|
||||
/>
|
||||
) : (
|
||||
<ProcessLineChart />
|
||||
)
|
||||
) : (
|
||||
<DeformationPanel />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SafetyPanel;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
.safety-panel-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
color: #fff;
|
||||
|
||||
.sidebar {
|
||||
width: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
.sidebar-btn {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
border: 1px solid rgba(0, 160, 233, 0.6);
|
||||
background: rgba(0, 70, 120, 0.3);
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 160, 233, 0.3);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(0, 160, 233, 0.8);
|
||||
border-color: #00a0e9;
|
||||
box-shadow: 0 0 10px rgba(0, 160, 233, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content-area {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.filters-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.filter-label {
|
||||
margin-right: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden; // Prevent overflow
|
||||
|
||||
.echarts-for-react {
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,9 @@ import './index.less';
|
|||
import RainMonitor from './RainMonitor';
|
||||
import ReservoirPanel from './ReservoirPanel';
|
||||
import FlowPanel from './FlowPanel';
|
||||
import SafetyPanel from './SafetyPanel';
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const SafetyPanel = () => {
|
||||
return (
|
||||
<div className="awm-empty">内容待接入</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AllWeatherModal = ({ active }) => {
|
||||
if (active === 'rain') return <RainMonitor />;
|
||||
if (active === 'reservoir') return <ReservoirPanel />;
|
||||
|
|
|
|||
Loading…
Reference in New Issue