ソースを参照

Merge branch 'lili' of qw/soilgd12 into master

Ding 16 時間 前
コミット
d455e8e909

+ 82 - 82
package-lock.json

@@ -35,11 +35,11 @@
         "proj4": "^2.19.10",
         "proj4leaflet": "^1.0.2",
         "sass": "^1.84.0",
-        "vue": "^3.5.13",
+        "vue": "^3.5.25",
         "vue-echarts": "^7.0.3",
         "vue-i18n": "^9.8.0",
         "vue-leaflet": "^0.1.0",
-        "vue-router": "^4.5.0",
+        "vue-router": "^4.6.3",
         "xlsx": "^0.18.5"
       },
       "devDependencies": {
@@ -50,7 +50,7 @@
         "@types/jsdom": "^21.1.7",
         "@types/leaflet": "^1.9.16",
         "@types/node": "^22.15.31",
-        "@vitejs/plugin-vue": "^5.2.1",
+        "@vitejs/plugin-vue": "^5.2.4",
         "@vue/test-utils": "^2.4.6",
         "@vue/tsconfig": "^0.7.0",
         "jsdom": "^25.0.1",
@@ -60,7 +60,7 @@
         "unplugin-icons": "^22.0.0",
         "unplugin-vue-components": "^28.0.0",
         "unplugin-vue-i18n": "^1.0.11",
-        "vite": "^6.3.5",
+        "vite": "^6.4.1",
         "vite-plugin-vue-devtools": "^7.6.8",
         "vitest": "^2.1.9",
         "vue-tsc": "^2.1.10"
@@ -508,9 +508,9 @@
       }
     },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.27.1",
-      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
-      "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
       "license": "MIT",
       "engines": {
         "node": ">=6.9.0"
@@ -541,12 +541,12 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.28.0",
-      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz",
-      "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
       "license": "MIT",
       "dependencies": {
-        "@babel/types": "^7.28.0"
+        "@babel/types": "^7.28.5"
       },
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -714,13 +714,13 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.28.2",
-      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz",
-      "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
       "license": "MIT",
       "dependencies": {
         "@babel/helper-string-parser": "^7.27.1",
-        "@babel/helper-validator-identifier": "^7.27.1"
+        "@babel/helper-validator-identifier": "^7.28.5"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -5559,53 +5559,53 @@
       }
     },
     "node_modules/@vue/compiler-core": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
-      "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
+      "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.28.0",
-        "@vue/shared": "3.5.18",
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.25",
         "entities": "^4.5.0",
         "estree-walker": "^2.0.2",
         "source-map-js": "^1.2.1"
       }
     },
     "node_modules/@vue/compiler-dom": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
-      "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
+      "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
       "license": "MIT",
       "dependencies": {
-        "@vue/compiler-core": "3.5.18",
-        "@vue/shared": "3.5.18"
+        "@vue/compiler-core": "3.5.25",
+        "@vue/shared": "3.5.25"
       }
     },
     "node_modules/@vue/compiler-sfc": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
-      "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
+      "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
       "license": "MIT",
       "dependencies": {
-        "@babel/parser": "^7.28.0",
-        "@vue/compiler-core": "3.5.18",
-        "@vue/compiler-dom": "3.5.18",
-        "@vue/compiler-ssr": "3.5.18",
-        "@vue/shared": "3.5.18",
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.25",
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25",
         "estree-walker": "^2.0.2",
-        "magic-string": "^0.30.17",
+        "magic-string": "^0.30.21",
         "postcss": "^8.5.6",
         "source-map-js": "^1.2.1"
       }
     },
     "node_modules/@vue/compiler-ssr": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
-      "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
+      "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
       "license": "MIT",
       "dependencies": {
-        "@vue/compiler-dom": "3.5.18",
-        "@vue/shared": "3.5.18"
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/shared": "3.5.25"
       }
     },
     "node_modules/@vue/compiler-vue2": {
@@ -5721,53 +5721,53 @@
       }
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.18.tgz",
-      "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz",
+      "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
       "license": "MIT",
       "dependencies": {
-        "@vue/shared": "3.5.18"
+        "@vue/shared": "3.5.25"
       }
     },
     "node_modules/@vue/runtime-core": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
