Browse Source

添加首页和水功能区地图功能

MaXueLi 1 tuần trước cách đây
mục cha
commit
ce35c4ce10
28 tập tin đã thay đổi với 866 bổ sung11 xóa
  1. 1 1
      ruoyi-ui/package.json
  2. BIN
      ruoyi-ui/src/assets/yujing/img/close.png
  3. BIN
      ruoyi-ui/src/assets/yujing/img/left-box.png
  4. BIN
      ruoyi-ui/src/assets/yujing/map/area-active.png
  5. BIN
      ruoyi-ui/src/assets/yujing/map/area-deactive.png
  6. BIN
      ruoyi-ui/src/assets/yujing/map/blue-circle.png
  7. BIN
      ruoyi-ui/src/assets/yujing/map/measure-active.png
  8. BIN
      ruoyi-ui/src/assets/yujing/map/measure-deactive.png
  9. BIN
      ruoyi-ui/src/assets/yujing/map/square-black.png
  10. BIN
      ruoyi-ui/src/assets/yujing/map/square-brown.png
  11. BIN
      ruoyi-ui/src/assets/yujing/map/square-green.png
  12. BIN
      ruoyi-ui/src/assets/yujing/map/square-pink.png
  13. BIN
      ruoyi-ui/src/assets/yujing/map/square-yellow.png
  14. BIN
      ruoyi-ui/src/assets/yujing/map/Ⅰ.png
  15. BIN
      ruoyi-ui/src/assets/yujing/map/Ⅱ.png
  16. BIN
      ruoyi-ui/src/assets/yujing/map/Ⅲ.png
  17. BIN
      ruoyi-ui/src/assets/yujing/map/Ⅳ.png
  18. BIN
      ruoyi-ui/src/assets/yujing/map/Ⅴ.png
  19. BIN
      ruoyi-ui/src/assets/yujing/map/劣Ⅴ.png
  20. BIN
      ruoyi-ui/src/assets/yujing/map/取消.png
  21. BIN
      ruoyi-ui/src/assets/yujing/map/维修.png
  22. BIN
      ruoyi-ui/src/assets/yujing/map/视频监控.png
  23. 2 0
      ruoyi-ui/src/main.js
  24. 4 1
      ruoyi-ui/src/views/YuJing/index.vue
  25. 145 0
      ruoyi-ui/src/views/YuJing/shouye/components/StnmDialog.vue
  26. 458 6
      ruoyi-ui/src/views/YuJing/shouye/index.vue
  27. 255 2
      ruoyi-ui/src/views/YuJing/water/monitorAlert.vue
  28. 1 1
      ruoyi-ui/src/views/index.vue

+ 1 - 1
ruoyi-ui/package.json

@@ -21,7 +21,7 @@
     "@vueuse/core": "13.3.0",
     "axios": "1.9.0",
     "clipboard": "2.0.11",
-    "echarts": "5.6.0",
+    "echarts": "^5.6.0",
     "element-plus": "2.9.9",
     "file-saver": "2.0.5",
     "fuse.js": "6.6.2",

BIN
ruoyi-ui/src/assets/yujing/img/close.png


BIN
ruoyi-ui/src/assets/yujing/img/left-box.png


BIN
ruoyi-ui/src/assets/yujing/map/area-active.png


BIN
ruoyi-ui/src/assets/yujing/map/area-deactive.png


BIN
ruoyi-ui/src/assets/yujing/map/blue-circle.png


BIN
ruoyi-ui/src/assets/yujing/map/measure-active.png


BIN
ruoyi-ui/src/assets/yujing/map/measure-deactive.png


BIN
ruoyi-ui/src/assets/yujing/map/square-black.png


BIN
ruoyi-ui/src/assets/yujing/map/square-brown.png


BIN
ruoyi-ui/src/assets/yujing/map/square-green.png


BIN
ruoyi-ui/src/assets/yujing/map/square-pink.png


BIN
ruoyi-ui/src/assets/yujing/map/square-yellow.png


BIN
ruoyi-ui/src/assets/yujing/map/Ⅰ.png


BIN
ruoyi-ui/src/assets/yujing/map/Ⅱ.png


BIN
ruoyi-ui/src/assets/yujing/map/Ⅲ.png


