page.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. "use client"
  2. import {useState} from "react"
  3. import {
  4. Alert,
  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. EditOutlined,
  28. EnvironmentOutlined,
  29. EyeOutlined,
  30. PlusOutlined,
  31. SettingOutlined,
  32. } from "@ant-design/icons"
  33. import dynamic from "next/dynamic"
  34. import EChart from "@/components/echarts"
  35. const { Header, Sider, Content } = Layout
  36. const { TabPane } = Tabs
  37. const { Option } = Select
  38. // 动态导入地图组件避免SSR问题
  39. const MapView = dynamic(() => import("./components/map-view"), { ssr: false })
  40. export default function ManholeMonitoringSystem() {
  41. const [selectedMenu, setSelectedMenu] = useState("dashboard")
  42. const [deviceModalVisible, setDeviceModalVisible] = useState(false)
  43. const [selectedDevice, setSelectedDevice] = useState(null)
  44. const [addDeviceModalVisible, setAddDeviceModalVisible] = useState(false)
  45. const [editDeviceModalVisible, setEditDeviceModalVisible] = useState(false)
  46. const [editingDevice, setEditingDevice] = useState(null)
  47. const [devices, setDevices] = useState([
  48. {
  49. id: 1,
  50. name: "设备001",
  51. type: "智能井盖",
  52. status: "normal",
  53. location: "朝阳区建国路",
  54. lat: 39.9042,
  55. lng: 116.4074,
  56. battery: 85,
  57. signal: 90,
  58. },
  59. {
  60. id: 2,
  61. name: "设备002",
  62. type: "智能井盖",
  63. status: "warning",
  64. location: "海淀区中关村",
  65. lat: 39.9826,
  66. lng: 116.3186,
  67. battery: 45,
  68. signal: 75,
  69. },
  70. {
  71. id: 3,
  72. name: "设备003",
  73. type: "智能井盖",
  74. status: "error",
  75. location: "西城区金融街",
  76. lat: 39.926,
  77. lng: 116.3663,
  78. battery: 20,
  79. signal: 60,
  80. },
  81. {
  82. id: 4,
  83. name: "设备004",
  84. type: "智能井盖",
  85. status: "normal",
  86. location: "东城区王府井",
  87. lat: 39.9097,
  88. lng: 116.4109,
  89. battery: 92,
  90. signal: 95,
  91. },
  92. {
  93. id: 5,
  94. name: "设备005",
  95. type: "智能井盖",
  96. status: "maintenance",
  97. location: "丰台区丽泽",
  98. lat: 39.8584,
  99. lng: 116.3135,
  100. battery: 78,
  101. signal: 80,
  102. },
  103. ])
  104. const [alerts, setAlerts] = useState([
  105. {
  106. id: 1,
  107. deviceName: "设备002",
  108. type: "位移异常",
  109. level: "warning",
  110. time: "2024-01-15 14:30:25",
  111. location: "海淀区中关村",
  112. status: "pending", // pending, processed, ignored
  113. },
  114. {
  115. id: 2,
  116. deviceName: "设备003",
  117. type: "电池电压低",
  118. level: "error",
  119. time: "2024-01-15 14:25:10",
  120. location: "西城区金融街",
  121. status: "pending",
  122. },
  123. {
  124. id: 3,
  125. deviceName: "设备001",
  126. type: "震动异常",
  127. level: "info",
  128. time: "2024-01-15 14:20:15",
  129. location: "朝阳区建国路",
  130. status: "processed",
  131. },
  132. ])
  133. const [addDeviceForm] = Form.useForm()
  134. const [editDeviceForm] = Form.useForm()
  135. const handleAddDevice = (values: any) => {
  136. const newDevice = {
  137. id: devices.length + 1,
  138. name: values.name,
  139. type: values.type,
  140. status: values.status,
  141. location: values.location,
  142. lat: Number.parseFloat(values.lat),
  143. lng: Number.parseFloat(values.lng),
  144. battery: Number.parseInt(values.battery),
  145. signal: Number.parseInt(values.signal),
  146. }
  147. setDevices([...devices, newDevice])
  148. setAddDeviceModalVisible(false)
  149. addDeviceForm.resetFields()
  150. message.success("设备添加成功!")
  151. }
  152. const handleEditDevice = (values: any) => {
  153. const updatedDevices = devices.map((device) =>
  154. device.id === editingDevice.id
  155. ? {
  156. ...device,
  157. ...values,
  158. lat: Number.parseFloat(values.lat),
  159. lng: Number.parseFloat(values.lng),
  160. battery: Number.parseInt(values.battery),
  161. signal: Number.parseInt(values.signal),
  162. }
  163. : device,
  164. )
  165. setDevices(updatedDevices)
  166. setEditDeviceModalVisible(false)
  167. setEditingDevice(null)
  168. editDeviceForm.resetFields()
  169. message.success("设备信息更新成功!")
  170. }
  171. const openEditModal = (device: any) => {
  172. setEditingDevice(device)
  173. editDeviceForm.setFieldsValue(device)
  174. setEditDeviceModalVisible(true)
  175. }
  176. const handleProcessAlert = (alertId: number) => {
  177. const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? { ...alert, status: "processed" } : alert))
  178. setAlerts(updatedAlerts)
  179. message.success("报警已处理!")
  180. }
  181. const handleIgnoreAlert = (alertId: number) => {
  182. const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? { ...alert, status: "ignored" } : alert))
  183. setAlerts(updatedAlerts)
  184. message.success("报警已忽略!")
  185. }
  186. // 设备状态统计
  187. const deviceStats = {
  188. total: devices.length,
  189. normal: devices.filter((d) => d.status === "normal").length,
  190. warning: devices.filter((d) => d.status === "warning").length,
  191. error: devices.filter((d) => d.status === "error").length,
  192. maintenance: devices.filter((d) => d.status === "maintenance").length,
  193. }
  194. // 区域分布图表配置
  195. const regionChartOption = {
  196. title: { text: "设备区域分布", left: "center" },
  197. tooltip: { trigger: "item" },
  198. series: [
  199. {
  200. type: "pie",
  201. radius: "50%",
  202. data: [
  203. { value: 1, name: "朝阳区" },
  204. { value: 1, name: "海淀区" },
  205. { value: 1, name: "西城区" },
  206. { value: 1, name: "东城区" },
  207. { value: 1, name: "丰台区" },
  208. ],
  209. },
  210. ],
  211. }
  212. // 设备状态分布图表配置
  213. const statusChartOption = {
  214. title: { text: "设备状态分布", left: "center" },
  215. tooltip: { trigger: "item" },
  216. series: [
  217. {
  218. type: "pie",
  219. radius: "50%",
  220. data: [
  221. { value: deviceStats.normal, name: "正常", itemStyle: { color: "#52c41a" } },
  222. { value: deviceStats.warning, name: "告警", itemStyle: { color: "#faad14" } },
  223. { value: deviceStats.error, name: "故障", itemStyle: { color: "#f5222d" } },
  224. { value: deviceStats.maintenance, name: "维修中", itemStyle: { color: "#722ed1" } },
  225. ],
  226. },
  227. ],
  228. }
  229. // 实时监测数据图表配置
  230. const monitoringChartOption = {
  231. title: { text: "实时监测数据趋势", left: "center" },
  232. tooltip: { trigger: "axis" },
  233. legend: { data: ["倾斜角度", "位移距离", "震动强度"], top: 30 },
  234. xAxis: {
  235. type: "category",
  236. data: ["00:00", "04:00", "08:00", "12:00", "16:00", "20:00"],
  237. },
  238. yAxis: { type: "value" },
  239. series: [
  240. {
  241. name: "倾斜角度",
  242. type: "line",
  243. data: [2.1, 2.3, 2.0, 2.5, 2.8, 2.4],
  244. itemStyle: { color: "#1890ff" },
  245. },
  246. {
  247. name: "位移距离",
  248. type: "line",
  249. data: [0.5, 0.7, 0.4, 0.9, 1.2, 0.8],
  250. itemStyle: { color: "#52c41a" },
  251. },
  252. {
  253. name: "震动强度",
  254. type: "line",
  255. data: [15, 18, 12, 22, 28, 20],
  256. itemStyle: { color: "#faad14" },
  257. },
  258. ],
  259. }
  260. const deviceColumns = [
  261. { title: "设备编号", dataIndex: "name", key: "name" },
  262. { title: "设备类型", dataIndex: "type", key: "type" },
  263. {
  264. title: "运维状态",
  265. dataIndex: "status",
  266. key: "status",
  267. render: (status: string) => {
  268. const statusMap = {
  269. normal: { color: "success", text: "正常" },
  270. warning: { color: "warning", text: "告警" },
  271. error: { color: "error", text: "故障" },
  272. maintenance: { color: "processing", text: "维修中" },
  273. }
  274. const config = statusMap[status as keyof typeof statusMap]
  275. return <Badge status={config.color as any} text={config.text} />
  276. },
  277. },
  278. { title: "所在位置", dataIndex: "location", key: "location" },
  279. {
  280. title: "电池电量",
  281. dataIndex: "battery",
  282. key: "battery",
  283. render: (battery: number) => `${battery}%`,
  284. },
  285. {
  286. title: "信号强度",
  287. dataIndex: "signal",
  288. key: "signal",
  289. render: (signal: number) => `${signal}%`,
  290. },
  291. {
  292. title: "操作",
  293. key: "action",
  294. render: (_, record: any) => (
  295. <Space>
  296. <Button
  297. type="link"
  298. icon={<EyeOutlined />}
  299. onClick={() => {
  300. setSelectedDevice(record)
  301. setDeviceModalVisible(true)
  302. }}
  303. >
  304. 查看
  305. </Button>
  306. <Button type="link" icon={<EditOutlined />} onClick={() => openEditModal(record)}>
  307. 编辑
  308. </Button>
  309. </Space>
  310. ),
  311. },
  312. ]
  313. const alertColumns = [
  314. { title: "设备名称", dataIndex: "deviceName", key: "deviceName" },
  315. { title: "报警类型", dataIndex: "type", key: "type" },
  316. {
  317. title: "报警级别",
  318. dataIndex: "level",
  319. key: "level",
  320. render: (level: string) => {
  321. const levelMap = {
  322. info: { color: "blue", text: "信息" },
  323. warning: { color: "orange", text: "警告" },
  324. error: { color: "red", text: "严重" },
  325. }
  326. const config = levelMap[level as keyof typeof levelMap]
  327. return <Tag color={config.color}>{config.text}</Tag>
  328. },
  329. },
  330. { title: "报警时间", dataIndex: "time", key: "time" },
  331. { title: "设备位置", dataIndex: "location", key: "location" },
  332. {
  333. title: "状态",
  334. dataIndex: "status",
  335. key: "status",
  336. render: (status: string) => {
  337. const statusMap = {
  338. pending: { color: "orange", text: "待处理" },
  339. processed: { color: "green", text: "已处理" },
  340. ignored: { color: "gray", text: "已忽略" },
  341. }
  342. const config = statusMap[status as keyof typeof statusMap]
  343. return <Tag color={config.color}>{config.text}</Tag>
  344. },
  345. },
  346. {
  347. title: "操作",
  348. key: "action",
  349. render: (_, record: any) => (
  350. <Space>
  351. {record.status === "pending" && (
  352. <>
  353. <Button type="link" onClick={() => handleProcessAlert(record.id)}>
  354. 处理
  355. </Button>
  356. <Button type="link" onClick={() => handleIgnoreAlert(record.id)}>
  357. 忽略
  358. </Button>
  359. </>
  360. )}
  361. {record.status !== "pending" && <span className="text-gray-400">已处理</span>}
  362. </Space>
  363. ),
  364. },
  365. ]
  366. const renderContent = () => {
  367. switch (selectedMenu) {
  368. case "dashboard":
  369. return (
  370. <div className="space-y-6">
  371. {/* 统计卡片 */}
  372. <Row gutter={16}>
  373. <Col span={6}>
  374. <Card>
  375. <Statistic title="设备总数" value={deviceStats.total} valueStyle={{ color: "#1890ff" }} />
  376. </Card>
  377. </Col>
  378. <Col span={6}>
  379. <Card>
  380. <Statistic title="正常设备" value={deviceStats.normal} valueStyle={{ color: "#52c41a" }} />
  381. </Card>
  382. </Col>
  383. <Col span={6}>
  384. <Card>
  385. <Statistic title="告警设备" value={deviceStats.warning} valueStyle={{ color: "#faad14" }} />
  386. </Card>
  387. </Col>
  388. <Col span={6}>
  389. <Card>
  390. <Statistic title="故障设备" value={deviceStats.error} valueStyle={{ color: "#f5222d" }} />
  391. </Card>
  392. </Col>
  393. </Row>
  394. {/* 图表展示 */}
  395. <Row gutter={16}>
  396. <Col span={12}>
  397. <Card title="设备区域分布">
  398. <EChart option={regionChartOption} />
  399. </Card>
  400. </Col>
  401. <Col span={12}>
  402. <Card title="设备状态分布">
  403. <EChart option={statusChartOption} />
  404. </Card>
  405. </Col>
  406. </Row>
  407. {/* 实时监测趋势 */}
  408. <Card title="实时监测数据趋势">
  409. <EChart option={monitoringChartOption} />
  410. </Card>
  411. </div>
  412. )
  413. case "devices":
  414. return (
  415. <div className="space-y-6">
  416. <Card
  417. title="监测设备台账"
  418. extra={
  419. <Button type="primary" icon={<PlusOutlined />} onClick={() => setAddDeviceModalVisible(true)}>
  420. 添加设备
  421. </Button>
  422. }
  423. >
  424. <Table columns={deviceColumns} dataSource={devices} rowKey="id" pagination={{ pageSize: 10 }} />
  425. </Card>
  426. </div>
  427. )
  428. case "map":
  429. return (
  430. <div className="space-y-6">
  431. <Card title="监测设备 GIS 一张图">
  432. <div style={{ height: "600px" }}>
  433. <MapView devices={devices} />
  434. </div>
  435. </Card>
  436. </div>
  437. )
  438. case "monitoring":
  439. return (
  440. <div className="space-y-6">
  441. <Row gutter={16}>
  442. <Col span={24}>
  443. <Alert
  444. message="实时监测状态"
  445. description="系统正在实时监测所有窨井盖设备状态,包括开启、位移、倾斜、震动、溢水等情况"
  446. type="info"
  447. showIcon
  448. className="mb-4"
  449. />
  450. </Col>
  451. </Row>
  452. <Tabs defaultActiveKey="realtime">
  453. <TabPane tab="实时监测" key="realtime">
  454. <Row gutter={16}>
  455. <Col span={24}>
  456. <Card title="实时监测一张图">
  457. <div style={{ height: "500px" }}>
  458. <MapView devices={devices} showRealTimeData />
  459. </div>
  460. </Card>
  461. </Col>
  462. </Row>
  463. </TabPane>
  464. <TabPane tab="监测数据" key="data">
  465. <Card title="实时监测数据">
  466. <EChart option={monitoringChartOption} />
  467. </Card>
  468. </TabPane>
  469. </Tabs>
  470. </div>
  471. )
  472. case "alerts":
  473. return (
  474. <div className="space-y-6">
  475. <Row gutter={16}>
  476. <Col span={6}>
  477. <Card>
  478. <Statistic title="今日告警" value={alerts.length} valueStyle={{ color: "#f5222d" }} />
  479. </Card>
  480. </Col>
  481. <Col span={6}>
  482. <Card>
  483. <Statistic
  484. title="待处理"
  485. value={alerts.filter((a) => a.status === "pending").length}
  486. valueStyle={{ color: "#faad14" }}
  487. />
  488. </Card>
  489. </Col>
  490. <Col span={6}>
  491. <Card>
  492. <Statistic
  493. title="已处理"
  494. value={alerts.filter((a) => a.status === "processed").length}
  495. valueStyle={{ color: "#52c41a" }}
  496. />
  497. </Card>
  498. </Col>
  499. <Col span={6}>
  500. <Card>
  501. <Statistic
  502. title="已忽略"
  503. value={alerts.filter((a) => a.status === "ignored").length}
  504. valueStyle={{ color: "#d9d9d9" }}
  505. />
  506. </Card>
  507. </Col>
  508. </Row>
  509. <Card title="监测报警记录">
  510. <Table columns={alertColumns} dataSource={alerts} rowKey="id" pagination={{ pageSize: 10 }} />
  511. </Card>
  512. </div>
  513. )
  514. default:
  515. return <div>页面开发中...</div>
  516. }
  517. }
  518. return (
  519. <Layout className="min-h-screen">
  520. <Header className="bg-blue-600 text-white" style={{backgroundColor:'white'}}>
  521. <div className="flex items-center justify-between">
  522. <h1 className="text-xl font-bold">窨井盖安全运行监测子系统</h1>
  523. <div className="text-sm">当前时间: {new Date().toLocaleString("zh-CN")}</div>
  524. </div>
  525. </Header>
  526. <Layout>
  527. <Sider width={200} className="bg-white">
  528. <Menu
  529. mode="inline"
  530. selectedKeys={[selectedMenu]}
  531. onClick={({ key }) => setSelectedMenu(key)}
  532. className="h-full border-r"
  533. >
  534. <Menu.Item key="dashboard" icon={<DashboardOutlined />}>
  535. 监控面板
  536. </Menu.Item>
  537. <Menu.Item key="devices" icon={<SettingOutlined />}>
  538. 设备管理
  539. </Menu.Item>
  540. <Menu.Item key="map" icon={<EnvironmentOutlined />}>
  541. GIS地图
  542. </Menu.Item>
  543. <Menu.Item key="monitoring" icon={<BarChartOutlined />}>
  544. 实时监测
  545. </Menu.Item>
  546. <Menu.Item key="alerts" icon={<AlertOutlined />}>
  547. 报警管理
  548. </Menu.Item>
  549. </Menu>
  550. </Sider>
  551. <Layout className="p-6">
  552. <Content className="bg-gray-50 min-h-full">{renderContent()}</Content>
  553. </Layout>
  554. </Layout>
  555. {/* 设备详情弹窗 */}
  556. <Modal
  557. title="设备详情"
  558. open={deviceModalVisible}
  559. onCancel={() => setDeviceModalVisible(false)}
  560. footer={null}
  561. width={600}
  562. >
  563. {selectedDevice && (
  564. <div className="space-y-4">
  565. <Row gutter={16}>
  566. <Col span={12}>
  567. <div>
  568. <strong>设备编号:</strong> {selectedDevice.name}
  569. </div>
  570. </Col>
  571. <Col span={12}>
  572. <div>
  573. <strong>设备类型:</strong> {selectedDevice.type}
  574. </div>
  575. </Col>
  576. </Row>
  577. <Row gutter={16}>
  578. <Col span={12}>
  579. <div>
  580. <strong>所在位置:</strong> {selectedDevice.location}
  581. </div>
  582. </Col>
  583. <Col span={12}>
  584. <div>
  585. <strong>运维状态:</strong>
  586. <Badge
  587. status={selectedDevice.status === "normal" ? "success" : "error"}
  588. text={selectedDevice.status === "normal" ? "正常" : "异常"}
  589. />
  590. </div>
  591. </Col>
  592. </Row>
  593. <Row gutter={16}>
  594. <Col span={12}>
  595. <div>
  596. <strong>电池电量:</strong> {selectedDevice.battery}%
  597. </div>
  598. </Col>
  599. <Col span={12}>
  600. <div>
  601. <strong>信号强度:</strong> {selectedDevice.signal}%
  602. </div>
  603. </Col>
  604. </Row>
  605. </div>
  606. )}
  607. </Modal>
  608. {/* 添加设备弹窗 */}
  609. <Modal
  610. title="添加设备"
  611. open={addDeviceModalVisible}
  612. onCancel={() => {
  613. setAddDeviceModalVisible(false)
  614. addDeviceForm.resetFields()
  615. }}
  616. onOk={() => addDeviceForm.submit()}
  617. width={600}
  618. >
  619. <Form form={addDeviceForm} layout="vertical" onFinish={handleAddDevice}>
  620. <Row gutter={16}>
  621. <Col span={12}>
  622. <Form.Item name="name" label="设备编号" rules={[{ required: true, message: "请输入设备编号" }]}>
  623. <Input placeholder="请输入设备编号" />
  624. </Form.Item>
  625. </Col>
  626. <Col span={12}>
  627. <Form.Item name="type" label="设备类型" rules={[{ required: true, message: "请选择设备类型" }]}>
  628. <Select placeholder="请选择设备类型">
  629. <Option value="智能井盖">智能井盖</Option>
  630. <Option value="传感器">传感器</Option>
  631. <Option value="监控设备">监控设备</Option>
  632. </Select>
  633. </Form.Item>
  634. </Col>
  635. </Row>
  636. <Row gutter={16}>
  637. <Col span={12}>
  638. <Form.Item name="status" label="运维状态" rules={[{ required: true, message: "请选择运维状态" }]}>
  639. <Select placeholder="请选择运维状态">
  640. <Option value="normal">正常</Option>
  641. <Option value="warning">告警</Option>
  642. <Option value="error">故障</Option>
  643. <Option value="maintenance">维修中</Option>
  644. </Select>
  645. </Form.Item>
  646. </Col>
  647. <Col span={12}>
  648. <Form.Item name="location" label="所在位置" rules={[{ required: true, message: "请输入所在位置" }]}>
  649. <Input placeholder="请输入所在位置" />
  650. </Form.Item>
  651. </Col>
  652. </Row>
  653. <Row gutter={16}>
  654. <Col span={12}>
  655. <Form.Item name="lat" label="纬度" rules={[{ required: true, message: "请输入纬度" }]}>
  656. <Input placeholder="请输入纬度" type="number" step="0.000001" />
  657. </Form.Item>
  658. </Col>
  659. <Col span={12}>
  660. <Form.Item name="lng" label="经度" rules={[{ required: true, message: "请输入经度" }]}>
  661. <Input placeholder="请输入经度" type="number" step="0.000001" />
  662. </Form.Item>
  663. </Col>
  664. </Row>
  665. <Row gutter={16}>
  666. <Col span={12}>
  667. <Form.Item name="battery" label="电池电量(%)" rules={[{ required: true, message: "请输入电池电量" }]}>
  668. <Input placeholder="请输入电池电量" type="number" min="0" max="100" />
  669. </Form.Item>
  670. </Col>
  671. <Col span={12}>
  672. <Form.Item name="signal" label="信号强度(%)" rules={[{ required: true, message: "请输入信号强度" }]}>
  673. <Input placeholder="请输入信号强度" type="number" min="0" max="100" />
  674. </Form.Item>
  675. </Col>
  676. </Row>
  677. </Form>
  678. </Modal>
  679. {/* 编辑设备弹窗 */}
  680. <Modal
  681. title="编辑设备"
  682. open={editDeviceModalVisible}
  683. onCancel={() => {
  684. setEditDeviceModalVisible(false)
  685. setEditingDevice(null)
  686. editDeviceForm.resetFields()
  687. }}
  688. onOk={() => editDeviceForm.submit()}
  689. width={600}
  690. >
  691. <Form form={editDeviceForm} layout="vertical" onFinish={handleEditDevice}>
  692. <Row gutter={16}>
  693. <Col span={12}>
  694. <Form.Item name="name" label="设备编号" rules={[{ required: true, message: "请输入设备编号" }]}>
  695. <Input placeholder="请输入设备编号" />
  696. </Form.Item>
  697. </Col>
  698. <Col span={12}>
  699. <Form.Item name="type" label="设备类型" rules={[{ required: true, message: "请选择设备类型" }]}>
  700. <Select placeholder="请选择设备类型">
  701. <Option value="智能井盖">智能井盖</Option>
  702. <Option value="传感器">传感器</Option>
  703. <Option value="监控设备">监控设备</Option>
  704. </Select>
  705. </Form.Item>
  706. </Col>
  707. </Row>
  708. <Row gutter={16}>
  709. <Col span={12}>
  710. <Form.Item name="status" label="运维状态" rules={[{ required: true, message: "请选择运维状态" }]}>
  711. <Select placeholder="请选择运维状态">
  712. <Option value="normal">正常</Option>
  713. <Option value="warning">告警</Option>
  714. <Option value="error">故障</Option>
  715. <Option value="maintenance">维修中</Option>
  716. </Select>
  717. </Form.Item>
  718. </Col>
  719. <Col span={12}>
  720. <Form.Item name="location" label="所在位置" rules={[{ required: true, message: "请输入所在位置" }]}>
  721. <Input placeholder="请输入所在位置" />
  722. </Form.Item>
  723. </Col>
  724. </Row>
  725. <Row gutter={16}>
  726. <Col span={12}>
  727. <Form.Item name="lat" label="纬度" rules={[{ required: true, message: "请输入纬度" }]}>
  728. <Input placeholder="请输入纬度" type="number" step="0.000001" />
  729. </Form.Item>
  730. </Col>
  731. <Col span={12}>
  732. <Form.Item name="lng" label="经度" rules={[{ required: true, message: "请输入经度" }]}>
  733. <Input placeholder="请输入经度" type="number" step="0.000001" />
  734. </Form.Item>
  735. </Col>
  736. </Row>
  737. <Row gutter={16}>
  738. <Col span={12}>
  739. <Form.Item name="battery" label="电池电量(%)" rules={[{ required: true, message: "请输入电池电量" }]}>
  740. <Input placeholder="请输入电池电量" type="number" min="0" max="100" />
  741. </Form.Item>
  742. </Col>
  743. <Col span={12}>
  744. <Form.Item name="signal" label="信号强度(%)" rules={[{ required: true, message: "请输入信号强度" }]}>
  745. <Input placeholder="请输入信号强度" type="number" min="0" max="100" />
  746. </Form.Item>
  747. </Col>
  748. </Row>
  749. </Form>
  750. </Modal>
  751. </Layout>
  752. )
  753. }