"use client";
import React, { useEffect, useMemo, useRef } from "react";
import ReactECharts from "echarts-for-react";
import {
Box,
Button,
Dialog,
GlobalStyles,
IconButton,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
} from "@mui/material";
import {
Close,
PrintOutlined,
WaterDropOutlined,
} from "@mui/icons-material";
import { PredictionResult } from "./types";
import { buildHealthRiskReportData } from "./healthRiskReport";
interface HealthRiskReportProps {
open: boolean;
onClose: () => void;
predictionResults: PredictionResult[];
currentYear: number;
analysisQueryTime: string | null;
networkName: string;
generatedAt: Date;
}
const REPORT_BLUE = "#0b4f87";
const REPORT_INK = "#172435";
const REPORT_MUTED = "#5f6f80";
const REPORT_LINE = "#d8e0e8";
const reportFontFamily =
'-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif';
const formatDateTime = (value: Date | string | null) => {
if (!value) return "未记录";
const date = value instanceof Date ? value : new Date(value);
if (Number.isNaN(date.getTime())) return "未记录";
return new Intl.DateTimeFormat("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
}).format(date);
};
const formatPercent = (value: number | null, fractionDigits = 1) =>
value === null ? "无有效数据" : `${(value * 100).toFixed(fractionDigits)}%`;
const SectionTitle = ({ children }: { children: React.ReactNode }) => (
{children}
);
const HealthRiskReport: React.FC = ({
open,
onClose,
predictionResults,
currentYear,
analysisQueryTime,
networkName,
generatedAt,
}) => {
const printStateRef = useRef<{ title: string } | null>(null);
const reportData = useMemo(
() => buildHealthRiskReportData(predictionResults, currentYear),
[currentYear, predictionResults],
);
useEffect(
() => () => {
if (printStateRef.current) {
document.title = printStateRef.current.title;
document.body.classList.remove("health-risk-report-printing");
}
},
[],
);
const restoreAfterPrint = () => {
if (!printStateRef.current) return;
document.title = printStateRef.current.title;
printStateRef.current = null;
document.body.classList.remove("health-risk-report-printing");
};
const handlePrint = () => {
if (printStateRef.current) return;
printStateRef.current = { title: document.title };
document.title = `管网健康风险体检报告-${networkName || "未命名管网"}-${new Intl.DateTimeFormat(
"zh-CN",
{ year: "numeric", month: "2-digit", day: "2-digit" },
)
.format(generatedAt)
.replaceAll("/", "-")}`;
document.body.classList.add("health-risk-report-printing");
window.addEventListener("afterprint", restoreAfterPrint, { once: true });
window.print();
};
const distributionOption = {
animation: false,
grid: { top: 4, right: 38, bottom: 28, left: 148 },
xAxis: {
type: "value",
name: "管段数",
minInterval: 1,
axisLine: { lineStyle: { color: "#8b99a8" } },
axisLabel: { color: REPORT_MUTED, fontSize: 10 },
splitLine: { lineStyle: { color: "#e7edf2" } },
nameTextStyle: { color: REPORT_MUTED, fontSize: 10 },
},
yAxis: {
type: "category",
inverse: true,
data: reportData.distribution.map((item) => item.label),
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { color: REPORT_INK, fontSize: 10 },
},
tooltip: { show: false },
series: [
{
type: "bar",
barMaxWidth: 14,
data: reportData.distribution.map((item) => ({
value: item.count,
itemStyle: { color: item.color },
})),
label: {
show: true,
position: "right",
color: REPORT_INK,
fontSize: 10,
},
},
],
};
const trendOption = {
animation: false,
grid: { top: 24, right: 24, bottom: 38, left: 48 },
xAxis: {
type: "category",
name: "预测年份",
boundaryGap: false,
data: reportData.trend.map((item) => item.year),
axisLine: { lineStyle: { color: "#8b99a8" } },
axisLabel: {
color: REPORT_MUTED,
fontSize: 10,
interval: Math.max(0, Math.ceil(reportData.trend.length / 9) - 1),
},
nameTextStyle: { color: REPORT_MUTED, fontSize: 10 },
},
yAxis: {
type: "value",
name: "较高及以上风险管段数",
minInterval: 1,
axisLine: { show: false },
axisLabel: { color: REPORT_MUTED, fontSize: 10 },
splitLine: { lineStyle: { color: "#e7edf2" } },
nameTextStyle: { color: REPORT_MUTED, fontSize: 10 },
},
tooltip: { show: false },
series: [
{
type: "line",
smooth: true,
symbol: "none",
lineStyle: { color: "#c83d3d", width: 2.5 },
areaStyle: { color: "rgba(200, 61, 61, 0.08)" },
data: reportData.trend.map((item) => item.count),
},
],
};
const metricItems = [
{ label: "分析管段", value: reportData.totalCount.toLocaleString("zh-CN") },
{ label: "有效结果", value: reportData.validCount.toLocaleString("zh-CN") },
{
label: "平均生存概率",
value: formatPercent(reportData.averageProbability),
},
{
label: "较高及以上风险",
value: `${reportData.highRiskCount.toLocaleString("zh-CN")} 条`,
detail: formatPercent(reportData.highRiskRatio),
danger: reportData.highRiskCount > 0,
},
];
return (
<>
*:not(.health-risk-report-dialog)":
{
display: "none !important",
},
".health-risk-report-dialog": {
position: "static !important",
display: "block !important",
},
".health-risk-report-dialog .MuiBackdrop-root, .health-risk-report-actions":
{
display: "none !important",
},
".health-risk-report-dialog .MuiDialog-container": {
display: "block !important",
height: "auto !important",
},
".health-risk-report-dialog .MuiDialog-paper": {
width: "210mm !important",
maxWidth: "none !important",
minHeight: "297mm !important",
maxHeight: "none !important",
margin: "0 !important",
overflow: "visible !important",
boxShadow: "none !important",
},
".health-risk-report-preview": {
padding: "0 !important",
overflow: "visible !important",
backgroundColor: "#fff !important",
},
"#health-risk-print-root": {
width: "210mm !important",
minHeight: "297mm !important",
padding: "12mm !important",
boxShadow: "none !important",
printColorAdjust: "exact",
WebkitPrintColorAdjust: "exact",
},
".health-risk-report-section, .health-risk-report-metrics": {
breakInside: "avoid",
pageBreakInside: "avoid",
},
".health-risk-report-table-row": {
breakInside: "avoid",
pageBreakInside: "avoid",
},
},
}}
/>
>
);
};
export default HealthRiskReport;