From 5f44d21b3921abc88506a7ec46b3fe6f078664aa Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 14 三月 2025 15:33:24 +0800
Subject: [PATCH] 新增走航融合功能(初版)

---
 src/model/Legend.js                       |  100 ++
 src/model/SatelliteGrid.js                |  488 +++++++++++++++
 src/views/underwaymix/UnderwayMixMode.vue |  398 ++++++++++++
 src/utils/map/grid.js                     |  412 ++++++++++++
 src/api/gridApi.js                        |  152 ++++
 src/components.d.ts                       |    1 
 src/utils/map/calculate.js                |   51 +
 src/utils/map/line.js                     |   31 
 src/styles/base.scss                      |    2 
 src/utils/map/3dLayer.js                  |    1 
 src/views/realtimemode/RealtimeMode.vue   |    2 
 README.md                                 |  225 +++++++
 12 files changed, 1,819 insertions(+), 44 deletions(-)

diff --git a/README.md b/README.md
index 4a961b4..bec6a41 100644
--- a/README.md
+++ b/README.md
@@ -39,3 +39,228 @@
 ```sh
 npm run lint
 ```
+
+```
+ff-ai-ep-underway-vue
+鈹溾攢 .env.development
+鈹溾攢 .env.development.jingan
+鈹溾攢 .env.production
+鈹溾攢 .env.production.jingan
+鈹溾攢 .eslintrc.cjs
+鈹溾攢 .prettierrc.json
+鈹溾攢 index.html
+鈹溾攢 jsconfig.json
+鈹溾攢 package-lock.json
+鈹溾攢 package.json
+鈹溾攢 public
+鈹�  鈹斺攢 favicon.ico
+鈹溾攢 README.md
+鈹溾攢 src
+鈹�  鈹溾攢 api
+鈹�  鈹�  鈹溾攢 deviceApi.js
+鈹�  鈹�  鈹溾攢 index.js
+鈹�  鈹�  鈹溾攢 missionApi.js
+鈹�  鈹�  鈹溾攢 monitorDataApi.js
+鈹�  鈹�  鈹溾攢 sceneInfoApi.js
+鈹�  鈹�  鈹斺攢 thirdPartyDataApi.js
+鈹�  鈹溾攢 App.vue
+鈹�  鈹溾攢 assets
+鈹�  鈹�  鈹溾攢 3dmap.css
+鈹�  鈹�  鈹溾攢 base.css
+鈹�  鈹�  鈹溾攢 border.css
+鈹�  鈹�  鈹溾攢 common-style.css
+鈹�  鈹�  鈹溾攢 logo.svg
+鈹�  鈹�  鈹溾攢 main.css
+鈹�  鈹�  鈹斺攢 mipmap
+鈹�  鈹�     鈹溾攢 boat_driving.png
+鈹�  鈹�     鈹溾攢 border.png
+鈹�  鈹�     鈹溾攢 car_driving.png
+鈹�  鈹�     鈹溾攢 car_offline.png
+鈹�  鈹�     鈹溾攢 car_stop.png
+鈹�  鈹�     鈹溾攢 company.png
+鈹�  鈹�     鈹溾攢 c_level_0.png
+鈹�  鈹�     鈹溾攢 c_level_1.png
+鈹�  鈹�     鈹溾攢 c_level_2.png
+鈹�  鈹�     鈹溾攢 c_level_no.png
+鈹�  鈹�     鈹溾攢 data_chart.png
+鈹�  鈹�     鈹溾攢 device.png
+鈹�  鈹�     鈹溾攢 dust.png
+鈹�  鈹�     鈹溾攢 ic_down_white.png
+鈹�  鈹�     鈹溾攢 ic_up_white.png
+鈹�  鈹�     鈹溾攢 location.png
+鈹�  鈹�     鈹溾攢 oil_paint.png
+鈹�  鈹�     鈹溾攢 other_smell.png
+鈹�  鈹�     鈹溾攢 plastics.png
+鈹�  鈹�     鈹溾攢 pungent.png
+鈹�  鈹�     鈹溾攢 scene.png
+鈹�  鈹�     鈹溾攢 scene_1.png
+鈹�  鈹�     鈹溾攢 scene_15.png
+鈹�  鈹�     鈹溾攢 scene_16.png
+鈹�  鈹�     鈹溾攢 scene_17.png
+鈹�  鈹�     鈹溾攢 scene_18.png
+鈹�  鈹�     鈹溾攢 scene_19.png
+鈹�  鈹�     鈹溾攢 scene_20.png
+鈹�  鈹�     鈹溾攢 scene_4.png
+鈹�  鈹�     鈹溾攢 scene_5.png
+鈹�  鈹�     鈹溾攢 scene_6.png
+鈹�  鈹�     鈹溾攢 shrink_left.png
+鈹�  鈹�     鈹溾攢 shrink_right.png
+鈹�  鈹�     鈹溾攢 slider_handle.png
+鈹�  鈹�     鈹溾攢 stink.png
+鈹�  鈹�     鈹溾攢 title_bg.png
+鈹�  鈹�     鈹溾攢 underway-2.png
+鈹�  鈹�     鈹溾攢 underway-3.png
+鈹�  鈹�     鈹溾攢 underway.png
+鈹�  鈹�     鈹溾攢 寰俊鍥剧墖_20210608110133.png
+鈹�  鈹�     鈹溾攢 寰俊鍥剧墖_202106081101331.png
+鈹�  鈹�     鈹斺攢 寰俊鍥剧墖_202106081101332.png
+鈹�  鈹溾攢 components
+鈹�  鈹�  鈹溾攢 animation
+鈹�  鈹�  鈹�  鈹溾攢 HistoricalTrajectory.vue
+鈹�  鈹�  鈹�  鈹斺攢 TrajectoryState.vue
+鈹�  鈹�  鈹溾攢 BaseCard.vue
+鈹�  鈹�  鈹溾攢 CardButton.vue
+鈹�  鈹�  鈹溾攢 CardDialog.vue
+鈹�  鈹�  鈹溾攢 chart
+鈹�  鈹�  鈹�  鈹溾攢 GaugeChart.vue
+鈹�  鈹�  鈹�  鈹溾攢 ProgressLineChart.vue
+鈹�  鈹�  鈹�  鈹斺攢 RealTimeLineChart.vue
+鈹�  鈹�  鈹溾攢 core
+鈹�  鈹�  鈹�  鈹溾攢 CoreHeader.vue
+鈹�  鈹�  鈹�  鈹斺攢 CoreMenu.vue
+鈹�  鈹�  鈹溾攢 device
+鈹�  鈹�  鈹�  鈹溾攢 DeviceCreate.vue
+鈹�  鈹�  鈹�  鈹斺攢 DeviceManage.vue
+鈹�  鈹�  鈹溾攢 map
+鈹�  鈹�  鈹�  鈹溾攢 BaseMap.vue
+鈹�  鈹�  鈹�  鈹溾攢 ConfigManage.vue
+鈹�  鈹�  鈹�  鈹溾攢 MapLocation.vue
+鈹�  鈹�  鈹�  鈹溾攢 MapScene.vue
+鈹�  鈹�  鈹�  鈹斺攢 MapToolbox.vue
+鈹�  鈹�  鈹溾攢 MessageBox.vue
+鈹�  鈹�  鈹溾攢 mission
+鈹�  鈹�  鈹�  鈹溾攢 MissionEdit.vue
+鈹�  鈹�  鈹�  鈹溾攢 MissionImport.vue
+鈹�  鈹�  鈹�  鈹斺攢 MissionManage.vue
+鈹�  鈹�  鈹溾攢 monitor
+鈹�  鈹�  鈹�  鈹溾攢 DataSummary.vue
+鈹�  鈹�  鈹�  鈹溾攢 DataTable.vue
+鈹�  鈹�  鈹�  鈹溾攢 FactorCheckbox.vue
+鈹�  鈹�  鈹�  鈹溾攢 FactorLegend.vue
+鈹�  鈹�  鈹�  鈹溾攢 FactorRadio.vue
+鈹�  鈹�  鈹�  鈹溾攢 FactorTrend.vue
+鈹�  鈹�  鈹�  鈹溾攢 VehicleData.vue
+鈹�  鈹�  鈹�  鈹溾攢 WeatherData-copy.vue
+鈹�  鈹�  鈹�  鈹斺攢 WeatherData.vue
+鈹�  鈹�  鈹溾攢 scene
+鈹�  鈹�  鈹�  鈹斺攢 SceneSearch.vue
+鈹�  鈹�  鈹溾攢 search
+鈹�  鈹�  鈹�  鈹溾攢 OptionDevice.vue
+鈹�  鈹�  鈹�  鈹溾攢 OptionLocation.vue
+鈹�  鈹�  鈹�  鈹溾攢 OptionLocation2.vue
+鈹�  鈹�  鈹�  鈹溾攢 OptionMission.vue
+鈹�  鈹�  鈹�  鈹溾攢 OptionTime.vue
+鈹�  鈹�  鈹�  鈹溾攢 OptionType.vue
+鈹�  鈹�  鈹�  鈹斺攢 SearchBar.vue
+鈹�  鈹�  鈹斺攢 SliderBar.vue
+鈹�  鈹溾攢 components.d.ts
+鈹�  鈹溾攢 composables
+鈹�  鈹�  鈹溾攢 defaultFactorType.js
+鈹�  鈹�  鈹溾攢 fetchData.js
+鈹�  鈹�  鈹溾攢 formConfirm.js
+鈹�  鈹�  鈹斺攢 messageBox.js
+鈹�  鈹溾攢 constant
+鈹�  鈹�  鈹溾攢 checkbox-options
+鈹�  鈹�  鈹�  鈹溾攢 options-jingan.js
+鈹�  鈹�  鈹�  鈹斺攢 options.js
+鈹�  鈹�  鈹溾攢 checkbox-options.js
+鈹�  鈹�  鈹溾攢 device-type.js
+鈹�  鈹�  鈹溾攢 factor-name.js
+鈹�  鈹�  鈹溾攢 factor-unit.js
+鈹�  鈹�  鈹溾攢 location.js
+鈹�  鈹�  鈹溾攢 radio-options
+鈹�  鈹�  鈹�  鈹溾攢 options-jingan.js
+鈹�  鈹�  鈹�  鈹斺攢 options.js
+鈹�  鈹�  鈹溾攢 radio-options.js
+鈹�  鈹�  鈹溾攢 scene-types
+鈹�  鈹�  鈹�  鈹溾攢 options-jingan.js
+鈹�  鈹�  鈹�  鈹斺攢 options.js
+鈹�  鈹�  鈹溾攢 scene-types.js
+鈹�  鈹�  鈹斺攢 wind-dir.js
+鈹�  鈹溾攢 lib
+鈹�  鈹�  鈹溾攢 jquery-3.5.1.min.js
+鈹�  鈹�  鈹溾攢 jquery.soap.js
+鈹�  鈹�  鈹斺攢 jquery.xml2json.js
+鈹�  鈹溾攢 main.js
+鈹�  鈹溾攢 model
+鈹�  鈹�  鈹溾攢 Factor.js
+鈹�  鈹�  鈹溾攢 FactorDatas.js
+鈹�  鈹�  鈹溾攢 FrameAnimation.js
+鈹�  鈹�  鈹斺攢 Legend.js
+鈹�  鈹溾攢 router
+鈹�  鈹�  鈹斺攢 index.js
+鈹�  鈹溾攢 stores
+鈹�  鈹�  鈹溾攢 device.js
+鈹�  鈹�  鈹溾攢 map-animation.js
+鈹�  鈹�  鈹溾攢 mission.js
+鈹�  鈹�  鈹溾攢 scene.js
+鈹�  鈹�  鈹斺攢 toolbox.js
+鈹�  鈹溾攢 styles
+鈹�  鈹�  鈹溾攢 base.scss
+鈹�  鈹�  鈹溾攢 elementUI.scss
+鈹�  鈹�  鈹斺攢 index.scss
+鈹�  鈹溾攢 test.xml
+鈹�  鈹溾攢 utils
+鈹�  鈹�  鈹溾攢 chart
+鈹�  鈹�  鈹�  鈹斺攢 chart-option.js
+鈹�  鈹�  鈹溾攢 color.js
+鈹�  鈹�  鈹溾攢 expand
+鈹�  鈹�  鈹�  鈹斺攢 expand.js
+鈹�  鈹�  鈹溾攢 factor
+鈹�  鈹�  鈹�  鈹斺攢 data.js
+鈹�  鈹�  鈹溾攢 file.js
+鈹�  鈹�  鈹溾攢 map
+鈹�  鈹�  鈹�  鈹溾攢 3dLayer.js
+鈹�  鈹�  鈹�  鈹溾攢 animation.js
+鈹�  鈹�  鈹�  鈹溾攢 calculate.js
+鈹�  鈹�  鈹�  鈹溾攢 dialog.js
+鈹�  鈹�  鈹�  鈹溾攢 grid.js
+鈹�  鈹�  鈹�  鈹溾攢 index.js
+鈹�  鈹�  鈹�  鈹溾攢 index_old.js
+鈹�  鈹�  鈹�  鈹溾攢 line.js
+鈹�  鈹�  鈹�  鈹溾攢 marks.js
+鈹�  鈹�  鈹�  鈹溾攢 sector.js
+鈹�  鈹�  鈹�  鈹溾攢 security.js
+鈹�  鈹�  鈹�  鈹溾攢 toolbox.js
+鈹�  鈹�  鈹�  鈹斺攢 util.js
+鈹�  鈹�  鈹斺攢 number.js
+鈹�  鈹斺攢 views
+鈹�     鈹溾攢 electricitymode
+鈹�     鈹�  鈹斺攢 ElectricityMode.vue
+鈹�     鈹溾攢 gridmonitor
+鈹�     鈹�  鈹斺攢 GridMode.vue
+鈹�     鈹溾攢 historymode
+鈹�     鈹�  鈹溾攢 component
+鈹�     鈹�  鈹�  鈹溾攢 DataSheet.vue
+鈹�     鈹�  鈹�  鈹斺攢 TrendAnalysis.vue
+鈹�     鈹�  鈹溾攢 HistoryMode.vue
+鈹�     鈹�  鈹斺攢 HistoryMode2.vue
+鈹�     鈹溾攢 HomePage.vue
+鈹�     鈹溾攢 LoginPage.vue
+鈹�     鈹溾攢 LoginPage_Backup.vue
+鈹�     鈹溾攢 realtimemode
+鈹�     鈹�  鈹溾攢 component
+鈹�     鈹�  鈹�  鈹溾攢 DashBoard.vue
+鈹�     鈹�  鈹�  鈹溾攢 DeviceChange.vue
+鈹�     鈹�  鈹�  鈹斺攢 RealTimeTrend.vue
+鈹�     鈹�  鈹斺攢 RealtimeMode.vue
+鈹�     鈹溾攢 riskmodel
+鈹�     鈹�  鈹斺攢 RiskMode.vue
+鈹�     鈹斺攢 underwaymix
+鈹�        鈹斺攢 UnderwayMixMode.vue
+鈹溾攢 test
+鈹�  鈹斺攢 grid_test_data.js
+鈹溾攢 vite.config.js
+鈹斺攢 vitest.config.js
+
+```
\ No newline at end of file
diff --git a/src/api/gridApi.js b/src/api/gridApi.js
new file mode 100644
index 0000000..8abfe14
--- /dev/null
+++ b/src/api/gridApi.js
@@ -0,0 +1,152 @@
+import { $http } from './index';
+
+import { Base64 } from 'js-base64';
+/**
+ * 鍗槦閬ユ祴缃戞牸鐩稿叧鎺ュ彛API
+ */
+export default {
+  fetchGridGroup(area, type, page, perPage) {
+    return $http
+      .post(`air/satellite/grid/group`, area, {
+        params: {
+          type,
+          page: page,
+          per_page: perPage
+        }
+      })
+      .then((res) => res.data);
+  },
+
+  fetchGridCell(groupId) {
+    return $http
+      .get(`air/satellite/grid/cell`, {
+        params: {
+          groupId
+        }
+      })
+      .then((res) => res.data);
+  },
+
+  /**
+   * 鑾峰彇缃戞牸缁勪笅鐨勯仴娴嬫暟鎹�
+   * @param {*} groupId
+   * @param {*} dataTime
+   * @param {number} type 閬ユ祴鏁版嵁绫诲瀷锛�0锛氬師濮嬪崼鏄熸暟鎹紝1锛氳瀺鍚堟暟鎹�
+   * @returns
+   */
+  fetchGridData(groupId, dataTime, type) {
+    return $http
+      .get(`air/satellite/grid/data`, {
+        params: {
+          groupId,
+          dataTime,
+          type
+        }
+      })
+      .then((res) => res.data);
+  },
+
+  // /**
+  //  * 鑾峰彇缃戞牸缁勪笅鐨勯仴娴媋od
+  //  * @param {*} groupId
+  //  * @param {*} dataTime
+  //  * @returns
+  //  */
+  // fetchGridAod(groupId, dataTime) {
+  //   return $http
+  //     .get(`air/satellite/grid/aod`, {
+  //       params: {
+  //         groupId,
+  //         dataTime
+  //       }
+  //     })
+  //     .then((res) => res.data);
+  // },
+
+  fetchGridDataDetail(dataId, groupId, cellId) {
+    return $http
+      .get(`air/satellite/grid/data/detail`, {
+        params: {
+          dataId,
+          groupId,
+          cellId
+        }
+      })
+      .then((res) => res.data);
+  },
+
+  createGridDataAndDataDetail(groupId, dataTime, dataDetailList) {
+    return $http
+      .post(`air/satellite/grid/data/create`, {
+        params: {
+          groupId,
+          dataTime
+        },
+        data: dataDetailList
+      })
+      .then((res) => res.data);
+  },
+
+  downloadTemplate() {
+    return $http
+      .get(`air/satellite/import/grid/data/download/template`, {
+        responseType: 'blob'
+      })
+      .then((res) => {
+        const name = Base64.decode(res.headers.get('fileName'));
+        const blob = new Blob([res.data], {
+          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+        });
+        const url = window.URL.createObjectURL(blob);
+        const link = document.createElement('a');
+        link.href = url;
+        link.download = name;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        window.URL.revokeObjectURL(url);
+      });
+  },
+  importData(dataForm) {
+    return $http
+      .post(`air/satellite/import/grid/data`, dataForm)
+      .then((res) => res.data);
+  },
+  downloadAODTemplate() {
+    return $http
+      .get(`air/satellite/import/grid/aod/download/template`, {
+        responseType: 'blob'
+      })
+      .then((res) => {
+        const name = Base64.decode(res.headers.get('fileName'));
+        const blob = new Blob([res.data], {
+          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+        });
+        const url = window.URL.createObjectURL(blob);
+        const link = document.createElement('a');
+        link.href = url;
+        link.download = name;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        window.URL.revokeObjectURL(url);
+      });
+  },
+  importAOD(dataForm) {
+    return $http
+      .post(`air/satellite/import/grid/aod`, dataForm)
+      .then((res) => res.data);
+  },
+
+  mixGridData(dataIdList) {
+    return $http
+      .post(`air/satellite/grid/data/mix`, dataIdList)
+      .then((res) => res.data);
+  },
+
+  buildUnderwayProduct(missionCode, groupId) {
+    return $http.get(`air/satellite/import/grid/aod/download/template`, {
+      responseType: 'blob'
+    });
+  }
+};
diff --git a/src/components.d.ts b/src/components.d.ts
index d714fd9..4e0dac8 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -33,6 +33,7 @@
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
diff --git a/src/model/Legend.js b/src/model/Legend.js
index 9dfbcc4..05ee593 100644
--- a/src/model/Legend.js
+++ b/src/model/Legend.js
@@ -142,21 +142,13 @@
     ]
   },
 
