Bläddra i källkod

feat(test11, test10): 新增地图视图组件- 在 test11 目录下添加 MapView 组件,用于展示设备位置和状态
- 在 test10 目录下添加 MapView 组件,用于展示不同类型的地图信息
- 两个组件都使用了 react-leaflet 库来实现地图功能
- 自定义了图标和弹窗样式,以适应不同的业务需求

nahida 9 månader sedan
förälder
incheckning
8ee4863d8b
2 ändrade filer med 259 tillägg och 0 borttagningar
  1. 152 0
      app/(other)/test10/components/MapView.tsx
  2. 107 0
      app/(other)/test11/components/map-view.tsx

+ 152 - 0
app/(other)/test10/components/MapView.tsx

@@ -0,0 +1,152 @@
+"use client"
+
+import {useRef} from "react"
+import {Circle, LayersControl, MapContainer, Marker, Popup, TileLayer} from "react-leaflet"
+import L from "leaflet"
+import "leaflet/dist/leaflet.css"
+
+// 修复默认图标问题
+delete (L.Icon.Default.prototype as any)._getIconUrl
+L.Icon.Default.mergeOptions({
+  iconRetinaUrl: "/placeholder.svg?height=25&width=25",
+  iconUrl: "/placeholder.svg?height=25&width=25",
+  shadowUrl: "/placeholder.svg?height=25&width=25",
+})
+
+interface MapViewProps {
+  type?: "overview" | "waterlogging" | "pipeline" | "pump"
+}
+
+export default function MapView({ type = "overview" }: MapViewProps) {
+  const mapRef = useRef<L.Map>(null)
+
+  // 模拟数据
+  const waterloggingPoints = [
+    { id: 1, lat: 31.2304, lng: 121.4737, level: 0.8, threshold: 0.5, name: "人民路积水点" },
+    { id: 2, lat: 31.2404, lng: 121.4837, level: 0.3, threshold: 0.5, name: "南京路积水点" },
+    { id: 3, lat: 31.2204, lng: 121.4637, level: 0.6, threshold: 0.5, name: "淮海路积水点" },
+  ]
+
+  const pipelinePoints = [
+    { id: 1, lat: 31.2304, lng: 121.4737, flow: 2.5, capacity: 3.0, name: "主干道管网" },
+    { id: 2, lat: 31.2404, lng: 121.4837, flow: 1.8, capacity: 2.5, name: "支线管网" },
+  ]
+
+  const pumpStations = [
+    { id: 1, lat: 31.2504, lng: 121.4937, status: "running", name: "第一泵站" },
+    { id: 2, lat: 31.2104, lng: 121.4537, status: "stopped", name: "第二泵站" },
+  ]
+
+  const getMarkerColor = (type: string, data: any) => {
+    switch (type) {
+      case "waterlogging":
+        return data.level > data.threshold ? "red" : data.level > data.threshold * 0.8 ? "orange" : "blue"
+      case "pipeline":
+        return data.flow > data.capacity * 0.9 ? "red" : data.flow > data.capacity * 0.7 ? "orange" : "green"
+      case "pump":
+        return data.status === "running" ? "green" : data.status === "stopped" ? "gray" : "red"
+      default:
+        return "blue"
+    }
+  }
+
+  return (
+    <div className="h-full w-full">
+      <MapContainer center={[31.2304, 121.4737]} zoom={13} className="h-full w-full" ref={mapRef}>
+        <TileLayer
+          attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
+        />
+
+        <LayersControl position="topright">
+          {(type === "overview" || type === "waterlogging") && (
+            <LayersControl.Overlay checked name="易积水点">
+              <>
+                {waterloggingPoints.map((point) => (
+                  <Marker key={point.id} position={[point.lat, point.lng]}>
+                    <Popup>
+                      <div>
+                        <h4>{point.name}</h4>
+                        <p>当前液位: {point.level}m</p>
+                        <p>报警阈值: {point.threshold}m</p>
+                        <p>状态: {point.level > point.threshold ? "报警" : "正常"}</p>
+                      </div>
+                    </Popup>
+                  </Marker>
+                ))}
+                {waterloggingPoints.map((point) => (
+                  <Circle
+                    key={`circle-${point.id}`}
+                    center={[point.lat, point.lng]}
+                    radius={200}
+                    color={getMarkerColor("waterlogging", point)}
+                    fillOpacity={0.3}
+                  />
+                ))}
+              </>
+            </LayersControl.Overlay>
+          )}
+
+          {(type === "overview" || type === "pipeline") && (
+            <LayersControl.Overlay checked name="管网监测点">
+              <>
+                {pipelinePoints.map((point) => (
+                  <Marker key={point.id} position={[point.lat, point.lng]}>
+                    <Popup>
+                      <div>
+                        <h4 className="font-bold">{point.name}</h4>
+                        <p>当前流量: {point.flow}m³/s</p>
+                        <p>设计流量: {point.capacity}m³/s</p>
+                        <p>负荷率: {((point.flow / point.capacity) * 100).toFixed(1)}%</p>
+                        <p>状态:
+                          <span className={
+                            point.flow > point.capacity * 0.9
+                              ? "text-red-600 font-bold"
+                              : point.flow > point.capacity * 0.7
+                                ? "text-orange-600 font-bold"
+                                : "text-green-600"
+                          }>
+                            {point.flow > point.capacity * 0.9
+                              ? " 高负荷"
+                              : point.flow > point.capacity * 0.7
+                                ? " 中负荷"
+                                : " 正常"}
+                          </span>
+                        </p>
+                      </div>
+                    </Popup>
+                  </Marker>
+                ))}
+              </>
+            </LayersControl.Overlay>
+          )}
+
+          {(type === "overview" || type === "pump") && (
+            <LayersControl.Overlay checked name="泵站">
+              <>
+                {pumpStations.map((station) => (
+                  <Marker key={`marker-${station.id}`} position={[station.lat, station.lng]}>
+                    <Popup>
+                      <div>
+                        <h4 className="font-bold">{station.name}</h4>
+                        <p>运行状态:
+                          <span className={
+                            station.status === "running"
+                              ? "text-green-600 font-bold"
+                              : "text-gray-600"
+                          }>
+                            {station.status === "running" ? " 运行中" : " 停止"}
+                          </span>
+                        </p>
+                      </div>
+                    </Popup>
+                  </Marker>
+                ))}
+              </>
+            </LayersControl.Overlay>
+          )}
+        </LayersControl>
+      </MapContainer>
+    </div>
+  )
+}

