Hua há 2 meses atrás
pai
commit
ce9d8990cf

+ 2 - 2
ruoyi-ui/.env.development

@@ -10,8 +10,8 @@ VITE_APP_BASE_Title = '/sh'
 # 若依管理系统/生产环境
 VITE_APP_BASE_API = '/sh-api'
 
-VITE_DEV_PATH = 'http://localhost:8082'
-# VITE_DEV_PATH = 'http://192.168.2.104:8082'
+# VITE_DEV_PATH = 'http://localhost:8082'
+VITE_DEV_PATH = 'http://192.168.2.104:8082'
 VITE_WS_BASE_URL = '/websocket'
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 1 - 1
ruoyi-ui/src/App.vue

@@ -1,5 +1,5 @@
 <template>
-  <router-view />
+  <router-view :key="$route.fullPath"/>
 </template>
 
 <script setup>

+ 19 - 3
ruoyi-ui/src/layout/components/Navbar.vue

@@ -2,8 +2,24 @@
   <div class="navbar">
     <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"  />
     <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
+    <div style="margin-left: 47%;font-size: 14px;align-items: center;height: 100%;display: flex;">
+      <div style="color: #409EFF;" v-if="route.params.id=='20'">
+        温带风暴潮预报模型
+      </div>
+      <div style="color: #409EFF;" v-if="route.params.id=='25'">
+        上海沿海风暴潮预报模型
+      </div>
+      <div style="color: #409EFF;" v-if="route.params.id=='26'">
+        上海市城区洪涝仿真模型
+      </div>
+      <div style="color: #409EFF;" v-if="route.params.id=='28'">
+        苏州河水系水情预报模型
+      </div>
+      <div style="color: #409EFF;" v-if="route.params.id=='30'">
+        上海市中心城区排水系统模型
+      </div>
+    </div>
     <top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
-
     <!-- <div class="right-menu">
       <template v-if="appStore.device !== 'mobile'">
         <header-search id="header-search" class="right-menu-item" />
@@ -60,11 +76,11 @@ import RuoYiDoc from '@/components/RuoYi/Doc'
 import useAppStore from '@/store/modules/app'
 import useUserStore from '@/store/modules/user'
 import useSettingsStore from '@/store/modules/settings'
-
+import {useRoute} from "vue-router";
 const appStore = useAppStore()
 const userStore = useUserStore()
 const settingsStore = useSettingsStore()
-
+const route = useRoute();
 function toggleSideBar() {
   appStore.toggleSideBar()
 }

+ 1 - 1
ruoyi-ui/src/utils/websocket.js