BIN
ruoyi-ui/src/assets/yujing/map/Ⅳ.png


BIN
ruoyi-ui/src/assets/yujing/map/Ⅴ.png


BIN
ruoyi-ui/src/assets/yujing/map/劣Ⅴ.png


BIN
ruoyi-ui/src/assets/yujing/map/取消.png


BIN
ruoyi-ui/src/assets/yujing/map/维修.png


BIN
ruoyi-ui/src/assets/yujing/map/视频监控.png


+ 2 - 0
ruoyi-ui/src/main.js

@@ -6,6 +6,7 @@ import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 import 'element-plus/theme-chalk/dark/css-vars.css'
 import locale from 'element-plus/es/locale/lang/zh-cn'
+import * as echarts from 'echarts';
 
 import '@/assets/styles/index.scss' // global css
 
@@ -58,6 +59,7 @@ app.config.globalProperties.addDateRange = addDateRange
 app.config.globalProperties.getConfigKey = getConfigKey
 app.config.globalProperties.selectDictLabel = selectDictLabel
 app.config.globalProperties.selectDictLabels = selectDictLabels
+app.config.globalProperties.echarts = echarts;
 
 // 全局组件挂载
 app.component('DictTag', DictTag)

+ 4 - 1
ruoyi-ui/src/views/YuJing/index.vue

@@ -19,6 +19,9 @@
           </div>
           <template #dropdown>
             <el-dropdown-menu>
+              <router-link to="/index">
+                <el-dropdown-item>返回首页</el-dropdown-item>
+              </router-link>
               <router-link to="/user/profile">
                 <el-dropdown-item>个人中心</el-dropdown-item>
               </router-link>
