浏览代码

feat:燃气管网-可燃气体监测 管线压力监测实现接口联调

null 1 周之前
父节点
当前提交
28f1c8c26c
共有 2 个文件被更改,包括 172 次插入385 次删除
  1. 71 144
      src/views/subSystem/gas/gasOperamon/gxyljc.vue
  2. 101 241
      src/views/subSystem/gas/gasOperamon/krqildjc.vue

+ 71 - 144
src/views/subSystem/gas/gasOperamon/gxyljc.vue

@@ -45,9 +45,9 @@
         <div class="point-list">
           <div
             v-for="point in filteredPoints"
-            :key="point.id"
+            :key="point.pointId"
             class="point-item"
-            :class="{ active: selectedPoint?.id === point.id, [point.status]: true }"
+            :class="{ active: selectedPoint?.pointId === point.pointId, [point.status]: true }"
             @click="selectPoint(point)"
           >
             <div class="point-header">
@@ -135,7 +135,6 @@
                   </span>
                 </template>
               </el-table-column>
-              <el-table-column prop="temperature" label="温度(°C)" width="100" />
               <el-table-column prop="status" label="状态" width="100">
                 <template #default="{ row }">
                   <el-tag :type="row.pressure > selectedPoint.maxPressure || row.pressure < selectedPoint.minPressure ? 'danger' : 'success'" size="small">
@@ -149,7 +148,7 @@
             <el-pagination
                 v-model:current-page="historyPage"
                 v-model:page-size="historyPageSize"
-                :total="historyData.length"
+                :total="pagedTotal"
                 layout="total, prev, pager, next"
                 small
             />
@@ -170,6 +169,7 @@
 import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
 import { Odometer, Search, Refresh } from '@element-plus/icons-vue'
 import * as echarts from 'echarts'
+import { getPressureMonitorPoints, getPressureMonitorTrend, getPressureMonitorHistory } from '@/api/pipeNetwork/gasMonitor'
 
 // 筛选
 const searchKeyword = ref('')
@@ -187,94 +187,34 @@ const historyPageSize = ref(10)
 const chartRef = ref(null)
 let chartInstance = null
 
