Quellcode durchsuchen

feat(app): 新增风险可视化组件

- 添加风险可视化组件,包含风险点位、风险类型分布、风险趋势分析等功能
-集成地图组件和图表组件,实现风险四色图、区域风险热力图等展示
-增加图层控制、风险阈值设置等交互功能
- 引入 tailwind 动画插件,优化界面动画效果
nahida vor 9 Monaten
Ursprung
Commit
c9d146ed2f
2 geänderte Dateien mit 480 neuen und 0 gelöschten Zeilen
  1. 468 0
      app/(other)/test8/components/risk-visualization.tsx
  2. 12 0
      tailwind.config.ts

+ 468 - 0
app/(other)/test8/components/risk-visualization.tsx

@@ -0,0 +1,468 @@
+"use client"
+
+import {useState} from "react"
+import {Button, Card, Col, Row, Select, Slider, Space, Switch, Tag} from "antd"
+import {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 mockRiskPoints = [
+  {
+    id: "R001",
+    name: "解放路段高压管线风险",
+    position: [39.9042, 116.4074],
+    riskLevel: "高",
+    riskScore: 85,
+    riskType: "管线老化",
+    color: "#f5222d",
+    affectedPopulation: 2000,
+  },
+  {
+    id: "R002",
+    name: "人民路段泄漏风险",
+    position: [39.9052, 116.4084],
+    riskLevel: "中",
+    riskScore: 65,
+    riskType: "泄漏隐患",
+    color: "#faad14",
+    affectedPopulation: 800,
+  },
+  {
+    id: "R003",
+    name: "建设路段压力风险",
+    position: [39.9032, 116.4064],
+    riskLevel: "低",
+    riskScore: 35,
+    riskType: "压力异常",
+    color: "#52c41a",
+    affectedPopulation: 300,
+  },
+  {
+    id: "R004",
+    name: "商业街管网风险",
+    position: [39.9062, 116.4094],
+    riskLevel: "中",
+    riskScore: 70,
+    riskType: "管线老化",
+    color: "#faad14",
+    affectedPopulation: 1500,
+  },
+]
+
+// 模拟风险区域数据(热力图)
+const mockRiskHeatmapData = [
+  { x: "解放路", y: "北段", value: 85 },
+  { x: "解放路", y: "中段", value: 65 },
+  { x: "解放路", y: "南段", value: 45 },
+  { x: "人民路", y: "北段", value: 70 },
+  { x: "人民路", y: "中段", value: 55 },
+  { x: "人民路", y: "南段", value: 40 },
+  { x: "建设路", y: "北段", value: 35 },
+  { x: "建设路", y: "中段", value: 50 },
+  { x: "建设路", y: "南段", value: 60 },
+  { x: "商业街", y: "东段", value: 75 },
+  { x: "商业街", y: "西段", value: 80 },
+]
+
+// 模拟风险统计数据
+const mockRiskTypeStats = [
+  { type: "管线老化", count: 12, percentage: 48 },
+  { type: "泄漏隐患", count: 8, percentage: 32 },
+  { type: "压力异常", count: 3, percentage: 12 },
+  { type: "其他", count: 2, percentage: 8 },
+]
+
+const mockRiskLevelStats = [
+  { level: "高风险", count: 5, color: "#f5222d" },
+  { level: "中风险", count: 12, color: "#faad14" },
+  { level: "低风险", count: 8, color: "#52c41a" },
+]
+
+const mockRiskTrendData = [
+  { month: "2023-07", 高风险: 3, 中风险: 8, 低风险: 12 },
+  { month: "2023-08", 高风险: 4, 中风险: 10, 低风险: 11 },
+  { month: "2023-09", 高风险: 6, 中风险: 12, 低风险: 9 },
+  { month: "2023-10", 高风险: 5, 中风险: 11, 低风险: 10 },
+  { month: "2023-11", 高风险: 4, 中风险: 9, 低风险: 12 },
+  { month: "2023-12", 高风险: 3, 中风险: 8, 低风险: 14 },
+  { month: "2024-01", 高风险: 5, 中风险: 12, 低风险: 8 },
+]
+
+export default function RiskVisualization() {
+  const [layerVisibility, setLayerVisibility] = useState({
+    pipeline: true,
+    highRisk: true,
+    mediumRisk: true,
+    lowRisk: true,
+    riskHeatmap: true,
+    riskBuffer: true,
+  })
+  const [riskThreshold, setRiskThreshold] = useState([40, 70])
+  const [selectedRiskType, setSelectedRiskType] = useState("all")
+  const [isFullscreen, setIsFullscreen] = useState(false)
+
+  const handleLayerToggle = (layer: string, visible: boolean) => {
+    setLayerVisibility((prev) => ({
+      ...prev,
+      [layer]: visible,
+    }))
+  }
+
+  const handleRefresh = () => {
+    console.log("刷新风险数据")
+  }
+
+  const getRiskLevelColor = (level: string) => {
+    switch (level) {
+      case "高":
+        return "#f5222d"
+      case "中":
+        return "#faad14"
+      case "低":
+        return "#52c41a"
+      default:
+        return "#1890ff"
+    }
+  }
+
+  const pieOption = {
+    title: { text: "风险类型分布", left: "center" },
+    tooltip: { trigger: "item" },
+    legend: { orient: "vertical", left: "left" },
+    series: [
+      {
+        type: "pie",
+        radius: "50%",
+        data: mockRiskTypeStats.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: "风险趋势分析", left: "center" },
+    tooltip: { trigger: "axis" },
+    legend: { data: ["高风险", "中风险", "低风险"] },
+    grid: { left: "3%", right: "4%", bottom: "3%", containLabel: true },
+    xAxis: {
+      type: "category",
+      data: mockRiskTrendData.map((item) => item.month),
+    },
+    yAxis: { type: "value", stack: "total" },
+    series: [
+      {
+        name: "高风险",
+        type: "bar",
+        stack: "total",
+        data: mockRiskTrendData.map((item) => item.高风险),
+        itemStyle: { color: "#f5222d" },
+      },
+      {
+        name: "中风险",
+        type: "bar",
+        stack: "total",
+        data: mockRiskTrendData.map((item) => item.中风险),
+        itemStyle: { color: "#faad14" },
+      },
+      {
+        name: "低风险",
+        type: "bar",
+        stack: "total",
+        data: mockRiskTrendData.map((item) => item.低风险),
+        itemStyle: { color: "#52c41a" },
+      },
+    ],
+  }
+
+  const heatmapOption = {
+    title: { text: "区域风险热力图", left: "center" },
+    tooltip: { position: "top" },
+    grid: { height: "50%", top: "10%" },
+    xAxis: {
+      type: "category",
+      data: [...new Set(mockRiskHeatmapData.map((item) => item.x))],
+      splitArea: { show: true },
+    },
+    yAxis: {
+      type: "category",
+      data: [...new Set(mockRiskHeatmapData.map((item) => item.y))],
+      splitArea: { show: true },
+    },
+    visualMap: {
+      min: 0,
+      max: 100,
+      calculable: true,
+      orient: "horizontal",
+      left: "center",
+      bottom: "15%",
+      inRange: {
+        color: ["#52c41a", "#faad14", "#f5222d"],
+      },
+    },
+    series: [
+      {
+        type: "heatmap",
+        data: mockRiskHeatmapData.map((item) => [item.x, item.y, item.value]),
+        label: {
+          show: true,
+          color: "#fff",
+          fontSize: 12,
+        },
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowColor: "rgba(0, 0, 0, 0.5)",
+          },
+        },
+      },
+    ],
+  }
+
+  const highRiskCount = mockRiskPoints.filter((point) => point.riskLevel === "高").length
+  const mediumRiskCount = mockRiskPoints.filter((point) => point.riskLevel === "中").length
+  const lowRiskCount = mockRiskPoints.filter((point) => point.riskLevel === "低").length
+
+  return (
+    <div className="p-6">
+      <Card title="风险评估可视化">
+        {/* 风险概览统计 */}
+        <Row gutter={16} className="mb-6">
+          <Col span={6}>
+            <Card size="small">
+              <div className="text-center">
+                <div className="text-2xl font-bold text-red-600">{highRiskCount}</div>
+                <div className="text-gray-600">高风险区域</div>
+              </div>
+            </Card>
+          </Col>
+          <Col span={6}>
+            <Card size="small">
+              <div className="text-center">
+                <div className="text-2xl font-bold text-orange-600">{mediumRiskCount}</div>
+                <div className="text-gray-600">中风险区域</div>
+              </div>
+            </Card>
+          </Col>
+          <Col span={6}>
+            <Card size="small">
+              <div className="text-center">
+                <div className="text-2xl font-bold text-blue-600">{lowRiskCount}</div>
+                <div className="text-gray-600">低风险区域</div>
+              </div>
+            </Card>
+          </Col>
+          <Col span={6}>
+            <Card size="small">
+              <div className="text-center">
+                <div className="text-2xl font-bold text-gray-600">{mockRiskPoints.length}</div>
+                <div className="text-gray-600">风险点总数</div>
+              </div>
+            </Card>
+          </Col>
+        </Row>
+
+        {/* 图层控制 */}
+        <Row gutter={16} className="mb-4">
+          <Col span={24}>
+            <Card size="small" title="风险图层控制">
+              <Row gutter={16}>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.pipeline}
+                      onChange={(checked) => handleLayerToggle("pipeline", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">管网管线</span>
+                  </div>
+                </Col>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.highRisk}
+                      onChange={(checked) => handleLayerToggle("highRisk", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">高风险区域</span>
+                  </div>
+                </Col>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.mediumRisk}
+                      onChange={(checked) => handleLayerToggle("mediumRisk", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">中风险区域</span>
+                  </div>
+                </Col>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.lowRisk}
+                      onChange={(checked) => handleLayerToggle("lowRisk", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">低风险区域</span>
+                  </div>
+                </Col>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.riskHeatmap}
+                      onChange={(checked) => handleLayerToggle("riskHeatmap", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">风险热力图</span>
+                  </div>
+                </Col>
+                <Col span={4}>
+                  <div className="mb-2">
+                    <Switch
+                      checked={layerVisibility.riskBuffer}
+                      onChange={(checked) => handleLayerToggle("riskBuffer", checked)}
+                      size="small"
+                    />
+                    <span className="ml-2">影响范围</span>
+                  </div>
+                </Col>
+              </Row>
+            </Card>
+          </Col>
+        </Row>
+
+        {/* 风险控制参数 */}
+        <Row gutter={16} className="mb-4">
+          <Col span={8}>
+            <Card size="small" title="风险阈值设置">
+              <div className="mb-2">
+                <span className="text-sm text-gray-600">风险评分范围: </span>
+              </div>
+              <Slider
+                range
+                min={0}
+                max={100}
+                value={riskThreshold}
+                onChange={setRiskThreshold}
+                marks={{ 0: "0", 40: "40", 70: "70", 100: "100" }}
+              />
+              <div className="text-center text-sm text-gray-500 mt-2">
+                当前范围: {riskThreshold[0]} - {riskThreshold[1]}
+              </div>
+            </Card>
+          </Col>
+          <Col span={8}>
+            <Card size="small" title="风险类型筛选">
+              <Select value={selectedRiskType} onChange={setSelectedRiskType} style={{ width: "100%" }}>
+                <Option value="all">全部类型</Option>
+                <Option value="aging">管线老化</Option>
+                <Option value="leak">泄漏隐患</Option>
+                <Option value="pressure">压力异常</Option>
+                <Option value="other">其他</Option>
+              </Select>
+            </Card>
+          </Col>
+          <Col span={8}>
+            <Card size="small" title="操作控制">
+              <Space>
+                <Button icon={<ReloadOutlined />} onClick={handleRefresh} size="small">
+                  刷新数据
+                </Button>
+                <Button icon={<FullscreenOutlined />} onClick={() => setIsFullscreen(!isFullscreen)} size="small">
+                  全屏显示
+                </Button>
+              </Space>
+            </Card>
+          </Col>
+        </Row>
+
+        {/* 风险四色图例 */}
+        <Row gutter={16} className="mb-4">
+          <Col span={24}>
+            <Card size="small" title="风险四色图例">
+              <Space>
+                <Tag color="#f5222d">红色 - 高风险 (80-100分)</Tag>
+                <Tag color="#faad14">橙色 - 中高风险 (60-79分)</Tag>
+                <Tag color="#52c41a">黄色 - 中低风险 (40-59分)</Tag>
+                <Tag color="#1890ff">蓝色 - 低风险 (0-39分)</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: 200 }} />
+                </Card>
+              </Col>
+              <Col span={24}>
+                <Card title="风险趋势分析" size="small" className="mb-4">
+                  <EChart option={columnOption} style={{ height: 200 }} />
+                </Card>
+              </Col>
+              <Col span={24}>
+                <Card title="区域风险热力图" size="small">
+                  <EChart option={heatmapOption} style={{ height: 200 }} />
+                </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">
+                {mockRiskPoints
+                  .filter((point) => point.riskLevel === "高")
+                  .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={getRiskLevelColor(point.riskLevel)}>{point.riskLevel}风险</Tag>
+                          <Tag color="blue">评分: {point.riskScore}</Tag>
+                        </div>
+                      </div>
+                      <div className="text-sm text-gray-600 space-y-1">
+                        <div>风险类型: {point.riskType}</div>
+                        <div>影响人口: {point.affectedPopulation}人</div>
+                        <div>风险等级: {point.riskLevel}</div>
+                      </div>
+                    </div>
+                  ))}
+              </div>
+            </Card>
+          </Col>
+        </Row>
+      </Card>
+    </div>
+  )
+}

+ 12 - 0
tailwind.config.ts

@@ -0,0 +1,12 @@
+// tailwind.config.js
+// @ts-ignore
+import twAnimateCss from 'tw-animate-css';
+
+// eslint-disable-next-line import/no-anonymous-default-export
+export default {
+  // ...其他配置
+  plugins: [
+    twAnimateCss, // 引入插件
+    // ...其他插件
+  ],
+}