@@ -284,6 +287,7 @@ function setLayout() {
     .contentLeft {
       width: 10%;
       height: 100%;
+      margin-right: 10px;
       transition: all 0.3s ease-in-out; /* 添加过渡效果 */
       overflow: hidden; /* 防止内容溢出 */
       .leftTitle {
@@ -326,7 +330,6 @@ function setLayout() {
       flex: 1;
       transition: margin-left 0.3s;
       height: 100%;
-      margin-left: 10px;
       &.expanded {
         margin-left: 73px; /* 63px + 10px */
       }

+ 145 - 0
ruoyi-ui/src/views/YuJing/shouye/components/StnmDialog.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="stnm-diglog" v-drag v-if="props.showStnmDialog">
+    <div class="stnm-dialog-title">
+      <span>{{dialogTitle}}</span>
+      <img src="@/assets/yujing/img/close.png" @click="closeDialog"/>
+    </div>
+    <el-form ref="stnmDialogFormRef" :model="dialogForm" :inline="true" style="margin-left: 10px;">
+      <el-form-item label="时间范围:" 
+        v-if="dialogType == 'stnm'"
+      >
+        <el-date-picker
+          v-model="dialogForm.daterange"
+          value-format="YYYY-MM-DD"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="监测日期:" 
+        v-if="dialogType == 'river'"
+      >
+        <el-date-picker
+          v-model="dialogForm.tm"
+          value-format="YYYY-MM-DD"
+          type="date"
+          placeholder="请输入监测日期"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="监测指标:" 
+        v-if="dialogType == 'river'"
+      >
+        <el-select-v2
+          v-model="dialogForm.region"
+          placeholder="请选择监测指标"
+          :options="regionOptions"
+          style="width: 150px;"
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="编码:" 
+        v-if="dialogType == 'river'"
+      >
+      <el-input v-model="dialogForm.stcd" placeholder="请输入编码" size="small"/>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" size="small" @click="handleSearch">查询</el-button>
+        <el-button type="primary" size="small" @click="handelExport">导出</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup>
+const emit = defineEmits(['close-dialog']);
+const props = defineProps({
+  showStnmDialog:{type: Boolean},
+  stnmData:{type:Object},
+  closeDialog:{type:Function}
+});
+
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const dialogType = ref("");
+const dialogForm = ref({
+  daterange: [],
+  tm: "",
+  region: "t",
+  stcd:""
+})
+const regionOptions = [
+  {label:"高锰酸盐指数(mg/L)",value:'codmn'},
+  {label:"总磷(mg/L)",value:'tp'},
+  {label:"总氮(mg/L)",value:'tn'},
+  {label:"氨氮(mg/L)",value:'nh4'},
+  {label:"溶解氧(mg/L)",value:'cod'},
+  {label:"锑(μg/L)",value:'t'},
+  {label:"二氯甲炕(mg/L)",value:'cas'},
+  {label:"蓝藻数量(万个/L)",value:'algaeDensity'},
+  {label:"电导率(μS/cm)",value:'ec'},
+  {label:"二甲基异莰醇(mg/L)",value:'methy'},
+  {label:"氯化物(mg/L)",value:'na'},
+];
+
+watch(() => props.showStnmDialog, (newVal) => {
+  if (newVal) {
+    dialogVisible.value = newVal;
+  }
+});
+watch(() => props.stnmData, (newVal) => {
+  if (newVal) {
+    dialogTitle.value = newVal.type == 'stnm'
+      ? `${newVal.stnm}监测数据信息`
+      : `${newVal.river}各测站沿程监测数据信息`;
+    dialogType.value = newVal.type;
+  }
+});
+const closeDialog = () => {
+  dialogVisible.value = false;
+  emit('close-dialog', dialogVisible.value);
+}
+</script>
+
+<style scoped lang="scss">
+.stnm-diglog{
+  position: absolute;
+  top: 10%;
+  left: 10%;
+  height: 80%;
+  width: 80%;
+  background: #fff;
+  border-radius: 5px;
+  box-shadow: 0 0 8px #909399;/* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影颜色 */
+  .stnm-dialog-title{
+    width: 100%;
+    height: 32px;
+    background: url("@/assets/images/lefttitle-bg.png");
+    background-size: 100% 100%;
+    border-radius: 5px 5px 0 0;
+    position: relative;
+    span{
+      color: #fff;
+      font-size: 14px;
+      line-height: 32px;
+      margin-left: 20px;
+    }
+    img{
+      height: 18px;
+      width: 18px;
+      position: absolute;
+      top: 7px;
+      right: 10px;
+      cursor: pointer;
+    }
+  }
+  .shuizhiform-top-left{
+    width: 100%;
+    display: flex;
+  }
+}
+</style>

+ 458 - 6
ruoyi-ui/src/views/YuJing/shouye/index.vue

@@ -1,10 +1,157 @@
 <template>
-  <div id="mapChart"></div>
+  <div class="shouye-index">
+    <div id="mapChart"></div>
+    <!-- 地图工具 -->
+    <div class="map-tools">
+      <div :class="{'map-tool-item':mapToolName!='测距','map-tool-itemActive':mapToolName=='测距'}" 
+            @click="handleMapTools('测距')">
+        <img v-if="mapToolName!='测距'" src="@/assets/yujing/map/measure-deactive.png"/>
+        <img v-else src="@/assets/yujing/map/measure-active.png"/>
+      </div>
+      <div :class="{'map-tool-item':mapToolName!='测面','map-tool-itemActive':mapToolName=='测面'}"                       @click="handleMapTools('测面')">
+        <img v-if="mapToolName!='测面'" src="@/assets/yujing/map/area-deactive.png"/>
+        <img v-else src="@/assets/yujing/map/area-active.png"/>
+      </div>
+    </div>
+    <!-- 勾选框 -->
+    <div class="shouye-checkbox">
+      <el-checkbox v-model="mapCheckbox.wyhChecked" label="望虞河测站" size="small"/>
+      <el-checkbox v-model="mapCheckbox.tphChecked" label="太浦河测站" size="small"/>
+      <el-checkbox v-model="mapCheckbox.xmhChecked" label="新孟河测站" size="small"/>
+      <el-checkbox v-model="mapCheckbox.xmhChecked_js" label="新孟河测站-江苏省共享" size="small"/>
+      <el-checkbox v-model="mapCheckbox.wyhChecked_lh" label="望虞河-浏河区间河网" size="small"/>
+      <el-checkbox v-model="mapCheckbox.zdChecked" label="自动监测站" size="small"/>
+      <el-checkbox v-model="mapCheckbox.lzspChecked" label="蓝藻视频站" size="small"/>
+      <el-checkbox v-model="mapCheckbox.thsyChecked" label="太湖水源地" size="small"/>
+      <el-checkbox v-model="mapCheckbox.thlzChecked" label="太湖蓝藻" size="small"/>
+      <el-checkbox v-model="mapCheckbox.zysyChecked" label="流域片重要水源地" size="small"/>
+      <el-checkbox v-model="mapCheckbox.wrjzysyChecked" label="无人机数据查看" size="small"/>
+    </div>
+    <!-- 图例 -->
+    <div class="shouye-tuli">
+      <div class="tuli-title">图例</div>
+      <div class="tuli-content">
+        <div class="tuli-content-left">
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/blue-circle.png" style="width: 25px;height: 25px;margin-left: 5px;"/>
+            <span style="line-height: 25px;">自动监测站</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/视频监控.png" style="width: 18px;height: 20px;margin:0 5px 0 8px;"/>
+            <span>视频监控站</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/Ⅰ.png" class="left-item-img"/>
+            <span>测站(Ⅰ类水)</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/Ⅱ.png" class="left-item-img"/>
+            <span>测站(Ⅱ类水)</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/Ⅲ.png" class="left-item-img"/>
+            <span>测站(Ⅲ类水)</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/Ⅳ.png" class="left-item-img"/>
+            <span>测站(Ⅳ类水)</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/Ⅴ.png" class="left-item-img"/>
+            <span>测站(Ⅴ类水)</span>
+          </div>
+          <div class="content-left-item">
+            <img src="@/assets/yujing/map/劣Ⅴ.png" class="left-item-img"/>
+            <span>测站(劣Ⅴ类水)</span>
+          </div>
+        </div>
+        <div class="tuli-content-right">
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/square-black.png" class="right-item-img"/>
+            <span>沉水植被</span>
+          </div>
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/square-brown.png" class="right-item-img"/>
+            <span>沉水植被</span>
+          </div>
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/square-pink.png" class="right-item-img"/>
+            <span>圈围区</span>
+          </div>
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/square-yellow.png" class="right-item-img"/>
+            <span>人工养殖水葫芦</span>
+          </div>
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/square-green.png" class="right-item-img"/>
+            <span>围网养殖</span>
+          </div>
+          <div class="content-right-item">
+            <img src="@/assets/yujing/map/维修.png" class="right-item-img"/>
+            <span>维修中</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="map-select">
+      <el-form
+        ref="mapFormRef"
+        :model="mapForm"
+        label-width="auto"
+        size="small"
+        >
+        <el-form-item label="监测图标:" prop="region">
+          <el-select-v2
+            v-model="mapForm.region"
+            placeholder="请选择要监测的图标"
+            :options="maprRegionOptions"
+          />
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 地图站点信息弹框 -->
+    <div ref="popupRefs" class="map-popup" v-show="showPopup">
+      <div class="map-popup-top">
+        <img src="@/assets/yujing/map/取消.png" @click="closePopup"/>
+      </div>
+      <div class="map-popup-content">
+        <div class="popup-content-left">
+          <span>测站名称:</span>
+          <span>所在河流:</span>
+          <span>最新监测时间:</span>
+          <span>溶解氧(mg/L):</span>
+          <span>高锰酸盐指数(mg/L):</span>
+          <span>氨氮(mg/L):</span>
+          <span>总磷(mg/L):</span>
+          <span>锑(μg/L):</span>
+        </div>
+        <div class="popup-content-right">
+          <span 
+            style="color: #4a93f5;cursor: pointer;" 
+            @click="handleStnmDialog(mapPopupData,'stnm')"
+          >{{ mapPopupData.stnm }}</span>
+          <span 
+            style="color: #4a93f5;cursor: pointer;"
+            @click="handleStnmDialog(mapPopupData,'river')"
+          >{{ mapPopupData.river }}</span>
+          <span>{{ mapPopupData.tm }}</span>
+          <span>{{ mapPopupData.cod }}</span>
+          <span></span>
+          <span></span>
+          <span></span>
+          <span></span>
+        </div>
+      </div>
+      <div class="popup-trangle"></div>
+    </div>
+    <StnmDialog :showStnmDialog="stnmDialogVisible" :stnmData="stnmDialogData" @closeDialog="closeStnmDialog"/>
+  </div>
 </template>
 
-<script setup name="BigscreenIndex">
+<script setup>
 import {ElMessage} from 'element-plus';
 import 'ol/css';
+import {ScaleLine, defaults as defaultControls} from 'ol/control';
 import Map from 'ol/Map';
 import View from 'ol/View';
 import TileLayer from "ol/layer/Tile";
@@ -16,12 +163,55 @@ import Fill from "ol/style/Fill";
 import Feature from 'ol/Feature';
 import Stroke from "ol/style/Stroke";
 import Overlay from 'ol/Overlay';
-import { LinearRing } from "ol/geom";
-import { fromExtent } from "ol/geom/Polygon";
+import StnmDialog from "./components/StnmDialog.vue"
+import imgⅣ from "@/assets/yujing/map/Ⅳ.png";
 
 const mapChart = ref(null);
 const mapCenter = ref([120.745, 31.120]);
 const mapZoom = ref(10.6);
+const mapToolName = ref("");
+const mapCheckbox = ref({
+  wyhChecked: true,
+  tphChecked: true,
+  xmhChecked: false,
+  xmhChecked_js: false,
+  wyhChecked_lh: false,
+  zdChecked: false,
+  lzspChecked: false,
+  thsyChecked: false,
+  thlzChecked: false,
+  zysyChecked: false,
+  wrjzysyChecked: false,
+});
+const mapForm = ref({
+  region:'cod',
+});
+const maprRegionOptions = [
+  {label:"溶解氧(mg/L)",value:'cod'}
+];
+const mapPointData = [
+  {
+    stnm: "太浦闸下",
+    river: "太浦河干流",
+    tm: "2025-08-21 13:51",
+    lgtd: 120.745,
+    lttd: 31.120,
+    cod:10.87,
+  },
+  {
+    stnm: "太浦闸上",
+    river: "太浦河干流",
+    tm: "2025-09-05 15:48",
+    lgtd: 120.545,
+    lttd: 31.020,
+    cod:8.87,
+  },
+];
+const popupRefs = ref(null);
+const showPopup = ref(false);
+const mapPopupData = ref({});
+const stnmDialogVisible = ref(false);
+const stnmDialogData = ref({});
 
 onMounted(() => {
   initMap();
@@ -52,14 +242,276 @@ const initMap = () => {
       maxZoom: 16,
       projection: 'EPSG:4326',
     }),
-    layers: [vecLayer,cvaLayer],
+    layers: [vecLayer, cvaLayer],
+    controls: defaultControls({
+      zoom: false,//不显示放大放小按钮
+      rotate: false,//不显示指北针控件
+      attribution: false,//不显示右下角的地图信息控件
+      scaleLine:false,//不显示比例尺控件
+    })
+  });
+  // 添加地图站点
+  let featuresData = [];
+  mapPointData.forEach((item) => {
+    let iconFeature = new Feature({
+      geometry: new Point([item.lgtd,item.lttd]), 
+      properties: item,
+    });
+    iconFeature.setStyle(
+      new Style({
+        image: new Icon({
+          src: imgⅣ,
+          anchor: [0.5, 0.5],
+          scale: 0.08,
+        }),
+      })
+    );
+    featuresData.push(iconFeature);
+  })
+  var polygonLayer = new VectorLayer({
+    source:new VectorSource({
+      features: featuresData,
+    }),
+    zIndex:5,
+    visible:true,
+  })
+  mapChart.value.addLayer(polygonLayer);
+  singleclick();
+};
+// 地图点击事件
+const singleclick = () => {
+  let elpopup = popupRefs.value;
+  let popupOverlay = new Overlay({
+    element: elpopup,
+    positioning: "bottom-center",
+    stopEvent: false,
+    offset: [220, 110],
+  });
+  mapChart.value.on("singleclick", (e) => {
+    let feature = mapChart.value.forEachFeatureAtPixel(e.pixel, (feature) => feature);
+    if (feature) {
+      showPopup.value = true;
+      mapPopupData.value = feature.values_.properties;
+      let position = [mapPopupData.value.lgtd, mapPopupData.value.lttd];
+      popupOverlay.setPosition(position);
+    }
   });
+  // 设置弹窗位置
+  mapChart.value.addOverlay(popupOverlay);
 };
+// 关闭站点信息弹框
+const closePopup = () => { 
+  showPopup.value = false;
+};
+const handleStnmDialog = (data,type) => { 
+  stnmDialogData.value = {
+    ...data,
+    type
+  };
+  stnmDialogVisible.value = true;
+};
+const closeStnmDialog = (status) => { 
+  stnmDialogVisible.value = status;
+};
+const handleMapTools = (name) => {
+  mapToolName.value = name;
+}
 </script>
 
 <style scoped lang="scss">
-#mapChart{
+.shouye-index{
   height: 100%;
   width: 100%;
+  position: relative;
+  #mapChart{
+    height: 100%;
+    width: 100%;
+  }
+  .map-tools{
+    position: absolute;
+    top: 8%;
+    left: 1%;
+    width: 30px;
+    height: 80px;
+    display: flex;
+    flex-direction: column;
+    .map-tool-item{
+      width: 100%;
+      height: 30px;
+      background: rgba(255,255,255,0.9);
+      border-radius: 3px;
+      margin: 5px 0;
+      display:flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      img{
+        width: 18px;
+        height: 18px;
+      }
+    }
+    .map-tool-itemActive{
+      width: 100%;
+      height: 30px;
+      background: rgba(74, 147, 245,0.9);
+      border-radius: 3px;
+      margin: 5px 0;
+      display:flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      img{
+        width: 18px;
+        height: 18px;
+      }
+    }
+    .map-tool-item:hover{
+      background: rgba(74, 147, 245,0.9);
+    }
+  }
+  .shouye-checkbox{
+    position: absolute;
+    top:2%;
+    left: 3%;
+    width: 200px;
+    height: 180px;
+    padding: 10px 0 10px 20px;
+    background: url("@/assets/yujing/img/left-box.png");
+    background-size: 100% 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center; /* 垂直居中 */
+    .el-checkbox {
+      height:18px;
+      font-size: 12px;
+    }
+  }
+  .shouye-tuli{
+    width: 300px;
+    height: 230px;
+    background: #fff;
+    border: 1px solid #4a93f5;
+    border-radius: 5px;
+    position: absolute;
+    bottom: 1%;
+    left:0.5%;
+    .tuli-title{
+      width: 100%;
+      line-height: 30px;
+      background: url("@/assets/images/lefttitle-bg.png");
+      background-size: 100% 100%;
+      font-size: 16px;
+      color: #fff;
+      text-align: center;
+    }
+    .tuli-content{
+      width: 100%;
+      height: 80%;
+      display: flex;
+      .tuli-content-left{
+        width: 50%;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        .content-left-item{
+          line-height: 20px;
+          margin-bottom: 3px;
+          display: flex;
+          .left-item-img{
+            height: 20px;
+            width: 20px;
+            margin: 0 5px;
+          }
+          span{
+            color: #000;
+            font-size: 12px;
+          }
+        }
+      }
+      .tuli-content-right{
+        width: 50%;
+        height: 100%;
+        .content-right-item{
+          line-height: 20px;
+          margin-bottom: 4px;
+          display: flex;
+          .right-item-img{
+            height: 20px;
+            width: 20px;
+            margin: 0 5px;
+          }
+          span{
+            color: #000;
+            font-size: 12px;
+          }
+        }
+      }
+    }
+  }
+  .map-select{
+    position: absolute;
+    right: 1%;
+    top: 1%;
+    width: 13%;
+  }
+}
+.map-popup {
+  width:400px;
+  height: 220px;
+  background-color: rgba(255, 255, 255,0.9);
+  border-radius: 5px;
+  position: relative;
+  .map-popup-top{
+    width: 100%;
+    height: 20px;
+    background: url("@/assets/images/lefttitle-bg.png");
+    background-size: 100% 100%;
+    border-radius: 5px 5px 0 0;
+    img{
+      position: absolute;
+      top: 2px;
+      right: 2px;
+      width: 16px;
+      height: 16px;
+      cursor: pointer;
+    }
+  }
+  .map-popup-content{
+    width: 95%;
+    height: 82%;
+    margin: 2.5%;
+    background-color: #fff;
+    border: 1px solid #e9e9e9;
+    display: flex;
+    .popup-content-left{
+      display: flex;
+      flex-direction: column;
+      font-size: 14px;
+      font-weight: bolder;
+      color: #444444;
+      line-height: 22px;
+      margin-left: 5PX;
+    }
+    .popup-content-right{
+      display: flex;
+      flex-direction: column;
+      font-size: 14px;
+      color: #444444;
+      line-height: 22px;
+    }
+  }
+  .popup-trangle{
+    width:0;
+    height:0;
+    content:'';
+    border-right:10px solid rgba(255,255,255,0.9);
+    border-top:10px solid transparent;
+    border-bottom:10px solid transparent;
+    position:absolute;
+    left:-10px;
+    bottom:100px;
+  }
 }
 </style>

+ 255 - 2
ruoyi-ui/src/views/YuJing/water/monitorAlert.vue

@@ -1,3 +1,256 @@
 <template>
-  <div>监视告警</div>
-</template>
+  <div class="jiance-index">
+    <div id="mapChart"></div>
+    <!-- 地图工具 -->
+    <div class="map-tools">
+      <div :class="{'map-tool-item':mapToolName!='测距','map-tool-itemActive':mapToolName=='测距'}" 
+            @click="handleMapTools('测距')">
+        <img v-if="mapToolName!='测距'" src="@/assets/yujing/map/measure-deactive.png"/>
+        <img v-else src="@/assets/yujing/map/measure-active.png"/>
+      </div>
+      <div :class="{'map-tool-item':mapToolName!='测面','map-tool-itemActive':mapToolName=='测面'}"                       @click="handleMapTools('测面')">
+        <img v-if="mapToolName!='测面'" src="@/assets/yujing/map/area-deactive.png"/>
+        <img v-else src="@/assets/yujing/map/area-active.png"/>
+      </div>
+    </div>
+    <!-- 图例 -->
+    <div class="jiance-tuli">
+      <div class="tuli-title">图例</div>
+      <div class="tuli-content">
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/Ⅰ.png" class="left-item-img"/>
+          <span>测站(Ⅰ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/Ⅱ.png" class="left-item-img"/>
+          <span>测站(Ⅱ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/Ⅲ.png" class="left-item-img"/>
+          <span>测站(Ⅲ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/Ⅳ.png" class="left-item-img"/>
+          <span>测站(Ⅳ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/Ⅴ.png" class="left-item-img"/>
+          <span>测站(Ⅴ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+        <div class="content-left-item">
+          <img src="@/assets/yujing/map/劣Ⅴ.png" class="left-item-img"/>
+          <span>测站(劣Ⅴ类水)<span class="left-item-num">19</span>个</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {ElMessage} from 'element-plus';
+import 'ol/css';
+import {ScaleLine, defaults as defaultControls} from 'ol/control';
+import Map from 'ol/Map';
+import View from 'ol/View';
+import TileLayer from "ol/layer/Tile";
+import { XYZ, Vector as VectorSource } from 'ol/source.js';
+import VectorLayer from "ol/layer/Vector";
+import { LineString, Point } from "ol/geom";
+import { Icon, Style, Text,Circle } from 'ol/style';
+import Fill from "ol/style/Fill";
+import Feature from 'ol/Feature';
+import Stroke from "ol/style/Stroke";
+import Overlay from 'ol/Overlay';
+import imgⅣ from "@/assets/yujing/map/Ⅳ.png";
+
+const mapChart = ref(null);
+const mapCenter = ref([121.045, 30.920]);
+const mapZoom = ref(9.5);
+const mapToolName = ref("");
+const mapPointData = [
+  {
+    stnm: "太浦闸下",
+    river: "太浦河干流",
+    tm: "2025-08-21 13:51",
+    lgtd: 120.745,
+    lttd: 31.120,
+    cod:10.87,
+  },
+  {
+    stnm: "太浦闸上",
+    river: "太浦河干流",
+    tm: "2025-09-05 15:48",
+    lgtd: 120.545,
+    lttd: 31.020,
+    cod:8.87,
+  },
+];
+
+onMounted(() => {
+  initMap();
+});
+const initMap = () => { 
+  let vecLayer = new TileLayer({
+    source: new XYZ({
+      url: "http://t0.tianditu.gov.cn/vec_w/wmts?" +
+        "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles" +
+        "&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}" +
+        "&tk=9bb941214f10fbf9a3eab43f45cb2b7e",
+    }),
+  });
+  let cvaLayer = new TileLayer({
+    source: new XYZ({
+      url: "http://t0.tianditu.gov.cn/cva_w/wmts?" +
+        "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles" +
+        "&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}" +
+        "&tk=9bb941214f10fbf9a3eab43f45cb2b7e",
+    }),
+  });
+  mapChart.value= new Map({
+    target: 'mapChart',
+    view: new View({
+      center: mapCenter.value,
+      zoom: mapZoom.value,
+      minZoom: 3,
+      maxZoom: 16,
+      projection: 'EPSG:4326',
+    }),
+    layers: [vecLayer, cvaLayer],
+    controls: defaultControls({
+      zoom: false,//不显示放大放小按钮
+      rotate: false,//不显示指北针控件
+      attribution: false,//不显示右下角的地图信息控件
+      scaleLine:false,//不显示比例尺控件
+    })
+  });
+  // 添加地图站点
+  let featuresData = [];
+  mapPointData.forEach((item) => {
+    let iconFeature = new Feature({
+      geometry: new Point([item.lgtd,item.lttd]), 
+      properties: item,
+    });
+    iconFeature.setStyle(
+      new Style({
+        image: new Icon({
+          src: imgⅣ,
+          anchor: [0.5, 0.5],
+          scale: 0.08,
+        }),
+      })
+    );
+    featuresData.push(iconFeature);
+  })
+  var polygonLayer = new VectorLayer({
+    source:new VectorSource({
+      features: featuresData,
+    }),
+    zIndex:5,
+    visible:true,
+  })
+  mapChart.value.addLayer(polygonLayer);
+};
+const handleMapTools = (name) => {
+  mapToolName.value = name;
+}
+</script>
+
+<style scoped lang="scss">
+.jiance-index{
+  height: 100%;
+  width: 100%;
+  position: relative;
+  #mapChart{
+    height: 100%;
+    width: 100%;
+  }
+  .map-tools{
+    position: absolute;
+    top: 8%;
+    left: 1%;
+    width: 30px;
+    height: 80px;
+    display: flex;
+    flex-direction: column;
+    .map-tool-item{
+      width: 100%;
+      height: 30px;
+      background: rgba(255,255,255,0.9);
+      border-radius: 3px;
+      margin: 5px 0;
+      display:flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      img{
+        width: 18px;
+        height: 18px;
+      }
+    }
+    .map-tool-itemActive{
+      width: 100%;
+      height: 30px;
+      background: rgba(74, 147, 245,0.9);
+      border-radius: 3px;
+      margin: 5px 0;
+      display:flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      img{
+        width: 18px;
+        height: 18px;
+      }
+    }
+    .map-tool-item:hover{
+      background: rgba(74, 147, 245,0.9);
+    }
+  }
+  .jiance-tuli{
+    width: 180px;
+    height: 180px;
+    background: #fff;
+    border: 1px solid #4a93f5;
+    border-radius: 5px;
+    position: absolute;
+    bottom: 1%;
+    left:0.5%;
+    .tuli-title{
+      width: 100%;
+      line-height: 30px;
+      background: url("@/assets/images/lefttitle-bg.png");
+      background-size: 100% 100%;
+      font-size: 16px;
+      color: #fff;
+      text-align: center;
+    }
+    .tuli-content{
+      width: 100%;
+      height: 80%;
+      display: flex;
+      flex-direction: column;
+        .content-left-item{
+          line-height: 20px;
+          margin-bottom: 3px;
+          display: flex;
+          .left-item-img{
+            height: 20px;
+            width: 20px;
+            margin: 0 5px;
+          }
+          span{
+            color: #000;
+            font-size: 12px;
+            .left-item-num{
+              color: #5b964c;
+            font-size: 14px;
+              font-weight: bolder;
+            }
+          }
+        }
+    }
+  }
+}
+</style>

+ 1 - 1
ruoyi-ui/src/views/index.vue

@@ -62,7 +62,7 @@ const router = useRouter();
 const userStore = useUserStore();
 const modules = [Autoplay, Pagination, Navigation, A11y];
 const slides = ref([
-  { image: "", title: '预警系统', secondTitle: "水资源保护系统", path: "/YuJing/index" },
+  { image: "", title: '预警系统', secondTitle: "水资源保护系统", path: "/YuJing/shouye/index" },
   { image: "", title: '1', },
   { image: "", title: '2', },
   { image: "", title: '3', },