| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- "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"
- import GisMapBaidu from "@/components/gisMapBaidu";
- 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" }}>
- <GisMapBaidu height={'50'} />
- </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>
- )
- }
|