| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- "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>
- )
- }
|