"use client"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { Box, Button, IconButton, Stack, TextField, Typography, } from "@mui/material"; import { Close as CloseIcon } from "@mui/icons-material"; import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; import { zhCN as pickerZhCN } from "@mui/x-date-pickers/locales"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import "dayjs/locale/zh-cn"; import dayjs, { Dayjs } from "dayjs"; import { useNotification } from "@refinedev/core"; import axios from "axios"; import { config, NETWORK_NAME } from "@/config/config"; import { useMap } from "@app/OlMap/MapComponent"; import VectorLayer from "ol/layer/Vector"; import VectorSource from "ol/source/Vector"; import { Style, Stroke, Fill, Circle as CircleStyle } from "ol/style"; import Feature from "ol/Feature"; import { handleMapClickSelectFeatures as mapClickSelectFeatures } from "@/utils/mapQueryService"; const AnalysisParameters: React.FC = () => { const map = useMap(); const { open } = useNotification(); const network = NETWORK_NAME; const [startTime, setStartTime] = useState(dayjs(new Date())); const [sourceNode, setSourceNode] = useState(""); const [concentration, setConcentration] = useState(1); const [duration, setDuration] = useState(900); const [pattern, setPattern] = useState(""); const [isSelecting, setIsSelecting] = useState(false); const [submitting, setSubmitting] = useState(false); const [highlightLayer, setHighlightLayer] = useState | null>(null); const [highlightFeature, setHighlightFeature] = useState( null ); const isFormValid = useMemo(() => { return ( Boolean(network) && Boolean(startTime) && Boolean(sourceNode) && concentration > 0 && duration > 0 ); }, [network, startTime, sourceNode, concentration, duration]); useEffect(() => { if (!map) return; const sourceStyle = new Style({ image: new CircleStyle({ radius: 10, fill: new Fill({ color: "rgba(37, 125, 212, 0.35)" }), stroke: new Stroke({ color: "rgba(37, 125, 212, 1)", width: 3 }), }), }); const layer = new VectorLayer({ source: new VectorSource(), style: sourceStyle, properties: { name: "污染源节点", value: "contaminant_source_highlight", }, }); map.addLayer(layer); setHighlightLayer(layer); return () => { map.removeLayer(layer); map.un("click", handleMapClickSelectFeatures); }; }, [map]); useEffect(() => { if (!highlightLayer) return; const source = highlightLayer.getSource(); if (!source) return; source.clear(); if (highlightFeature) { source.addFeature(highlightFeature); } }, [highlightFeature, highlightLayer]); const handleMapClickSelectFeatures = useCallback( async (event: { coordinate: number[] }) => { if (!map) return; const feature = await mapClickSelectFeatures(event, map); if (!feature) return; const layerId = feature.getId()?.toString().split(".")[0] || ""; const isJunction = layerId.includes("junction"); if (!isJunction) { open?.({ type: "error", message: "请选择节点类型要素作为污染源。", }); return; } const id = feature.getProperties().id; if (!id) return; setSourceNode(id); setHighlightFeature(feature); setIsSelecting(false); map.un("click", handleMapClickSelectFeatures); }, [map, open] ); const handleStartSelection = () => { if (!map) return; setIsSelecting(true); map.on("click", handleMapClickSelectFeatures); }; const handleEndSelection = () => { if (!map) return; setIsSelecting(false); map.un("click", handleMapClickSelectFeatures); }; const handleClearSource = () => { setSourceNode(""); setHighlightFeature(null); }; const handleAnalyze = async () => { if (!startTime) return; setSubmitting(true); open?.({ key: "contaminant-analysis", type: "progress", message: "方案提交分析中", undoableTimeout: 3, }); try { const params = { network, start_time: startTime.toISOString(), source: sourceNode, concentration, duration, pattern: pattern || undefined, }; await axios.get(`${config.BACKEND_URL}/api/v1/contaminant_simulation/`, { params, }); open?.({ key: "contaminant-analysis", type: "success", message: "方案分析成功", description: "水质模拟完成,请在方案查询中查看结果。", }); } catch (error) { console.error("水质模拟请求失败:", error); open?.({ key: "contaminant-analysis", type: "error", message: "提交分析失败", description: error instanceof Error ? error.message : "请检查网络连接或稍后重试", }); } finally { setSubmitting(false); } }; return ( 选择污染源节点 {!isSelecting ? ( ) : ( )} {isSelecting && ( 💡 点击地图上的节点作为污染源 )} {sourceNode ? ( {sourceNode} 污染源节点 ) : ( 暂未选择污染源节点 )} 选择开始时间 value && dayjs.isDayjs(value) && setStartTime(value) } format="YYYY-MM-DD HH:mm" slotProps={{ textField: { size: "small", fullWidth: true, }, }} localeText={ pickerZhCN.components.MuiLocalizationProvider.defaultProps .localeText } /> 管网名称 污染源浓度 (mg/L) setConcentration(parseFloat(e.target.value) || 0)} placeholder="输入浓度" /> 持续时长 (秒) setDuration(parseInt(e.target.value, 10) || 0)} placeholder="输入持续时长" /> 时间模式 setPattern(e.target.value)} placeholder="可选,输入 pattern 名称" /> ); }; export default AnalysisParameters;