map-component.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. "use client"
  2. import {useRef} from "react"
  3. import {Circle, MapContainer, Marker, Popup, TileLayer} from "react-leaflet"
  4. import L from "leaflet"
  5. import "leaflet/dist/leaflet.css"
  6. if (typeof window !== "undefined") {
  7. delete (L.Icon.Default.prototype as any)._getIconUrl
  8. L.Icon.Default.mergeOptions({
  9. iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png",
  10. iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png",
  11. shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png",
  12. })
  13. }
  14. interface MapComponentProps {
  15. facilities?: Array<{
  16. id: string
  17. name: string
  18. type: "water_source" | "plant" | "pump" | "pipeline" | "user"
  19. position: [number, number]
  20. status: "normal" | "warning" | "alarm"
  21. data?: any
  22. }>
  23. alarms?: Array<{
  24. id: string
  25. position: [number, number]
  26. level: "high" | "medium" | "low"
  27. message: string
  28. }>
  29. }
  30. const facilityIcons = {
  31. water_source: "🏞️",
  32. plant: "🏭",
  33. pump: "⚡",
  34. pipeline: "🔧",
  35. user: "🏢",
  36. }
  37. const statusColors = {
  38. normal: "#52c41a",
  39. warning: "#faad14",
  40. alarm: "#ff4d4f",
  41. }
  42. const alarmColors = {
  43. high: "#ff4d4f",
  44. medium: "#faad14",
  45. low: "#1890ff",
  46. }
  47. export default function MapComponent({ facilities = [], alarms = [] }: MapComponentProps) {
  48. const mapRef = useRef<L.Map | null>(null)
  49. // 长沙地区的设施数据
  50. const defaultFacilities = [
  51. {
  52. id: "1",
  53. name: "湘江水源地",
  54. type: "water_source" as const,
  55. position: [28.2282, 112.9388] as [number, number],
  56. status: "normal" as const,
  57. },
  58. {
  59. id: "2",
  60. name: "长沙第一水厂",
  61. type: "plant" as const,
  62. position: [28.2382, 112.9488] as [number, number],
  63. status: "normal" as const,
  64. },
  65. {
  66. id: "3",
  67. name: "岳麓区增压泵站",
  68. type: "pump" as const,
  69. position: [28.2482, 112.9588] as [number, number],
  70. status: "warning" as const,
  71. },
  72. {
  73. id: "4",
  74. name: "五一大道主管网",
  75. type: "pipeline" as const,
  76. position: [28.1982, 112.9688] as [number, number],
  77. status: "normal" as const,
  78. },
  79. {
  80. id: "5",
  81. name: "中南大学用水点",
  82. type: "user" as const,
  83. position: [28.1582, 112.9388] as [number, number],
  84. status: "alarm" as const,
  85. },
  86. {
  87. id: "6",
  88. name: "橘子洲水源地",
  89. type: "water_source" as const,
  90. position: [28.2082, 112.9588] as [number, number],
  91. status: "normal" as const,
  92. },
  93. ]
  94. const defaultAlarms = [
  95. {
  96. id: "1",
  97. position: [28.2482, 112.9588] as [number, number],
  98. level: "medium" as const,
  99. message: "岳麓区泵站压力异常",
  100. },
  101. { id: "2", position: [28.1582, 112.9388] as [number, number], level: "high" as const, message: "中南大学流量超限" },
  102. { id: "3", position: [28.1882, 112.9288] as [number, number], level: "low" as const, message: "管网轻微漏损" },
  103. ]
  104. const displayFacilities = facilities.length > 0 ? facilities : defaultFacilities
  105. const displayAlarms = alarms.length > 0 ? alarms : defaultAlarms
  106. return (
  107. <MapContainer center={[28.2282, 112.9388]} zoom={12} className="h-full w-full" ref={mapRef}>
  108. <TileLayer
  109. attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
  110. url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  111. />
  112. {/* 设施标记 */}
  113. {displayFacilities.map((facility) => (
  114. <Marker key={facility.id} position={facility.position}>
  115. <Popup>
  116. <div className="p-3">
  117. <h3 className="font-semibold text-lg mb-2">{facility.name}</h3>
  118. <p className="text-sm text-gray-600 mb-1">
  119. 类型: {facilityIcons[facility.type]}
  120. {facility.type === "water_source" && "水源地"}
  121. {facility.type === "plant" && "水厂"}
  122. {facility.type === "pump" && "泵站"}
  123. {facility.type === "pipeline" && "管网"}
  124. {facility.type === "user" && "大用户"}
  125. </p>
  126. <p className="text-sm">
  127. 状态:{" "}
  128. <span style={{ color: statusColors[facility.status] }} className="font-medium">
  129. {facility.status === "normal" && "正常"}
  130. {facility.status === "warning" && "预警"}
  131. {facility.status === "alarm" && "报警"}
  132. </span>
  133. </p>
  134. </div>
  135. </Popup>
  136. </Marker>
  137. ))}
  138. {/* 报警圆圈 */}
  139. {displayAlarms.map((alarm) => (
  140. <Circle
  141. key={alarm.id}
  142. center={alarm.position}
  143. radius={300}
  144. pathOptions={{
  145. color: alarmColors[alarm.level],
  146. fillColor: alarmColors[alarm.level],
  147. fillOpacity: 0.2,
  148. weight: 2,
  149. }}
  150. >
  151. <Popup>
  152. <div className="p-3">
  153. <h3 className="font-semibold text-red-600 mb-2">🚨 报警信息</h3>
  154. <p className="text-sm mb-1">
  155. 级别:{" "}
  156. <span className="font-medium" style={{ color: alarmColors[alarm.level] }}>
  157. {alarm.level === "high" && "高级"}
  158. {alarm.level === "medium" && "中级"}
  159. {alarm.level === "low" && "低级"}
  160. </span>
  161. </p>
  162. <p className="text-sm">信息: {alarm.message}</p>
  163. </div>
  164. </Popup>
  165. </Circle>
  166. ))}
  167. </MapContainer>
  168. )
  169. }