-  // _custom: [
-  //   [0.05, 0.9, 0.03, 0.75],
-  //   [0.3, 0.65, 0.02, 0.75],
-  //   [0.87, 0.92, 0.03, 0.75],
-  //   [0.8, 0.67, 0.04, 0.75],
-  //   [0.92, 0.28, 0.07, 0.75],
-  //   [0.6, 0.05, 0.05, 0.75]
-  // ],
   _custom: [
-    [0, 0.89, 0, 0.75],
-    [1, 1, 0, 0.75],
-    [1, 0.49, 0, 0.75],
-    [1, 0, 0, 0.75],
-    [0.6, 0, 0.3, 0.75],
-    [0.49, 0, 0.14, 0.75]
+    [0.05, 0.9, 0.03, 0.75],
+    [0.3, 0.65, 0.02, 0.75],
+    [0.87, 0.92, 0.03, 0.75],
+    [0.8, 0.67, 0.04, 0.75],
+    [0.92, 0.28, 0.07, 0.75],
+    [0.96, 0.05, 0.05, 0.75]
   ],
 
   getStandardRange: function (name) {
@@ -181,6 +173,9 @@
   },
 
   getColor: function (name, type, data, min, max) {
+    if (!data) {
+      return [0, 0, 0, 0];
+    }
     if (type == this.S_TYPE) {
       return this.getStandardColor(name, data);
     } else {
@@ -247,6 +242,54 @@
     return colors[selected];
   },
 
+  getStandardColorAndNext: function (name, data) {
+    if (!data) {
+      return {
+        color: [0, 0, 0, 0],
+        nextColor: [0, 0, 0, 0],
+        range: 0,
+        nextRange: 0
+      };
+    }
+    let range = this._legend_r[name];
+    let colors = this._legend_c[name];
+    if (range == undefined) {
+      range = this._legend_r['PM25'];
+      colors = this._legend_c['PM25'];
+    }
+
+    let selected = undefined;
+    for (let i = 0; i < range.length; i++) {
+      const d = range[i];
+      let d1 = d;
+      if (name == 'CO') {
+        d1 *= 1000;
+      }
+      if (data >= d1) {
+        selected = i;
+      } else {
+        break;
+      }
+    }
+
+    // 閬垮厤涓嬫爣瓒婄晫
+    if (selected >= colors.length) {
+      selected = colors.length - 1;
+    }
+
+    let nextIndex = selected + 1;
+    if (nextIndex >= colors.length) {
+      nextIndex = colors.length - 1;
+    }
+
+    return {
+      color: colors[selected],
+      nextColor: colors[nextIndex],
+      range: range[selected],
+      nextRange: range[nextIndex]
+    };
+  },
+
   getCustomColor: function (data, min, max) {
     var per = (max - min) / this._custom.length;
     var i = parseInt((data - min) / per);
@@ -254,6 +297,35 @@
       i = this._custom.length - 1;
     }
     return this._custom[i];
+  },
+
+  getCustomColorAndNext: function (data, min, max) {
+    if (!data) {
+      return {
+        color: [0, 0, 0, 0],
+        nextColor: [0, 0, 0, 0],
+        range: 0,
+        nextRange: 0
+      };
+    }
+
+    // 灏嗘暟鎹寜鐓ч鑹叉暟閲忓垎闅旓紝姹傚嚭姣忎竴娈电殑鏁版嵁鍋忕Щ閲�
+    var per = (max - min) / (this._custom.length - 1);
+    // 璁$畻褰撳墠鏁版嵁鎵�鍦ㄧ殑鍒嗘鑼冨洿
+    var i = parseInt((data - min) / per);
+    // 濡傛灉鏄渶澶у�硷紝鍚屾牱鍒嗗壊鍒版渶鍚庝竴娈�
+    if (i == this._custom.length - 1) i--;
+    var range = min + i * per;
+
+    let nextIndex = i + 1;
+    let nextRange = min + nextIndex * per;
+
+    return {
+      color: this._custom[i],
+      nextColor: this._custom[nextIndex],
+      range,
+      nextRange
+    };
   }
 };
 
