diff --git a/src/components/ant_override.less b/src/components/ant_override.less index e68048b..98437ec 100644 --- a/src/components/ant_override.less +++ b/src/components/ant_override.less @@ -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 { diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/DeformationPanel.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/DeformationPanel.js new file mode 100644 index 0000000..344f1ea --- /dev/null +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/DeformationPanel.js @@ -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 ( + + {/* Left Chart */} + + + + + {/* Right Table */} + +
+ {mainType === 'horizontal' && ( + setSubType(e.target.value)} + buttonStyle="solid" + size="small" + style={{ marginRight: 12 }} + > + 上下游 + 左右岸 + + )} + + { + setMainType(e.target.value); + if (e.target.value === 'horizontal') { + setSubType('upDown'); // Reset subType when switching back + } + }} + buttonStyle="solid" + size="small" + > + 水平位移 + 垂直位移 + +
+ + + + + ); +}; + +export default DeformationPanel; diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/ProcessLineChart.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/ProcessLineChart.js new file mode 100644 index 0000000..c4a07b0 --- /dev/null +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/ProcessLineChart.js @@ -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 ( + + + {summary.max.date} + {summary.max.reservoirLevel} + {summary.max.rainfall} + {pipeNames.map((name, i) => {summary.max[name]})} + + + 日期 + {summary.max.reservoirLevel_date} + {summary.max.rainfall_date} + {pipeNames.map((name, i) => {summary.max[`${name}_date`]})} + + + {summary.min.date} + {summary.min.reservoirLevel} + {summary.min.rainfall} + {pipeNames.map((name, i) => {summary.min[name]})} + + + 日期 + {summary.min.reservoirLevel_date} + {summary.min.rainfall_date} + {pipeNames.map((name, i) => {summary.min[`${name}_date`]})} + + + {summary.range.date} + {summary.range.reservoirLevel} + {summary.range.rainfall} + {pipeNames.map((name, i) => {summary.range[name]})} + + + ); + }; + + return ( + + + + + +
+ + + ); +}; + +export default ProcessLineChart; diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/chartOption.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/chartOption.js new file mode 100644 index 0000000..b116dac --- /dev/null +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/chartOption.js @@ -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 + }, + ] + }; +}; diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.js new file mode 100644 index 0000000..bee5b54 --- /dev/null +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.js @@ -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 ( +
+ {/* Sidebar for navigation */} +
+
handleTabChange('seepage')} + > + 渗压监测 +
+
handleTabChange('deformation')} + > + 变形监测 +
+
+ + {/* Main content area */} +
+ {/* Filters bar */} +
+ {activeTab === 'seepage' ? ( + <> + + + 断面: + + + 时间: + setFilter({...filter, date: v})} + style={{ width: 260, marginRight: 12 }} + dropdownClassName="dark-picker-dropdown" + /> + + + + ) : ( + <> + + + { + setFilter({...filter, date: v}); + setQuickDate(''); + }} + style={{ width: 260, marginRight: 12 }} + dropdownClassName="dark-picker-dropdown" + /> + +
+ + + +
+ + + + )} +
+ + {/* Chart/Table content */} +
+ {activeTab === 'seepage' ? ( + filter.type === 'saturation' ? ( + + ) : ( + + ) + ) : ( + + )} +
+
+
+ ); +}; + +export default SafetyPanel; diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.less b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.less new file mode 100644 index 0000000..edc8375 --- /dev/null +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/SafetyPanel/index.less @@ -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; + } + } + } +} diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js index 7d137ef..b08bd18 100644 --- a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js +++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js @@ -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 ( -
内容待接入
- ); -}; - const AllWeatherModal = ({ active }) => { if (active === 'rain') return ; if (active === 'reservoir') return ;