|
@@ -0,0 +1,297 @@
|
|
|
|
|
+"use client"
|
|
|
|
|
+
|
|
|
|
|
+import {useState} from "react"
|
|
|
|
|
+import {Button, Card, Col, Row, Select, Slider, Space, Switch, Tag} from "antd"
|
|
|
|
|
+import {FullscreenOutlined, ReloadOutlined} from "@ant-design/icons"
|
|
|
|
|
+import dynamic from "next/dynamic"
|
|
|
|
|
+
|
|
|
|
|
+const GasNetworkMap = dynamic(() => import("./gas-network-map"), {
|
|
|
|
|
+ ssr: false,
|
|
|
|
|
+ loading: () => <div className="flex items-center justify-center h-96">加载地图中...</div>,
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const { Option } = Select
|
|
|
|
|
+
|
|
|
|
|
+// 模拟实时监测数据点
|
|
|
|
|
+const mockMonitoringPoints = [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "MP001",
|
|
|
|
|
+ name: "解放路监���点",
|
|
|
|
|
+ position: [39.9042, 116.4074],
|
|
|
|
|
+ type: "pressure",
|
|
|
|
|
+ value: 0.42,
|
|
|
|
|
+ unit: "MPa",
|
|
|
|
|
+ status: "normal",
|
|
|
|
|
+ lastUpdate: "2024-01-15 14:30:25",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "MP002",
|
|
|
|
|
+ name: "人民路监测点",
|
|
|
|
|
+ position: [39.9052, 116.4084],
|
|
|
|
|
+ type: "flow",
|
|
|
|
|
+ value: 2850,
|
|
|
|
|
+ unit: "m³/h",
|
|
|
|
|
+ status: "normal",
|
|
|
|
|
+ lastUpdate: "2024-01-15 14:30:20",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "MP003",
|
|
|
|
|
+ name: "建设路监测点",
|
|
|
|
|
+ position: [39.9032, 116.4064],
|
|
|
|
|
+ type: "temperature",
|
|
|
|
|
+ value: 18.5,
|
|
|
|
|
+ unit: "°C",
|
|
|
|
|
+ status: "normal",
|
|
|
|
|
+ lastUpdate: "2024-01-15 14:30:15",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: "MP004",
|
|
|
|
|
+ name: "泄漏检测点",
|
|
|
|
|
+ position: [39.9062, 116.4094],
|
|
|
|
|
+ type: "leakage",
|
|
|
|
|
+ value: 0.02,
|
|
|
|
|
+ unit: "ppm",
|
|
|
|
|
+ status: "warning",
|
|
|
|
|
+ lastUpdate: "2024-01-15 14:30:30",
|
|
|
|
|
+ },
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+export default function MonitoringVisualization() {
|
|
|
|
|
+ const [layerVisibility, setLayerVisibility] = useState({
|
|
|
|
|
+ pipeline: true,
|
|
|
|
|
+ pressure: true,
|
|
|
|
|
+ flow: true,
|
|
|
|
|
+ temperature: true,
|
|
|
|
|
+ leakage: true,
|
|
|
|
|
+ devices: true,
|
|
|
|
|
+ })
|
|
|
|
|
+ const [selectedTimeRange, setSelectedTimeRange] = useState("realtime")
|
|
|
|
|
+ const [dataRefreshInterval, setDataRefreshInterval] = useState(5)
|
|
|
|
|
+ const [isFullscreen, setIsFullscreen] = useState(false)
|
|
|
|
|
+
|
|
|
|
|
+ const handleLayerToggle = (layer: string, visible: boolean) => {
|
|
|
|
|
+ setLayerVisibility((prev) => ({
|
|
|
|
|
+ ...prev,
|
|
|
|
|
+ [layer]: visible,
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const handleRefresh = () => {
|
|
|
|
|
+ // 刷新数据逻辑
|
|
|
|
|
+ console.log("刷新监测数据")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const getStatusColor = (status: string) => {
|
|
|
|
|
+ switch (status) {
|
|
|
|
|
+ case "normal":
|
|
|
|
|
+ return "#52c41a"
|
|
|
|
|
+ case "warning":
|
|
|
|
|
+ return "#faad14"
|
|
|
|
|
+ case "danger":
|
|
|
|
|
+ return "#f5222d"
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "#1890ff"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const getTypeLabel = (type: string) => {
|
|
|
|
|
+ const typeMap = {
|
|
|
|
|
+ pressure: "压力",
|
|
|
|
|
+ flow: "流量",
|
|
|
|
|
+ temperature: "温度",
|
|
|
|
|
+ leakage: "泄漏",
|
|
|
|
|
+ }
|
|
|
|
|
+ return typeMap[type as keyof typeof typeMap] || type
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="p-6">
|
|
|
|
|
+ <Card title="运行监测可视化" className="mb-4">
|
|
|
|
|
+ {/* 控制面板 */}
|
|
|
|
|
+ <Row gutter={16} className="mb-4">
|
|
|
|
|
+ <Col span={24}>
|
|
|
|
|
+ <Card size="small" title="图层控制">
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.pipeline}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("pipeline", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">管网管线</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.pressure}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("pressure", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">压力监测</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.flow}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("flow", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">流量监测</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.temperature}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("temperature", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">温度监测</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.leakage}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("leakage", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">泄漏检测</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <div className="mb-2">
|
|
|
|
|
+ <Switch
|
|
|
|
|
+ checked={layerVisibility.devices}
|
|
|
|
|
+ onChange={(checked) => handleLayerToggle("devices", checked)}
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ />
|
|
|
|
|
+ <span className="ml-2">监测设备</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ <Row gutter={16} className="mb-4">
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card size="small" title="时间范围">
|
|
|
|
|
+ <Select value={selectedTimeRange} onChange={setSelectedTimeRange} style={{ width: "100%" }}>
|
|
|
|
|
+ <Option value="realtime">实时数据</Option>
|
|
|
|
|
+ <Option value="1hour">近1小时</Option>
|
|
|
|
|
+ <Option value="6hours">近6小时</Option>
|
|
|
|
|
+ <Option value="24hours">近24小时</Option>
|
|
|
|
|
+ <Option value="7days">近7天</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card size="small" title="刷新间隔">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Slider
|
|
|
|
|
+ min={1}
|
|
|
|
|
+ max={60}
|
|
|
|
|
+ value={dataRefreshInterval}
|
|
|
|
|
+ onChange={setDataRefreshInterval}
|
|
|
|
|
+ marks={{ 1: "1s", 30: "30s", 60: "60s" }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <div className="text-center text-sm text-gray-500">{dataRefreshInterval}秒</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card size="small" title="操作控制">
|
|
|
|
|
+ <Space>
|
|
|
|
|
+ <Button icon={<ReloadOutlined />} onClick={handleRefresh} size="small">
|
|
|
|
|
+ 刷新
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button icon={<FullscreenOutlined />} onClick={() => setIsFullscreen(!isFullscreen)} size="small">
|
|
|
|
|
+ 全屏
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card size="small" title="监测状态">
|
|
|
|
|
+ <div className="space-y-1">
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>正常:</span>
|
|
|
|
|
+ <Tag color="green">3</Tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>预警:</span>
|
|
|
|
|
+ <Tag color="orange">1</Tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>异常:</span>
|
|
|
|
|
+ <Tag color="red">0</Tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 地图展示 */}
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={18}>
|
|
|
|
|
+ <Card title="实时监测一张图" size="small" className={isFullscreen ? "fixed inset-0 z-50" : ""}>
|
|
|
|
|
+ <GasNetworkMap height={isFullscreen ? "calc(100vh - 120px)" : "700px"} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card title="监测点详情" size="small">
|
|
|
|
|
+ <div className="space-y-3 max-h-96 overflow-y-auto">
|
|
|
|
|
+ {mockMonitoringPoints.map((point) => (
|
|
|
|
|
+ <div key={point.id} className="p-3 border rounded-lg">
|
|
|
|
|
+ <div className="flex justify-between items-center mb-2">
|
|
|
|
|
+ <span className="font-medium">{point.name}</span>
|
|
|
|
|
+ <Tag color={getStatusColor(point.status)}>
|
|
|
|
|
+ {point.status === "normal" ? "正常" : point.status === "warning" ? "预警" : "异常"}
|
|
|
|
|
+ </Tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="text-sm text-gray-600 space-y-1">
|
|
|
|
|
+ <div>类型: {getTypeLabel(point.type)}</div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ 当前值: {point.value} {point.unit}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>更新时间: {point.lastUpdate}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+
|
|
|
|
|
+ <Card title="实时统计" size="small" className="mt-4">
|
|
|
|
|
+ <div className="space-y-3">
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>监测点总数:</span>
|
|
|
|
|
+ <span className="font-bold text-blue-600">342</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>在线设备:</span>
|
|
|
|
|
+ <span className="font-bold text-green-600">298</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>离线设备:</span>
|
|
|
|
|
+ <span className="font-bold text-red-600">12</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>维护中:</span>
|
|
|
|
|
+ <span className="font-bold text-orange-600">32</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="flex justify-between">
|
|
|
|
|
+ <span>数据更新率:</span>
|
|
|
|
|
+ <span className="font-bold text-blue-600">98.5%</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|