diff --git a/src/model/SatelliteGrid.js b/src/model/SatelliteGrid.js
new file mode 100644
index 0000000..4bc2504
--- /dev/null
+++ b/src/model/SatelliteGrid.js
@@ -0,0 +1,488 @@
+import calculate from '@/utils/map/calculate';
+import gridMapUtil from '@/utils/map/grid';
+import { map, onMapMounted } from '@/utils/map/index_old';
+import { useCloned } from '@vueuse/core';
+
+/**
+ * 鍗槦閬ユ祴缃戞牸绠$悊
+ */
+export class SatelliteGrid {
+  constructor(name) {
+    this.name = name;
+  }
+
+  // 鍦板浘缃戞牸鐩稿叧瀵硅薄
+  mapViews;
+
+  mapViewsList = [];
+
+  mapViewsMap = new Map();
+
+  districtPolygon;
+
+  events = new Map();
+
+  // 缁樺埗鍖哄幙杈圭晫
+  drawDistrict(districtName, isNew) {
+    onMapMounted(() => {
+      if (this.districtPolygon && !isNew) {
+        map.remove(this.districtPolygon);
+        map.add(this.districtPolygon);
+      } else {
+        // eslint-disable-next-line no-undef
+        var district = new AMap.DistrictSearch({
+          extensions: 'all', //杩斿洖琛屾斂鍖鸿竟鐣屽潗鏍囩瓑鍏蜂綋淇℃伅
+          level: 'district' //璁剧疆鏌ヨ琛屾斂鍖虹骇鍒负鍖�
+        });
+        district.search(districtName, (status, result) => {
+          var bounds = result.districtList[0].boundaries; //鑾峰彇鏈濋槼鍖虹殑杈圭晫淇℃伅
+          if (bounds) {
+            for (var i = 0; i < bounds.length; i++) {
+              //鐢熸垚琛屾斂鍖哄垝 polygon
+              // eslint-disable-next-line no-undef
+              this.districtPolygon = new AMap.Polygon({
+                map: map, //鏄剧ず璇ヨ鐩栫墿鐨勫湴鍥惧璞�
+                strokeWeight: 2, //杞粨绾垮搴�
+                path: bounds[i], //澶氳竟褰㈣疆寤撶嚎鐨勮妭鐐瑰潗鏍囨暟缁�
+                fillOpacity: 0, //澶氳竟褰㈠~鍏呴�忔槑搴�
+                fillColor: '#CCF3FF', //澶氳竟褰㈠~鍏呴鑹�
+                // strokeColor: '#ffffff' //绾挎潯棰滆壊
+                strokeColor: '#0552f7', //绾挎潯棰滆壊
+                zIndex: 9
+              });
+            }
+            map.setFitView(); //灏嗚鐩栫墿璋冩暣鍒板悎閫傝閲�
+          }
+        });
+      }
+    });
+  }
+
+  clearAll(mapViews) {
+    if (mapViews) {
+      if (typeof mapViews.gridViews === 'object') {
+        map.remove(mapViews.gridViews);
+      }
+    }
+    this.clearText(mapViews);
+  }
+
+  clearText(mapViews) {
+    if (mapViews) {
+      if (typeof mapViews.dataTxt === 'object') {
+        map.remove(mapViews.dataTxt);
+      }
+      if (typeof mapViews.dataLayer === 'object') {
+        map.remove(mapViews.dataLayer);
+      }
+      if (typeof mapViews.rankTxt === 'object') {
+        map.remove(mapViews.rankTxt);
+      }
+      if (typeof mapViews.rankLayer === 'object') {
+        map.remove(mapViews.rankLayer);
+      }
+    }
+  }
+
+  firstEvent;
+
+  // 缁樺埗缃戞牸绾�
+  drawPolyline(gridInfo, event) {
+    this.firstEvent = event;
+    // 缁樺埗缃戞牸
+    const points = gridInfo.map((v) => {
+      return calculate.wgs84_To_Gcj02(v.longitude, v.latitude);
+      // return [v.longitude, v.latitude];
+    });
+    // const gridPoints = gridMapUtil.parseGridPoint(points);
+    // console.log('gridPoints:', gridPoints);
+
+    const gridPoints = gridInfo.map((v, i) => {
+      return {
+        path: [
+          calculate.wgs84_To_Gcj02(v.point1Lon, v.point1Lat),
+          calculate.wgs84_To_Gcj02(v.point2Lon, v.point2Lat),
+          calculate.wgs84_To_Gcj02(v.point3Lon, v.point3Lat),
+          calculate.wgs84_To_Gcj02(v.point4Lon, v.point4Lat)
+          // [v.point1Lon, v.point1Lat],
+          // [v.point2Lon, v.point2Lat],
+          // [v.point3Lon, v.point3Lat],
+          // [v.point4Lon, v.point4Lat]
+        ]
+          // eslint-disable-next-line no-undef
+          .map((d) => new AMap.LngLat(d[0], d[1])),
+        extData: {
+          centerPoint: points[i],
+          // gridPoints,
+          gridCell: v
+        }
+      };
+    });
+    const gridViews = gridMapUtil.drawPolylines({ points: gridPoints, event });
+    return { gridViews, gridPoints, points };
+  }
+
+  // 缁樺埗鐩戞祴鏁版嵁鍊�
+  drawDataText(points, gridDataDetail, textViews, isCustomColor, useColor) {
+    const data = gridDataDetail.map((v, i) => {
+      return {
+        lnglat_GD: points[i],
+        // data: v.pm25 ? (v.pm25 + '渭g/m鲁') : ''
+        data: v.pm25 ? v.pm25 : ''
+      };
+    });
+    // return gridMapUtil.drawGridTextLabel(data, textViews, labelsLayer, 'bottom');
+    return gridMapUtil.drawGridText({
+      points: data,
+      textViews,
+      anchor: 'top-center',
+      type: 'data',
+      isCustomColor,
+      useColor
+    });
+  }
+
+  // 缁樺埗鐩戞祴鏁版嵁鎺掑悕鏂囨湰
+  drawRankText(points, gridDataDetail, textViews, labelsLayer) {
+    const data = gridDataDetail.map((v, i) => {
+      return {
+        lnglat_GD: points[i],
+        // data: v.pm25 ? ('鎺掑悕: ' + v.rank) : ''
+        data: v.pm25 ? v.rank : ''
+      };
+    });
+    // return gridMapUtil.drawGridTextLabel(data, textViews, labelsLayer, 'top');
+    return gridMapUtil.drawGridText({
+      points: data,
+      textViews,
+      anchor: 'bottom-center',
+      type: 'rank'
+    });
+  }
+
+  // 缁樺埗鐩戞祴鏁版嵁鍊煎搴旂綉鏍奸鑹�
+  drawColor({
+    gridViews,
+    points,
+    gridDataDetail,
+    lastGridViews,
+    opacity,
+    zIndex,
+    customColor
+  }) {
+    // 鏍规嵁鏁版嵁绛涢�夋湁鏁版嵁鐨勭綉鏍�
+    const res = [];
+    // 浠ュ強瀵瑰簲鐨勪腑蹇冪偣鍧愭爣
+    const pointsRes = [];
+    gridDataDetail.forEach((d) => {
+      // 鏍规嵁鏁版嵁鍏宠仈鐨勭綉鏍肩紪鍙凤紝鎵惧埌瀵瑰簲缃戞牸
+      const cellId = d.cellId;
+      if (cellId > gridViews.length) {
+        throw Error(
+          '閬ユ祴鏁版嵁鐨勭綉鏍肩储寮曠紪鍙疯秴鍑虹綉鏍肩粍鑼冨洿锛屾暟鎹拰缃戞牸缁勫彲鑳戒笉瀵瑰簲'
+        );
+      }
+      res.push(gridViews[cellId - 1]);
+      pointsRes.push(points[cellId - 1]);
+    });
+
+    // 鏍规嵁缁樺埗棰滆壊鏂瑰紡缁樺埗缃戞牸
+    let resGridViews;
+    if (customColor) {
+      resGridViews = gridMapUtil.drawGridColorCustom(
+        res,
+        gridDataDetail,
+        opacity,
+        zIndex
+      );
+    } else {
+      resGridViews = gridMapUtil.drawGridColor(
+        res,
+        gridDataDetail,
+        'PM25',
+        opacity,
+        zIndex
+      );
+    }
+
+    if (lastGridViews) {
+      map.remove(lastGridViews);
+    }
+    map.add(resGridViews);
+    // map.setFitView(resGridViews);
+
+    return { resGridViews, pointsRes };
+  }
+
+  // 鍗槦缃戞牸閰嶇疆鍑嗗
+  gridPrepare(gridInfo, event) {
+    // clearText(mapViews);
+    this.clearAll(this.mapViews);
+    this.mapViews = this.drawPolyline(gridInfo, event);
+  }
+
+  // 缁樺埗缃戞牸閬ユ劅鏁版嵁鍊煎拰缃戞牸棰滆壊
+  drawGrid({
+    gridDataDetail,
+    useCustomColor,
+    opacity,
+    zIndex,
+    showDataTxt,
+    showRankTxt,
+    useDataTxtColor,
+    mapViews
+  }) {
+    const _mapViews = mapViews ? mapViews : this.mapViews;
+    // SatelliteProxy.clearText(mapViews);
+    const { resGridViews, pointsRes } = this.drawColor({
+      gridViews: _mapViews.gridViews,
+      points: _mapViews.points,
+      gridDataDetail: gridDataDetail,
+      lastGridViews: _mapViews.lastGridViews,
+      customColor: useCustomColor,
+      opacity: opacity,
+      zIndex: zIndex
+    });
+    _mapViews.lastGridViews = resGridViews;
+    _mapViews.lastPoints = pointsRes;
+
+    // 鏁版嵁鏍囪
+    const { textViews: dataTxt, labelsLayer: dataLayer } = this.drawDataText(
+      _mapViews.lastPoints,
+      gridDataDetail,
+      _mapViews.dataTxt,
+      useCustomColor,
+      useDataTxtColor
+    );
+    _mapViews.dataTxt = dataTxt;
+    _mapViews.dataLayer = dataLayer;
+
+    const { textViews: rankTxt, labelsLayer: rankLayer } = this.drawRankText(
+      _mapViews.lastPoints,
+      gridDataDetail,
+      _mapViews.rankTxt,
+      _mapViews.rankLayer
+    );
+    _mapViews.rankTxt = rankTxt;
+    _mapViews.rankLayer = rankLayer;
+
+    if (showDataTxt) {
+      map.add(_mapViews.dataTxt);
+    }
+
+    if (showRankTxt) {
+      map.add(_mapViews.rankTxt);
+    }
+  }
+
+  //
+  drawGrids({
+    gridDataDetailList,
+    useCustomColor,
+    opacity,
+    zIndex,
+    showDataTxt,
+    showRankTxt
+  }) {
+    if (this.mapViewsList.length < gridDataDetailList.length) {
+      let index = this.mapViewsList.length;
+      while (index < gridDataDetailList.length) {
+        const newMapViews = {
+          gridViews: gridMapUtil.drawPolylines({
+            points: this.mapViews.gridPoints,
+            event: this.firstEvent
+          }),
+          gridPoints: JSON.parse(JSON.stringify(this.mapViews.gridPoints)),
+          points: JSON.parse(JSON.stringify(this.mapViews.points))
+        };
+        this.mapViewsList.push(newMapViews);
+      }
+    }
+
+    this.mapViewsList.forEach((m, i) => {
+      this.drawGrid({
+        gridDataDetail: gridDataDetailList[i],
+        useCustomColor,
+        opacity,
+        zIndex,
+        showDataTxt,
+        showRankTxt,
+        mapViews: m
+      });
+    });
+  }
+
+  drawNewGrid({
+    gridDataDetail,
+    useCustomColor,
+    opacity,
+    zIndex,
+    showDataTxt,
+    showRankTxt
+  }) {
+    const newMapViews = {
+      gridViews: gridMapUtil.drawPolylines({
+        points: this.mapViews.gridPoints,
+        event: this.firstEvent
+      }),
+      gridPoints: JSON.parse(JSON.stringify(this.mapViews.gridPoints)),
+      points: JSON.parse(JSON.stringify(this.mapViews.points))
+    };
+    this.mapViewsList.push(newMapViews);
+    this.drawGrid({
+      gridDataDetail,
+      useCustomColor,
+      opacity,
+      zIndex,
+      showDataTxt,
+      showRankTxt,
+      mapViews: newMapViews
+    });
+  }
+  drawTagGrid({
+    tag,
+    gridDataDetail,
+    useCustomColor,
+    opacity,
+    zIndex,
+    showDataTxt,
+    showRankTxt
+  }) {
+    if (!this.mapViewsMap.has(tag)) {
+      const newMapViews = {
+        gridViews: gridMapUtil.drawPolylines({
+          points: this.mapViews.gridPoints,
+          event: this.firstEvent
+        }),
+        gridPoints: JSON.parse(JSON.stringify(this.mapViews.gridPoints)),
+        points: JSON.parse(JSON.stringify(this.mapViews.points))
+      };
+      this.mapViewsMap.set(tag, newMapViews);
+    }
+    const _mapViews = this.mapViewsMap.get(tag);
+    this.drawGrid({
+      gridDataDetail,
+      useCustomColor,
+      opacity,
+      zIndex,
+      showDataTxt,
+      showRankTxt,
+      mapViews: _mapViews
+    });
+  }
+
+  // 璋冩暣鍚勭被鍦板浘瑕嗙洊鐗╃殑鍙鎬�
+  changeVisibility({ tag, showGridViews, showDataTxt, showRankTxt }) {
+    let _mapViewsList = [];
+    if (this.mapViews) {
+      _mapViewsList.push(this.mapViews);
+    }
+    if (tag && this.mapViewsMap.has(tag)) {
+      _mapViewsList.push(this.mapViewsMap.get(tag));
+    } else {
+      this.mapViewsMap.forEach((v) => {
+        _mapViewsList.push(v);
+      });
+    }
+    if (showGridViews != undefined) {
+      if (showGridViews) {
+        // map.add(this.mapViews.lastGridViews);
+        _mapViewsList.forEach((v) => {
+          if (v.lastGridViews) map.add(v.lastGridViews);
+        });
+      } else {
+        // map.remove(this.mapViews.lastGridViews);
+        _mapViewsList.forEach((v) => {
+          if (v.lastGridViews) map.remove(v.lastGridViews);
+        });
+      }
+    }
+    if (showDataTxt != undefined) {
+      if (showDataTxt) {
+        // map.add(this.mapViews.dataTxt);
+        _mapViewsList.forEach((v) => {
+          if (v.dataTxt) map.add(v.dataTxt);
+        });
+      } else {
+        // map.remove(this.mapViews.dataTxt);
+        _mapViewsList.forEach((v) => {
+          if (v.dataTxt) map.remove(v.dataTxt);
+        });
+      }
+    }
+    if (showRankTxt != undefined) {
+      if (showRankTxt) {
+        // map.add(this.mapViews.rankTxt);
+        _mapViewsList.forEach((v) => {
+          if (v.rankTxt) map.add(v.rankTxt);
+        });
+      } else {
+        // map.remove(this.mapViews.rankTxt);
+        _mapViewsList.forEach((v) => {
+          if (v.rankTxt) map.remove(v.rankTxt);
+        });
+      }
+    }
+  }
+
+  changeGridOpacity({ tag, isOpacity, opacityValue }) {
+    let _mapViewsList = [];
+    if (this.mapViews) {
+      _mapViewsList.push(this.mapViews);
+    }
+    if (tag && this.mapViewsMap.has(tag)) {
+      _mapViewsList.push(this.mapViewsMap.get(tag));
+    } else {
+      this.mapViewsMap.forEach((v) => {
+        _mapViewsList.push(v);
+      });
+    }
+    _mapViewsList.forEach((v) => {
+      if (v.lastGridViews) {
+        v.lastGridViews.forEach((e) => {
+          e.setOptions({
+            fillOpacity:
+              typeof opacityValue === 'number'
+                ? opacityValue
+                : isOpacity
+                  ? 0.1
+                  : 0.7
+          });
+        });
+      }
+    });
+  }
+
+  setGridEvent(name, event) {
+    if (!this.events.has(name)) {
+      this.events.set(name, []);
+    }
+    const list = this.events.get(name);
+    if (list.length > 0) {
+      const lastEvent = list[list.length - 1];
+      this.mapViews.gridViews.forEach((polygon) => {
+        polygon.off(name, lastEvent);
+      });
+    }
+    this.events.get(name).push(event);
+    this.mapViews.gridViews.forEach((polygon) => {
+      polygon.on(name, event);
+    });
+  }
+
+  goBackGridEvent(name) {
+    if (this.events.has(name)) {
+      const eventList = this.events.get(name);
+      //鍏堢Щ闄ゅ師鏈夌殑浜嬩欢
+      const lastEvent = eventList.pop();
+      this.mapViews.gridViews.forEach((polygon) => {
+        polygon.off(name, lastEvent);
+      });
+      //鑾峰彇涓婁竴涓簨浠�
+      const event = eventList.pop();
+      this.mapViews.gridViews.forEach((polygon) => {
+        polygon.on(name, event);
+      });
+    }
+  }
+}
diff --git a/src/styles/base.scss b/src/styles/base.scss
index 8e29e01..bf66424 100644
--- a/src/styles/base.scss
+++ b/src/styles/base.scss
@@ -71,7 +71,7 @@
   default: var(--el-component-size-default),
   large: var(--el-component-size-large)
 );