-      "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz",
+      "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
       "license": "MIT",
       "dependencies": {
-        "@vue/reactivity": "3.5.18",
-        "@vue/shared": "3.5.18"
+        "@vue/reactivity": "3.5.25",
+        "@vue/shared": "3.5.25"
       }
     },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
-      "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz",
+      "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
       "license": "MIT",
       "dependencies": {
-        "@vue/reactivity": "3.5.18",
-        "@vue/runtime-core": "3.5.18",
-        "@vue/shared": "3.5.18",
+        "@vue/reactivity": "3.5.25",
+        "@vue/runtime-core": "3.5.25",
+        "@vue/shared": "3.5.25",
         "csstype": "^3.1.3"
       }
     },
     "node_modules/@vue/server-renderer": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
-      "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz",
+      "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
       "license": "MIT",
       "dependencies": {
-        "@vue/compiler-ssr": "3.5.18",
-        "@vue/shared": "3.5.18"
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25"
       },
       "peerDependencies": {
-        "vue": "3.5.18"
+        "vue": "3.5.25"
       }
     },
     "node_modules/@vue/shared": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.18.tgz",
-      "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz",
+      "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
       "license": "MIT"
     },
     "node_modules/@vue/test-utils": {
@@ -9340,12 +9340,12 @@
       "license": "ISC"
     },
     "node_modules/magic-string": {
-      "version": "0.30.17",
-      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
-      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
       "license": "MIT",
       "dependencies": {
-        "@jridgewell/sourcemap-codec": "^1.5.0"
+        "@jridgewell/sourcemap-codec": "^1.5.5"
       }
     },
     "node_modules/marchingsquares": {
@@ -12218,9 +12218,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "6.3.5",
-      "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz",
-      "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.1.tgz",
+      "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -13511,16 +13511,16 @@
       "license": "MIT"
     },
     "node_modules/vue": {
-      "version": "3.5.18",
-      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz",
-      "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
+      "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
       "license": "MIT",
       "dependencies": {
-        "@vue/compiler-dom": "3.5.18",
-        "@vue/compiler-sfc": "3.5.18",
-        "@vue/runtime-dom": "3.5.18",
-        "@vue/server-renderer": "3.5.18",
-        "@vue/shared": "3.5.18"
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-sfc": "3.5.25",
+        "@vue/runtime-dom": "3.5.25",
+        "@vue/server-renderer": "3.5.25",
+        "@vue/shared": "3.5.25"
       },
       "peerDependencies": {
         "typescript": "*"
@@ -13648,9 +13648,9 @@
       "license": "MIT"
     },
     "node_modules/vue-router": {
-      "version": "4.5.1",
-      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
-      "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+      "version": "4.6.3",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.3.tgz",
+      "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==",
       "license": "MIT",
       "dependencies": {
         "@vue/devtools-api": "^6.6.4"
@@ -13659,7 +13659,7 @@
         "url": "https://github.com/sponsors/posva"
       },
       "peerDependencies": {
-        "vue": "^3.2.0"
+        "vue": "^3.5.0"
       }
     },
     "node_modules/vue-tsc": {

+ 4 - 4
package.json

@@ -39,11 +39,11 @@
     "proj4": "^2.19.10",
     "proj4leaflet": "^1.0.2",
     "sass": "^1.84.0",
-    "vue": "^3.5.13",
+    "vue": "^3.5.25",
     "vue-echarts": "^7.0.3",
     "vue-i18n": "^9.8.0",
     "vue-leaflet": "^0.1.0",
-    "vue-router": "^4.5.0",
+    "vue-router": "^4.6.3",
     "xlsx": "^0.18.5"
   },
   "devDependencies": {
@@ -54,7 +54,7 @@
     "@types/jsdom": "^21.1.7",
     "@types/leaflet": "^1.9.16",
     "@types/node": "^22.15.31",
-    "@vitejs/plugin-vue": "^5.2.1",
+    "@vitejs/plugin-vue": "^5.2.4",
     "@vue/test-utils": "^2.4.6",
     "@vue/tsconfig": "^0.7.0",
     "jsdom": "^25.0.1",
@@ -64,7 +64,7 @@
     "unplugin-icons": "^22.0.0",
     "unplugin-vue-components": "^28.0.0",
     "unplugin-vue-i18n": "^1.0.11",
-    "vite": "^6.3.5",
+    "vite": "^6.4.1",
     "vite-plugin-vue-devtools": "^7.6.8",
     "vitest": "^2.1.9",
     "vue-tsc": "^2.1.10"

+ 1 - 1
src/components/layout/menuItems.ts

@@ -41,7 +41,7 @@ export const tabMenuMap: Record<string, MenuItem[]> = {
   acidmodelmap: [
     {
       index: "/acidmodelmap",
-      label: "土壤酸化地图展示",
+      label: "土壤酸化地块级预测",
       icon: Location,
     },
   ],

+ 157 - 144
src/views/User/acidModel/acidmodelmap.vue

@@ -1,8 +1,8 @@
 <template>
-  <div class="container">
   <el-card class="map-card">
     <div class="title">
-      <p class="map-title">乐昌市土地酸化预测</p>
+      <div class="section-icon">🗺️</div>
+      <p class="map-title">细粒度地块级酸化预测</p>
     </div>
 
     <div id="map-container" class="map-container">
@@ -25,8 +25,9 @@
          ref="popupElement"  
          class="feature-popup fixed-center"
          :style="{
-          left: popupPosition.x + 'px',
-          top: popupPosition.y + 'px'
+          left: popupPosition.x + '%',
+          top: popupPosition.y + '%',
+          translate:'none'
          }">
       <div class="popup-content">
         <div class="popup-header" @mousedown="startDrag">
@@ -102,7 +103,7 @@
               </div>
 
     <div class="params-row">
-      <el-form-item label="NO3" prop="NO3" class="form-item-compact">
+      <el-form-item label="硝酸盐" prop="NO3" class="form-item-compact">
         
         <el-input
           v-model.number="acidReductionParams.NO3"
@@ -113,7 +114,7 @@
           :disabled="phLoading"
         />
       </el-form-item>
-      <el-form-item label="NH4" prop="NH4" class="form-item-compact">
+      <el-form-item label="铵盐" prop="NH4" class="form-item-compact">
         
         <el-input
           v-model.number="acidReductionParams.NH4"
@@ -158,7 +159,7 @@
 
     <!-- NO3和NH4在同一行 -->
     <div class="params-row">
-      <el-form-item label="NO3" prop="NO3" class="form-item-compact">
+      <el-form-item label="硝酸盐" prop="NO3" class="form-item-compact">
         
         <el-input
           v-model.number="acidInversionParams.NO3"
@@ -169,7 +170,7 @@
           :disabled="phLoading"
         />
       </el-form-item>
-      <el-form-item label="NH4" prop="NH4" class="form-item-compact">
+      <el-form-item label="铵盐" prop="NH4" class="form-item-compact">
         
         <el-input
           v-model.number="acidInversionParams.NH4"
@@ -204,7 +205,7 @@
             <div v-else-if="predictionResult" class="prediction-result">
               <!--降酸结果-->
               <div class="result-item" v-if="currentPredictionType === 'reduction' && predictionResult.prediction_reduce !== undefined">
-                <span class="prediction-value reduction">每亩地土壤表层20cm撒{{ formatPredictionValue(predictionResult.prediction_reduce/10)}} 吨</span>
+                <span class="prediction-value reduction">每亩地土壤表层20cm撒{{ formatPredictionValue(predictionResult.prediction_reduce)}} 吨</span>
               </div>
               <!-- 反酸预测结果 -->
               <div class="result-item" v-if="currentPredictionType === 'inversion' && predictionResult.prediction_reflux !== undefined">
@@ -225,14 +226,12 @@
          :style="connectionLine.style">
     </div>
   </el-card>
-  </div>
 </template>
 
 <script setup lang="ts">
 import { reactive, ref, nextTick, onMounted, onUnmounted, computed } from "vue";
 import { ElMessage } from "element-plus";
 import type { FormInstance } from "element-plus";
-import { api5000, api8000, api8080 } from '@/utils/request'; // 导入封装好的API实例
 
 // 新增:反酸预测相关状态(区分降酸/反酸的显示)
 const showInversionPrediction = ref(false); // 控制反酸预测区域显示
@@ -404,20 +403,20 @@ const acidReductionRules = reactive({
 // 格式化预测值
 const formatPredictionValue = (value: any): string => {
   console.log('预测值原始数据:', value, '类型:', typeof value);
-
+  
   if (value === null || value === undefined) return '无数据';
-
+  
   if (Array.isArray(value)) {
     if (value.length === 0) return '无数据';
     const num = Number(value[0]);
     return isNaN(num) ? '无效数据' : num.toFixed(4);
   }
-
+  
   if (typeof value === 'object') {
     console.warn('预测值是对象类型:', value);
     return '数据格式错误';
   }
-
+  
   const num = Number(value);
   return isNaN(num) ? '无效数据' : num.toFixed(4);
 };
@@ -472,13 +471,13 @@ const drawHighlightFeature = (geoJsonFeature: any) => {
     return scaledFeature;
   };
 
-  // 创建放大1.3倍的地块
-  const scaledFeature = scaleFeatureCoordinates(geoJsonFeature, 1.3);
+  // 创建放大1倍的地块
+  const scaledFeature = scaleFeatureCoordinates(geoJsonFeature, 1);
 
   // 使用放大后的GeoJSON创建高亮图层
   highlightLayer.value = L.geoJSON(scaledFeature, {
     style: {
-      color: '#ff4d4f',
+      color: '#000',
       weight: 4,
       fillColor: '#ff4d4f',
       fillOpacity: 1,
@@ -513,7 +512,7 @@ const drawHighlightFeature = (geoJsonFeature: any) => {
 // 获取地块信息
 const getFeatureInfo = async (latlng: any, point: any): Promise<boolean> => {
   if (!map.value) return false;
-
+  
   featureInfo.loading = true;
   featureInfo.error = false;
   featureInfo.data = null;
@@ -525,7 +524,7 @@ const getFeatureInfo = async (latlng: any, point: any): Promise<boolean> => {
       layerGroup: "mapwithboundary", 
     };
 
-   const bounds = map.value.getBounds();
+    const bounds = map.value.getBounds();
     const size = map.value.getSize();
     
     const params = new URLSearchParams();
@@ -544,30 +543,24 @@ const getFeatureInfo = async (latlng: any, point: any): Promise<boolean> => {
     params.append('bbox', `${bounds.getWest()},${bounds.getSouth()},${bounds.getEast()},${bounds.getNorth()}`);
 
     const url = `${GEOSERVER_CONFIG.url}?${params.toString()}`;
-    console.log('GetFeatureInfo URL:', url); // 添加日志
     
-    // 修改:使用 apiMain 代替 fetch
-    const response = await api8080.get(url);
+    const response = await fetch(url);
     
-    if (response.status !== 200) {
+    if (!response.ok) {
       throw new Error(`HTTP error! status: ${response.status}`);
     }
     
-    const data = response.data;
-    console.log('GeoServer返回数据:', data); // 添加日志
+    const data = await response.json();
     
     if (data.features && data.features.length > 0) {
-      
       const feature = data.features[0]; // 保存完整的要素(包含几何信息)
       const properties = feature.properties;
       const hasValidData = properties.QSDWMC || properties.DLMC;
-      const village = properties.QSDWMC || properties.village || properties.VILLAGE || properties.name || '未知';
-      const landType = properties.DLMC || properties.landType || properties.LANDTYPE || properties.type || '未知';
       
       if (hasValidData) {
         featureInfo.data = {
-          village: village,
-          landType: landType,
+          village: properties.QSDWMC,
+          landType: properties.DLMC,
         };
         // 绘制高亮地块
         drawHighlightFeature(feature);
@@ -575,21 +568,11 @@ const getFeatureInfo = async (latlng: any, point: any): Promise<boolean> => {
       }
     }
     
-    // 如果没有数据,显示默认信息
-    featureInfo.data = {
-      village: '无数据',
-      landType: '无数据',
-    };
     return false;
     
   } catch (error) {
     console.error('获取地块信息失败:', error);
     featureInfo.error = true;
-    // 错误时也显示默认信息
-    featureInfo.data = {
-      village: '获取失败',
-      landType: '获取失败',
-    };
     return false;
   } finally {
     featureInfo.loading = false;
@@ -603,18 +586,22 @@ const fetchCurrentPH = async () => {
 
   phLoading.value = true;
   try {
-    const params = {
-      target_lon: currentClickCoords.lng.toString(),
-      target_lat: currentClickCoords.lat.toString(),
-      NO3: defaultParams.NO3.toString(),
-      NH4: defaultParams.NH4.toString()
-    };
-
-   // 修改:使用 api8000 代替 fetch
-   const response = await api8000.get('/api/vector/nearest-with-predictions', { params });
+    const urlParams = new URLSearchParams();
+    urlParams.append('target_lon', currentClickCoords.lng.toString());
+    urlParams.append('target_lat', currentClickCoords.lat.toString());
+    urlParams.append('NO3', defaultParams.NO3.toString());
+    urlParams.append('NH4', defaultParams.NH4.toString());
+
+    // 调用接口获取当前pH(接口会返回nearest_point.ph)
+    const response = await fetch(
+      `http://localhost:8000/api/vector/nearest-with-predictions?${urlParams.toString()}`
+    );
 
-  // 从接口提取当前pH(根据你的接口结构:data.nearest_point.ph)
-   currentPH.value = response.data.nearest_point?.ph !== undefined ? Number(response.data.nearest_point.ph) : null;
+    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
+    const data = await response.json();
+    
+    // 从接口提取当前pH(根据你的接口结构:data.nearest_point.ph)
+    currentPH.value = data.nearest_point?.ph !== undefined ? Number(data.nearest_point.ph) : null;
     return currentPH.value !== null; // 返回是否获取成功
   } catch (error) {
     console.error('获取当前pH失败:', error);
@@ -730,37 +717,42 @@ const callPredictionAPI = async (
   predictionResult.value = null;
 
   try {
-    const requestParams: any = {
-      target_lon: lng.toString(),
-      target_lat: lat.toString()
-    };
+    const urlParams = new URLSearchParams();
+    urlParams.append('target_lon', lng.toString());
+    urlParams.append('target_lat', lat.toString());
 
     // 明确传递 prediction_type
     if (currentPredictionType.value === 'reduction') {
-      requestParams.prediction_type = 'reduce';
+      urlParams.append('prediction_type', 'reduce');
     } else if (currentPredictionType.value === 'inversion') {
-      requestParams.prediction_type = 'reflux';
+      urlParams.append('prediction_type', 'reflux');
     }
 
     if (params) {
       Object.entries(params).forEach(([key, value]) => {
         if (value !== undefined && value !== null) {
-          requestParams[key] = value.toString();
+          urlParams.append(key, value.toString());
         }
       });
     }
 
-    console.log('调用预测接口,参数:', requestParams);
+    console.log('调用预测接口,参数:', urlParams.toString());
 
-    // 修改:使用 api8000 代替 fetch
-    const response = await api8000.get('/api/vector/nearest-with-predictions', { 
-      params: requestParams 
-    });
+    const response = await fetch(
+      `http://localhost:8000/api/vector/nearest-with-predictions?${urlParams.toString()}`
+    );
 
-    predictionResult.value = response.data;
-    console.log('预测结果:', response.data);
+    if (!response.ok) {
+      const errorData = await response.json();
+      console.error('预测接口返回错误:', errorData);
+      throw new Error(`HTTP error! status: ${response.status}`);
+    }
 
-    currentPH.value = response.data.nearest_point?.ph !== undefined ? Number(response.data.nearest_point.ph) : null;
+    const data = await response.json();
+    predictionResult.value = data;
+    console.log('预测结果:', data);
+
+    currentPH.value = data.nearest_point?.ph !== undefined ? Number(data.nearest_point.ph) : null;
 
     // 显示预测结果
     showPredictionResult.value = true;
@@ -791,9 +783,7 @@ const resetPrediction = () => {
 const handleMapClick = async (e: any) => {
   const lng = e.latlng.lng;
   const lat = e.latlng.lat;
-
-  console.log('地图点击坐标:', { lng, lat }); // 添加日志
-
+  
   // 更新当前坐标
   currentClickCoords.lng = lng;
   currentClickCoords.lat = lat;
@@ -811,8 +801,8 @@ const handleMapClick = async (e: any) => {
   clickPoint.y = mapRect.top + containerPoint.y;
   
   // 设置弹窗初始位置(在点击点右侧)
-  popupPosition.x = clickPoint.x + 20;
-  popupPosition.y = clickPoint.y - 100; // 居中显示
+  popupPosition.x = 35;
+  popupPosition.y = 35; // 居中显示
   
   showPopup.value = true;
   showConnectionLine.value=false;
@@ -879,8 +869,8 @@ const closePopup = () => {
 // 新增:弹窗拖动相关状态
 const popupElement = ref<HTMLElement>();
 const popupPosition = reactive({
-  x: 0,
-  y: 0
+  x: 35,
+  y: 35
 });
 
 // 新增:连接线相关状态
@@ -903,8 +893,14 @@ const startDrag = (event: MouseEvent) => {
   if (!popupElement.value) return;
   
   isDragging.value = true;
-  dragStartPos.x = event.clientX - popupPosition.x;
-  dragStartPos.y = event.clientY - popupPosition.y;
+  // 初始值:百分比转像素
+  const viewportWidth = window.innerWidth;
+  const viewportHeight = window.innerHeight;
+  const popupLeft = (popupPosition.x / 100) * viewportWidth;
+  const popupTop = (popupPosition.y / 100) * viewportHeight;
+  
+  dragStartPos.x = event.clientX - popupLeft;
+  dragStartPos.y = event.clientY - popupTop;
   
   document.addEventListener('mousemove', onDrag);
   document.addEventListener('mouseup', stopDrag);
@@ -914,8 +910,15 @@ const startDrag = (event: MouseEvent) => {
 const onDrag = (event: MouseEvent) => {
   if (!isDragging.value) return;
   
-  popupPosition.x = event.clientX - dragStartPos.x;
-  popupPosition.y = event.clientY - dragStartPos.y;
+  // 拖拽后转成百分比
+  const viewportWidth = window.innerWidth;
+  const viewportHeight = window.innerHeight;
+  // 计算拖拽后的像素位置
+  const newX = event.clientX - dragStartPos.x;
+  const newY = event.clientY - dragStartPos.y;
+  // 转成百分比(限制0-95,避免弹窗超出可视区)
+  popupPosition.x = Math.max(0, Math.min(95, (newX / viewportWidth) * 100));
+  popupPosition.y = Math.max(0, Math.min(95, (newY / viewportHeight) * 100));
   updateConnectionLine();
 };
 
@@ -923,6 +926,9 @@ const stopDrag = () => {
   isDragging.value = false;
   document.removeEventListener('mousemove', onDrag);
   document.removeEventListener('mouseup', stopDrag);
+  nextTick(() => {
+    updateConnectionLine();
+  });
 };
 
 // 修改更新连接线方法,使用经纬度坐标
@@ -939,8 +945,15 @@ const updateConnectionLine = () => {
   
   // 获取弹窗中心点
   const popupRect = popupElement.value.getBoundingClientRect();
-  const endX = popupPosition.x + popupRect.width / 2;
-  const endY = popupPosition.y + popupRect.height / 2;
+  // 视口总宽度/高度
+  const viewportWidth = window.innerWidth;
+  const viewportHeight = window.innerHeight;
+  // 弹窗左上角像素坐标(百分比转像素)
+  const popupLeft = (popupPosition.x / 100) * viewportWidth;
+  const popupTop = (popupPosition.y / 100) * viewportHeight;
+  // 弹窗中心点像素坐标
+  const endX = popupLeft + popupRect.width / 2;
+  const endY = popupTop + popupRect.height / 2;
   
   // 计算线的长度和角度
   const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
@@ -951,7 +964,8 @@ const updateConnectionLine = () => {
     top: startY + 'px',
     width: length + 'px',
     transform: `rotate(${angle}deg)`,
-    transformOrigin: '0 0'
+    transformOrigin: '0 0',
+    '--arrow-angle':`${angle}deg`
   };
 };
 
@@ -960,21 +974,11 @@ const initMap = async () => {
   mapError.value = false;
 
   try {
-    console.log('开始初始化地图...');
-    
-    // 检查地图容器是否存在
-    const mapContainer = document.getElementById('map-container');
-    if (!mapContainer) {
-      throw new Error('地图容器未找到');
-    }
-    console.log('地图容器尺寸:', mapContainer.offsetWidth, 'x', mapContainer.offsetHeight);
-
     // 动态导入Leaflet
     if (!L) {
       L = await import('leaflet');
       await import('leaflet/dist/leaflet.css');
       
-      // 修复图标问题
       delete (L.Icon.Default.prototype as any)._getIconUrl;
       L.Icon.Default.mergeOptions({
         iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
@@ -986,6 +990,7 @@ const initMap = async () => {
     // 清除现有地图
     if (map.value) {
       map.value.remove();
+      map.value = null;
     }
 
     // 创建地图实例
@@ -996,34 +1001,48 @@ const initMap = async () => {
       zoom: 10
     });
 
-    console.log('地图实例创建成功');
-
-    // 然后尝试添加WMS图层
+    // WMS配置
     const GEOSERVER_CONFIG = {
       url: "/geoserver/wms",
       workspace: "acidmap",
       layerGroup: "mapwithboundary", 
     };
 
-    try {
-      const wmsLayer = L.tileLayer.wms(GEOSERVER_CONFIG.url, {
-        layers: `${GEOSERVER_CONFIG.workspace}:${GEOSERVER_CONFIG.layerGroup}`,
-        format: "image/png",
-        transparent: true,
-        version: "1.1.1",
-        crs: L.CRS.EPSG4326,
-        attribution: "Data from GeoServer"
-      }).addTo(map.value);
-      console.log('WMS图层添加成功');
-    } catch (wmsError) {
-      console.warn('WMS图层加载失败,使用OSM底图:', wmsError);
-    }
+    // WMS图层配置
+    const wmsLayer = L.tileLayer.wms(GEOSERVER_CONFIG.url, {
+      layers: `${GEOSERVER_CONFIG.workspace}:${GEOSERVER_CONFIG.layerGroup}`,
+      format: "image/png",
+      transparent: true,
+      version: "1.1.1",
+      crs: L.CRS.EPSG4326,
+      attribution: "Data from GeoServer"
+    });
+
+    // 添加图层到地图
+    wmsLayer.addTo(map.value);
 
     // 绑定点击事件
     map.value.on('click', handleMapClick);
 
+    // 添加地图移动和缩放事件监听,实时更新连接线
+    map.value.on('moveend zoomend', () => {
+      if (showPopup.value && showConnectionLine.value) {
+        updateConnectionLine();
+      }
+    });
+
+    // 监听地图飞行动画完成事件
+    map.value.on('moveend', () => {
+      // 地图动画完成后显示连接线
+      if (showPopup.value && !showConnectionLine.value && featureCenter.lng && featureCenter.lat) {
+        showConnectionLine.value = true;
+        nextTick(() => {
+          updateConnectionLine();
+        });
+      }
+    });
+
     mapLoading.value = false;
-    console.log('地图初始化完成');
 
   } catch (error) {
     console.error('地图初始化失败:', error);
@@ -1055,21 +1074,31 @@ onUnmounted(() => {
   // 移除拖拽事件监听
   document.removeEventListener('mousemove', onDrag);
   document.removeEventListener('mouseup', stopDrag);
+
+  window.removeEventListener('resize', handleWindowResize);
 });
 
 onMounted(() => {
   nextTick(() => {
     initMap();
   });
+  // 监听窗口缩放
+  window.addEventListener('resize', handleWindowResize);
 });
+
+// 窗口缩放时重新计算连接线
+const handleWindowResize = () => {
+  if (showPopup.value && showConnectionLine.value) {
+    nextTick(() => {
+      updateConnectionLine();
+    });
+  }
+};
 </script>
 
 <style scoped>
 .feature-popup {
   position: fixed;
-  top: 45%;
-  left: 40%;
-  transform: translate(-50%,-50%);
   z-index: 1000;
   background: white;
   border-radius: 8px;
@@ -1260,13 +1289,10 @@ onMounted(() => {
 }
 
 .map-card {
-  background-color: rgba(255, 255, 255, 0.8);
-  border-radius: 8px;
-  padding: 15px;
-  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
-  position: relative;
-  backdrop-filter: blur(5px);
-  overflow: visible !important;
+  width: 850px;
+  flex: 1;
+  min-height: 600px;
+  margin: 0 auto;
 }
 
 .map-container {
@@ -1457,15 +1483,6 @@ onMounted(() => {
   color: #48bb78; /* 反酸结果用绿色 */
 }
 
-.container {
-  padding: 20px;
-  background: linear-gradient(
-    135deg, 
-    rgba(230, 247, 255, 0.7) 0%, 
-    rgba(240, 248, 255, 0.7) 100%
-  );
-  box-sizing: border-box;
-}
 /* 新增:高亮图层的z-index确保在最上层 */
 :deep(.leaflet-geojson) {
   z-index: 999 !important;
@@ -1481,28 +1498,24 @@ onMounted(() => {
   border: none;
   z-index: 999;
   pointer-events: none;
+  --arrow-angle: 0deg;
 }
 
 .connection-line::before {
   content: '';
   position: absolute;
-  width: 6px;
-  height: 6px;
-  background: #409eff;
-  border-radius: 50%;
-  top: -2.5px;
-  left: -3px;
-}
-
-.connection-line::after {
-  content: '';
-  position: absolute;
-  width: 6px;
-  height: 6px;
-  background: #409eff;
-  border-radius: 50%;
-  top: -2.5px;
-  right: -3px;
+  left: -5px; /* 箭头在连接线起点左侧,对准地块 */
+  top: 50%;
+  /* 跟随连接线角度旋转,保持垂直居中 */
+  transform: translateY(-50%) rotate(var(--arrow-angle));
+  /* 三角形箭头:右向箭头(指向地块) */
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 4px 8px 4px 0; /* 箭头尺寸:高4px*2,长8px */
+  border-color: transparent #409eff transparent transparent; /* 箭头颜色与连接线一致 */
+  transform-origin: center center;
+  z-index: 1;
 }
 
 </style>

+ 6 - 1
tsconfig.app.json

@@ -10,6 +10,7 @@
     "src/views/Admin/modelManagement/AcidReductionModel/thres.vue",
     "src/views/User/heavyMetalFluxCalculation/outputFluxCalculation/outputFluxCalculation.vue"
   ],
+  "extends": "./tsconfig.base.json",
   "compilerOptions": {
     "target": "ES2020",
     "useDefineForClassFields": true,
@@ -25,10 +26,14 @@
     "isolatedModules": true,
     "jsx": "preserve",
     "incremental": true,
-    "types": ["vite/client"],
+    "types": ["vite/client",
+      "vue-router"],
     "paths": {
       "@/*": ["./src/*"]
     },
     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
+  },
+  "vueCompilerOptions": {
+    "globalTypesPath": "./node_modules/.vite/vue-types/global.d.ts" // 新增这部分
   }
 }

+ 10 - 0
vite.config.ts

@@ -57,6 +57,16 @@ export default defineConfig({
       '@': fileURLToPath(new URL('./src', import.meta.url))
     },
   },
+  server: {
+    proxy: {
+      // 匹配以/geoserver开头的请求,转发到GeoServer
+      '/geoserver': {
+        target: 'http://localhost:8080', // GeoServer的地址(端口对应你的实际配置)
+        changeOrigin: true, // 开启跨域
+        rewrite: (path) => path // 不需要重写路径,直接转发
+      }
+    }
+  }
 })
 
 function kebabCase(text: any) {