-// ==================== 沅陵县管线压力测点数据 ====================
-const mockPoints = ref([
-  {
-    id: 1, name: '辰州北路高压测点1#', pipelineName: '辰州北路高压段', pressureLevel: 'high',
-    address: '沅陵县太常片区辰州北路与建设路交叉口北侧', lng: 110.3938, lat: 28.4692,
-    latestPressure: 385, minPressure: 240, maxPressure: 420, status: 'normal', latestTime: '2026-06-13 15:32:18'
-  },
-  {
-    id: 2, name: '辰州中路高压测点2#', pipelineName: '辰州中路高压段', pressureLevel: 'high',
-    address: '沅陵县太常片区辰州中路与天宁路交叉口南侧', lng: 110.3935, lat: 28.4658,
-    latestPressure: 432, minPressure: 240, maxPressure: 420, status: 'danger', latestTime: '2026-06-13 15:31:45'
-  },
-  {
-    id: 3, name: '辰州南路高压测点3#', pipelineName: '辰州南路高压段', pressureLevel: 'high',
-    address: '沅陵县太常片区辰州南路近滨江路口', lng: 110.3934, lat: 28.4580,
-    latestPressure: 368, minPressure: 240, maxPressure: 420, status: 'normal', latestTime: '2026-06-13 15:32:05'
-  },
-  {
-    id: 4, name: '古城北路中压测点1#', pipelineName: '古城北路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区古城北路与天宁路交叉口', lng: 110.3977, lat: 28.4741,
-    latestPressure: 218, minPressure: 150, maxPressure: 250, status: 'normal', latestTime: '2026-06-13 15:30:22'
-  },
-  {
-    id: 5, name: '古城中路中压测点2#', pipelineName: '古城中路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区古城中路太常安置区段', lng: 110.3970, lat: 28.4669,
-    latestPressure: 142, minPressure: 150, maxPressure: 250, status: 'warning', latestTime: '2026-06-13 15:29:58'
-  },
-  {
-    id: 6, name: '古城南路中压测点3#', pipelineName: '古城南路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区古城南路近建设路口', lng: 110.3963, lat: 28.4598,
-    latestPressure: 205, minPressure: 150, maxPressure: 250, status: 'normal', latestTime: '2026-06-13 15:31:30'
-  },
-  {
-    id: 7, name: '迎宾西路中压测点1#', pipelineName: '迎宾西路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区迎宾西路县政府西侧', lng: 110.3824, lat: 28.4645,
-    latestPressure: 188, minPressure: 150, maxPressure: 250, status: 'normal', latestTime: '2026-06-13 15:28:15'
-  },
-  {
-    id: 8, name: '迎宾东路中压测点2#', pipelineName: '迎宾东路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区迎宾东路城郊公交站东侧', lng: 110.4054, lat: 28.4630,
-    latestPressure: 198, minPressure: 150, maxPressure: 250, status: 'normal', latestTime: '2026-06-13 15:30:50'
-  },
-  {
-    id: 9, name: '建设路中压测点1#', pipelineName: '建设路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区建设路县政府南侧', lng: 110.3937, lat: 28.4582,
-    latestPressure: 0, minPressure: 150, maxPressure: 250, status: 'offline', latestTime: '2026-06-13 12:05:00'
-  },
-  {
-    id: 10, name: '建设西路中压测点2#', pipelineName: '建设西路中压段', pressureLevel: 'medium',
-    address: '沅陵县太常片区建设西路人行道绿化带', lng: 110.3840, lat: 28.4588,
-    latestPressure: 175, minPressure: 150, maxPressure: 250, status: 'normal', latestTime: '2026-06-13 15:29:30'
-  },
-  {
-    id: 11, name: '滨江路低压测点1#', pipelineName: '滨江路段低压段', pressureLevel: 'low',
-    address: '沅陵县太常片区滨江西路河堤段', lng: 110.3820, lat: 28.4568,
-    latestPressure: 112, minPressure: 80, maxPressure: 150, status: 'normal', latestTime: '2026-06-13 15:31:10'
-  },
-  {
-    id: 12, name: '滨江东路低压测点2#', pipelineName: '滨江东路低压段', pressureLevel: 'low',
-    address: '沅陵县太常片区滨江东路与天宁东路交叉口', lng: 110.4066, lat: 28.4557,
-    latestPressure: 78, minPressure: 80, maxPressure: 150, status: 'warning', latestTime: '2026-06-13 15:30:40'
-  },
-  {
-    id: 13, name: '天宁西路低压测点1#', pipelineName: '天宁西路低压段', pressureLevel: 'low',
-    address: '沅陵县太常片区天宁西路太常卫生院外墙', lng: 110.3850, lat: 28.4703,
-    latestPressure: 128, minPressure: 80, maxPressure: 150, status: 'normal', latestTime: '2026-06-13 15:32:00'
-  },
-  {
-    id: 14, name: '天宁东路低压测点2#', pipelineName: '天宁东路低压段', pressureLevel: 'low',
-    address: '沅陵县太常片区天宁东路末端近滨江路', lng: 110.4088, lat: 28.4690,
-    latestPressure: 136, minPressure: 80, maxPressure: 150, status: 'normal', latestTime: '2026-06-13 15:31:55'
-  },
-  {
-    id: 15, name: '建设东路高压测点4#', pipelineName: '建设东路高压段', pressureLevel: 'high',
-    address: '沅陵县太常片区建设东路工业园入口处', lng: 110.4050, lat: 28.4577,
-    latestPressure: 410, minPressure: 240, maxPressure: 420, status: 'warning', latestTime: '2026-06-13 15:31:25'
+// ==================== 从后端加载测点数据 ====================
+const points = ref([])
+const loading = ref(false)
+const trendData = ref({ times: [], pressures: [], minPressure: 0, maxPressure: 0 })
+const historyTotal = ref(0)
+
+async function loadPoints() {
+  loading.value = true
+  try {
+    const res = await getPressureMonitorPoints()
+    points.value = res.data || []
+    if (points.value.length > 0) {
+      selectPoint(points.value[0])
+    }
+  } finally {
+    loading.value = false
   }
-])
+}
 
 // 统计数据
-const normalCount = computed(() => mockPoints.value.filter(p => p.status === 'normal').length)
-const warningCount = computed(() => mockPoints.value.filter(p => p.status === 'warning').length)
-const dangerCount = computed(() => mockPoints.value.filter(p => p.status === 'danger').length)
-const offlineCount = computed(() => mockPoints.value.filter(p => p.status === 'offline').length)
+const normalCount = computed(() => points.value.filter(p => p.status === 'normal').length)
+const warningCount = computed(() => points.value.filter(p => p.status === 'warning').length)
+const dangerCount = computed(() => points.value.filter(p => p.status === 'danger').length)
+const offlineCount = computed(() => points.value.filter(p => p.status === 'offline').length)
 
 // 筛选
 const filteredPoints = computed(() => {
-  let list = mockPoints.value
+  let list = points.value
   if (searchKeyword.value) {
     const kw = searchKeyword.value.toLowerCase()
     list = list.filter(p => p.name.toLowerCase().includes(kw) || p.pipelineName.toLowerCase().includes(kw))
@@ -297,67 +237,58 @@ function pressureLevelLabel(level) {
   return map[level] || level
 }
 
-// ==================== 近10天静态压力数据(每天6个时段: 0/4/8/12/16/20时) ====================
-function generateHistory(point) {
-  const baseP = point.latestPressure > 0 ? point.latestPressure : (point.minPressure + point.maxPressure) / 2
-  // 模拟每日压力波动基准模式: 凌晨低、早高峰高、午间稳、晚高峰高
-  const hourOffsets = [0, 4, 8, 12, 16, 20]
-  const hourFactors = [0.92, 0.95, 1.08, 1.02, 1.06, 0.98]
-  // 每点预设的压力日波动序列(10天,每天6个时段)
-  const daySeeds = [
-    [0, -3, 6, 2, 5, -1], [-2, -5, 4, -1, 3, -4], [1, 2, 8, 4, 6, 0], [-3, -2, 3, 0, 4, -2], [2, 1, 7, 3, 5, 1],
-    [-1, -4, 5, 1, 2, -3], [3, 0, 9, 5, 7, 2], [-4, -1, 2, -2, 1, -5], [0, 3, 6, 2, 4, -1], [2, -2, 5, 3, 6, 0]
-  ]
-  const data = []
-  const now = new Date()
-  for (let d = 9; d >= 0; d--) {
-    const dayIdx = 9 - d
-    for (let hi = 0; hi < 6; hi++) {
-      const h = hourOffsets[hi]
-      const date = new Date(now)
-      date.setDate(date.getDate() - d)
-      date.setHours(h, 0, 0)
-      const variation = baseP * (hourFactors[hi] - 1) + daySeeds[dayIdx][hi] * (point.maxPressure - point.minPressure) / 50
-      const pressure = Math.round((baseP + variation) * 10) / 10
-      const temp = (24 + (h < 6 ? 0 : h < 12 ? 3 : h < 18 ? 4 : 1)).toFixed(1)
-      data.push({
-        time: date.toISOString().replace('T', ' ').substring(0, 19),
-        pressure,
-        temperature: temp,
-        collector: `YLCG-${String(point.id).padStart(3, '0')}`,
-        remark: pressure > point.maxPressure ? '压力超上限' : pressure < point.minPressure ? '压力低于下限' : ''
-      })
-    }
+// ==================== 历史数据(从API分页获取) ====================
+const historyData = ref([])
+const pagedHistory = ref([])
+const pagedTotal = ref(0)
+
+async function loadHistory() {
+  if (!selectedPoint.value) return
+  try {
+    const res = await getPressureMonitorHistory(selectedPoint.value.pointId, historyPage.value, historyPageSize.value)
+    const data = res.data || {}
+    pagedHistory.value = data.rows || []
+    pagedTotal.value = data.total || 0
+  } catch {
+    pagedHistory.value = []
+    pagedTotal.value = 0
   }
-  return data
 }
 
-const historyData = ref([])
-const pagedHistory = computed(() => {
-  const start = (historyPage.value - 1) * historyPageSize.value
-  return historyData.value.slice(start, start + historyPageSize.value)
-})
-
 // 今日极值
 const todayMax = computed(() => {
-  const today = historyData.value.filter(d => d.time.startsWith('2026-06-13'))
-  if (!today.length) return '--'
-  return Math.max(...today.map(d => d.pressure))
+  const today = new Date().toISOString().substring(0, 10)
+  const todayData = pagedHistory.value.filter(d => d.time && d.time.startsWith(today))
+  if (!todayData.length) return '--'
+  return Math.max(...todayData.map(d => d.pressure))
 })
 const todayMin = computed(() => {
-  const today = historyData.value.filter(d => d.time.startsWith('2026-06-13'))
-  if (!today.length) return '--'
-  return Math.min(...today.map(d => d.pressure))
+  const today = new Date().toISOString().substring(0, 10)
+  const todayData = pagedHistory.value.filter(d => d.time && d.time.startsWith(today))
+  if (!todayData.length) return '--'
+  return Math.min(...todayData.map(d => d.pressure))
 })
 
 // ==================== 选择测点 ====================
-function selectPoint(point) {
+async function selectPoint(point) {
   selectedPoint.value = point
   historyPage.value = 1
-  historyData.value = generateHistory(point)
+  // 并行加载趋势和分页历史
+  await Promise.all([loadTrend(point.pointId), loadHistory()])
   nextTick(() => renderChart())
 }
 
+async function loadTrend(pointId) {
+  try {
+    const res = await getPressureMonitorTrend(pointId, 10)
+    if (res.data) {
+      trendData.value = res.data
+    }
+  } catch {
+    trendData.value = { times: [], pressures: [], minPressure: 0, maxPressure: 0 }
+  }
+}
+
 // ==================== ECharts趋势图 ====================
 function renderChart() {
   if (!chartRef.value || !selectedPoint.value) return
@@ -365,8 +296,8 @@ function renderChart() {
     chartInstance = echarts.init(chartRef.value)
   }
   const point = selectedPoint.value
-  const times = historyData.value.map(d => d.time.substring(5, 16).replace(' ', '\n'))
-  const pressures = historyData.value.map(d => d.pressure)
+  const times = trendData.value.times || []
+  const pressures = trendData.value.pressures || []
 
   chartInstance.setOption({
     tooltip: {
@@ -389,7 +320,7 @@ function renderChart() {
       type: 'value', name: 'kPa',
       axisLine: { show: false }, axisTick: { show: false },
       splitLine: { lineStyle: { color: '#f0f0f0' } },
-      min: Math.min(point.minPressure - 30, ...pressures) - 10,
+      min: Math.min(point.minPressure - 30, ...pressures.filter(v => v > 0)) - 10,
       max: Math.max(point.maxPressure + 30, ...pressures) + 10
     },
     series: [
@@ -418,20 +349,16 @@ function renderChart() {
   })
 }
 
-function refreshData() {
-  historyData.value = selectedPoint.value ? generateHistory(selectedPoint.value) : []
-  nextTick(() => renderChart())
+async function refreshData() {
+  await loadPoints()
 }
 
-watch(pressureLevel, () => {})
-watch(statusFilter, () => {})
+// 分页变化时重载历史
+watch(historyPage, () => { loadHistory() })
 
 onMounted(() => {
   window.addEventListener('resize', () => { try { chartInstance?.resize() } catch {} })
-  // 默认选中第一条数据
-  if (mockPoints.value.length > 0) {
-    selectPoint(mockPoints.value[0])
-  }
+  loadPoints()
 })
 
 onBeforeUnmount(() => {

+ 101 - 241
src/views/subSystem/gas/gasOperamon/krqildjc.vue

@@ -15,17 +15,7 @@
         </div>
       </div>
       <div class="header-right">
-        <el-date-picker
-            v-model="dateRange"
-            type="datetimerange"
-            range-separator="至"
-            start-placeholder="开始时间"
-            end-placeholder="结束时间"
-            style="width: 300px"
-            size="default"
-        />
         <el-button type="primary" :icon="Refresh" @click="refreshData">刷新</el-button>
-        <el-button :icon="Download" @click="exportReport">导出报表</el-button>
       </div>
     </div>
 
@@ -36,20 +26,10 @@
         <div class="kpi-label">最高甲烷浓度</div>
         <div class="kpi-trend">场站: {{ maxMethaneStation }}</div>
       </div>
-      <div class="kpi-card">
-        <div class="kpi-value">{{ avgInPressure }}<span class="unit">kPa</span></div>
-        <div class="kpi-label">平均进口压力</div>
-        <div class="kpi-trend">较昨日 ↑0.2%</div>
-      </div>
-      <div class="kpi-card">
-        <div class="kpi-value">{{ avgOutPressure }}<span class="unit">kPa</span></div>
-        <div class="kpi-label">平均出口压力</div>
-        <div class="kpi-trend">较昨日 ↓0.1%</div>
-      </div>
       <div class="kpi-card">
         <div class="kpi-value">{{ riskScore }}<span class="unit">分</span></div>
         <div class="kpi-label">综合风险评分</div>
-        <div class="kpi-trend trend-down">较昨日 ↓12%</div>
+        <div class="kpi-trend trend-down">越低越安全</div>
       </div>
     </div>
 
@@ -71,34 +51,30 @@
         <div class="station-list">
           <div
               v-for="station in paginatedStations"
-              :key="station.id"
+              :key="station.pointId"
               class="station-item"
               :class="{
-              selected: selectedStation?.id === station.id,
+              selected: selectedStation?.pointId === station.pointId,
               alert: station.alertStatus
             }"
               @click="selectStation(station)"
           >
             <div class="station-header">
-              <span class="station-name">{{ station.name }}</span>
+              <span class="station-name">{{ station.pointName }}</span>
               <el-tag :type="station.alertStatus ? 'danger' : 'success'" size="small">
                 {{ station.alertStatus ? '告警' : '正常' }}
               </el-tag>
             </div>
             <div class="station-address">
-              <el-icon><Location /></el-icon> {{ station.address }}
+              <el-icon><Location /></el-icon> {{ station.location }}
             </div>
             <div class="station-metrics">
               <div class="metric">
                 <span class="label">甲烷浓度</span>
-                <span class="value" :class="{ 'alert-value': station.methane > station.methaneThreshold }">
-                  {{ station.methane }} ppm
+                <span class="value" :class="{ 'alert-value': station.alertStatus }">
+                  {{ station.methaneValue }} ppm
                 </span>
               </div>
-              <div class="metric">
-                <span class="label">进口压力</span>
-                <span class="value">{{ station.inPressure }} kPa</span>
-              </div>
             </div>
           </div>
         </div>
@@ -117,44 +93,28 @@
       <div class="detail-panel" v-if="selectedStation">
         <div class="detail-header">
           <div class="title-section">
-            <h3>{{ selectedStation.name }}</h3>
+            <h3>{{ selectedStation.pointName }}</h3>
             <el-tag :type="selectedStation.alertStatus ? 'danger' : 'success'" size="large">
               {{ selectedStation.alertStatus ? '实时告警中' : '运行正常' }}
             </el-tag>
           </div>
           <div class="address-info">
-            <el-icon><Location /></el-icon> {{ selectedStation.address }}
+            <el-icon><Location /></el-icon> {{ selectedStation.location }}
           </div>
         </div>
 
         <!-- 实时监测数据卡片 -->
         <div class="realtime-cards">
-          <div class="realtime-card" :class="{ warning: selectedStation.methane > selectedStation.methaneThreshold }">
+          <div class="realtime-card" :class="{ warning: selectedStation.alertStatus }">
             <div class="card-icon"><el-icon><WindPower /></el-icon></div>
             <div class="card-content">
               <div class="card-label">甲烷浓度 (CH₄)</div>
-              <div class="card-value">{{ selectedStation.methane }} <span class="unit">ppm</span></div>
+              <div class="card-value">{{ selectedStation.methaneValue }} <span class="unit">ppm</span></div>
               <div class="card-threshold">阈值: {{ selectedStation.methaneThreshold }} ppm</div>
             </div>
           </div>
           <div class="realtime-card">
-            <div class="card-icon"><el-icon><Connection /></el-icon></div>
-            <div class="card-content">
-              <div class="card-label">进口压力</div>
-              <div class="card-value">{{ selectedStation.inPressure }} <span class="unit">kPa</span></div>
-              <div class="card-threshold">范围: 200-400 kPa</div>
-            </div>
-          </div>
-          <div class="realtime-card">
-            <div class="card-icon"><el-icon><Connection /></el-icon></div>
-            <div class="card-content">
-              <div class="card-label">出口压力</div>
-              <div class="card-value">{{ selectedStation.outPressure }} <span class="unit">kPa</span></div>
-              <div class="card-threshold">范围: 150-250 kPa</div>
-            </div>
-          </div>
-          <div class="realtime-card">
-            <div class="card-icon"><el-icon></el-icon></div>
+            <div class="card-icon"><el-icon><Odometer /></el-icon></div>
             <div class="card-content">
               <div class="card-label">环境温度</div>
               <div class="card-value">{{ selectedStation.temperature }} <span class="unit">°C</span></div>
@@ -166,35 +126,11 @@
         <!-- 监测数据趋势图 -->
         <div class="chart-section">
           <div class="section-header">
-            <span><el-icon><TrendCharts /></el-icon> 监测数据趋势(最近24小时)</span>
-            <el-radio-group v-model="trendMetric" size="small">
-              <el-radio-button label="methane">甲烷浓度</el-radio-button>
-              <el-radio-button label="inPressure">进口压力</el-radio-button>
-              <el-radio-button label="outPressure">出口压力</el-radio-button>
-            </el-radio-group>
+            <span><el-icon><TrendCharts /></el-icon> 甲烷浓度趋势(最近24小时)</span>
           </div>
           <div ref="trendChartRef" class="chart-container"></div>
         </div>
 
-        <!-- 告警记录与处置建议 -->
-        <div class="alert-section" v-if="selectedStation.alertStatus">
-          <div class="section-header">
-            <span><el-icon><BellFilled /></el-icon> 实时告警信息</span>
-            <el-button type="primary" size="small" @click="handleAlert">立即处理</el-button>
-          </div>
-          <el-alert
-              :title="`${selectedStation.name} 甲烷浓度 ${selectedStation.methane}ppm 超过阈值 ${selectedStation.methaneThreshold}ppm`"
-              type="error"
-              show-icon
-              :closable="false"
-              description="请立即安排检修人员前往现场排查泄漏点,同时通知周边居民做好防护措施。"
-          />
-          <div class="alert-actions">
-            <el-button type="danger" @click="dispatchEmergency">派发应急工单</el-button>
-            <el-button @click="viewAlertHistory">查看历史告警</el-button>
-          </div>
-        </div>
-
         <!-- SCADA系统接入信息 -->
         <div class="scada-section">
           <div class="section-header">
@@ -202,33 +138,15 @@
           </div>
           <el-descriptions :column="2" border size="small">
             <el-descriptions-item label="数据来源">燃气公司SCADA系统</el-descriptions-item>
-            <el-descriptions-item label="最后通讯时间">{{ selectedStation.lastUpdate }}</el-descriptions-item>
+            <el-descriptions-item label="最后通讯时间">{{ selectedStation.lastUpdateTime || '--' }}</el-descriptions-item>
             <el-descriptions-item label="数据采集频率">10秒/次</el-descriptions-item>
             <el-descriptions-item label="通讯状态">
               <el-tag type="success">在线</el-tag>
             </el-descriptions-item>
             <el-descriptions-item label="场站编号">{{ selectedStation.stationCode }}</el-descriptions-item>
-            <el-descriptions-item label="设备总数">{{ selectedStation.deviceCount }}台</el-descriptions-item>
+            <el-descriptions-item label="关联设备">{{ selectedStation.deviceCount }}台</el-descriptions-item>
           </el-descriptions>
         </div>
-
-        <!-- 历史告警记录 -->
-        <div class="history-section">
-          <div class="section-header"><span><el-icon><Document /></el-icon> 历史告警记录</span></div>
-          <el-timeline>
-            <el-timeline-item
-                v-for="alert in selectedStation.alertHistory"
-                :key="alert.id"
-                :timestamp="alert.time"
-                :type="alert.type"
-            >
-              {{ alert.content }}
-            </el-timeline-item>
-            <el-timeline-item v-if="!selectedStation.alertHistory?.length" timestamp="暂无告警记录" type="info">
-              该场站近30天无告警记录
-            </el-timeline-item>
-          </el-timeline>
-        </div>
       </div>
 
       <!-- 未选中状态 -->
@@ -240,124 +158,64 @@
 </template>
 
 <script setup>
-import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
-import { Warning, Refresh, Download, Location, WindPower, Connection, TrendCharts, BellFilled, Monitor, Document, Search } from '@element-plus/icons-vue'
+import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { Warning, Refresh, Location, WindPower, Odometer, TrendCharts, Monitor, Search } from '@element-plus/icons-vue'
 import * as echarts from 'echarts'
 import { ElMessage } from 'element-plus'
+import { getGasMonitorPoints, getGasMonitorTrend } from '@/api/pipeNetwork/gasMonitor'
 
-// 小时标签(24小时)
-const hourLabels = ['00:00','01:00','02:00','03:00','04:00','05:00','06:00','07:00','08:00','09:00','10:00','11:00','12:00','13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00','21:00','22:00','23:00']
+// ECharts实例
+const trendChartRef = ref(null)
+let trendChartInstance = null
 
-// 沅陵县燃气场站数据(6站,接入SCADA系统)
-const mockStations = ref([
-  {
-    id: 1, name: '太常LNG储配站', stationCode: 'YL-LNG-01', address: '沅陵县太常片区辰州东路末段',
-    methane: 48, methaneThreshold: 20, inPressure: 395, outPressure: 242, temperature: 23.2,
-    alertStatus: true, lastUpdate: '2026-06-15 15:23:45', deviceCount: 32,
-    alertHistory: [
-      { id: 1, time: '2026-06-15 15:23:45', type: 'danger', content: '甲烷浓度48ppm超过阈值20ppm,触发严重告警' },
-      { id: 2, time: '2026-06-15 10:15:00', type: 'warning', content: '储罐区甲烷传感器示值偏高,安排巡检' },
-      { id: 3, time: '2026-06-12 14:30:00', type: 'danger', content: 'LNG储罐B区微量泄漏,已紧急处置' }
-    ],
-    trendData: {
-      methane: [8,7,9,8,10,12,15,18,20,22,25,28,30,32,35,38,40,42,44,45,46,47,48,48],
-      inPressure: [388,390,392,394,395,396,394,392,390,388,390,393,395,397,398,396,394,393,392,394,395,396,394,393],
-      outPressure: [238,240,241,242,243,244,242,240,239,238,240,242,243,244,242,241,240,241,242,243,242,241,240,242]
-    }
-  },
-  {
-    id: 2, name: '工业园计量站', stationCode: 'YL-GY-01', address: '沅陵县太常片区建设东路工业园入口东侧',
-    methane: 35, methaneThreshold: 20, inPressure: 360, outPressure: 218, temperature: 24.5,
-    alertStatus: true, lastUpdate: '2026-06-15 15:22:30', deviceCount: 20,
-    alertHistory: [
-      { id: 1, time: '2026-06-15 14:32:05', type: 'danger', content: '甲烷浓度35ppm超过阈值20ppm,触发告警' },
-      { id: 2, time: '2026-06-14 09:20:00', type: 'warning', content: '计量间甲烷浓度异常波动,正在排查' }
-    ],
-    trendData: {
-      methane: [6,7,8,9,10,11,13,15,18,20,22,25,27,28,30,31,32,33,34,35,35,34,33,35],
-      inPressure: [355,357,358,360,361,362,360,358,356,355,357,359,361,362,360,358,357,358,359,360,361,359,357,358],
-      outPressure: [215,216,217,218,219,220,218,216,215,214,216,218,219,220,218,217,216,217,218,219,218,217,216,218]
-    }
-  },
-  {
-    id: 3, name: '太常调压站', stationCode: 'YL-TY-01', address: '沅陵县太常片区古城中路与天宁路交叉口东侧',
-    methane: 18, methaneThreshold: 20, inPressure: 345, outPressure: 205, temperature: 22.8,
-    alertStatus: false, lastUpdate: '2026-06-15 15:21:18', deviceCount: 16,
-    alertHistory: [
-      { id: 1, time: '2026-06-13 14:58:12', type: 'warning', content: '甲烷浓度接近阈值达到18ppm,加强监控' },
-      { id: 2, time: '2026-05-20 08:00:00', type: 'success', content: '调压设备定期维护完成,恢复正常运行' }
-    ],
-    trendData: {
-      methane: [12,11,12,13,14,14,15,15,16,16,17,17,18,18,17,17,16,16,15,15,14,14,13,12],
-      inPressure: [340,342,343,345,346,347,345,343,341,340,342,344,346,347,345,343,342,343,344,345,346,344,342,343],
-      outPressure: [200,202,203,205,206,207,205,203,201,200,202,204,206,207,205,203,202,203,204,205,206,204,202,203]
-    }
-  },
-  {
-    id: 4, name: '城南调压站', stationCode: 'YL-CNTY-01', address: '沅陵县太常片区建设路与古城南路交叉口南侧',
-    methane: 8, methaneThreshold: 20, inPressure: 330, outPressure: 195, temperature: 21.5,
-    alertStatus: false, lastUpdate: '2026-06-15 15:20:45', deviceCount: 12,
-    alertHistory: [
-      { id: 1, time: '2026-06-12 22:45:00', type: 'warning', content: '出口压力瞬时波动,自动调节恢复' }
-    ],
-    trendData: {
-      methane: [5,5,6,6,7,7,8,8,9,9,8,8,7,7,6,6,5,5,6,6,7,7,8,8],
-      inPressure: [325,327,328,330,331,332,330,328,326,325,327,329,331,332,330,328,327,328,329,330,331,329,327,328],
-      outPressure: [190,192,193,195,196,197,195,193,191,190,192,194,196,197,195,193,192,193,194,195,196,194,192,193]
-    }
-  },
-  {
-    id: 5, name: '辰州路门站', stationCode: 'YL-CS-01', address: '沅陵县太常片区辰州中路与天宁路交叉口',
-    methane: 12, methaneThreshold: 20, inPressure: 375, outPressure: 228, temperature: 22.0,
-    alertStatus: false, lastUpdate: '2026-06-15 15:19:30', deviceCount: 24,
-    alertHistory: [],
-    trendData: {
-      methane: [10,10,11,11,12,12,12,11,11,11,12,12,12,11,11,10,10,11,11,12,12,12,11,12],
-      inPressure: [370,372,373,375,376,377,375,373,371,370,372,374,376,377,375,373,372,373,374,375,376,374,372,373],
-      outPressure: [224,225,226,228,229,230,228,226,225,224,225,227,229,230,228,226,225,226,227,228,229,227,225,228]
-    }
-  },
-  {
-    id: 6, name: '迎宾路调压站', stationCode: 'YL-YB-01', address: '沅陵县太常片区迎宾东路与建设路交叉口北侧',
-    methane: 5, methaneThreshold: 20, inPressure: 310, outPressure: 188, temperature: 20.8,
-    alertStatus: false, lastUpdate: '2026-06-15 15:18:50', deviceCount: 10,
-    alertHistory: [],
-    trendData: {
-      methane: [4,4,5,5,4,4,5,5,6,5,5,4,4,5,5,6,5,5,4,4,5,5,4,5],
-      inPressure: [305,307,308,310,311,312,310,308,306,305,307,309,311,312,310,308,307,308,309,310,311,309,307,308],
-      outPressure: [184,185,186,188,189,190,188,186,185,184,185,187,189,190,188,186,185,186,187,188,189,187,185,188]
+// 加载数据
+const stations = ref([])
+const loading = ref(false)
+
+async function loadStations() {
+  loading.value = true
+  try {
+    const res = await getGasMonitorPoints()
+    stations.value = res.data || []
+    if (stations.value.length > 0) {
+      selectStation(stations.value[0])
     }
+  } finally {
+    loading.value = false
   }
-])
+}
 
 // 统计数据
-const totalStations = computed(() => mockStations.value.length)
-const alertCount = computed(() => mockStations.value.filter(s => s.alertStatus).length)
+const totalStations = computed(() => stations.value.length)
+const alertCount = computed(() => stations.value.filter(s => s.alertStatus).length)
 const normalCount = computed(() => totalStations.value - alertCount.value)
-const maxMethane = computed(() => Math.max(...mockStations.value.map(s => s.methane)))
-const maxMethaneStation = computed(() => mockStations.value.find(s => s.methane === maxMethane.value)?.name || '—')
-const avgInPressure = computed(() => (mockStations.value.reduce((sum, s) => sum + s.inPressure, 0) / totalStations.value).toFixed(0))
-const avgOutPressure = computed(() => (mockStations.value.reduce((sum, s) => sum + s.outPressure, 0) / totalStations.value).toFixed(0))
+const maxMethane = computed(() => {
+  if (!stations.value.length) return 0
+  return Math.max(...stations.value.map(s => s.methaneValue))
+})
+const maxMethaneStation = computed(() => {
+  const max = maxMethane.value
+  return stations.value.find(s => s.methaneValue === max)?.pointName || '—'
+})
 const riskScore = computed(() => {
+  if (!stations.value.length) return 0
   const alertRatio = alertCount.value / totalStations.value
-  const avgMethane = mockStations.value.reduce((sum, s) => sum + s.methane, 0) / totalStations.value
+  const avgMethane = stations.value.reduce((sum, s) => sum + s.methaneValue, 0) / totalStations.value
   let score = 100 - (alertRatio * 50 + (avgMethane / 40) * 30)
   return Math.max(0, Math.min(100, Math.round(score)))
 })
 
 // 筛选
 const searchKeyword = ref('')
-const dateRange = ref([])
 const currentPage = ref(1)
 const pageSize = 5
-const selectedStation = ref(mockStations.value[0])
-const trendMetric = ref('methane')
+const selectedStation = ref(null)
 
 const filteredStations = computed(() => {
-  let list = mockStations.value
+  let list = stations.value
   if (searchKeyword.value) {
     const kw = searchKeyword.value.toLowerCase()
-    list = list.filter(s => s.name.toLowerCase().includes(kw))
+    list = list.filter(s => s.pointName.toLowerCase().includes(kw))
   }
   return list
 })
@@ -366,66 +224,69 @@ const paginatedStations = computed(() => {
   return filteredStations.value.slice(start, start + pageSize)
 })
 
-// 趋势图(echarts.init)
-const trendChartRef = ref(null)
-let trendChartInstance = null
-
-function renderTrendChart() {
+// 趋势图
+function renderTrendChart(trendRes) {
   if (!trendChartRef.value || !selectedStation.value) return
   if (!trendChartInstance) {
     trendChartInstance = echarts.init(trendChartRef.value)
   }
-  const data = selectedStation.value.trendData[trendMetric.value]
-  const unit = trendMetric.value === 'methane' ? 'ppm' : 'kPa'
-  const color = trendMetric.value === 'methane' ? '#f56c6c' : '#409eff'
-  const threshold = trendMetric.value === 'methane' ? selectedStation.value.methaneThreshold : null
+  const data = trendRes || {}
+  const times = data.times || []
+  const values = data.methaneValues || []
+  const threshold = data.threshold || 20
 
-  const series = [{
-    name: trendMetric.value === 'methane' ? '甲烷浓度' : trendMetric.value === 'inPressure' ? '进口压力' : '出口压力',
-    type: 'line',
-    data: data,
-    smooth: true,
-    lineStyle: { color, width: 2 },
-    areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-      { offset: 0, color: color === '#f56c6c' ? 'rgba(245,108,108,0.3)' : 'rgba(64,158,255,0.3)' },
-      { offset: 1, color: 'rgba(255,255,255,0.05)' }
-    ])},
-    symbol: 'none'
-  }]
-  if (threshold) {
-    series.push({
-      name: '报警阈值',
-      type: 'line',
-      data: Array(24).fill(threshold),
-      lineStyle: { color: '#f56c6c', width: 1.5, type: 'dashed' },
-      symbol: 'none'
-    })
-  }
   trendChartInstance.setOption({
     tooltip: { trigger: 'axis' },
-    legend: { data: threshold ? [series[0].name, '报警阈值'] : [series[0].name], bottom: 0 },
+    legend: { data: ['甲烷浓度', '报警阈值'], bottom: 0 },
     grid: { left: 50, right: 30, top: 20, bottom: 30 },
-    xAxis: { type: 'category', data: hourLabels, axisLabel: { rotate: 45, interval: 3, fontSize: 10 } },
-    yAxis: { type: 'value', name: unit, axisLabel: { fontSize: 10 } },
-    series
+    xAxis: {
+      type: 'category', data: times,
+      axisLabel: { rotate: 45, interval: 3, fontSize: 10 }
+    },
+    yAxis: { type: 'value', name: 'ppm', axisLabel: { fontSize: 10 } },
+    series: [
+      {
+        name: '甲烷浓度', type: 'line', data: values,
+        smooth: true,
+        lineStyle: { color: '#f56c6c', width: 2 },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: 'rgba(245,108,108,0.3)' },
+            { offset: 1, color: 'rgba(255,255,255,0.05)' }
+          ])
+        },
+        symbol: 'none'
+      },
+      {
+        name: '报警阈值', type: 'line',
+        data: Array(times.length || 24).fill(threshold),
+        lineStyle: { color: '#f56c6c', width: 1.5, type: 'dashed' },
+        symbol: 'none'
+      }
+    ]
   })
 }
 
-const selectStation = (station) => {
+async function selectStation(station) {
   selectedStation.value = station
-  nextTick(() => renderTrendChart())
+  currentPage.value = 1
+  try {
+    const res = await getGasMonitorTrend(station.pointId, 24)
+    await nextTick()
+    renderTrendChart(res.data || {})
+  } catch {
+    await nextTick()
+    renderTrendChart({})
+  }
 }
 
-const refreshData = () => { ElMessage.success('数据已刷新') }
-const exportReport = () => { ElMessage.success('导出监测报表(演示)') }
-const handleAlert = () => { ElMessage.warning('正在处理告警...') }
-const dispatchEmergency = () => { ElMessage.success('应急工单已派发') }
-const viewAlertHistory = () => { ElMessage.info('查看历史告警记录') }
-
-watch(trendMetric, () => nextTick(() => renderTrendChart()))
+function refreshData() {
+  ElMessage.success('数据已刷新')
+  loadStations()
+}
 
 onMounted(() => {
-  nextTick(() => renderTrendChart())
+  loadStations()
 })
 
 onBeforeUnmount(() => {
@@ -464,7 +325,7 @@ onBeforeUnmount(() => {
 
 .kpi-cards {
   display: grid;
-  grid-template-columns: repeat(4, 1fr);
+  grid-template-columns: repeat(2, 1fr);
   gap: 16px;
   margin-bottom: 20px;
 }
@@ -541,7 +402,7 @@ onBeforeUnmount(() => {
 
 .realtime-cards {
   display: grid;
-  grid-template-columns: repeat(4, 1fr);
+  grid-template-columns: repeat(2, 1fr);
   gap: 16px;
   margin-bottom: 24px;
 }
@@ -561,7 +422,7 @@ onBeforeUnmount(() => {
 .card-value .unit { font-size: 12px; font-weight: normal; }
 .card-threshold { font-size: 11px; color: #aaa; margin-top: 4px; }
 
-.chart-section, .alert-section, .scada-section, .history-section {
+.chart-section, .scada-section {
   margin-bottom: 24px;
 }
 .section-header {
@@ -572,7 +433,6 @@ onBeforeUnmount(() => {
   align-items: center;
 }
 .chart-container { height: 260px; }
-.alert-actions { display: flex; gap: 12px; margin-top: 16px; }
 .empty-panel { flex: 1; display: flex; align-items: center; justify-content: center; background: white; border-radius: 16px; }
 .pagination-area { padding: 12px; text-align: center; border-top: 1px solid #eee; }
-</style>
+</style>