BAI 1 週間 前
コミット
7df97f6f3c

+ 14 - 0
public/Pint.geojson

@@ -0,0 +1,14 @@
+{
+"type": "FeatureCollection",
+"name": "Pint",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
+"features": [
+{ "type": "Feature", "properties": { "id": 1, "X": 80.434131, "Y": 44.360906, "Z": 1312.0, "remark": "上游水文站1" }, "geometry": { "type": "Point", "coordinates": [ 80.43413105484079, 44.360906173657909, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 2, "X": 80.462951, "Y": 44.2926, "Z": 971.0, "remark": "库水位" }, "geometry": { "type": "Point", "coordinates": [ 80.462951159696956, 44.292599559518898, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 3, "X": 80.410842, "Y": 44.446385, "Z": 1906.0, "remark": "上游水文站2" }, "geometry": { "type": "Point", "coordinates": [ 80.410842081219656, 44.446385409182881, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 4, "X": 80.43384, "Y": 44.363196, "Z": 1312.0, "remark": "上游水量" }, "geometry": { "type": "Point", "coordinates": [ 80.433839942670545, 44.36319556572866, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 5, "X": 80.473868, "Y": 44.262586, "Z": 842.0, "remark": "下游水量" }, "geometry": { "type": "Point", "coordinates": [ 80.473867866081903, 44.262586164391742, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 6, "X": 80.509384, "Y": 44.238085, "Z": 794.0, "remark": "下游水位" }, "geometry": { "type": "Point", "coordinates": [ 80.509383550854139, 44.238084709605737, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 8, "X": 80.447522, "Y": 44.240692, "Z": 835.0, "remark": "灌区节点水位" }, "geometry": { "type": "Point", "coordinates": [ 80.447522214672972, 44.240691732511642, 0.0 ] } }
+]
+}

ファイルの差分が大きいため隠しています
+ 5 - 0
src/assets/Geojson/Mian.geojson


+ 27 - 0
src/assets/Geojson/Mian.qmd

@@ -0,0 +1,27 @@
+<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
+<qgis version="3.44.3-Solothurn">
+  <identifier></identifier>
+  <parentidentifier></parentidentifier>
+  <language></language>
+  <type>dataset</type>
+  <title></title>
+  <abstract></abstract>
+  <links/>
+  <dates/>
+  <fees></fees>
+  <encoding></encoding>
+  <crs>
+    <spatialrefsys nativeFormat="Wkt">
+      <wkt>GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],MEMBER["World Geodetic System 1984 (G2296)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]]</wkt>
+      <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+      <srsid>3452</srsid>
+      <srid>4326</srid>
+      <authid>EPSG:4326</authid>
+      <description>WGS 84</description>
+      <projectionacronym>longlat</projectionacronym>
+      <ellipsoidacronym>EPSG:7030</ellipsoidacronym>
+      <geographicflag>true</geographicflag>
+    </spatialrefsys>
+  </crs>
+  <extent/>
+</qgis>

+ 14 - 0
src/assets/Geojson/Pint.geojson

@@ -0,0 +1,14 @@
+{
+"type": "FeatureCollection",
+"name": "Pint",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
+"features": [
+{ "type": "Feature", "properties": { "id": 1, "X": 80.434131, "Y": 44.360906, "Z": 1312.0, "remark": "上游水文站1" }, "geometry": { "type": "Point", "coordinates": [ 80.43413105484079, 44.360906173657909, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 2, "X": 80.462951, "Y": 44.2926, "Z": 971.0, "remark": "库水位" }, "geometry": { "type": "Point", "coordinates": [ 80.462951159696956, 44.292599559518898, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 3, "X": 80.410842, "Y": 44.446385, "Z": 1906.0, "remark": "上游水文站2" }, "geometry": { "type": "Point", "coordinates": [ 80.410842081219656, 44.446385409182881, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 4, "X": 80.43384, "Y": 44.363196, "Z": 1312.0, "remark": "上游水量" }, "geometry": { "type": "Point", "coordinates": [ 80.433839942670545, 44.36319556572866, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 5, "X": 80.473868, "Y": 44.262586, "Z": 842.0, "remark": "下游水量" }, "geometry": { "type": "Point", "coordinates": [ 80.473867866081903, 44.262586164391742, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 6, "X": 80.509384, "Y": 44.238085, "Z": 794.0, "remark": "下游水位" }, "geometry": { "type": "Point", "coordinates": [ 80.509383550854139, 44.238084709605737, 0.0 ] } },
+{ "type": "Feature", "properties": { "id": 8, "X": 80.447522, "Y": 44.240692, "Z": 835.0, "remark": "灌区节点水位" }, "geometry": { "type": "Point", "coordinates": [ 80.447522214672972, 44.240691732511642, 0.0 ] } }
+]
+}

+ 44 - 0
src/assets/Geojson/Pint.qmd

@@ -0,0 +1,44 @@
+<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
+<qgis version="3.44.3-Solothurn">
+  <identifier></identifier>
+  <parentidentifier></parentidentifier>
+  <language></language>
+  <type>dataset</type>
+  <title></title>
+  <abstract></abstract>
+  <contact>
+    <name></name>
+    <organization></organization>
+    <position></position>
+    <voice></voice>
+    <fax></fax>
+    <email></email>
+    <role></role>
+  </contact>
+  <links/>
+  <dates/>
+  <fees></fees>
+  <encoding></encoding>
+  <crs>
+    <spatialrefsys nativeFormat="Wkt">
+      <wkt>GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],MEMBER["World Geodetic System 1984 (G2296)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]]</wkt>
+      <proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
+      <srsid>3452</srsid>
+      <srid>4326</srid>
+      <authid>EPSG:4326</authid>
+      <description>WGS 84</description>
+      <projectionacronym>longlat</projectionacronym>
+      <ellipsoidacronym>EPSG:7030</ellipsoidacronym>
+      <geographicflag>true</geographicflag>
+    </spatialrefsys>
+  </crs>
+  <extent>
+    <spatial dimensions="2" crs="EPSG:4326" minx="179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368" maxy="-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368" minz="0" miny="179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368" maxx="-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368" maxz="0"/>
+    <temporal>
+      <period>
+        <start></start>
+        <end></end>
+      </period>
+    </temporal>
+  </extent>
+</qgis>

+ 126 - 0
src/components/CesiumMap.vue

@@ -9,10 +9,12 @@ import * as Cesium from 'cesium'
 import 'cesium/Build/CesiumUnminified/Widgets/widgets.css'
 import autofit from 'autofit.js'
 import modelUrl from '@/assets/Mesh/乌拉海沟水库.glb'
+import poiIcon from '@/assets/images/Heilin/POI.png'
 
 const emit = defineEmits(['toggleMap', 'loadEmbankmentWarningData'])
 
 const viewer = ref(null)
+let poiDataSource = null
 
 const props = defineProps({
   simulationTime: {
@@ -26,6 +28,48 @@ const props = defineProps({
   loadEmbankmentWarning: {
     type: Boolean,
     default: false
+  },
+  poiVisible: {
+    type: Boolean,
+    default: false
+  },
+  cameraTarget: {
+    type: String,
+    default: 'default'
+  }
+})
+
+const flyToHydrology = () => {
+  if (!viewer.value) return
+  viewer.value.camera.flyTo({
+    destination: Cesium.Cartesian3.fromDegrees(80.404548, 44.129512, 7865.0),
+    orientation: {
+      heading: Cesium.Math.toRadians(13.78),
+      pitch: Cesium.Math.toRadians(-16.16),
+      roll: Cesium.Math.toRadians(359.99)
+    },
+    duration: 1.5
+  })
+}
+
+const flyToDefault = () => {
+  if (!viewer.value) return
+  viewer.value.camera.flyTo({
+    destination: Cesium.Cartesian3.fromDegrees(80.465506, 44.275181, 1049.7),
+    orientation: {
+      heading: Cesium.Math.toRadians(338.78),
+      pitch: Cesium.Math.toRadians(-10.52),
+      roll: Cesium.Math.toRadians(360.00)
+    },
+    duration: 1.5
+  })
+}
+
+watch(() => props.cameraTarget, (target) => {
+  if (target === 'hydrology') {
+    flyToHydrology()
+  } else {
+    flyToDefault()
   }
 })
 
@@ -135,6 +179,88 @@ const loadEmbankmentWarningData = async () => {
   }
 }
 
+// ---- 水情测报 POI 标注 ----
+watch(() => props.poiVisible, async (newVal) => {
+  if (newVal) {
+    await loadPoiData()
+  } else {
+    removePoiData()
+  }
+})
+
+const loadPoiData = async () => {
+  if (!viewer.value) return
+  removePoiData()
+
+  try {
+    const response = await fetch('/Pint.geojson')
+    const geojson = await response.json()
+
+    poiDataSource = new Cesium.CustomDataSource('水情测报POI')
+    viewer.value.dataSources.add(poiDataSource)
+
+    const dummyWaterData = {
+      '上游水文站1': {水位: '15.2m', 流量: '18.5m³/s'},
+      '上游水文站2': {水位: '12.8m', 流量: '14.2m³/s'},
+      '库水位': {水位: '1048.52m', 库容: '2350.8万m³'},
+      '上游水量': {水位: '8.6m³/s', 流量: '16.2m³/s'},
+      '下游水量': {水位: '14.8m³/s', 流量: '14.8m³/s'},
+      '下游水位': {水位: '6.5m', 流量: '14.5m³/s'},
+      '灌区节点水位': {水位: '3.6m', 流量: '12.0m³/s'}
+    }
+
+    for (const feature of geojson.features) {
+      const coords = feature.geometry.coordinates
+      const remark = feature.properties.remark || '监测点'
+      const waterInfo = dummyWaterData[remark] || {水位: '—', 流量: '—'}
+
+      const heightZ = feature.properties.Z || coords[2] || 0
+
+      const entity = poiDataSource.entities.add({
+        position: Cesium.Cartesian3.fromDegrees(coords[0], coords[1], heightZ),
+        billboard: {
+          image: poiImage,
+          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+          scale: 0.4,
+          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
+        },
+        label: {
+          text: remark + '\n' + waterInfo.水位,
+          font: '12px sans-serif',
+          fillColor: Cesium.Color.WHITE,
+          style: Cesium.LabelStyle.FILL,
+          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+          pixelOffset: new Cesium.Cartesian2(0, -50),
+          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+          backgroundColor: new Cesium.Color(0.07, 0.34, 0.62, 0.85),
+          backgroundPadding: new Cesium.Cartesian2(6, 4),
+          showBackground: true
+        },
+        description: remark + ' 水位: ' + waterInfo.水位 + ' 流量: ' + waterInfo.流量,
+        properties: {
+          remark: remark,
+          waterLevel: waterInfo.水位
+        }
+      })
+    }
+
+    console.log('水情测报POI数据加载成功, 共', geojson.features.length, '个点')
+  } catch (error) {
+    console.error('加载水情测报POI数据失败:', error)
+  }
+}
+
+const removePoiData = () => {
+  if (viewer.value && poiDataSource) {
+    viewer.value.dataSources.remove(poiDataSource)
+    poiDataSource = null
+  }
+}
+
+// 预加载 POI 图片为 DOM Image 对象,避免打包后 Cesium billboard 加载失败
+const poiImage = new Image()
+poiImage.src = poiIcon
+
 const createPoiImage = () => {
   const canvas = document.createElement('canvas')
   canvas.width = 40

+ 1 - 1
src/components/TopNav.vue

@@ -155,7 +155,7 @@ export default {
   top: 2px;
   left: 50%;
   transform: translateX(-50%);
-  font-size: 48px;
+  font-size: 44px;
   font-weight: 900;
   background: linear-gradient(to top, #00d5ff 0%, #ffffff 50%);
   -webkit-background-clip: text;

+ 10 - 1
src/views/HomeView.vue

@@ -13,6 +13,8 @@
         :simulationTime="simulationTime"
         :simulationData="simulationData"
         :loadEmbankmentWarning="loadEmbankmentWarning"
+        :poiVisible="activeTab === '水文四预'"
+        :cameraTarget="cameraTarget"
       />
     </div>
     
@@ -120,7 +122,8 @@ export default {
           waterLevel: '18.5',
           storage: '2350.8'
         }
-      }
+      },
+      cameraTarget: 'default'
     }
   },
   created() {
@@ -152,6 +155,12 @@ export default {
       if (tab !== '水文四预') {
         this.loadEmbankmentWarning = false
       }
+      // 镜头切换
+      if (tab === '水文四预') {
+        this.cameraTarget = 'hydrology'
+      } else {
+        this.cameraTarget = 'default'
+      }
       // 工作台和水资源调度页面隐藏地图(水资源调度有独立地图),其他页面显示地图
       if (tab === '工作台' || tab === '水资源调度') {
         this.showMap = false

+ 62 - 24
src/views/admin/DeviceMaintainView.vue

@@ -79,7 +79,7 @@
       </el-tab-pane>
 
       <!-- 设备详情弹窗 -->
-      <el-dialog v-model="showDeviceDetail" :title="'设备详情 - ' + (selectedDevice?.name || '')" width="960px" top="5vh" @closed="handleCloseDetail">
+      <el-dialog v-model="showDeviceDetail" :title="'设备详情 - ' + (selectedDevice?.name || '')" width="1100px" top="5vh" class="device-detail-dialog" @closed="handleCloseDetail">
         <div class="device-detail-body" v-if="selectedDevice">
           <div class="detail-top-row">
             <div class="detail-info-section">
@@ -93,6 +93,10 @@
                   <span class="info-label">设备名称</span>
                   <span class="info-value">{{ selectedDevice.name }}</span>
                 </div>
+                <div class="info-item">
+                  <span class="info-label">设备品牌</span>
+                  <span class="info-value">{{ selectedDevice.brand }}</span>
+                </div>
                 <div class="info-item">
                   <span class="info-label">设备分类</span>
                   <span class="info-value">{{ selectedDevice.category }}</span>
@@ -109,6 +113,10 @@
                   <span class="info-label">安装日期</span>
                   <span class="info-value">{{ selectedDevice.installDate }}</span>
                 </div>
+                <div class="info-item">
+                  <span class="info-label">安装人员</span>
+                  <span class="info-value">{{ selectedDevice.installer }}</span>
+                </div>
                 <div class="info-item">
                   <span class="info-label">运行状态</span>
                   <span class="info-value">
@@ -117,6 +125,10 @@
                     </el-tag>
                   </span>
                 </div>
+                <div class="info-item">
+                  <span class="info-label">维护人员</span>
+                  <span class="info-value">{{ selectedDevice.maintainer }}</span>
+                </div>
                 <div class="info-item">
                   <span class="info-label">上次维保</span>
                   <span class="info-value">{{ selectedDevice.lastMaintain }}</span>
@@ -161,7 +173,7 @@
                   <span class="ps-value" style="color:#10b981">{{ avgChartValue }}</span>
                 </div>
               </div>
-              <div ref="deviceChartRef" style="width:100%;height:300px"></div>
+              <div ref="deviceChartRef" style="width:100%;height:280px"></div>
             </div>
           </div>
         </div>
@@ -380,6 +392,7 @@
 <script>
 import { Search, Plus, Download, Minus } from '@element-plus/icons-vue'
 import * as echarts from 'echarts'
+import deviceImage from '@/assets/images/Heilin/image.png'
 
 export default {
   name: 'DeviceMaintainView',
@@ -418,14 +431,14 @@ export default {
       archivePage: 1,
       archiveTotal: 56,
       deviceArchive: [
-        { code: 'WL-001', name: '主坝水位计', category: '水位监测', model: 'WL-2000', location: '大坝坝顶', installDate: '2022-03-15', status: 'online', statusText: '在线', lastMaintain: '2026-04-10', nextMaintain: '2026-07-10' },
-        { code: 'WL-002', name: '副坝水位计', category: '水位监测', model: 'WL-2000', location: '副坝坝顶', installDate: '2022-03-15', status: 'online', statusText: '在线', lastMaintain: '2026-04-10', nextMaintain: '2026-07-10' },
-        { code: 'SP-001', name: '副坝渗压计', category: '渗流监测', model: 'SP-500', location: '副坝坝体', installDate: '2022-05-20', status: 'warning', statusText: '异常', lastMaintain: '2026-03-15', nextMaintain: '2026-06-15' },
-        { code: 'GT-001', name: '溢洪道闸门', category: '闸门设备', model: 'ZM-3000', location: '溢洪道', installDate: '2021-11-08', status: 'online', statusText: '运行', lastMaintain: '2026-05-01', nextMaintain: '2026-08-01' },
-        { code: 'FL-001', name: '入库流量计', category: '水位监测', model: 'FL-1500', location: '进水口', installDate: '2022-06-10', status: 'offline', statusText: '离线', lastMaintain: '2026-02-20', nextMaintain: '2026-05-20' },
-        { code: 'VD-001', name: '大坝监控摄像头', category: '视频监控', model: 'HK-4K', location: '大坝坝顶', installDate: '2023-01-15', status: 'online', statusText: '在线', lastMaintain: '2026-05-10', nextMaintain: '2026-08-10' },
-        { code: 'DM-001', name: '坝顶位移计', category: '变形监测', model: 'DM-100', location: '大坝坝顶', installDate: '2022-04-20', status: 'online', statusText: '在线', lastMaintain: '2026-04-25', nextMaintain: '2026-07-25' },
-        { code: 'WT-001', name: '自动气象站', category: '气象监测', model: 'AWS-300', location: '管理区', installDate: '2022-08-01', status: 'maintenance', statusText: '维护中', lastMaintain: '2026-06-01', nextMaintain: '2026-09-01' }
+        { code: 'WL-001', name: '主坝水位计', category: '水位监测', model: 'WL-2000', brand: '华为', location: '大坝坝顶', installDate: '2022-03-15', installer: '张三', maintainer: '李四', status: 'online', statusText: '在线', lastMaintain: '2026-04-10', nextMaintain: '2026-07-10' },
+        { code: 'WL-002', name: '副坝水位计', category: '水位监测', model: 'WL-2000', brand: '华为', location: '副坝坝顶', installDate: '2022-03-15', installer: '张三', maintainer: '王五', status: 'online', statusText: '在线', lastMaintain: '2026-04-10', nextMaintain: '2026-07-10' },
+        { code: 'SP-001', name: '副坝渗压计', category: '渗流监测', model: 'SP-500', brand: '中兴', location: '副坝坝体', installDate: '2022-05-20', installer: '李四', maintainer: '赵六', status: 'warning', statusText: '异常', lastMaintain: '2026-03-15', nextMaintain: '2026-06-15' },
+        { code: 'GT-001', name: '溢洪道闸门', category: '闸门设备', model: 'ZM-3000', brand: '中信重工', location: '溢洪道', installDate: '2021-11-08', installer: '王五', maintainer: '张三', status: 'online', statusText: '运行', lastMaintain: '2026-05-01', nextMaintain: '2026-08-01' },
+        { code: 'FL-001', name: '入库流量计', category: '水位监测', model: 'FL-1500', brand: '西门子', location: '进水口', installDate: '2022-06-10', installer: '赵六', maintainer: '李四', status: 'offline', statusText: '离线', lastMaintain: '2026-02-20', nextMaintain: '2026-05-20' },
+        { code: 'VD-001', name: '大坝监控摄像头', category: '视频监控', model: 'HK-4K', brand: '海康威视', location: '大坝坝顶', installDate: '2023-01-15', installer: '张三', maintainer: '王五', status: 'online', statusText: '在线', lastMaintain: '2026-05-10', nextMaintain: '2026-08-10' },
+        { code: 'DM-001', name: '坝顶位移计', category: '变形监测', model: 'DM-100', brand: '基康', location: '大坝坝顶', installDate: '2022-04-20', installer: '李四', maintainer: '赵六', status: 'online', statusText: '在线', lastMaintain: '2026-04-25', nextMaintain: '2026-07-25' },
+        { code: 'WT-001', name: '自动气象站', category: '气象监测', model: 'AWS-300', brand: '华云', location: '管理区', installDate: '2022-08-01', installer: '王五', maintainer: '张三', status: 'maintenance', statusText: '维护中', lastMaintain: '2026-06-01', nextMaintain: '2026-09-01' }
       ],
       // 维保记录
       maintainDateRange: null,
@@ -525,7 +538,7 @@ export default {
       return params[dev.code] || '—'
     },
     getDeviceImage(dev) {
-      return '/src/assets/images/Heilin/image.png'
+      return deviceImage
     },
     initDeviceChart() {
       if (!echarts) return
@@ -561,14 +574,39 @@ export default {
       this.avgChartValue = (data.reduce((a, b) => a + b, 0) / data.length).toFixed(2)
       const labelInterval = Math.max(1, Math.floor(dataPoints / 12))
       const unitMap = { temperature: '℃', stress: 'MPa', displacement: 'mm', voltage: 'V' }
+      const unitStr = unitMap[this.activeChartTab]
       this.chartInstance.setOption({
-        animation: false, tooltip: { trigger: 'axis' },
+        animation: true,
+        tooltip: {
+          trigger: 'axis',
+          triggerOn: 'mousemove',
+          backgroundColor: 'rgba(255,255,255,0.95)',
+          borderColor: '#e2e8f0',
+          borderWidth: 1,
+          textStyle: { color: '#1e293b', fontSize: 12 },
+          formatter: function(params) {
+            const p = params[0]
+            if (!p) return ''
+            return '<div style="font-weight:600;margin-bottom:4px">' + p.axisValue + '</div>'
+              + '<div style="display:flex;align-items:center;gap:6px">'
+              + '<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + p.color + '"></span>'
+              + '<span style="font-weight:700">' + p.value + '</span>'
+              + '<span style="color:#94a3b8;margin-left:2px">' + unitStr + '</span>'
+              + '</div>'
+          }
+        },
         grid: { left: 50, right: 20, top: 20, bottom: 30 },
-        xAxis: { type: 'category', boundaryGap: false, data: timeList, axisLine: { lineStyle: { color: '#e5e7eb' } }, axisLabel: { color: '#94a3b8', fontSize: 10, interval: labelInterval } },
-        yAxis: { type: 'value', name: unitMap[this.activeChartTab], nameTextStyle: { color: '#94a3b8', fontSize: 11 }, splitLine: { lineStyle: { color: 'rgba(0,0,0,0.04)' } }, axisLabel: { color: '#94a3b8', fontSize: 10 } },
+        xAxis: { type: 'category', boundaryGap: false, data: timeList,
+          axisLine: { lineStyle: { color: '#e5e7eb' } },
+          axisLabel: { color: '#94a3b8', fontSize: 10, interval: labelInterval },
+          axisPointer: { show: true, type: 'shadow', label: { show: true, formatter: function(p) { return p.value } } }
+        },
+        yAxis: { type: 'value', name: unitStr, nameTextStyle: { color: '#94a3b8', fontSize: 11 }, splitLine: { lineStyle: { color: 'rgba(0,0,0,0.04)' } }, axisLabel: { color: '#94a3b8', fontSize: 10 } },
         dataZoom: [{ type: 'inside', start: 0, end: 100 }],
         series: [{
-          type: 'line', smooth: true, symbol: 'none', data: data,
+          type: 'line', smooth: true, symbol: 'circle', symbolSize: 6, showSymbol: false,
+          emphasis: { focus: 'series', lineStyle: { width: 3 }, itemStyle: { borderWidth: 3, borderColor: '#fff' } },
+          data: data,
           lineStyle: { width: 2, color: this.chartColors[this.activeChartTab] },
           itemStyle: { color: this.chartColors[this.activeChartTab] },
           areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: this.chartColors[this.activeChartTab] + '33' }, { offset: 1, color: this.chartColors[this.activeChartTab] + '05' }]) }
@@ -740,26 +778,28 @@ export default {
 }
 
 /* ==================== 设备详情弹窗 ==================== */
+.device-detail-dialog { padding: 15px; margin-top: 15px !important; margin-bottom: 15px !important; }
+.device-detail-dialog .el-dialog__body { padding: 16px 20px; }
 .device-detail-body {
-  display: flex; flex-direction: column; gap: 20px;
+  display: flex; flex-direction: column; gap: 16px;
 }
-.detail-top-row { display: flex; gap: 20px; }
-.detail-info-section { flex: 1; }
+.detail-top-row { display: flex; gap: 24px; }
+.detail-info-section { flex: 1; min-width: 0; }
 .detail-info-header {
   font-size: 15px; font-weight: 600; color: #1e293b;
   margin-bottom: 12px; padding-bottom: 8px; border-bottom: 2px solid #3b82f6;
 }
 .detail-info-grid {
-  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px;
+  display: grid; grid-template-columns: 1fr 1fr; gap: 12px 28px;
 }
 .info-item { display: flex; flex-direction: column; gap: 2px; }
 .info-label { font-size: 11px; color: #94a3b8; }
 .info-value { font-size: 14px; font-weight: 500; color: #1e293b; }
 .detail-image-section {
-  width: 220px; flex-shrink: 0; display: flex; flex-direction: column; gap: 10px;
+  width: 260px; flex-shrink: 0; display: flex; flex-direction: column; gap: 10px;
 }
 .detail-image-frame {
-  width: 220px; height: 150px; border-radius: 10px; overflow: hidden;
+  width: 100%; height: 170px; border-radius: 10px; overflow: hidden;
   border: 1px solid #e2e8f0; background: #f8fafc;
   display: flex; align-items: center; justify-content: center;
 }
@@ -778,7 +818,7 @@ export default {
 }
 .chart-tab:hover { background: #e2e8f0; }
 .chart-tab.active { background: #3b82f6; color: #fff; }
-.param-summary-row { display: flex; gap: 12px; margin-bottom: 10px; }
+.param-summary-row { display: flex; gap: 12px; margin-bottom: 10px; flex-shrink: 0; }
 .param-summary-item {
   flex: 1; background: #fff; border-radius: 8px; padding: 8px 10px;
   display: flex; flex-direction: column; align-items: center; gap: 2px;
@@ -786,8 +826,6 @@ export default {
 }
 .ps-label { font-size: 11px; color: #94a3b8; }
 .ps-value { font-size: 18px; font-weight: 700; }
-.detail-chart-body { flex: 1; }
-
 /* ==================== 分页 ==================== */
 .pagination-wrap {
   display: flex;

+ 2 - 1
src/views/mainPages/EngineeringSafetyView.vue

@@ -650,7 +650,7 @@ export default {
 /* ========== Data Card ========== */
 .data-card { width: 100%; background: rgba(0,20,40,0.7); border-radius: 4px; overflow: hidden; box-shadow: 0 0 10px rgba(0,212,255,0.2); display: flex; flex-direction: column; }
 .left-sidebar .data-card { flex: unset; }
-.left-sidebar .data-card:first-child .card-body { max-height: 150px; overflow-y: auto; }
+.left-sidebar .data-card:first-child .card-body { max-height: 155px; overflow-y: auto; }
 .card-header { height: 38px; background-image: url("/src/assets/images/数据小标题.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; display: flex; align-items: flex-start; justify-content: space-between; padding: 4px 12px 0; }
 .card-title { font-size: 13px; font-weight: bold; color: #e0fcff; margin: 0; text-shadow: 0 0 5px rgba(0,212,255,0.5); padding-left: 25px; padding-right: 25px; }
 .card-body { padding: 8px; min-height: auto; font-size: 12px; line-height: 1.4; margin-top: -4px; }
@@ -659,6 +659,7 @@ export default {
 
 /* ========== Safety Evaluation ========== */
 .safety-eval-section { background: rgba(0,20,40,0.5); border-radius: 4px; border: 1px solid rgba(0,212,255,0.12); padding: 6px 10px; display: flex; flex-direction: column; gap: 5px; }
+.left-sidebar .data-card:first-child .safety-eval-section { padding-top: 2px; padding-bottom: 2px; }
 .safety-eval-top { display: flex; justify-content: space-between; align-items: center; height: 26px; padding: 0 10px; border-radius: 3px; background: #22c55e; }
 .safety-eval-section-title { color: #ffffff; font-size: 13px; }
 .safety-eval-grade { color: #ffffff; font-size: 14px; font-weight: bold; }

+ 16 - 6
src/views/mainPages/LifecycleView.vue

@@ -109,7 +109,7 @@
             <div class="benefits-float">
               <!-- 农业灌溉效益 -->
               <div class="benefit-item float-item" style="top: 5%; left: 8%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆1.png'); width: 220px; height: 180px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse1 + ')', width: '220px', height: '180px' }">
                   <div class="benefit-title">农业灌溉</div>
                   <div class="benefit-value">3.82万亩</div>
                   <div class="benefit-unit">灌溉面积</div>
@@ -117,7 +117,7 @@
               </div>
               <!-- 城乡饮水效益 -->
               <div class="benefit-item float-item" style="top: 8%; left: 55%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆2.png'); width: 260px; height: 200px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse2 + ')', width: '260px', height: '200px' }">
                   <div class="benefit-title">城乡饮水</div>
                   <div class="benefit-value">1.26万人</div>
                   <div class="benefit-unit">水质达标100%</div>
@@ -125,7 +125,7 @@
               </div>
               <!-- 工业供水效益 -->
               <div class="benefit-item float-item" style="top: 35%; left: 20%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆3.png'); width: 240px; height: 190px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse3 + ')', width: '240px', height: '190px' }">
                   <div class="benefit-title">工业供水</div>
                   <div class="benefit-value">360万元</div>
                   <div class="benefit-unit">年新增产值</div>
@@ -133,7 +133,7 @@
               </div>
               <!-- 防洪减灾效益 -->
               <div class="benefit-item float-item" style="top: 40%; left: 58%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆4.png'); width: 280px; height: 220px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse4 + ')', width: '280px', height: '220px' }">
                   <div class="benefit-title">防洪减灾</div>
                   <div class="benefit-value">96.6亿m³</div>
                   <div class="benefit-unit">拦蓄洪量</div>
@@ -141,7 +141,7 @@
               </div>
               <!-- 生态环境效益 -->
               <div class="benefit-item float-item" style="top: 8%; left: 75%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆1.png'); width: 200px; height: 160px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse1 + ')', width: '200px', height: '160px' }">
                   <div class="benefit-title">生态环境</div>
                   <div class="benefit-value">58万m³</div>
                   <div class="benefit-unit">生态补水</div>
@@ -149,7 +149,7 @@
               </div>
               <!-- 社会经济效益 -->
               <div class="benefit-item float-item" style="top: 68%; left: 45%;">
-                <div class="benefit-ellipse" style="background-image: url('/src/assets/images/椭圆2.png'); width: 230px; height: 185px;">
+                <div class="benefit-ellipse" :style="{ backgroundImage: 'url(' + ellipse2 + ')', width: '230px', height: '185px' }">
                   <div class="benefit-title">社会经济</div>
                   <div class="benefit-value">1015元</div>
                   <div class="benefit-unit">人均年增收</div>
@@ -270,7 +270,17 @@
 <script setup>
 import { ref, computed } from 'vue'
 import reservoirImage from '@/assets/images/乌拉海沟水库.png'
+import ellipse1 from '@/assets/images/椭圆1.png'
+import ellipse2 from '@/assets/images/椭圆2.png'
+import ellipse3 from '@/assets/images/椭圆3.png'
+import ellipse4 from '@/assets/images/椭圆4.png'
 
+const ellipseImgMap = {
+  e1: ellipse1,
+  e2: ellipse2,
+  e3: ellipse3,
+  e4: ellipse4
+}
 const activeMilestoneTab = ref('construction')
 const activeProcessTab = ref('调度运用')
 const activeDispatchCategory = ref('供水调度')

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません