page.tsx 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  1. "use client"
  2. // 在文件顶部导入 useMemo
  3. import {useState} from "react"
  4. import {
  5. Badge,
  6. Button,
  7. Card,
  8. Col,
  9. Form,
  10. Input,
  11. Layout,
  12. Menu,
  13. message,
  14. Modal,
  15. Row,
  16. Select,
  17. Space,
  18. Statistic,
  19. Table,
  20. Tabs,
  21. Tag,
  22. } from "antd"
  23. import {
  24. AlertOutlined,
  25. BarChartOutlined,
  26. DashboardOutlined,
  27. DatabaseOutlined,
  28. EditOutlined,
  29. EyeOutlined,
  30. HomeOutlined,
  31. PlusOutlined,
  32. SettingOutlined
  33. } from "@ant-design/icons"
  34. import dynamic from "next/dynamic"
  35. import EChart from "@/components/echarts"
  36. import GisMapBaidu from "@/components/gisMapBaidu";
  37. import GisMapJinggai from "@/components/GISModule/gisMapJinggai";
  38. import PicEcharts from "@/components/GISModule/PicEcharts";
  39. import BarChart from "@/components/GISModule/eqClassIficationEcharts";
  40. import Dtxxdata from "@/components/GISModule/Dtxxdata";
  41. import Sbledata from "@/components/GISModule/Sbledata";
  42. import DbgdData from "@/components/GISModule/DbgdData";
  43. import SbqyxxData from "@/components/GISModule/SbqyxxData";
  44. //实时监测-实时监测一张图
  45. import RTMMGis from "@/components/realTimeMontorModule/RTMMGis";
  46. const {Header, Sider, Content} = Layout
  47. const {TabPane} = Tabs
  48. const {Option} = Select
  49. // 动态导入地图组件避免SSR问题
  50. const MapView = dynamic(() => import("./components/map-view"), {ssr: false})
  51. export default function ManholeMonitoringSystem() {
  52. const [selectedMenu, setSelectedMenu] = useState("home")
  53. const [deviceModalVisible, setDeviceModalVisible] = useState(false)
  54. const [selectedDevice, setSelectedDevice] = useState(null)
  55. const [addDeviceModalVisible, setAddDeviceModalVisible] = useState(false)
  56. const [editDeviceModalVisible, setEditDeviceModalVisible] = useState(false)
  57. const [editingDevice, setEditingDevice] = useState(null)
  58. const [devices, setDevices] = useState([
  59. {
  60. id: 1,
  61. name: "设备001",
  62. type: "智能井盖",
  63. status: "normal",
  64. location: "朝阳区建国路",
  65. lat: 39.9042,
  66. lng: 116.4074,
  67. battery: 85,
  68. signal: 90,
  69. },
  70. {
  71. id: 2,
  72. name: "设备002",
  73. type: "智能井盖",
  74. status: "warning",
  75. location: "海淀区中关村",
  76. lat: 39.9826,
  77. lng: 116.3186,
  78. battery: 45,
  79. signal: 75,
  80. },
  81. {
  82. id: 3,
  83. name: "设备003",
  84. type: "智能井盖",
  85. status: "error",
  86. location: "西城区金融街",
  87. lat: 39.926,
  88. lng: 116.3663,
  89. battery: 20,
  90. signal: 60,
  91. },
  92. {
  93. id: 4,
  94. name: "设备004",
  95. type: "智能井盖",
  96. status: "normal",
  97. location: "东城区王府井",
  98. lat: 39.9097,
  99. lng: 116.4109,
  100. battery: 92,
  101. signal: 95,
  102. },
  103. {
  104. id: 5,
  105. name: "设备005",
  106. type: "智能井盖",
  107. status: "maintenance",
  108. location: "丰台区丽泽",
  109. lat: 39.8584,
  110. lng: 116.3135,
  111. battery: 78,
  112. signal: 80,
  113. },
  114. ])
  115. const [alerts, setAlerts] = useState([
  116. {
  117. id: 1,
  118. deviceName: "设备002",
  119. type: "位移异常",
  120. level: "warning",
  121. time: "2024-01-15 14:30:25",
  122. location: "海淀区中关村",
  123. status: "pending", // pending, processed, ignored
  124. },
  125. {
  126. id: 2,
  127. deviceName: "设备003",
  128. type: "电池电压低",
  129. level: "error",
  130. time: "2024-01-15 14:25:10",
  131. location: "西城区金融街",
  132. status: "pending",
  133. },
  134. {
  135. id: 3,
  136. deviceName: "设备001",
  137. type: "震动异常",
  138. level: "info",
  139. time: "2024-01-15 14:20:15",
  140. location: "朝阳区建国路",
  141. status: "processed",
  142. },
  143. ])
  144. const [addDeviceForm] = Form.useForm()
  145. const [editDeviceForm] = Form.useForm()
  146. const handleAddDevice = (values: any) => {
  147. const newDevice = {
  148. id: devices.length + 1,
  149. name: values.name,
  150. type: values.type,
  151. status: values.status,
  152. location: values.location,
  153. lat: Number.parseFloat(values.lat),
  154. lng: Number.parseFloat(values.lng),
  155. battery: Number.parseInt(values.battery),
  156. signal: Number.parseInt(values.signal),
  157. }
  158. setDevices([...devices, newDevice])
  159. setAddDeviceModalVisible(false)
  160. addDeviceForm.resetFields()
  161. message.success("设备添加成功!")
  162. }
  163. const handleEditDevice = (values: any) => {
  164. const updatedDevices = devices.map((device) =>
  165. device.id === editingDevice.id
  166. ? {
  167. ...device,
  168. ...values,
  169. lat: Number.parseFloat(values.lat),
  170. lng: Number.parseFloat(values.lng),
  171. battery: Number.parseInt(values.battery),
  172. signal: Number.parseInt(values.signal),
  173. }
  174. : device,
  175. )
  176. setDevices(updatedDevices)
  177. setEditDeviceModalVisible(false)
  178. setEditingDevice(null)
  179. editDeviceForm.resetFields()
  180. message.success("设备信息更新成功!")
  181. }
  182. const openEditModal = (device: any) => {
  183. setEditingDevice(device)
  184. editDeviceForm.setFieldsValue(device)
  185. setEditDeviceModalVisible(true)
  186. }
  187. const handleProcessAlert = (alertId: number) => {
  188. const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? {...alert, status: "processed"} : alert))
  189. setAlerts(updatedAlerts)
  190. message.success("报警已处理!")
  191. }
  192. const handleIgnoreAlert = (alertId: number) => {
  193. const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? {...alert, status: "ignored"} : alert))
  194. setAlerts(updatedAlerts)
  195. message.success("报警已忽略!")
  196. }
  197. // 设备状态统计
  198. const deviceStats = {
  199. total: devices.length,
  200. normal: devices.filter((d) => d.status === "normal").length,
  201. warning: devices.filter((d) => d.status === "warning").length,
  202. error: devices.filter((d) => d.status === "error").length,
  203. maintenance: devices.filter((d) => d.status === "maintenance").length,
  204. }
  205. // 区域分布图表配置
  206. const regionChartOption = {
  207. title: {text: "设备区域分布", left: "center"},
  208. tooltip: {trigger: "item"},
  209. series: [
  210. {
  211. type: "pie",
  212. radius: "50%",
  213. data: [
  214. {value: 1, name: "朝阳区"},
  215. {value: 1, name: "海淀区"},
  216. {value: 1, name: "西城区"},
  217. {value: 1, name: "东城区"},
  218. {value: 1, name: "丰台区"},
  219. ],
  220. },
  221. ],
  222. }
  223. // 设备状态分布图表配置
  224. const statusChartOption = {
  225. title: {text: "设备状态分布", left: "center"},
  226. tooltip: {trigger: "item"},
  227. series: [
  228. {
  229. type: "pie",
  230. radius: "50%",
  231. data: [
  232. {value: deviceStats.normal, name: "正常", itemStyle: {color: "#52c41a"}},
  233. {value: deviceStats.warning, name: "告警", itemStyle: {color: "#faad14"}},
  234. {value: deviceStats.error, name: "故障", itemStyle: {color: "#f5222d"}},
  235. {value: deviceStats.maintenance, name: "维修中", itemStyle: {color: "#722ed1"}},
  236. ],
  237. },
  238. ],
  239. }
  240. // 实时监测数据图表配置
  241. const monitoringChartOption = {
  242. title: {text: "实时监测数据趋势", left: "center"},
  243. tooltip: {trigger: "axis"},
  244. legend: {data: ["倾斜角度", "位移距离", "震动强度"], top: 30},
  245. xAxis: {
  246. type: "category",
  247. data: ["00:00", "04:00", "08:00", "12:00", "16:00", "20:00"],
  248. },
  249. yAxis: {type: "value"},
  250. series: [
  251. {
  252. name: "倾斜角度",
  253. type: "line",
  254. data: [2.1, 2.3, 2.0, 2.5, 2.8, 2.4],
  255. itemStyle: {color: "#1890ff"},
  256. },
  257. {
  258. name: "位移距离",
  259. type: "line",
  260. data: [0.5, 0.7, 0.4, 0.9, 1.2, 0.8],
  261. itemStyle: {color: "#52c41a"},
  262. },
  263. {
  264. name: "震动强度",
  265. type: "line",
  266. data: [15, 18, 12, 22, 28, 20],
  267. itemStyle: {color: "#faad14"},
  268. },
  269. ],
  270. }
  271. const deviceColumns = [
  272. {title: "设备编号", dataIndex: "name", key: "name"},
  273. {title: "设备类型", dataIndex: "type", key: "type"},
  274. {
  275. title: "运维状态",
  276. dataIndex: "status",
  277. key: "status",
  278. render: (status: string) => {
  279. const statusMap = {
  280. normal: {color: "success", text: "正常"},
  281. warning: {color: "warning", text: "告警"},
  282. error: {color: "error", text: "故障"},
  283. maintenance: {color: "processing", text: "维修中"},
  284. }
  285. const config = statusMap[status as keyof typeof statusMap]
  286. return <Badge status={config.color as any} text={config.text}/>
  287. },
  288. },
  289. {title: "所在位置", dataIndex: "location", key: "location"},
  290. {
  291. title: "电池电量",
  292. dataIndex: "battery",
  293. key: "battery",
  294. render: (battery: number) => `${battery}%`,
  295. },
  296. {
  297. title: "信号强度",
  298. dataIndex: "signal",
  299. key: "signal",
  300. render: (signal: number) => `${signal}%`,
  301. },
  302. {
  303. title: "操作",
  304. key: "action",
  305. render: (_, record: any) => (
  306. <Space>
  307. <Button
  308. type="link"
  309. icon={<EyeOutlined/>}
  310. onClick={() => {
  311. setSelectedDevice(record)
  312. setDeviceModalVisible(true)
  313. }}
  314. >
  315. 查看
  316. </Button>
  317. <Button type="link" icon={<EditOutlined/>} onClick={() => openEditModal(record)}>
  318. 编辑
  319. </Button>
  320. </Space>
  321. ),
  322. },
  323. ]
  324. const alertColumns = [
  325. {title: "设备名称", dataIndex: "deviceName", key: "deviceName"},
  326. {title: "报警类型", dataIndex: "type", key: "type"},
  327. {
  328. title: "报警级别",
  329. dataIndex: "level",
  330. key: "level",
  331. render: (level: string) => {
  332. const levelMap = {
  333. info: {color: "blue", text: "信息"},
  334. warning: {color: "orange", text: "警告"},
  335. error: {color: "red", text: "严重"},
  336. }
  337. const config = levelMap[level as keyof typeof levelMap]
  338. return <Tag color={config.color}>{config.text}</Tag>
  339. },
  340. },
  341. {title: "报警时间", dataIndex: "time", key: "time"},
  342. {title: "设备位置", dataIndex: "location", key: "location"},
  343. {
  344. title: "状态",
  345. dataIndex: "status",
  346. key: "status",
  347. render: (status: string) => {
  348. const statusMap = {
  349. pending: {color: "orange", text: "待处理"},
  350. processed: {color: "green", text: "已处理"},
  351. ignored: {color: "gray", text: "已忽略"},
  352. }
  353. const config = statusMap[status as keyof typeof statusMap]
  354. return <Tag color={config.color}>{config.text}</Tag>
  355. },
  356. },
  357. {
  358. title: "操作",
  359. key: "action",
  360. render: (_, record: any) => (
  361. <Space>
  362. {record.status === "pending" && (
  363. <>
  364. <Button type="link" onClick={() => handleProcessAlert(record.id)}>
  365. 处理
  366. </Button>
  367. <Button type="link" onClick={() => handleIgnoreAlert(record.id)}>
  368. 忽略
  369. </Button>
  370. </>
  371. )}
  372. {record.status !== "pending" && <span className="text-gray-400">已处理</span>}
  373. </Space>
  374. ),
  375. },
  376. ]
  377. const renderContent = () => {
  378. switch (selectedMenu) {
  379. case "home":
  380. return (
  381. <div>
  382. <div className="space-y-6" style={{height: '93vh', backgroundColor: '#3a3899'}}>
  383. <div className="flex m-0" style={{height: '70%'}}>
  384. <div className="" style={{width: '25%'}}>
  385. <div className='' style={{height: '50%'}}>
  386. <div style={{
  387. height: '12%',
  388. backgroundImage: 'url(/boxTitle.png)',
  389. backgroundSize: '100% 100%',
  390. textAlign: 'left',
  391. color: 'white',
  392. fontSize: '18px',
  393. fontWeight: 'bold',
  394. paddingLeft: '20px'
  395. }}>
  396. 待办工单
  397. </div>
  398. <div className='' style={{
  399. height: '88%',
  400. backgroundImage: 'url(/eject.PNG)',
  401. backgroundSize: '100% 100%',
  402. padding: '10px'
  403. }}>
  404. <DbgdData></DbgdData>
  405. </div>
  406. </div>
  407. <div className='' style={{height: '50%'}}>
  408. <div style={{
  409. height: '12%',
  410. backgroundImage: 'url(/boxTitle.png)',
  411. backgroundSize: '100% 100%',
  412. textAlign: 'left',
  413. color: 'white',
  414. fontSize: '18px',
  415. fontWeight: 'bold',
  416. paddingLeft: '20px'
  417. }}>
  418. 告警类型统计(15天内)
  419. </div>
  420. <div className='' style={{
  421. height: '88%',
  422. backgroundImage: 'url(/eject.PNG)',
  423. backgroundSize: '100% 100%'
  424. }}>
  425. <PicEcharts
  426. // title="告警类型统计"
  427. data={[
  428. {value: 335, name: '位移告警'},
  429. {value: 310, name: '倾斜告警'},
  430. {value: 234, name: '水位告警'},
  431. {value: 135, name: '电压告警'},
  432. {value: 1548, name: '温度告警'}
  433. ]}
  434. style={{height: '100%', width: '100%'}}
  435. />
  436. </div>
  437. </div>
  438. </div>
  439. <div className="" style={{width: '70%'}}>
  440. <GisMapJinggai height={'65'}/>
  441. </div>
  442. <div className="" style={{width: '25%'}}>
  443. <div className='' style={{height: '100%'}}>
  444. <div style={{
  445. height: '6%',
  446. backgroundImage: 'url(/boxTitle.png)',
  447. backgroundSize: '100% 100%',
  448. textAlign: 'left',
  449. color: 'white',
  450. fontSize: '18px',
  451. fontWeight: 'bold',
  452. paddingLeft: '20px'
  453. }}>
  454. 设备列表
  455. </div>
  456. {/* 修改表格容器样式,添加背景色覆盖白色区域 */}
  457. <div className='' style={{
  458. height: '94%',
  459. backgroundImage: 'url(/eject.PNG)',
  460. backgroundSize: '100% 100%',
  461. padding: '40px 20px 40px 20px',
  462. boxSizing: 'border-box',
  463. overflowY: 'hidden',
  464. backgroundColor: '#3a3899' // 添加与页面主色调一致的背景色
  465. }}>
  466. <div style={{height: '100%',}}>
  467. <Sbledata></Sbledata>
  468. </div>
  469. </div>
  470. </div>
  471. </div>
  472. </div>
  473. <div className="flex m-0" style={{height: '30%', justifyContent: 'space-evenly'}}>
  474. <div style={{width: '33%'}}>
  475. <div style={{
  476. height: '12%',
  477. backgroundImage: 'url(/boxTitle.png)',
  478. backgroundSize: '100% 100%',
  479. textAlign: 'left',
  480. color: 'white',
  481. fontSize: '18px',
  482. fontWeight: 'bold',
  483. paddingLeft: '30px'
  484. }}>
  485. 设备区域信息
  486. </div>
  487. <div className='' style={{
  488. height: '88%',
  489. backgroundImage: 'url(/eject.PNG)',
  490. backgroundSize: '100% 100%'
  491. }}>
  492. <SbqyxxData
  493. data={[
  494. {value: 81, name: '太安社区'},
  495. {value: 85, name: '桃花岭社区'},
  496. {value: 60, name: '龙泉山社区'},
  497. {value: 45, name: '鸳鸯山社区'},
  498. {value: 50, name: '龙兴社区'},
  499. {value: 55, name: '胜利门社区'},
  500. {value: 88, name: '鹤鸣山社区'},
  501. {value: 25, name: '教场坪社区'},
  502. {value: 45, name: '荷花池社区'},
  503. {value: 22, name: '廻龙山社区'},
  504. {value: 35, name: '凤鸣塔社区'},
  505. {value: 35, name: '望圣坡社区'},
  506. {value: 22, name: '凤凰山社区'},
  507. {value: 58, name: '苦藤铺社区'},
  508. {value: 75, name: '黄草尾社区'},
  509. {value: 65, name: '老鸦溪社区'},
  510. {value: 66, name: '渔家巷社区'}
  511. ]}
  512. style={{height: '99%', width: '100%'}}
  513. />
  514. </div>
  515. </div>
  516. <div style={{width: '33%'}}>
  517. <div style={{
  518. height: '12%',
  519. backgroundImage: 'url(/boxTitle.png)',
  520. backgroundSize: '100% 100%',
  521. textAlign: 'left',
  522. color: 'white',
  523. fontSize: '18px',
  524. fontWeight: 'bold',
  525. paddingLeft: '30px'
  526. }}>
  527. 设备分类
  528. </div>
  529. <div className='' style={{
  530. height: '88%',
  531. backgroundImage: 'url(/eject.PNG)',
  532. backgroundSize: '100% 100%'
  533. }}>
  534. <BarChart
  535. data={[
  536. {value: 120, name: '电用井盖'},
  537. {value: 85, name: '供水井盖'},
  538. {value: 60, name: '通信井盖'},
  539. {value: 45, name: '燃气井盖'},
  540. {value: 30, name: '污水井盖'},
  541. {value: 30, name: '雨水井盖'},
  542. {value: 30, name: '路灯井盖'},
  543. {value: 30, name: '网络井盖'},
  544. {value: 30, name: '电缆井盖'},
  545. {value: 30, name: '园林井盖'},
  546. {value: 30, name: '化粪池井盖'}
  547. ]}
  548. style={{height: '99%', width: '100%'}}
  549. />
  550. </div>
  551. </div>
  552. <div style={{width: '33%'}}>
  553. <div style={{
  554. height: '12%',
  555. backgroundImage: 'url(/boxTitle.png)',
  556. backgroundSize: '100% 100%',
  557. textAlign: 'left',
  558. color: 'white',
  559. fontSize: '18px',
  560. fontWeight: 'bold',
  561. paddingLeft: '30px'
  562. }}>
  563. 动态信息
  564. </div>
  565. <div className='' style={{
  566. height: '88%',
  567. backgroundImage: 'url(/eject.PNG)',
  568. backgroundSize: '100% 100%',
  569. padding: '20px'
  570. }}>
  571. <Dtxxdata height={'100%'} width={'100%'}/>
  572. </div>
  573. </div>
  574. </div>
  575. </div>
  576. {/*<div>*/}
  577. {/* <GisMapJinggai height={'100'}/>*/}
  578. {/*</div>*/}
  579. </div>
  580. )
  581. case "dashboard":
  582. return (
  583. <div className="space-y-6">
  584. {/* 统计卡片 */}
  585. <Row gutter={16}>
  586. <Col span={6}>
  587. <Card>
  588. <Statistic title="设备总数" value={deviceStats.total} valueStyle={{color: "#1890ff"}}/>
  589. </Card>
  590. </Col>
  591. <Col span={6}>
  592. <Card>
  593. <Statistic title="正常设备" value={deviceStats.normal} valueStyle={{color: "#52c41a"}}/>
  594. </Card>
  595. </Col>
  596. <Col span={6}>
  597. <Card>
  598. <Statistic title="告警设备" value={deviceStats.warning}
  599. valueStyle={{color: "#faad14"}}/>
  600. </Card>
  601. </Col>
  602. <Col span={6}>
  603. <Card>
  604. <Statistic title="故障设备" value={deviceStats.error} valueStyle={{color: "#f5222d"}}/>
  605. </Card>
  606. </Col>
  607. </Row>
  608. {/* 图表展示 */}
  609. <Row gutter={16}>
  610. <Col span={12}>
  611. <Card title="设备区域分布">
  612. <EChart option={regionChartOption}/>
  613. </Card>
  614. </Col>
  615. <Col span={12}>
  616. <Card title="设备状态分布">
  617. <EChart option={statusChartOption}/>
  618. </Card>
  619. </Col>
  620. </Row>
  621. {/* 实时监测趋势 */}
  622. <Card title="实时监测数据趋势">
  623. <EChart option={monitoringChartOption}/>
  624. </Card>
  625. </div>
  626. )
  627. case "devices":
  628. return (
  629. <div className="space-y-6">
  630. <Card
  631. title="监测设备台账"
  632. extra={
  633. <Button type="primary" icon={<PlusOutlined/>}
  634. onClick={() => setAddDeviceModalVisible(true)}>
  635. 添加设备
  636. </Button>
  637. }
  638. >
  639. <Table columns={deviceColumns} dataSource={devices} rowKey="id"
  640. pagination={{pageSize: 10}}/>
  641. </Card>
  642. </div>
  643. )
  644. case "map":
  645. return (
  646. <div className="space-y-6">
  647. <Card title="监测设备 GIS 一张图">
  648. <div style={{height: "600px"}}>
  649. <GisMapBaidu height={'50'}/>
  650. </div>
  651. </Card>
  652. </div>
  653. )
  654. case "monitoring":
  655. return (
  656. <div className="space-y-6" style={{paddingLeft:'10px'}}>
  657. <Tabs defaultActiveKey="realtime">
  658. <TabPane tab="实时监测" key="realtime">
  659. <RTMMGis height={'86.2'}></RTMMGis>
  660. </TabPane>
  661. <TabPane tab="窨井状态监测" key="data">
  662. <Card title="窨井状态监测">
  663. <EChart option={monitoringChartOption}/>
  664. </Card>
  665. </TabPane>
  666. </Tabs>
  667. </div>
  668. )
  669. case "alerts":
  670. return (
  671. <div className="space-y-6">
  672. <Row gutter={16}>
  673. <Col span={6}>
  674. <Card>
  675. <Statistic title="今日告警" value={alerts.length} valueStyle={{color: "#f5222d"}}/>
  676. </Card>
  677. </Col>
  678. <Col span={6}>
  679. <Card>
  680. <Statistic
  681. title="待处理"
  682. value={alerts.filter((a) => a.status === "pending").length}
  683. valueStyle={{color: "#faad14"}}
  684. />
  685. </Card>
  686. </Col>
  687. <Col span={6}>
  688. <Card>
  689. <Statistic
  690. title="已处理"
  691. value={alerts.filter((a) => a.status === "processed").length}
  692. valueStyle={{color: "#52c41a"}}
  693. />
  694. </Card>
  695. </Col>
  696. <Col span={6}>
  697. <Card>
  698. <Statistic
  699. title="已忽略"
  700. value={alerts.filter((a) => a.status === "ignored").length}
  701. valueStyle={{color: "#d9d9d9"}}
  702. />
  703. </Card>
  704. </Col>
  705. </Row>
  706. <Card title="监测报警记录">
  707. <Table columns={alertColumns} dataSource={alerts} rowKey="id" pagination={{pageSize: 10}}/>
  708. </Card>
  709. </div>
  710. )
  711. case "settlement":
  712. return (
  713. <div className="space-y-6 p-2">
  714. <Tabs defaultActiveKey="realtime">
  715. <TabPane tab="案件流转管理" key="realtime">
  716. <Row gutter={16}>
  717. <Col span={24}>
  718. <Card title="案件流转管理">
  719. <div style={{height: "500px"}}>
  720. <MapView devices={devices} showRealTimeData/>
  721. </div>
  722. </Card>
  723. </Col>
  724. </Row>
  725. </TabPane>
  726. <TabPane tab="工单处置管理" key="data">
  727. <Card title="工单处置管理">
  728. <EChart option={monitoringChartOption}/>
  729. </Card>
  730. </TabPane>
  731. <TabPane tab="经办工单" key="data1">
  732. <Card title="经办工单">
  733. <EChart option={monitoringChartOption}/>
  734. </Card>
  735. </TabPane>
  736. <TabPane tab="办结工单" key="data2">
  737. <Card title="办结工单">
  738. <EChart option={monitoringChartOption}/>
  739. </Card>
  740. </TabPane>
  741. </Tabs>
  742. </div>
  743. )
  744. default:
  745. return <div>页面开发中...</div>
  746. }
  747. }
  748. return (
  749. <Layout className="min-h-screen">
  750. <Header className="bg-blue-600 text-white" style={{backgroundColor: 'white'}}>
  751. <div className="flex items-center justify-between">
  752. <h1 className="text-xl font-bold">窨井盖安全运行监测子系统</h1>
  753. <div className="text-sm">当前时间: {new Date().toLocaleString("zh-CN")}</div>
  754. </div>
  755. </Header>
  756. <Layout>
  757. <Sider width={200} className="bg-white">
  758. <Menu
  759. mode="inline"
  760. selectedKeys={[selectedMenu]}
  761. onClick={({key}) => setSelectedMenu(key)}
  762. className="h-full border-r"
  763. >
  764. <Menu.Item key="home" icon={<HomeOutlined/>}>
  765. GIS地图
  766. </Menu.Item>
  767. <Menu.Item key="dashboard" icon={<DashboardOutlined/>}>
  768. 监控面板
  769. </Menu.Item>
  770. <Menu.Item key="devices" icon={<SettingOutlined/>}>
  771. 设备管理
  772. </Menu.Item>
  773. {/*<Menu.Item key="map" icon={<EnvironmentOutlined/>}>*/}
  774. {/* GIS地图*/}
  775. {/*</Menu.Item>*/}
  776. <Menu.Item key="monitoring" icon={<BarChartOutlined/>}>
  777. 实时监测
  778. </Menu.Item>
  779. <Menu.Item key="alerts" icon={<AlertOutlined/>}>
  780. 报警管理
  781. </Menu.Item>
  782. <Menu.Item key="settlement" icon={<DatabaseOutlined/>}>
  783. 案件办结
  784. </Menu.Item>
  785. </Menu>
  786. </Sider>
  787. <Layout>
  788. <Content className="bg-gray-50 min-h-full">{renderContent()}</Content>
  789. </Layout>
  790. </Layout>
  791. {/* 设备详情弹窗 */}
  792. <Modal
  793. title="设备详情"
  794. open={deviceModalVisible}
  795. onCancel={() => setDeviceModalVisible(false)}
  796. footer={null}
  797. width={600}
  798. >
  799. {selectedDevice && (
  800. <div className="space-y-4">
  801. <Row gutter={16}>
  802. <Col span={12}>
  803. <div>
  804. <strong>设备编号:</strong> {selectedDevice.name}
  805. </div>
  806. </Col>
  807. <Col span={12}>
  808. <div>
  809. <strong>设备类型:</strong> {selectedDevice.type}
  810. </div>
  811. </Col>
  812. </Row>
  813. <Row gutter={16}>
  814. <Col span={12}>
  815. <div>
  816. <strong>所在位置:</strong> {selectedDevice.location}
  817. </div>
  818. </Col>
  819. <Col span={12}>
  820. <div>
  821. <strong>运维状态:</strong>
  822. <Badge
  823. status={selectedDevice.status === "normal" ? "success" : "error"}
  824. text={selectedDevice.status === "normal" ? "正常" : "异常"}
  825. />
  826. </div>
  827. </Col>
  828. </Row>
  829. <Row gutter={16}>
  830. <Col span={12}>
  831. <div>
  832. <strong>电池电量:</strong> {selectedDevice.battery}%
  833. </div>
  834. </Col>
  835. <Col span={12}>
  836. <div>
  837. <strong>信号强度:</strong> {selectedDevice.signal}%
  838. </div>
  839. </Col>
  840. </Row>
  841. </div>
  842. )}
  843. </Modal>
  844. {/* 添加设备弹窗 */}
  845. <Modal
  846. title="添加设备"
  847. open={addDeviceModalVisible}
  848. onCancel={() => {
  849. setAddDeviceModalVisible(false)
  850. addDeviceForm.resetFields()
  851. }}
  852. onOk={() => addDeviceForm.submit()}
  853. width={600}
  854. >
  855. <Form form={addDeviceForm} layout="vertical" onFinish={handleAddDevice}>
  856. <Row gutter={16}>
  857. <Col span={12}>
  858. <Form.Item name="name" label="设备编号" rules={[{required: true, message: "请输入设备编号"}]}>
  859. <Input placeholder="请输入设备编号"/>
  860. </Form.Item>
  861. </Col>
  862. <Col span={12}>
  863. <Form.Item name="type" label="设备类型" rules={[{required: true, message: "请选择设备类型"}]}>
  864. <Select placeholder="请选择设备类型">
  865. <Option value="智能井盖">智能井盖</Option>
  866. <Option value="传感器">传感器</Option>
  867. <Option value="监控设备">监控设备</Option>
  868. </Select>
  869. </Form.Item>
  870. </Col>
  871. </Row>
  872. <Row gutter={16}>
  873. <Col span={12}>
  874. <Form.Item name="status" label="运维状态" rules={[{required: true, message: "请选择运维状态"}]}>
  875. <Select placeholder="请选择运维状态">
  876. <Option value="normal">正常</Option>
  877. <Option value="warning">告警</Option>
  878. <Option value="error">故障</Option>
  879. <Option value="maintenance">维修中</Option>
  880. </Select>
  881. </Form.Item>
  882. </Col>
  883. <Col span={12}>
  884. <Form.Item name="location" label="所在位置" rules={[{required: true, message: "请输入所在位置"}]}>
  885. <Input placeholder="请输入所在位置"/>
  886. </Form.Item>
  887. </Col>
  888. </Row>
  889. <Row gutter={16}>
  890. <Col span={12}>
  891. <Form.Item name="lat" label="纬度" rules={[{required: true, message: "请输入纬度"}]}>
  892. <Input placeholder="请输入纬度" type="number" step="0.000001"/>
  893. </Form.Item>
  894. </Col>
  895. <Col span={12}>
  896. <Form.Item name="lng" label="经度" rules={[{required: true, message: "请输入经度"}]}>
  897. <Input placeholder="请输入经度" type="number" step="0.000001"/>
  898. </Form.Item>
  899. </Col>
  900. </Row>
  901. <Row gutter={16}>
  902. <Col span={12}>
  903. <Form.Item name="battery" label="电池电量(%)" rules={[{required: true, message: "请输入电池电量"}]}>
  904. <Input placeholder="请输入电池电量" type="number" min="0" max="100"/>
  905. </Form.Item>
  906. </Col>
  907. <Col span={12}>
  908. <Form.Item name="signal" label="信号强度(%)" rules={[{required: true, message: "请输入信号强度"}]}>
  909. <Input placeholder="请输入信号强度" type="number" min="0" max="100"/>
  910. </Form.Item>
  911. </Col>
  912. </Row>
  913. </Form>
  914. </Modal>
  915. {/* 编辑设备弹窗 */}
  916. <Modal
  917. title="编辑设备"
  918. open={editDeviceModalVisible}
  919. onCancel={() => {
  920. setEditDeviceModalVisible(false)
  921. setEditingDevice(null)
  922. editDeviceForm.resetFields()
  923. }}
  924. onOk={() => editDeviceForm.submit()}
  925. width={600}
  926. >
  927. <Form form={editDeviceForm} layout="vertical" onFinish={handleEditDevice}>
  928. <Row gutter={16}>
  929. <Col span={12}>
  930. <Form.Item name="name" label="设备编号" rules={[{required: true, message: "请输入设备编号"}]}>
  931. <Input placeholder="请输入设备编号"/>
  932. </Form.Item>
  933. </Col>
  934. <Col span={12}>
  935. <Form.Item name="type" label="设备类型" rules={[{required: true, message: "请选择设备类型"}]}>
  936. <Select placeholder="请选择设备类型">
  937. <Option value="智能井盖">智能井盖</Option>
  938. <Option value="传感器">传感器</Option>
  939. <Option value="监控设备">监控设备</Option>
  940. </Select>
  941. </Form.Item>
  942. </Col>
  943. </Row>
  944. <Row gutter={16}>
  945. <Col span={12}>
  946. <Form.Item name="status" label="运维状态" rules={[{required: true, message: "请选择运维状态"}]}>
  947. <Select placeholder="请选择运维状态">
  948. <Option value="normal">正常</Option>
  949. <Option value="warning">告警</Option>
  950. <Option value="error">故障</Option>
  951. <Option value="maintenance">维修中</Option>
  952. </Select>
  953. </Form.Item>
  954. </Col>
  955. <Col span={12}>
  956. <Form.Item name="location" label="所在位置" rules={[{required: true, message: "请输入所在位置"}]}>
  957. <Input placeholder="请输入所在位置"/>
  958. </Form.Item>
  959. </Col>
  960. </Row>
  961. <Row gutter={16}>
  962. <Col span={12}>
  963. <Form.Item name="lat" label="纬度" rules={[{required: true, message: "请输入纬度"}]}>
  964. <Input placeholder="请输入纬度" type="number" step="0.000001"/>
  965. </Form.Item>
  966. </Col>
  967. <Col span={12}>
  968. <Form.Item name="lng" label="经度" rules={[{required: true, message: "请输入经度"}]}>
  969. <Input placeholder="请输入经度" type="number" step="0.000001"/>
  970. </Form.Item>
  971. </Col>
  972. </Row>
  973. <Row gutter={16}>
  974. <Col span={12}>
  975. <Form.Item name="battery" label="电池电量(%)" rules={[{required: true, message: "请输入电池电量"}]}>
  976. <Input placeholder="请输入电池电量" type="number" min="0" max="100"/>
  977. </Form.Item>
  978. </Col>
  979. <Col span={12}>
  980. <Form.Item name="signal" label="信号强度(%)" rules={[{required: true, message: "请输入信号强度"}]}>
  981. <Input placeholder="请输入信号强度" type="number" min="0" max="100"/>
  982. </Form.Item>
  983. </Col>
  984. </Row>
  985. </Form>
  986. </Modal>
  987. </Layout>
  988. )
  989. }