-$ws: (20, 40, 50, 60, 80, 100, 120, 150, 300);
+$ws: (20, 40, 50, 60, 80, 100, 120, 150, 200, 250, 300);
 @each $name, $value in $csize {
   .w-#{$name} {
     width: #{$value};
diff --git a/src/utils/map/3dLayer.js b/src/utils/map/3dLayer.js
index 9720c1a..01ff1d2 100644
--- a/src/utils/map/3dLayer.js
+++ b/src/utils/map/3dLayer.js
@@ -168,6 +168,7 @@
 
 export default {
   clear() {
+    map.off('zoomend', onMapZoom);
     if (_cylinder != undefined) {
       object3Dlayer.remove(_cylinder);
     }
diff --git a/src/utils/map/calculate.js b/src/utils/map/calculate.js
index 71859bc..5fe8f49 100644
--- a/src/utils/map/calculate.js
+++ b/src/utils/map/calculate.js
@@ -133,6 +133,57 @@
   },
 
   /**
+   * 鑾峰彇涓や釜缁忕含搴︿箣闂寸殑瑙掑害锛�0搴�-360搴︼級
+   */
+  getAngle(lng_a, lat_a, lng_b, lat_b) {
+    var a = ((90 - lat_b) * Math.PI) / 180;
+    var b = ((90 - lat_a) * Math.PI) / 180;
+    var AOC_BOC = ((lng_b - lng_a) * Math.PI) / 180;
+    var cosc =
+      Math.cos(a) * Math.cos(b) + Math.sin(a) * Math.sin(b) * Math.cos(AOC_BOC);
+    var sinc = Math.sqrt(1 - cosc * cosc);
+    var sinA = (Math.sin(a) * Math.sin(AOC_BOC)) / sinc;
+    var A = (Math.asin(sinA) * 180) / Math.PI;
+    var res = 0;
+    if (lng_b > lng_a && lat_b > lat_a) res = A;
+    else if (lng_b > lng_a && lat_b < lat_a) res = 180 - A;
+    else if (lng_b < lng_a && lat_b < lat_a) res = 180 - A;
+    else if (lng_b < lng_a && lat_b > lat_a) res = 360 + A;
+    else if (lng_b > lng_a && lat_b == lat_a) res = 90;
+    else if (lng_b < lng_a && lat_b == lat_a) res = 270;
+    else if (lng_b == lng_a && lat_b > lat_a) res = 0;
+    else if (lng_b == lng_a && lat_b < lat_a) res = 180;
+    return res;
+  },
+
+  /**
+   * 鑾峰彇涓ょ粡绾害闂寸殑璺濈
+   */
+  getDistance(lng1, lat1, lng2, lat2) {
+    lat1 = lat1 || 0;
+    lng1 = lng1 || 0;
+    lat2 = lat2 || 0;
+    lng2 = lng2 || 0;
+
+    var rad1 = (lat1 * Math.PI) / 180.0;
+    var rad2 = (lat2 * Math.PI) / 180.0;
+    var a = rad1 - rad2;
+    var b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0;
+    var r = 6378137;
+    var distance =
+      r *
+      2 *
+      Math.asin(
+        Math.sqrt(
+          Math.pow(Math.sin(a / 2), 2) +
+            Math.cos(rad1) * Math.cos(rad2) * Math.pow(Math.sin(b / 2), 2)
+        )
+      );
+
+    return distance;
+  },
+
+  /**
    * 灏嗕簩缁存暟缁勫舰寮忕殑鍧愭爣鐐规暟缁勮浆鎹负楂樺痉鍦板浘涓� LngLat 绫�
    * @param {*} lnglats
    * @returns
diff --git a/src/utils/map/grid.js b/src/utils/map/grid.js
index 351bd2c..cbd6e1c 100644
--- a/src/utils/map/grid.js
+++ b/src/utils/map/grid.js
@@ -2,51 +2,419 @@
  * 缃戞牸缁樺埗
  */
 import { map } from './index_old';
+import calculate from './calculate';
+import { Legend } from '@/model/Legend';
+import { getHexColor, getColorBetweenTwoColors } from '../color';
+
+/**
+ * 瑙掑害澧炲噺锛岀‘淇濊搴﹀浜�0 - 360搴︿箣闂�
+ * @param {number} angle 鍘熻搴�
+ * @param {number} offset 鍋忕Щ閲�
+ */
+function plusAngle(angle, offset) {
+  const result = angle + offset;
+  if (result > 360) {
+    return result - 360;
+  } else if (result < 0) {
+    return result + 360;
+  } else {
+    return result;
+  }
+}
+
+/**
+ * 鏍规嵁缃戞牸涓績鐐癸紝鐢熸垚姝f柟褰㈢綉鏍�4涓《鐐圭殑鍧愭爣
+ * @param {Array} points 缃戞牸涓績鐐圭粡绾害鏁扮粍
+ */
+function parseGridPoint(points) {
+  if (points.length < 2) throw new Error('鍧愭爣鐐规暟閲忓皬浜�2');
+  const p1 = points[0];
+  const p2 = points[1];
+  // 涓や腑蹇冪偣闂寸殑瑙掑害
+  const angle = calculate.getAngle(p1[0], p1[1], p2[0], p2[1]);
+  // const angle = calculate.bearing(
+  //   { latitude: p1[0], longitude: p1[1] },
+  //   { latitude: p2[0], longitude: p2[1] }
+  // );
+  // 涓や腑蹇冪偣闂寸殑璺濈
+  const dis = calculate.getDistance(p1[0], p1[1], p2[0], p2[1]);
+  // 缃戞牸姝f柟褰㈠瑙掔嚎鐨勪竴鍗婇暱搴�
+  const halfDiagonal = Math.sqrt((dis / 2) * (dis / 2) * 2);
+  // 璁$畻棣栦釜姝f柟褰㈠悇椤剁偣鐩稿浜庝腑蹇冪偣鐨勮搴︼紝寰楀埌姝f柟褰㈠悇椤剁偣鐨勫潗鏍�
+  const angle1 = plusAngle(angle, 45);
+  const gp1 = calculate.getLatLon(p1, halfDiagonal, angle1);
+  const angle2 = plusAngle(angle1, 90);
+  const gp2 = calculate.getLatLon(p1, halfDiagonal, angle2);
+  const angle3 = plusAngle(angle2, 90);
+  const gp3 = calculate.getLatLon(p1, halfDiagonal, angle3);
+  const angle4 = plusAngle(angle3, 90);
+  const gp4 = calculate.getLatLon(p1, halfDiagonal, angle4);
+  // 璁$畻4涓《鐐瑰垎鍒笌涓績鐐圭殑缁忕含搴﹀樊鍊�
+  const diff = {
+    diff1: {
+      dx: gp1[0] - p1[0],
+      dy: gp1[1] - p1[1]
+    },
+    diff2: {
+      dx: gp2[0] - p1[0],
+      dy: gp2[1] - p1[1]
+    },
+    diff3: {
+      dx: gp3[0] - p1[0],
+      dy: gp3[1] - p1[1]
+    },
+    diff4: {
+      dx: gp4[0] - p1[0],
+      dy: gp4[1] - p1[1]
+    }
+  };
+
+  // 寰楀埌鎵�鏈夋鏂瑰舰缃戞牸鐨�4涓《鐐逛俊鎭�
+  return points.map((p) => {
+    return [
+      [p[0] + diff.diff1.dx, p[1] + diff.diff1.dy],
+      [p[0] + diff.diff2.dx, p[1] + diff.diff2.dy],
+      [p[0] + diff.diff3.dx, p[1] + diff.diff3.dy],
+      [p[0] + diff.diff4.dx, p[1] + diff.diff4.dy]
+    ];
+  });
+}
+
+/**
+ * 鏂囨湰鏍囪
+ * 鍙慨鏀筽osition
+ */
+function textMaker({ position, text, anchor, type, color }) {
+  let style = {};
+  if (type == 'data') {
+    style = {
+      'font-size': '12px',
+      'text-align': 'center',
+      'font-weight': 600,
+      color: color ? color : 'white',
+      background: '#122b54a9',
+      // background: 'white',
+      'text-shadow': 'black 1px 1px 1px',
+      border: '0px',
+      'margin-top': '4px'
+    };
+  } else if (type == 'rank') {
+    style = {
+      'font-size': '14px',
+      'text-align': 'center',
+      color: color ? color : 'white',
+      background: 'transparent',
+      'text-shadow': 'black 2px 2px 2px',
+      'border-radius': '2px',
+      border: '1px solid #122b54a9',
+      // border: '1px solid rgba(255, 255, 255, 0.62)',
+      'margin-bottom': '4px'
+    };
+  }
+  // eslint-disable-next-line no-undef
+  return new AMap.Text({
+    text: text,
+    anchor,
+    position: position,
+    style: style
+  });
+}
+
+/**
+ * 娴烽噺鏂囨湰鏍囨敞
+ */
+function textLabelMarker(position, text, direction, style) {
+  // eslint-disable-next-line no-undef
+  return new AMap.LabelMarker({
+    position: position,
+    zooms: [10, 20],
+    opacity: 1,
+    zIndex: 2,
+    // icon: {
+    //   image: 'https://a.amap.com/jsapi_demos/static/images/poi-marker.png',
+    //   anchor: 'bottom-center',
+    //   size: [25, 34],
+    //   clipOrigin: [459, 92],
+    //   clipSize: [50, 68]
+    // },
+    text: {
+      // 娉ㄦ剰鍐呭鏍煎紡蹇呴』鏄痵tring
+      content: text ? text + '' : '',
+      direction: direction ? direction : 'center',
+      style: {
+        'border-radius': '.25rem',
+        fontSize: 12,
+        fillColor: '#fff',
+        strokeColor: 'rgba(0, 0, 0, 1)',
+        strokeWidth: 4,
+        // backgroundColor: '#122b54a9',
+        padding: [3, 10],
+        // backgroundColor: 'yellow',
+        borderColor: '#ccc',
+        borderWidth: 30
+      }
+    }
+  });
+}
+
+/**
+ * 璁$畻姣忎釜缃戞牸棰滆壊
+ */
+function calGridColor({ factorName, data, isCustomColor }) {
+  let _colorList = [];
+  if (isCustomColor) {
+    var max, min;
+    data.forEach((t) => {
+      if (!t) return;
+      if (!max || t > max) {
+        max = t;
+      }
+      if (!min || t < min) {
+        min = t;
+      }
+    });
+    data.forEach((d) => {
+      if (d) {
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const { color, nextColor, range, nextRange } =
+          Legend.getCustomColorAndNext(d, min, max);
+        const ratio = (d - range) / (nextRange - range);
+
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        _colorList.push(_color);
+      } else {
+        _colorList.push(undefined);
+      }
+    });
+  } else {
+    data.forEach((d) => {
+      if (d) {
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const { color, nextColor, range, nextRange } =
+          Legend.getStandardColorAndNext(factorName, d);
+        const ratio = (d - range) / (nextRange - range);
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        _colorList.push(_color);
+      } else {
+        _colorList.push(undefined);
+      }
+    });
+  }
+  return _colorList;
+}
 
 export default {
+  parseGridPoint,
+
   /**
    * 缁樺埗缃戞牸椋庨櫓鍥�
    * @param {*} points
    */
-  drawRectangle: function (points) {
+  drawRectangle(points) {
     const gridViews = [];
     points.forEach((p) => {
       const { lb, rt, c } = p;
 
-      // eslint-disable-next-line no-undef
-      let pList = [lb, rt].map((v) => new AMap.LngLat(v[0], v[1]));
+      let pList = [lb, rt].map((v) => {
+        // eslint-disable-next-line no-undef
+        return new AMap.LngLat(v[0], v[1]);
+      });
       // eslint-disable-next-line no-undef
       var bounds = new AMap.Bounds(...pList);
       // eslint-disable-next-line no-undef
       var rectangle = new AMap.Rectangle({
         bounds: bounds,
-        // strokeColor: '#ffffffff',
-        strokeWeight: 0,
-        strokeOpacity: 0,
+        strokeColor: '#ffffffff',
+        strokeWeight: 1,
+        strokeOpacity: 1,
         // strokeStyle杩樻敮鎸� solid
         strokeStyle: 'solid',
-        fillColor: '990D0D',
+        fillColor: '#990D0D',
         fillOpacity: 0.8,
         cursor: 'pointer',
         zIndex: 50
       });
-
-      // var text = new AMap.Text({
-      //   text: p.value,
-      //   anchor: 'center', // 璁剧疆鏂囨湰鏍囪閿氱偣
-      //   draggable: false,
-      //   style: {
-      //     'background-color': 'transparent',
-      //     'border-width': 0,
-      //     'text-align': 'center',
-      //     'font-size': '12px',
-      //     color: 'white'
-      //   },
-      //   position: c
-      // });
       gridViews.push(rectangle);
-      // that.textView.push(text);
     });
     map.add(gridViews);
+    map.setFitView(gridViews);
+  },
+
+  /**
+   * 缁樺埗涓�缁勫杈瑰舰
+   * @param {Array} points 缃戞牸鍧愭爣鐐规暟缁�
+   * @param {Boolean} draw 鏄惁鍒涘缓瀹屾垚鍚庡悓鏃剁粯鍒�
+   */
+  drawPolylines({ points, draw, event }) {
+    const gridViews = [];
+    points.forEach((p) => {
+      //鍒涘缓澶氳竟褰� Polygon 瀹炰緥
+      // eslint-disable-next-line no-undef
+      var polygon = new AMap.Polygon({
+        path: p.path, //璺緞
+        fillColor: '#fff', //澶氳竟褰㈠~鍏呴鑹�
+        strokeWeight: 1, //绾挎潯瀹藉害锛岄粯璁や负 2
+        strokeColor: 'white', //绾挎潯棰滆壊
+        fillOpacity: 0,
+        extData: p.extData
+      });
+
+      if (typeof event === 'function') {
+        event(polygon);
+      }
+      gridViews.push(polygon);
+    });
+    if (draw) {
+      map.add(gridViews);
+      map.setFitView(gridViews);
+    }
+    return gridViews;
+  },
+
+  drawGridText({ points, textViews, anchor, type, isCustomColor, useColor }) {
+    let colorList = [];
+    if (useColor) {
+      colorList = calGridColor({
+        factorName: 'PM25',
+        data: points.map((p) => p.data),
+        isCustomColor: isCustomColor
+      });
+    }
+    if (textViews) {
+      map.remove(textViews);
+    }
+    const _textViews = [];
+    points.forEach((p, i) => {
+      const m = textMaker({
+        position: p.lnglat_GD,
+        text: p.data,
+        anchor,
+        type,
+        color: useColor ? colorList[i] : 'white'
+      });
+      _textViews.push(m);
+    });
+    // map.add(_textViews);
+    return { textViews: _textViews };
+  },
+
+  drawGridTextLabel(points, textViews, labelsLayer, direction) {
+    if (textViews) {
+      points.forEach((p, i) => {
+        textViews[i].setPosition(p.lnglat_GD);
+        textViews[i].setText({
+          content: p.data ? p.data + '' : ''
+        });
+      });
+      return { textViews, labelsLayer };
+    } else {
+      // 鍒涘缓涓�涓� LabelsLayer 瀹炰緥鏉ユ壙杞� LabelMarker锛孾LabelsLayer 鏂囨。](https://lbs.amap.com/api/jsapi-v2/documentation#labelslayer)
+      // eslint-disable-next-line no-undef
+      const labelsLayer = new AMap.LabelsLayer({
+        zooms: [12, 20],
+        zIndex: 1000,
+        // 寮�鍚爣娉ㄩ伩璁╋紝榛樿涓哄紑鍚紝v1.4.15 鏂板灞炴��
+        collision: false,
+        // 寮�鍚爣娉ㄦ贰鍏ュ姩鐢伙紝榛樿涓哄紑鍚紝v1.4.15 鏂板灞炴��
+        animation: false
+      });
+      const _textViews = [];
+      points.forEach((p) => {
+        const m = textLabelMarker(p.lnglat_GD, p.data, direction);
+        _textViews.push(m);
+        // 灏� LabelMarker 瀹炰緥娣诲姞鍒� LabelsLayer 涓�
+        labelsLayer.add(m);
+      });
+      map.add(labelsLayer);
+      // map.on('zoomend', () => {
+      //   console.log(map.getZoom());
+      // });
+      return { textViews: _textViews, labelsLayer };
+    }
+  },
+
+  /**
+   * 鏍规嵁閬ユ祴鏁版嵁锛岃缃搴旂綉鏍肩殑鏍囧噯鑹诧紝杩斿洖鏈夋暟鎹殑缃戞牸
+   * @param {Array} gridViews 缃戞牸澶氳竟褰㈠璞℃暟缁�
+   * @param {Array} gridDataDetail 鍗槦閬ユ祴鏁版嵁鏁扮粍
+   * @param {string} factorName 鐩戞祴鍥犲瓙鍚嶇О
+   * @param {number} opacity 閫忔槑搴�
+   */
+  drawGridColor(gridViews, gridDataDetail, factorName, opacity, zIndex) {
+    const res = [];
+    // 閬嶅巻鍗槦閬ユ祴鏁版嵁鏁扮粍
+    gridDataDetail.forEach((d, i) => {
+      if (d.pm25) {
+        const grid = gridViews[i];
+
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const data = d.pm25;
+        const { color, nextColor, range, nextRange } =
+          Legend.getStandardColorAndNext(factorName, data);
+        const ratio = (data - range) / (nextRange - range);
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        grid.setOptions({
+          zIndex: zIndex ? zIndex : 10,
+          fillColor: _color,
+          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
+        });
+
+        res.push(grid);
+      }
+    });
+
+    return res;
+  },
+
+  drawGridColorCustom(gridViews, gridDataDetail, opacity) {
+    var max, min;
+    gridDataDetail.forEach((t) => {
+      if (!t.pm25) return;
+      if (!max || t.pm25 > max) {
+        max = t.pm25;
+      }
+      if (!min || t.pm25 < min) {
+        min = t.pm25;
+      }
+    });
+    const res = [];
+    // 閬嶅巻鍗槦閬ユ祴鏁版嵁鏁扮粍
+    gridDataDetail.forEach((d, i) => {
+      if (d.pm25) {
+        const grid = gridViews[i];
+
+        // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊
+        const data = d.pm25;
+        const { color, nextColor, range, nextRange } =
+          Legend.getCustomColorAndNext(data, min, max);
+        const ratio = (data - range) / (nextRange - range);
+
+        const _color = getColorBetweenTwoColors(
+          color.map((v) => v * 255),
+          nextColor.map((v) => v * 255),
+          ratio
+        );
+        grid.setOptions({
+          fillColor: _color,
+          fillOpacity: opacity ? opacity : color[3] == 0 ? 0 : 0.7
+        });
+
+        res.push(grid);
+      }
+    });
+
+    return res;
   }
 };
diff --git a/src/utils/map/line.js b/src/utils/map/line.js
index d2211a5..39dbccc 100644
--- a/src/utils/map/line.js
+++ b/src/utils/map/line.js
@@ -2,7 +2,8 @@
 import calculate from './calculate';
 import { getHexColor } from '../color';
 
-var _polylineArr = [];
+// var _polylineArr = [];
+const lineMap = new Map();
 
 function newPolyline(path, color) {
   // eslint-disable-next-line no-undef
@@ -21,13 +22,12 @@
 
 export default {
   drawLine(fDatas, factor) {
+    const _polylineArr = [];
     const lnglats_GD = fDatas.lnglats_GD;
     const colors = factor.colors;
 
-    if (_polylineArr) {
-      map.remove(_polylineArr);
-      _polylineArr = [];
-    }
+    // this.hideLine();
+
     var path = calculate.parse2LngLat(lnglats_GD);
 
     let sIndex = 0;
@@ -80,5 +80,26 @@
     // 灏嗘姌绾挎坊鍔犺嚦鍦板浘瀹炰緥
     map.add(_polylineArr);
     return _polylineArr;
+  },
+
+  drawTagLine(tag, fDatas, factor) {
+    if (lineMap.has(tag)) {
+      const _polylineArr = lineMap.get(tag);
+      map.add(_polylineArr);
+    } else {
+      const _polylineArr = this.drawLine(fDatas, factor);
+      lineMap.set(tag, _polylineArr);
+    }
+  },
+
+  hideLine(tag) {
+    if (tag && lineMap.has(tag)) {
+      const _polylineArr = lineMap.get(tag);
+      map.remove(_polylineArr);
+    } else {
+      lineMap.forEach((v) => {
+        map.remove(v);
+      });
+    }
   }
 };
diff --git a/src/views/realtimemode/RealtimeMode.vue b/src/views/realtimemode/RealtimeMode.vue
index da8a10b..7fce948 100644
--- a/src/views/realtimemode/RealtimeMode.vue
+++ b/src/views/realtimemode/RealtimeMode.vue
@@ -26,6 +26,7 @@
 
 <script>
 import moment from 'moment';
+import mapUtil from '@/utils/map/util';
 import { useFetchData } from '@/composables/fetchData';
 import { TYPE0 } from '@/constant/device-type';
 import { defaultOptions } from '@/constant/radio-options';
@@ -173,6 +174,7 @@
   unmounted() {
     this.clearFetchingTask();
     realTimeMapAnimation.stop();
+    mapUtil.clearMap();
   }
 };
 </script>
diff --git a/src/views/underwaymix/UnderwayMixMode.vue b/src/views/underwaymix/UnderwayMixMode.vue
index a26096a..bad7c78 100644
--- a/src/views/underwaymix/UnderwayMixMode.vue
+++ b/src/views/underwaymix/UnderwayMixMode.vue
@@ -1,6 +1,400 @@
 <template>
-  <div class="p-events-none m-t-2"></div>
+  <div class="p-events-none m-t-2">
+    <el-row justify="space-between">
+      <el-row class="wrap">
+        <el-col span="2">
+          <el-row>
+            <BaseCard v-show="show" size="medium" direction="left">
+              <template #content>
+                <el-row>
+                  <el-form :inline="true">
+                    <el-form-item label="璧拌埅铻嶅悎">
+                      <el-select
+                        v-model="selectedfusionData"
+                        multiple
+                        clearable
+                        @change="handleChange"
+                        placeholder="閫夋嫨浠诲姟"
+                        size="small"
+                        class="w-250"
+                        :loading="fusionLoading"
+                      >
+                        <el-option
+                          v-for="(s, i) in fusionDataList"
+                          :key="i"
+                          :label="s.mixDataId"
+                          :value="i"
+                        />
+                      </el-select>
+                    </el-form-item>
+                    <el-form-item>
+                      <el-button
+                        type="primary"
+                        class="el-button-custom"
+                        size="small"
+                        :disabled="
+                          !gridCellList || selectedfusionData.length == 0
+                        "
+                        @click="handleFusionClick"
+                      >
+                        {{ '鍙犲姞铻嶅悎' }}
+                      </el-button>
+                    </el-form-item>
+                  </el-form>
+                </el-row>
+                <div class="m-t-8">缃戞牸瑕佺礌</div>
+                <el-row class="m-t-8">
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    @click="handleGridClick"
+                  >
+                    {{ gridVisible ? '闅愯棌铻嶅悎' : '鏄剧ず铻嶅悎' }}
+                  </el-button>
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    @click="handleRankClick"
+                  >
+                    {{ rankVisible ? '闅愯棌鎺掑悕' : '鏄剧ず鎺掑悕' }}
+                  </el-button>
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    @click="handleDataClick"
+                  >
+                    {{ dataVisible ? '闅愯棌鏁版嵁' : '鏄剧ず鏁版嵁' }}
+                  </el-button>
+                </el-row>
+                <div class="m-t-8">缃戞牸鏍峰紡</div>
+                <el-row class="m-t-8">
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    @click="handleOpacityClick"
+                  >
+                    {{ !isOpacity ? '閫忔槑鍖�' : '鍙栨秷閫忔槑鍖�' }}
+                  </el-button>
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    @click="handleColorClick"
+                  >
+                    {{ isStandardColor ? '缁樺埗瀵规瘮鑹�' : '缁樺埗鏍囧噯鑹�' }}
+                  </el-button>
+                </el-row>
+                <div class="m-t-8">璧拌埅瑕佺礌</div>
+                <el-row class="m-t-8">
+                  <!-- <el-divider content-position="left"></el-divider> -->
+
+                  <el-button
+                    type="primary"
+                    class="el-button-custom"
+                    size="small"
+                    :loading="missionLoading || loading"
+                    @click="handleUnderwayClick"
+                  >
+                    {{ underwayVisible ? '闅愯棌璧拌埅璺嚎' : '鏄剧ず璧拌埅璺嚎' }}
+                  </el-button>
+                </el-row>
+              </template>
+              <template #footer> </template>
+            </BaseCard>
+          </el-row>
+        </el-col>
+        <el-col span="2">
+          <el-row>
+            <CardButton
+              name="璧拌埅铻嶅悎"
+              direction="right"
+              @click="() => (show = !show)"
+            ></CardButton>
+          </el-row>
+        </el-col>
+      </el-row>
+    </el-row>
+  </div>
 </template>
 
-<script setup></script>
+<script setup>
+import { ref, onMounted, onUnmounted, watch } from 'vue';
+import moment from 'moment';
+import gridApi from '@/api/gridApi';
+import { SatelliteGrid } from '@/model/SatelliteGrid';
+
+const satelliteGrid = new SatelliteGrid();
+
+// 鍊熺敤鍗槦閬ユ祴妯″潡涓殑100绫崇綉鏍�
+const props = defineProps({
+  groupId: {
+    type: Number,
+    default: 3
+  }
+});
+const show = ref(true);
+
+const mission = ref(undefined);
+
+const gridCellList = ref(undefined);
+const fusionData = ref(undefined);
+// 璧拌埅铻嶅悎鏁版嵁
+const fusionLoading = ref(false);
+const fusionDataList = ref([]);
+const selectedfusionData = ref([]);
+
+const gridDataDetailMap = new Map();
+
+const gridVisible = ref(true);
+const underwayVisible = ref(false);
+const rankVisible = ref(false);
+const dataVisible = ref(false);
+const isStandardColor = ref(true);
+const isOpacity = ref(false);
+
+function timeFormatter(value) {
+  return moment(value).format('YYYY-MM-DD');
+}
+
+function fetchFusionData() {
+  fusionLoading.value = true;
+  gridApi
+    .fetchGridData(props.groupId, undefined, 3)
+    .then((res) => {
+      fusionDataList.value = res.data;
+    })
+    .finally(() => (fusionLoading.value = false));
+}
+
+// 妫�鏌ヨ蛋鑸暟鎹槸鍚﹀拰100绫崇綉鏍煎凡铻嶅悎
+// function checkUnderwayFusionResult() {
+//   const time = moment(mission.value.startTime).format('YYYY-MM-DD HH:mm:ss');
+//   gridApi.fetchGridData(props.groupId, time, 3).then((res) => {
+//     if (res.data.length > 0) {
+//       fusionData.value = res.data[0];
+//     } else {
+//       fusionData.value = undefined;
+//     }
+//   });
+// }
+
+function resetButton() {
+  gridVisible.value = true;
+  underwayVisible.value = false;
+  rankVisible.value = false;
+  dataVisible.value = false;
+  isStandardColor.value = true;
+  isOpacity.value = false;
+}
+
+function prepareGrid(gridInfo) {
+  satelliteGrid.gridPrepare(gridInfo);
+}
+
+// 缁樺埗缃戞牸閬ユ劅鏁版嵁鍊煎拰缃戞牸棰滆壊
+function drawGrid(gridDataDetail) {
+  satelliteGrid.drawGrid({ gridDataDetail, opacity: 1, zIndex: 11 });
+}
+
+// watch(mission, (nV, oV) => {
+//   if (nV != oV) {
+//     checkUnderwayFusionResult();
+//     search(nV);
+//   }
+// });
+
+watch(
+  () => props.groupId,
+  (nV, oV) => {
+    if (nV != oV) {
+      gridApi.fetchGridCell(nV).then((res) => {
+        gridCellList.value = res.data;
+        prepareGrid(gridCellList.value);
+      });
+      fetchFusionData();
+    }
+  },
+  {
+    immediate: true
+  }
+);
+
+function handleFusionClick() {
+  // resetButton();
+  satelliteGrid.changeVisibility({ showGridViews: false });
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+    if (gridDataDetailMap.has(d.id)) {
+      // const gdd = gridDataDetailMap.get(d.id);
+      // satelliteGrid.drawTagGrid({
+      //   tag: d.id,
+      //   gridDataDetail: gdd,
+      //   opacity: 1,
+      //   zIndex: 11
+      // });
+      satelliteGrid.changeVisibility({ tag: d.id, showGridViews: true });
+    } else {
+      gridApi.fetchGridDataDetail(d.id, d.groupId).then((res) => {
+        gridDataDetailMap.set(d.id, res.data);
+        const gdd = res.data;
+        satelliteGrid.drawTagGrid({
+          tag: d.id,
+          gridDataDetail: gdd,
+          opacity: 1,
+          zIndex: 11
+        });
+      });
+    }
+  });
+}
+
+function handleGridClick() {
+  gridVisible.value = !gridVisible.value;
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+    satelliteGrid.changeVisibility({
+      tag: d.id,
+      showGridViews: gridVisible.value
+    });
+  });
+}
+
+function handleUnderwayClick() {
+  underwayVisible.value = !underwayVisible.value;
+  underwayVisible.value ? draw() : mapLine.hideLine();
+}
+
+function handleRankClick() {
+  rankVisible.value = !rankVisible.value;
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+    satelliteGrid.changeVisibility({
+      tag: d.id,
+      showRankTxt: rankVisible.value
+    });
+  });
+}
+
+function handleDataClick() {
+  dataVisible.value = !dataVisible.value;
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+    satelliteGrid.changeVisibility({
+      tag: d.id,
+      showDataTxt: dataVisible.value
+    });
+  });
+}
+
+function handleColorClick() {
+  isStandardColor.value = !isStandardColor.value;
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+    if (gridDataDetailMap.has(d.id)) {
+      const gdd = gridDataDetailMap.get(d.id);
+      satelliteGrid.drawTagGrid({
+        tag: d.id,
+        gridDataDetail: gdd,
+        useCustomColor: !isStandardColor.value,
+        opacity: 1,
+        zIndex: 11
+      });
+    }
+  });
+}
+
+function handleOpacityClick() {
+  isOpacity.value = !isOpacity.value;
+  satelliteGrid.changeGridOpacity({ opacityValue: isOpacity.value ? 0.1 : 1 });
+}
+
+/**璧拌埅杞ㄨ抗*******************************************************************/
+import { FactorDatas } from '@/model/FactorDatas';
+import sector from '@/utils/map/sector';
+import mapLine from '@/utils/map/line';
+import mapUtil from '@/utils/map/util';
+import { fetchHistoryData } from '@/utils/factor/data';
+import { useFetchData } from '@/composables/fetchData';
+import { useMissionStore } from '@/stores/mission';
+
+onMounted(() => {
+  isUnmounted.value = false;
+  fetchMission();
+});
+onUnmounted(() => {
+  mapUtil.clearMap();
+  isUnmounted.value = true;
+});
+const { loading, fetchData } = useFetchData(10000);
+
+const missionStore = useMissionStore();
+const missionLoading = ref(false);
+
+const isUnmounted = ref(false);
+
+const drawMode = ref(0);
+// 鐩戞祴鏁版嵁
+const factorDataMap = new Map();
+// pm2.5
+const factorType = 6;
+
+function fetchMission() {
+  missionLoading.value = true;
+  missionStore.fetchMission().finally(() => (missionLoading.value = false));
+}
+
+function search(option) {
+  const { deviceType, deviceCode, startTime, endTime } = option;
+  const _startTime = moment(startTime).format('YYYY-MM-DD HH:mm:ss');
+  const _endTime = moment(endTime).format('YYYY-MM-DD HH:mm:ss');
+  return fetchData((page, pageSize) => {
+    return fetchHistoryData({
+      deviceType,
+      deviceCode,
+      startTime: _startTime,
+      endTime: _endTime,
+      page,
+      perPage: pageSize
+    });
+  });
+}
+
+function draw() {
+  if (isUnmounted.value) return;
+
+  selectedfusionData.value.forEach((i) => {
+    const d = fusionDataList.value[i];
+
+    const mission = missionStore.missionList.find((v) => {
+      return v.missionCode == d.mixDataId;
+    });
+
+    if (factorDataMap.has(mission.missionCode)) {
+      const fd = factorDataMap.get(mission.missionCode);
+      drawLine(mission.missionCode, fd);
+    } else {
+      search(mission).then((res) => {
+        const fd = new FactorDatas();
+        fd.setData(res.data, drawMode.value, () => {
+          fd.refreshHeight(factorType.value);
+          factorDataMap.set(mission.missionCode, fd);
+          drawLine(mission.missionCode, fd);
+        });
+      });
+    }
+  });
+}
+
+function drawLine(missionCode, fd) {
+  // 鍒锋柊鍥句緥
+  const factor = fd.factor[factorType];
+  sector.clearSector();
+  fd.refreshHeight(factorType);
+  mapLine.drawTagLine(missionCode, fd, factor);
+}
+</script>
 <style scoped></style>

--
Gitblit v1.9.3