AlarmPanel.tsx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. "use client"
  2. import {useState} from "react"
  3. import dynamic from "next/dynamic"
  4. import {
  5. Alert,
  6. Badge,
  7. Button,
  8. Card,
  9. Col,
  10. DatePicker,
  11. Form,
  12. Input,
  13. Modal,
  14. Row,
  15. Select,
  16. Space,
  17. Statistic,
  18. Table,
  19. Tabs,
  20. Upload,
  21. } from "antd"
  22. import {EditOutlined, ExportOutlined, EyeOutlined, SearchOutlined, UploadOutlined} from "@ant-design/icons"
  23. import globalMessage from "@/app/_modules/globalMessage";
  24. const MapView = dynamic(() => import("./MapView"), {
  25. ssr: false,
  26. loading: () => <div className="flex items-center justify-center h-full">地图加载中...</div>,
  27. })
  28. const { Option } = Select
  29. const { RangePicker } = DatePicker
  30. const { TabPane } = Tabs
  31. const { TextArea } = Input
  32. export default function AlarmPanel() {
  33. const [selectedTab, setSelectedTab] = useState("current-alarms")
  34. const [modalVisible, setModalVisible] = useState(false)
  35. const [selectedAlarm, setSelectedAlarm] = useState<any>(null)
  36. // 模拟报警数据
  37. const alarmData = [
  38. {
  39. key: "1",
  40. id: "AL001",
  41. time: "2024-01-15 14:25:30",
  42. device: "LV002",
  43. deviceName: "人民路积水点液位计",
  44. location: "人民路与南京路交叉口",
  45. level: "高",
  46. type: "液位超限",
  47. currentValue: "0.8m",
  48. threshold: "0.5m",
  49. status: "未处理",
  50. description: "液位持续超过报警阈值,可能存在积水风险",
  51. },
  52. {
  53. key: "2",
  54. id: "AL002",
  55. time: "2024-01-15 14:20:15",
  56. device: "FL001",
  57. deviceName: "主干道流量计",
  58. location: "淮海路主干管网",
  59. level: "中",
  60. type: "流量异常",
  61. currentValue: "3.2m³/s",
  62. threshold: "3.0m³/s",
  63. status: "处理中",
  64. description: "流量超过设计值,需要关注管网负荷情况",
  65. },
  66. {
  67. key: "3",
  68. id: "AL003",
  69. time: "2024-01-15 14:15:45",
  70. device: "PS001",
  71. deviceName: "第一泵站",
  72. location: "城东泵站",
  73. level: "高",
  74. type: "设备故障",
  75. currentValue: "停机",
  76. threshold: "正常运行",
  77. status: "已派单",
  78. description: "泵站突然停机,疑似电机故障",
  79. },
  80. ]
  81. const handleAlarmDetail = (record: any) => {
  82. setSelectedAlarm(record)
  83. setModalVisible(true)
  84. }
  85. const handleAlarmProcess = (record: any) => {
  86. globalMessage.success(`报警 ${record.id} 已开始处理`)
  87. }
  88. const columns = [
  89. { title: "报警编号", dataIndex: "id", key: "id", width: 100 },
  90. { title: "报警时间", dataIndex: "time", key: "time", width: 150 },
  91. { title: "设备编号", dataIndex: "device", key: "device", width: 100 },
  92. { title: "设备名称", dataIndex: "deviceName", key: "deviceName", width: 150 },
  93. { title: "位置", dataIndex: "location", key: "location", width: 150 },
  94. {
  95. title: "报警级别",
  96. dataIndex: "level",
  97. key: "level",
  98. width: 100,
  99. render: (level: string) => (
  100. <Badge color={level === "高" ? "red" : level === "中" ? "orange" : "blue"} text={level} />
  101. ),
  102. },
  103. { title: "报警类型", dataIndex: "type", key: "type", width: 100 },
  104. { title: "当前值", dataIndex: "currentValue", key: "currentValue", width: 100 },
  105. {
  106. title: "处理状态",
  107. dataIndex: "status",
  108. key: "status",
  109. width: 100,
  110. render: (status: string) => (
  111. <Badge status={status === "未处理" ? "error" : status === "处理中" ? "processing" : "success"} text={status} />
  112. ),
  113. },
  114. {
  115. title: "操作",
  116. key: "action",
  117. width: 200,
  118. render: (_, record) => (
  119. <Space>
  120. <Button size="small" icon={<EyeOutlined />} onClick={() => handleAlarmDetail(record)}>
  121. 详情
  122. </Button>
  123. <Button size="small" type="primary" icon={<EditOutlined />} onClick={() => handleAlarmProcess(record)}>
  124. 处理
  125. </Button>
  126. </Space>
  127. ),
  128. },
  129. ]
  130. return (
  131. <div className="space-y-6">
  132. {/* 统计概览 */}
  133. <Row gutter={[16, 16]}>
  134. <Col span={6}>
  135. <Card>
  136. <Statistic title="当前报警总数" value={23} valueStyle={{ color: "#cf1322" }} suffix="条" />
  137. </Card>
  138. </Col>
  139. <Col span={6}>
  140. <Card>
  141. <Statistic title="高级别报警" value={8} valueStyle={{ color: "#ff4d4f" }} suffix="条" />
  142. </Card>
  143. </Col>
  144. <Col span={6}>
  145. <Card>
  146. <Statistic title="今日处理" value={15} valueStyle={{ color: "#52c41a" }} suffix="条" />
  147. </Card>
  148. </Col>
  149. <Col span={6}>
  150. <Card>
  151. <Statistic title="处理率" value={87.5} valueStyle={{ color: "#1890ff" }} suffix="%" />
  152. </Card>
  153. </Col>
  154. </Row>
  155. {/* 滚动报警提醒 */}
  156. <Alert
  157. message="实时报警"
  158. description="【14:25:30】人民路积水点液位超限报警 | 【14:20:15】主干道流量异常 | 【14:15:45】第一泵站设备故障"
  159. type="error"
  160. showIcon
  161. closable
  162. />
  163. <Tabs activeKey={selectedTab} onChange={setSelectedTab}>
  164. <TabPane tab="监测报警" key="current-alarms">
  165. <Card title="报警清单">
  166. <div className="mb-4">
  167. <Space wrap>
  168. <Input placeholder="设备编号" prefix={<SearchOutlined />} style={{ width: 150 }} />
  169. <Select placeholder="报警级别" style={{ width: 120 }}>
  170. <Option value="high">高</Option>
  171. <Option value="medium">中</Option>
  172. <Option value="low">低</Option>
  173. </Select>
  174. <Select placeholder="处理状态" style={{ width: 120 }}>
  175. <Option value="unprocessed">未处理</Option>
  176. <Option value="processing">处理中</Option>
  177. <Option value="processed">已处理</Option>
  178. </Select>
  179. <RangePicker placeholder={["开始时间", "结束时间"]} />
  180. <Button type="primary" icon={<SearchOutlined />}>
  181. 查询
  182. </Button>
  183. <Button icon={<ExportOutlined />}>导出</Button>
  184. </Space>
  185. </div>
  186. <Table columns={columns} dataSource={alarmData} pagination={{ pageSize: 10 }} scroll={{ x: 1200 }} />
  187. </Card>
  188. </TabPane>
  189. <TabPane tab="报警地图" key="alarm-map">
  190. <Card title="当前报警 GIS " className="h-96">
  191. <MapView type="overview" />
  192. </Card>
  193. </TabPane>
  194. <TabPane tab="监测预警" key="warnings">
  195. <Card title="当前预警管理">
  196. <div className="space-y-4">
  197. <Alert
  198. message="管网运行风险预警"
  199. description="检测到3个区域管网负荷率超过85%,建议加强监控"
  200. type="warning"
  201. showIcon
  202. />
  203. <Alert
  204. message="积水内涝预警"
  205. description="气象部门发布暴雨预警,5个易积水点需重点关注"
  206. type="info"
  207. showIcon
  208. />
  209. </div>
  210. </Card>
  211. </TabPane>
  212. </Tabs>
  213. {/* 报警详情模态框 */}
  214. <Modal
  215. title="报警详情"
  216. open={modalVisible}
  217. onCancel={() => setModalVisible(false)}
  218. width={800}
  219. footer={[
  220. <Button key="close" onClick={() => setModalVisible(false)}>
  221. 关闭
  222. </Button>,
  223. <Button key="process" type="primary">
  224. 处理报警
  225. </Button>,
  226. ]}
  227. >
  228. {selectedAlarm && (
  229. <div className="space-y-4">
  230. <Row gutter={[16, 16]}>
  231. <Col span={12}>
  232. <div>
  233. <strong>报警编号:</strong> {selectedAlarm.id}
  234. </div>
  235. <div>
  236. <strong>报警时间:</strong> {selectedAlarm.time}
  237. </div>
  238. <div>
  239. <strong>设备编号:</strong> {selectedAlarm.device}
  240. </div>
  241. <div>
  242. <strong>设备名称:</strong> {selectedAlarm.deviceName}
  243. </div>
  244. </Col>
  245. <Col span={12}>
  246. <div>
  247. <strong>报警级别:</strong>{" "}
  248. <Badge color={selectedAlarm.level === "高" ? "red" : "orange"} text={selectedAlarm.level} />
  249. </div>
  250. <div>
  251. <strong>报警类型:</strong> {selectedAlarm.type}
  252. </div>
  253. <div>
  254. <strong>当前值:</strong> {selectedAlarm.currentValue}
  255. </div>
  256. <div>
  257. <strong>阈值:</strong> {selectedAlarm.threshold}
  258. </div>
  259. </Col>
  260. </Row>
  261. <div>
  262. <strong>位置信息:</strong> {selectedAlarm.location}
  263. </div>
  264. <div>
  265. <strong>报警描述:</strong> {selectedAlarm.description}
  266. </div>
  267. <Card title="处理记录" size="small">
  268. <Form layout="vertical">
  269. <Form.Item label="处理意见">
  270. <TextArea rows={3} placeholder="请输入处理意见..." />
  271. </Form.Item>
  272. <Form.Item label="附件上传">
  273. <Upload>
  274. <Button icon={<UploadOutlined />}>上传文件</Button>
  275. </Upload>
  276. </Form.Item>
  277. </Form>
  278. </Card>
  279. </div>
  280. )}
  281. </Modal>
  282. </div>
  283. )
  284. }