+ 107 - 0
app/(other)/test11/components/map-view.tsx

@@ -0,0 +1,107 @@
+"use client"
+import {MapContainer, Marker, Popup, TileLayer} from "react-leaflet"
+import {Icon} from "leaflet"
+import "leaflet/dist/leaflet.css"
+
+// 修复 Leaflet 默认图标问题
+const createCustomIcon = (status: string) => {
+  const colors = {
+    normal: "#52c41a",
+    warning: "#faad14",
+    error: "#f5222d",
+    maintenance: "#722ed1",
+  }
+
+  return new Icon({
+    iconUrl: `data:image/svg+xml;base64,${btoa(`
+      <svg width="25" height="25" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
+        <circle cx="12.5" cy="12.5" r="10" fill="${colors[status as keyof typeof colors]}" stroke="white" strokeWidth="2"/>
+        <circle cx="12.5" cy="12.5" r="4" fill="white"/>
+      </svg>
+    `)}`,
+    iconSize: [25, 25],
+    iconAnchor: [12.5, 12.5],
+    popupAnchor: [0, -12.5],
+  })
+}
+
+interface Device {
+  id: number
+  name: string
+  type: string
+  status: string
+  location: string
+  lat: number
+  lng: number
+  battery: number
+  signal: number
+}
+
+interface MapViewProps {
+  devices: Device[]
+  showRealTimeData?: boolean
+}
+
+export default function MapView({ devices, showRealTimeData = false }: MapViewProps) {
+  const center: [number, number] = [39.9042, 116.4074] // 北京中心
+
+  return (
+    <MapContainer center={center} zoom={11} style={{ height: "100%", width: "100%" }} className="rounded-lg">
+      <TileLayer
+        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
+      />
+
+      {devices.map((device) => (
+        <Marker key={device.id} position={[device.lat, device.lng]} icon={createCustomIcon(device.status)}>
+          <Popup>
+            <div className="space-y-2">
+              <h3 className="font-bold">{device.name}</h3>
+              <p>
+                <strong>位置:</strong> {device.location}
+              </p>
+              <p>
+                <strong>状态:</strong>
+                <span
+                  className={`ml-1 ${
+                    device.status === "normal"
+                      ? "text-green-600"
+                      : device.status === "warning"
+                        ? "text-yellow-600"
+                        : device.status === "error"
+                          ? "text-red-600"
+                          : "text-purple-600"
+                  }`}
+                >
+                  {device.status === "normal"
+                    ? "正常"
+                    : device.status === "warning"
+                      ? "告警"
+                      : device.status === "error"
+                        ? "故障"
+                        : "维修中"}
+                </span>
+              </p>
+              <p>
+                <strong>电池:</strong> {device.battery}%
+              </p>
+              <p>
+                <strong>信号:</strong> {device.signal}%
+              </p>
+              {showRealTimeData && (
+                <div className="mt-2 p-2 bg-gray-100 rounded">
+                  <p className="text-sm">
+                    <strong>实时数据:</strong>
+                  </p>
+                  <p className="text-xs">倾斜角度: 2.3°</p>
+                  <p className="text-xs">位移距离: 0.8mm</p>
+                  <p className="text-xs">震动强度: 18dB</p>
+                </div>
+              )}
+            </div>
+          </Popup>
+        </Marker>
+      ))}
+    </MapContainer>
+  )
+}