ソースを参照

添加两个模型成功展示、添加服务器监控网络监控

Lin Qilong 2 ヶ月 前
コミット
9bc1fe7760
23 ファイル変更886 行追加165 行削除
  1. 5 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java
  2. 19 0
      ruoyi-admin/src/main/java/com/ruoyi/web/core/config/ServerMonitorProperties.java
  3. 6 1
      ruoyi-admin/src/main/resources/application-dev.yml
  4. 6 1
      ruoyi-admin/src/main/resources/application-test.yml
  5. 4 1
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/BizDataShowConfigController.java
  6. 4 0
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/GatewayRoutes.java
  7. 2 1
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/BizDataShowConfigService.java
  8. 3 2
      ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/BizDataShowConfigServiceImpl.java
  9. 9 5
      ruoyi-api-patform/src/main/resources/mapper/interfaces/GatewayRoutesMapper.xml
  10. 2 2
      ruoyi-ui/.env.development
  11. 1 0
      ruoyi-ui/package.json
  12. 25 7
      ruoyi-ui/src/views/map/components/map.vue
  13. 20 1
      ruoyi-ui/src/views/map/hooks/dataSourceManager.js
  14. 119 5
      ruoyi-ui/src/views/map/hooks/popupContent.js
  15. 281 90
      ruoyi-ui/src/views/map/index.vue
  16. 235 25
      ruoyi-ui/src/views/map/utils/jsonToGeojson.js
  17. 27 1
      ruoyi-ui/src/views/monitor/server/index.vue
  18. 19 0
      ruoyi-ui/src/views/service/gateway/index.vue
  19. 1 1
      ruoyi-ui/src/views/service/info/shenhe.vue
  20. 42 4
      ruoyi-ui/src/views/standardization/bizDataShowConfig/list/index.vue
  21. 42 0
      ruoyi-ui/src/views/standardization/bizDataShowConfig/show/index.vue
  22. 6 8
      ruoyi-ui/src/views/standardization/modelUsing/index.vue
  23. 8 8
      ruoyi-ui/vite.config.js

+ 5 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java

@@ -1,9 +1,9 @@
 package com.ruoyi.web.controller.monitor;
 
-import com.google.common.collect.Lists;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.framework.web.domain.Server;
 import com.ruoyi.interfaces.service.ServerMonitorService;
