| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- "use client"
- import {useState} from "react"
- import {Button, Card, Col, Row, Select, Space, Statistic, Switch, Tag} from "antd"
- import {BellOutlined, FullscreenOutlined, ReloadOutlined} from "@ant-design/icons"
- import EChart from "@/components/echarts"
- import dynamic from "next/dynamic"
- const GasNetworkMap = dynamic(() => import("./gas-network-map"), {
- ssr: false,
- loading: () => <div className="flex items-center justify-center h-96">加载地图中...</div>,
- })
- const { Option } = Select
- // 模拟报警点位数据
- const mockAlarmPoints = [
- {
- id: "A001",
- name: "解放路压力报警",
- position: [39.9042, 116.4074],
- type: "pressure",
- level: "高",
- status: "未处理",
- createTime: "2024-01-15 14:30:25",
- value: 0.52,
- threshold: 0.45,
- unit: "MPa",
- },
- {
- id: "A002",
- name: "人民路流量报警",
- position: [39.9052, 116.4084],
- type: "flow",
- level: "中",
- status: "处理中",
- createTime: "2024-01-15 13:45:10",
- value: 4200,
- threshold: 3500,
- unit: "m³/h",
- },
- {
- id: "A003",
- name: "建设路泄漏报警",
- position: [39.9032, 116.4064],
- type: "leakage",
- level: "高",
- status: "已处理",
- createTime: "2024-01-15 12:20:15",
- value: 0.08,
- threshold: 0.05,
- unit: "ppm",
- },
- ]
- // 模拟报警统计数据
- const mockAlarmTypeStats = [
- { type: "压力异常", count: 15, percentage: 42.9 },
- { type: "流量异常", count: 12, percentage: 34.3 },
- { type: "泄漏检测", count: 6, percentage: 17.1 },
- { type: "温度异常", count: 2, percentage: 5.7 },
- ]
- const mockAlarmLevelStats = [
- { level: "高", count: 8, color: "#f5222d" },
- { level: "中", count: 18, color: "#faad14" },
- { level: "低", count: 9, color: "#52c41a" },
- ]
- const mockHourlyStats = [
- { hour: "00", count: 2 },
- { hour: "01", count: 1 },
- { hour: "02", count: 0 },
- { hour: "03", count: 1 },
- { hour: "04", count: 3 },
- { hour: "05", count: 2 },
- { hour: "06", count: 4 },
- { hour: "07", count: 6 },
- { hour: "08", count: 8 },
- { hour: "09", count: 5 },
- { hour: "10", count: 7 },
- { hour: "11", count: 9 },
- { hour: "12", count: 12 },
- { hour: "13", count: 8 },
- { hour: "14", count: 6 },
- { hour: "15", count: 4 },
- { hour: "16", count: 3 },
- { hour: "17", count: 5 },
- { hour: "18", count: 7 },
- { hour: "19", count: 4 },
- { hour: "20", count: 2 },
- { hour: "21", count: 1 },
- { hour: "22", count: 1 },
- { hour: "23", count: 0 },
- ]
- export default function AlarmVisualization() {
- const [layerVisibility, setLayerVisibility] = useState({
- pipeline: true,
- pressureAlarm: true,
- flowAlarm: true,
- temperatureAlarm: true,
- leakageAlarm: true,
- highLevel: true,
- mediumLevel: true,
- lowLevel: true,
- })
- const [selectedTimeRange, setSelectedTimeRange] = useState("24hours")
- const [isFullscreen, setIsFullscreen] = useState(false)
- const handleLayerToggle = (layer: string, visible: boolean) => {
- setLayerVisibility((prev) => ({
- ...prev,
- [layer]: visible,
- }))
- }
- const handleRefresh = () => {
- console.log("刷新报警数据")
- }
- const getStatusColor = (status: string) => {
- switch (status) {
- case "未处理":
- return "#f5222d"
- case "处理中":
- return "#faad14"
- case "已处理":
- return "#52c41a"
- default:
- return "#1890ff"
- }
- }
- const getLevelColor = (level: string) => {
- switch (level) {
- case "高":
- return "#f5222d"
- case "中":
- return "#faad14"
- case "低":
- return "#52c41a"
- default:
- return "#1890ff"
- }
- }
- const getTypeLabel = (type: string) => {
- const typeMap = {
- pressure: "压力",
- flow: "流量",
- temperature: "温度",
- leakage: "泄漏",
- }
- return typeMap[type as keyof typeof typeMap] || type
- }
- const pieOption = {
- title: { text: "报警类型分布", left: "center" },
- tooltip: { trigger: "item" },
- legend: { orient: "vertical", left: "left" },
- series: [
- {
- type: "pie",
- radius: "50%",
- data: mockAlarmTypeStats.map((item) => ({
- value: item.count,
- name: item.type,
- })),
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: "rgba(0, 0, 0, 0.5)",
- },
- },
- },
- ],
- }
- const columnOption = {
- title: { text: "24小时报警分布", left: "center" },
- tooltip: { trigger: "axis" },
- grid: { left: "3%", right: "4%", bottom: "3%", containLabel: true },
- xAxis: {
- type: "category",
- data: mockHourlyStats.map((item) => item.hour + ":00"),
- },
- yAxis: { type: "value", name: "报警数量" },
- series: [
- {
- type: "bar",
- data: mockHourlyStats.map((item) => item.count),
- itemStyle: { color: "#f5222d" },
- },
- ],
- }
- const unhandledCount = mockAlarmPoints.filter((point) => point.status === "未处理").length
- const processingCount = mockAlarmPoints.filter((point) => point.status === "处理中").length
- const handledCount = mockAlarmPoints.filter((point) => point.status === "已处理").length
- return (
- <div className="p-6">
- <Card title="监测报警可视化">
- {/* 报警概览统计 */}
- <Row gutter={16} className="mb-6">
- <Col span={6}>
- <Card size="small">
- <Statistic
- title="未处理报警"
- value={unhandledCount}
- valueStyle={{ color: "#f5222d" }}
- prefix={<BellOutlined />}
- />
- </Card>
- </Col>
- <Col span={6}>
- <Card size="small">
- <Statistic title="处理中" value={processingCount} valueStyle={{ color: "#faad14" }} />
- </Card>
- </Col>
- <Col span={6}>
- <Card size="small">
- <Statistic title="已处理" value={handledCount} valueStyle={{ color: "#52c41a" }} />
- </Card>
- </Col>
- <Col span={6}>
- <Card size="small">
- <Statistic title="报警总数" value={mockAlarmPoints.length} valueStyle={{ color: "#1890ff" }} />
- </Card>
- </Col>
- </Row>
- {/* 图层控制 */}
- <Row gutter={16} className="mb-4">
- <Col span={24}>
- <Card size="small" title="报警图层控制">
- <Row gutter={16}>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.pipeline}
- onChange={(checked) => handleLayerToggle("pipeline", checked)}
- size="small"
- />
- <span className="ml-2">管网管线</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.pressureAlarm}
- onChange={(checked) => handleLayerToggle("pressureAlarm", checked)}
- size="small"
- />
- <span className="ml-2">压力报警</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.flowAlarm}
- onChange={(checked) => handleLayerToggle("flowAlarm", checked)}
- size="small"
- />
- <span className="ml-2">流量报警</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.leakageAlarm}
- onChange={(checked) => handleLayerToggle("leakageAlarm", checked)}
- size="small"
- />
- <span className="ml-2">泄漏报警</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.highLevel}
- onChange={(checked) => handleLayerToggle("highLevel", checked)}
- size="small"
- />
- <span className="ml-2">高级报警</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.mediumLevel}
- onChange={(checked) => handleLayerToggle("mediumLevel", checked)}
- size="small"
- />
- <span className="ml-2">中级报警</span>
- </div>
- </Col>
- <Col span={3}>
- <div className="mb-2">
- <Switch
- checked={layerVisibility.lowLevel}
- onChange={(checked) => handleLayerToggle("lowLevel", checked)}
- size="small"
- />
- <span className="ml-2">低级报警</span>
- </div>
- </Col>
- <Col span={3}>
- <Space>
- <Button icon={<ReloadOutlined />} onClick={handleRefresh} size="small">
- 刷新
- </Button>
- <Button icon={<FullscreenOutlined />} onClick={() => setIsFullscreen(!isFullscreen)} size="small">
- 全屏
- </Button>
- </Space>
- </Col>
- </Row>
- </Card>
- </Col>
- </Row>
- {/* 时间范围选择 */}
- <Row gutter={16} className="mb-4">
- <Col span={6}>
- <Card size="small" title="时间范围">
- <Select value={selectedTimeRange} onChange={setSelectedTimeRange} style={{ width: "100%" }}>
- <Option value="1hour">近1小时</Option>
- <Option value="6hours">近6小时</Option>
- <Option value="24hours">近24小时</Option>
- <Option value="7days">近7天</Option>
- <Option value="30days">近30天</Option>
- </Select>
- </Card>
- </Col>
- <Col span={18}>
- <Card size="small" title="报警态势图例">
- <Space>
- <Tag color="#f5222d">高级报警</Tag>
- <Tag color="#faad14">中级报警</Tag>
- <Tag color="#52c41a">低级报警</Tag>
- <Tag color="#1890ff">压力异常</Tag>
- <Tag color="#722ed1">流量异常</Tag>
- <Tag color="#13c2c2">泄漏检测</Tag>
- <Tag color="#eb2f96">温度异常</Tag>
- </Space>
- </Card>
- </Col>
- </Row>
- {/* 地图和统计图表 */}
- <Row gutter={16}>
- <Col span={16}>
- <Card title="报警态势一张图" size="small" className={isFullscreen ? "fixed inset-0 z-50" : ""}>
- <GasNetworkMap height={isFullscreen ? "calc(100vh - 120px)" : "600px"} />
- </Card>
- </Col>
- <Col span={8}>
- <Row gutter={16}>
- <Col span={24}>
- <Card title="报警类型分布" size="small" className="mb-4">
- <EChart option={pieOption} style={{ height: 250 }} />
- </Card>
- </Col>
- <Col span={24}>
- <Card title="24小时报警分布" size="small">
- <EChart option={columnOption} style={{ height: 250 }} />
- </Card>
- </Col>
- </Row>
- </Col>
- </Row>
- {/* 报警点位详情 */}
- <Row gutter={16} className="mt-4">
- <Col span={24}>
- <Card title="当前活跃报警" size="small">
- <div className="space-y-3 max-h-64 overflow-y-auto">
- {mockAlarmPoints
- .filter((point) => point.status !== "已处理")
- .map((point) => (
- <div key={point.id} className="p-3 border rounded-lg">
- <div className="flex justify-between items-center mb-2">
- <span className="font-medium">{point.name}</span>
- <div className="space-x-2">
- <Tag color={getLevelColor(point.level)}>{point.level}</Tag>
- <Tag color={getStatusColor(point.status)}>{point.status}</Tag>
- </div>
- </div>
- <div className="text-sm text-gray-600 space-y-1">
- <div>类型: {getTypeLabel(point.type)}</div>
- <div>
- 异常值: {point.value} {point.unit} (阈值: {point.threshold} {point.unit})
- </div>
- <div>报警时间: {point.createTime}</div>
- </div>
- </div>
- ))}
- </div>
- </Card>
- </Col>
- </Row>
- </Card>
- </div>
- )
- }
|