page.tsx 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. "use client"
  2. import {useState} from "react"
  3. import {
  4. App,
  5. Badge,
  6. Button,
  7. Card,
  8. Col,
  9. Form,
  10. Input,
  11. Modal,
  12. Row,
  13. Select,
  14. Space,
  15. Statistic,
  16. Table,
  17. Tag,
  18. Upload,
  19. } from "antd"
  20. import {
  21. ArrowDownOutlined,
  22. ArrowUpOutlined,
  23. CheckCircleOutlined,
  24. CloseCircleOutlined,
  25. EditOutlined,
  26. ExclamationCircleOutlined,
  27. ExportOutlined,
  28. EyeOutlined,
  29. PlusOutlined,
  30. SyncOutlined,
  31. UploadOutlined,
  32. } from "@ant-design/icons"
  33. import EChart from "@/components/echarts"
  34. import WaterMap from "./components/water-map"
  35. import AdminLayout from "./components/admin-layout"
  36. import type {EChartsCoreOption} from "echarts/core"
  37. import globalMessage from "@/app/_modules/globalMessage";
  38. const { Search } = Input
  39. const { Option } = Select
  40. export default function WaterSupplyMonitoring() {
  41. const [activeTab, setActiveTab] = useState("dashboard")
  42. const [modalVisible, setModalVisible] = useState(false)
  43. const [facilityDetailModal, setFacilityDetailModal] = useState(false)
  44. const [deviceDetailModal, setDeviceDetailModal] = useState(false)
  45. const [thresholdModal, setThresholdModal] = useState(false)
  46. const [exportModal, setExportModal] = useState(false)
  47. const [selectedRecord, setSelectedRecord] = useState<any>(null)
  48. const [loading, setLoading] = useState(false)
  49. const [form] = Form.useForm()
  50. const [thresholdForm] = Form.useForm()
  51. // 模拟数据
  52. const facilitiesData = [
  53. { key: "1", name: "湘江水源地", type: "水源地", location: "长沙市岳麓区", status: "正常", capacity: "10000m³/d" },
  54. { key: "2", name: "长沙第一水厂", type: "水厂", location: "长沙市芙蓉区", status: "正常", capacity: "50000m³/d" },
  55. { key: "3", name: "岳麓区增压泵站", type: "泵站", location: "长沙市岳麓区", status: "预警", capacity: "20000m³/d" },
  56. { key: "4", name: "五一大道主管网", type: "管网", location: "长沙市芙蓉区", status: "正常", length: "15km" },
  57. { key: "5", name: "中南大学", type: "大用户", location: "长沙市岳麓区", status: "报警", usage: "500m³/d" },
  58. ]
  59. const devicesData = [
  60. { key: "1", name: "流量计001", type: "流量计", location: "泵站A", status: "正常", lastMaintenance: "2024-01-15" },
  61. {
  62. key: "2",
  63. name: "压力计002",
  64. type: "压力计",
  65. location: "管网节点1",
  66. status: "故障",
  67. lastMaintenance: "2024-01-10",
  68. },
  69. {
  70. key: "3",
  71. name: "声波监测003",
  72. type: "漏失监测",
  73. location: "主管线",
  74. status: "正常",
  75. lastMaintenance: "2024-01-20",
  76. },
  77. {
  78. key: "4",
  79. name: "流量计004",
  80. type: "流量计",
  81. location: "水厂出口",
  82. status: "维修中",
  83. lastMaintenance: "2024-01-12",
  84. },
  85. ]
  86. const monitoringData = [
  87. { key: "1", point: "监测点001", type: "流量", value: "125.6 m³/h", status: "正常", time: "2024-01-25 14:30" },
  88. { key: "2", point: "监测点002", type: "压力", value: "0.45 MPa", status: "正常", time: "2024-01-25 14:30" },
  89. { key: "3", point: "监测点003", type: "漏失", value: "检测到异常", status: "报警", time: "2024-01-25 14:25" },
  90. { key: "4", point: "监测点004", type: "流量", value: "89.2 m³/h", status: "预警", time: "2024-01-25 14:30" },
  91. ]
  92. const alarmsData = [
  93. { key: "1", location: "增压泵站A", type: "压力异常", level: "中级", status: "待处理", time: "2024-01-25 14:25" },
  94. { key: "2", location: "大用户A", type: "流量超限", level: "高级", status: "处理中", time: "2024-01-25 14:20" },
  95. { key: "3", location: "主管网节点", type: "设备故障", level: "低级", status: "已处理", time: "2024-01-25 14:15" },
  96. ]
  97. // 图表配置
  98. const flowChartOption: EChartsCoreOption = {
  99. title: {
  100. text: "长沙市供水流量趋势",
  101. left: "center",
  102. textStyle: { fontSize: 16, fontWeight: "bold" },
  103. },
  104. tooltip: {
  105. trigger: "axis",
  106. backgroundColor: "rgba(0,0,0,0.8)",
  107. borderColor: "#1890ff",
  108. textStyle: { color: "#fff" },
  109. },
  110. legend: { data: ["湘江取水量", "供水总量", "用水需求"], bottom: 0 },
  111. xAxis: {
  112. type: "category",
  113. data: ["00:00", "04:00", "08:00", "12:00", "16:00", "20:00", "24:00"],
  114. axisLine: { lineStyle: { color: "#d9d9d9" } },
  115. },
  116. yAxis: {
  117. type: "value",
  118. name: "流量(万m³/h)",
  119. axisLine: { lineStyle: { color: "#d9d9d9" } },
  120. },
  121. series: [
  122. {
  123. name: "湘江取水量",
  124. type: "line",
  125. data: [120, 132, 101, 134, 90, 230, 210],
  126. smooth: true,
  127. itemStyle: { color: "#1890ff" },
  128. areaStyle: { opacity: 0.3 },
  129. animationDelay: 0,
  130. },
  131. {
  132. name: "供水总量",
  133. type: "line",
  134. data: [110, 125, 95, 128, 85, 220, 200],
  135. smooth: true,
  136. itemStyle: { color: "#52c41a" },
  137. areaStyle: { opacity: 0.3 },
  138. animationDelay: 300,
  139. },
  140. {
  141. name: "用水需求",
  142. type: "line",
  143. data: [100, 120, 90, 125, 80, 215, 195],
  144. smooth: true,
  145. itemStyle: { color: "#faad14" },
  146. areaStyle: { opacity: 0.3 },
  147. animationDelay: 600,
  148. },
  149. ],
  150. animationDuration: 2000,
  151. animationEasing: "cubicOut",
  152. }
  153. const pressureChartOption: EChartsCoreOption = {
  154. title: {
  155. text: "长沙市管网压力分布",
  156. left: "center",
  157. textStyle: { fontSize: 16, fontWeight: "bold" },
  158. },
  159. tooltip: {
  160. trigger: "item",
  161. formatter: "{a} <br/>{b}: {c} ({d}%)",
  162. },
  163. series: [
  164. {
  165. name: "压力分布",
  166. type: "pie",
  167. radius: ["40%", "70%"],
  168. center: ["50%", "60%"],
  169. data: [
  170. { value: 45, name: "正常压力", itemStyle: { color: "#52c41a" } },
  171. { value: 25, name: "低压预警", itemStyle: { color: "#faad14" } },
  172. { value: 15, name: "高压预警", itemStyle: { color: "#ff7a45" } },
  173. { value: 15, name: "压力异常", itemStyle: { color: "#ff4d4f" } },
  174. ],
  175. emphasis: {
  176. itemStyle: {
  177. shadowBlur: 10,
  178. shadowOffsetX: 0,
  179. shadowColor: "rgba(0, 0, 0, 0.5)",
  180. },
  181. },
  182. animationType: "scale",
  183. animationEasing: "elasticOut",
  184. animationDelay: (idx: number) => Math.random() * 200,
  185. },
  186. ],
  187. }
  188. const alarmTrendOption: EChartsCoreOption = {
  189. title: {
  190. text: "长沙市供水报警趋势统计",
  191. left: "center",
  192. textStyle: { fontSize: 16, fontWeight: "bold" },
  193. },
  194. tooltip: {
  195. trigger: "axis",
  196. backgroundColor: "rgba(0,0,0,0.8)",
  197. borderColor: "#1890ff",
  198. },
  199. legend: { data: ["高级报警", "中级报警", "低级报警"], bottom: 0 },
  200. xAxis: {
  201. type: "category",
  202. data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
  203. },
  204. yAxis: { type: "value", name: "报警次数" },
  205. series: [
  206. {
  207. name: "高级报警",
  208. type: "bar",
  209. data: [2, 1, 3, 2, 1, 0, 1],
  210. itemStyle: { color: "#ff4d4f" },
  211. animationDelay: (idx: number) => idx * 100,
  212. },
  213. {
  214. name: "中级报警",
  215. type: "bar",
  216. data: [5, 3, 4, 6, 3, 2, 4],
  217. itemStyle: { color: "#faad14" },
  218. animationDelay: (idx: number) => idx * 100 + 300,
  219. },
  220. {
  221. name: "低级报警",
  222. type: "bar",
  223. data: [8, 6, 7, 9, 5, 4, 6],
  224. itemStyle: { color: "#1890ff" },
  225. animationDelay: (idx: number) => idx * 100 + 600,
  226. },
  227. ],
  228. animationDuration: 1500,
  229. }
  230. // 实时监控仪表盘配置
  231. const gaugeOption: EChartsCoreOption = {
  232. series: [
  233. {
  234. name: "供水压力",
  235. type: "gauge",
  236. progress: {
  237. show: true,
  238. width: 18,
  239. },
  240. axisLine: {
  241. lineStyle: {
  242. width: 18,
  243. },
  244. },
  245. axisTick: {
  246. show: false,
  247. },
  248. splitLine: {
  249. length: 15,
  250. lineStyle: {
  251. width: 2,
  252. color: "#999",
  253. },
  254. },
  255. axisLabel: {
  256. distance: 25,
  257. color: "#999",
  258. fontSize: 12,
  259. },
  260. anchor: {
  261. show: true,
  262. showAbove: true,
  263. size: 25,
  264. itemStyle: {
  265. borderWidth: 10,
  266. },
  267. },
  268. title: {
  269. show: false,
  270. },
  271. detail: {
  272. valueAnimation: true,
  273. fontSize: 20,
  274. offsetCenter: [0, "70%"],
  275. },
  276. data: [
  277. {
  278. value: 0.65,
  279. name: "MPa",
  280. },
  281. ],
  282. },
  283. ],
  284. }
  285. // 仪表盘数据
  286. const dashboardStats = {
  287. totalFacilities: 156,
  288. normalFacilities: 142,
  289. warningFacilities: 8,
  290. alarmFacilities: 6,
  291. totalDevices: 324,
  292. onlineDevices: 298,
  293. offlineDevices: 26,
  294. todayAlarms: 12,
  295. processedAlarms: 9,
  296. pendingAlarms: 3,
  297. }
  298. const handleExport = (type: string) => {
  299. setLoading(true)
  300. setTimeout(() => {
  301. globalMessage.success(`${type}数据导出成功!`)
  302. setLoading(false)
  303. setExportModal(false)
  304. }, 2000)
  305. }
  306. const handleRefresh = () => {
  307. setLoading(true)
  308. setTimeout(() => {
  309. globalMessage.success("数据刷新成功!")
  310. setLoading(false)
  311. }, 1500)
  312. }
  313. const handleViewDetail = (record: any, type: string) => {
  314. setSelectedRecord(record)
  315. if (type === "facility") {
  316. setFacilityDetailModal(true)
  317. } else if (type === "device") {
  318. setDeviceDetailModal(true)
  319. }
  320. }
  321. const handleProcessAlarm = (record: any) => {
  322. setSelectedRecord(record)
  323. setModalVisible(true)
  324. }
  325. const handleThresholdSetting = () => {
  326. setThresholdModal(true)
  327. }
  328. const renderDashboard = () => (
  329. <div className="space-y-6">
  330. {/* 系统概览仪表盘 */}
  331. <Row gutter={[16, 16]}>
  332. <Col xs={24} sm={12} md={6}>
  333. <Card className="hover:shadow-lg transition-shadow duration-300">
  334. <Statistic
  335. title="供水设施总数"
  336. value={dashboardStats.totalFacilities}
  337. prefix={<CheckCircleOutlined style={{ color: "#1890ff" }} />}
  338. suffix="个"
  339. valueStyle={{ color: "#1890ff", fontSize: "24px", fontWeight: "bold" }}
  340. />
  341. </Card>
  342. </Col>
  343. <Col xs={24} sm={12} md={6}>
  344. <Card className="hover:shadow-lg transition-shadow duration-300">
  345. <Statistic
  346. title="正常运行"
  347. value={dashboardStats.normalFacilities}
  348. prefix={<CheckCircleOutlined style={{ color: "#52c41a" }} />}
  349. suffix="个"
  350. valueStyle={{ color: "#52c41a", fontSize: "24px", fontWeight: "bold" }}
  351. />
  352. </Card>
  353. </Col>
  354. <Col xs={24} sm={12} md={6}>
  355. <Card className="hover:shadow-lg transition-shadow duration-300">
  356. <Statistic
  357. title="预警设施"
  358. value={dashboardStats.warningFacilities}
  359. prefix={<ExclamationCircleOutlined style={{ color: "#faad14" }} />}
  360. suffix="个"
  361. valueStyle={{ color: "#faad14", fontSize: "24px", fontWeight: "bold" }}
  362. />
  363. </Card>
  364. </Col>
  365. <Col xs={24} sm={12} md={6}>
  366. <Card className="hover:shadow-lg transition-shadow duration-300">
  367. <Statistic
  368. title="报警设施"
  369. value={dashboardStats.alarmFacilities}
  370. prefix={<CloseCircleOutlined style={{ color: "#ff4d4f" }} />}
  371. suffix="个"
  372. valueStyle={{ color: "#ff4d4f", fontSize: "24px", fontWeight: "bold" }}
  373. />
  374. </Card>
  375. </Col>
  376. </Row>
  377. {/* 实时监控数据 */}
  378. <Row gutter={[16, 16]}>
  379. <Col xs={24} lg={16}>
  380. <Card title="长沙市供水管网实时监控地图" className="hover:shadow-lg transition-shadow duration-300">
  381. <div className="h-96">
  382. <WaterMap />
  383. </div>
  384. </Card>
  385. </Col>
  386. <Col xs={24} lg={8}>
  387. <Card title="实时供水压力" className="hover:shadow-lg transition-shadow duration-300">
  388. <EChart option={gaugeOption} style={{ height: 300 }} />
  389. </Card>
  390. </Col>
  391. </Row>
  392. {/* 图表展示 */}
  393. <Row gutter={[16, 16]}>
  394. <Col xs={24} lg={12}>
  395. <Card title="供水流量趋势" className="hover:shadow-lg transition-shadow duration-300">
  396. <EChart option={flowChartOption} />
  397. </Card>
  398. </Col>
  399. <Col xs={24} lg={12}>
  400. <Card title="管网压力分布" className="hover:shadow-lg transition-shadow duration-300">
  401. <EChart option={pressureChartOption} />
  402. </Card>
  403. </Col>
  404. </Row>
  405. <Card title="报警趋势统计" className="hover:shadow-lg transition-shadow duration-300">
  406. <EChart option={alarmTrendOption} />
  407. </Card>
  408. </div>
  409. )
  410. const renderContent = () => {
  411. switch (activeTab) {
  412. case "dashboard":
  413. return renderDashboard()
  414. case "facilities":
  415. return (
  416. <div className="space-y-6">
  417. {/* 统计卡片 */}
  418. <Row gutter={[16, 16]}>
  419. <Col xs={24} sm={12} md={6}>
  420. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  421. <Statistic
  422. title="供水设施总数"
  423. value={25}
  424. prefix={<CheckCircleOutlined style={{ color: "#1890ff" }} />}
  425. valueStyle={{ color: "#1890ff" }}
  426. />
  427. </Card>
  428. </Col>
  429. <Col xs={24} sm={12} md={6}>
  430. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  431. <Statistic
  432. title="正常运行"
  433. value={22}
  434. prefix={<ArrowUpOutlined />}
  435. valueStyle={{ color: "#52c41a" }}
  436. />
  437. </Card>
  438. </Col>
  439. <Col xs={24} sm={12} md={6}>
  440. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  441. <Statistic
  442. title="预警状态"
  443. value={2}
  444. prefix={<ExclamationCircleOutlined />}
  445. valueStyle={{ color: "#faad14" }}
  446. />
  447. </Card>
  448. </Col>
  449. <Col xs={24} sm={12} md={6}>
  450. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  451. <Statistic
  452. title="报警状态"
  453. value={1}
  454. prefix={<ArrowDownOutlined />}
  455. valueStyle={{ color: "#ff4d4f" }}
  456. />
  457. </Card>
  458. </Col>
  459. </Row>
  460. {/* 图表展示 */}
  461. <Row gutter={[16, 16]}>
  462. <Col xs={24} lg={12}>
  463. <Card title="供水流量趋势" className="hover:shadow-lg transition-shadow duration-300">
  464. <EChart option={flowChartOption} />
  465. </Card>
  466. </Col>
  467. <Col xs={24} lg={12}>
  468. <Card title="管网压力分布" className="hover:shadow-lg transition-shadow duration-300">
  469. <EChart option={pressureChartOption} />
  470. </Card>
  471. </Col>
  472. </Row>
  473. {/* 供水设施管理表格 */}
  474. <Card
  475. title="供水设施管理"
  476. className="hover:shadow-lg transition-shadow duration-300"
  477. extra={
  478. <Space>
  479. <Search placeholder="搜索设施" style={{ width: 200 }} />
  480. <Select defaultValue="all" style={{ width: 120 }}>
  481. <Option value="all">全部类型</Option>
  482. <Option value="水源地">水源地</Option>
  483. <Option value="水厂">水厂</Option>
  484. <Option value="泵站">泵站</Option>
  485. <Option value="管网">管网</Option>
  486. <Option value="大用户">大用户</Option>
  487. </Select>
  488. <Button
  489. type="primary"
  490. icon={<ExportOutlined />}
  491. onClick={() => setExportModal(true)}
  492. className="hover:scale-105 transition-transform duration-200"
  493. >
  494. 批量导出
  495. </Button>
  496. <Button
  497. icon={<SyncOutlined />}
  498. onClick={handleRefresh}
  499. loading={loading}
  500. className="hover:scale-105 transition-transform duration-200"
  501. >
  502. 刷新数据
  503. </Button>
  504. </Space>
  505. }
  506. >
  507. <Table
  508. columns={[
  509. { title: "设施名称", dataIndex: "name", key: "name" },
  510. { title: "设施类型", dataIndex: "type", key: "type" },
  511. { title: "位置", dataIndex: "location", key: "location" },
  512. {
  513. title: "运行状态",
  514. dataIndex: "status",
  515. key: "status",
  516. render: (status: string) => (
  517. <Badge
  518. status={status === "正常" ? "success" : status === "预警" ? "warning" : "error"}
  519. text={status}
  520. />
  521. ),
  522. },
  523. { title: "容量/长度", dataIndex: "capacity", key: "capacity" },
  524. {
  525. title: "操作",
  526. key: "action",
  527. render: (_, record) => (
  528. <Space>
  529. <Button
  530. size="small"
  531. icon={<EyeOutlined />}
  532. onClick={() => handleViewDetail(record, "facility")}
  533. className="hover:scale-110 transition-transform duration-200"
  534. >
  535. 详情
  536. </Button>
  537. <Button
  538. size="small"
  539. icon={<EditOutlined />}
  540. onClick={() => globalMessage.info("编辑功能开发中...")}
  541. className="hover:scale-110 transition-transform duration-200"
  542. >
  543. 编辑
  544. </Button>
  545. </Space>
  546. ),
  547. },
  548. ]}
  549. dataSource={facilitiesData}
  550. pagination={{ pageSize: 10 }}
  551. loading={loading}
  552. />
  553. </Card>
  554. {/* GIS一图展示 */}
  555. <Card title="供水设施一图展示" className="hover:shadow-lg transition-shadow duration-300">
  556. <div className="h-96">
  557. <WaterMap />
  558. </div>
  559. </Card>
  560. </div>
  561. )
  562. case "devices":
  563. return (
  564. <div className="space-y-6">
  565. {/* 设备统计 */}
  566. <Row gutter={[16, 16]}>
  567. <Col xs={24} sm={12} md={6}>
  568. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  569. <Statistic
  570. title="监测设备总数"
  571. value={dashboardStats.totalDevices}
  572. prefix={<CheckCircleOutlined style={{ color: "#1890ff" }} />}
  573. valueStyle={{ color: "#1890ff" }}
  574. />
  575. </Card>
  576. </Col>
  577. <Col xs={24} sm={12} md={6}>
  578. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  579. <Statistic
  580. title="在线设备"
  581. value={dashboardStats.onlineDevices}
  582. prefix={<CheckCircleOutlined style={{ color: "#52c41a" }} />}
  583. valueStyle={{ color: "#52c41a" }}
  584. />
  585. </Card>
  586. </Col>
  587. <Col xs={24} sm={12} md={6}>
  588. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  589. <Statistic
  590. title="离线设备"
  591. value={dashboardStats.offlineDevices}
  592. prefix={<CloseCircleOutlined style={{ color: "#ff4d4f" }} />}
  593. valueStyle={{ color: "#ff4d4f" }}
  594. />
  595. </Card>
  596. </Col>
  597. <Col xs={24} sm={12} md={6}>
  598. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  599. <Statistic
  600. title="故障设备"
  601. value={8}
  602. prefix={<ExclamationCircleOutlined style={{ color: "#faad14" }} />}
  603. valueStyle={{ color: "#faad14" }}
  604. />
  605. </Card>
  606. </Col>
  607. </Row>
  608. {/* 监测设备管理表格 */}
  609. <Card
  610. title="监测设备管理"
  611. className="hover:shadow-lg transition-shadow duration-300"
  612. extra={
  613. <Space>
  614. <Search placeholder="搜索设备" style={{ width: 200 }} />
  615. <Select defaultValue="all" style={{ width: 120 }}>
  616. <Option value="all">全部类型</Option>
  617. <Option value="流量计">流量计</Option>
  618. <Option value="压力计">压力计</Option>
  619. <Option value="漏失监测">漏失监测</Option>
  620. </Select>
  621. <Button
  622. type="primary"
  623. icon={<PlusOutlined />}
  624. onClick={() => globalMessage.info("添加设备功能开发中...")}
  625. className="hover:scale-105 transition-transform duration-200"
  626. >
  627. 添加设备
  628. </Button>
  629. <Button
  630. icon={<ExportOutlined />}
  631. onClick={() => setExportModal(true)}
  632. className="hover:scale-105 transition-transform duration-200"
  633. >
  634. 导出
  635. </Button>
  636. </Space>
  637. }
  638. >
  639. <Table
  640. columns={[
  641. { title: "设备名称", dataIndex: "name", key: "name" },
  642. { title: "设备类型", dataIndex: "type", key: "type" },
  643. { title: "安装位置", dataIndex: "location", key: "location" },
  644. {
  645. title: "运行状态",
  646. dataIndex: "status",
  647. key: "status",
  648. render: (status: string) => (
  649. <Badge
  650. status={
  651. status === "正常"
  652. ? "success"
  653. : status === "故障"
  654. ? "error"
  655. : status === "维修中"
  656. ? "processing"
  657. : "default"
  658. }
  659. text={status}
  660. />
  661. ),
  662. },
  663. { title: "最后维护", dataIndex: "lastMaintenance", key: "lastMaintenance" },
  664. {
  665. title: "操作",
  666. key: "action",
  667. render: (_, record) => (
  668. <Space>
  669. <Button
  670. size="small"
  671. icon={<EyeOutlined />}
  672. onClick={() => handleViewDetail(record, "device")}
  673. className="hover:scale-110 transition-transform duration-200"
  674. >
  675. 详情
  676. </Button>
  677. {record.status === "故障" && (
  678. <Button
  679. size="small"
  680. type="primary"
  681. onClick={() => globalMessage.success("维修派单已生成!")}
  682. className="hover:scale-110 transition-transform duration-200"
  683. >
  684. 派单
  685. </Button>
  686. )}
  687. </Space>
  688. ),
  689. },
  690. ]}
  691. dataSource={devicesData}
  692. pagination={{ pageSize: 10 }}
  693. loading={loading}
  694. />
  695. </Card>
  696. </div>
  697. )
  698. case "monitoring":
  699. return (
  700. <div className="space-y-6">
  701. {/* 运行监测表格 */}
  702. <Card
  703. title="运行监测数据"
  704. className="hover:shadow-lg transition-shadow duration-300"
  705. extra={
  706. <Space>
  707. <Search placeholder="搜索监测点" style={{ width: 200 }} />
  708. <Select defaultValue="all" style={{ width: 120 }}>
  709. <Option value="all">全部类型</Option>
  710. <Option value="流量">流量</Option>
  711. <Option value="压力">压力</Option>
  712. <Option value="漏失">漏失</Option>
  713. </Select>
  714. <Button
  715. icon={<SyncOutlined />}
  716. onClick={handleRefresh}
  717. loading={loading}
  718. className="hover:scale-105 transition-transform duration-200"
  719. >
  720. 实时刷新
  721. </Button>
  722. <Button
  723. icon={<ExportOutlined />}
  724. onClick={() => setExportModal(true)}
  725. className="hover:scale-105 transition-transform duration-200"
  726. >
  727. 导出数据
  728. </Button>
  729. </Space>
  730. }
  731. >
  732. <Table
  733. columns={[
  734. { title: "监测点位", dataIndex: "point", key: "point" },
  735. { title: "监测类型", dataIndex: "type", key: "type" },
  736. { title: "监测值", dataIndex: "value", key: "value" },
  737. {
  738. title: "状态",
  739. dataIndex: "status",
  740. key: "status",
  741. render: (status: string) => (
  742. <Badge
  743. status={status === "正常" ? "success" : status === "预警" ? "warning" : "error"}
  744. text={status}
  745. />
  746. ),
  747. },
  748. { title: "更新时间", dataIndex: "time", key: "time" },
  749. {
  750. title: "操作",
  751. key: "action",
  752. render: (_, record) => (
  753. <Space>
  754. <Button
  755. size="small"
  756. icon={<EyeOutlined />}
  757. onClick={() => globalMessage.info(`查看${record.point}历史数据`)}
  758. className="hover:scale-110 transition-transform duration-200"
  759. >
  760. 历史
  761. </Button>
  762. <Button
  763. size="small"
  764. onClick={() => globalMessage.info(`定位到${record.point}`)}
  765. className="hover:scale-110 transition-transform duration-200"
  766. >
  767. 定位
  768. </Button>
  769. </Space>
  770. ),
  771. },
  772. ]}
  773. dataSource={monitoringData}
  774. pagination={{ pageSize: 10 }}
  775. loading={loading}
  776. />
  777. </Card>
  778. </div>
  779. )
  780. case "alarms":
  781. return (
  782. <div className="space-y-6">
  783. {/* 报警统计 */}
  784. <Row gutter={[16, 16]}>
  785. <Col xs={24} sm={12} md={6}>
  786. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  787. <Statistic
  788. title="今日报警总数"
  789. value={dashboardStats.todayAlarms}
  790. prefix={<CloseCircleOutlined style={{ color: "#ff4d4f" }} />}
  791. valueStyle={{ color: "#ff4d4f" }}
  792. />
  793. </Card>
  794. </Col>
  795. <Col xs={24} sm={12} md={6}>
  796. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  797. <Statistic
  798. title="高级报警"
  799. value={dashboardStats.processedAlarms}
  800. prefix={<CloseCircleOutlined style={{ color: "#ff4d4f" }} />}
  801. valueStyle={{ color: "#ff4d4f" }}
  802. />
  803. </Card>
  804. </Col>
  805. <Col xs={24} sm={12} md={6}>
  806. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  807. <Statistic
  808. title="中级报警"
  809. value={dashboardStats.pendingAlarms}
  810. prefix={<ExclamationCircleOutlined style={{ color: "#faad14" }} />}
  811. valueStyle={{ color: "#faad14" }}
  812. />
  813. </Card>
  814. </Col>
  815. <Col xs={24} sm={12} md={6}>
  816. <Card className="hover:shadow-lg transition-all duration-300 hover:scale-105">
  817. <Statistic
  818. title="低级报警"
  819. value={3} // 假设低级报警数量为3
  820. prefix={<CheckCircleOutlined style={{ color: "#1890ff" }} />}
  821. valueStyle={{ color: "#1890ff" }}
  822. />
  823. </Card>
  824. </Col>
  825. </Row>
  826. {/* 报警趋势图 */}
  827. <Card title="报警趋势统计" className="hover:shadow-lg transition-shadow duration-300">
  828. <EChart option={alarmTrendOption} />
  829. </Card>
  830. {/* 报警管理表格 */}
  831. <Card
  832. title="报警事件管理"
  833. className="hover:shadow-lg transition-shadow duration-300"
  834. extra={
  835. <Space>
  836. <Search placeholder="搜索报警" style={{ width: 200 }} />
  837. <Select defaultValue="all" style={{ width: 120 }}>
  838. <Option value="all">全部级别</Option>
  839. <Option value="high">高级</Option>
  840. <Option value="medium">中级</Option>
  841. <Option value="low">低级</Option>
  842. </Select>
  843. <Button
  844. type="primary"
  845. onClick={() => globalMessage.info("批量处理功能开发中...")}
  846. className="hover:scale-105 transition-transform duration-200"
  847. >
  848. 批量处理
  849. </Button>
  850. <Button
  851. onClick={handleThresholdSetting}
  852. className="hover:scale-105 transition-transform duration-200"
  853. >
  854. 阈值设置
  855. </Button>
  856. </Space>
  857. }
  858. >
  859. <Table
  860. columns={[
  861. { title: "报警位置", dataIndex: "location", key: "location" },
  862. { title: "报警类型", dataIndex: "type", key: "type" },
  863. {
  864. title: "报警级别",
  865. dataIndex: "level",
  866. key: "level",
  867. render: (level: string) => (
  868. <Tag color={level === "高级" ? "red" : level === "中级" ? "orange" : "blue"}>{level}</Tag>
  869. ),
  870. },
  871. {
  872. title: "处理状态",
  873. dataIndex: "status",
  874. key: "status",
  875. render: (status: string) => (
  876. <Badge
  877. status={status === "已处理" ? "success" : status === "处理中" ? "processing" : "default"}
  878. text={status}
  879. />
  880. ),
  881. },
  882. { title: "报警时间", dataIndex: "time", key: "time" },
  883. {
  884. title: "操作",
  885. key: "action",
  886. render: (_, record) => (
  887. <Space>
  888. <Button
  889. size="small"
  890. type="primary"
  891. onClick={() => handleProcessAlarm(record)}
  892. disabled={record.status === "已处理"}
  893. className="hover:scale-110 transition-transform duration-200"
  894. >
  895. 处理
  896. </Button>
  897. <Button
  898. size="small"
  899. onClick={() => handleViewDetail(record, "alarm")}
  900. className="hover:scale-110 transition-transform duration-200"
  901. >
  902. 详情
  903. </Button>
  904. </Space>
  905. ),
  906. },
  907. ]}
  908. dataSource={alarmsData}
  909. pagination={{ pageSize: 10 }}
  910. loading={loading}
  911. />
  912. </Card>
  913. {/* 报警一张图 */}
  914. <Card title="监测报警一张图" className="hover:shadow-lg transition-shadow duration-300">
  915. <div className="h-96">
  916. <WaterMap
  917. alarms={[
  918. { id: "1", position: [39.9242, 116.4274], level: "medium", message: "压力异常" },
  919. { id: "2", position: [39.9442, 116.4474], level: "high", message: "流量超限" },
  920. ]}
  921. />
  922. </div>
  923. </Card>
  924. </div>
  925. )
  926. default:
  927. return <div className="text-center py-20 text-gray-500">功能开发中...</div>
  928. }
  929. }
  930. return (
  931. <App className={'h-min-[100vh]'}>
  932. <AdminLayout activeKey={activeTab} onMenuSelect={setActiveTab}>
  933. {renderContent()}
  934. <Modal
  935. title="设施详细信息"
  936. open={facilityDetailModal}
  937. onCancel={() => setFacilityDetailModal(false)}
  938. footer={[
  939. <Button key="close" onClick={() => setFacilityDetailModal(false)}>
  940. 关闭
  941. </Button>,
  942. ]}
  943. width={600}
  944. >
  945. {selectedRecord && (
  946. <div className="space-y-4">
  947. <Row gutter={[16, 16]}>
  948. <Col span={12}>
  949. <div>
  950. <strong>设施名称:</strong>
  951. {selectedRecord.name}
  952. </div>
  953. </Col>
  954. <Col span={12}>
  955. <div>
  956. <strong>设施类型:</strong>
  957. {selectedRecord.type}
  958. </div>
  959. </Col>
  960. <Col span={12}>
  961. <div>
  962. <strong>位置:</strong>
  963. {selectedRecord.location}
  964. </div>
  965. </Col>
  966. <Col span={12}>
  967. <div>
  968. <strong>运行状态:</strong>
  969. <Badge
  970. status={
  971. selectedRecord.status === "正常"
  972. ? "success"
  973. : selectedRecord.status === "预警"
  974. ? "warning"
  975. : "error"
  976. }
  977. text={selectedRecord.status}
  978. />
  979. </div>
  980. </Col>
  981. <Col span={24}>
  982. <div>
  983. <strong>容量/规模:</strong>
  984. {selectedRecord.capacity || selectedRecord.length || selectedRecord.usage}
  985. </div>
  986. </Col>
  987. </Row>
  988. </div>
  989. )}
  990. </Modal>
  991. <Modal
  992. title="设备详细信息"
  993. open={deviceDetailModal}
  994. onCancel={() => setDeviceDetailModal(false)}
  995. footer={[
  996. <Button key="close" onClick={() => setDeviceDetailModal(false)}>
  997. 关闭
  998. </Button>,
  999. ]}
  1000. width={600}
  1001. >
  1002. {selectedRecord && (
  1003. <div className="space-y-4">
  1004. <Row gutter={[16, 16]}>
  1005. <Col span={12}>
  1006. <div>
  1007. <strong>设备名称:</strong>
  1008. {selectedRecord.name}
  1009. </div>
  1010. </Col>
  1011. <Col span={12}>
  1012. <div>
  1013. <strong>设备类型:</strong>
  1014. {selectedRecord.type}
  1015. </div>
  1016. </Col>
  1017. <Col span={12}>
  1018. <div>
  1019. <strong>安装位置:</strong>
  1020. {selectedRecord.location}
  1021. </div>
  1022. </Col>
  1023. <Col span={12}>
  1024. <div>
  1025. <strong>运行状态:</strong>
  1026. <Badge
  1027. status={
  1028. selectedRecord.status === "正常"
  1029. ? "success"
  1030. : selectedRecord.status === "故障"
  1031. ? "error"
  1032. : selectedRecord.status === "维修中"
  1033. ? "processing"
  1034. : "default"
  1035. }
  1036. text={selectedRecord.status}
  1037. />
  1038. </div>
  1039. </Col>
  1040. <Col span={24}>
  1041. <div>
  1042. <strong>最后维护时间:</strong>
  1043. {selectedRecord.lastMaintenance}
  1044. </div>
  1045. </Col>
  1046. </Row>
  1047. </div>
  1048. )}
  1049. </Modal>
  1050. <Modal
  1051. title="阈值设置"
  1052. open={thresholdModal}
  1053. onOk={() => {
  1054. thresholdForm.validateFields().then(() => {
  1055. globalMessage.success("阈值设置成功")
  1056. setThresholdModal(false)
  1057. thresholdForm.resetFields()
  1058. })
  1059. }}
  1060. onCancel={() => {
  1061. setThresholdModal(false)
  1062. thresholdForm.resetFields()
  1063. }}
  1064. width={600}
  1065. >
  1066. <Form form={thresholdForm} layout="vertical">
  1067. <Form.Item label="监测类型" name="type" rules={[{ required: true }]}>
  1068. <Select>
  1069. <Option value="flow">流量监测</Option>
  1070. <Option value="pressure">压力监测</Option>
  1071. <Option value="leak">漏失监测</Option>
  1072. </Select>
  1073. </Form.Item>
  1074. <Row gutter={16}>
  1075. <Col span={12}>
  1076. <Form.Item label="预警阈值" name="warningThreshold" rules={[{ required: true }]}>
  1077. <Input placeholder="请输入预警阈值" />
  1078. </Form.Item>
  1079. </Col>
  1080. <Col span={12}>
  1081. <Form.Item label="报警阈值" name="alarmThreshold" rules={[{ required: true }]}>
  1082. <Input placeholder="请输入报警阈值" />
  1083. </Form.Item>
  1084. </Col>
  1085. </Row>
  1086. <Form.Item label="生效时间段" name="timeRange">
  1087. <Select>
  1088. <Option value="all">全天</Option>
  1089. <Option value="peak">高峰时段</Option>
  1090. <Option value="normal">正常时段</Option>
  1091. </Select>
  1092. </Form.Item>
  1093. </Form>
  1094. </Modal>
  1095. <Modal
  1096. title="数据导出"
  1097. open={exportModal}
  1098. onCancel={() => setExportModal(false)}
  1099. footer={[
  1100. <Button key="cancel" onClick={() => setExportModal(false)}>
  1101. 取消
  1102. </Button>,
  1103. <Button key="export" type="primary" loading={loading} onClick={() => handleExport("当前页面")}>
  1104. 确认导出
  1105. </Button>,
  1106. ]}
  1107. >
  1108. <div className="space-y-4">
  1109. <div>
  1110. <strong>导出格式:</strong>
  1111. <Select defaultValue="excel" style={{ width: 120, marginLeft: 8 }}>
  1112. <Option value="excel">Excel</Option>
  1113. <Option value="csv">CSV</Option>
  1114. <Option value="pdf">PDF</Option>
  1115. </Select>
  1116. </div>
  1117. <div>
  1118. <strong>导出范围:</strong>
  1119. <Select defaultValue="current" style={{ width: 150, marginLeft: 8 }}>
  1120. <Option value="current">当前页面</Option>
  1121. <Option value="all">全部数据</Option>
  1122. <Option value="selected">选中数据</Option>
  1123. </Select>
  1124. </div>
  1125. </div>
  1126. </Modal>
  1127. {/* 处理报警模态框 */}
  1128. <Modal
  1129. title="处理报警事件"
  1130. open={modalVisible}
  1131. onOk={() => {
  1132. form.validateFields().then(() => {
  1133. globalMessage.success("报警处理成功")
  1134. setModalVisible(false)
  1135. form.resetFields()
  1136. })
  1137. }}
  1138. onCancel={() => {
  1139. setModalVisible(false)
  1140. form.resetFields()
  1141. }}
  1142. className="hover:shadow-2xl transition-shadow duration-300"
  1143. >
  1144. <Form form={form} layout="vertical">
  1145. <Form.Item label="处理结果" name="result" rules={[{ required: true }]}>
  1146. <Select>
  1147. <Option value="resolved">已解决</Option>
  1148. <Option value="false_alarm">误报</Option>
  1149. <Option value="pending">待进一步处理</Option>
  1150. </Select>
  1151. </Form.Item>
  1152. <Form.Item label="处理意见" name="comment">
  1153. <Input.TextArea rows={4} placeholder="请输入处理意见..." />
  1154. </Form.Item>
  1155. <Form.Item label="附件上传" name="attachment">
  1156. <Upload>
  1157. <Button icon={<UploadOutlined />}>上传附件</Button>
  1158. </Upload>
  1159. </Form.Item>
  1160. </Form>
  1161. </Modal>
  1162. </AdminLayout>
  1163. </App>
  1164. )
  1165. }