+import com.ruoyi.web.core.config.ServerMonitorProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -22,6 +22,9 @@ public class ServerController {
     @Autowired
     private ServerMonitorService serverMonitorService;
 
+    @Autowired
+    private ServerMonitorProperties serverMonitorProperties;
+
     @PreAuthorize("@ss.hasPermi('monitor:server:list')")
     @GetMapping()
     public AjaxResult getInfo() throws Exception {
@@ -32,7 +35,7 @@ public class ServerController {
 
     @GetMapping("/list")
     public AjaxResult serverList() {
-        return AjaxResult.success(serverMonitorService.getServerMonitorData(Lists.newArrayList("http://172.16.196.174:18099")));
+        return AjaxResult.success(serverMonitorService.getServerMonitorData(serverMonitorProperties.getUrls()));
     }
 }
 

+ 19 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/core/config/ServerMonitorProperties.java

@@ -0,0 +1,19 @@
+package com.ruoyi.web.core.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import java.util.List;
+
+@Component
+@ConfigurationProperties(prefix = "server.monitor")
+public class ServerMonitorProperties {
+    private List<String> urls;
+
+    public List<String> getUrls() {
+        return urls;
+    }
+
+    public void setUrls(List<String> urls) {
+        this.urls = urls;
+    }
+}

+ 6 - 1
ruoyi-admin/src/main/resources/application-dev.yml

@@ -98,4 +98,9 @@ snail-job:
   token: SJ_bM5DeeFSAV6ltYwqqCKoA7v5HuwGH5Hr
 
 docker:
-  host: tcp://172.16.196.174:2375
+  host: tcp://172.16.196.174:2375
+
+server:
+  monitor:
+    urls:
+      - http://172.16.196.174:18099

+ 6 - 1
ruoyi-admin/src/main/resources/application-test.yml

@@ -104,4 +104,9 @@ snail-job:
   token: SJ_bM5DeeFSAV6ltYwqqCKoA7v5HuwGH5Hr
 
 docker:
-  host: tcp://localhost:2375
+  host: tcp://localhost:2375
+
+server:
+  monitor:
+    urls:
+      - http://localhost:18099

+ 4 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/controller/BizDataShowConfigController.java

@@ -3,6 +3,7 @@ package com.ruoyi.interfaces.controller;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.ServletUtils;
 import com.ruoyi.interfaces.domain.BizDataShowConfig;
 import com.ruoyi.interfaces.service.BizDataShowConfigService;
 import io.swagger.annotations.Api;
@@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author lql
@@ -71,7 +73,8 @@ public class BizDataShowConfigController extends BaseController {
 
     @GetMapping("/getBizData/{id}")
     public AjaxResult getBizDataById(@PathVariable String id) {
-        return AjaxResult.success(bizDataShowConfigService.getBizDataById(id));
+        Map<String, String> params = ServletUtils.getParamMap(ServletUtils.getRequest());
+        return AjaxResult.success(bizDataShowConfigService.getBizDataById(id, params));
     }
 
     @PostMapping("/getBizDataByConfig")

+ 4 - 0
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/domain/GatewayRoutes.java

@@ -24,4 +24,8 @@ public class GatewayRoutes implements Serializable {
 
     private String authQueryOptions;
 
+    private String tokenLocation;
+
+    private String tokenKey;
+
 }

+ 2 - 1
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/BizDataShowConfigService.java

@@ -5,6 +5,7 @@ import com.ruoyi.interfaces.domain.BizDataShowConfig;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 public interface BizDataShowConfigService {
 
@@ -28,7 +29,7 @@ public interface BizDataShowConfigService {
 
     boolean testApi(String apiConfig) throws IOException;
 
-    Object getBizDataById(String id);
+    Object getBizDataById(String id, Map<String, String> params);
 
     Object getBizDataByConfig(String config);
 }

+ 3 - 2
ruoyi-api-patform/src/main/java/com/ruoyi/interfaces/service/impl/BizDataShowConfigServiceImpl.java

@@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
 
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 @Service
@@ -57,10 +58,10 @@ public class BizDataShowConfigServiceImpl implements BizDataShowConfigService {
     }
 
     @Override
-    public Object getBizDataById(String id) {
+    public Object getBizDataById(String id, Map<String, String> params) {
         BizDataShowConfig bizDataShowConfig = this.getById(id);
         Optional.ofNullable(bizDataShowConfig).orElseThrow(() -> new CheckException("id.no"));
-        return BizDataApiRequest.requestByQueryOptions(bizDataShowConfig.getQueryOptions());
+        return BizDataApiRequest.requestByQueryOptions(bizDataShowConfig.getQueryOptions(), params);
     }
 
     @Override

+ 9 - 5
ruoyi-api-patform/src/main/resources/mapper/interfaces/GatewayRoutesMapper.xml

@@ -11,13 +11,15 @@
         <result column="STATUS" property="status"/>
         <result column="RESULT_SUCCESS_FORMAT" property="resultSuccessFormat"/>
         <result column="AUTH" property="auth"/>
+        <result column="TOKEN_LOCATION" property="tokenLocation"/>
+        <result column="TOKEN_KEY" property="tokenKey"/>
         <result column="AUTH_EXPIRATION_TIME" property="authExpirationTime"/>
         <result column="AUTH_QUERY_OPTIONS" property="authQueryOptions"/>
     </resultMap>
 
     <sql id="table_columns">
         ID
-        , SERVICE_NAME, URI, PREDICATES, FILTERS, STATUS, RESULT_SUCCESS_FORMAT, AUTH, AUTH_EXPIRATION_TIME, AUTH_QUERY_OPTIONS
+        , SERVICE_NAME, URI, PREDICATES, FILTERS, STATUS, RESULT_SUCCESS_FORMAT, AUTH, AUTH_EXPIRATION_TIME, AUTH_QUERY_OPTIONS,TOKEN_LOCATION,TOKEN_KEY
     </sql>
     <sql id="entity_properties">
         #{id},
@@ -29,7 +31,9 @@
         #{resultSuccessFormat},
         #{auth},
         #{authExpirationTime},
-        #{authQueryOptions}
+        #{authQueryOptions},
+        #{tokenLocation},
+        #{tokenKey}
     </sql>
 
     <!-- 使用like用法:columnName like concat('%',#columnName#,'%') -->
@@ -68,7 +72,6 @@
         <include refid="page_where"/>
     </select>
 
-
     <insert id="insert" parameterType="com.ruoyi.interfaces.domain.GatewayRoutes">
         insert into GATEWAY_ROUTES(
         <include refid="table_columns"/>
@@ -80,8 +83,7 @@
 
     <delete id="delete" parameterType="java.lang.String">
         delete
-        GATEWAY_ROUTES where ID =
-        #{id}
+        GATEWAY_ROUTES where ID = #{id}
     </delete>
 
     <update id="update" parameterType="com.ruoyi.interfaces.domain.GatewayRoutes">
@@ -96,6 +98,8 @@
             <if test="auth != null  and auth != ''">AUTH = #{auth},</if>
             <if test="authExpirationTime != null and authExpirationTime != ''">AUTH_EXPIRATION_TIME = #{authExpirationTime},</if>
             <if test="authQueryOptions != null and authQueryOptions != ''">AUTH_QUERY_OPTIONS = #{authQueryOptions},</if>
+            <if test="tokenLocation != null and tokenLocation != ''">TOKEN_LOCATION = #{tokenLocation},</if>
+            <if test="tokenKey != null and tokenKey != ''">TOKEN_KEY = #{tokenKey},</if>
         </trim>
         <where>ID = #{id}</where>
     </update>

+ 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.119:8082'
+VITE_DEV_PATH = 'http://localhost:8082'
+# VITE_DEV_PATH = 'http://192.168.2.119:8082'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 1 - 0
ruoyi-ui/package.json

@@ -57,6 +57,7 @@
     "ol-ext": "^4.0.35",
     "pinia": "2.1.7",
     "pinyin": "^4.0.0",
+    "proj4": "^2.19.10",
     "qs": "^6.14.0",
     "sortablejs": "^1.15.6",
     "vue": "^3.5.13",

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

@@ -17,6 +17,8 @@ import {createDynamicStyle} from "@/views/map/utils/styleParser.js";
 import {loadSource} from "../hooks/dataSourceManager.js";
 import bus from "@/utils/bus.js";
 import {getPopupContentByTemplate, loadPopupChartByTemplate} from "@/views/map/hooks/popupContent.js";
+import {isArray} from "@/utils/validate.js";
+import {getCenter} from "ol/extent.js";
 
 
 const props = defineProps({
@@ -63,7 +65,7 @@ const initMap = () => {
       maxZoom: 16,
       projection: 'EPSG:4326',
     }),
-    layers: [vecLayer, cvaLayer],
+    // layers: [vecLayer, cvaLayer],
     controls: defaultControls({
       zoom: false,//不显示放大放小按钮
       rotate: false,//不显示指北针控件
@@ -71,6 +73,13 @@ const initMap = () => {
       scaleLine: false,//不显示比例尺控件
     })
   });
+
+  mapChart.value.on('moveend', function (event) {
+    var center = mapChart.value.getView().getCenter(); // 获取中心点位置
+    var zoom = mapChart.value.getView().getZoom(); // 获取层级
+    console.log('Center:', center); // 转换为经纬度格式
+    console.log('Zoom:', zoom);
+  });
 };
 
 const addVectorLayer = async (config) => {
@@ -142,6 +151,12 @@ const showFeaturePopup = (feature, popupConfig) => {
   const properties = feature.getProperties();
   const geometry = feature.getGeometry();
   const coordinates = geometry.getCoordinates();
+  let center = coordinates
+
+  if (isArray(coordinates)) {
+    const extent = geometry.getExtent()
+    center = getCenter(extent); // 计算包围盒的中心点坐标
+  }
 
   // 创建弹窗元素
   const popupElement = document.createElement('div');
@@ -185,9 +200,10 @@ const showFeaturePopup = (feature, popupConfig) => {
     positioning: popupConfig?.positioning || "bottom-center",
     stopEvent: popupConfig?.stopEvent !== undefined ? popupConfig.stopEvent : true,
     offset: popupConfig?.offset || [0, -10],
+    autoPan: true
   });
 
-  popupOverlay.setPosition(coordinates);
+  popupOverlay.setPosition(center);
   mapChart.value.addOverlay(popupOverlay);
   popupOverlays.value.push(popupOverlay);
 
@@ -311,11 +327,13 @@ bus.on('show-map-position', ({latitude, longitude}) => {
 
 // 实时更新地图
 watch(() => props.config, async (config) => {
-  // 渲染配置
-  // 1. 跳转中心点
-  toCenter(config.center, config.zoom);
-  // 渲染图层
-  await loadLayers(config.layers);
+  if (config) {
+    // 渲染配置
+    // 1. 跳转中心点
+    toCenter(config.center, config.zoom);
+    // 渲染图层
+    await loadLayers(config.layers);
+  }
 }, {deep: true});
 </script>
 <style scoped lang="scss">

+ 20 - 1
ruoyi-ui/src/views/map/hooks/dataSourceManager.js

@@ -10,7 +10,26 @@ export async function loadSource(config) {
         case 'api':
             const response = await getBizDataByConfig(JSON.stringify(config.source));
             const rawData = response.data;
-            const features = jsonToFeatures(rawData, config.source.mapping);
+
+            // 如果geometry是字符串格式的GeoJSON,则需要解析
+            let processedData = rawData;
+            if (rawData && Array.isArray(rawData) && rawData.length > 0) {
+                processedData = rawData.map(item => {
+                    if (item.geometry && typeof item.geometry === 'string') {
+                        try {
+                            return {
+                                ...item,
+                                geometry: JSON.parse(item.geometry)
+                            };
+                        } catch (e) {
+                            console.warn('Failed to parse geometry string:', e);
+                        }
+                    }
+                    return item;
+                });
+            }
+
+            const features = jsonToFeatures(processedData, config.source.mapping);
             return new VectorSource({features})
         case 'geojson':
             return setupGeojsonSource(id, config);

+ 119 - 5
ruoyi-ui/src/views/map/hooks/popupContent.js

@@ -1,4 +1,5 @@
 import * as echarts from 'echarts';
+import {getBizDataByConfig} from "@/api/standardization/bizDataShowConfig.js";
 
 
 export function getPopupContentByTemplate(template, properties, popupElement) {
@@ -6,20 +7,32 @@ export function getPopupContentByTemplate(template, properties, popupElement) {
         case 'SCSSFM_POPUP':
             popupElement.style.width = '600px';
             return getPopupContentBySCSSFM(properties, popupElement);
+        case 'HPJ_POPUP':
+            popupElement.style.width = '600px';
+            return getPopupContentByHPJ(properties, popupElement);
+        case 'FLOOD_GRID_POPUP':
+            popupElement.style.width = '300px';
+            return getPopupContentByFloodGrid(properties, popupElement);
         default:
             return getPopupContentBySCSSFM(properties, popupElement);
     }
 }
 
-export function loadPopupChartByTemplate(template, properties) {
+export async function loadPopupChartByTemplate(template, properties) {
     let option = null;
     switch (template) {
         case 'SCSSFM_POPUP':
-            option = loadSCSSFMPopupChart(properties);
+            option = loadPopupChartBySCSSFM(properties);
+            break;
+        case 'HPJ_POPUP':
+            option = await loadPopupChartByHPJ(properties);
             break;
+        case 'FLOOD_GRID_POPUP':
+            return null;
         default:
-            option = loadSCSSFMPopupChart(properties);
+            option = loadPopupChartBySCSSFM(properties);
     }
+
     const popupChart = echarts.init(document.getElementById("popupChart"));
     popupChart.setOption(option);
 }
@@ -50,7 +63,56 @@ function getPopupContentBySCSSFM(properties) {
     return content;
 }
 
-function loadSCSSFMPopupChart(properties) {
+function getPopupContentByHPJ(properties) {
+    // 生成默认弹窗内容
+    let content = `
+      <div class="popup-header">
+        <h3>${properties['测站名称']}</h3>
+        <button class="popup-close" onclick="closePopup()">×</button>
+      </div>
+      <div class="popup-content">
+        <div class="popup-info">
+    `;
+    // 添加属性信息
+    Object.keys(properties).forEach(key => {
+        if (key !== 'geometry' && key !== 'name' && key !== 'stationName' && key !== '预报潮位') {
+            content += `<div class="info-item"><span class="label">${key}:</span><span class="value">${properties[key]}</span></div>`;
+        }
+    });
+    content += `
+        </div>
+        <div class="popup-chart">
+             <div id="popupChart" style="width: 100%;height: 200px;"></div>
+        </div>
+      </div>
+    `;
+    return content;
+}
+
+function getPopupContentByFloodGrid(properties) {
+    // 生成默认弹窗内容
+    let content = `
+      <div class="popup-header">
+        <h3>${properties['名称']}</h3>
+        <button class="popup-close" onclick="closePopup()">×</button>
+      </div>
+      <div class="popup-content">
+        <div class="popup-info">
+    `;
+    // 添加属性信息
+    Object.keys(properties).forEach(key => {
+        if (key !== 'geometry' && key !== 'name' && key !== 'stationName') {
+            content += `<div class="info-item"><span class="label">${key}:</span><span class="value">${properties[key]}</span></div>`;
+        }
+    });
+    content += `
+        </div>
+      </div>
+    `;
+    return content;
+}
+
+function loadPopupChartBySCSSFM(properties) {
     const data = properties['预报潮位']
     return {
         title: {
@@ -82,4 +144,56 @@ function loadSCSSFMPopupChart(properties) {
             }
         ]
     };
-}
+}
+
+async function loadPopupChartByHPJ(properties) {
+    const config = {
+        "url": "http://localhost:8081/hpj_api/swzz/SWZZ_MODE_DD_SOLUTION/MODE_BDMS_PREDICTSel",
+        "method": "POST",
+        "body": [
+            {"id": 1, "key": "SOLUTIONID", "value": "20251101102203557131", "type": "", "title": ""},
+            {"id": 2, "key": "STCD", "value": properties['测站编码'], "type": "", "title": ""},
+        ],
+        "headers": [
+            {"id": 1, "key": "Username", "value": "swzzswhy", "type": "", "title": ""},
+        ],
+        "response": [
+            {"id": 78, "key": "YMDHM", "value": "时间", "type": "string", "title": ""},
+            {"id": 79, "key": "DATA", "value": "水位", "type": "", "title": ""},
+        ],
+        "responseResolution": "data"
+    }
+    const data = await getBizDataByConfig(JSON.stringify(config)).then(res => res.data)
+    return {
+        title: {
+            text: '水位预报'
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        grid: {
+            left: '2%',
+            right: '2%',
+            top: '14%',
+            bottom: '2%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            data: data.map(item => item.YMDHM.substring(5, 16).replace(' ', '\n'))
+        },
+        yAxis: {
+            type: 'value',
+            name: 'm'
+        },
+        series: [
+            {
+                data: data.map(item => item.DATA?.toFixed(2)),
+                showSymbol: false,
+                type: 'line',
+                smooth: true
+            }
+        ]
+    };
+}
+

+ 281 - 90
ruoyi-ui/src/views/map/index.vue

@@ -13,115 +13,306 @@ import {useRoute} from "vue-router";
 
 const route = useRoute();
 const mapRef = ref(null);
-const mapConfig = ref({
-  zoom: 9,
-  center: [116.397428, 39.90923],
-  layers: [
-    {
-      id: 'weather-stations',
-      type: 'vector', // 指定为矢量图层
-      name: '行政区划面图层',
-      source: {
-        type: 'api', // 数据源类型为API
-        url: 'http://localhost:8082/SCSSFM/getCalResultsByStation',
-        method: 'POST',
-        body: [
-          {key: 'projectId', value: '230103', type: 'string'},
-          {key: 'forecastSchemeId', value: '2301031', type: 'string'},
-          {key: 'calSchemeIds', value: ["20421b55-5383-4ef0-a"], type: 'array'},
-        ],
-        headers: [
-          {
-            key: 'authorization',
-            value: 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjBhOTE3ZDdhLTUwODktNDg1MC05YTUyLTk1NzRjZWUzYWU5ZSJ9.QuFdSq-_mLFwOlM249-ledRlM4U2_qIRVfdOzGIOnf38XY-QXyaP0k-me2gT1wf5LCjOW0z-zJnO-SnNo78eOg',
-            type: 'string'
+const mapConfig = ref(null);
+const bizDataShowConfigList = ref([]);
+
+const maps = {
+  "25": {
+    zoom: 9,
+    center: [116.397428, 39.90923],
+    layers: [
+      {
+        id: 'weather-stations',
+        type: 'vector', // 指定为矢量图层
+        name: '行政区划面图层',
+        source: {
+          type: 'api', // 数据源类型为API
+          url: 'http://localhost:8082/SCSSFM/getCalResultsByStation',
+          method: 'POST',
+          body: [
+            {key: 'projectId', value: '230103', type: 'string'},
+            {key: 'forecastSchemeId', value: '2301031', type: 'string'},
+            {key: 'calSchemeIds', value: ["20421b55-5383-4ef0-a"], type: 'array'},
+          ],
+          headers: [
+            {
+              key: 'authorization',
+              value: 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjBhOTE3ZDdhLTUwODktNDg1MC05YTUyLTk1NzRjZWUzYWU5ZSJ9.QuFdSq-_mLFwOlM249-ledRlM4U2_qIRVfdOzGIOnf38XY-QXyaP0k-me2gT1wf5LCjOW0z-zJnO-SnNo78eOg',
+              type: 'string'
+            },
+          ],
+          responseResolution: "$.data",
+          // 定义数据映射规则
+          mapping: {
+            // 指定哪些字段包含几何信息(经度、纬度)
+            geometry: {
+              type: 'Point', // 几何类型
+              coordinates: {
+                longitude: 'lgtd', // 接口中的经度字段名
+                latitude: 'lttd'   // 接口中的纬度字段名
+              }
+            },
+            textField: 'stationName',
+            // 定义要保留的属性字段
+            properties: {
+              '测站编码': 'stationCode',     // 目标属性名: 源数据字段名
+              '测站名称': 'stationName',
+              '预报最大水位': 'calSchemeInfos[0].results[0].maxData.value',
+              '预报最大水位时间': 'calSchemeInfos[0].results[0].maxData.tm',
+              '预报最小水位': 'calSchemeInfos[0].results[0].minData.value',
+              '预报最小水位时间': 'calSchemeInfos[0].results[0].minData.tm',
+              '预报平均水位': 'calSchemeInfos[0].results[0].average',
+              '警戒水位': 'alarmValue',
+              '预报潮位': 'calSchemeInfos[0].results[0].datas',
+            }
           },
-        ],
-        responseResolution: "$.data",
-        // 定义数据映射规则
-        mapping: {
-          // 指定哪些字段包含几何信息(经度、纬度)
-          geometry: {
-            type: 'Point', // 几何类型
-            coordinates: {
-              longitude: 'lgtd', // 接口中的经度字段名
-              latitude: 'lttd'   // 接口中的纬度字段名
+          // 轮询更新(可选)
+          refreshInterval: 30000 // 30秒刷新一次
+        },
+        visible: true,
+        zIndex: 1000,
+        style: {
+          // 点要素样式
+          point: {
+            image: {
+              type: 'icon',           // circle|icon
+              // type: 'circle',           // circle|icon
+              name: 'red_trangle',
+              radius: 8,
+              fill: {color: '#FF5722'},
+              stroke: {color: '#fff', width: 2}
+            },
+            text: {
+              field: 'stationName',
+              font: '12px Arial',
+              fill: {color: '#000000'},
+              stroke: {color: '#ffffff', width: 2},
+              offsetY: -20
             }
           },
-          textField: 'stationName',
-          // 定义要保留的属性字段
-          properties: {
-            '测站编码': 'stationCode',     // 目标属性名: 源数据字段名
-            '测站名称': 'stationName',
-            '预报最大水位': 'calSchemeInfos[0].results[0].maxData.value',
-            '预报最大水位时间': 'calSchemeInfos[0].results[0].maxData.tm',
-            '预报最小水位': 'calSchemeInfos[0].results[0].minData.value',
-            '预报最小水位时间': 'calSchemeInfos[0].results[0].minData.tm',
-            '预报平均水位': 'calSchemeInfos[0].results[0].average',
-            '警戒水位': 'alarmValue',
-            '预报潮位': 'calSchemeInfos[0].results[0].datas',
+          // 线要素样式
+          line: {
+            stroke: {
+              color: '#0066ff',
+              width: 3,
+              lineDash: [5, 5]
+            }
+          },
+          // 面要素样式
+          polygon: {
+            fill: {color: 'rgba(0, 102, 255, 0.2)'},
+            stroke: {color: '#0066ff', width: 2}
           }
         },
-        // 轮询更新(可选)
-        refreshInterval: 30000 // 30秒刷新一次
+        events: {
+          click: {
+            action: 'popup',           // 点击触发弹窗
+            popupConfig: {
+              // 弹窗模板ID
+              template: 'SCSSFM_POPUP',
+              offset: [0, -20]
+            }
+          },
+          // hover: {
+          //   action: 'highlight'       // 悬停高亮
+          // }
+        }
       },
-      visible: true,
-      zIndex: 1000,
-      style: {
-        // 点要素样式
-        point: {
-          image: {
-            type: 'icon',           // circle|icon
-            // type: 'circle',           // circle|icon
-            name: 'red_trangle',
-            radius: 8,
-            fill: {color: '#FF5722'},
-            stroke: {color: '#fff', width: 2}
+    ]
+  },
+  "26": {
+    zoom: 11,
+    center: [121.52720881780915, 31.297834087410234],
+    layers: [
+      {
+        id: 'weather-stations',
+        type: 'vector', // 指定为矢量图层
+        name: '行政区划面图层',
+        source: {
+          type: 'api', // 数据源类型为API
+          url: 'http://localhost:8081/sh_data_platform/service/api/service/api/fxt/getPlanGridMaxWaterDepth?client_id=a8f4f426a8684caf8a63051385aa1836&planId=34046&includeGeom=true&pageNumber=1&pageSize=1000',
+          method: 'GET',
+          responseResolution: "$.result",
+          // 定义数据映射规则
+          mapping: {
+            // 指定哪些字段包含几何信息(经度、纬度)
+            geometry: {
+              type: 'MultiPolygon', // 几何类型
+              coordinates: {
+                geometry: 'geometry', // 接口中的经度字段名
+              }
+            },
+            textField: 'gridName',
+            // 定义要保留的属性字段
+            properties: {
+              '名称': 'gridName',     // 目标属性名: 源数据字段名
+              '地区': 'district',
+              '所属村镇': 'town',
+              '预报最大水深': 'maxWaterDepth',
+              '预报最大水深时间': 'maxWaterDepthTime',
+            }
           },
-          text: {
-            field: 'stationName',
-            font: '12px Arial',
-            fill: {color: '#000000'},
-            stroke: {color: '#ffffff', width: 2},
-            offsetY: -20
-          }
+          // 轮询更新(可选)
+          refreshInterval: 30000 // 30秒刷新一次
         },
-        // 线要素样式
-        line: {
-          stroke: {
-            color: '#0066ff',
-            width: 3,
-            lineDash: [5, 5]
+        visible: true,
+        zIndex: 1000,
+        style: {
+          // 点要素样式
+          point: {
+            image: {
+              type: 'icon',           // circle|icon
+              // type: 'circle',           // circle|icon
+              name: 'red_trangle',
+              radius: 8,
+              fill: {color: '#FF5722'},
+              stroke: {color: '#fff', width: 2}
+            },
+            text: {
+              field: 'stationName',
+              font: '12px Arial',
+              fill: {color: '#000000'},
+              stroke: {color: '#ffffff', width: 2},
+              offsetY: -20
+            }
+          },
+          // 线要素样式
+          line: {
+            stroke: {
+              color: '#0066ff',
+              width: 3,
+              lineDash: [5, 5]
+            }
+          },
+          // 面要素样式
+          polygon: {
+            fill: {color: 'rgba(0, 102, 255, 0.2)'},
+            stroke: {color: '#0066ff', width: 2}
+          },
+          multipolygon: {
+            fill: {color: 'rgba(0, 102, 255, 0.2)'},
+            stroke: {color: '#0066ff', width: 2}
           }
         },
-        // 面要素样式
-        polygon: {
-          fill: {color: 'rgba(0, 102, 255, 0.2)'},
-          stroke: {color: '#0066ff', width: 2}
+        events: {
+          click: {
+            action: 'popup',           // 点击触发弹窗
+            popupConfig: {
+              // 弹窗模板ID
+              template: 'FLOOD_GRID_POPUP',
+              offset: [0, -20]
+            }
+          },
+          // hover: {
+          //   action: 'highlight'       // 悬停高亮
+          // }
         }
       },
-      events: {
-        click: {
-          action: 'popup',           // 点击触发弹窗
-          popupConfig: {
-            // 弹窗模板ID
-            template: 'SCSSFM_POPUP',
-            offset: [0, -20]
+    ]
+  },
+  "27": {
+    zoom: 10,
+    center: [121.49574023547144, 31.084153451834073],
+    layers: [
+      {
+        id: 'weather-stations',
+        type: 'vector', // 指定为矢量图层
+        name: '行政区划面图层',
+        source: {
+          type: 'api', // 数据源类型为API
+          url: 'http://localhost:8081/hpj_api/swzz/SWZZ_MODE_DD_SOLUTION/SWZZ_MODELSINGRESULT',
+          method: 'POST',
+          body: [
+            {key: 'SOLUTIONID', value: '20251101102203557131', type: 'string'},
+            {key: 'DATA_TYPE', value: '1', type: 'string'},
+          ],
+          headers: [
+            {key: 'Username', value: 'swzzswhy', type: 'string'},
+            {key: 'Content-Type', value: 'application/json; charset=UTF-8', type: 'string'},
+          ],
+          responseResolution: "$.data",
+          // 定义数据映射规则
+          mapping: {
+            // 指定哪些字段包含几何信息(经度、纬度)
+            geometry: {
+              type: 'Point', // 几何类型
+              coordinates: {
+                longitude: 'LGTD', // 接口中的经度字段名
+                latitude: 'LTTD'   // 接口中的纬度字段名
+              },
+            },
+            textField: 'STNM',
+            // 定义要保留的属性字段
+            properties: {
+              '测站编码': 'STCD',     // 目标属性名: 源数据字段名
+              '测站名称': 'STNM',
+              '预报最大水位': 'MAXZ',
+              '预报最大水位时间': 'MAXTM',
+              '预报最小水位': 'MINZ',
+              '预报最小水位时间': 'MINTM',
+            }
+          },
+          // 轮询更新(可选)
+          refreshInterval: 30000 // 30秒刷新一次
+        },
+        visible: true,
+        zIndex: 1000,
+        style: {
+          // 点要素样式
+          point: {
+            image: {
+              type: 'icon',           // circle|icon
+              // type: 'circle',           // circle|icon
+              name: 'red_trangle',
+              radius: 8,
+              fill: {color: '#FF5722'},
+              stroke: {color: '#fff', width: 2}
+            },
+            text: {
+              field: 'STNM',
+              font: '12px Arial',
+              fill: {color: '#000000'},
+              stroke: {color: '#ffffff', width: 2},
+              offsetY: -20
+            }
+          },
+          // 线要素样式
+          line: {
+            stroke: {
+              color: '#0066ff',
+              width: 3,
+              lineDash: [5, 5]
+            }
+          },
+          // 面要素样式
+          polygon: {
+            fill: {color: 'rgba(0, 102, 255, 0.2)'},
+            stroke: {color: '#0066ff', width: 2}
           }
         },
-        // hover: {
-        //   action: 'highlight'       // 悬停高亮
-        // }
-      }
-    },
-  ]
-});
-const bizDataShowConfigList = ref([]);
+        events: {
+          click: {
+            action: 'popup',           // 点击触发弹窗
+            popupConfig: {
+              // 弹窗模板ID
+              template: 'HPJ_POPUP',
+              offset: [0, -20]
+            }
+          },
+          // hover: {
+          //   action: 'highlight'       // 悬停高亮
+          // }
+        }
+      },
+    ]
+  }
+}
 
 function getBizDataConfigList() {
   getBizDataShowConfigList({appId: route.params.id}).then(res => {
     bizDataShowConfigList.value = res.data;
   })
+  mapConfig.value = maps[route.params.id]
 }
 
 onMounted(() => {

+ 235 - 25
ruoyi-ui/src/views/map/utils/jsonToGeojson.js

@@ -3,54 +3,258 @@ import Feature from 'ol/Feature';
 import Point from 'ol/geom/Point';
 import {filterData} from "@/utils/data.js";
 import {isNumber} from "@/utils/validate.js";
+import {transform} from "ol/proj";
+import {register} from "ol/proj/proj4";
+import proj4 from 'proj4';
+import {MultiPolygon} from "ol/geom";
+import {GeoJSON} from "ol/format";
+
+// 定义自定义投影(在模块级别初始化,避免重复注册)
+let projectionInitialized = false;
+
+function initCustomProjection() {
+    if (projectionInitialized) return;
+
+    // 定义北京1954坐标系的Proj4字符串
+    const beijing1954Proj4 = `
+        +proj=tmerc 
+        +lat_0=0 
+        +lon_0=121.4671519444444 
+        +k=1 
+        +x_0=8 
+        +y_0=-3457143.04 
+        +ellps=krass 
+        +towgs84=15.8,-154.4,-82.3,0,0,0,0 
+        +units=m 
+        +no_defs
+    `.replace(/\s+/g, ' ').trim();
+
+    // 注册自定义投影
+    proj4.defs('CUSTOM:Shanghai_Beijing54', beijing1954Proj4);
+
+    // 注册到OpenLayers
+    register(proj4);
+
+    projectionInitialized = true;
+    console.log('自定义投影注册完成');
+}
+
+// 坐标转换函数
+function convertCustomToWGS84(lgtd, lttd) {
+    if (!projectionInitialized) {
+        initCustomProjection();
+    }
+
+    try {
+        const sourceProjection = 'CUSTOM:Shanghai_Beijing54';
+        const targetProjection = 'EPSG:4326';
+
+        // 转换坐标
+        return transform([lgtd, lttd], sourceProjection, targetProjection);
+    } catch (error) {
+        console.error('坐标转换失败:', error);
+        return null;
+    }
+}
 
 export function jsonToFeatures(data, mappingConfig) {
+    if (!data || !mappingConfig) {
+        return [];
+    }
+
+    // 预先初始化投影(如果需要)
+    if (data.length > 0 && mappingConfig.geometry) {
+        const sampleItem = data[0];
+        const {coordinates} = mappingConfig.geometry;
+        const lng = sampleItem[coordinates.longitude];
+        const lat = sampleItem[coordinates.latitude];
+
+        // 如果样本坐标需要转换,则初始化投影
+        if (!isValidCoordinate(lng, lat)) {
+            initCustomProjection();
+        }
+    }
+
     const features = [];
-    data.forEach(item => {
-        // 构建几何图形
-        const geometry = buildGeometry(item, mappingConfig.geometry);
-        // 构建属性
-        const properties = buildProperties(item, mappingConfig.properties);
-        const feature = new Feature({
-            geometry: geometry,
-            name: item[mappingConfig.textField],
-            ...properties,  // 直接将属性展开到 Feature 中
-        });
-        features.push(feature)
+    data.forEach((item, index) => {
+        try {
+            // 构建几何图形
+            const geometry = buildGeometry(item, mappingConfig.geometry);
+            if (!geometry) {
+                console.warn(`无法创建几何图形,跳过第${index}个要素:`, item);
+                return;
+            }
+            // 构建属性
+            const properties = buildProperties(item, mappingConfig.properties);
+            const feature = new Feature({
+                geometry: geometry,
+                name: item[mappingConfig.textField],
+                ...properties,
+            });
+            features.push(feature);
+        } catch (error) {
+            console.warn(`创建第${index}个要素时出错:`, error, item);
+        }
     });
-    return features
+    return features;
 }
 
+// 修改buildGeometry函数以支持多面要素
 function buildGeometry(item, geomMapping) {
+    if (!geomMapping) {
+        console.warn('Missing geometry mapping configuration');
+        return null;
+    }
+
     const {type, coordinates} = geomMapping;
+    switch (type) {
+        case 'Point':
+            return buildPointGeometry(item, coordinates);
+        case 'MultiPolygon':
+            return buildMultiPolygonGeometry(item, coordinates);
+        default:
+            console.warn('Unsupported geometry type:', type);
+            return null;
+    }
+}
 
-    if (type === 'Point') {
-        const lngField = coordinates.longitude;
-        const latField = coordinates.latitude;
-        // 使用 OpenLayers Point 类创建几何对象
-        return new Point([
-            parseFloat(item[lngField]),
-            parseFloat(item[latField])
-        ]);
+// function buildGeometry(item, geomMapping) {
+//     if (!geomMapping || geomMapping.type !== 'Point') {
+//         console.warn('不支持的几何类型或缺少几何映射配置');
+//         return null;
+//     }
+//
+//     const {coordinates} = geomMapping;
+//     const lngField = coordinates.longitude;
+//     const latField = coordinates.latitude;
+//
+//     if (!lngField || !latField) {
+//         console.warn('缺少经纬度字段配置');
+//         return null;
+//     }
+//
+//     let lng = parseFloat(item[lngField]);
+//     let lat = parseFloat(item[latField]);
+//
+//     if (isNaN(lng) || isNaN(lat)) {
+//         console.warn('坐标值不是有效数字:', lng, lat);
+//         return null;
+//     }
+//
+//     // 检查坐标是否在WGS84范围内,如果不是则进行坐标转换
+//     if (!isValidCoordinate(lng, lat)) {
+//         const convertedCoord = convertCustomToWGS84(lng, lat);
+//         if (convertedCoord) {
+//             [lng, lat] = convertedCoord;
+//         } else {
+//             console.warn('坐标转换失败,使用原始坐标');
+//         }
+//     }
+//
+//     // 最终验证坐标有效性
+//     if (!isValidCoordinate(lng, lat)) {
+//         console.warn('最终坐标无效:', lng, lat);
+//         return null;
+//     }
+//
+//     return new Point([lng, lat]);
+// }
+
+function buildPointGeometry(item, coordinates) {
+    const lngField = coordinates.longitude;
+    const latField = coordinates.latitude;
+
+    if (!lngField || !latField) {
+        console.warn('Missing longitude or latitude field configuration');
+        return null;
+    }
+
+    let lng = parseFloat(item[lngField]);
+    let lat = parseFloat(item[latField]);
+
+    if (isNaN(lng) || isNaN(lat)) {
+        console.warn('Coordinate values are not valid numbers:', lng, lat);
+        return null;
+    }
+
+    // Check if coordinates need transformation
+    if (!isValidCoordinate(lng, lat)) {
+        const convertedCoord = convertCustomToWGS84(lng, lat);
+        if (convertedCoord) {
+            [lng, lat] = convertedCoord;
+        } else {
+            console.warn('Coordinate conversion failed, using original coordinates');
+        }
+    }
+
+    // Final validation
+    if (!isValidCoordinate(lng, lat)) {
+        console.warn('Final coordinates invalid:', lng, lat);
+        return null;
+    }
+
+    return new Point([lng, lat]);
+}
+
+function buildMultiPolygonGeometry(item, coordinates) {
+    const geomField = coordinates.geometry;
+
+    if (!geomField) {
+        console.warn('Missing geometry field configuration');
+        return null;
+    }
+
+    let geometryData = item[geomField];
+    if (geometryData !== null && typeof geometryData === 'string') {
+        try {
+            geometryData = JSON.parse(geometryData);
+        } catch (e) {
+            console.warn('Failed to parse geometry data:', e);
+            return null;
+        }
+    }
+
+    // 确保 geometryData 是有效的 GeoJSON 几何对象
+    if (!geometryData || !geometryData.type || !geometryData.coordinates) {
+        console.warn('Invalid geometry data structure');
+        return null;
+    }
+    // return new MultiPolygon(geometryData.coordinates)
+    // return new MultiPolygon([
+    //     [ // 外环
+    //         [121.628914715, 31.288878176], [121.632536148, 31.290276026], [121.635910927, 31.286463955], [121.631588814, 31.284766503],
+    //         [121.628914715, 31.288878176]
+    //     ],
+    //     [ // 另一个外环或内环,如果需要的话
+    //         [121.546405806, 31.269162807], [121.543034769, 31.266598421], [121.541532267, 31.269498732], [121.543824542, 31.271765011],
+    //         [121.546405806, 31.269162807]
+    //     ]
+    // ])
+
+    try {
+        // 使用 OpenLayers 的 GeoJSON 格式读取器来创建几何对象
+        const geojsonFormat = new GeoJSON();
+        return geojsonFormat.readGeometry(geometryData);
+    } catch (e) {
+        console.warn('Failed to create geometry from GeoJSON:', e);
+        return null;
     }
-    // 可以扩展支持LineString、Polygon等
 }
 
 function buildProperties(item, mapping) {
     const properties = {};
 
-    // 如果没有提供映射配置,返回空属性对象
     if (!mapping) {
         return properties;
     }
 
-    // 遍历映射配置,将原始数据字段映射到属性中
     Object.keys(mapping).forEach(key => {
         const sourceKey = mapping[key];
         let value = filterData(item, sourceKey, "auto");
-        if (value) {
+
+        if (value !== undefined && value !== null) {
             if (isNumber(value)) {
-                value = value.toFixed(2)
+                value = parseFloat(value).toFixed(2);
             }
             properties[key] = value;
         }
@@ -58,3 +262,9 @@ function buildProperties(item, mapping) {
 
     return properties;
 }
+
+function isValidCoordinate(lng, lat) {
+    return !isNaN(lng) && !isNaN(lat) &&
+        lng >= -180 && lng <= 180 &&
+        lat >= -90 && lat <= 90;
+}

+ 27 - 1
ruoyi-ui/src/views/monitor/server/index.vue

@@ -14,7 +14,7 @@
           </div>
         </el-col>
         <el-col :span="18">
-          <div style="height: 25vh;">
+          <div style="height: 20vh;">
             <gw-echart ref="cpuChartRef"></gw-echart>
           </div>
         </el-col>
@@ -80,6 +80,22 @@
         </el-table-column>
       </el-table>
     </el-card>
+    <el-card class="model-item">
+      <template #header>
+        <MessageBox style="width: 1em; height: 1em; vertical-align: middle;"/>
+        <span style="vertical-align: middle;"> 网络</span>
+      </template>
+      <el-table :data="networkData" highlight-current-row>
+        <el-table-column label="序号" width="60" type="index"></el-table-column>
+        <el-table-column label="服务器" align="center" prop="name" :show-overflow-tooltip="true"/>
+        <el-table-column label="总下载速度(Mbps)" align="center" prop="total_download_mbps"
+                         :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column label="总上传速度(Mbps)" align="center" prop="total_upload_mbps"
+                         :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column label="网络延迟(ms)" align="center" prop="latency_ms"
+                         :show-overflow-tooltip="true"></el-table-column>
+      </el-table>
+    </el-card>
   </div>
 </template>
 <script setup>
@@ -92,6 +108,7 @@ const serverList = ref([])
 const cpuChartRef = ref(null)
 const memoryData = ref([])
 const diskData = ref([])
+const networkData = ref([])
 
 function getList() {
   proxy.$modal.loading("正在加载服务监控数据,请稍候!");
@@ -110,6 +127,15 @@ function getList() {
       a.name = item.system.primary_ip
       return a
     })
+    networkData.value = serverList.value.map(item => {
+      debugger
+      return {
+        name: item.system.primary_ip,
+        total_download_mbps: (item.network.speed.total_download_mbps || 0).toFixed(2),
+        total_upload_mbps: (item.network.speed.total_upload_mbps || 0).toFixed(2),
+        latency_ms: (item.network.quality[0].latency_ms || 0).toFixed(2),
+      }
+    })
   }).catch(() => {
     proxy.$modal.closeLoading();
   });

+ 19 - 0
ruoyi-ui/src/views/service/gateway/index.vue

@@ -138,6 +138,23 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="TOKEN位置">
+              <el-select v-model="form.tokenLocation" placeholder="请选择TOKEN位置">
+                <el-option label="参数(param)" value="param"></el-option>
+                <el-option label="请求体(body)" value="body"></el-option>
+                <el-option label="请求头(header)" value="header"></el-option>
+                <el-option label="Cookie" value="cookie"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="TOKEN KEY值">
+              <el-input v-model="form.tokenKey" placeholder="请输入TOKEN KEY值"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
         <el-row v-if="form.auth == 1">
           <el-col :span="24" style="max-height: 300px;overflow: auto;">
             <biz-data-show-config-api v-model="form.authQueryOptions"></biz-data-show-config-api>
@@ -241,6 +258,8 @@ function reset() {
     status: "0",
     auth: "0",
     authExpirationTime: null,
+    tokenLocation: "header",
+    tokenKey: "Authorization",
     authQueryOptions: null
   };
   proxy.resetForm("userRef");

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

@@ -854,7 +854,7 @@ async function handleNodeClick(node,data,event){
     if(res.data){
       parTree.value = res.data
       tableData.value = res.data.serviceList
-      const allright = true
+      let allright = true
       tableData.value.forEach(item=>{
         if(item.senState!==1){
           allright = false

+ 42 - 4
ruoyi-ui/src/views/standardization/bizDataShowConfig/list/index.vue

@@ -63,6 +63,7 @@
         <el-radio label="map">跳转地图</el-radio>
         <el-radio label="detail">查看详情</el-radio>
         <el-radio label="link">跳转链接</el-radio>
+        <el-radio label="paramMapping">参数映射</el-radio>
       </el-radio-group>
     </el-form-item>
 
@@ -71,7 +72,7 @@
       <el-form-item label="经纬度字段">
         <el-row :gutter="10">
           <el-col :span="12">
-            <el-select v-model="form.clickAction.longitudeField" placeholder="请选择经度字段" style="width: 100%;">
+            <el-select v-model="form.clickAction.longitudeField" placeholder="请选择经度字段" style="width: 100px;">
               <el-option
                   v-for="column in columns"
                   :key="column.key"
@@ -80,7 +81,7 @@
             </el-select>
           </el-col>
           <el-col :span="12">
-            <el-select v-model="form.clickAction.latitudeField" placeholder="请选择纬度字段" style="width: 100%;">
+            <el-select v-model="form.clickAction.latitudeField" placeholder="请选择纬度字段" style="width: 100px;">
               <el-option
                   v-for="column in columns"
                   :key="column.key"
@@ -101,6 +102,29 @@
       </el-form-item>
     </template>
 
+    <el-form-item v-if="form.clickAction.type === 'paramMapping'" label="参数映射">
+      <el-table :data="form.clickAction.paramMapping" style="width: 100%">
+        <el-table-column label="参数名">
+          <template #default="scope">
+            <el-input v-model="scope.row.paramName" placeholder="请输入参数名"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="字段值">
+          <template #default="scope">
+            <el-select v-model="scope.row.fieldKey" placeholder="请选择字段">
+              <el-option v-for="column in columns" :key="column.key" :label="column.value" :value="column.key"/>
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button @click="removeParamMapping(scope.$index)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-button @click="addParamMapping">添加参数映射</el-button>
+    </el-form-item>
+
     <!-- 详情配置项 -->
     <el-form-item v-if="form.clickAction.type === 'detail'" label="详情页面路径">
       <el-input v-model="form.clickAction.detailPath" placeholder="请输入详情页面路径"/>
@@ -153,11 +177,24 @@ const form = ref({
     mapTitleField: '',     // 地图标题字段
     detailPath: '',        // 详情页面路径
     linkUrl: '',           // 链接地址
-    linkTarget: '_blank'   // 链接目标
+    linkTarget: '_blank',   // 链接目标
+    paramMapping: []   // 链接目标
   }
 })
 const rules = ref({})
 
+function addParamMapping() {
+  form.value.clickAction.paramMapping.push({
+    paramName: '',
+    fieldKey: ''
+  })
+}
+
+function removeParamMapping(index) {
+  form.value.clickAction.paramMapping.splice(index, 1)
+}
+
+
 watch(() => props.modelValue, modelValue => {
   if (modelValue) {
     const parsed = JSON.parse(modelValue)
@@ -180,7 +217,8 @@ watch(() => props.modelValue, modelValue => {
         mapTitleField: parsed.clickAction?.mapTitleField || '',
         detailPath: parsed.clickAction?.detailPath || '',
         linkUrl: parsed.clickAction?.linkUrl || '',
-        linkTarget: parsed.clickAction?.linkTarget || '_blank'
+        linkTarget: parsed.clickAction?.linkTarget || '_blank',
+        paramMapping: parsed.clickAction?.paramMapping || []
       }
     }
   }

+ 42 - 0
ruoyi-ui/src/views/standardization/bizDataShowConfig/show/index.vue

@@ -120,6 +120,12 @@ function loadData(config) {
 async function loadTableData(config, page = 1) {
   try {
     const params = {}
+
+    // 添加从外部传入的参数
+    if (config.value && config.value.params) {
+      Object.assign(params, config.value.params);
+    }
+
     if (paginationConfig.value.enabled) {
       params[paginationConfig.value.pageNumParam || 'pageNum'] = page
       params[paginationConfig.value.pageSizeParam || 'pageSize'] = paginationConfig.value.pageSize || 10
@@ -195,6 +201,15 @@ function handleRowClick({row, column, event}) {
     if (clickConfig.value.linkUrl) {
       window.open(clickConfig.value.linkUrl, clickConfig.value.linkTarget || '_blank')
     }
+  } else if (clickConfig.value.type === 'paramMapping') {
+    const params = {}
+    form.value.clickAction.paramMapping.forEach(mapping => {
+      if (mapping.paramName && mapping.fieldKey) {
+        params[mapping.paramName] = row[mapping.fieldKey]
+      }
+    })
+    // 发送参数
+    bus.emit('load-params', params)
   }
 }
 
@@ -216,6 +231,33 @@ function getValueByKey(obj, key) {
   return value
 }
 
+bus.on('load-params', (params) => {
+  // 修改配置里的参数
+  if (params && typeof params === 'object') {
+    // 遍历传入的参数并更新到当前配置中
+    Object.keys(params).forEach(key => {
+      // 这里可以将参数应用到当前组件的配置中
+      // 例如,如果需要更新分页参数或其他配置项
+      if (config.value) {
+        // 如果配置中已有该参数,则更新它
+        if (config.value[key] !== undefined) {
+          config.value[key] = params[key];
+        }
+        // 或者可以将参数存储到一个专门的参数对象中
+        if (!config.value.params) {
+          config.value.params = {};
+        }
+        config.value.params[key] = params[key];
+      }
+    });
+
+    // 重新加载数据以应用新的参数
+    if (config.value) {
+      loadData(config.value);
+    }
+  }
+})
+
 watch(() => props.config, (config) => {
   if (config) {
     loadData(config)

+ 6 - 8
ruoyi-ui/src/views/standardization/modelUsing/index.vue

@@ -41,10 +41,10 @@
           <div style="margin-left: 3%;font-size: 12px;">
             创建人:{{ item.createBy }} 更新时间:{{ item.createTime }}
           </div>
-          <!-- <el-icon style="color: #79bbff;margin-left: auto;cursor: pointer;" @click.stop="handleBizDataShowConfig(item)"
+          <el-icon style="color: #79bbff;margin-left: auto;cursor: pointer;" @click.stop="handleBizDataShowConfig(item)"
                    v-if="hoverIndex === index">
             <TrendCharts/>
-          </el-icon> -->
+          </el-icon>
           <el-icon style="color: #79bbff;margin-left: auto;cursor: pointer;" @click.stop="editModel(item)"
                    v-if="hoverIndex === index">
             <Edit/>
@@ -102,7 +102,7 @@
               :auto-upload="false"
           >
             <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;"
-              :icon="Upload">上传
+                       :icon="Upload">上传
             </el-button>
           </el-upload>
         </el-form-item>
@@ -120,7 +120,7 @@
 
 import {onMounted, ref} from 'vue'
 import {Delete, Plus, Upload} from '@element-plus/icons-vue'
-import {addModeling, delModeling, editModeling, getModelingFlow, getModellist,getModelingById} from '@/api/standardization/modeling'
+import {addModeling, delModeling, editModeling, getModelingById, getModellist} from '@/api/standardization/modeling'
 import {getToken} from '@/utils/auth'
 import imagePath from '@/assets/images/defaultModel.png';
 import {useStore} from 'vuex';
@@ -219,8 +219,7 @@ function subEdit() {
         })
       }
     })
-  } 
-  else {
+  } else {
     formAddref.value.validate(async (valid) => {
       if (valid) {
         nextTick(() => {
@@ -273,8 +272,7 @@ function submitAdd() {
       }
     })
 
-  } 
-  else {
+  } else {
     formAddref.value.validate(async (valid) => {
       if (valid) {
         nextTick(() => {

+ 8 - 8
ruoyi-ui/vite.config.js

@@ -1,7 +1,7 @@
 import {defineConfig, loadEnv} from 'vite'
 import path from 'path'
 import createVitePlugins from './vite/plugins'
-import { viteCommonjs } from '@originjs/vite-plugin-commonjs'
+import {viteCommonjs} from '@originjs/vite-plugin-commonjs'
 // https://vitejs.dev/config/
 export default defineConfig(({mode, command}) => {
     const env = loadEnv(mode, process.cwd())
@@ -13,7 +13,7 @@ export default defineConfig(({mode, command}) => {
         // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
         base: env.VITE_APP_BASE_Title,
         plugins: [
-            viteCommonjs({ 
+            viteCommonjs({
                 include: ['jsoneditor'] // 必须为首个插件[3](@ref)
             }),
             ...createVitePlugins(env, isBuild) // 展开原有插件
@@ -35,7 +35,7 @@ export default defineConfig(({mode, command}) => {
             host: '0.0.0.0',
             open: true,
             proxy: {
-                  '/profile/upload': {
+                '/profile/upload': {
                     target: env.VITE_DEV_PATH,
                     changeOrigin: true,
                     rewrite: (p) => p.replace(/^\/profile\/upload/, '/profile/upload')
@@ -46,11 +46,11 @@ export default defineConfig(({mode, command}) => {
                     changeOrigin: true,
                     rewrite: (p) => p.replace(/^\/sh-api/, '')
                 },
-                '/service_api': {
-                    target: 'http://localhost:8081',
-                    changeOrigin: true,
-                    rewrite: (p) => p.replace(/^\/service_api/, '')
-                }
+                // '/service_api': {
+                //     target: 'http://localhost:8081',
+                //     changeOrigin: true,
+                //     rewrite: (p) => p.replace(/^\/service_api/, '')
+                // }
                 // [env.VITE_APP_BASE_API]: {
                 //     target: env.VITE_DEV_PATH,
                 //     changeOrigin: true,