Эх сурвалжийг харах

排水管道系统代码提交

LAPTOP-JI2IUVG1\26646 1 сар өмнө
parent
commit
a0926e5f8e

+ 1 - 0
index.html

@@ -209,6 +209,7 @@
       <div class="load_title">正在加载系统资源,请耐心等待</div>
     </div>
   </div>
+  <script type="text/javascript" src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak=vMma3LYQQyrfhlFUm0CFzXFQhBrwxAmi"></script>
   <script type="module" src="/src/main.js"></script>
 </body>
 

BIN
src/assets/images/location.png


+ 13 - 0
src/views/subSystem/drainage/fxpg/zhfxtjfx.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>综合风险统计分析</h2>
+  </div>
+</template>
+
+<script setup name="Zhfxtjfx">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/fxpg/zhfxxqfx.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>综合风险详情分析</h2>
+  </div>
+</template>
+
+<script setup name="Zhfxxqfx">
+
+</script>
+
+<style scoped>
+
+</style>

+ 0 - 0
src/views/subSystem/drainage/index.vue → src/views/subSystem/drainage/home.vue


+ 13 - 0
src/views/subSystem/drainage/jcbj/bjqd.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>报警清单</h2>
+  </div>
+</template>
+
+<script setup name="Bjqd">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcbj/bjsh.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>报警审核</h2>
+  </div>
+</template>
+
+<script setup name="Bjsh">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcbj/bjtx.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>报警提醒</h2>
+  </div>
+</template>
+
+<script setup name="Bjtx">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcbj/bjyp.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>报警研判</h2>
+  </div>
+</template>
+
+<script setup name="Bjyp">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcbj/dqbj.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>当前报警</h2>
+  </div>
+</template>
+
+<script setup name="Dqbj">
+
+</script>
+
+<style scoped>
+
+</style>

+ 713 - 0
src/views/subSystem/drainage/jcsj/gcssgl.vue

