|
|
@@ -0,0 +1,182 @@
|
|
|
+"use client"
|
|
|
+
|
|
|
+import {useRef} from "react"
|
|
|
+import {Circle, MapContainer, Marker, Popup, TileLayer} from "react-leaflet"
|
|
|
+import L from "leaflet"
|
|
|
+import "leaflet/dist/leaflet.css"
|
|
|
+
|
|
|
+if (typeof window !== "undefined") {
|
|
|
+ delete (L.Icon.Default.prototype as any)._getIconUrl
|
|
|
+ L.Icon.Default.mergeOptions({
|
|
|
+ iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png",
|
|
|
+ iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png",
|
|
|
+ shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png",
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+interface MapComponentProps {
|
|
|
+ facilities?: Array<{
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+ type: "water_source" | "plant" | "pump" | "pipeline" | "user"
|
|
|
+ position: [number, number]
|
|
|
+ status: "normal" | "warning" | "alarm"
|
|
|
+ data?: any
|
|
|
+ }>
|
|
|
+ alarms?: Array<{
|
|
|
+ id: string
|
|
|
+ position: [number, number]
|
|
|
+ level: "high" | "medium" | "low"
|
|
|
+ message: string
|
|
|
+ }>
|
|
|
+}
|
|
|
+
|
|
|
+const facilityIcons = {
|
|
|
+ water_source: "🏞️",
|
|
|
+ plant: "🏭",
|
|
|
+ pump: "⚡",
|
|
|
+ pipeline: "🔧",
|
|
|
+ user: "🏢",
|
|
|
+}
|
|
|
+
|
|
|
+const statusColors = {
|
|
|
+ normal: "#52c41a",
|
|
|
+ warning: "#faad14",
|
|
|
+ alarm: "#ff4d4f",
|
|
|
+}
|
|
|
+
|
|
|
+const alarmColors = {
|
|
|
+ high: "#ff4d4f",
|
|
|
+ medium: "#faad14",
|
|
|
+ low: "#1890ff",
|
|
|
+}
|
|
|
+
|
|
|
+export default function MapComponent({ facilities = [], alarms = [] }: MapComponentProps) {
|
|
|
+ const mapRef = useRef<L.Map | null>(null)
|
|
|
+
|
|
|
+ // 长沙地区的设施数据
|
|
|
+ const defaultFacilities = [
|
|
|
+ {
|
|
|
+ id: "1",
|
|
|
+ name: "湘江水源地",
|
|
|
+ type: "water_source" as const,
|
|
|
+ position: [28.2282, 112.9388] as [number, number],
|
|
|
+ status: "normal" as const,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "2",
|
|
|
+ name: "长沙第一水厂",
|
|
|
+ type: "plant" as const,
|
|
|
+ position: [28.2382, 112.9488] as [number, number],
|
|
|
+ status: "normal" as const,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "3",
|
|
|
+ name: "岳麓区增压泵站",
|
|
|
+ type: "pump" as const,
|
|
|
+ position: [28.2482, 112.9588] as [number, number],
|
|
|
+ status: "warning" as const,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "4",
|
|
|
+ name: "五一大道主管网",
|
|
|
+ type: "pipeline" as const,
|
|
|
+ position: [28.1982, 112.9688] as [number, number],
|
|
|
+ status: "normal" as const,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "5",
|
|
|
+ name: "中南大学用水点",
|
|
|
+ type: "user" as const,
|
|
|
+ position: [28.1582, 112.9388] as [number, number],
|
|
|
+ status: "alarm" as const,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "6",
|
|
|
+ name: "橘子洲水源地",
|
|
|
+ type: "water_source" as const,
|
|
|
+ position: [28.2082, 112.9588] as [number, number],
|
|
|
+ status: "normal" as const,
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const defaultAlarms = [
|
|
|
+ {
|
|
|
+ id: "1",
|
|
|
+ position: [28.2482, 112.9588] as [number, number],
|
|
|
+ level: "medium" as const,
|
|
|
+ message: "岳麓区泵站压力异常",
|
|
|
+ },
|
|
|
+ { id: "2", position: [28.1582, 112.9388] as [number, number], level: "high" as const, message: "中南大学流量超限" },
|
|
|
+ { id: "3", position: [28.1882, 112.9288] as [number, number], level: "low" as const, message: "管网轻微漏损" },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const displayFacilities = facilities.length > 0 ? facilities : defaultFacilities
|
|
|
+ const displayAlarms = alarms.length > 0 ? alarms : defaultAlarms
|
|
|
+
|
|
|
+ return (
|
|
|
+ <MapContainer center={[28.2282, 112.9388]} zoom={12} className="h-full w-full" ref={mapRef}>
|
|
|
+ <TileLayer
|
|
|
+ attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
|
+ url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* 设施标记 */}
|
|
|
+ {displayFacilities.map((facility) => (
|
|
|
+ <Marker key={facility.id} position={facility.position}>
|
|
|
+ <Popup>
|
|
|
+ <div className="p-3">
|
|
|
+ <h3 className="font-semibold text-lg mb-2">{facility.name}</h3>
|
|
|
+ <p className="text-sm text-gray-600 mb-1">
|
|
|
+ 类型: {facilityIcons[facility.type]}
|
|
|
+ {facility.type === "water_source" && "水源地"}
|
|
|
+ {facility.type === "plant" && "水厂"}
|
|
|
+ {facility.type === "pump" && "泵站"}
|
|
|
+ {facility.type === "pipeline" && "管网"}
|
|
|
+ {facility.type === "user" && "大用户"}
|
|
|
+ </p>
|
|
|
+ <p className="text-sm">
|
|
|
+ 状态:{" "}
|
|
|
+ <span style={{ color: statusColors[facility.status] }} className="font-medium">
|
|
|
+ {facility.status === "normal" && "正常"}
|
|
|
+ {facility.status === "warning" && "预警"}
|
|
|
+ {facility.status === "alarm" && "报警"}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+ </Marker>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {/* 报警圆圈 */}
|
|
|
+ {displayAlarms.map((alarm) => (
|
|
|
+ <Circle
|
|
|
+ key={alarm.id}
|
|
|
+ center={alarm.position}
|
|
|
+ radius={300}
|
|
|
+ pathOptions={{
|
|
|
+ color: alarmColors[alarm.level],
|
|
|
+ fillColor: alarmColors[alarm.level],
|
|
|
+ fillOpacity: 0.2,
|
|
|
+ weight: 2,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Popup>
|
|
|
+ <div className="p-3">
|
|
|
+ <h3 className="font-semibold text-red-600 mb-2">🚨 报警信息</h3>
|
|
|
+ <p className="text-sm mb-1">
|
|
|
+ 级别:{" "}
|
|
|
+ <span className="font-medium" style={{ color: alarmColors[alarm.level] }}>
|
|
|
+ {alarm.level === "high" && "高级"}
|
|
|
+ {alarm.level === "medium" && "中级"}
|
|
|
+ {alarm.level === "low" && "低级"}
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p className="text-sm">信息: {alarm.message}</p>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+ </Circle>
|
|
|
+ ))}
|
|
|
+ </MapContainer>
|
|
|
+ )
|
|
|
+}
|