monitoring-visualization.tsx 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. "use client"
  2. import {useState} from "react"
  3. import {Button, Card, Col, Row, Select, Slider, Space, Switch, Tag} from "antd"
  4. import {FullscreenOutlined, ReloadOutlined} from "@ant-design/icons"
  5. import dynamic from "next/dynamic"
  6. const GasNetworkMap = dynamic(() => import("./gas-network-map"), {
  7. ssr: false,
  8. loading: () => <div className="flex items-center justify-center h-96">加载地图中...</div>,
  9. })
  10. const { Option } = Select
  11. // 模拟实时监测数据点
  12. const mockMonitoringPoints = [
  13. {
  14. id: "MP001",
  15. name: "解放路监���点",
  16. position: [39.9042, 116.4074],
  17. type: "pressure",
  18. value: 0.42,
  19. unit: "MPa",
  20. status: "normal",
  21. lastUpdate: "2024-01-15 14:30:25",
  22. },
  23. {
  24. id: "MP002",
  25. name: "人民路监测点",
  26. position: [39.9052, 116.4084],
  27. type: "flow",
  28. value: 2850,
  29. unit: "m³/h",
  30. status: "normal",
  31. lastUpdate: "2024-01-15 14:30:20",
  32. },
  33. {
  34. id: "MP003",
  35. name: "建设路监测点",
  36. position: [39.9032, 116.4064],
  37. type: "temperature",
  38. value: 18.5,
  39. unit: "°C",
  40. status: "normal",
  41. lastUpdate: "2024-01-15 14:30:15",
  42. },
  43. {
  44. id: "MP004",
  45. name: "泄漏检测点",
  46. position: [39.9062, 116.4094],
  47. type: "leakage",
  48. value: 0.02,
  49. unit: "ppm",
  50. status: "warning",
  51. lastUpdate: "2024-01-15 14:30:30",
  52. },
  53. ]
  54. export default function MonitoringVisualization() {
  55. const [layerVisibility, setLayerVisibility] = useState({
  56. pipeline: true,
  57. pressure: true,
  58. flow: true,
  59. temperature: true,
  60. leakage: true,
  61. devices: true,
  62. })
  63. const [selectedTimeRange, setSelectedTimeRange] = useState("realtime")
  64. const [dataRefreshInterval, setDataRefreshInterval] = useState(5)
  65. const [isFullscreen, setIsFullscreen] = useState(false)
  66. const handleLayerToggle = (layer: string, visible: boolean) => {
  67. setLayerVisibility((prev) => ({
  68. ...prev,
  69. [layer]: visible,
  70. }))
  71. }
  72. const handleRefresh = () => {
  73. // 刷新数据逻辑
  74. console.log("刷新监测数据")
  75. }
  76. const getStatusColor = (status: string) => {
  77. switch (status) {
  78. case "normal":
  79. return "#52c41a"
  80. case "warning":
  81. return "#faad14"
  82. case "danger":
  83. return "#f5222d"
  84. default:
  85. return "#1890ff"
  86. }
  87. }
  88. const getTypeLabel = (type: string) => {
  89. const typeMap = {
  90. pressure: "压力",
  91. flow: "流量",
  92. temperature: "温度",
  93. leakage: "泄漏",
  94. }
  95. return typeMap[type as keyof typeof typeMap] || type
  96. }
  97. return (
  98. <div className="p-6">
  99. <Card title="运行监测可视化" className="mb-4">
  100. {/* 控制面板 */}
  101. <Row gutter={16} className="mb-4">
  102. <Col span={24}>
  103. <Card size="small" title="图层控制">
  104. <Row gutter={16}>
  105. <Col span={4}>
  106. <div className="mb-2">
  107. <Switch
  108. checked={layerVisibility.pipeline}
  109. onChange={(checked) => handleLayerToggle("pipeline", checked)}
  110. size="small"
  111. />
  112. <span className="ml-2">管网管线</span>
  113. </div>
  114. </Col>
  115. <Col span={4}>
  116. <div className="mb-2">
  117. <Switch
  118. checked={layerVisibility.pressure}
  119. onChange={(checked) => handleLayerToggle("pressure", checked)}
  120. size="small"
  121. />
  122. <span className="ml-2">压力监测</span>
  123. </div>
  124. </Col>
  125. <Col span={4}>
  126. <div className="mb-2">
  127. <Switch
  128. checked={layerVisibility.flow}
  129. onChange={(checked) => handleLayerToggle("flow", checked)}
  130. size="small"
  131. />
  132. <span className="ml-2">流量监测</span>
  133. </div>
  134. </Col>
  135. <Col span={4}>
  136. <div className="mb-2">
  137. <Switch
  138. checked={layerVisibility.temperature}
  139. onChange={(checked) => handleLayerToggle("temperature", checked)}
  140. size="small"
  141. />
  142. <span className="ml-2">温度监测</span>
  143. </div>
  144. </Col>
  145. <Col span={4}>
  146. <div className="mb-2">
  147. <Switch
  148. checked={layerVisibility.leakage}
  149. onChange={(checked) => handleLayerToggle("leakage", checked)}
  150. size="small"
  151. />
  152. <span className="ml-2">泄漏检测</span>
  153. </div>
  154. </Col>
  155. <Col span={4}>
  156. <div className="mb-2">
  157. <Switch
  158. checked={layerVisibility.devices}
  159. onChange={(checked) => handleLayerToggle("devices", checked)}
  160. size="small"
  161. />
  162. <span className="ml-2">监测设备</span>
  163. </div>
  164. </Col>
  165. </Row>
  166. </Card>
  167. </Col>
  168. </Row>
  169. <Row gutter={16} className="mb-4">
  170. <Col span={6}>
  171. <Card size="small" title="时间范围">
  172. <Select value={selectedTimeRange} onChange={setSelectedTimeRange} style={{ width: "100%" }}>
  173. <Option value="realtime">实时数据</Option>
  174. <Option value="1hour">近1小时</Option>
  175. <Option value="6hours">近6小时</Option>
  176. <Option value="24hours">近24小时</Option>
  177. <Option value="7days">近7天</Option>
  178. </Select>
  179. </Card>
  180. </Col>
  181. <Col span={6}>
  182. <Card size="small" title="刷新间隔">
  183. <div>
  184. <Slider
  185. min={1}
  186. max={60}
  187. value={dataRefreshInterval}
  188. onChange={setDataRefreshInterval}
  189. marks={{ 1: "1s", 30: "30s", 60: "60s" }}
  190. />
  191. <div className="text-center text-sm text-gray-500">{dataRefreshInterval}秒</div>
  192. </div>
  193. </Card>
  194. </Col>
  195. <Col span={6}>
  196. <Card size="small" title="操作控制">
  197. <Space>
  198. <Button icon={<ReloadOutlined />} onClick={handleRefresh} size="small">
  199. 刷新
  200. </Button>
  201. <Button icon={<FullscreenOutlined />} onClick={() => setIsFullscreen(!isFullscreen)} size="small">
  202. 全屏
  203. </Button>
  204. </Space>
  205. </Card>
  206. </Col>
  207. <Col span={6}>
  208. <Card size="small" title="监测状态">
  209. <div className="space-y-1">
  210. <div className="flex justify-between">
  211. <span>正常:</span>
  212. <Tag color="green">3</Tag>
  213. </div>
  214. <div className="flex justify-between">
  215. <span>预警:</span>
  216. <Tag color="orange">1</Tag>
  217. </div>
  218. <div className="flex justify-between">
  219. <span>异常:</span>
  220. <Tag color="red">0</Tag>
  221. </div>
  222. </div>
  223. </Card>
  224. </Col>
  225. </Row>
  226. {/* 地图展示 */}
  227. <Row gutter={16}>
  228. <Col span={18}>
  229. <Card title="实时监测一张图" size="small" className={isFullscreen ? "fixed inset-0 z-50" : ""}>
  230. <GasNetworkMap height={isFullscreen ? "calc(100vh - 120px)" : "700px"} />
  231. </Card>
  232. </Col>
  233. <Col span={6}>
  234. <Card title="监测点详情" size="small">
  235. <div className="space-y-3 max-h-96 overflow-y-auto">
  236. {mockMonitoringPoints.map((point) => (
  237. <div key={point.id} className="p-3 border rounded-lg">
  238. <div className="flex justify-between items-center mb-2">
  239. <span className="font-medium">{point.name}</span>
  240. <Tag color={getStatusColor(point.status)}>
  241. {point.status === "normal" ? "正常" : point.status === "warning" ? "预警" : "异常"}
  242. </Tag>
  243. </div>
  244. <div className="text-sm text-gray-600 space-y-1">
  245. <div>类型: {getTypeLabel(point.type)}</div>
  246. <div>
  247. 当前值: {point.value} {point.unit}
  248. </div>
  249. <div>更新时间: {point.lastUpdate}</div>
  250. </div>
  251. </div>
  252. ))}
  253. </div>
  254. </Card>
  255. <Card title="实时统计" size="small" className="mt-4">
  256. <div className="space-y-3">
  257. <div className="flex justify-between">
  258. <span>监测点总数:</span>
  259. <span className="font-bold text-blue-600">342</span>
  260. </div>
  261. <div className="flex justify-between">
  262. <span>在线设备:</span>
  263. <span className="font-bold text-green-600">298</span>
  264. </div>
  265. <div className="flex justify-between">
  266. <span>离线设备:</span>
  267. <span className="font-bold text-red-600">12</span>
  268. </div>
  269. <div className="flex justify-between">
  270. <span>维护中:</span>
  271. <span className="font-bold text-orange-600">32</span>
  272. </div>
  273. <div className="flex justify-between">
  274. <span>数据更新率:</span>
  275. <span className="font-bold text-blue-600">98.5%</span>
  276. </div>
  277. </div>
  278. </Card>
  279. </Col>
  280. </Row>
  281. </Card>
  282. </div>
  283. )
  284. }