@@ -0,0 +1,713 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="监测点名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="行政区域">
+        <el-input v-model="queryParams.area" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">删除</el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="监测点名称" prop="name" min-width="160" />
+      <el-table-column label="行政区域" prop="area" min-width="100" />
+      <el-table-column label="数据时间" prop="time" min-width="120" />
+      <el-table-column label="闸门数量" prop="gateCount" min-width="80" />
+      <el-table-column label="调蓄数量" prop="storageCount" min-width="80" />
+      <el-table-column label="泵站数量" prop="pumpCount" min-width="80" />
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">查看</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog v-model="dialogVisible" title="详情" width="700px" :before-close="handleClose" @opened="onDialogOpened">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">监测点名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">行政区域:</span>
+              <span class="info-value">{{ currentRow.area }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">数据时间:</span>
+              <span class="info-value">{{ currentRow.time }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">闸门数量:</span>
+              <span class="info-value">{{ (currentRow.gatePoints || []).length }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">调蓄数量:</span>
+              <span class="info-value">{{ (currentRow.storagePoints || []).length }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">泵站数量:</span>
+              <span class="info-value">{{ (currentRow.pumpPoints || []).length }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 地图区域 -->
+      <div class="map-section">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: viewMapTab === 'gate' }"
+            @click="switchViewMapTab('gate')">闸门</div>
+          <div class="map-tab" :class="{ active: viewMapTab === 'storage' }"
+            @click="switchViewMapTab('storage')">调蓄设施</div>
+          <div class="map-tab" :class="{ active: viewMapTab === 'pump' }"
+            @click="switchViewMapTab('pump')">泵站</div>
+        </div>
+        <div id="gcssglViewMap" class="map-container"></div>
+      </div>
+    </el-dialog>
+
+    <!-- 新增/修改对话框 -->
+    <el-dialog v-model="editDialogVisible" :title="editDialogTitle" width="800px" :before-close="handleEditClose"
+      @opened="onEditDialogOpened">
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="监测点名称" prop="name">
+              <el-input v-model="editForm.name" placeholder="请输入监测点名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="行政区域" prop="area">
+              <el-input v-model="editForm.area" placeholder="请输入行政区域" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="数据时间" prop="time">
+              <el-date-picker v-model="editForm.time" type="date" placeholder="请选择日期" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="闸门数量">
+              <el-input :model-value="editGatePoints.length" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="调蓄数量">
+              <el-input :model-value="editStoragePoints.length" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="泵站数量">
+              <el-input :model-value="editPumpPoints.length" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 地图标点区域 -->
+      <div class="map-section" style="margin-top: 16px;">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: editMapTab === 'gate' }"
+            @click="switchEditMapTab('gate')">闸门</div>
+          <div class="map-tab" :class="{ active: editMapTab === 'storage' }"
+            @click="switchEditMapTab('storage')">调蓄设施</div>
+          <div class="map-tab" :class="{ active: editMapTab === 'pump' }"
+            @click="switchEditMapTab('pump')">泵站</div>
+          <div class="map-tab-action" @click="clearEditMarkers">清除所有标记</div>
+        </div>
+        <div id="gcssglEditMap" class="map-container" style="height: 350px;"></div>
+        <div class="marker-count">
+          已标记 {{ currentEditPoints.length }} 个{{ editMapTabLabel }}
+        </div>
+      </div>
+
+      <template #footer>
+        <el-button @click="handleEditClose">取 消</el-button>
+        <el-button type="primary" @click="handleEditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Gcssgl">
+import { ref, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import locationIcon from '@/assets/images/location.png'
+
+// 地图标记创建(用Label+img代替Marker)
+function createMapMarker(map, bPoint, name) {
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:#409eff;border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${name || ''}</div>
+    <div><img src="${locationIcon}" style="width:28px;height:28px;display:block;margin:0 auto;"/></div>
+  </div>`
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -50)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  map.addOverlay(label)
+  return label
+}
+
+// ==================== 搜索参数 ====================
+const queryParams = ref({
+  name: '',
+  area: ''
+})
+
+// ==================== 表格数据 ====================
+let nextId = 5
+const tableData = ref([
+  {
+    id: 1, name: '沅陵北路1号工程设施', area: '沅陵县', time: '2025-12-31',
+    gateCount: 3, storageCount: 2, pumpCount: 2,
+    gatePoints: [
+      { name: '闸门1', lng: 110.393, lat: 28.452 },
+      { name: '闸门2', lng: 110.398, lat: 28.455 },
+      { name: '闸门3', lng: 110.402, lat: 28.450 }
+    ],
+    storagePoints: [
+      { name: '调蓄设施1', lng: 110.395, lat: 28.448 },
+      { name: '调蓄设施2', lng: 110.400, lat: 28.460 }
+    ],
+    pumpPoints: [
+      { name: '泵站1', lng: 110.390, lat: 28.445 },
+      { name: '泵站2', lng: 110.408, lat: 28.458 }
+    ]
+  },
+  {
+    id: 2, name: '沅陵南路1号工程设施', area: '沅陵县', time: '2025-12-30',
+    gateCount: 2, storageCount: 1, pumpCount: 1,
+    gatePoints: [
+      { name: '闸门1', lng: 110.385, lat: 28.446 },
+      { name: '闸门2', lng: 110.390, lat: 28.444 }
+    ],
+    storagePoints: [
+      { name: '调蓄设施1', lng: 110.388, lat: 28.450 }
+    ],
+    pumpPoints: [
+      { name: '泵站1', lng: 110.392, lat: 28.442 }
+    ]
+  },
+  {
+    id: 3, name: '沅陵东路2号工程设施', area: '沅陵县', time: '2025-12-29',
+    gateCount: 2, storageCount: 2, pumpCount: 3,
+    gatePoints: [
+      { name: '闸门1', lng: 110.400, lat: 28.456 },
+      { name: '闸门2', lng: 110.405, lat: 28.458 }
+    ],
+    storagePoints: [
+      { name: '调蓄设施1', lng: 110.403, lat: 28.454 },
+      { name: '调蓄设施2', lng: 110.409, lat: 28.461 }
+    ],
+    pumpPoints: [
+      { name: '泵站1', lng: 110.413, lat: 28.461 },
+      { name: '泵站2', lng: 110.396, lat: 28.457 },
+      { name: '泵站3', lng: 110.410, lat: 28.453 }
+    ]
+  },
+  {
+    id: 4, name: '沅陵西路1号工程设施', area: '沅陵县', time: '2025-12-28',
+    gateCount: 1, storageCount: 1, pumpCount: 1,
+    gatePoints: [
+      { name: '闸门1', lng: 110.380, lat: 28.450 }
+    ],
+    storagePoints: [
+      { name: '调蓄设施1', lng: 110.382, lat: 28.448 }
+    ],
+    pumpPoints: [
+      { name: '泵站1', lng: 110.378, lat: 28.446 }
+    ]
+  }
+])
+
+// 多选
+const selectedIds = ref([])
+function handleSelectionChange(selection) {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+// 过滤后的列表
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const areaMatch = !queryParams.value.area || item.area.includes(queryParams.value.area)
+    return nameMatch && areaMatch
+  })
+})
+
+// 搜索
+function handleQuery() {
+  queryParams.value = { ...queryParams.value }
+}
+
+// ==================== 查看详情对话框 ====================
+const dialogVisible = ref(false)
+const currentRow = ref({})
+const viewMapTab = ref('gate')
+let viewMapInstance = null
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  viewMapTab.value = 'gate'
+  dialogVisible.value = true
+}
+
+function handleClose() {
+  dialogVisible.value = false
+  if (viewMapInstance) {
+    viewMapInstance = null
+  }
+}
+
+function onDialogOpened() {
+  setTimeout(() => {
+    initViewMap()
+  }, 300)
+}
+
+function initViewMap() {
+  const container = document.getElementById('gcssglViewMap')
+  if (!container) return
+
+  try {
+    viewMapInstance = new BMapGL.Map('gcssglViewMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    viewMapInstance.centerAndZoom(centerPoint, 15)
+    viewMapInstance.enableScrollWheelZoom(true)
+    renderViewMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function getViewPoints() {
+  const tab = viewMapTab.value
+  if (tab === 'gate') return currentRow.value.gatePoints || []
+  if (tab === 'storage') return currentRow.value.storagePoints || []
+  return currentRow.value.pumpPoints || []
+}
+
+function renderViewMarkers() {
+  if (!viewMapInstance) return
+  viewMapInstance.clearOverlays()
+
+  const points = getViewPoints()
+
+  points.forEach(point => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    createMapMarker(viewMapInstance, bPoint, point.name)
+  })
+
+  if (points.length > 0) {
+    const viewPoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    viewMapInstance.setViewport(viewPoints)
+  }
+}
+
+function switchViewMapTab(tab) {
+  viewMapTab.value = tab
+  renderViewMarkers()
+}
+
+// ==================== 新增/修改对话框 ====================
+const editDialogVisible = ref(false)
+const editDialogTitle = ref('新增监测点')
+const editFormRef = ref(null)
+const isEditMode = ref(false)
+const editRowId = ref(null)
+
+const editForm = ref({
+  name: '',
+  area: '',
+  time: '',
+  gateCount: '',
+  storageCount: '',
+  pumpCount: ''
+})
+
+const editRules = {
+  name: [{ required: true, message: '请输入监测点名称', trigger: 'blur' }],
+  area: [{ required: true, message: '请输入行政区域', trigger: 'blur' }],
+  time: [{ required: true, message: '请选择数据时间', trigger: 'change' }]
+}
+
+// 编辑弹窗地图相关
+const editMapTab = ref('gate')
+let editMapInstance = null
+let editGatePoints = ref([])
+let editStoragePoints = ref([])
+let editPumpPoints = ref([])
+let editGateMarkerCount = ref(0)
+let editStorageMarkerCount = ref(0)
+let editPumpMarkerCount = ref(0)
+
+// tab 中文名称
+const editMapTabLabel = computed(() => {
+  const map = { gate: '闸门', storage: '调蓄设施', pump: '泵站' }
+  return map[editMapTab.value] || ''
+})
+
+// 当前 tab 对应的点列表
+const currentEditPoints = computed(() => {
+  if (editMapTab.value === 'gate') return editGatePoints.value
+  if (editMapTab.value === 'storage') return editStoragePoints.value
+  return editPumpPoints.value
+})
+
+function handleAdd() {
+  isEditMode.value = false
+  editDialogTitle.value = '新增监测点'
+  editRowId.value = null
+  editForm.value = {
+    name: '',
+    area: '',
+    time: '',
+    gateCount: '',
+    storageCount: '',
+    pumpCount: ''
+  }
+  editMapTab.value = 'gate'
+  editGatePoints.value = []
+  editStoragePoints.value = []
+  editPumpPoints.value = []
+  editGateMarkerCount.value = 0
+  editStorageMarkerCount.value = 0
+  editPumpMarkerCount.value = 0
+  editDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  isEditMode.value = true
+  editDialogTitle.value = '修改监测点'
+  editRowId.value = row.id
+  editForm.value = {
+    name: row.name,
+    area: row.area,
+    time: row.time,
+    gateCount: row.gateCount,
+    storageCount: row.storageCount,
+    pumpCount: row.pumpCount
+  }
+  editMapTab.value = 'gate'
+  // 深拷贝标记点数据
+  editGatePoints.value = (row.gatePoints || []).map(p => ({ ...p }))
+  editStoragePoints.value = (row.storagePoints || []).map(p => ({ ...p }))
+  editPumpPoints.value = (row.pumpPoints || []).map(p => ({ ...p }))
+  editGateMarkerCount.value = editGatePoints.value.length
+  editStorageMarkerCount.value = editStoragePoints.value.length
+  editPumpMarkerCount.value = editPumpPoints.value.length
+  editDialogVisible.value = true
+}
+
+function handleEditClose() {
+  editDialogVisible.value = false
+  if (editMapInstance) {
+    editMapInstance = null
+  }
+}
+
+function onEditDialogOpened() {
+  setTimeout(() => {
+    initEditMap()
+  }, 300)
+}
+
+function initEditMap() {
+  const container = document.getElementById('gcssglEditMap')
+  if (!container) return
+
+  try {
+    editMapInstance = new BMapGL.Map('gcssglEditMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(centerPoint, 15)
+    editMapInstance.enableScrollWheelZoom(true)
+
+    // 点击地图添加标记
+    editMapInstance.addEventListener('click', function (e) {
+      addEditMarker(e.latlng)
+    })
+
+    // 渲染已有标记
+    renderEditMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function getCurrentEditPointList() {
+  if (editMapTab.value === 'gate') return editGatePoints.value
+  if (editMapTab.value === 'storage') return editStoragePoints.value
+  return editPumpPoints.value
+}
+
+function addEditMarker(point) {
+  const tab = editMapTab.value
+  let pointList, count, labelPrefix
+
+  if (tab === 'gate') {
+    pointList = editGatePoints.value
+    count = ++editGateMarkerCount.value
+    labelPrefix = '闸门'
+  } else if (tab === 'storage') {
+    pointList = editStoragePoints.value
+    count = ++editStorageMarkerCount.value
+    labelPrefix = '调蓄设施'
+  } else {
+    pointList = editPumpPoints.value
+    count = ++editPumpMarkerCount.value
+    labelPrefix = '泵站'
+  }
+
+  pointList.push({
+    name: `${labelPrefix}${count}`,
+    lng: point.lng,
+    lat: point.lat
+  })
+
+  // 统一重绘,保证地图与数据完全一致
+  renderEditMarkers(false)
+}
+
+function renderEditMarkers(shouldCenter = true) {
+  if (!editMapInstance) return
+  editMapInstance.clearOverlays()
+
+  const points = getCurrentEditPointList()
+  if (shouldCenter) {
+    const center = points.length > 0
+      ? new BMapGL.Point(points[0].lng, points[0].lat)
+      : new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(center, 15)
+  }
+
+  points.forEach((point, index) => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    const label = createMapMarker(editMapInstance, bPoint, point.name)
+
+    // 右键删除标记
+    label.addEventListener('rightclick', () => {
+      const list = getCurrentEditPointList()
+      list.splice(index, 1)
+      renderEditMarkers(false)
+    })
+  })
+}
+
+function switchEditMapTab(tab) {
+  editMapTab.value = tab
+  renderEditMarkers()
+}
+
+function clearEditMarkers() {
+  if (editMapTab.value === 'gate') {
+    editGatePoints.value = []
+    editGateMarkerCount.value = 0
+  } else if (editMapTab.value === 'storage') {
+    editStoragePoints.value = []
+    editStorageMarkerCount.value = 0
+  } else {
+    editPumpPoints.value = []
+    editPumpMarkerCount.value = 0
+  }
+  renderEditMarkers()
+}
+
+function handleEditSubmit() {
+  editFormRef.value.validate(valid => {
+    if (!valid) return
+
+    const formData = {
+      ...editForm.value,
+      gatePoints: editGatePoints.value.map(p => ({ ...p })),
+      storagePoints: editStoragePoints.value.map(p => ({ ...p })),
+      pumpPoints: editPumpPoints.value.map(p => ({ ...p })),
+      gateCount: editGatePoints.value.length,
+      storageCount: editStoragePoints.value.length,
+      pumpCount: editPumpPoints.value.length
+    }
+
+    if (isEditMode.value) {
+      // 修改
+      const index = tableData.value.findIndex(item => item.id === editRowId.value)
+      if (index !== -1) {
+        tableData.value[index] = { ...tableData.value[index], ...formData }
+      }
+      ElMessage.success('修改成功')
+    } else {
+      // 新增
+      formData.id = nextId++
+      tableData.value.push(formData)
+      ElMessage.success('新增成功')
+    }
+
+    editDialogVisible.value = false
+    if (editMapInstance) {
+      editMapInstance = null
+    }
+  })
+}
+
+// ==================== 删除功能 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(`确认删除监测点"${row.name}"吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+
+function handleBatchDelete() {
+  if (selectedIds.value.length === 0) return
+  ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条数据吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id))
+    selectedIds.value = []
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-form {
+  margin-bottom: 16px;
+}
+
+.toolbar {
+  margin-bottom: 16px;
+}
+
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+
+.map-section {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.map-tabs {
+  display: flex;
+  background: #f5f7fa;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.map-tab {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #606266;
+  transition: all 0.3s;
+  user-select: none;
+}
+
+.map-tab:hover {
+  color: #409eff;
+}
+
+.map-tab.active {
+  background: #409eff;
+  color: #fff;
+}
+
+.map-tab-action {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #f56c6c;
+  transition: all 0.3s;
+  user-select: none;
+  margin-left: auto;
+}
+
+.map-tab-action:hover {
+  color: #f78989;
+  text-decoration: underline;
+}
+
+.map-container {
+  width: 100%;
+  height: 400px;
+}
+
+.marker-count {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #909399;
+  background: #f5f7fa;
+  border-top: 1px solid #e4e7ed;
+}
+</style>

+ 661 - 0
src/views/subSystem/drainage/jcsj/psfqgl.vue

@@ -0,0 +1,661 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="区域名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="类型">
+        <el-select v-model="queryParams.type" placeholder="请输入" clearable style="width: 200px">
+          <el-option label="积水点" value="1" />
+          <el-option label="排水点" value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">删除</el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="监测点名称" prop="name" min-width="160" />
+      <el-table-column label="行政区域" prop="area" min-width="100" />
+      <el-table-column label="数据时间" prop="time" min-width="120" />
+      <el-table-column label="排水量" prop="drainage" min-width="80" />
+      <el-table-column label="积水量" prop="waterlogging" min-width="80" />
+      <el-table-column label="积水等级" prop="level" min-width="80">
+        <template #default="{ row }">
+          <span>{{ levelText(row.level) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">查看</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog v-model="dialogVisible" title="详情" width="700px" :before-close="handleClose" @opened="onDialogOpened">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">监测点名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">行政区域:</span>
+              <span class="info-value">{{ currentRow.area }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">数据时间:</span>
+              <span class="info-value">{{ currentRow.time }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">积水量:</span>
+              <span class="info-value">{{ currentRow.waterlogging }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">积水等级:</span>
+              <span class="info-value">{{ levelText(currentRow.level) }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">排水量:</span>
+              <span class="info-value">{{ currentRow.drainage }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 地图区域 -->
+      <div class="map-section">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: viewMapTab === 'waterlogging' }"
+            @click="switchViewMapTab('waterlogging')">积水点</div>
+          <div class="map-tab" :class="{ active: viewMapTab === 'drainage' }" @click="switchViewMapTab('drainage')">排水点
+          </div>
+        </div>
+        <div id="psfqglViewMap" class="map-container"></div>
+      </div>
+    </el-dialog>
+
+    <!-- 新增/修改对话框 -->
+    <el-dialog v-model="editDialogVisible" :title="editDialogTitle" width="800px" :before-close="handleEditClose"
+      @opened="onEditDialogOpened">
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="监测点名称" prop="name">
+              <el-input v-model="editForm.name" placeholder="请输入监测点名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="行政区域" prop="area">
+              <el-input v-model="editForm.area" placeholder="请输入行政区域" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="数据时间" prop="time">
+              <el-date-picker v-model="editForm.time" type="date" placeholder="请选择日期" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="排水量" prop="drainage">
+              <el-input v-model="editForm.drainage" placeholder="请输入排水量" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="积水量" prop="waterlogging">
+              <el-input v-model="editForm.waterlogging" placeholder="请输入积水量" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="积水等级" prop="level">
+              <el-input v-model="editForm.level" placeholder="请输入积水等级" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 地图标点区域 -->
+      <div class="map-section" style="margin-top: 16px;">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: editMapTab === 'waterlogging' }"
+            @click="switchEditMapTab('waterlogging')">积水点</div>
+          <div class="map-tab" :class="{ active: editMapTab === 'drainage' }" @click="switchEditMapTab('drainage')">排水点
+          </div>
+          <div class="map-tab-action" @click="clearEditMarkers">清除所有标记</div>
+        </div>
+        <div id="psfqglEditMap" class="map-container" style="height: 350px;"></div>
+        <div class="marker-count">
+          已标记 {{ currentEditPoints.length }} 个{{ editMapTab === 'waterlogging' ? '积水点' : '排水点' }}
+        </div>
+      </div>
+
+      <template #footer>
+        <el-button @click="handleEditClose">取 消</el-button>
+        <el-button type="primary" @click="handleEditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Psfqgl">
+import { ref, computed, nextTick } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import locationIcon from '@/assets/images/location.png'
+
+// 地图标记创建(用Label+img代替Marker)
+function createMapMarker(map, bPoint, name) {
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:#409eff;border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${name || ''}</div>
+    <div><img src="${locationIcon}" style="width:28px;height:28px;display:block;margin:0 auto;"/></div>
+  </div>`
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -50)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  map.addOverlay(label)
+  return label
+}
+
+// ==================== 搜索参数 ====================
+const queryParams = ref({
+  name: '',
+  type: ''
+})
+
+// ==================== 表格数据 ====================
+let nextId = 5
+const tableData = ref([
+  {
+    id: 1, name: 'XX北路1号积水点', area: 'XX区', time: '2025-12-31',
+    drainage: 1, waterlogging: 2, level: 1,
+    waterloggingPoints: [
+      { name: '积水点1', lng: 110.393, lat: 28.452 },
+      { name: '积水点2', lng: 110.400, lat: 28.458 },
+      { name: '积水点3', lng: 110.385, lat: 28.445 }
+    ],
+    drainagePoints: [
+      { name: '排水点1', lng: 110.396, lat: 28.455 },
+      { name: '排水点2', lng: 110.390, lat: 28.448 }
+    ]
+  },
+  {
+    id: 2, name: 'XX北路2号积水点', area: 'XX区', time: '2025-12-31',
+    drainage: 1, waterlogging: 2, level: 1,
+    waterloggingPoints: [
+      { name: '积水点1', lng: 110.404, lat: 28.460 }
+    ],
+    drainagePoints: [
+      { name: '排水点1', lng: 110.398, lat: 28.456 }
+    ]
+  },
+  {
+    id: 3, name: 'XX南路1号积水点', area: 'XX区', time: '2025-12-31',
+    drainage: 2, waterlogging: 2, level: 1,
+    waterloggingPoints: [
+      { name: '积水点1', lng: 110.408, lat: 28.462 },
+      { name: '积水点2', lng: 110.412, lat: 28.448 }
+    ],
+    drainagePoints: [
+      { name: '排水点1', lng: 110.402, lat: 28.450 }
+    ]
+  },
+  {
+    id: 4, name: 'XX南路2号积水点', area: 'XX区', time: '2025-12-31',
+    drainage: 1, waterlogging: 2, level: 1,
+    waterloggingPoints: [],
+    drainagePoints: []
+  }
+])
+
+// 多选
+const selectedIds = ref([])
+function handleSelectionChange(selection) {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+// 过滤后的列表
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const typeMatch = !queryParams.value.type || String(item.level) === queryParams.value.type
+    return nameMatch && typeMatch
+  })
+})
+
+// 积水等级文本
+function levelText(level) {
+  const map = { 1: 'I级', 2: 'II级', 3: 'III级', 4: 'IV级', 5: 'V级' }
+  return map[level] || '-'
+}
+
+// 搜索
+function handleQuery() {
+  queryParams.value = { ...queryParams.value }
+}
+
+// ==================== 查看详情对话框 ====================
+const dialogVisible = ref(false)
+const currentRow = ref({})
+const viewMapTab = ref('waterlogging')
+let viewMapInstance = null
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  viewMapTab.value = 'waterlogging'
+  dialogVisible.value = true
+}
+
+function handleClose() {
+  dialogVisible.value = false
+  if (viewMapInstance) {
+    viewMapInstance = null
+  }
+}
+
+function onDialogOpened() {
+  setTimeout(() => {
+    initViewMap()
+  }, 300)
+}
+
+function initViewMap() {
+  const container = document.getElementById('psfqglViewMap')
+  if (!container) return
+
+  try {
+    viewMapInstance = new BMapGL.Map('psfqglViewMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    viewMapInstance.centerAndZoom(centerPoint, 15)
+    viewMapInstance.enableScrollWheelZoom(true)
+    renderViewMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function renderViewMarkers() {
+  if (!viewMapInstance) return
+  viewMapInstance.clearOverlays()
+
+  const isWater = viewMapTab.value === 'waterlogging'
+  const points = isWater
+    ? (currentRow.value.waterloggingPoints || [])
+    : (currentRow.value.drainagePoints || [])
+
+  points.forEach(point => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    createMapMarker(viewMapInstance, bPoint, point.name)
+  })
+
+  if (points.length > 0) {
+    const viewPoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    viewMapInstance.setViewport(viewPoints)
+  }
+}
+
+function switchViewMapTab(tab) {
+  viewMapTab.value = tab
+  renderViewMarkers()
+}
+
+// ==================== 新增/修改对话框 ====================
+const editDialogVisible = ref(false)
+const editDialogTitle = ref('新增监测点')
+const editFormRef = ref(null)
+const isEditMode = ref(false)
+const editRowId = ref(null)
+
+const editForm = ref({
+  name: '',
+  area: '',
+  time: '',
+  drainage: 0,
+  waterlogging: 0,
+  level: 1
+})
+
+const editRules = {
+  name: [{ required: true, message: '请输入监测点名称', trigger: 'blur' }],
+  area: [{ required: true, message: '请输入行政区域', trigger: 'blur' }],
+  time: [{ required: true, message: '请选择数据时间', trigger: 'change' }],
+  level: [{ required: true, message: '请选择积水等级', trigger: 'change' }]
+}
+
+// 编辑弹窗地图相关
+const editMapTab = ref('waterlogging')
+let editMapInstance = null
+let editWaterPoints = ref([]) // 积水点临时数据
+let editDrainagePoints = ref([]) // 排水点临时数据
+let editWaterMarkerCount = ref(0)
+let editDrainageMarkerCount = ref(0)
+
+// 当前 tab 对应的点列表
+const currentEditPoints = computed(() => {
+  return editMapTab.value === 'waterlogging' ? editWaterPoints.value : editDrainagePoints.value
+})
+
+function handleAdd() {
+  isEditMode.value = false
+  editDialogTitle.value = '新增监测点'
+  editRowId.value = null
+  editForm.value = {
+    name: '',
+    area: '',
+    time: '',
+    drainage: 0,
+    waterlogging: 0,
+    level: 1
+  }
+  editMapTab.value = 'waterlogging'
+  editWaterPoints.value = []
+  editDrainagePoints.value = []
+  editWaterMarkerCount.value = 0
+  editDrainageMarkerCount.value = 0
+  editDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  isEditMode.value = true
+  editDialogTitle.value = '修改监测点'
+  editRowId.value = row.id
+  editForm.value = {
+    name: row.name,
+    area: row.area,
+    time: row.time,
+    drainage: row.drainage,
+    waterlogging: row.waterlogging,
+    level: row.level
+  }
+  editMapTab.value = 'waterlogging'
+  // 深拷贝标记点数据
+  editWaterPoints.value = (row.waterloggingPoints || []).map(p => ({ ...p }))
+  editDrainagePoints.value = (row.drainagePoints || []).map(p => ({ ...p }))
+  editWaterMarkerCount.value = editWaterPoints.value.length
+  editDrainageMarkerCount.value = editDrainagePoints.value.length
+  editDialogVisible.value = true
+}
+
+function handleEditClose() {
+  editDialogVisible.value = false
+  if (editMapInstance) {
+    editMapInstance = null
+  }
+}
+
+function onEditDialogOpened() {
+  setTimeout(() => {
+    initEditMap()
+  }, 300)
+}
+
+function initEditMap() {
+  const container = document.getElementById('psfqglEditMap')
+  if (!container) return
+
+  try {
+    editMapInstance = new BMapGL.Map('psfqglEditMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(centerPoint, 15)
+    editMapInstance.enableScrollWheelZoom(true)
+
+    // 点击地图添加标记
+    editMapInstance.addEventListener('click', function (e) {
+      addEditMarker(e.latlng)
+    })
+
+    // 渲染已有标记
+    renderEditMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function addEditMarker(point) {
+  console.log('addEditMarker called:', point.lng, point.lat)
+  const isWater = editMapTab.value === 'waterlogging'
+  const pointList = isWater ? editWaterPoints.value : editDrainagePoints.value
+  const count = isWater ? ++editWaterMarkerCount.value : ++editDrainageMarkerCount.value
+  const labelPrefix = isWater ? '积水点' : '排水点'
+
+  pointList.push({
+    name: `${labelPrefix}${count}`,
+    lng: point.lng,
+    lat: point.lat
+  })
+
+  // 统一重绘,保证地图与数据完全一致
+  renderEditMarkers(false)
+}
+
+function renderEditMarkers(shouldCenter = true) {
+  if (!editMapInstance) return
+  editMapInstance.clearOverlays()
+
+  const isWater = editMapTab.value === 'waterlogging'
+  const points = isWater ? editWaterPoints.value : editDrainagePoints.value
+  if (shouldCenter) {
+    const center = points.length > 0
+      ? new BMapGL.Point(points[0].lng, points[0].lat)
+      : new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(center, 15)
+  }
+
+  points.forEach((point, index) => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    const label = createMapMarker(editMapInstance, bPoint, point.name)
+
+    // 右键删除标记
+    label.addEventListener('rightclick', () => {
+      const list = isWater ? editWaterPoints.value : editDrainagePoints.value
+      list.splice(index, 1)
+      renderEditMarkers(false)
+    })
+  })
+}
+
+function switchEditMapTab(tab) {
+  editMapTab.value = tab
+  renderEditMarkers()
+}
+
+function clearEditMarkers() {
+  if (editMapTab.value === 'waterlogging') {
+    editWaterPoints.value = []
+    editWaterMarkerCount.value = 0
+  } else {
+    editDrainagePoints.value = []
+    editDrainageMarkerCount.value = 0
+  }
+  renderEditMarkers()
+}
+
+function handleEditSubmit() {
+  editFormRef.value.validate(valid => {
+    if (!valid) return
+
+    const formData = {
+      ...editForm.value,
+      waterloggingPoints: editWaterPoints.value.map(p => ({ ...p })),
+      drainagePoints: editDrainagePoints.value.map(p => ({ ...p }))
+    }
+
+    if (isEditMode.value) {
+      // 修改
+      const index = tableData.value.findIndex(item => item.id === editRowId.value)
+      if (index !== -1) {
+        tableData.value[index] = { ...tableData.value[index], ...formData }
+      }
+      ElMessage.success('修改成功')
+    } else {
+      // 新增
+      formData.id = nextId++
+      tableData.value.push(formData)
+      ElMessage.success('新增成功')
+    }
+
+    editDialogVisible.value = false
+    if (editMapInstance) {
+      editMapInstance = null
+    }
+  })
+}
+
+// ==================== 删除功能 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(`确认删除监测点"${row.name}"吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+
+function handleBatchDelete() {
+  if (selectedIds.value.length === 0) return
+  ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条数据吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id))
+    selectedIds.value = []
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-form {
+  margin-bottom: 16px;
+}
+
+.toolbar {
+  margin-bottom: 16px;
+}
+
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+
+.map-section {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.map-tabs {
+  display: flex;
+  background: #f5f7fa;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.map-tab {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #606266;
+  transition: all 0.3s;
+  user-select: none;
+}
+
+.map-tab:hover {
+  color: #409eff;
+}
+
+.map-tab.active {
+  background: #409eff;
+  color: #fff;
+}
+
+.map-tab-action {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #f56c6c;
+  transition: all 0.3s;
+  user-select: none;
+  margin-left: auto;
+}
+
+.map-tab-action:hover {
+  color: #f78989;
+  text-decoration: underline;
+}
+
+.map-container {
+  width: 100%;
+  height: 400px;
+}
+
+.marker-count {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #909399;
+  background: #f5f7fa;
+  border-top: 1px solid #e4e7ed;
+}
+</style>

+ 728 - 0
src/views/subSystem/drainage/jcsj/psgwgl.vue

@@ -0,0 +1,728 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="监测点名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="行政区域">
+        <el-input v-model="queryParams.area" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">删除</el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="监测点名称" prop="name" min-width="160" />
+      <el-table-column label="行政区域" prop="area" min-width="100" />
+      <el-table-column label="数据时间" prop="time" min-width="120" />
+      <el-table-column label="管点数量" prop="pipePointCount" min-width="80" />
+      <el-table-column label="排口数量" prop="outletCount" min-width="80" />
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">查看</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog v-model="dialogVisible" title="详情" width="700px" :before-close="handleClose" @opened="onDialogOpened">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">监测点名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">行政区域:</span>
+              <span class="info-value">{{ currentRow.area }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">数据时间:</span>
+              <span class="info-value">{{ currentRow.time }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">管点数量:</span>
+              <span class="info-value">{{ (currentRow.pipePoints || []).length }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">排口数量:</span>
+              <span class="info-value">{{ (currentRow.outletPoints || []).length }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 地图区域 -->
+      <div class="map-section">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: viewMapTab === 'pipeline' }"
+            @click="switchViewMapTab('pipeline')">管线</div>
+          <div class="map-tab" :class="{ active: viewMapTab === 'pipePoint' }"
+            @click="switchViewMapTab('pipePoint')">管点</div>
+          <div class="map-tab" :class="{ active: viewMapTab === 'outlet' }"
+            @click="switchViewMapTab('outlet')">排口</div>
+        </div>
+        <div id="psgwglViewMap" class="map-container"></div>
+      </div>
+    </el-dialog>
+
+    <!-- 新增/修改对话框 -->
+    <el-dialog v-model="editDialogVisible" :title="editDialogTitle" width="800px" :before-close="handleEditClose"
+      @opened="onEditDialogOpened">
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="监测点名称" prop="name">
+              <el-input v-model="editForm.name" placeholder="请输入监测点名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="行政区域" prop="area">
+              <el-input v-model="editForm.area" placeholder="请输入行政区域" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="数据时间" prop="time">
+              <el-date-picker v-model="editForm.time" type="date" placeholder="请选择日期" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="管点数量">
+              <el-input :model-value="editPipePoints.length" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="排口数量">
+              <el-input :model-value="editOutletPoints.length" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 地图标点区域 -->
+      <div class="map-section" style="margin-top: 16px;">
+        <div class="map-tabs">
+          <div class="map-tab" :class="{ active: editMapTab === 'pipeline' }"
+            @click="switchEditMapTab('pipeline')">管线</div>
+          <div class="map-tab" :class="{ active: editMapTab === 'pipePoint' }"
+            @click="switchEditMapTab('pipePoint')">管点</div>
+          <div class="map-tab" :class="{ active: editMapTab === 'outlet' }"
+            @click="switchEditMapTab('outlet')">排口</div>
+          <div class="map-tab-action" @click="clearEditMarkers">清除所有标记</div>
+        </div>
+        <div id="psgwglEditMap" class="map-container" style="height: 350px;"></div>
+        <div class="marker-count">
+          已标记 {{ currentEditPoints.length }} 个{{ editMapTabLabel }}
+        </div>
+      </div>
+
+      <template #footer>
+        <el-button @click="handleEditClose">取 消</el-button>
+        <el-button type="primary" @click="handleEditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Psgwgl">
+import { ref, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import locationIcon from '@/assets/images/location.png'
+
+// 地图标记创建(用Label+img代替Marker)
+function createMapMarker(map, bPoint, name) {
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:#409eff;border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${name || ''}</div>
+    <div><img src="${locationIcon}" style="width:28px;height:28px;display:block;margin:0 auto;"/></div>
+  </div>`
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -50)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  map.addOverlay(label)
+  return label
+}
+
+// ==================== 搜索参数 ====================
+const queryParams = ref({
+  name: '',
+  area: ''
+})
+
+// ==================== 表格数据 ====================
+let nextId = 5
+const tableData = ref([
+  {
+    id: 1, name: '沅陵北路1号管网', area: '沅陵县', time: '2025-12-31',
+    pipelineLength: '3.2km', pipePointCount: 12, outletCount: 3,
+    pipelinePoints: [
+      { name: '管线节点1', lng: 110.393, lat: 28.452 },
+      { name: '管线节点2', lng: 110.398, lat: 28.455 },
+      { name: '管线节点3', lng: 110.402, lat: 28.450 },
+      { name: '管线节点4', lng: 110.406, lat: 28.453 }
+    ],
+    pipePoints: [
+      { name: '管点1', lng: 110.395, lat: 28.448 },
+      { name: '管点2', lng: 110.400, lat: 28.460 }
+    ],
+    outletPoints: [
+      { name: '排口1', lng: 110.390, lat: 28.445 },
+      { name: '排口2', lng: 110.408, lat: 28.458 }
+    ]
+  },
+  {
+    id: 2, name: '沅陵南路1号管网', area: '沅陵县', time: '2025-12-30',
+    pipelineLength: '2.5km', pipePointCount: 8, outletCount: 2,
+    pipelinePoints: [
+      { name: '管线节点1', lng: 110.385, lat: 28.446 },
+      { name: '管线节点2', lng: 110.390, lat: 28.444 },
+      { name: '管线节点3', lng: 110.395, lat: 28.448 }
+    ],
+    pipePoints: [
+      { name: '管点1', lng: 110.388, lat: 28.450 }
+    ],
+    outletPoints: [
+      { name: '排口1', lng: 110.392, lat: 28.442 }
+    ]
+  },
+  {
+    id: 3, name: '沅陵东路2号管网', area: '沅陵县', time: '2025-12-29',
+    pipelineLength: '4.1km', pipePointCount: 15, outletCount: 5,
+    pipelinePoints: [
+      { name: '管线节点1', lng: 110.400, lat: 28.456 },
+      { name: '管线节点2', lng: 110.405, lat: 28.458 },
+      { name: '管线节点3', lng: 110.410, lat: 28.455 },
+      { name: '管线节点4', lng: 110.412, lat: 28.460 },
+      { name: '管线节点5', lng: 110.408, lat: 28.462 }
+    ],
+    pipePoints: [
+      { name: '管点1', lng: 110.403, lat: 28.454 },
+      { name: '管点2', lng: 110.409, lat: 28.461 },
+      { name: '管点3', lng: 110.398, lat: 28.459 }
+    ],
+    outletPoints: [
+      { name: '排口1', lng: 110.413, lat: 28.461 },
+      { name: '排口2', lng: 110.396, lat: 28.457 }
+    ]
+  },
+  {
+    id: 4, name: '沅陵西路1号管网', area: '沅陵县', time: '2025-12-28',
+    pipelineLength: '1.8km', pipePointCount: 6, outletCount: 1,
+    pipelinePoints: [
+      { name: '管线节点1', lng: 110.380, lat: 28.450 },
+      { name: '管线节点2', lng: 110.385, lat: 28.453 }
+    ],
+    pipePoints: [
+      { name: '管点1', lng: 110.382, lat: 28.448 }
+    ],
+    outletPoints: [
+      { name: '排口1', lng: 110.378, lat: 28.446 }
+    ]
+  }
+])
+
+// 多选
+const selectedIds = ref([])
+function handleSelectionChange(selection) {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+// 过滤后的列表
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const areaMatch = !queryParams.value.area || item.area.includes(queryParams.value.area)
+    return nameMatch && areaMatch
+  })
+})
+
+// 搜索
+function handleQuery() {
+  queryParams.value = { ...queryParams.value }
+}
+
+// ==================== 查看详情对话框 ====================
+const dialogVisible = ref(false)
+const currentRow = ref({})
+const viewMapTab = ref('pipeline')
+let viewMapInstance = null
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  viewMapTab.value = 'pipeline'
+  dialogVisible.value = true
+}
+
+function handleClose() {
+  dialogVisible.value = false
+  if (viewMapInstance) {
+    viewMapInstance = null
+  }
+}
+
+function onDialogOpened() {
+  setTimeout(() => {
+    initViewMap()
+  }, 300)
+}
+
+function initViewMap() {
+  const container = document.getElementById('psgwglViewMap')
+  if (!container) return
+
+  try {
+    viewMapInstance = new BMapGL.Map('psgwglViewMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    viewMapInstance.centerAndZoom(centerPoint, 15)
+    viewMapInstance.enableScrollWheelZoom(true)
+    renderViewMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function getViewPoints() {
+  const tab = viewMapTab.value
+  if (tab === 'pipeline') return currentRow.value.pipelinePoints || []
+  if (tab === 'pipePoint') return currentRow.value.pipePoints || []
+  return currentRow.value.outletPoints || []
+}
+
+function renderViewMarkers() {
+  if (!viewMapInstance) return
+  viewMapInstance.clearOverlays()
+
+  const points = getViewPoints()
+
+  points.forEach(point => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    createMapMarker(viewMapInstance, bPoint, point.name)
+  })
+
+  // 管线模式:绘制折线
+  if (viewMapTab.value === 'pipeline' && points.length >= 2) {
+    const polylinePoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    const polyline = new BMapGL.Polyline(polylinePoints, {
+      strokeColor: '#409eff',
+      strokeWeight: 4,
+      strokeOpacity: 0.8
+    })
+    viewMapInstance.addOverlay(polyline)
+  }
+
+  if (points.length > 0) {
+    const viewPoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    viewMapInstance.setViewport(viewPoints)
+  }
+}
+
+function switchViewMapTab(tab) {
+  viewMapTab.value = tab
+  renderViewMarkers()
+}
+
+// ==================== 新增/修改对话框 ====================
+const editDialogVisible = ref(false)
+const editDialogTitle = ref('新增监测点')
+const editFormRef = ref(null)
+const isEditMode = ref(false)
+const editRowId = ref(null)
+
+const editForm = ref({
+  name: '',
+  area: '',
+  time: '',
+  pipelineLength: '',
+  pipePointCount: '',
+  outletCount: ''
+})
+
+const editRules = {
+  name: [{ required: true, message: '请输入监测点名称', trigger: 'blur' }],
+  area: [{ required: true, message: '请输入行政区域', trigger: 'blur' }],
+  time: [{ required: true, message: '请选择数据时间', trigger: 'change' }]
+}
+
+// 编辑弹窗地图相关
+const editMapTab = ref('pipeline')
+let editMapInstance = null
+let editPipelinePoints = ref([])
+let editPipePoints = ref([])
+let editOutletPoints = ref([])
+let editPipelineMarkerCount = ref(0)
+let editPipeMarkerCount = ref(0)
+let editOutletMarkerCount = ref(0)
+
+// tab 中文名称
+const editMapTabLabel = computed(() => {
+  const map = { pipeline: '管线节点', pipePoint: '管点', outlet: '排口' }
+  return map[editMapTab.value] || ''
+})
+
+// 当前 tab 对应的点列表
+const currentEditPoints = computed(() => {
+  if (editMapTab.value === 'pipeline') return editPipelinePoints.value
+  if (editMapTab.value === 'pipePoint') return editPipePoints.value
+  return editOutletPoints.value
+})
+
+function handleAdd() {
+  isEditMode.value = false
+  editDialogTitle.value = '新增监测点'
+  editRowId.value = null
+  editForm.value = {
+    name: '',
+    area: '',
+    time: '',
+    pipelineLength: '',
+    pipePointCount: '',
+    outletCount: ''
+  }
+  editMapTab.value = 'pipeline'
+  editPipelinePoints.value = []
+  editPipePoints.value = []
+  editOutletPoints.value = []
+  editPipelineMarkerCount.value = 0
+  editPipeMarkerCount.value = 0
+  editOutletMarkerCount.value = 0
+  editDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  isEditMode.value = true
+  editDialogTitle.value = '修改监测点'
+  editRowId.value = row.id
+  editForm.value = {
+    name: row.name,
+    area: row.area,
+    time: row.time,
+    pipelineLength: row.pipelineLength,
+    pipePointCount: row.pipePointCount,
+    outletCount: row.outletCount
+  }
+  editMapTab.value = 'pipeline'
+  // 深拷贝标记点数据
+  editPipelinePoints.value = (row.pipelinePoints || []).map(p => ({ ...p }))
+  editPipePoints.value = (row.pipePoints || []).map(p => ({ ...p }))
+  editOutletPoints.value = (row.outletPoints || []).map(p => ({ ...p }))
+  editPipelineMarkerCount.value = editPipelinePoints.value.length
+  editPipeMarkerCount.value = editPipePoints.value.length
+  editOutletMarkerCount.value = editOutletPoints.value.length
+  editDialogVisible.value = true
+}
+
+function handleEditClose() {
+  editDialogVisible.value = false
+  if (editMapInstance) {
+    editMapInstance = null
+  }
+}
+
+function onEditDialogOpened() {
+  setTimeout(() => {
+    initEditMap()
+  }, 300)
+}
+
+function initEditMap() {
+  const container = document.getElementById('psgwglEditMap')
+  if (!container) return
+
+  try {
+    editMapInstance = new BMapGL.Map('psgwglEditMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(centerPoint, 15)
+    editMapInstance.enableScrollWheelZoom(true)
+
+    // 点击地图添加标记
+    editMapInstance.addEventListener('click', function (e) {
+      addEditMarker(e.latlng)
+    })
+
+    // 渲染已有标记
+    renderEditMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function getCurrentEditPointList() {
+  if (editMapTab.value === 'pipeline') return editPipelinePoints.value
+  if (editMapTab.value === 'pipePoint') return editPipePoints.value
+  return editOutletPoints.value
+}
+
+function addEditMarker(point) {
+  const tab = editMapTab.value
+  let pointList, count, labelPrefix
+
+  if (tab === 'pipeline') {
+    pointList = editPipelinePoints.value
+    count = ++editPipelineMarkerCount.value
+    labelPrefix = '管线节点'
+  } else if (tab === 'pipePoint') {
+    pointList = editPipePoints.value
+    count = ++editPipeMarkerCount.value
+    labelPrefix = '管点'
+  } else {
+    pointList = editOutletPoints.value
+    count = ++editOutletMarkerCount.value
+    labelPrefix = '排口'
+  }
+
+  pointList.push({
+    name: `${labelPrefix}${count}`,
+    lng: point.lng,
+    lat: point.lat
+  })
+
+  // 统一重绘,保证地图与数据完全一致
+  renderEditMarkers(false)
+}
+
+function renderEditMarkers(shouldCenter = true) {
+  if (!editMapInstance) return
+  editMapInstance.clearOverlays()
+
+  const points = getCurrentEditPointList()
+  if (shouldCenter) {
+    const center = points.length > 0
+      ? new BMapGL.Point(points[0].lng, points[0].lat)
+      : new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(center, 15)
+  }
+
+  points.forEach((point, index) => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    const label = createMapMarker(editMapInstance, bPoint, point.name)
+
+    // 右键删除标记
+    label.addEventListener('rightclick', () => {
+      const list = getCurrentEditPointList()
+      list.splice(index, 1)
+      renderEditMarkers(false)
+    })
+  })
+
+  // 管线模式:绘制折线
+  if (editMapTab.value === 'pipeline' && points.length >= 2) {
+    const polylinePoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    const polyline = new BMapGL.Polyline(polylinePoints, {
+      strokeColor: '#409eff',
+      strokeWeight: 4,
+      strokeOpacity: 0.8
+    })
+    editMapInstance.addOverlay(polyline)
+  }
+}
+
+function switchEditMapTab(tab) {
+  editMapTab.value = tab
+  renderEditMarkers()
+}
+
+function clearEditMarkers() {
+  if (editMapTab.value === 'pipeline') {
+    editPipelinePoints.value = []
+    editPipelineMarkerCount.value = 0
+  } else if (editMapTab.value === 'pipePoint') {
+    editPipePoints.value = []
+    editPipeMarkerCount.value = 0
+  } else {
+    editOutletPoints.value = []
+    editOutletMarkerCount.value = 0
+  }
+  renderEditMarkers()
+}
+
+function handleEditSubmit() {
+  editFormRef.value.validate(valid => {
+    if (!valid) return
+
+    const formData = {
+      ...editForm.value,
+      pipelinePoints: editPipelinePoints.value.map(p => ({ ...p })),
+      pipePoints: editPipePoints.value.map(p => ({ ...p })),
+      outletPoints: editOutletPoints.value.map(p => ({ ...p })),
+      pipePointCount: editPipePoints.value.length,
+      outletCount: editOutletPoints.value.length
+    }
+
+    if (isEditMode.value) {
+      // 修改
+      const index = tableData.value.findIndex(item => item.id === editRowId.value)
+      if (index !== -1) {
+        tableData.value[index] = { ...tableData.value[index], ...formData }
+      }
+      ElMessage.success('修改成功')
+    } else {
+      // 新增
+      formData.id = nextId++
+      tableData.value.push(formData)
+      ElMessage.success('新增成功')
+    }
+
+    editDialogVisible.value = false
+    if (editMapInstance) {
+      editMapInstance = null
+    }
+  })
+}
+
+// ==================== 删除功能 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(`确认删除监测点"${row.name}"吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+
+function handleBatchDelete() {
+  if (selectedIds.value.length === 0) return
+  ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条数据吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id))
+    selectedIds.value = []
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-form {
+  margin-bottom: 16px;
+}
+
+.toolbar {
+  margin-bottom: 16px;
+}
+
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+
+.map-section {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.map-tabs {
+  display: flex;
+  background: #f5f7fa;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.map-tab {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #606266;
+  transition: all 0.3s;
+  user-select: none;
+}
+
+.map-tab:hover {
+  color: #409eff;
+}
+
+.map-tab.active {
+  background: #409eff;
+  color: #fff;
+}
+
+.map-tab-action {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #f56c6c;
+  transition: all 0.3s;
+  user-select: none;
+  margin-left: auto;
+}
+
+.map-tab-action:hover {
+  color: #f78989;
+  text-decoration: underline;
+}
+
+.map-container {
+  width: 100%;
+  height: 400px;
+}
+
+.marker-count {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #909399;
+  background: #f5f7fa;
+  border-top: 1px solid #e4e7ed;
+}
+</style>

+ 483 - 0
src/views/subSystem/drainage/jcsj/sbjcgl.vue

@@ -0,0 +1,483 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="设备名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="设备类型">
+        <el-select v-model="queryParams.type" placeholder="请选择" clearable style="width: 200px">
+          <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="设备状态">
+        <el-select v-model="queryParams.status" placeholder="请选择" clearable style="width: 200px">
+          <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">删除</el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="设备名称" prop="name" min-width="140" />
+      <el-table-column label="设备编号" prop="code" min-width="130" />
+      <el-table-column label="设备类型" prop="type" min-width="110">
+        <template #default="{ row }">
+          <span>{{ typeText(row.type) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="所属监测点" prop="monitorPoint" min-width="140" />
+      <el-table-column label="安装位置" prop="location" min-width="150" />
+      <el-table-column label="设备状态" prop="status" min-width="90" align="center">
+        <template #default="{ row }">
+          <el-tag :type="statusTagType(row.status)">{{ statusText(row.status) }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="安装时间" prop="installTime" min-width="120" />
+      <el-table-column label="最近维护时间" prop="lastMaintenanceTime" min-width="130" />
+      <el-table-column label="操作" width="180" align="center" fixed="right">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">查看</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog v-model="dialogVisible" title="设备详情" width="700px" :before-close="handleClose">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备编号:</span>
+              <span class="info-value">{{ currentRow.code }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备类型:</span>
+              <span class="info-value">{{ typeText(currentRow.type) }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">所属监测点:</span>
+              <span class="info-value">{{ currentRow.monitorPoint }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">安装位置:</span>
+              <span class="info-value">{{ currentRow.location }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备状态:</span>
+              <span class="info-value">
+                <el-tag :type="statusTagType(currentRow.status)">{{ statusText(currentRow.status) }}</el-tag>
+              </span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">安装时间:</span>
+              <span class="info-value">{{ currentRow.installTime }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">最近维护时间:</span>
+              <span class="info-value">{{ currentRow.lastMaintenanceTime }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <div class="info-item">
+              <span class="info-label">备注:</span>
+              <span class="info-value">{{ currentRow.remark || '-' }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+    </el-dialog>
+
+    <!-- 新增/修改对话框 -->
+    <el-dialog v-model="editDialogVisible" :title="editDialogTitle" width="700px" :before-close="handleEditClose">
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="110px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="设备名称" prop="name">
+              <el-input v-model="editForm.name" placeholder="请输入设备名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备编号" prop="code">
+              <el-input v-model="editForm.code" placeholder="请输入设备编号" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="设备类型" prop="type">
+              <el-select v-model="editForm.type" placeholder="请选择设备类型" style="width: 100%">
+                <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="所属监测点" prop="monitorPoint">
+              <el-input v-model="editForm.monitorPoint" placeholder="请输入所属监测点" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="安装位置" prop="location">
+              <el-input v-model="editForm.location" placeholder="请输入安装位置" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备状态" prop="status">
+              <el-select v-model="editForm.status" placeholder="请选择设备状态" style="width: 100%">
+                <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="安装时间" prop="installTime">
+              <el-date-picker v-model="editForm.installTime" type="date" placeholder="请选择安装时间" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="最近维护时间" prop="lastMaintenanceTime">
+              <el-date-picker v-model="editForm.lastMaintenanceTime" type="date" placeholder="请选择维护时间" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="editForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button @click="handleEditClose">取 消</el-button>
+        <el-button type="primary" @click="handleEditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Sbjcgl">
+import { ref, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+// ==================== 选项配置 ====================
+const typeOptions = [
+  { label: '液位计', value: '1' },
+  { label: '流量计', value: '2' },
+  { label: '水质监测仪', value: '3' },
+  { label: '雨量计', value: '4' }
+]
+
+const statusOptions = [
+  { label: '在线', value: '1' },
+  { label: '离线', value: '2' },
+  { label: '故障', value: '3' },
+  { label: '维修中', value: '4' }
+]
+
+function typeText(val) {
+  const map = { '1': '液位计', '2': '流量计', '3': '水质监测仪', '4': '雨量计' }
+  return map[val] || '-'
+}
+
+function statusText(val) {
+  const map = { '1': '在线', '2': '离线', '3': '故障', '4': '维修中' }
+  return map[val] || '-'
+}
+
+function statusTagType(val) {
+  const map = { '1': 'success', '2': 'info', '3': 'danger', '4': 'warning' }
+  return map[val] || 'info'
+}
+
+// ==================== 搜索参数 ====================
+const queryParams = ref({
+  name: '',
+  type: '',
+  status: ''
+})
+
+// ==================== 表格数据 ====================
+let nextId = 8
+const tableData = ref([
+  {
+    id: 1, name: '城北液位监测仪', code: 'YWL-2025-001', type: '1',
+    monitorPoint: '城北排水监测站', location: '城北路段排水井A3号',
+    status: '1', installTime: '2024-03-15', lastMaintenanceTime: '2025-10-20',
+    remark: '主要监测城北路段地下排水管网液位'
+  },
+  {
+    id: 2, name: '河西流量计', code: 'LXJ-2025-002', type: '2',
+    monitorPoint: '河西排水监测站', location: '河西大道排水管道B7号',
+    status: '2', installTime: '2024-05-20', lastMaintenanceTime: '2025-08-12',
+    remark: '监测河西大道主干排水流量'
+  },
+  {
+    id: 3, name: '南区水质监测仪', code: 'SZJ-2025-003', type: '3',
+    monitorPoint: '南区水质监测站', location: '南环路污水处理厂入口',
+    status: '3', installTime: '2023-11-08', lastMaintenanceTime: '2025-11-05',
+    remark: '监测南区排水口水质指标'
+  },
+  {
+    id: 4, name: '东区雨量计', code: 'YLJ-2025-004', type: '4',
+    monitorPoint: '东区气象监测站', location: '东环路市政大楼楼顶',
+    status: '1', installTime: '2024-07-10', lastMaintenanceTime: '2025-09-18',
+    remark: '实时监测东区降雨量数据'
+  },
+  {
+    id: 5, name: '中心区液位监测仪', code: 'YWL-2025-005', type: '1',
+    monitorPoint: '中心区排水监测站', location: '人民路地下管网C2号',
+    status: '4', installTime: '2023-06-25', lastMaintenanceTime: '2025-12-01',
+    remark: '中心区核心排水节点液位监测,正在维修传感器'
+  },
+  {
+    id: 6, name: '北区流量计', code: 'LXJ-2025-006', type: '2',
+    monitorPoint: '北区排水监测站', location: '北环路排水泵站出口',
+    status: '1', installTime: '2024-09-12', lastMaintenanceTime: '2025-11-22',
+    remark: '监测北区泵站出口排水流量'
+  },
+  {
+    id: 7, name: '西区水质监测仪', code: 'SZJ-2025-007', type: '3',
+    monitorPoint: '西区水质监测站', location: '西环路河道排放口',
+    status: '2', installTime: '2024-01-18', lastMaintenanceTime: '2025-07-30',
+    remark: '监测西区河道排水口水质,设备离线待检修'
+  },
+  {
+    id: 8, name: '开发区雨量计', code: 'YLJ-2025-008', type: '4',
+    monitorPoint: '开发区气象监测站', location: '开发区管委会大楼楼顶',
+    status: '1', installTime: '2024-11-05', lastMaintenanceTime: '2025-12-10',
+    remark: '开发区降雨量实时监测设备'
+  }
+])
+
+// 多选
+const selectedIds = ref([])
+function handleSelectionChange(selection) {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+// 过滤后的列表
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const typeMatch = !queryParams.value.type || item.type === queryParams.value.type
+    const statusMatch = !queryParams.value.status || item.status === queryParams.value.status
+    return nameMatch && typeMatch && statusMatch
+  })
+})
+
+// 搜索
+function handleQuery() {
+  queryParams.value = { ...queryParams.value }
+}
+
+// ==================== 查看详情对话框 ====================
+const dialogVisible = ref(false)
+const currentRow = ref({})
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  dialogVisible.value = true
+}
+
+function handleClose() {
+  dialogVisible.value = false
+}
+
+// ==================== 新增/修改对话框 ====================
+const editDialogVisible = ref(false)
+const editDialogTitle = ref('新增设备')
+const editFormRef = ref(null)
+const isEditMode = ref(false)
+const editRowId = ref(null)
+
+const editForm = ref({
+  name: '',
+  code: '',
+  type: '',
+  monitorPoint: '',
+  location: '',
+  status: '',
+  installTime: '',
+  lastMaintenanceTime: '',
+  remark: ''
+})
+
+const editRules = {
+  name: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
+  code: [{ required: true, message: '请输入设备编号', trigger: 'blur' }],
+  type: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
+  monitorPoint: [{ required: true, message: '请输入所属监测点', trigger: 'blur' }],
+  location: [{ required: true, message: '请输入安装位置', trigger: 'blur' }],
+  status: [{ required: true, message: '请选择设备状态', trigger: 'change' }]
+}
+
+function handleAdd() {
+  isEditMode.value = false
+  editDialogTitle.value = '新增设备'
+  editRowId.value = null
+  editForm.value = {
+    name: '',
+    code: '',
+    type: '',
+    monitorPoint: '',
+    location: '',
+    status: '',
+    installTime: '',
+    lastMaintenanceTime: '',
+    remark: ''
+  }
+  editDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  isEditMode.value = true
+  editDialogTitle.value = '修改设备'
+  editRowId.value = row.id
+  editForm.value = {
+    name: row.name,
+    code: row.code,
+    type: row.type,
+    monitorPoint: row.monitorPoint,
+    location: row.location,
+    status: row.status,
+    installTime: row.installTime,
+    lastMaintenanceTime: row.lastMaintenanceTime,
+    remark: row.remark || ''
+  }
+  editDialogVisible.value = true
+}
+
+function handleEditClose() {
+  editDialogVisible.value = false
+}
+
+function handleEditSubmit() {
+  editFormRef.value.validate(valid => {
+    if (!valid) return
+
+    const formData = { ...editForm.value }
+
+    if (isEditMode.value) {
+      const index = tableData.value.findIndex(item => item.id === editRowId.value)
+      if (index !== -1) {
+        tableData.value[index] = { ...tableData.value[index], ...formData }
+      }
+      ElMessage.success('修改成功')
+    } else {
+      formData.id = nextId++
+      tableData.value.push(formData)
+      ElMessage.success('新增成功')
+    }
+
+    editDialogVisible.value = false
+  })
+}
+
+// ==================== 删除功能 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(`确认删除设备"${row.name}"吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+
+function handleBatchDelete() {
+  if (selectedIds.value.length === 0) return
+  ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条数据吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id))
+    selectedIds.value = []
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-form {
+  margin-bottom: 16px;
+}
+
+.toolbar {
+  margin-bottom: 16px;
+}
+
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+</style>

+ 546 - 0
src/views/subSystem/drainage/jcsj/yjsdgl.vue

@@ -0,0 +1,546 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="监测点名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="行政区域">
+        <el-input v-model="queryParams.area" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedIds.length === 0" @click="handleBatchDelete">删除</el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="监测点名称" prop="name" min-width="160" />
+      <el-table-column label="行政区域" prop="area" min-width="100" />
+      <el-table-column label="数据时间" prop="time" min-width="120" />
+      <el-table-column label="易积水点数量" prop="waterloggingCount" min-width="100" />
+      <el-table-column label="操作" width="180" align="center">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">查看</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情对话框 -->
+    <el-dialog v-model="dialogVisible" title="详情" width="700px" :before-close="handleClose" @opened="onDialogOpened">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">监测点名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">行政区域:</span>
+              <span class="info-value">{{ currentRow.area }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">数据时间:</span>
+              <span class="info-value">{{ currentRow.time }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">易积水点数量:</span>
+              <span class="info-value">{{ (currentRow.waterloggingPoints || []).length }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 地图区域 -->
+      <div class="map-section">
+        <div id="yjsdglViewMap" class="map-container"></div>
+      </div>
+    </el-dialog>
+
+    <!-- 新增/修改对话框 -->
+    <el-dialog v-model="editDialogVisible" :title="editDialogTitle" width="800px" :before-close="handleEditClose"
+      @opened="onEditDialogOpened">
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="监测点名称" prop="name">
+              <el-input v-model="editForm.name" placeholder="请输入监测点名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="行政区域" prop="area">
+              <el-input v-model="editForm.area" placeholder="请输入行政区域" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="数据时间" prop="time">
+              <el-date-picker v-model="editForm.time" type="date" placeholder="请选择日期" value-format="YYYY-MM-DD"
+                style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="易积水点数量">
+              <el-input :model-value="editWaterloggingPoints.length" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <!-- 地图标点区域 -->
+      <div class="map-section" style="margin-top: 16px;">
+        <div class="map-tabs">
+          <div class="map-tab active">易积水点</div>
+          <div class="map-tab-action" @click="clearEditMarkers">清除所有标记</div>
+        </div>
+        <div id="yjsdglEditMap" class="map-container" style="height: 350px;"></div>
+        <div class="marker-count">
+          已标记 {{ editWaterloggingPoints.length }} 个易积水点
+        </div>
+      </div>
+
+      <template #footer>
+        <el-button @click="handleEditClose">取 消</el-button>
+        <el-button type="primary" @click="handleEditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Yjsdgl">
+import { ref, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import locationIcon from '@/assets/images/location.png'
+
+// 地图标记创建(用Label+img代替Marker)
+function createMapMarker(map, bPoint, name) {
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:#409eff;border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${name || ''}</div>
+    <div><img src="${locationIcon}" style="width:28px;height:28px;display:block;margin:0 auto;"/></div>
+  </div>`
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -50)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  map.addOverlay(label)
+  return label
+}
+
+// ==================== 搜索参数 ====================
+const queryParams = ref({
+  name: '',
+  area: ''
+})
+
+// ==================== 表格数据 ====================
+let nextId = 5
+const tableData = ref([
+  {
+    id: 1, name: '沅陵北路1号易积水点', area: '沅陵县', time: '2025-12-31',
+    waterloggingCount: 3,
+    waterloggingPoints: [
+      { name: '易积水点1', lng: 110.393, lat: 28.452 },
+      { name: '易积水点2', lng: 110.398, lat: 28.455 },
+      { name: '易积水点3', lng: 110.402, lat: 28.450 }
+    ]
+  },
+  {
+    id: 2, name: '沅陵南路2号易积水点', area: '沅陵县', time: '2025-12-30',
+    waterloggingCount: 2,
+    waterloggingPoints: [
+      { name: '易积水点1', lng: 110.385, lat: 28.446 },
+      { name: '易积水点2', lng: 110.390, lat: 28.444 }
+    ]
+  },
+  {
+    id: 3, name: '沅陵东路3号易积水点', area: '沅陵县', time: '2025-12-29',
+    waterloggingCount: 4,
+    waterloggingPoints: [
+      { name: '易积水点1', lng: 110.400, lat: 28.456 },
+      { name: '易积水点2', lng: 110.405, lat: 28.458 },
+      { name: '易积水点3', lng: 110.403, lat: 28.454 },
+      { name: '易积水点4', lng: 110.409, lat: 28.461 }
+    ]
+  },
+  {
+    id: 4, name: '沅陵西路4号易积水点', area: '沅陵县', time: '2025-12-28',
+    waterloggingCount: 2,
+    waterloggingPoints: [
+      { name: '易积水点1', lng: 110.380, lat: 28.450 },
+      { name: '易积水点2', lng: 110.382, lat: 28.448 }
+    ]
+  }
+])
+
+// 多选
+const selectedIds = ref([])
+function handleSelectionChange(selection) {
+  selectedIds.value = selection.map(item => item.id)
+}
+
+// 过滤后的列表
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const areaMatch = !queryParams.value.area || item.area.includes(queryParams.value.area)
+    return nameMatch && areaMatch
+  })
+})
+
+// 搜索
+function handleQuery() {
+  queryParams.value = { ...queryParams.value }
+}
+
+// ==================== 查看详情对话框 ====================
+const dialogVisible = ref(false)
+const currentRow = ref({})
+let viewMapInstance = null
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  dialogVisible.value = true
+}
+
+function handleClose() {
+  dialogVisible.value = false
+  if (viewMapInstance) {
+    viewMapInstance = null
+  }
+}
+
+function onDialogOpened() {
+  setTimeout(() => {
+    initViewMap()
+  }, 300)
+}
+
+function initViewMap() {
+  const container = document.getElementById('yjsdglViewMap')
+  if (!container) return
+
+  try {
+    viewMapInstance = new BMapGL.Map('yjsdglViewMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    viewMapInstance.centerAndZoom(centerPoint, 15)
+    viewMapInstance.enableScrollWheelZoom(true)
+    renderViewMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function renderViewMarkers() {
+  if (!viewMapInstance) return
+  viewMapInstance.clearOverlays()
+
+  const points = currentRow.value.waterloggingPoints || []
+
+  points.forEach(point => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    createMapMarker(viewMapInstance, bPoint, point.name)
+  })
+
+  if (points.length > 0) {
+    const viewPoints = points.map(p => new BMapGL.Point(p.lng, p.lat))
+    viewMapInstance.setViewport(viewPoints)
+  }
+}
+
+// ==================== 新增/修改对话框 ====================
+const editDialogVisible = ref(false)
+const editDialogTitle = ref('新增监测点')
+const editFormRef = ref(null)
+const isEditMode = ref(false)
+const editRowId = ref(null)
+
+const editForm = ref({
+  name: '',
+  area: '',
+  time: ''
+})
+
+const editRules = {
+  name: [{ required: true, message: '请输入监测点名称', trigger: 'blur' }],
+  area: [{ required: true, message: '请输入行政区域', trigger: 'blur' }],
+  time: [{ required: true, message: '请选择数据时间', trigger: 'change' }]
+}
+
+// 编辑弹窗地图相关
+let editMapInstance = null
+let editWaterloggingPoints = ref([])
+let editWaterloggingMarkerCount = ref(0)
+
+function handleAdd() {
+  isEditMode.value = false
+  editDialogTitle.value = '新增监测点'
+  editRowId.value = null
+  editForm.value = {
+    name: '',
+    area: '',
+    time: ''
+  }
+  editWaterloggingPoints.value = []
+  editWaterloggingMarkerCount.value = 0
+  editDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  isEditMode.value = true
+  editDialogTitle.value = '修改监测点'
+  editRowId.value = row.id
+  editForm.value = {
+    name: row.name,
+    area: row.area,
+    time: row.time
+  }
+  // 深拷贝标记点数据
+  editWaterloggingPoints.value = (row.waterloggingPoints || []).map(p => ({ ...p }))
+  editWaterloggingMarkerCount.value = editWaterloggingPoints.value.length
+  editDialogVisible.value = true
+}
+
+function handleEditClose() {
+  editDialogVisible.value = false
+  if (editMapInstance) {
+    editMapInstance = null
+  }
+}
+
+function onEditDialogOpened() {
+  setTimeout(() => {
+    initEditMap()
+  }, 300)
+}
+
+function initEditMap() {
+  const container = document.getElementById('yjsdglEditMap')
+  if (!container) return
+
+  try {
+    editMapInstance = new BMapGL.Map('yjsdglEditMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(centerPoint, 15)
+    editMapInstance.enableScrollWheelZoom(true)
+
+    // 点击地图添加标记
+    editMapInstance.addEventListener('click', function (e) {
+      addEditMarker(e.latlng)
+    })
+
+    // 渲染已有标记
+    renderEditMarkers()
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function addEditMarker(point) {
+  const count = ++editWaterloggingMarkerCount.value
+  editWaterloggingPoints.value.push({
+    name: `易积水点${count}`,
+    lng: point.lng,
+    lat: point.lat
+  })
+
+  // 统一重绘,保证地图与数据完全一致
+  renderEditMarkers(false)
+}
+
+function renderEditMarkers(shouldCenter = true) {
+  if (!editMapInstance) return
+  editMapInstance.clearOverlays()
+
+  const points = editWaterloggingPoints.value
+  if (shouldCenter) {
+    const center = points.length > 0
+      ? new BMapGL.Point(points[0].lng, points[0].lat)
+      : new BMapGL.Point(110.393, 28.452)
+    editMapInstance.centerAndZoom(center, 15)
+  }
+
+  points.forEach((point, index) => {
+    const bPoint = new BMapGL.Point(point.lng, point.lat)
+    const label = createMapMarker(editMapInstance, bPoint, point.name)
+
+    // 右键删除标记
+    label.addEventListener('rightclick', () => {
+      editWaterloggingPoints.value.splice(index, 1)
+      renderEditMarkers(false)
+    })
+  })
+}
+
+function clearEditMarkers() {
+  editWaterloggingPoints.value = []
+  editWaterloggingMarkerCount.value = 0
+  renderEditMarkers()
+}
+
+function handleEditSubmit() {
+  editFormRef.value.validate(valid => {
+    if (!valid) return
+
+    const formData = {
+      ...editForm.value,
+      waterloggingPoints: editWaterloggingPoints.value.map(p => ({ ...p })),
+      waterloggingCount: editWaterloggingPoints.value.length
+    }
+
+    if (isEditMode.value) {
+      // 修改
+      const index = tableData.value.findIndex(item => item.id === editRowId.value)
+      if (index !== -1) {
+        tableData.value[index] = { ...tableData.value[index], ...formData }
+      }
+      ElMessage.success('修改成功')
+    } else {
+      // 新增
+      formData.id = nextId++
+      tableData.value.push(formData)
+      ElMessage.success('新增成功')
+    }
+
+    editDialogVisible.value = false
+    if (editMapInstance) {
+      editMapInstance = null
+    }
+  })
+}
+
+// ==================== 删除功能 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(`确认删除监测点"${row.name}"吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+
+function handleBatchDelete() {
+  if (selectedIds.value.length === 0) return
+  ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条数据吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id))
+    selectedIds.value = []
+    ElMessage.success('删除成功')
+  }).catch(() => { })
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.search-form {
+  margin-bottom: 16px;
+}
+
+.toolbar {
+  margin-bottom: 16px;
+}
+
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+
+.map-section {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.map-tabs {
+  display: flex;
+  background: #f5f7fa;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.map-tab {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: default;
+  color: #fff;
+  background: #409eff;
+  user-select: none;
+}
+
+.map-tab-action {
+  padding: 8px 24px;
+  font-size: 14px;
+  cursor: pointer;
+  color: #f56c6c;
+  transition: all 0.3s;
+  user-select: none;
+  margin-left: auto;
+}
+
+.map-tab-action:hover {
+  color: #f78989;
+  text-decoration: underline;
+}
+
+.map-container {
+  width: 100%;
+  height: 400px;
+}
+
+.marker-count {
+  padding: 8px 16px;
+  font-size: 13px;
+  color: #909399;
+  background: #f5f7fa;
+  border-top: 1px solid #e4e7ed;
+}
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcyj/dqyjgl.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>当前预警管理</h2>
+  </div>
+</template>
+
+<script setup name="Dqyjgl">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/jcyj/lsyjgl.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>历史预警管理</h2>
+  </div>
+</template>
+
+<script setup name="Lsyjgl">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/spjc/jcspGIS.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>监测视频GIS</h2>
+  </div>
+</template>
+
+<script setup name="JcspGIS">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/spjc/spfzgl.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>视频分组管理</h2>
+  </div>
+</template>
+
+<script setup name="Spfzgl">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/spjc/spgngl.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>视频关联管理</h2>
+  </div>
+</template>
+
+<script setup name="Spgngl">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/spjc/spplgl.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>视频批量管理</h2>
+  </div>
+</template>
+
+<script setup name="Spplgl">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/ssjc/bzyxqk.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>泵站运行情况</h2>
+  </div>
+</template>
+
+<script setup name="Bzyxqk">
+
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
src/views/subSystem/drainage/ssjc/gwyxqk.vue

@@ -0,0 +1,13 @@
+<template>
+  <div class="app-container">
+    <h2>管网运行情况</h2>
+  </div>
+</template>
+
+<script setup name="Gwyxqk">
+
+</script>
+
+<style scoped>
+
+</style>

+ 1047 - 0
src/views/subSystem/drainage/ssjc/jsqc.vue

@@ -0,0 +1,1047 @@
+<template>
+  <div class="jsqc-screen">
+    <!-- ==================== 顶部标题栏 ==================== -->
+    <div class="screen-header">
+      <div class="header-left">
+        <span class="header-time">{{ currentDateTime }}</span>
+      </div>
+      <div class="header-center">
+        <h1 class="header-title">排水管网安全运行监测系统</h1>
+      </div>
+      <div class="header-right">
+      </div>
+    </div>
+
+    <!-- ==================== 主体三栏布局 ==================== -->
+    <div class="screen-body">
+      <!-- ========== 左侧面板 ========== -->
+      <div class="panel-left">
+        <!-- 天气预报 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 天气预报
+          </div>
+          <div class="weather-cards">
+            <div class="weather-card" v-for="(w, i) in weatherList" :key="i">
+              <div class="weather-date">{{ w.date }}</div>
+              <div class="weather-icon">{{ w.icon }}</div>
+              <div class="weather-desc">{{ w.desc }}</div>
+              <div class="weather-temp">{{ w.tempRange }}</div>
+              <div class="weather-wind">{{ w.wind }}</div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 监测点概况 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 监测点概况
+          </div>
+          <div class="monitor-stats-row">
+            <span class="stat-tag online">在线 {{ monitorStats.online }}</span>
+            <span class="stat-divider">|</span>
+            <span class="stat-tag offline">离线 {{ monitorStats.offline }}</span>
+            <span class="stat-divider">|</span>
+            <span class="stat-tag rate">在线率 {{ monitorStats.onlineRate }}%</span>
+          </div>
+          <div class="pie-chart-wrapper">
+            <div id="jsqcPieChart" class="chart-area"></div>
+            <div class="pie-legend">
+              <div class="legend-item" v-for="(item, i) in pieData" :key="i">
+                <span class="legend-dot" :style="{ background: item.color }"></span>
+                <span class="legend-label">{{ item.name }}</span>
+                <span class="legend-value">{{ item.value }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 实时数据 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 实时数据
+            <span class="refresh-time">刷新: {{ refreshTime }}</span>
+          </div>
+          <div class="realtime-table-wrap">
+            <table class="dark-table">
+              <thead>
+                <tr>
+                  <th>序号</th>
+                  <th>监测点</th>
+                  <th>时间</th>
+                  <th>积水量(cm)</th>
+                  <th>级别</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(row, i) in realtimeData" :key="i">
+                  <td>{{ i + 1 }}</td>
+                  <td :title="row.name">{{ row.name }}</td>
+                  <td>{{ row.time }}</td>
+                  <td>{{ row.depth }}</td>
+                  <td>
+                    <span :class="'level-tag level-' + row.levelClass">{{ row.level }}</span>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+
+      <!-- ========== 中间地图区域 ========== -->
+      <div class="panel-center">
+        <div class="map-search">
+          <input type="text" placeholder="输入水源地" class="search-input" />
+          <span class="search-icon">🔍</span>
+        </div>
+        <div id="jsqcMap" class="map-container"></div>
+      </div>
+
+      <!-- ========== 右侧面板 ========== -->
+      <div class="panel-right">
+        <!-- 积水分析 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 积水分析
+          </div>
+          <div class="analysis-filter">
+            <select class="dark-select" v-model="analysisYear">
+              <option v-for="y in yearOptions" :key="y" :value="y">{{ y }}年</option>
+            </select>
+            <select class="dark-select" v-model="analysisLevel">
+              <option value="严重积水">严重积水</option>
+              <option value="中度积水">中度积水</option>
+              <option value="轻度积水">轻度积水</option>
+              <option value="未积水">未积水</option>
+            </select>
+            <button class="analysis-btn" @click="startAnalysis">开始分析</button>
+          </div>
+          <div class="analysis-result" v-if="analysisResult">
+            {{ analysisResult }}
+          </div>
+          <div id="jsqcBarChart" class="chart-area-sm"></div>
+        </div>
+
+        <!-- 积水统计 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 积水统计
+          </div>
+          <div id="jsqcLineChart" class="chart-area-sm"></div>
+        </div>
+
+        <!-- 预警信息 -->
+        <div class="panel-box">
+          <div class="panel-title">
+            <span class="title-icon">▶</span> 预警信息
+          </div>
+          <div class="alert-stats">
+            <div class="alert-stat-card">
+              <div class="alert-stat-val">{{ alertStats.warning }}</div>
+              <div class="alert-stat-label">积水预警数</div>
+            </div>
+            <div class="alert-stat-card">
+              <div class="alert-stat-val green">{{ alertStats.online }}</div>
+              <div class="alert-stat-label">设备在线数</div>
+            </div>
+            <div class="alert-stat-card">
+              <div class="alert-stat-val red">{{ alertStats.offline }}</div>
+              <div class="alert-stat-label">设备离线数</div>
+            </div>
+          </div>
+          <div class="alert-table-wrap">
+            <table class="dark-table">
+              <thead>
+                <tr>
+                  <th>序号</th>
+                  <th>地址</th>
+                  <th>时间</th>
+                  <th>报警类型</th>
+                  <th>实时数据</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(row, i) in alertList" :key="i">
+                  <td>{{ i + 1 }}</td>
+                  <td :title="row.address">{{ row.address }}</td>
+                  <td>{{ row.time }}</td>
+                  <td>{{ row.type }}</td>
+                  <td>{{ row.data }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="Jsqc">
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import * as echarts from 'echarts'
+import { useRouter } from 'vue-router'
+import locationIcon from '@/assets/images/location.png'
+
+const router = useRouter()
+
+// ==================== 实时时间 ====================
+const currentDateTime = ref('')
+let timer = null
+
+function updateTime() {
+  const now = new Date()
+  const pad = n => String(n).padStart(2, '0')
+  const weekDays = ['日', '一', '二', '三', '四', '五', '六']
+  const dateStr = `${now.getFullYear()}/${pad(now.getMonth() + 1)}/${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())} 星期${weekDays[now.getDay()]}`
+  currentDateTime.value = dateStr
+}
+
+// ==================== 刷新时间 ====================
+const refreshTime = ref('')
+function updateRefreshTime() {
+  const now = new Date()
+  const pad = n => String(n).padStart(2, '0')
+  refreshTime.value = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
+}
+
+// ==================== 天气数据 ====================
+const weatherList = ref([
+  { date: '06/11', icon: '☁️', desc: '多云', tempRange: '18°C ~ 26°C', wind: '南风 2级' },
+  { date: '06/12', icon: '🌧️', desc: '小雨', tempRange: '16°C ~ 23°C', wind: '东风 3级' },
+  { date: '06/13', icon: '⛅', desc: '阴转多云', tempRange: '15°C ~ 24°C', wind: '北风 2级' }
+])
+
+// ==================== 监测点概况 ====================
+const monitorStats = reactive({ online: 15, offline: 0, onlineRate: 100 })
+
+const pieData = ref([
+  { name: '未积水', value: 10, color: '#67c23a' },
+  { name: '轻度积水', value: 3, color: '#409eff' },
+  { name: '中度积水', value: 1, color: '#e6a23c' },
+  { name: '严重积水', value: 1, color: '#f56c6c' }
+])
+
+// ==================== 实时数据 ====================
+const realtimeData = ref([
+  { name: '沅陵大道1号闸口', time: '08:32:15', depth: 0, level: '未积水', levelClass: 'none' },
+  { name: '城北路段排水井A3', time: '08:30:42', depth: 3.2, level: '轻度积水', levelClass: 'light' },
+  { name: '河西大道排水管道B7', time: '08:31:08', depth: 8.5, level: '轻度积水', levelClass: 'light' },
+  { name: '人民路地下管网C2', time: '08:29:55', depth: 15.3, level: '中度积水', levelClass: 'moderate' },
+  { name: '南环路排水管道D3', time: '08:33:01', depth: 0, level: '未积水', levelClass: 'none' },
+  { name: '北环路排水泵站出口', time: '08:28:47', depth: 5.1, level: '轻度积水', levelClass: 'light' },
+  { name: '西环路河道排放口', time: '08:31:55', depth: 28.6, level: '严重积水', levelClass: 'severe' },
+  { name: '东环路市政大楼段', time: '08:32:40', depth: 0, level: '未积水', levelClass: 'none' },
+  { name: '开发区管委会路段', time: '08:30:22', depth: 12.4, level: '中度积水', levelClass: 'moderate' },
+  { name: '城北二号排水井A7', time: '08:33:18', depth: 0, level: '未积水', levelClass: 'none' }
+])
+
+// ==================== 积水分析 ====================
+const yearOptions = [2026, 2025, 2024, 2023]
+const analysisYear = ref(2025)
+const analysisLevel = ref('严重积水')
+const analysisResult = ref('')
+
+function startAnalysis() {
+  analysisResult.value = `沅陵县在${analysisYear.value}年,按${analysisLevel.value}次数进行统计,积水次数最多的监测点是河西大道排水管道B7号,${analysisLevel.value} 10 次。${analysisYear.value}-03-05 19:39:58 积水最深,积水深度达 0.34 m。`
+}
+
+// ==================== 预警信息 ====================
+const alertStats = reactive({ warning: 3, online: 12, offline: 0 })
+
+const alertList = ref([
+  { address: '沅陵县河西大道', time: '06-11 08:32', type: '降雨预警', data: '大雨' },
+  { address: '沅陵县城北路段', time: '06-11 08:30', type: '积水预警', data: '12.4cm' },
+  { address: '沅陵县西环路', time: '06-11 08:28', type: '积水预警', data: '28.6cm' },
+  { address: '沅陵县南环路', time: '06-11 07:55', type: '降雨预警', data: '中雨' },
+  { address: '沅陵县人民路', time: '06-11 07:42', type: '积水预警', data: '15.3cm' }
+])
+
+// ==================== 地图监测点数据 ====================
+const mapPoints = ref([
+  { name: '沅陵大道1号闸口', lng: 110.393, lat: 28.452, depth: 0, level: '未积水' },
+  { name: '城北路段排水井A3', lng: 110.385, lat: 28.462, depth: 3.2, level: '轻度积水' },
+  { name: '河西大道排水管道B7', lng: 110.378, lat: 28.456, depth: 8.5, level: '轻度积水' },
+  { name: '人民路地下管网C2', lng: 110.396, lat: 28.451, depth: 15.3, level: '中度积水' },
+  { name: '南环路排水管道D3', lng: 110.402, lat: 28.444, depth: 0, level: '未积水' },
+  { name: '北环路排水泵站出口', lng: 110.401, lat: 28.460, depth: 5.1, level: '轻度积水' },
+  { name: '西环路河道排放口', lng: 110.372, lat: 28.449, depth: 28.6, level: '严重积水' },
+  { name: '东环路市政大楼段', lng: 110.410, lat: 28.454, depth: 0, level: '未积水' },
+  { name: '开发区管委会路段', lng: 110.415, lat: 28.458, depth: 12.4, level: '中度积水' },
+  { name: '城北二号排水井A7', lng: 110.394, lat: 28.465, depth: 0, level: '未积水' },
+  { name: '白羊坪村监测点', lng: 110.380, lat: 28.468, depth: 0, level: '未积水' },
+  { name: '杜家山监测站', lng: 110.405, lat: 28.467, depth: 2.1, level: '轻度积水' },
+  { name: '阳光水岸监测点', lng: 110.388, lat: 28.445, depth: 0, level: '未积水' },
+  { name: '大营村监测站', lng: 110.418, lat: 28.446, depth: 0, level: '未积水' },
+  { name: '能瓦溪村委监测点', lng: 110.398, lat: 28.470, depth: 0, level: '未积水' }
+])
+
+// ==================== 导航方法 ====================
+function goBack() {
+  router.push('/')
+}
+function goAdmin() {
+  router.push('/index')
+}
+
+// ==================== 地图初始化 ====================
+let mapInstance = null
+
+function initMap() {
+  const container = document.getElementById('jsqcMap')
+  if (!container || typeof BMapGL === 'undefined') {
+    console.warn('BMapGL 未加载或容器不存在')
+    return
+  }
+
+  try {
+    mapInstance = new BMapGL.Map('jsqcMap')
+    const centerPoint = new BMapGL.Point(110.393, 28.452)
+    mapInstance.centerAndZoom(centerPoint, 15)
+    mapInstance.enableScrollWheelZoom(true)
+
+    // 添加标记点
+    mapPoints.value.forEach(point => {
+      addMapMarker(point)
+    })
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function addMapMarker(point) {
+  const levelColor = {
+    '未积水': '#67c23a',
+    '轻度积水': '#409eff',
+    '中度积水': '#e6a23c',
+    '严重积水': '#f56c6c'
+  }
+  const color = levelColor[point.level] || '#409eff'
+  const bPoint = new BMapGL.Point(point.lng, point.lat)
+
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:${color};border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${point.name}</div>
+    <div style="color:#fff;font-size:10px;background:rgba(0,0,0,0.6);border-radius:3px;padding:1px 4px;margin-bottom:2px;">水位:${point.depth}cm ${point.level}</div>
+    <div><img src="${locationIcon}" style="width:24px;height:24px;display:block;margin:0 auto;"/></div>
+  </div>`
+
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -55)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  mapInstance.addOverlay(label)
+
+  // 点击标记弹出信息窗口
+  label.addEventListener('click', function () {
+    const infoContent = `<div style="color:#333;font-size:13px;padding:4px;">
+      <div style="font-weight:bold;margin-bottom:6px;">${point.name}</div>
+      <div>积水量:<b style="color:${color}">${point.depth} cm</b></div>
+      <div>级别:<b style="color:${color}">${point.level}</b></div>
+      <div>经度:${point.lng}</div>
+      <div>纬度:${point.lat}</div>
+    </div>`
+    const infoWindow = new BMapGL.InfoWindow(infoContent, {
+      width: 200,
+      title: ''
+    })
+    mapInstance.openInfoWindow(infoWindow, bPoint)
+  })
+}
+
+// ==================== ECharts 图表 ====================
+let pieChart = null
+let barChart = null
+let lineChart = null
+
+function initPieChart() {
+  const dom = document.getElementById('jsqcPieChart')
+  if (!dom) return
+  pieChart = echarts.init(dom)
+  const total = pieData.value.reduce((s, d) => s + d.value, 0)
+  pieChart.setOption({
+    tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
+    legend: { show: false },
+    graphic: [{
+      type: 'text',
+      left: 'center',
+      top: '38%',
+      style: { text: '积水点个数', fill: '#a0cfff', fontSize: 12, textAlign: 'center' }
+    }, {
+      type: 'text',
+      left: 'center',
+      top: '52%',
+      style: { text: String(total), fill: '#fff', fontSize: 22, fontWeight: 'bold', textAlign: 'center' }
+    }],
+    series: [{
+      type: 'pie',
+      radius: ['45%', '70%'],
+      center: ['50%', '50%'],
+      label: { show: false },
+      labelLine: { show: false },
+      data: pieData.value.map(d => ({ name: d.name, value: d.value, itemStyle: { color: d.color } })),
+      emphasis: { scale: true, scaleSize: 5 }
+    }]
+  })
+}
+
+function initBarChart() {
+  const dom = document.getElementById('jsqcBarChart')
+  if (!dom) return
+  barChart = echarts.init(dom)
+  const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
+  const values = [3, 5, 10, 6, 4, 8, 12, 7, 3, 2, 5, 6]
+  barChart.setOption({
+    tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
+    grid: { left: 35, right: 10, top: 10, bottom: 25 },
+    xAxis: {
+      type: 'category',
+      data: months,
+      axisLabel: { color: '#7ec8e3', fontSize: 10 },
+      axisLine: { lineStyle: { color: '#1a3a5c' } }
+    },
+    yAxis: {
+      type: 'value',
+      axisLabel: { color: '#7ec8e3', fontSize: 10 },
+      splitLine: { lineStyle: { color: '#1a3a5c' } },
+      axisLine: { show: false }
+    },
+    series: [{
+      type: 'bar',
+      data: values,
+      barWidth: 12,
+      itemStyle: {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          { offset: 0, color: '#409eff' },
+          { offset: 1, color: '#1a3a5c' }
+        ]),
+        borderRadius: [2, 2, 0, 0]
+      }
+    }]
+  })
+}
+
+function initLineChart() {
+  const dom = document.getElementById('jsqcLineChart')
+  if (!dom) return
+  lineChart = echarts.init(dom)
+  const hours = []
+  const depthData = []
+  for (let i = 0; i < 24; i++) {
+    hours.push(String(i).padStart(2, '0') + ':00')
+    depthData.push(i < 8 || i > 20 ? Math.round(Math.random() * 3 * 10) / 10 : Math.round((5 + Math.random() * 20) * 10) / 10)
+  }
+  lineChart.setOption({
+    tooltip: { trigger: 'axis' },
+    grid: { left: 40, right: 10, top: 10, bottom: 25 },
+    xAxis: {
+      type: 'category',
+      data: hours,
+      axisLabel: { color: '#7ec8e3', fontSize: 9, interval: 2 },
+      axisLine: { lineStyle: { color: '#1a3a5c' } }
+    },
+    yAxis: {
+      type: 'value',
+      name: 'cm',
+      nameTextStyle: { color: '#7ec8e3', fontSize: 10 },
+      axisLabel: { color: '#7ec8e3', fontSize: 10 },
+      splitLine: { lineStyle: { color: '#1a3a5c' } },
+      axisLine: { show: false }
+    },
+    series: [{
+      type: 'bar',
+      data: depthData,
+      barWidth: 8,
+      itemStyle: {
+        color: (params) => {
+          const val = params.value
+          if (val >= 20) return '#f56c6c'
+          if (val >= 10) return '#e6a23c'
+          if (val > 0) return '#409eff'
+          return '#67c23a'
+        },
+        borderRadius: [2, 2, 0, 0]
+      }
+    }]
+  })
+}
+
+// ==================== 生命周期 ====================
+onMounted(() => {
+  updateTime()
+  timer = setInterval(() => {
+    updateTime()
+    updateRefreshTime()
+  }, 1000)
+  updateRefreshTime()
+
+  // 延迟初始化确保 DOM 渲染完成
+  setTimeout(() => {
+    initMap()
+    initPieChart()
+    initBarChart()
+    initLineChart()
+  }, 300)
+
+  // 默认执行一次分析
+  startAnalysis()
+})
+
+onUnmounted(() => {
+  if (timer) clearInterval(timer)
+  if (pieChart) pieChart.dispose()
+  if (barChart) barChart.dispose()
+  if (lineChart) lineChart.dispose()
+  if (mapInstance) mapInstance = null
+})
+
+// 监听窗口大小变化,重绘图表
+window.addEventListener('resize', () => {
+  if (pieChart) pieChart.resize()
+  if (barChart) barChart.resize()
+  if (lineChart) lineChart.resize()
+})
+</script>
+
+<style scoped>
+/* ==================== 全局暗色大屏 ==================== */
+.jsqc-screen {
+  width: 100%;
+  height: 100vh;
+  background: #0a1929;
+  color: #fff;
+  font-family: 'Microsoft YaHei', sans-serif;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+
+/* ==================== 顶部标题栏 ==================== */
+.screen-header {
+  height: 64px;
+  background: linear-gradient(180deg, #0d2137 0%, #0a1929 100%);
+  border-bottom: 1px solid rgba(64, 158, 255, 0.3);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 0 24px;
+  flex-shrink: 0;
+  position: relative;
+}
+
+.screen-header::after {
+  content: '';
+  position: absolute;
+  bottom: 0;
+  left: 10%;
+  right: 10%;
+  height: 1px;
+  background: linear-gradient(90deg, transparent, #409eff, transparent);
+}
+
+.header-left {
+  flex: 1;
+  text-align: left;
+}
+
+.header-time {
+  font-size: 15px;
+  color: #a0cfff;
+  letter-spacing: 1px;
+}
+
+.header-center {
+  flex: 2;
+  text-align: center;
+}
+
+.header-title {
+  font-size: 26px;
+  font-weight: 700;
+  margin: 0;
+  letter-spacing: 4px;
+  background: linear-gradient(180deg, #fff 0%, #7ec8e3 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  text-shadow: none;
+}
+
+.header-right {
+  flex: 1;
+  text-align: right;
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+.header-btn {
+  background: rgba(64, 158, 255, 0.15);
+  border: 1px solid rgba(64, 158, 255, 0.4);
+  color: #a0cfff;
+  padding: 6px 16px;
+  border-radius: 4px;
+  font-size: 13px;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.header-btn:hover {
+  background: rgba(64, 158, 255, 0.3);
+  border-color: #409eff;
+  color: #fff;
+}
+
+/* ==================== 主体三栏 ==================== */
+.screen-body {
+  flex: 1;
+  display: flex;
+  padding: 12px;
+  gap: 12px;
+  min-height: 0;
+}
+
+.panel-left,
+.panel-right {
+  width: 22%;
+  min-width: 260px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  min-height: 0;
+}
+
+.panel-center {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  position: relative;
+}
+
+/* ==================== 面板盒子 ==================== */
+.panel-box {
+  background: rgba(6, 30, 65, 0.8);
+  border: 1px solid rgba(64, 158, 255, 0.2);
+  border-radius: 6px;
+  padding: 12px;
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+
+.panel-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #7ec8e3;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  flex-shrink: 0;
+}
+
+.title-icon {
+  color: #409eff;
+  font-size: 10px;
+}
+
+.refresh-time {
+  margin-left: auto;
+  font-size: 11px;
+  color: #607080;
+  font-weight: 400;
+}
+
+/* ==================== 天气预报 ==================== */
+.weather-cards {
+  display: flex;
+  gap: 8px;
+  flex-shrink: 0;
+}
+
+.weather-card {
+  flex: 1;
+  background: rgba(20, 60, 110, 0.5);
+  border: 1px solid rgba(64, 158, 255, 0.15);
+  border-radius: 6px;
+  padding: 10px 8px;
+  text-align: center;
+}
+
+.weather-date {
+  font-size: 12px;
+  color: #a0cfff;
+  margin-bottom: 4px;
+}
+
+.weather-icon {
+  font-size: 24px;
+  margin: 4px 0;
+}
+
+.weather-desc {
+  font-size: 12px;
+  color: #d0e8ff;
+  margin-bottom: 2px;
+}
+
+.weather-temp {
+  font-size: 11px;
+  color: #7ec8e3;
+}
+
+.weather-wind {
+  font-size: 10px;
+  color: #607080;
+  margin-top: 2px;
+}
+
+/* ==================== 监测点概况 ==================== */
+.monitor-stats-row {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+  flex-shrink: 0;
+  font-size: 12px;
+}
+
+.stat-tag {
+  font-size: 12px;
+}
+
+.stat-tag.online {
+  color: #67c23a;
+}
+
+.stat-tag.offline {
+  color: #909399;
+}
+
+.stat-tag.rate {
+  color: #409eff;
+}
+
+.stat-divider {
+  color: #1a3a5c;
+}
+
+.pie-chart-wrapper {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  min-height: 0;
+}
+
+.chart-area {
+  width: 55%;
+  height: 100%;
+  min-height: 120px;
+}
+
+.pie-legend {
+  width: 45%;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 12px;
+}
+
+.legend-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 2px;
+  flex-shrink: 0;
+}
+
+.legend-label {
+  color: #a0cfff;
+  flex: 1;
+}
+
+.legend-value {
+  color: #fff;
+  font-weight: 600;
+}
+
+/* ==================== 实时数据表格 ==================== */
+.realtime-table-wrap {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0;
+}
+
+.dark-table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 12px;
+}
+
+.dark-table th {
+  background: rgba(20, 60, 110, 0.6);
+  color: #7ec8e3;
+  padding: 7px 6px;
+  text-align: center;
+  border-bottom: 1px solid rgba(64, 158, 255, 0.2);
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+.dark-table td {
+  padding: 6px;
+  text-align: center;
+  color: #d0e8ff;
+  border-bottom: 1px solid rgba(64, 158, 255, 0.08);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  max-width: 80px;
+}
+
+.dark-table tbody tr:nth-child(odd) {
+  background: rgba(10, 25, 50, 0.4);
+}
+
+.dark-table tbody tr:nth-child(even) {
+  background: rgba(20, 40, 70, 0.3);
+}
+
+.dark-table tbody tr:hover {
+  background: rgba(64, 158, 255, 0.1);
+}
+
+.level-tag {
+  padding: 1px 6px;
+  border-radius: 3px;
+  font-size: 11px;
+}
+
+.level-none {
+  color: #67c23a;
+  background: rgba(103, 194, 58, 0.15);
+}
+
+.level-light {
+  color: #409eff;
+  background: rgba(64, 158, 255, 0.15);
+}
+
+.level-moderate {
+  color: #e6a23c;
+  background: rgba(230, 162, 60, 0.15);
+}
+
+.level-severe {
+  color: #f56c6c;
+  background: rgba(245, 108, 108, 0.15);
+}
+
+/* ==================== 中间地图 ==================== */
+.map-container {
+  flex: 1;
+  border-radius: 6px;
+  overflow: hidden;
+  border: 1px solid rgba(64, 158, 255, 0.2);
+  min-height: 0;
+}
+
+.map-search {
+  position: absolute;
+  top: 12px;
+  left: 12px;
+  z-index: 10;
+  display: flex;
+  align-items: center;
+}
+
+.search-input {
+  background: rgba(6, 30, 65, 0.85);
+  border: 1px solid rgba(64, 158, 255, 0.3);
+  color: #a0cfff;
+  padding: 6px 30px 6px 12px;
+  border-radius: 4px;
+  font-size: 12px;
+  outline: none;
+  width: 160px;
+}
+
+.search-input::placeholder {
+  color: #4a6a8a;
+}
+
+.search-icon {
+  position: absolute;
+  right: 8px;
+  font-size: 14px;
+  cursor: pointer;
+}
+
+.map-tabs {
+  display: flex;
+  gap: 0;
+  background: rgba(6, 30, 65, 0.9);
+  border: 1px solid rgba(64, 158, 255, 0.2);
+  border-radius: 6px;
+  margin-top: 8px;
+  flex-shrink: 0;
+  overflow: hidden;
+}
+
+.map-tab {
+  flex: 1;
+  text-align: center;
+  padding: 8px 4px;
+  font-size: 12px;
+  color: #a0cfff;
+  cursor: pointer;
+  transition: all 0.3s;
+  border-right: 1px solid rgba(64, 158, 255, 0.1);
+}
+
+.map-tab:last-child {
+  border-right: none;
+}
+
+.map-tab.active {
+  background: rgba(64, 158, 255, 0.25);
+  color: #fff;
+  font-weight: 600;
+}
+
+.map-tab:hover {
+  background: rgba(64, 158, 255, 0.15);
+}
+
+/* ==================== 积水分析 ==================== */
+.analysis-filter {
+  display: flex;
+  gap: 6px;
+  margin-bottom: 8px;
+  flex-shrink: 0;
+  flex-wrap: wrap;
+}
+
+.dark-select {
+  background: rgba(10, 25, 50, 0.8);
+  border: 1px solid rgba(64, 158, 255, 0.3);
+  color: #a0cfff;
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  outline: none;
+  cursor: pointer;
+}
+
+.dark-select option {
+  background: #0a1929;
+  color: #a0cfff;
+}
+
+.analysis-btn {
+  background: rgba(64, 158, 255, 0.2);
+  border: 1px solid rgba(64, 158, 255, 0.5);
+  color: #7ec8e3;
+  padding: 4px 12px;
+  border-radius: 4px;
+  font-size: 12px;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.analysis-btn:hover {
+  background: rgba(64, 158, 255, 0.35);
+  color: #fff;
+}
+
+.analysis-result {
+  font-size: 12px;
+  color: #a0cfff;
+  line-height: 1.8;
+  margin-bottom: 8px;
+  padding: 8px;
+  background: rgba(10, 25, 50, 0.5);
+  border-radius: 4px;
+  border-left: 3px solid #409eff;
+  flex-shrink: 0;
+}
+
+.chart-area-sm {
+  width: 100%;
+  height: 130px;
+  flex-shrink: 0;
+}
+
+/* ==================== 预警信息 ==================== */
+.alert-stats {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 8px;
+  flex-shrink: 0;
+}
+
+.alert-stat-card {
+  flex: 1;
+  background: rgba(10, 25, 50, 0.6);
+  border: 1px solid rgba(64, 158, 255, 0.15);
+  border-radius: 6px;
+  padding: 8px;
+  text-align: center;
+}
+
+.alert-stat-val {
+  font-size: 20px;
+  font-weight: 700;
+  color: #e6a23c;
+}
+
+.alert-stat-val.green {
+  color: #67c23a;
+}
+
+.alert-stat-val.red {
+  color: #f56c6c;
+}
+
+.alert-stat-label {
+  font-size: 10px;
+  color: #7ec8e3;
+  margin-top: 2px;
+}
+
+.alert-table-wrap {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0;
+}
+
+/* ==================== 滚动条样式 ==================== */
+::-webkit-scrollbar {
+  width: 4px;
+}
+
+::-webkit-scrollbar-track {
+  background: rgba(10, 25, 50, 0.3);
+}
+
+::-webkit-scrollbar-thumb {
+  background: rgba(64, 158, 255, 0.3);
+  border-radius: 2px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background: rgba(64, 158, 255, 0.5);
+}
+</style>

+ 1211 - 0
src/views/subSystem/drainage/ssjc/sbyx.vue

@@ -0,0 +1,1211 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部统计卡片区 -->
+    <div class="stat-cards">
+      <el-card class="stat-card stat-blue" shadow="hover">
+        <div class="stat-card-inner">
+          <div class="stat-icon"><el-icon :size="36"><Monitor /></el-icon></div>
+          <div class="stat-info">
+            <div class="stat-value">{{ stats.total }}</div>
+            <div class="stat-label">设备总数</div>
+          </div>
+        </div>
+      </el-card>
+      <el-card class="stat-card stat-green" shadow="hover">
+        <div class="stat-card-inner">
+          <div class="stat-icon"><el-icon :size="36"><CircleCheck /></el-icon></div>
+          <div class="stat-info">
+            <div class="stat-value">{{ stats.online }}</div>
+            <div class="stat-label">在线设备</div>
+          </div>
+        </div>
+      </el-card>
+      <el-card class="stat-card stat-gray" shadow="hover">
+        <div class="stat-card-inner">
+          <div class="stat-icon"><el-icon :size="36"><SwitchButton /></el-icon></div>
+          <div class="stat-info">
+            <div class="stat-value">{{ stats.offline }}</div>
+            <div class="stat-label">离线设备</div>
+          </div>
+        </div>
+      </el-card>
+      <el-card class="stat-card stat-red" shadow="hover">
+        <div class="stat-card-inner">
+          <div class="stat-icon"><el-icon :size="36"><Warning /></el-icon></div>
+          <div class="stat-info">
+            <div class="stat-value">{{ stats.alarm }}</div>
+            <div class="stat-label">报警设备</div>
+          </div>
+        </div>
+      </el-card>
+      <el-card class="stat-card stat-cyan" shadow="hover">
+        <div class="stat-card-inner">
+          <div class="stat-icon"><el-icon :size="36"><DataLine /></el-icon></div>
+          <div class="stat-info">
+            <div class="stat-value">{{ stats.onlineRate }}%</div>
+            <div class="stat-label">在线率</div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 搜索/筛选区域 -->
+    <el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
+      <el-form-item label="设备名称">
+        <el-input v-model="queryParams.name" placeholder="请输入" clearable style="width: 200px" />
+      </el-form-item>
+      <el-form-item label="设备类型">
+        <el-select v-model="queryParams.type" placeholder="请选择" clearable style="width: 200px">
+          <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </el-form-item>
+      <template v-if="showMoreSearch">
+        <el-form-item label="监测状态">
+          <el-select v-model="queryParams.status" placeholder="请选择" clearable style="width: 200px">
+            <el-option v-for="item in monitorStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="所属区域">
+          <el-select v-model="queryParams.area" placeholder="请选择" clearable style="width: 200px">
+            <el-option v-for="item in areaOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+      </template>
+      <el-form-item>
+        <el-button type="primary" @click="handleQuery">搜索</el-button>
+        <el-button @click="resetQuery">重置</el-button>
+        <el-button link type="primary" @click="showMoreSearch = !showMoreSearch">
+          {{ showMoreSearch ? '收起' : '展开' }}
+          <el-icon class="expand-icon" :class="{ 'is-rotate': showMoreSearch }"><ArrowUp /></el-icon>
+        </el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区 -->
+    <div class="toolbar">
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+      <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchDelete">删除</el-button>
+      <el-button type="warning" @click="handleExport">导出</el-button>
+      <el-button type="primary" :disabled="selectedRows.length === 0" @click="handleBatchRepair">维修派单</el-button>
+      <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchWarning">发布预警</el-button>
+    </div>
+
+    <!-- 实时数据表格 -->
+    <el-table :data="filteredList" border style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="设备名称" prop="name" min-width="140" />
+      <el-table-column label="设备编号" prop="code" min-width="130" />
+      <el-table-column label="设备类型" prop="type" min-width="110" align="center">
+        <template #default="{ row }">
+          <span>{{ typeText(row.type) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="所属监测点" prop="monitorPoint" min-width="140" />
+      <el-table-column label="监测状态" prop="status" min-width="90" align="center">
+        <template #default="{ row }">
+          <el-tag :type="monitorStatusTagType(row.status)">{{ monitorStatusText(row.status) }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="液位值" prop="waterLevel" min-width="100" align="center">
+        <template #default="{ row }">
+          <span>{{ row.waterLevel }} m</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="流量值" prop="flowRate" min-width="110" align="center">
+        <template #default="{ row }">
+          <span>{{ row.flowRate }} m³/h</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="水质指标" prop="waterQuality" min-width="90" align="center">
+        <template #default="{ row }">
+          <span :class="'wq-' + row.waterQuality">{{ row.waterQuality }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="报警阈值" prop="alarmThreshold" min-width="140" />
+      <el-table-column label="最后更新时间" prop="updateTime" min-width="160" />
+      <el-table-column label="操作" width="280" align="center" fixed="right">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="handleView(row)">详情</el-button>
+          <el-button link type="primary" @click="handleLocate(row)">定位</el-button>
+          <el-button link type="warning" @click="handleRepair(row)">派单</el-button>
+          <el-button link type="primary" @click="handleEdit(row)">修改</el-button>
+          <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 查看详情弹窗 -->
+    <el-dialog v-model="detailDialogVisible" title="设备实时详情" width="750px">
+      <div class="detail-info">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备名称:</span>
+              <span class="info-value">{{ currentRow.name }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备编号:</span>
+              <span class="info-value">{{ currentRow.code }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">设备类型:</span>
+              <span class="info-value">{{ typeText(currentRow.type) }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">所属监测点:</span>
+              <span class="info-value">{{ currentRow.monitorPoint }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">监测状态:</span>
+              <span class="info-value">
+                <el-tag :type="monitorStatusTagType(currentRow.status)">{{ monitorStatusText(currentRow.status) }}</el-tag>
+              </span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">在线状态:</span>
+              <span class="info-value">
+                <el-tag :type="currentRow.onlineStatus === '1' ? 'success' : 'info'">
+                  {{ currentRow.onlineStatus === '1' ? '在线' : '离线' }}
+                </el-tag>
+              </span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">液位值:</span>
+              <span class="info-value">{{ currentRow.waterLevel }} m</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">流量值:</span>
+              <span class="info-value">{{ currentRow.flowRate }} m³/h</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">水质指标:</span>
+              <span class="info-value">{{ currentRow.waterQuality }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">报警阈值:</span>
+              <span class="info-value">{{ currentRow.alarmThreshold }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">所属区域:</span>
+              <span class="info-value">{{ currentRow.area }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="info-item">
+              <span class="info-label">最后更新时间:</span>
+              <span class="info-value">{{ currentRow.updateTime }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <div class="info-item">
+              <span class="info-label">安装位置:</span>
+              <span class="info-value">{{ currentRow.location }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+    </el-dialog>
+
+    <!-- 定位弹窗 -->
+    <el-dialog v-model="locateDialogVisible" title="设备定位" width="700px" @opened="onLocateDialogOpened" :before-close="handleLocateClose">
+      <div class="locate-info">
+        <div class="locate-device">
+          <span class="info-label">设备名称:</span>
+          <span class="info-value">{{ locateRow.name }}</span>
+          <span style="margin-left: 20px" class="info-label">设备编号:</span>
+          <span class="info-value">{{ locateRow.code }}</span>
+        </div>
+        <div id="sbyxLocationMap" class="map-container"></div>
+        <div class="locate-coords">
+          经度:{{ locateTempLng }}  纬度:{{ locateTempLat }}
+        </div>
+        <div class="locate-tip">点击地图可修改设备定位</div>
+      </div>
+      <template #footer>
+        <el-button @click="handleLocateCancel">取 消</el-button>
+        <el-button type="primary" @click="handleLocateConfirm">确 定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 维修派单弹窗 -->
+    <el-dialog v-model="repairDialogVisible" title="维修派单" width="600px">
+      <el-form ref="repairFormRef" :model="repairForm" :rules="repairRules" label-width="100px">
+        <el-form-item label="设备名称" prop="name">
+          <el-input v-model="repairForm.name" disabled />
+        </el-form-item>
+        <el-form-item label="故障描述" prop="faultDesc">
+          <el-input v-model="repairForm.faultDesc" type="textarea" :rows="3" placeholder="请输入故障描述" />
+        </el-form-item>
+        <el-form-item label="维修人员" prop="repairPerson">
+          <el-input v-model="repairForm.repairPerson" placeholder="请输入维修人员" />
+        </el-form-item>
+        <el-form-item label="联系电话" prop="phone">
+          <el-input v-model="repairForm.phone" placeholder="请输入联系电话" />
+        </el-form-item>
+        <el-form-item label="紧急程度" prop="urgency">
+          <el-select v-model="repairForm.urgency" placeholder="请选择紧急程度" style="width: 100%">
+            <el-option v-for="item in urgencyOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="repairForm.remark" type="textarea" :rows="2" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="repairDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleRepairSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 发布预警弹窗 -->
+    <el-dialog v-model="warningDialogVisible" title="发布预警" width="600px">
+      <el-form ref="warningFormRef" :model="warningForm" :rules="warningRules" label-width="100px">
+        <el-form-item label="预警设备" prop="name">
+          <el-input v-model="warningForm.name" disabled />
+        </el-form-item>
+        <el-form-item label="预警级别" prop="level">
+          <el-select v-model="warningForm.level" placeholder="请选择预警级别" style="width: 100%">
+            <el-option v-for="item in warningLevelOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="预警内容" prop="content">
+          <el-input v-model="warningForm.content" type="textarea" :rows="3" placeholder="请输入预警内容" />
+        </el-form-item>
+        <el-form-item label="发布范围" prop="scope">
+          <el-select v-model="warningForm.scope" placeholder="请选择发布范围" style="width: 100%">
+            <el-option v-for="item in scopeOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="warningDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleWarningSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 新增/修改设备弹窗 -->
+    <el-dialog v-model="deviceDialogVisible" :title="deviceDialogTitle" width="700px" @close="resetDeviceForm">
+      <el-form ref="deviceFormRef" :model="deviceForm" :rules="deviceFormRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="设备名称" prop="name">
+              <el-input v-model="deviceForm.name" placeholder="请输入设备名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备编号" prop="code">
+              <el-input v-model="deviceForm.code" placeholder="请输入设备编号" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="设备类型" prop="type">
+              <el-select v-model="deviceForm.type" placeholder="请选择设备类型" style="width: 100%">
+                <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="所属监测点" prop="monitorPoint">
+              <el-input v-model="deviceForm.monitorPoint" placeholder="请输入所属监测点" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="安装位置" prop="location">
+              <el-input v-model="deviceForm.location" placeholder="请输入安装位置" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备状态" prop="status">
+              <el-select v-model="deviceForm.status" placeholder="请选择设备状态" style="width: 100%">
+                <el-option v-for="item in deviceStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="监测状态" prop="monitorStatus">
+              <el-select v-model="deviceForm.monitorStatus" placeholder="请选择监测状态" style="width: 100%">
+                <el-option v-for="item in monitorStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="液位值" prop="waterLevel">
+              <el-input v-model="deviceForm.waterLevel" placeholder="请输入液位值" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="流量值" prop="flowRate">
+              <el-input v-model="deviceForm.flowRate" placeholder="请输入流量值" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="水质指标" prop="waterQuality">
+              <el-select v-model="deviceForm.waterQuality" placeholder="请选择水质指标" style="width: 100%">
+                <el-option v-for="item in waterQualityOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="报警阈值" prop="alarmThreshold">
+              <el-input v-model="deviceForm.alarmThreshold" placeholder="请输入报警阈值" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="安装时间" prop="installTime">
+              <el-date-picker v-model="deviceForm.installTime" type="date" placeholder="请选择安装时间" value-format="YYYY-MM-DD" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="deviceForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button @click="deviceDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleDeviceSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Sbyx">
+import { ref, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import locationIcon from '@/assets/images/location.png'
+
+// 地图标记创建(用Label+img代替Marker)
+function createMapMarker(map, bPoint, name) {
+  const html = `<div style="text-align:center;cursor:pointer;">
+    <div style="color:#fff;background:#409eff;border-radius:4px;padding:2px 6px;font-size:11px;white-space:nowrap;display:inline-block;margin-bottom:2px;">${name || ''}</div>
+    <div><img src="${locationIcon}" style="width:28px;height:28px;display:block;margin:0 auto;"/></div>
+  </div>`
+  const label = new BMapGL.Label(html, {
+    position: bPoint,
+    offset: new BMapGL.Size(-30, -50)
+  })
+  label.setStyle({
+    border: 'none',
+    background: 'transparent',
+    padding: '0'
+  })
+  map.addOverlay(label)
+  return label
+}
+
+// ==================== 选项配置 ====================
+const typeOptions = [
+  { label: '液位计', value: '1' },
+  { label: '流量计', value: '2' },
+  { label: '水质监测仪', value: '3' },
+  { label: '雨量计', value: '4' }
+]
+
+const monitorStatusOptions = [
+  { label: '正常', value: '1' },
+  { label: '预警', value: '2' },
+  { label: '报警', value: '3' }
+]
+
+const areaOptions = [
+  { label: '城北片区', value: '1' },
+  { label: '河西片区', value: '2' },
+  { label: '南区片区', value: '3' },
+  { label: '东区片区', value: '4' },
+  { label: '中心片区', value: '5' },
+  { label: '西区片区', value: '6' }
+]
+
+const urgencyOptions = [
+  { label: '一般', value: '1' },
+  { label: '紧急', value: '2' },
+  { label: '非常紧急', value: '3' }
+]
+
+const warningLevelOptions = [
+  { label: '一级(红色)', value: '1' },
+  { label: '二级(橙色)', value: '2' },
+  { label: '三级(黄色)', value: '3' },
+  { label: '四级(蓝色)', value: '4' }
+]
+
+const scopeOptions = [
+  { label: '全部', value: '1' },
+  { label: '相关部门', value: '2' },
+  { label: '指定人员', value: '3' }
+]
+
+const deviceStatusOptions = [
+  { label: '在线', value: '1' },
+  { label: '离线', value: '2' },
+  { label: '故障', value: '3' },
+  { label: '维修中', value: '4' }
+]
+
+const waterQualityOptions = [
+  { label: '优', value: '优' },
+  { label: '良', value: '良' },
+  { label: '中', value: '中' },
+  { label: '差', value: '差' }
+]
+
+function typeText(val) {
+  const map = { '1': '液位计', '2': '流量计', '3': '水质监测仪', '4': '雨量计' }
+  return map[val] || '-'
+}
+
+function monitorStatusText(val) {
+  const map = { '1': '正常', '2': '预警', '3': '报警' }
+  return map[val] || '-'
+}
+
+function monitorStatusTagType(val) {
+  const map = { '1': 'success', '2': 'warning', '3': 'danger' }
+  return map[val] || 'info'
+}
+
+// ==================== 模拟数据 ====================
+const tableData = ref([
+  {
+    id: 1, name: '城北液位监测仪', code: 'YWL-2025-001', type: '1',
+    monitorPoint: '城北排水监测站', area: '1', location: '城北路段排水井A3号',
+    status: '1', onlineStatus: '1',
+    waterLevel: 1.82, flowRate: 120, waterQuality: '良',
+    alarmThreshold: '液位>3.0m', updateTime: '2026-06-11 08:32:15',
+    lng: '110.393', lat: '28.452'
+  },
+  {
+    id: 2, name: '河西流量计', code: 'LXJ-2025-002', type: '2',
+    monitorPoint: '河西排水监测站', area: '2', location: '河西大道排水管道B7号',
+    status: '3', onlineStatus: '1',
+    waterLevel: 2.65, flowRate: 435, waterQuality: '中',
+    alarmThreshold: '流量>400m³/h', updateTime: '2026-06-11 08:30:42',
+    lng: '110.388', lat: '28.456'
+  },
+  {
+    id: 3, name: '南区水质监测仪', code: 'SZJ-2025-003', type: '3',
+    monitorPoint: '南区水质监测站', area: '3', location: '南环路污水处理厂入口',
+    status: '2', onlineStatus: '1',
+    waterLevel: 1.45, flowRate: 185, waterQuality: '差',
+    alarmThreshold: '水质>差', updateTime: '2026-06-11 08:31:08',
+    lng: '110.402', lat: '28.448'
+  },
+  {
+    id: 4, name: '东区雨量计', code: 'YLJ-2025-004', type: '4',
+    monitorPoint: '东区气象监测站', area: '4', location: '东环路市政大楼楼顶',
+    status: '1', onlineStatus: '1',
+    waterLevel: 0.82, flowRate: 65, waterQuality: '优',
+    alarmThreshold: '液位>2.5m', updateTime: '2026-06-11 08:29:55',
+    lng: '110.408', lat: '28.454'
+  },
+  {
+    id: 5, name: '中心区液位监测仪', code: 'YWL-2025-005', type: '1',
+    monitorPoint: '中心区排水监测站', area: '5', location: '人民路地下管网C2号',
+    status: '3', onlineStatus: '1',
+    waterLevel: 3.25, flowRate: 380, waterQuality: '中',
+    alarmThreshold: '液位>3.0m', updateTime: '2026-06-11 08:33:01',
+    lng: '110.396', lat: '28.451'
+  },
+  {
+    id: 6, name: '北区流量计', code: 'LXJ-2025-006', type: '2',
+    monitorPoint: '北区排水监测站', area: '1', location: '北环路排水泵站出口',
+    status: '1', onlineStatus: '1',
+    waterLevel: 1.15, flowRate: 210, waterQuality: '良',
+    alarmThreshold: '流量>350m³/h', updateTime: '2026-06-11 08:28:47',
+    lng: '110.401', lat: '28.460'
+  },
+  {
+    id: 7, name: '西区水质监测仪', code: 'SZJ-2025-007', type: '3',
+    monitorPoint: '西区水质监测站', area: '6', location: '西环路河道排放口',
+    status: '1', onlineStatus: '2',
+    waterLevel: 0.95, flowRate: 0, waterQuality: '-',
+    alarmThreshold: '水质>差', updateTime: '2026-06-10 17:22:30',
+    lng: '110.385', lat: '28.449'
+  },
+  {
+    id: 8, name: '开发区雨量计', code: 'YLJ-2025-008', type: '4',
+    monitorPoint: '开发区气象监测站', area: '4', location: '开发区管委会大楼楼顶',
+    status: '1', onlineStatus: '1',
+    waterLevel: 0.55, flowRate: 52, waterQuality: '优',
+    alarmThreshold: '液位>2.0m', updateTime: '2026-06-11 08:31:55',
+    lng: '110.410', lat: '28.458'
+  },
+  {
+    id: 9, name: '城北二号液位计', code: 'YWL-2025-009', type: '1',
+    monitorPoint: '城北排水监测站', area: '1', location: '城北路段排水井A7号',
+    status: '2', onlineStatus: '1',
+    waterLevel: 2.78, flowRate: 295, waterQuality: '良',
+    alarmThreshold: '液位>3.0m', updateTime: '2026-06-11 08:32:40',
+    lng: '110.394', lat: '28.456'
+  },
+  {
+    id: 10, name: '南区流量计', code: 'LXJ-2025-010', type: '2',
+    monitorPoint: '南区排水监测站', area: '3', location: '南环路排水管道D3号',
+    status: '1', onlineStatus: '2',
+    waterLevel: 1.38, flowRate: 0, waterQuality: '-',
+    alarmThreshold: '流量>300m³/h', updateTime: '2026-06-10 19:45:12',
+    lng: '110.398', lat: '28.444'
+  },
+  {
+    id: 11, name: '中心区水质监测仪', code: 'SZJ-2025-011', type: '3',
+    monitorPoint: '中心区水质监测站', area: '5', location: '人民路河道排放口',
+    status: '1', onlineStatus: '1',
+    waterLevel: 1.05, flowRate: 155, waterQuality: '优',
+    alarmThreshold: '水质>差', updateTime: '2026-06-11 08:30:22',
+    lng: '110.395', lat: '28.453'
+  },
+  {
+    id: 12, name: '河西液位监测仪', code: 'YWL-2025-012', type: '1',
+    monitorPoint: '河西排水监测站', area: '2', location: '河西大道排水井E5号',
+    status: '3', onlineStatus: '1',
+    waterLevel: 3.42, flowRate: 468, waterQuality: '差',
+    alarmThreshold: '液位>3.0m', updateTime: '2026-06-11 08:33:18',
+    lng: '110.389', lat: '28.455'
+  }
+])
+
+// ==================== 统计数据 ====================
+const stats = computed(() => {
+  const total = tableData.value.length
+  const online = tableData.value.filter(d => d.onlineStatus === '1').length
+  const offline = tableData.value.filter(d => d.onlineStatus === '2').length
+  const alarm = tableData.value.filter(d => d.status === '3').length
+  const onlineRate = total > 0 ? ((online / total) * 100).toFixed(1) : 0
+  return { total, online, offline, alarm, onlineRate }
+})
+
+// ==================== 搜索参数 ====================
+const showMoreSearch = ref(false)
+const queryParams = ref({
+  name: '',
+  type: '',
+  status: '',
+  area: ''
+})
+
+// ==================== 过滤列表 ====================
+const filteredList = computed(() => {
+  return tableData.value.filter(item => {
+    const nameMatch = !queryParams.value.name || item.name.includes(queryParams.value.name)
+    const typeMatch = !queryParams.value.type || item.type === queryParams.value.type
+    const statusMatch = !queryParams.value.status || item.status === queryParams.value.status
+    const areaMatch = !queryParams.value.area || item.area === queryParams.value.area
+    return nameMatch && typeMatch && statusMatch && areaMatch
+  })
+})
+
+function handleQuery() {
+  // 触发 computed 重新计算
+  queryParams.value = { ...queryParams.value }
+}
+
+function resetQuery() {
+  queryParams.value = { name: '', type: '', status: '', area: '' }
+}
+
+// ==================== 多选 ====================
+const selectedRows = ref([])
+function handleSelectionChange(selection) {
+  selectedRows.value = selection
+}
+
+// ==================== 导出 ====================
+function handleExport() {
+  ElMessage.success('导出成功(模拟)')
+}
+
+// ==================== 查看详情弹窗 ====================
+const detailDialogVisible = ref(false)
+const currentRow = ref({})
+
+function handleView(row) {
+  currentRow.value = { ...row }
+  detailDialogVisible.value = true
+}
+
+// ==================== 定位弹窗 ====================
+const locateDialogVisible = ref(false)
+const locateRow = ref({})
+let locateMapInstance = null
+let locateCurrentLabel = null
+const locateTempLng = ref('')
+const locateTempLat = ref('')
+
+function handleLocate(row) {
+  locateRow.value = { ...row }
+  locateTempLng.value = row.lng || ''
+  locateTempLat.value = row.lat || ''
+  locateDialogVisible.value = true
+}
+
+function onLocateDialogOpened() {
+  setTimeout(() => {
+    initLocateMap()
+  }, 300)
+}
+
+function initLocateMap() {
+  const container = document.getElementById('sbyxLocationMap')
+  if (!container) return
+
+  try {
+    locateMapInstance = new BMapGL.Map('sbyxLocationMap')
+    const lng = parseFloat(locateRow.value.lng)
+    const lat = parseFloat(locateRow.value.lat)
+    const centerPoint = (lng && lat)
+      ? new BMapGL.Point(lng, lat)
+      : new BMapGL.Point(110.393, 28.452)
+    locateMapInstance.centerAndZoom(centerPoint, 15)
+    locateMapInstance.enableScrollWheelZoom(true)
+
+    // 如果有坐标,显示标记
+    if (lng && lat) {
+      const bPoint = new BMapGL.Point(lng, lat)
+      locateCurrentLabel = createMapMarker(locateMapInstance, bPoint, locateRow.value.name)
+    }
+
+    // 点击地图修改定位
+    locateMapInstance.addEventListener('click', function (e) {
+      const clickLng = e.latlng.lng
+      const clickLat = e.latlng.lat
+      locateTempLng.value = clickLng.toFixed(6)
+      locateTempLat.value = clickLat.toFixed(6)
+
+      // 清除旧标记,添加新标记
+      if (locateCurrentLabel) {
+        locateMapInstance.removeOverlay(locateCurrentLabel)
+      }
+      const bPoint = new BMapGL.Point(clickLng, clickLat)
+      locateCurrentLabel = createMapMarker(locateMapInstance, bPoint, locateRow.value.name)
+    })
+  } catch (e) {
+    console.error('百度地图初始化失败:', e)
+  }
+}
+
+function handleLocateClose(done) {
+  if (locateMapInstance) {
+    locateMapInstance = null
+  }
+  locateCurrentLabel = null
+  done()
+}
+
+function handleLocateCancel() {
+  if (locateMapInstance) {
+    locateMapInstance = null
+  }
+  locateCurrentLabel = null
+  locateDialogVisible.value = false
+}
+
+function handleLocateConfirm() {
+  // 将临时坐标保存回设备数据
+  const index = tableData.value.findIndex(item => item.id === locateRow.value.id)
+  if (index !== -1) {
+    tableData.value[index].lng = locateTempLng.value
+    tableData.value[index].lat = locateTempLat.value
+  }
+  if (locateMapInstance) {
+    locateMapInstance = null
+  }
+  locateCurrentLabel = null
+  locateDialogVisible.value = false
+  ElMessage.success('设备定位更新成功')
+}
+
+// ==================== 维修派单弹窗 ====================
+const repairDialogVisible = ref(false)
+const repairFormRef = ref(null)
+const repairForm = ref({
+  name: '',
+  faultDesc: '',
+  repairPerson: '',
+  phone: '',
+  urgency: '',
+  remark: ''
+})
+
+const repairRules = {
+  faultDesc: [{ required: true, message: '请输入故障描述', trigger: 'blur' }],
+  repairPerson: [{ required: true, message: '请输入维修人员', trigger: 'blur' }],
+  phone: [
+    { required: true, message: '请输入联系电话', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  ],
+  urgency: [{ required: true, message: '请选择紧急程度', trigger: 'change' }]
+}
+
+function openRepairDialog(name) {
+  repairForm.value = {
+    name: name,
+    faultDesc: '',
+    repairPerson: '',
+    phone: '',
+    urgency: '',
+    remark: ''
+  }
+  repairDialogVisible.value = true
+}
+
+function handleRepair(row) {
+  openRepairDialog(row.name)
+}
+
+function handleBatchRepair() {
+  const names = selectedRows.value.map(r => r.name).join('、')
+  openRepairDialog(names)
+}
+
+function handleRepairSubmit() {
+  repairFormRef.value.validate(valid => {
+    if (!valid) return
+    ElMessage.success('维修派单成功')
+    repairDialogVisible.value = false
+  })
+}
+
+// ==================== 发布预警弹窗 ====================
+const warningDialogVisible = ref(false)
+const warningFormRef = ref(null)
+const warningForm = ref({
+  name: '',
+  level: '',
+  content: '',
+  scope: ''
+})
+
+const warningRules = {
+  level: [{ required: true, message: '请选择预警级别', trigger: 'change' }],
+  content: [{ required: true, message: '请输入预警内容', trigger: 'blur' }],
+  scope: [{ required: true, message: '请选择发布范围', trigger: 'change' }]
+}
+
+function openWarningDialog(name) {
+  warningForm.value = {
+    name: name,
+    level: '',
+    content: '',
+    scope: ''
+  }
+  warningDialogVisible.value = true
+}
+
+function handleBatchWarning() {
+  const names = selectedRows.value.map(r => r.name).join('、')
+  openWarningDialog(names)
+}
+
+function handleWarningSubmit() {
+  warningFormRef.value.validate(valid => {
+    if (!valid) return
+    ElMessage.success('预警发布成功')
+    warningDialogVisible.value = false
+  })
+}
+
+// ==================== 新增/修改设备弹窗 ====================
+const deviceDialogVisible = ref(false)
+const deviceDialogTitle = ref('新增设备')
+const deviceFormRef = ref(null)
+const deviceFormMode = ref('add') // add | edit
+const deviceForm = ref({
+  name: '',
+  code: '',
+  type: '',
+  monitorPoint: '',
+  location: '',
+  status: '',
+  monitorStatus: '',
+  waterLevel: '',
+  flowRate: '',
+  waterQuality: '',
+  alarmThreshold: '',
+  installTime: '',
+  remark: ''
+})
+
+const deviceFormRules = {
+  name: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
+  code: [{ required: true, message: '请输入设备编号', trigger: 'blur' }],
+  type: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
+  monitorPoint: [{ required: true, message: '请输入所属监测点', trigger: 'blur' }],
+  location: [{ required: true, message: '请输入安装位置', trigger: 'blur' }],
+  status: [{ required: true, message: '请选择设备状态', trigger: 'change' }]
+}
+
+function resetDeviceForm() {
+  deviceForm.value = {
+    name: '',
+    code: '',
+    type: '',
+    monitorPoint: '',
+    location: '',
+    status: '',
+    monitorStatus: '',
+    waterLevel: '',
+    flowRate: '',
+    waterQuality: '',
+    alarmThreshold: '',
+    installTime: '',
+    remark: ''
+  }
+  if (deviceFormRef.value) {
+    deviceFormRef.value.resetFields()
+  }
+}
+
+function handleAdd() {
+  deviceFormMode.value = 'add'
+  deviceDialogTitle.value = '新增设备'
+  resetDeviceForm()
+  deviceDialogVisible.value = true
+}
+
+function handleEdit(row) {
+  deviceFormMode.value = 'edit'
+  deviceDialogTitle.value = '修改设备'
+  deviceForm.value = {
+    name: row.name,
+    code: row.code,
+    type: row.type,
+    monitorPoint: row.monitorPoint,
+    location: row.location,
+    status: row.onlineStatus || '',
+    monitorStatus: row.status || '',
+    waterLevel: row.waterLevel ?? '',
+    flowRate: row.flowRate ?? '',
+    waterQuality: row.waterQuality || '',
+    alarmThreshold: row.alarmThreshold || '',
+    installTime: row.installTime || '',
+    remark: row.remark || ''
+  }
+  deviceForm.value._editId = row.id
+  deviceDialogVisible.value = true
+}
+
+function handleDeviceSubmit() {
+  deviceFormRef.value.validate(valid => {
+    if (!valid) return
+    const form = { ...deviceForm.value }
+    const now = new Date()
+    const pad = n => String(n).padStart(2, '0')
+    const currentTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
+
+    if (deviceFormMode.value === 'add') {
+      const maxId = tableData.value.reduce((max, item) => Math.max(max, item.id), 0)
+      const newItem = {
+        id: maxId + 1,
+        name: form.name,
+        code: form.code,
+        type: form.type,
+        monitorPoint: form.monitorPoint,
+        area: '',
+        location: form.location,
+        status: form.monitorStatus || '1',
+        onlineStatus: form.status || '1',
+        waterLevel: form.waterLevel !== '' ? Number(form.waterLevel) : 0,
+        flowRate: form.flowRate !== '' ? Number(form.flowRate) : 0,
+        waterQuality: form.waterQuality || '-',
+        alarmThreshold: form.alarmThreshold || '',
+        updateTime: currentTime,
+        lng: '',
+        lat: '',
+        installTime: form.installTime || '',
+        remark: form.remark || ''
+      }
+      tableData.value.push(newItem)
+      ElMessage.success('新增设备成功')
+    } else {
+      const index = tableData.value.findIndex(item => item.id === form._editId)
+      if (index !== -1) {
+        tableData.value[index] = {
+          ...tableData.value[index],
+          name: form.name,
+          code: form.code,
+          type: form.type,
+          monitorPoint: form.monitorPoint,
+          location: form.location,
+          status: form.monitorStatus || tableData.value[index].status,
+          onlineStatus: form.status || tableData.value[index].onlineStatus,
+          waterLevel: form.waterLevel !== '' ? Number(form.waterLevel) : tableData.value[index].waterLevel,
+          flowRate: form.flowRate !== '' ? Number(form.flowRate) : tableData.value[index].flowRate,
+          waterQuality: form.waterQuality || tableData.value[index].waterQuality,
+          alarmThreshold: form.alarmThreshold || tableData.value[index].alarmThreshold,
+          installTime: form.installTime || tableData.value[index].installTime,
+          remark: form.remark || tableData.value[index].remark,
+          updateTime: currentTime
+        }
+      }
+      ElMessage.success('修改设备成功')
+    }
+    deviceDialogVisible.value = false
+  })
+}
+
+// ==================== 删除设备 ====================
+function handleDelete(row) {
+  ElMessageBox.confirm(
+    `确认删除设备「${row.name}」吗?`,
+    '提示',
+    { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
+  ).then(() => {
+    const index = tableData.value.findIndex(item => item.id === row.id)
+    if (index !== -1) {
+      tableData.value.splice(index, 1)
+    }
+    ElMessage.success('删除成功')
+  }).catch(() => {})
+}
+
+function handleBatchDelete() {
+  const names = selectedRows.value.map(r => r.name).join('、')
+  ElMessageBox.confirm(
+    `确认删除以下设备吗?${names}`,
+    '提示',
+    { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
+  ).then(() => {
+    const ids = selectedRows.value.map(r => r.id)
+    tableData.value = tableData.value.filter(item => !ids.includes(item.id))
+    ElMessage.success('批量删除成功')
+  }).catch(() => {})
+}
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+/* 统计卡片 */
+.stat-cards {
+  display: flex;
+  gap: 16px;
+  margin-bottom: 20px;
+}
+
+.stat-card {
+  flex: 1;
+  min-width: 0;
+}
+
+.stat-card :deep(.el-card__body) {
+  padding: 16px 20px;
+}
+
+.stat-card-inner {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
+
+.stat-icon {
+  width: 60px;
+  height: 60px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+
+.stat-blue .stat-icon {
+  background: rgba(64, 158, 255, 0.1);
+  color: #409EFF;
+}
+
+.stat-green .stat-icon {
+  background: rgba(103, 194, 58, 0.1);
+  color: #67C23A;
+}
+
+.stat-gray .stat-icon {
+  background: rgba(144, 147, 153, 0.1);
+  color: #909399;
+}
+
+.stat-red .stat-icon {
+  background: rgba(245, 108, 108, 0.1);
+  color: #F56C6C;
+}
+
+.stat-cyan .stat-icon {
+  background: rgba(0, 210, 219, 0.1);
+  color: #00D2DB;
+}
+
+.stat-info {
+  flex: 1;
+  min-width: 0;
+}
+
+.stat-value {
+  font-size: 28px;
+  font-weight: 700;
+  line-height: 1.2;
+}
+
+.stat-blue .stat-value {
+  color: #409EFF;
+}
+
+.stat-green .stat-value {
+  color: #67C23A;
+}
+
+.stat-gray .stat-value {
+  color: #909399;
+}
+
+.stat-red .stat-value {
+  color: #F56C6C;
+}
+
+.stat-cyan .stat-value {
+  color: #00D2DB;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #909399;
+  margin-top: 4px;
+}
+
+/* 搜索表单 */
+.search-form {
+  margin-bottom: 16px;
+}
+
+.expand-icon {
+  transition: transform 0.3s;
+  margin-left: 2px;
+}
+
+.expand-icon.is-rotate {
+  transform: rotate(180deg);
+}
+
+/* 操作按钮 */
+.toolbar {
+  margin-bottom: 16px;
+}
+
+/* 详情弹窗 */
+.detail-info {
+  margin-bottom: 16px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+  line-height: 32px;
+}
+
+.info-label {
+  color: #909399;
+  font-size: 14px;
+  white-space: nowrap;
+}
+
+.info-value {
+  color: #303133;
+  font-size: 14px;
+}
+
+/* 定位弹窗 */
+.locate-info {
+  text-align: center;
+}
+
+.locate-device {
+  text-align: left;
+  margin-bottom: 16px;
+}
+
+.map-container {
+  width: 100%;
+  height: 350px;
+  border-radius: 8px;
+  overflow: hidden;
+  border: 1px solid #dcdfe6;
+}
+
+.locate-coords {
+  margin-top: 12px;
+  font-size: 14px;
+  color: #303133;
+}
+
+.locate-tip {
+  margin-top: 6px;
+  font-size: 12px;
+  color: #909399;
+}
+
+/* 水质指标颜色 */
+.wq-优 {
+  color: #67C23A;
+  font-weight: 600;
+}
+
+.wq-良 {
+  color: #409EFF;
+  font-weight: 600;
+}
+
+.wq-中 {
+  color: #E6A23C;
+  font-weight: 600;
+}
+
+.wq-差 {
+  color: #F56C6C;
+  font-weight: 600;
+}
+</style>