|
@@ -0,0 +1,786 @@
|
|
|
|
|
+"use client"
|
|
|
|
|
+
|
|
|
|
|
+import {useState} from "react"
|
|
|
|
|
+import {
|
|
|
|
|
+ Alert,
|
|
|
|
|
+ Badge,
|
|
|
|
|
+ Button,
|
|
|
|
|
+ Card,
|
|
|
|
|
+ Col,
|
|
|
|
|
+ Form,
|
|
|
|
|
+ Input,
|
|
|
|
|
+ Layout,
|
|
|
|
|
+ Menu,
|
|
|
|
|
+ message,
|
|
|
|
|
+ Modal,
|
|
|
|
|
+ Row,
|
|
|
|
|
+ Select,
|
|
|
|
|
+ Space,
|
|
|
|
|
+ Statistic,
|
|
|
|
|
+ Table,
|
|
|
|
|
+ Tabs,
|
|
|
|
|
+ Tag,
|
|
|
|
|
+} from "antd"
|
|
|
|
|
+import {
|
|
|
|
|
+ AlertOutlined,
|
|
|
|
|
+ BarChartOutlined,
|
|
|
|
|
+ DashboardOutlined,
|
|
|
|
|
+ EditOutlined,
|
|
|
|
|
+ EnvironmentOutlined,
|
|
|
|
|
+ EyeOutlined,
|
|
|
|
|
+ PlusOutlined,
|
|
|
|
|
+ SettingOutlined,
|
|
|
|
|
+} from "@ant-design/icons"
|
|
|
|
|
+import dynamic from "next/dynamic"
|
|
|
|
|
+import EChart from "@/components/echarts"
|
|
|
|
|
+
|
|
|
|
|
+const { Header, Sider, Content } = Layout
|
|
|
|
|
+const { TabPane } = Tabs
|
|
|
|
|
+const { Option } = Select
|
|
|
|
|
+
|
|
|
|
|
+// 动态导入地图组件避免SSR问题
|
|
|
|
|
+const MapView = dynamic(() => import("./components/map-view"), { ssr: false })
|
|
|
|
|
+
|
|
|
|
|
+export default function ManholeMonitoringSystem() {
|
|
|
|
|
+ const [selectedMenu, setSelectedMenu] = useState("dashboard")
|
|
|
|
|
+ const [deviceModalVisible, setDeviceModalVisible] = useState(false)
|
|
|
|
|
+ const [selectedDevice, setSelectedDevice] = useState(null)
|
|
|
|
|
+ const [addDeviceModalVisible, setAddDeviceModalVisible] = useState(false)
|
|
|
|
|
+ const [editDeviceModalVisible, setEditDeviceModalVisible] = useState(false)
|
|
|
|
|
+ const [editingDevice, setEditingDevice] = useState(null)
|
|
|
|
|
+ const [devices, setDevices] = useState([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ name: "设备001",
|
|
|
|
|
+ type: "智能井盖",
|
|
|
|
|
+ status: "normal",
|
|
|
|
|
+ location: "朝阳区建国路",
|
|
|
|
|
+ lat: 39.9042,
|
|
|
|
|
+ lng: 116.4074,
|
|
|
|
|
+ battery: 85,
|
|
|
|
|
+ signal: 90,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ name: "设备002",
|
|
|
|
|
+ type: "智能井盖",
|
|
|
|
|
+ status: "warning",
|
|
|
|
|
+ location: "海淀区中关村",
|
|
|
|
|
+ lat: 39.9826,
|
|
|
|
|
+ lng: 116.3186,
|
|
|
|
|
+ battery: 45,
|
|
|
|
|
+ signal: 75,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ name: "设备003",
|
|
|
|
|
+ type: "智能井盖",
|
|
|
|
|
+ status: "error",
|
|
|
|
|
+ location: "西城区金融街",
|
|
|
|
|
+ lat: 39.926,
|
|
|
|
|
+ lng: 116.3663,
|
|
|
|
|
+ battery: 20,
|
|
|
|
|
+ signal: 60,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 4,
|
|
|
|
|
+ name: "设备004",
|
|
|
|
|
+ type: "智能井盖",
|
|
|
|
|
+ status: "normal",
|
|
|
|
|
+ location: "东城区王府井",
|
|
|
|
|
+ lat: 39.9097,
|
|
|
|
|
+ lng: 116.4109,
|
|
|
|
|
+ battery: 92,
|
|
|
|
|
+ signal: 95,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 5,
|
|
|
|
|
+ name: "设备005",
|
|
|
|
|
+ type: "智能井盖",
|
|
|
|
|
+ status: "maintenance",
|
|
|
|
|
+ location: "丰台区丽泽",
|
|
|
|
|
+ lat: 39.8584,
|
|
|
|
|
+ lng: 116.3135,
|
|
|
|
|
+ battery: 78,
|
|
|
|
|
+ signal: 80,
|
|
|
|
|
+ },
|
|
|
|
|
+ ])
|
|
|
|
|
+ const [alerts, setAlerts] = useState([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ deviceName: "设备002",
|
|
|
|
|
+ type: "位移异常",
|
|
|
|
|
+ level: "warning",
|
|
|
|
|
+ time: "2024-01-15 14:30:25",
|
|
|
|
|
+ location: "海淀区中关村",
|
|
|
|
|
+ status: "pending", // pending, processed, ignored
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ deviceName: "设备003",
|
|
|
|
|
+ type: "电池电压低",
|
|
|
|
|
+ level: "error",
|
|
|
|
|
+ time: "2024-01-15 14:25:10",
|
|
|
|
|
+ location: "西城区金融街",
|
|
|
|
|
+ status: "pending",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ deviceName: "设备001",
|
|
|
|
|
+ type: "震动异常",
|
|
|
|
|
+ level: "info",
|
|
|
|
|
+ time: "2024-01-15 14:20:15",
|
|
|
|
|
+ location: "朝阳区建国路",
|
|
|
|
|
+ status: "processed",
|
|
|
|
|
+ },
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
|
|
+ const [addDeviceForm] = Form.useForm()
|
|
|
|
|
+ const [editDeviceForm] = Form.useForm()
|
|
|
|
|
+
|
|
|
|
|
+ const handleAddDevice = (values: any) => {
|
|
|
|
|
+ const newDevice = {
|
|
|
|
|
+ id: devices.length + 1,
|
|
|
|
|
+ name: values.name,
|
|
|
|
|
+ type: values.type,
|
|
|
|
|
+ status: values.status,
|
|
|
|
|
+ location: values.location,
|
|
|
|
|
+ lat: Number.parseFloat(values.lat),
|
|
|
|
|
+ lng: Number.parseFloat(values.lng),
|
|
|
|
|
+ battery: Number.parseInt(values.battery),
|
|
|
|
|
+ signal: Number.parseInt(values.signal),
|
|
|
|
|
+ }
|
|
|
|
|
+ setDevices([...devices, newDevice])
|
|
|
|
|
+ setAddDeviceModalVisible(false)
|
|
|
|
|
+ addDeviceForm.resetFields()
|
|
|
|
|
+ message.success("设备添加成功!")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const handleEditDevice = (values: any) => {
|
|
|
|
|
+ const updatedDevices = devices.map((device) =>
|
|
|
|
|
+ device.id === editingDevice.id
|
|
|
|
|
+ ? {
|
|
|
|
|
+ ...device,
|
|
|
|
|
+ ...values,
|
|
|
|
|
+ lat: Number.parseFloat(values.lat),
|
|
|
|
|
+ lng: Number.parseFloat(values.lng),
|
|
|
|
|
+ battery: Number.parseInt(values.battery),
|
|
|
|
|
+ signal: Number.parseInt(values.signal),
|
|
|
|
|
+ }
|
|
|
|
|
+ : device,
|
|
|
|
|
+ )
|
|
|
|
|
+ setDevices(updatedDevices)
|
|
|
|
|
+ setEditDeviceModalVisible(false)
|
|
|
|
|
+ setEditingDevice(null)
|
|
|
|
|
+ editDeviceForm.resetFields()
|
|
|
|
|
+ message.success("设备信息更新成功!")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const openEditModal = (device: any) => {
|
|
|
|
|
+ setEditingDevice(device)
|
|
|
|
|
+ editDeviceForm.setFieldsValue(device)
|
|
|
|
|
+ setEditDeviceModalVisible(true)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const handleProcessAlert = (alertId: number) => {
|
|
|
|
|
+ const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? { ...alert, status: "processed" } : alert))
|
|
|
|
|
+ setAlerts(updatedAlerts)
|
|
|
|
|
+ message.success("报警已处理!")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const handleIgnoreAlert = (alertId: number) => {
|
|
|
|
|
+ const updatedAlerts = alerts.map((alert) => (alert.id === alertId ? { ...alert, status: "ignored" } : alert))
|
|
|
|
|
+ setAlerts(updatedAlerts)
|
|
|
|
|
+ message.success("报警已忽略!")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设备状态统计
|
|
|
|
|
+ const deviceStats = {
|
|
|
|
|
+ total: devices.length,
|
|
|
|
|
+ normal: devices.filter((d) => d.status === "normal").length,
|
|
|
|
|
+ warning: devices.filter((d) => d.status === "warning").length,
|
|
|
|
|
+ error: devices.filter((d) => d.status === "error").length,
|
|
|
|
|
+ maintenance: devices.filter((d) => d.status === "maintenance").length,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 区域分布图表配置
|
|
|
|
|
+ const regionChartOption = {
|
|
|
|
|
+ title: { text: "设备区域分布", left: "center" },
|
|
|
|
|
+ tooltip: { trigger: "item" },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: "pie",
|
|
|
|
|
+ radius: "50%",
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: 1, name: "朝阳区" },
|
|
|
|
|
+ { value: 1, name: "海淀区" },
|
|
|
|
|
+ { value: 1, name: "西城区" },
|
|
|
|
|
+ { value: 1, name: "东城区" },
|
|
|
|
|
+ { value: 1, name: "丰台区" },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设备状态分布图表配置
|
|
|
|
|
+ const statusChartOption = {
|
|
|
|
|
+ title: { text: "设备状态分布", left: "center" },
|
|
|
|
|
+ tooltip: { trigger: "item" },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: "pie",
|
|
|
|
|
+ radius: "50%",
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: deviceStats.normal, name: "正常", itemStyle: { color: "#52c41a" } },
|
|
|
|
|
+ { value: deviceStats.warning, name: "告警", itemStyle: { color: "#faad14" } },
|
|
|
|
|
+ { value: deviceStats.error, name: "故障", itemStyle: { color: "#f5222d" } },
|
|
|
|
|
+ { value: deviceStats.maintenance, name: "维修中", itemStyle: { color: "#722ed1" } },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 实时监测数据图表配置
|
|
|
|
|
+ const monitoringChartOption = {
|
|
|
|
|
+ title: { text: "实时监测数据趋势", left: "center" },
|
|
|
|
|
+ tooltip: { trigger: "axis" },
|
|
|
|
|
+ legend: { data: ["倾斜角度", "位移距离", "震动强度"], top: 30 },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: "category",
|
|
|
|
|
+ data: ["00:00", "04:00", "08:00", "12:00", "16:00", "20:00"],
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: { type: "value" },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "倾斜角度",
|
|
|
|
|
+ type: "line",
|
|
|
|
|
+ data: [2.1, 2.3, 2.0, 2.5, 2.8, 2.4],
|
|
|
|
|
+ itemStyle: { color: "#1890ff" },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "位移距离",
|
|
|
|
|
+ type: "line",
|
|
|
|
|
+ data: [0.5, 0.7, 0.4, 0.9, 1.2, 0.8],
|
|
|
|
|
+ itemStyle: { color: "#52c41a" },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "震动强度",
|
|
|
|
|
+ type: "line",
|
|
|
|
|
+ data: [15, 18, 12, 22, 28, 20],
|
|
|
|
|
+ itemStyle: { color: "#faad14" },
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const deviceColumns = [
|
|
|
|
|
+ { title: "设备编号", dataIndex: "name", key: "name" },
|
|
|
|
|
+ { title: "设备类型", dataIndex: "type", key: "type" },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "运维状态",
|
|
|
|
|
+ dataIndex: "status",
|
|
|
|
|
+ key: "status",
|
|
|
|
|
+ render: (status: string) => {
|
|
|
|
|
+ const statusMap = {
|
|
|
|
|
+ normal: { color: "success", text: "正常" },
|
|
|
|
|
+ warning: { color: "warning", text: "告警" },
|
|
|
|
|
+ error: { color: "error", text: "故障" },
|
|
|
|
|
+ maintenance: { color: "processing", text: "维修中" },
|
|
|
|
|
+ }
|
|
|
|
|
+ const config = statusMap[status as keyof typeof statusMap]
|
|
|
|
|
+ return <Badge status={config.color as any} text={config.text} />
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ { title: "所在位置", dataIndex: "location", key: "location" },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "电池电量",
|
|
|
|
|
+ dataIndex: "battery",
|
|
|
|
|
+ key: "battery",
|
|
|
|
|
+ render: (battery: number) => `${battery}%`,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "信号强度",
|
|
|
|
|
+ dataIndex: "signal",
|
|
|
|
|
+ key: "signal",
|
|
|
|
|
+ render: (signal: number) => `${signal}%`,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "操作",
|
|
|
|
|
+ key: "action",
|
|
|
|
|
+ render: (_, record: any) => (
|
|
|
|
|
+ <Space>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type="link"
|
|
|
|
|
+ icon={<EyeOutlined />}
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setSelectedDevice(record)
|
|
|
|
|
+ setDeviceModalVisible(true)
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 查看
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button type="link" icon={<EditOutlined />} onClick={() => openEditModal(record)}>
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ const alertColumns = [
|
|
|
|
|
+ { title: "设备名称", dataIndex: "deviceName", key: "deviceName" },
|
|
|
|
|
+ { title: "报警类型", dataIndex: "type", key: "type" },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "报警级别",
|
|
|
|
|
+ dataIndex: "level",
|
|
|
|
|
+ key: "level",
|
|
|
|
|
+ render: (level: string) => {
|
|
|
|
|
+ const levelMap = {
|
|
|
|
|
+ info: { color: "blue", text: "信息" },
|
|
|
|
|
+ warning: { color: "orange", text: "警告" },
|
|
|
|
|
+ error: { color: "red", text: "严重" },
|
|
|
|
|
+ }
|
|
|
|
|
+ const config = levelMap[level as keyof typeof levelMap]
|
|
|
|
|
+ return <Tag color={config.color}>{config.text}</Tag>
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ { title: "报警时间", dataIndex: "time", key: "time" },
|
|
|
|
|
+ { title: "设备位置", dataIndex: "location", key: "location" },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "状态",
|
|
|
|
|
+ dataIndex: "status",
|
|
|
|
|
+ key: "status",
|
|
|
|
|
+ render: (status: string) => {
|
|
|
|
|
+ const statusMap = {
|
|
|
|
|
+ pending: { color: "orange", text: "待处理" },
|
|
|
|
|
+ processed: { color: "green", text: "已处理" },
|
|
|
|
|
+ ignored: { color: "gray", text: "已忽略" },
|
|
|
|
|
+ }
|
|
|
|
|
+ const config = statusMap[status as keyof typeof statusMap]
|
|
|
|
|
+ return <Tag color={config.color}>{config.text}</Tag>
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: "操作",
|
|
|
|
|
+ key: "action",
|
|
|
|
|
+ render: (_, record: any) => (
|
|
|
|
|
+ <Space>
|
|
|
|
|
+ {record.status === "pending" && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Button type="link" onClick={() => handleProcessAlert(record.id)}>
|
|
|
|
|
+ 处理
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button type="link" onClick={() => handleIgnoreAlert(record.id)}>
|
|
|
|
|
+ 忽略
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ {record.status !== "pending" && <span className="text-gray-400">已处理</span>}
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ const renderContent = () => {
|
|
|
|
|
+ switch (selectedMenu) {
|
|
|
|
|
+ case "dashboard":
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ {/* 统计卡片 */}
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic title="设备总数" value={deviceStats.total} valueStyle={{ color: "#1890ff" }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic title="正常设备" value={deviceStats.normal} valueStyle={{ color: "#52c41a" }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic title="告警设备" value={deviceStats.warning} valueStyle={{ color: "#faad14" }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic title="故障设备" value={deviceStats.error} valueStyle={{ color: "#f5222d" }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 图表展示 */}
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Card title="设备区域分布">
|
|
|
|
|
+ <EChart option={regionChartOption} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Card title="设备状态分布">
|
|
|
|
|
+ <EChart option={statusChartOption} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 实时监测趋势 */}
|
|
|
|
|
+ <Card title="实时监测数据趋势">
|
|
|
|
|
+ <EChart option={monitoringChartOption} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ case "devices":
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ <Card
|
|
|
|
|
+ title="监测设备台账"
|
|
|
|
|
+ extra={
|
|
|
|
|
+ <Button type="primary" icon={<PlusOutlined />} onClick={() => setAddDeviceModalVisible(true)}>
|
|
|
|
|
+ 添加设备
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ <Table columns={deviceColumns} dataSource={devices} rowKey="id" pagination={{ pageSize: 10 }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ case "map":
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ <Card title="监测设备 GIS 一张图">
|
|
|
|
|
+ <div style={{ height: "600px" }}>
|
|
|
|
|
+ <MapView devices={devices} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ case "monitoring":
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={24}>
|
|
|
|
|
+ <Alert
|
|
|
|
|
+ message="实时监测状态"
|
|
|
|
|
+ description="系统正在实时监测所有窨井盖设备状态,包括开启、位移、倾斜、震动、溢水等情况"
|
|
|
|
|
+ type="info"
|
|
|
|
|
+ showIcon
|
|
|
|
|
+ className="mb-4"
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ <Tabs defaultActiveKey="realtime">
|
|
|
|
|
+ <TabPane tab="实时监测" key="realtime">
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={24}>
|
|
|
|
|
+ <Card title="实时监测一张图">
|
|
|
|
|
+ <div style={{ height: "500px" }}>
|
|
|
|
|
+ <MapView devices={devices} showRealTimeData />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </TabPane>
|
|
|
|
|
+
|
|
|
|
|
+ <TabPane tab="监测数据" key="data">
|
|
|
|
|
+ <Card title="实时监测数据">
|
|
|
|
|
+ <EChart option={monitoringChartOption} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </TabPane>
|
|
|
|
|
+ </Tabs>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ case "alerts":
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic title="今日告警" value={alerts.length} valueStyle={{ color: "#f5222d" }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic
|
|
|
|
|
+ title="待处理"
|
|
|
|
|
+ value={alerts.filter((a) => a.status === "pending").length}
|
|
|
|
|
+ valueStyle={{ color: "#faad14" }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic
|
|
|
|
|
+ title="已处理"
|
|
|
|
|
+ value={alerts.filter((a) => a.status === "processed").length}
|
|
|
|
|
+ valueStyle={{ color: "#52c41a" }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={6}>
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <Statistic
|
|
|
|
|
+ title="已忽略"
|
|
|
|
|
+ value={alerts.filter((a) => a.status === "ignored").length}
|
|
|
|
|
+ valueStyle={{ color: "#d9d9d9" }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+
|
|
|
|
|
+ <Card title="监测报警记录">
|
|
|
|
|
+ <Table columns={alertColumns} dataSource={alerts} rowKey="id" pagination={{ pageSize: 10 }} />
|
|
|
|
|
+ </Card>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ return <div>页面开发中...</div>
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Layout className="min-h-screen">
|
|
|
|
|
+ <Header className="bg-blue-600 text-white" style={{backgroundColor:'white'}}>
|
|
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
|
|
+ <h1 className="text-xl font-bold">窨井盖安全运行监测子系统</h1>
|
|
|
|
|
+ <div className="text-sm">当前时间: {new Date().toLocaleString("zh-CN")}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Header>
|
|
|
|
|
+
|
|
|
|
|
+ <Layout>
|
|
|
|
|
+ <Sider width={200} className="bg-white">
|
|
|
|
|
+ <Menu
|
|
|
|
|
+ mode="inline"
|
|
|
|
|
+ selectedKeys={[selectedMenu]}
|
|
|
|
|
+ onClick={({ key }) => setSelectedMenu(key)}
|
|
|
|
|
+ className="h-full border-r"
|
|
|
|
|
+ >
|
|
|
|
|
+ <Menu.Item key="dashboard" icon={<DashboardOutlined />}>
|
|
|
|
|
+ 监控面板
|
|
|
|
|
+ </Menu.Item>
|
|
|
|
|
+ <Menu.Item key="devices" icon={<SettingOutlined />}>
|
|
|
|
|
+ 设备管理
|
|
|
|
|
+ </Menu.Item>
|
|
|
|
|
+ <Menu.Item key="map" icon={<EnvironmentOutlined />}>
|
|
|
|
|
+ GIS地图
|
|
|
|
|
+ </Menu.Item>
|
|
|
|
|
+ <Menu.Item key="monitoring" icon={<BarChartOutlined />}>
|
|
|
|
|
+ 实时监测
|
|
|
|
|
+ </Menu.Item>
|
|
|
|
|
+ <Menu.Item key="alerts" icon={<AlertOutlined />}>
|
|
|
|
|
+ 报警管理
|
|
|
|
|
+ </Menu.Item>
|
|
|
|
|
+ </Menu>
|
|
|
|
|
+ </Sider>
|
|
|
|
|
+
|
|
|
|
|
+ <Layout className="p-6">
|
|
|
|
|
+ <Content className="bg-gray-50 min-h-full">{renderContent()}</Content>
|
|
|
|
|
+ </Layout>
|
|
|
|
|
+ </Layout>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 设备详情弹窗 */}
|
|
|
|
|
+ <Modal
|
|
|
|
|
+ title="设备详情"
|
|
|
|
|
+ open={deviceModalVisible}
|
|
|
|
|
+ onCancel={() => setDeviceModalVisible(false)}
|
|
|
|
|
+ footer={null}
|
|
|
|
|
+ width={600}
|
|
|
|
|
+ >
|
|
|
|
|
+ {selectedDevice && (
|
|
|
|
|
+ <div className="space-y-4">
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>设备编号:</strong> {selectedDevice.name}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>设备类型:</strong> {selectedDevice.type}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>所在位置:</strong> {selectedDevice.location}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>运维状态:</strong>
|
|
|
|
|
+ <Badge
|
|
|
|
|
+ status={selectedDevice.status === "normal" ? "success" : "error"}
|
|
|
|
|
+ text={selectedDevice.status === "normal" ? "正常" : "异常"}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>电池电量:</strong> {selectedDevice.battery}%
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <strong>信号强度:</strong> {selectedDevice.signal}%
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </Modal>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 添加设备弹窗 */}
|
|
|
|
|
+ <Modal
|
|
|
|
|
+ title="添加设备"
|
|
|
|
|
+ open={addDeviceModalVisible}
|
|
|
|
|
+ onCancel={() => {
|
|
|
|
|
+ setAddDeviceModalVisible(false)
|
|
|
|
|
+ addDeviceForm.resetFields()
|
|
|
|
|
+ }}
|
|
|
|
|
+ onOk={() => addDeviceForm.submit()}
|
|
|
|
|
+ width={600}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Form form={addDeviceForm} layout="vertical" onFinish={handleAddDevice}>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="name" label="设备编号" rules={[{ required: true, message: "请输入设备编号" }]}>
|
|
|
|
|
+ <Input placeholder="请输入设备编号" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="type" label="设备类型" rules={[{ required: true, message: "请选择设备类型" }]}>
|
|
|
|
|
+ <Select placeholder="请选择设备类型">
|
|
|
|
|
+ <Option value="智能井盖">智能井盖</Option>
|
|
|
|
|
+ <Option value="传感器">传感器</Option>
|
|
|
|
|
+ <Option value="监控设备">监控设备</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="status" label="运维状态" rules={[{ required: true, message: "请选择运维状态" }]}>
|
|
|
|
|
+ <Select placeholder="请选择运维状态">
|
|
|
|
|
+ <Option value="normal">正常</Option>
|
|
|
|
|
+ <Option value="warning">告警</Option>
|
|
|
|
|
+ <Option value="error">故障</Option>
|
|
|
|
|
+ <Option value="maintenance">维修中</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="location" label="所在位置" rules={[{ required: true, message: "请输入所在位置" }]}>
|
|
|
|
|
+ <Input placeholder="请输入所在位置" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="lat" label="纬度" rules={[{ required: true, message: "请输入纬度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入纬度" type="number" step="0.000001" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="lng" label="经度" rules={[{ required: true, message: "请输入经度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入经度" type="number" step="0.000001" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="battery" label="电池电量(%)" rules={[{ required: true, message: "请输入电池电量" }]}>
|
|
|
|
|
+ <Input placeholder="请输入电池电量" type="number" min="0" max="100" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="signal" label="信号强度(%)" rules={[{ required: true, message: "请输入信号强度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入信号强度" type="number" min="0" max="100" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </Form>
|
|
|
|
|
+ </Modal>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 编辑设备弹窗 */}
|
|
|
|
|
+ <Modal
|
|
|
|
|
+ title="编辑设备"
|
|
|
|
|
+ open={editDeviceModalVisible}
|
|
|
|
|
+ onCancel={() => {
|
|
|
|
|
+ setEditDeviceModalVisible(false)
|
|
|
|
|
+ setEditingDevice(null)
|
|
|
|
|
+ editDeviceForm.resetFields()
|
|
|
|
|
+ }}
|
|
|
|
|
+ onOk={() => editDeviceForm.submit()}
|
|
|
|
|
+ width={600}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Form form={editDeviceForm} layout="vertical" onFinish={handleEditDevice}>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="name" label="设备编号" rules={[{ required: true, message: "请输入设备编号" }]}>
|
|
|
|
|
+ <Input placeholder="请输入设备编号" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="type" label="设备类型" rules={[{ required: true, message: "请选择设备类型" }]}>
|
|
|
|
|
+ <Select placeholder="请选择设备类型">
|
|
|
|
|
+ <Option value="智能井盖">智能井盖</Option>
|
|
|
|
|
+ <Option value="传感器">传感器</Option>
|
|
|
|
|
+ <Option value="监控设备">监控设备</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="status" label="运维状态" rules={[{ required: true, message: "请选择运维状态" }]}>
|
|
|
|
|
+ <Select placeholder="请选择运维状态">
|
|
|
|
|
+ <Option value="normal">正常</Option>
|
|
|
|
|
+ <Option value="warning">告警</Option>
|
|
|
|
|
+ <Option value="error">故障</Option>
|
|
|
|
|
+ <Option value="maintenance">维修中</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="location" label="所在位置" rules={[{ required: true, message: "请输入所在位置" }]}>
|
|
|
|
|
+ <Input placeholder="请输入所在位置" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="lat" label="纬度" rules={[{ required: true, message: "请输入纬度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入纬度" type="number" step="0.000001" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="lng" label="经度" rules={[{ required: true, message: "请输入经度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入经度" type="number" step="0.000001" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ <Row gutter={16}>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="battery" label="电池电量(%)" rules={[{ required: true, message: "请输入电池电量" }]}>
|
|
|
|
|
+ <Input placeholder="请输入电池电量" type="number" min="0" max="100" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Form.Item name="signal" label="信号强度(%)" rules={[{ required: true, message: "请输入信号强度" }]}>
|
|
|
|
|
+ <Input placeholder="请输入信号强度" type="number" min="0" max="100" />
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ </Form>
|
|
|
|
|
+ </Modal>
|
|
|
|
|
+ </Layout>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|