@@ -1,7 +1,7 @@
 
 
 export function createWebSocket(path = '/message') {
-  const baseUrl = import.meta.env.VITE_WS_BASE_URL
+  const baseUrl = import.meta.env.VITE_DEV_PATH
   
   // 开发环境使用相对路径(走代理)
   // 生产环境使用完整 WSS 地址

+ 176 - 25
ruoyi-ui/src/views/map/components/map.vue

@@ -2,13 +2,13 @@
   <div class="map-index">
     <!-- 苏州河 -->
     <div ref="scrollContainer" v-if="route.params.id=='28'" style="border: 2px solid #409EFF;background-color: rgba(127, 161, 215, 0.5);border-radius: 50px 50px 50px 50px;
-    align-items: center;display: flex;position: absolute;z-index: 9999;bottom: 1%;left: 1%;width:50vw;height: 10vh;overflow-x:auto;overflow-y: hidden;">
+    align-items: center;display: flex;position: absolute;z-index: 100;bottom: 1%;left: 1%;width:50vw;height: 10vh;overflow-x:auto;overflow-y: hidden;">
         <svg-icon icon-class="start1" v-if="!isStart" @click="reStart" style="margin-left: 10%;width: 30px;height:30px;cursor: pointer;"/>
         <svg-icon icon-class="pause1" v-if="isStart" @click="pause" style="margin-left: 10%;width: 30px;height:30px;cursor: pointer;"/>
         <el-slider @change="pauseIn" :format-tooltip="formatTooltip"  v-model="activities1" :max="dateLength"  :marks="marks" style="width: 80%;margin-left: 1%;"/>
       
     </div>
-    <div v-if="route.params.id=='28'" style="background-color: white;position: absolute;z-index: 9999;top: 1%;left: 1%;width: 20vw;height: 45vh;overflow:auto;">
+    <div v-if="route.params.id=='28'" style="background-color: white;position: absolute;z-index: 100;top: 1%;left: 1%;width: 20vw;height: 45vh;overflow:auto;">
       <div class="biz-data-card-header">方案列表</div>
       <el-table 
           :data="tableDataFangan"
@@ -31,7 +31,7 @@
           </el-table-column>
         </el-table>
     </div>
-    <div v-if="route.params.id=='28'" style="background-color: white;position: absolute;z-index: 9999;top: 1%;right: 1%;width: 30vw;height: 85vh;overflow:auto;">
+    <div v-if="route.params.id=='28'" style="background-color: white;position: absolute;z-index: 100;top: 1%;right: 1%;width: 30vw;height: 85vh;overflow:auto;">
       <div class="biz-data-card-header">水位信息</div>
       <el-table 
           :data="tableData"
@@ -53,6 +53,7 @@
           <el-table-column prop="time" label="时间"  width="210"/>
         </el-table>
     </div>
+    <div id="tooltip" ref="tooltipRef" class="tooltip"></div>
     <!-- 温带风暴潮 -->
     <div ref="scrollContainer" v-if="route.params.id=='20'" style="border: 2px solid #409EFF;background-color: rgba(127, 161, 215, 0.5);border-radius: 50px 50px 50px 50px;
     align-items: center;display: flex;position: absolute;z-index: 100;bottom: 1%;left: 1%;width:50vw;height: 10vh;overflow-x:auto;overflow-y: hidden;">
@@ -107,6 +108,30 @@
           <el-table-column prop="gfsWnddir" label="风向"  width=""/>
         </el-table>
     </div>
+    <!-- 上海风暴潮 -->
+    <div v-if="route.params.id=='30'" style="background-color: white;position: absolute;z-index: 100;top: 1%;left: 1%;width: 20vw;height: 45vh;overflow:auto;">
+      <div class="biz-data-card-header">方案列表</div>
+      <el-table 
+          :data="tableDataShanghaifeng"
+          style="width: 98%;margin-left: 1%;margin-top: 1%;height: 40vh;"
+          :cell-style="{ padding:'5px' }"
+          :header-cell-style="{height: heightAll*0.01+'px',}"
+          :row-style="{ fontSize: '16px',textAlign:'center'}"
+          border >
+          <el-table-column type="index" label="序号" width="80">
+            <template #default="{ $index }">
+              <div style="text-align: center;">
+                {{ $index + 1 }}
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="planName" label="名称">
+            <template #default="scope">
+              <span style="cursor: pointer;color:#409EFF" @click="getZ(scope.row)">{{ scope.row.planName }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+    </div>
     <el-dialog @close="clearFromLev" :title="titleFengbao" v-model="dialogVisibleFengbao" width="50%" destroy-on-close>
       <div>
         <div style="font-size: 20px;margin-left: 2%;">水位</div>
@@ -124,13 +149,40 @@
         <div style="font-size: 20px;margin-left: 2%;margin-top:1%">风向</div>
         <div id="fengxiang" style="width: 100%;height:18vh;margin-top: 1%;"></div>
       </div>
-      <!-- <template #footer>
-        <span class="dialog-footer">
-          <el-button type="primary" @click="dialogVisibleFengbao = false" size="mini">
-            确定
-          </el-button>
-          </span>
-      </template> -->
+    </el-dialog>
+    <el-dialog @close="clearFromLev" title="" v-model="dialogVisibleSuzhou" width="30%" destroy-on-close>
+      <el-descriptions
+        class="margin-top"
+        :title="titleSuzhou"
+        :column="1"
+        :size="size"
+        border
+      >
+        <el-descriptions-item>
+          <template #label>
+            <div class="cell-item">
+              站码
+            </div>
+          </template>
+          {{suzhou.stationId}}
+        </el-descriptions-item>
+        <el-descriptions-item>
+          <template #label>
+            <div class="cell-item">
+              水位
+            </div>
+          </template>
+          {{suzhou.waterLevel}}
+        </el-descriptions-item>
+        <el-descriptions-item>
+          <template #label>
+            <div class="cell-item">
+              时间
+            </div>
+          </template>
+          {{suzhou.tm}}
+        </el-descriptions-item>
+      </el-descriptions>
     </el-dialog>
     <div id="mapChart"></div>
   </div>
@@ -138,6 +190,7 @@
 <script setup>
 import 'ol/css';
 import {defaults as defaultControls} from 'ol/control';
+import {getForecastlist} from "@/api/standardization/bizDataShowConfig.js";
 import Map from 'ol/Map';
 import View from 'ol/View';
 import TileLayer from "ol/layer/Tile";
@@ -176,7 +229,9 @@ const tableDataFangan = ref([])
 const scrollContainer = ref(null);
 const scrollAmount =0;
 const widthAll = ref(window.innerWidth)
+const dialogVisibleSuzhou = ref(false)
 const route = useRoute();
+const suzhou = ref({})
 const props = defineProps({
   config: Object,
 });
@@ -190,12 +245,65 @@ const dateLength = ref(0)
 const marks = ref({
 })
 const isStart = ref(true)
+const tooltipRef = ref(null);
+let currentFeature = null
+let overlay = null
+const tableDataShanghaifeng = ref([])
 onMounted(async () => {
-  await initMap();
+  initMap();
   // 初始化完成后执行配置渲染
-  if(route.params.id==='25'){
+  if(route.params.id==='30'){
+    getForecastlist({appId: route.params.id}).then(res=>{
+      tableDataShanghaifeng.value = res.rows
+    })
     polygonLayer.value = createPolygonLayer(jsonDatak5)
     mapChart.value.addLayer(polygonLayer.value);
+    overlay = new Overlay({
+      element: tooltipRef.value,
+      positioning: 'bottom-center',
+      offset: [0, -10],
+      stopEvent: false,
+    });
+    mapChart.value.addOverlay(overlay);
+    mapChart.value.getView().setZoom(18)
+    mapChart.value.getView().setCenter([121.41335460526926,31.205820744558153]);
+    mapChart.value.on('pointermove', (e) => {
+    if (e.dragging) {
+      return; // 如果正在拖拽地图,则不处理[2,7](@ref)
+    }
+    
+    const pixel = mapChart.value.getEventPixel(e.originalEvent);
+    const feature = mapChart.value.forEachFeatureAtPixel(pixel, (feature) => feature);
+    
+    if (feature) {
+      // 鼠标在要素上[7](@ref)
+      if (feature !== currentFeature) {
+        currentFeature = feature;
+        console.log(feature.values_)
+        // 获取要素属性信息
+        const featureCode = feature.values_.element_no;
+        const featureDep = feature.values_.DEPTH2D
+        
+        // 更新工具提示内容[2](@ref)
+        tooltipRef.value.innerHTML = `
+          <div class="tooltip-header">编号:${featureCode}</div>
+          <div class="tooltip-content">水深: ${featureDep}</div>
+        `;
+        
+        // 设置工具提示位置并显示[7](@ref)
+        overlay.setPosition(e.coordinate);
+        tooltipRef.value.style.display = 'block';
+        
+        // 更改鼠标指针样式为手型[3,5](@ref)
+        mapChart.value.getTargetElement().style.cursor = 'pointer';
+      }
+    } else {
+      // 鼠标不在要素上
+      tooltipRef.value.style.display = 'none';
+      currentFeature = null;
+      mapChart.value.getTargetElement().style.cursor = ''; // 恢复默认指针[3](@ref)
+    }
+  });
   }
   if(route.params.id==='28'){
     tableDataFangan.value = fangan.data.records
@@ -241,11 +349,39 @@ onMounted(async () => {
       },
       label: parTime[3].timestamp.slice(5),
     }
+    console.log(activities)
     suzhouJson.data.inputParam = JSON.parse(suzhouJson.data.inputParam)
     console.log(suzhouJson.data)
     timerId = setInterval(changeMap, 1000)
+    console.log(suzhouPoint)
     pointLayer.value = createPointlayer(suzhouPoint)
     mapChart.value.addLayer(pointLayer.value);
+    mapChart.value.on('click', (event) => {
+      const feature = mapChart.value.forEachFeatureAtPixel(
+        event.pixel,
+        (feature) => feature
+      );
+      
+      if (feature) {
+        pause()
+        const properties = feature.getProperties();
+        dialogVisibleSuzhou.value = true
+        titleSuzhou.value = properties.name
+        console.log('要素属性:', properties);
+        suzhou.value = properties
+        suzhou.value.tm = activities.value[count.value-1].timestamp
+        const title = feature.get('title');
+        const id = feature.get('id');
+        console.log('标题:', title, 'ID:', id);
+        
+        // 获取几何信息
+        const geometry = feature.getGeometry();
+        const coordinates = geometry.getCoordinates();
+        console.log('几何坐标:', coordinates);
+      } else {
+        console.log('未点击到任何要素');
+      }
+    })
   }
   if(route.params.id==='20'){
     tableDataFangan.value = fangan.data.records
@@ -256,6 +392,7 @@ onMounted(async () => {
     await loadLayers(props.config.layers);
   }
 });
+const titleSuzhou = ref('')
 const count = ref(0)
 const tableData = ref()
 function getZ(row){
@@ -524,10 +661,6 @@ function changeMap(){
   if(count.value >= keys1.length){
     console.log(count.value,keys1.length)
     count.value = 0
-    scrollContainer.value.scrollTo({
-      left: 0, // 滚动到顶部
-      behavior: 'smooth' // 启用平滑滚动效果
-    });
   }
   activities1.value = count.value
   mapChart.value.removeLayer(pointLayer.value)
@@ -572,15 +705,6 @@ function createPolygonLayer(jsonData){
       })
     });
     // 创建多边形要素样式[6,7](@ref)
-    const polygonStyle = new Style({
-      fill: new Fill({
-        color: 'rgba(0, 102, 255, 0.3)'  // 半透明蓝色填充
-      }),
-      stroke: new Stroke({
-        color: '#0066ff',  // 蓝色边框
-        width: 2           // 边框宽度
-      })
-    });
     const styleFunction = function(feature) {
       const properties = feature.getProperties();
       if (properties.DEPTH2D >= 1) {
@@ -721,6 +845,7 @@ function createPointlayer(dataJson){
       area: station.QUYU,
       river: station.HELIU,
       type: station.TYPE,
+      // tm:,
       stationId: station.STATIONID
     });
     
@@ -1274,4 +1399,30 @@ watch(() => props.config, async (config) => {
 .active :deep(.el-timeline-item__tail) {
   border-color: #409EFF;
 }
+.tooltip {
+  position: absolute;
+  background: #409EFF;
+  color: white;
+  padding: 8px 12px;
+  border-radius: 4px;
+  width: 100px;
+  font-size: 12px;
+  pointer-events: none;
+  display: none; /* 初始隐藏 */
+  box-shadow: 0 2px 4px #409EFF;
+  max-width: 200px;
+  z-index: 1000;
+}
+
+.tooltip-header {
+  font-weight: bold;
+  margin-bottom: 4px;
+  border-bottom: 1px solid rgba(255,255,255,0.3);
+  padding-bottom: 2px;
+}
+
+.tooltip-content {
+  font-size: 11px;
+  opacity: 0.9;
+}
 </style>

+ 33 - 1
ruoyi-ui/src/views/service/info/editModel.vue

@@ -212,6 +212,21 @@
                   </el-select> -->
                 </el-form-item>
               </el-col>
+              <el-col :span="6">
+                <el-form-item label="所属网关:" prop="gatewayId" style="">
+                  <el-select
+                      v-model="formAdd.gatewayId"
+                      style="width: 100%;margin-left: 0%;"
+                  >
+                    <el-option
+                        v-for="item in gatewayRouters"
+                        :key="item.value"
+                        :label="item.predicates"
+                        :value="item.id"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
             </el-row>
             <el-form-item label="排序:" prop="dcSort">
               <el-input-number v-model="formAdd.sort" :min="1" style="width: 15%;"/>
@@ -559,6 +574,21 @@
               </el-form-item>
             </el-col>
           </el-row>
+          <el-col :span="6">
+            <el-form-item label="所属网关:" prop="" style="">
+              <el-select
+                  v-model="formJi.gatewayId"
+                  style="width: 100%;margin-left: 0%;"
+              >
+                <el-option
+                    v-for="item in gatewayRouters"
+                    :key="item.value"
+                    :label="item.predicates"
+                    :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
           <el-row :gutter="48">
             <el-col :span="10">
               <el-form-item label="响应类型:" prop="" style="display: flex; align-items: center;">
@@ -1040,9 +1070,11 @@ const formAdd = ref({
   rqtype: '',
   rptype: '',
   intro: '',
-  mdid: ''
+  mdid: '',
+  gatewayId:''
 });
 const rulesAdd = reactive({
+  gatewayId: [{required: true, message: '必填', trigger: 'blur'}],
   mdid: [{required: true, message: '必填', trigger: 'blur'}],
   name: [{required: true, message: '必填', trigger: 'blur'}],
   url: [{required: true, message: '必填', trigger: 'blur'}],

+ 3 - 15
ruoyi-ui/src/views/standardization/bizDataShowConfig/show/GwTableTwo.vue

@@ -1,23 +1,11 @@
 <template>
   <div class="descriptions-container">
-    <!-- <el-descriptions :column="columnNumber" border>
+    <el-descriptions :column="columnNumber" border>
       <el-descriptions-item v-for="(item, index) in data" :key="index" :label="item.name">
         {{ item.value ? item.value : '-' }}
       </el-descriptions-item>
-    </el-descriptions> -->
-    <el-table
-        :data="data"
-        height="14vh"
-        :cell-style="{ padding: '5px' }"
-        :row-style="{ fontSize: '1rem', textAlign:'center' }"
-        border>
-      <el-table-column align="center" width="" prop="planName" label="方案名称"></el-table-column>
-      <el-table-column align="center" width="" prop="updateTime" label="更新时间">
-        <template #default="scope">
-
-        </template>
-      </el-table-column>
-    </el-table>
+    </el-descriptions>
+    
   </div>
   <!--  <table class="gw-descriptions-table">-->
   <!--    <tbody>-->

+ 63 - 60
ruoyi-ui/src/views/standardization/modelUsing/index.vue

@@ -21,6 +21,7 @@
       <el-option label="已发布" :value="2"/>
     </el-select>
   </div>
+  
   <div
       style="height: 70vh;width: 98%;margin-left:1%;background-color: transparent;margin-top: 1%;display: flex;flex-wrap: wrap;justify-content: flex-start;gap: 0.65%">
     <div v-for="(item,index) in modelList">
@@ -30,9 +31,11 @@
           <div style="margin-left: 8%;margin-top: 5%;width: 60%;">
             {{ item.appTitle }}
           </div>
-          <img style="width: 90px;height: 90px;margin-left: 0%;margin-top: 8%;border-radius: 12px;" :src="item.appIcon" v-if="item.appIcon"
+          <!-- <img style="width: 90px;height: 90px;margin-left: 0%;margin-top: 8%;border-radius: 12px;" :src="item.appIcon" v-if="item.appIcon!==null"
                alt="">
-          <img style="width: 90px;height: 90px;margin-left: 0%;margin-top: 8%;border-radius: 12px;" v-if="!item.appIcon" src="@/assets/images/defaultModel.png"
+          <img style="width: 90px;height: 90px;margin-left: 0%;margin-top: 8%;border-radius: 12px;" v-if="item.appIcon==null" src="@/assets/images/defaultModel.png"
+               alt=""> -->
+               <img style="width: 90px;height: 90px;margin-left: 0%;margin-top: 8%;border-radius: 12px;"  src="@/assets/images/defaultModel.png"
                alt="">
         </div>
         <div>
@@ -221,31 +224,31 @@ function handleBizDataShowConfig(data) {
 }
 
 function subEdit() {
-  // if (parFile.value) {
-  //   formAddref.value.validate(async (valid) => {
-  //     if (valid) {
-  //       editModeling(formAdd.value).then(res => {
-  //         if (res.code === 200) {
-  //           proxy.$message({
-  //             message: '修改成功',
-  //             type: 'success'
-  //           });
-  //           isContentVisible.value = false;
-  //           getList();
-  //         }
-  //       })
-  //     }
-  //   })
-  // } else {
+  if (parFile.value) {
+    formAddref.value.validate(async (valid) => {
+      if (valid) {
+        editModeling(formAdd.value).then(res => {
+          if (res.code === 200) {
+            proxy.$message({
+              message: '修改成功',
+              type: 'success'
+            });
+            isContentVisible.value = false;
+            getList();
+          }
+        })
+      }
+    })
+  } else {
     
-  // }
-  formAddref.value.validate(async (valid) => {
-    if (valid) {
-      nextTick(() => {
-        uploadRef.value.submit();
-      });
-    }
-  })
+  }
+  // formAddref.value.validate(async (valid) => {
+  //   if (valid) {
+  //     nextTick(() => {
+  //       uploadRef.value.submit();
+  //     });
+  //   }
+  // })
 }
 
 function delModel(item) {
@@ -271,40 +274,40 @@ function delModel(item) {
 }
 
 function submitAdd() {
-  // if (parFile.value) {
-  //   formAddref.value.validate(async (valid) => {
-  //     if (valid) {
-  //       addModeling(formAdd.value).then(res => {
-  //         if (res.code === 200) {
-  //           proxy.$message({
-  //             message: '新增成功',
-  //             type: 'success'
-  //           });
-  //           isContentVisible.value = false;
-  //           getList();
-  //         } else {
-  //           proxy.$message.error('新增失败');
-  //         }
-  //       })
-  //     }
-  //   })
+  if (parFile.value) {
+    formAddref.value.validate(async (valid) => {
+      if (valid) {
+        addModeling(formAdd.value).then(res => {
+          if (res.code === 200) {
+            proxy.$message({
+              message: '新增成功',
+              type: 'success'
+            });
+            isContentVisible.value = false;
+            getList();
+          } else {
+            proxy.$message.error('新增失败');
+          }
+        })
+      }
+    })
 
-  // } else {
-  //   formAddref.value.validate(async (valid) => {
-  //     if (valid) {
-  //       nextTick(() => {
-  //         uploadRef.value.submit();
-  //       });
-  //     }
-  //   })
-  // }
-  formAddref.value.validate(async (valid) => {
-    if (valid) {
-      nextTick(() => {
-        uploadRef.value.submit();
-      });
-    }
-  })
+  } else {
+    formAddref.value.validate(async (valid) => {
+      if (valid) {
+        nextTick(() => {
+          uploadRef.value.submit();
+        });
+      }
+    })
+  }
+  // formAddref.value.validate(async (valid) => {
+  //   if (valid) {
+  //     nextTick(() => {
+  //       uploadRef.value.submit();
+  //     });
+  //   }
+  // })
 }
 
 function handleFileSuccess(response, file, fileList) {
@@ -378,7 +381,7 @@ function getList() {
     modelList.value = res.rows
     total.value = res.total
     modelList.value.forEach(item=>{
-      item.appIcon = parUrl + item.appIcon
+      item.appIcon = item.appIcon
     })
     console.log(parUrl)
   })

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

@@ -1191,7 +1191,7 @@ async function startTest(){
   messages.value = []
   const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
   const host = window.location.host;
-  ws.value = createWebSocket('/message')
+  ws.value = createWebSocket('/websocket/message')
   ws.value.onopen = () => {
     connected.value = true;
   };