From f16acb06ab278cdcb1d39a29680634010638326d Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期三, 08 五月 2024 17:35:48 +0800
Subject: [PATCH] 1. 完成表格模块的功能迁移; 2. 完成表格、折线图、地图标记三者点击联动

---
 src/components/SliderBar.vue                      |   13 
 src/utils/map/animation.js                        |  389 +++++++++++++++++
 src/views/historymode/HistoryMode.vue             |   43 +
 src/utils/map/dialog.js                           |   16 
 src/assets/border.css                             |    1 
 src/components/monitor/DataSummary.vue            |   91 ++++
 src/components/monitor/FactorCheckbox.vue         |   66 ++
 src/components.d.ts                               |    9 
 src/components/CardButton.vue                     |   13 
 src/views/historymode/component/DataSheet.vue     |   77 +++
 src/utils/map/marks.js                            |   11 
 src/components/map/MapToolbox.vue                 |    2 
 src/components/core/CoreMenu.vue                  |    2 
 src/components/monitor/DataTable.vue              |  161 ++++++-
 src/utils/map/sector.js                           |   38 
 src/utils/map/3dLayer.js                          |  165 +++---
 src/model/FrameAnimation.js                       |  143 ++++++
 src/views/historymode/component/TrendAnalysis.vue |   14 
 src/components/monitor/LineChart.vue              |   46 +
 19 files changed, 1,128 insertions(+), 172 deletions(-)

diff --git a/src/assets/border.css b/src/assets/border.css
index 30416bf..a45a338 100644
--- a/src/assets/border.css
+++ b/src/assets/border.css
@@ -565,6 +565,7 @@
 /* 鎸夐挳 */
 .ff-toggle-btn {
     position: relative;
+
 }
 
 .ff-toggle-btn .ff-border-top {
diff --git a/src/components.d.ts b/src/components.d.ts
index 2930d6a..a01e48b 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -12,6 +12,8 @@
     CardButton: typeof import('./components/CardButton.vue')['default']
     CoreHeader: typeof import('./components/core/CoreHeader.vue')['default']
     CoreMenu: typeof import('./components/core/CoreMenu.vue')['default']
+    DataStatistic: typeof import('./components/monitor/DataStatistic.vue')['default']
+    DataSummary: typeof import('./components/monitor/DataSummary.vue')['default']
     DataTable: typeof import('./components/monitor/DataTable.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@@ -26,11 +28,15 @@
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElRadio: typeof import('element-plus/es')['ElRadio']
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElText: typeof import('element-plus/es')['ElText']
     FactorCheckbox: typeof import('./components/monitor/FactorCheckbox.vue')['default']
     FactorLegend: typeof import('./components/monitor/FactorLegend.vue')['default']
     FactorRadio: typeof import('./components/monitor/FactorRadio.vue')['default']
@@ -45,4 +51,7 @@
     SearchBar: typeof import('./components/search/SearchBar.vue')['default']
     SliderBar: typeof import('./components/SliderBar.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }
diff --git a/src/components/CardButton.vue b/src/components/CardButton.vue
index e0cb7b3..6536cb9 100644
--- a/src/components/CardButton.vue
+++ b/src/components/CardButton.vue
@@ -1,9 +1,14 @@
 <template>
-  <BaseCard type="btn" size="medium" direction="right" @click="handleClick">
+  <BaseCard
+    type="btn"
+    size="medium"
+    :direction="direction"
+    @click="handleClick"
+  >
     <template #content>
       <div class="card-btn">
         <img :src="src" class="ff-img m-b-8" />
-        <span v-for="(item, index) in name" :key="index"> {{ item }}</span>
+        <span v-for="item in name" :key="item"> {{ item }}</span>
       </div>
     </template>
   </BaseCard>
@@ -18,13 +23,13 @@
     name: String,
     direction: {
       type: String,
-      default: 'left'
+      default: 'right'
     }
   },
   emits: ['click'],
   data() {
     return {
-      src: this.direction ? shrinkLeft : shrinkRight
+      src: this.direction == 'right' ? shrinkRight : shrinkLeft
     };
   },
   computed: {},
diff --git a/src/components/SliderBar.vue b/src/components/SliderBar.vue
index 7a1f519..6acf1bd 100644
--- a/src/components/SliderBar.vue
+++ b/src/components/SliderBar.vue
@@ -13,17 +13,22 @@
       </el-select>
     </el-form-item>
     <div class="slider-wrap m-l-16">
-      <el-slider v-model="progress" :marks="marks" @input="handleInput" />
+      <el-slider :model-value="progress" :marks="marks" @input="handleInput" />
     </div>
   </el-row>
 </template>
 <script>
 export default {
-  emits: ['input', 'sizeChange'],
+  props: {
+    progress: {
+      type: Number,
+      default: 0
+    }
+  },
+  emits: ['update:progress', 'sizeChange'],
   data() {
     return {
       pageSize: 200,
-      progress: 0,
       marks: {
         0: {
           style: {
@@ -43,7 +48,7 @@
   methods: {
     handleInput(e) {
       // console.log(e);
-      this.$emit('input', e);
+      this.$emit('update:progress', e);
     },
     handleSizeChange(e) {
       this.$emit('sizeChange', e);
diff --git a/src/components/core/CoreMenu.vue b/src/components/core/CoreMenu.vue
index 920f38f..a88e8cc 100644
--- a/src/components/core/CoreMenu.vue
+++ b/src/components/core/CoreMenu.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="map-mode-change p-events-auto">
-    <template v-for="(item, index) in menu" :key="index">
+    <template v-for="(item, index) in menu" :key="item.path">
       <a :class="btnClz(item.selected)" @click="navTo(index)">
         <div>{{ item.name }}</div>
       </a>
diff --git a/src/components/map/MapToolbox.vue b/src/components/map/MapToolbox.vue
index cabd44c..270e612 100644
--- a/src/components/map/MapToolbox.vue
+++ b/src/components/map/MapToolbox.vue
@@ -14,7 +14,7 @@
       <el-dropdown-menu>
         <el-dropdown-item
           v-for="(item, index) in toolItem"
-          :key="index"
+          :key="item.label"
           :command="index"
         >
           <el-button
diff --git a/src/components/monitor/DataSummary.vue b/src/components/monitor/DataSummary.vue
new file mode 100644
index 0000000..4d4b9e2
--- /dev/null
+++ b/src/components/monitor/DataSummary.vue
@@ -0,0 +1,91 @@
+<template>
+  <el-row justify="end">
+    <el-col span="4">
+      <CardButton
+        name="鏁版嵁缁熻"
+        direction="left"
+        @click="() => (show = !show)"
+      ></CardButton>
+    </el-col>
+    <el-col span="20">
+      <BaseCard v-show="show" direction="rigtht" borderless="r">
+        <template #content>
+          <!-- <el-row>
+            <el-text style="color: white">鏁版嵁缁熻</el-text>
+          </el-row> -->
+          <el-table
+            :data="showSummary"
+            v-loading="loading"
+            table-layout="auto"
+            size="small"
+            :show-overflow-tooltip="true"
+            border
+            row-class-name="t-row"
+            cell-class-name="t-cell"
+            header-row-class-name="t-header-row"
+            header-cell-class-name="t-header-cell"
+          >
+            <el-table-column prop="factor" label="鍥犲瓙" width="66" />
+            <el-table-column prop="avg" label="鍧囧��" width="66" />
+            <el-table-column prop="min" label="鏈�灏忓��" width="66" />
+            <el-table-column prop="max" label="鏈�澶у��" width="66" />
+          </el-table>
+        </template>
+      </BaseCard>
+    </el-col>
+  </el-row>
+</template>
+<script>
+import { FactorDatas } from '@/model/FactorDatas';
+import { factorName } from '@/constant/factor-name';
+
+export default {
+  props: {
+    loading: Boolean,
+    factorDatas: FactorDatas,
+    selectFactorType: Array
+  },
+  data() {
+    return {
+      show: true
+    };
+  },
+  computed: {
+    summary() {
+      const list = [];
+      for (const key in this.factorDatas.factor) {
+        if (Object.hasOwnProperty.call(this.factorDatas.factor, key)) {
+          const f = this.factorDatas.factor[key];
+          let min,
+            max,
+            total = 0,
+            count = 0;
+          f.datas.forEach((v) => {
+            if (!min || v.factorData < min) {
+              min = v.factorData;
+            }
+            if (!max || v.factorData > max) {
+              max = v.factorData;
+            }
+            total += v.factorData;
+            count++;
+          });
+          list.push({
+            factorId: f.factorId,
+            factor: factorName[f.factorName],
+            min,
+            max,
+            avg: count == 0 ? 0 : Math.round((total / count) * 100) / 100
+          });
+        }
+      }
+      return list;
+    },
+    showSummary() {
+      return this.summary.filter((v) => {
+        return this.selectFactorType.includes(v.factorId);
+      });
+    }
+  }
+};
+</script>
diff --git a/src/components/monitor/DataTable.vue b/src/components/monitor/DataTable.vue
index eff8fda..71de11d 100644
--- a/src/components/monitor/DataTable.vue
+++ b/src/components/monitor/DataTable.vue
@@ -1,61 +1,109 @@
 <template>
-  <BaseCard>
+  <BaseCard size="medium" direction="right">
     <template #content>
       <el-table
-        :data="tableData"
+        ref="tableRef"
+        :data="showData"
         v-loading="loading"
-        table-layout="fixed"
-        :row-class-name="tableRowClassName"
-        :height="tableHeight"
+        table-layout="auto"
+        height="calc(94vh - var(--bevel-length-2))"
+        size="small"
+        :show-overflow-tooltip="true"
         border
+        row-class-name="t-row"
+        cell-class-name="t-cell"
+        header-row-class-name="t-header-row"
+        header-cell-class-name="t-header-cell"
+        :show-summary="false"
+        :highlight-current-row="true"
+        @row-click="handleRowClick"
       >
-        <el-table-column prop="TIME" label="鏃堕棿" />
         <el-table-column
-          v-for="(item, index) in tableColumn"
-          :key="index"
-          :prop="item.name"
-          :label="item.label"
-        />
+          :fixed="true"
+          prop="TIME"
+          label="鏃堕棿"
+          :formatter="timeFormatter"
+          align="center"
+          width="66"
+        >
+        </el-table-column>
+        <template v-for="item in tableColumn" :key="item.name">
+          <el-table-column
+            v-if="selectFactorType.includes(item.value)"
+            :prop="item.name"
+            :label="item.label"
+            align="center"
+            width="64"
+          />
+        </template>
       </el-table>
       <el-pagination
-        v-if="pagination"
         ref="paginationRef"
         class="el-pagination"
+        small
         v-model:current-page="currentPage"
         v-model:page-size="pageSize"
-        :page-sizes="[10, 20, 50, 100]"
-        :background="true"
-        layout="total, sizes, prev, pager, next, jumper"
-        :total="total"
+        :page-sizes="[200, 500]"
+        :hide-on-single-page="false"
+        layout="prev, pager, next"
+        :total="tableData.length"
       />
     </template>
 
-    <template #footer> </template>
+    <template #footer>
+      <el-row justify="end">
+        <el-text size="small" type="warning"
+          >鍏� {{ tableData.length }} 鏉★紝{{ pageSize }}鏉�/椤�</el-text
+        >
+      </el-row>
+    </template>
   </BaseCard>
 </template>
 
 <script>
+import moment from 'moment';
 import { FactorDatas } from '@/model/FactorDatas';
 import { checkboxOptions } from '@/constant/checkbox-options';
 import { TYPE0 } from '@/constant/device-type';
 
 export default {
   props: {
+    loading: Boolean,
     factorDatas: FactorDatas,
     deviceType: {
       type: String,
       // type0: 杞﹁浇鎴栨棤浜烘満; type1:鏃犱汉鑸�
       default: TYPE0
-    }
+    },
+    selectFactorType: {
+      type: Array,
+      default: () => ['1', '2', '3']
+    },
+    // 褰撳墠閫変腑楂樹寒鐨勬暟鎹偣绱㈠紩
+    locateIndex: Number
   },
   data() {
     return {
       tableHeight: '500',
       total: 0,
       currentPage: 1,
-      pageSize: 20,
-      loading: false
+      pageSize: 200,
+      rowHeight: undefined
     };
+  },
+  emits: ['tableClick'],
+  watch: {
+    locateIndex(nV, oV) {
+      if (nV == oV) return;
+      this.$refs.tableRef.setCurrentRow(this.tableData[nV]);
+      // 璁$畻鍒嗛〉
+      this.currentPage = parseInt(nV / this.pageSize) + 1;
+      // 璁$畻瀵瑰簲鍒嗛〉涓殑绱㈠紩
+      const index = nV % this.pageSize;
+
+      const h = this.getRowHeight();
+      this.$refs.tableRef.setScrollTop(h * index - 350);
+    }
   },
   computed: {
     tableData() {
@@ -65,18 +113,87 @@
           const f = this.factorDatas.factor[key];
           f.datas.forEach((v, i) => {
             if (list.length <= i) {
-              list.push({ [f.factorName]: v });
+              list.push({
+                index: i,
+                [f.factorName]: v.factorData
+              });
             } else {
-              list[i][f.factorName] = v;
+              list[i][f.factorName] = v.factorData;
             }
           });
         }
       }
       return list;
     },
+    showData() {
+      const sIndex = (this.currentPage - 1) * this.pageSize;
+      const eIndex = sIndex + this.pageSize;
+      return this.tableData.slice(sIndex, eIndex);
+    },
     tableColumn() {
       return checkboxOptions(this.deviceType);
+    }
+  },
+  methods: {
+    // 鑾峰彇琛ㄦ牸绗竴琛岄珮搴�
+    getRowHeight() {
+      if (!this.rowHeight) {
+        const rowList = document.getElementsByClassName('t-row');
+        if (rowList.length != 0) {
+          const row = rowList[0];
+          this.rowHeight = row.getBoundingClientRect().height;
+        } else {
+          this.rowHeight = 0;
+        }
+      }
+      return this.rowHeight;
+    },
+    timeFormatter(row, col, cellValue, index) {
+      return moment(cellValue).format('HH:mm:ss');
+    },
+    handleRowClick(row, col, event) {
+      this.$emit('tableClick', row.index);
+      // console.log(row);
+      // console.log(col);
+      // console.log(event.target.getBoundingClientRect().height);
     }
   }
 };
 </script>
+<style>
+.el-table {
+  --el-table-bg-color: transparent;
+  --el-table-row-hover-bg-color: #23dad0a2;
+  --el-table-current-row-bg-color: #7dff5d96;
+  --el-table-text-color: var(--font-color);
+}
+
+.t-row {
+  cursor: pointer;
+  background-color: transparent !important;
+}
+
+.t-cell {
+  /* background: red !important; */
+  /* height: 40px;
+  border: 1px solid black; */
+}
+
+.t-header-row {
+}
+
+.t-header-cell {
+  background-color: var(--bg-color-2) !important;
+  text-align: center !important;
+  color: white !important;
+}
+.el-pagination {
+  --el-pagination-bg-color: transparent;
+  --el-pagination-button-bg-color: transparent;
+  --el-pagination-button-color: transparent;
+  --el-pagination-button-disabled-color: white;
+  --el-pagination-button-disabled-bg-color: transparent;
+  --el-pagination-text-color: white;
+  --el-pagination-button-color: white;
+}
+</style>
diff --git a/src/components/monitor/FactorCheckbox.vue b/src/components/monitor/FactorCheckbox.vue
index 43c1b93..7e236e3 100644
--- a/src/components/monitor/FactorCheckbox.vue
+++ b/src/components/monitor/FactorCheckbox.vue
@@ -1,17 +1,20 @@
 <template>
-  <BaseCard direction="top-left" borderless="t">
+  <BaseCard :direction="direction" :borderless="borderlessDirection">
     <template #content>
       <el-checkbox-group
-        v-model="checkbox"
+        :model-value="modelValue"
         size="default"
-        @change="handleChange"
+        :min="1"
+        @update:model-value="handleChange"
       >
-        <el-checkbox
-          v-for="(item, i) in options"
-          :key="i"
-          :value="item.value"
-          >{{ item.label }}</el-checkbox
-        >
+        <div :class="vertical ? 'vertical-class' : ''">
+          <el-checkbox
+            v-for="item in options"
+            :key="item.label"
+            :value="item.value"
+            >{{ item.label }}</el-checkbox
+          >
+        </div>
       </el-checkbox-group>
     </template>
   </BaseCard>
@@ -24,13 +27,41 @@
 
 export default {
   props: {
+    // 澶嶉�夋鍊�
+    modelValue: Array,
     deviceType: {
       type: String,
       // type0: 杞﹁浇鎴栨棤浜烘満; type1:鏃犱汉鑸�
       default: TYPE0
+    },
+    /**
+     *
+     * 鏍峰紡鏈濆悜
+     * top-left | left
+     */
+    direction: {
+      type: String,
+      default: 'top-left'
+    },
+    /**
+     * 鏃犺竟妗嗙殑鏂瑰悜
+     * t | r
+     */
+    borderlessDirection: {
+      type: String,
+      default: 't'
+    },
+    /**
+     * 鍐呭鏄惁鍨傜洿鎺掑竷
+     */
+    vertical: Boolean,
+    // 榛樿閫変腑鐨勪釜鏁帮紙浠庣涓�涓�夐」寮�濮嬶級
+    defaultNum: {
+      type: Number,
+      default: 1
     }
   },
-  emits: ['change'],
+  emits: ['update:modelValue'],
   data() {
     return {
       checkbox: [checkboxOptions(TYPE0)[0].value]
@@ -44,13 +75,19 @@
   watch: {
     deviceType(nV, oV) {
       if (nV != oV) {
-        this.checkbox = this.options[0].value;
+        const res = [];
+        const array = checkboxOptions(nV);
+        for (let i = 0; i < this.defaultNum; i++) {
+          const e = array[i];
+          res.push(e.value);
+        }
+        this.$emit('update:modelValue', res);
       }
     }
   },
   methods: {
     handleChange(value) {
-      this.$emit('change', value);
+      this.$emit('update:modelValue', value);
     }
   }
 };
@@ -61,4 +98,9 @@
   margin-right: 6px;
   /* height: initial; */
 }
+
+.vertical-class {
+  display: flex;
+  flex-direction: column;
+}
 </style>
diff --git a/src/components/monitor/LineChart.vue b/src/components/monitor/LineChart.vue
index 612cb61..2a0b846 100644
--- a/src/components/monitor/LineChart.vue
+++ b/src/components/monitor/LineChart.vue
@@ -6,7 +6,7 @@
     <template #footer>
       <!-- 鍗曢〉鏁版嵁閲�-->
       <SliderBar
-        @input="(e) => (progress = e)"
+        v-model:progress="progress"
         @size-change="(e) => (pageSize = e)"
       ></SliderBar>
     </template>
@@ -21,6 +21,7 @@
 
 export default {
   props: {
+    loading: Boolean,
     factorDatas: {
       type: FactorDatas
       // default: () => new FactorDatas()
@@ -28,7 +29,9 @@
     selectFactorType: {
       type: Array,
       default: () => ['1']
-    }
+    },
+    // 褰撳墠閫変腑楂樹寒鐨勬暟鎹偣绱㈠紩
+    locateIndex: Number
   },
   data() {
     return {
@@ -39,6 +42,7 @@
       progress: 0
     };
   },
+  emits: ['chartClick'],
   watch: {
     factorDatas: {
       handler() {
@@ -56,6 +60,35 @@
       this.changeChartRange();
     },
     pageSize() {
+      this.changeChartRange();
+    },
+    locateIndex(nV, oV) {
+      if (nV == oV) return;
+      // 1. 瀹氫綅鐐瑰簲璇ュ睍绀哄湪瓒嬪娍鍥句腑闂达紝鍥犳瀹氫綅鐧惧垎姣斿線鍓嶅亸绉诲綋鍓峗size鐨勪竴鍗�
+      let i = nV - parseInt(this.pageSize / 2);
+      // 2. 纭繚绱㈠紩涓嶄細瓒呭嚭鑼冨洿
+      i = i < 0 ? 0 : i;
+      // 3. 鑾峰彇绱㈠紩瀵瑰簲鐨勮繘搴︾櫨鍒嗘瘮
+      this.progress = (i / (this.allXAxis.length - this.pageSize)) * 100;
+
+      for (const iterator of this.allSeries) {
+        // if (iterator.name == factorName || (iterator.name == 'TVOC' || factorName == 'VOC')) {
+        iterator.markLine = {
+          symbol: 'none',
+          data: [
+            {
+              name: '閫変腑',
+              xAxis: this.allXAxis[nV],
+              label: {
+                color: 'white'
+              },
+              lineStyle: {
+                color: 'yellow'
+              }
+            }
+          ]
+        };
+      }
       this.changeChartRange();
     }
   },
@@ -103,16 +136,18 @@
       }
       // this.option.dataZoom[0].start = startPer;
       // this.option.dataZoom[0].end = endPer;
-      this.lineChart.setOption(this.option);
+      this.lineChart.setOption(this.option, { notMerge: true });
     },
     getShowXAxis(sIndex, eIndex) {
       return this.allXAxis.slice(sIndex, eIndex);
     },
     getShowSeries(sIndex, eIndex) {
-      const res = this.allSeries.filter((s) => {
+      this.allSeries.forEach((s) => {
         if (sIndex && eIndex) {
           s.data = s.allData.slice(sIndex, eIndex);
         }
+      });
+      const res = this.allSeries.filter((s) => {
         return this.selectFactorType.includes(s.key);
       });
       return res;
@@ -137,6 +172,9 @@
   },
   mounted() {
     this.lineChart = echarts.init(this.$refs.lineChart);
+    this.lineChart.on('click', (e) => {
+      this.$emit('chartClick', e.dataIndex);
+    });
   }
 };
 </script>
diff --git a/src/model/FrameAnimation.js b/src/model/FrameAnimation.js
new file mode 100644
index 0000000..ad4dd8c
--- /dev/null
+++ b/src/model/FrameAnimation.js
@@ -0,0 +1,143 @@
+import moment from 'moment';
+
+/**
+ * 甯у姩鐢绘帶鍒跺伐鍏�
+ *
+ */
+function FrameAnimation() {
+  // 鍔ㄧ敾甯ф暟
+  this.fps = 12;
+  // 甯ч棿闅�(ms)
+  this.timeout = 1000 / this.fps;
+  this.speed = 1;
+  this.animationTask = [];
+  // 鍗曟鎻掑叆鏈�澶氫换鍔℃暟
+  this.maxTasks = 10;
+  this.onNextTask;
+
+  this.isPause = false;
+  this.isRunning = false;
+  // 鏄惁寮�鍚姩鎬佺粯鍒堕�熷害
+  this.dynamicSpeed = false;
+  // 姣忎釜浠诲姟鑰楁椂锛堢锛�
+  this.taskPeriod = 4;
+  // 鏁版嵁鏈�鏂版椂闂�
+  this.lastestTime;
+}
+
+FrameAnimation.prototype = {
+  start: function () {
+    if (this.intervalFlag != undefined) {
+      this.isPause = false;
+    } else {
+      this.isRunning = true;
+      this._doTask();
+    }
+  },
+  /**
+   * 鍔ㄦ�佸姩鐢婚�熷害
+   * 褰撻〉闈㈠け鍘荤劍鐐规椂锛屽惊鐜换鍔s浼氭殏鍋滐紝姝ゆ椂鍚庣画鐨勮建杩瑰姩鐢诲氨浼氳寤跺悗锛�
+   * 鍚屾椂锛岀敱浜庢祻瑙堝櫒鎬ц兘闂锛屽綋杩愯鏃堕棿閫愭笎鍙橀暱鏃讹紝杞ㄨ抗鍔ㄧ敾涓庡疄闄呮椂闂寸殑宸�间篃浼氶�愭笎鎷夊ぇ锛�
+   * 鍥犳闇�瑕佸姩鎬佽皟鏁村姩鐢昏繍琛屽�嶉�燂紝涓庡疄闄呮椂闂翠繚鎸佺浉杩�
+   */
+  dynamicAdjustment: function (time) {
+    if (this.dynamicSpeed && this.lastestTime) {
+      // 浠ヤ袱涓潗鏍囩偣锛堜竴娆ask锛変负鏈�灏忓崟浣嶏紝鏍规嵁褰撳墠缁樺埗鏁版嵁鏃堕棿鍜屾渶鏂版暟鎹椂闂寸殑宸�硷紝璋冩暣缁樺埗閫熷害
+      var t1 = moment(time);
+      var t2 = moment(this.lastestTime);
+      var sec = Math.abs(t1.diff(t2, 'seconds')); // 鑾峰彇鏃堕棿宸�
+
+      // 褰撴椂闂村樊瓒呰繃鍗曚换鍔¤�楁椂鏃讹紝闇�瑕侀�傚綋鍔犲揩鍔ㄧ敾閫熷害
+      if (sec > this.taskPeriod) {
+        this.speed = Math.round(sec / this.taskPeriod);
+        if (this.speed > 16) {
+          // this.speed = 16
+        }
+      } else {
+        this.speed = 1;
+      }
+    }
+  },
+  changeSpeed: function (speed) {
+    if (typeof speed === 'number') {
+      this.speed = speed;
+    }
+  },
+  pause: function () {
+    this.isPause = true;
+  },
+  stop: function () {
+    if (this.intervalFlag != undefined) {
+      clearInterval(this.intervalFlag);
+      this.intervalFlag = undefined;
+      this.animationTask = [];
+    }
+
+    // 褰撳墠浠诲姟鍒楄〃缁撴潫鍚庯紝棣栧厛鍒ゆ柇鏄惁鏈変笅涓�娈典换鍔�
+    if (this.onNextTask) {
+      this.onNextTask();
+      return;
+    }
+
+    if (typeof this.onStopCallback === 'function') {
+      this.onStopCallback();
+    }
+
+    this.isRunning = false;
+    this.isPause = false;
+    this.lastestTime = undefined;
+  },
+  setOnStopCallback: function (callback) {
+    this.onStopCallback = callback;
+  },
+  addTask: function (count, data, callback) {
+    var task = {
+      data: data,
+      count: count,
+      task: callback
+    };
+    this.animationTask.push(task);
+    // console.log('FrameAnimation: addTask ' + count);
+  },
+  // 浠诲姟閲囧彇鍒嗘鎵ц鏂瑰紡锛屽嵆褰撳綋鍓嶄换鍔″垪琛ㄦ墽琛屽畬鎴愬悗锛岀户缁紑濮嬪鐞嗕笅涓�娈典换鍔�
+  addOnNextTasks: function (callback) {
+    this.onNextTask = callback;
+  },
+  runStatus: function () {
+    return this.isRunning;
+  },
+  _doTask: function () {
+    if (this.animationTask.length > 0) {
+      var t = this.animationTask[0];
+
+      // 鍔ㄦ�佽皟鏁撮�熷害
+      this.dynamicAdjustment(t.data.times[t.data.times.length - 1]);
+
+      var index = 0;
+      this.intervalFlag = setInterval(() => {
+        if (this.isPause) {
+          return;
+        }
+        if (index >= t.count) {
+          this._endTask(this.intervalFlag);
+          return;
+        }
+        t.task(t.data, index, t.count);
+
+        index += this.speed;
+        // console.log("_doTask:" + index);
+      }, this.timeout);
+    }
+  },
+  _endTask: function (intervalFlag) {
+    clearInterval(intervalFlag);
+    this.animationTask.splice(0, 1);
+    if (this.animationTask.length > 0) {
+      this._doTask();
+    } else {
+      this.stop();
+    }
+  }
+};
+
+export { FrameAnimation };
diff --git a/src/utils/map/3dLayer.js b/src/utils/map/3dLayer.js
index 251ce09..9ba66bd 100644
--- a/src/utils/map/3dLayer.js
+++ b/src/utils/map/3dLayer.js
@@ -87,89 +87,88 @@
   return scale;
 }
 
-/**
- * 缁樺浘
- */
-function drawMesh(fDatas, factor, center, merge) {
-  const lnglats_GD = fDatas.lnglats_GD;
-  const coors = fDatas.coors_GD;
-  const heights = factor.heights;
-  const colors = factor.colors;
-  const bColor = factor.bottomColor;
-  if (center) {
-    map.setZoomAndCenter(16, center);
-  }
-
-  // eslint-disable-next-line no-undef
-  var cylinder = new AMap.Object3D.Mesh();
-  cylinder.backOrFront = 'both';
-  cylinder.transparent = true;
-
-  var geometry = cylinder.geometry;
-
-  const scale = _getScale(_minH, _maxH);
-  for (let i = 0; i < coors.length; i++) {
-    var r = lnglats_GD[i];
-    var lastP = lnglats_GD[i - 1];
-    var p = coors[i];
-    var h = (heights[i] - _minH) * scale + _minHeight;
-    if (heights[i] == -1) {
-      h = -1;
-    }
-
-    geometry.vertices.push(p.x, p.y, 0); //搴曢儴椤剁偣
-    geometry.vertices.push(p.x, p.y, 0 - h); //椤堕儴椤剁偣
-
-    if (i > 0) {
-      // eslint-disable-next-line no-undef
-      var distance = AMap.GeometryUtil.distance(r, lastP);
-      //涓や釜鏁版嵁鐐规渶灏忛棿闅旀椂闂翠负4s锛屽亣璁捐溅閫熸寜鐓�120km/h璁$畻锛�4s琛岄┒鏈�澶ц窛绂讳綔涓�132绫筹紝
-      //璁惧畾瓒呰繃1鍒嗛挓鐨勬暟鎹粯鍒剁壒娈婄殑杩炵嚎
-      if (distance <= 500 && h != -1) {
-        var bottomIndex = i * 2;
-        var topIndex = bottomIndex + 1;
-        var lastBottomIndex = bottomIndex - 2;
-        var lastTopIndex = bottomIndex - 1;
-        geometry.faces.push(bottomIndex, topIndex, lastTopIndex);
-        geometry.faces.push(bottomIndex, lastBottomIndex, lastTopIndex);
-      }
-    }
-
-    // var bColor = bColor
-    var tColor = colors[i];
-    geometry.vertexColors.push.apply(geometry.vertexColors, bColor); //搴曢儴椤剁偣棰滆壊
-    geometry.vertexColors.push.apply(geometry.vertexColors, tColor); //椤堕儴椤剁偣棰滆壊
-  }
-
-  // 7.鏍规嵁鍚堝苟閫夐」閲嶇疆鎴栨柊澧炲綋鍓嶇紦瀛樻暟鎹�
-  if (merge != true) {
-    _factorDatas = fDatas;
-    _factor = factor;
-    if (_cylinder != undefined) {
-      object3Dlayer.remove(_cylinder);
-    }
-  } else {
-    // _factorDatas.lnglats.push.apply(
-    //   _factorDatas.lnglats,
-    //   lnglats_GD
-    // );
-    // _factorDatas.coors.push.apply(_factorDatas.coors, coors);
-    // _factorDatas.heights.push.apply(_factorDatas.heights, heights);
-    // _factorDatas.colors.push.apply(_factorDatas.colors, colors);
-    // _factorDatas.bottomColor = bColor;
-  }
-  object3Dlayer.add(cylinder);
-  /**************test ****************/
-  // object3Dlayer.on('mouseover', function (e) {
-  //   console.log(
-  //     `榧犳爣绉诲叆瑕嗙洊鐗�! [${e.lnglat.getlng()}, ${e.lnglat.getLat()}]`
-  //   );
-  // });
-  /**************test ****************/
-  _cylinder = cylinder;
-}
-
 export default {
+  /**
+   * 缁樺浘
+   */
+  drawMesh(fDatas, factor, center, merge) {
+    const lnglats_GD = fDatas.lnglats_GD;
+    const coors = fDatas.coors_GD;
+    const heights = factor.heights;
+    const colors = factor.colors;
+    const bColor = factor.bottomColor;
+    if (center) {
+      map.setZoomAndCenter(16, center);
+    }
+
+    // eslint-disable-next-line no-undef
+    var cylinder = new AMap.Object3D.Mesh();
+    cylinder.backOrFront = 'both';
+    cylinder.transparent = true;
+
+    var geometry = cylinder.geometry;
+
+    const scale = _getScale(_minH, _maxH);
+    for (let i = 0; i < coors.length; i++) {
+      var r = lnglats_GD[i];
+      var lastP = lnglats_GD[i - 1];
+      var p = coors[i];
+      var h = (heights[i] - _minH) * scale + _minHeight;
+      if (heights[i] == -1) {
+        h = -1;
+      }
+
+      geometry.vertices.push(p.x, p.y, 0); //搴曢儴椤剁偣
+      geometry.vertices.push(p.x, p.y, 0 - h); //椤堕儴椤剁偣
+
+      if (i > 0) {
+        // eslint-disable-next-line no-undef
+        var distance = AMap.GeometryUtil.distance(r, lastP);
+        //涓や釜鏁版嵁鐐规渶灏忛棿闅旀椂闂翠负4s锛屽亣璁捐溅閫熸寜鐓�120km/h璁$畻锛�4s琛岄┒鏈�澶ц窛绂讳綔涓�132绫筹紝
+        //璁惧畾瓒呰繃1鍒嗛挓鐨勬暟鎹粯鍒剁壒娈婄殑杩炵嚎
+        if (distance <= 500 && h != -1) {
+          var bottomIndex = i * 2;
+          var topIndex = bottomIndex + 1;
+          var lastBottomIndex = bottomIndex - 2;
+          var lastTopIndex = bottomIndex - 1;
+          geometry.faces.push(bottomIndex, topIndex, lastTopIndex);
+          geometry.faces.push(bottomIndex, lastBottomIndex, lastTopIndex);
+        }
+      }
+
+      // var bColor = bColor
+      var tColor = colors[i];
+      geometry.vertexColors.push.apply(geometry.vertexColors, bColor); //搴曢儴椤剁偣棰滆壊
+      geometry.vertexColors.push.apply(geometry.vertexColors, tColor); //椤堕儴椤剁偣棰滆壊
+    }
+
+    // 7.鏍规嵁鍚堝苟閫夐」閲嶇疆鎴栨柊澧炲綋鍓嶇紦瀛樻暟鎹�
+    if (merge != true) {
+      _factorDatas = fDatas;
+      _factor = factor;
+      if (_cylinder != undefined) {
+        object3Dlayer.remove(_cylinder);
+      }
+    } else {
+      // _factorDatas.lnglats.push.apply(
+      //   _factorDatas.lnglats,
+      //   lnglats_GD
+      // );
+      // _factorDatas.coors.push.apply(_factorDatas.coors, coors);
+      // _factorDatas.heights.push.apply(_factorDatas.heights, heights);
+      // _factorDatas.colors.push.apply(_factorDatas.colors, colors);
+      // _factorDatas.bottomColor = bColor;
+    }
+    object3Dlayer.add(cylinder);
+    /**************test ****************/
+    // object3Dlayer.on('mouseover', function (e) {
+    //   console.log(
+    //     `榧犳爣绉诲叆瑕嗙洊鐗�! [${e.lnglat.getlng()}, ${e.lnglat.getLat()}]`
+    //   );
+    // });
+    /**************test ****************/
+    _cylinder = cylinder;
+  },
   /**
    * 缁樺埗3D璧拌璺嚎鍥�
    * @param fDatas 瀹屾暣鐩戞祴鏁版嵁
@@ -213,7 +212,7 @@
     }
 
     // 5.缁樺埗3D鍥惧舰
-    drawMesh(fDatas, factor, center, merge);
+    this.drawMesh(fDatas, factor, center, merge);
 
     // 缂╂斁鍦板浘鍒板悎閫傜殑瑙嗛噹绾у埆
     // map.setFitView()
diff --git a/src/utils/map/animation.js b/src/utils/map/animation.js
new file mode 100644
index 0000000..551b3bb
--- /dev/null
+++ b/src/utils/map/animation.js
@@ -0,0 +1,389 @@
+import { FrameAnimation } from '@/model/FrameAnimation';
+import calculate from '@/utils/map/calculate';
+import Layer from '@/utils/map/3dLayer';
+import sector from '@/utils/map/sector';
+import map from '@/utils/map/index_old';
+
+function MapAnimation() {
+  // 闈炶繛缁潗鏍囩偣鏈�澶ц窛绂�(绫�)
+  this.maxD = 500;
+  // 褰撳墠缁樺埗鐨勭洃娴嬪洜瀛愮被鍨�
+  this.factorType = 0;
+  this.factorDatas;
+  // 杞藉叿绫诲瀷
+  this.vehicleType = 0; // 0: 杞﹁締锛�1锛氭棤浜烘満锛�2锛氭棤浜鸿埞
+  // 甯у姩鐢绘帶鍒跺伐鍏�
+  this.frameAnimation = new FrameAnimation();
+  // 鍔ㄧ敾甯ф暟
+  this._fps = this.frameAnimation.fps;
+  // 缁樺埗鍥惧舰缂撳瓨
+  this.moveViews = {};
+}
+
+MapAnimation.prototype = {
+  setFactorType: function (factorType) {
+    this.factorType = factorType;
+  },
+
+  /**
+   * 璁剧疆杞藉叿绫诲瀷锛屽搴旀樉绀轰笉鍚岀殑鍥炬爣
+   */
+  setVehicleType: function (t) {
+    this.vehicleType = t;
+    this.moveViews = {};
+  },
+
+  /**
+   * 杞﹁締绉诲姩鍔ㄧ敾
+   * @param {*} factorDatas
+   * @param {*} factorType
+   */
+  moveAnimation: function (factorDatas, factorType, startIndex) {
+    // MapUtil.drawLine(factorDatas.lnglats_GD)
+
+    // 璁剧疆褰撳墠鏁版嵁鐨勬渶鏂版椂闂�
+    this.frameAnimation.lastestTime =
+      factorDatas.times[factorDatas.times.length - 1];
+
+    // 褰撴暟鎹紦瀛樹笉涓虹┖鏃讹紝璇存槑涓婁竴娆$殑缁樺埗杩樻湭缁撴潫锛屾墍浠ュ彧鏇存柊鏁版嵁婧愬嵆鍙�
+    if (this.factorDatas != undefined) {
+      this.factorType = factorType;
+      this.factorDatas = factorDatas;
+      return;
+    }
+
+    this.factorType = factorType;
+    this.factorDatas = factorDatas;
+
+    this.startIndex = startIndex == undefined ? 0 : startIndex;
+    // 涓�娆℃�ф渶澶氳绠梉this.frameAnimation.maxTasks]涓潗鏍囩偣鐨勭粯鍒惰建杩癸紝缁樺埗瀹屾垚鍚庡啀娆¤绠楋紝鎻愬崌鍓嶇缃戦〉鍝嶅簲閫熷害
+    this.endIndex = this.startIndex + this.frameAnimation.maxTasks;
+    if (this.endIndex >= factorDatas.length()) {
+      this.endIndex = factorDatas.length() - 1;
+    }
+
+    // console.log(new Date());
+    this.nextAnimation(this.startIndex, factorDatas, this.endIndex);
+    // console.log(new Date());
+  },
+
+  nextAnimation: function (start, factorDatas, endIndex) {
+    if (factorDatas == undefined) {
+      return;
+    }
+    var that = this;
+
+    // 鏁版嵁鏍煎紡杞崲
+    for (let i = start; i < factorDatas.length(); i++) {
+      // 缁堢偣
+      if (i == endIndex || i == factorDatas.length() - 1) {
+        break;
+      }
+      // 鍔ㄧ敾杞ㄨ抗
+      var animationData = factorDatas.getByIndex(0, i + 1);
+
+      // 璺緞鐐�
+      var sPos = factorDatas.lnglats_GD[i];
+      var ePos = factorDatas.lnglats_GD[i + 1];
+      // 缁堢偣鐩稿璧风偣鐨勮搴�
+      var angle = this._getAngle(sPos, ePos);
+      // 璺緞鎬婚暱搴�
+      // eslint-disable-next-line no-undef
+      var distance = AMap.GeometryUtil.distance(sPos, ePos);
+
+      // 鏃堕棿宸�
+      var d1 = new Date(factorDatas.times[i]);
+      var d2 = new Date(factorDatas.times[i + 1]);
+      var duration = (d2 - d1) / 1000;
+      // 鎬诲抚鏁�
+      var count = duration * this._fps;
+      // 鍗曞抚璺濈
+      var d = distance / count;
+      // 姣忎釜fps瀵瑰簲鐨勫疄闄呮椂闀�(榛樿1绉�)
+      var t = 1;
+      // 涓ょ偣闂翠笉杩炵画鏃�
+      if (distance > this.maxD) {
+        count = 4 * this._fps;
+        d = distance / count;
+        t = duration / (count / this._fps);
+      }
+
+      // 鐩戞祴鏁版嵁
+      // var fData1 = factorDatas.getByIndex(i, i + 1)
+      var fData2 = factorDatas.getByIndex(i + 1, i + 2);
+
+      // 璁$畻鍔ㄧ敾杞ㄨ抗
+      for (let i = 0; i < count - 1; i++) {
+        // path
+        var length = d * (i + 1);
+        if (isNaN(angle)) {
+          animationData.lnglats_GD.push(sPos);
+        } else {
+          var point = this._getLatLon(sPos, length, angle);
+          animationData.lnglats_GD.push(point);
+        }
+        // time
+        var time = d1.format('yyyy-MM-dd hh:mm:ss');
+        if ((i + 1) % this._fps == 0) {
+          var t1 = d1.getTime();
+          d1.setTime(t1 + t * 1000);
+          time = d1.format('yyyy-MM-dd hh:mm:ss');
+        }
+        animationData.times.push(time);
+      }
+      // factor
+      for (const key in animationData.factor) {
+        var factor = animationData.factor[key];
+        factor.insertFrame(fData2.factor[key], count, distance <= this.maxD);
+      }
+
+      animationData.refreshHeight();
+      var coor_GD = calculate.lngLatToGeodeticCoord(animationData.lnglats_GD);
+      animationData.coors_GD = coor_GD;
+
+      this.frameAnimation.addTask(
+        count,
+        animationData,
+        function (data, index, count) {
+          var length = data.length();
+          var start = length - count + 1;
+          // 1.鑾峰彇鏁版嵁
+          var d = data.getByIndex(0, start + index + 1);
+          var f = d.factor[that.factorType + 1 + ''];
+          // 2.缁樺埗鍥惧舰
+          if (length > count || index > 0) {
+            // 3d鍥惧舰
+            // var lnglat = d.lnglats_GD[d.lnglats_GD.length - 1];
+            Layer.drawMesh(d, f);
+            // MapUtil.drawLine(lnglat)
+
+            // 椋庡悜椋庨��
+            sector.drawSector(d, start + index);
+            // if (d.factor['17'] != undefined && d.factor['16'] != undefined) {
+            //   var windDir = d.factor['17'].datas;
+            //   windDir = windDir[windDir.length - 1].factorData;
+            //   var windSpeed = d.factor['16'].datas;
+            //   windSpeed = windSpeed[windSpeed.length - 1].factorData;
+            //   MapUtil.drawSector4(lnglat, windDir, windSpeed);
+            // }
+          }
+
+          var pos = d.lnglats_GD[d.lnglats_GD.length - 1];
+          var time = d.times[d.times.length - 1].slice(10);
+          // 缁樺埗杞﹁締鍥炬爣鍜屾椂闂存枃鏈�
+          var status = 0;
+          // 鍋滄
+          if (index + 1 == count) {
+            status = 1;
+          }
+          // 琛岄┒涓�
+          else {
+            status = 2;
+          }
+          that._setPosition(pos, time, status);
+          // console.log("animation");
+        }
+      );
+    }
+
+    // 褰揺ndIndex宸茬粡鏄渶鍚庝竴涓潗鏍囩偣鏃讹紝璺緞缁樺埗缁撴潫锛屽惁鍒欏紑濮嬩笅涓�娈佃矾寰勭粯鍒�
+    if (that.endIndex < that.factorDatas.length() - 1) {
+      that.startIndex = that.endIndex;
+      that.endIndex = that.startIndex + that.frameAnimation.maxTasks;
+      if (that.endIndex >= that.factorDatas.length) {
+        that.endIndex = that.factorDatas.length - 1;
+      }
+      var cb = function () {
+        that.nextAnimation(that.startIndex, that.factorDatas, that.endIndex);
+      };
+      that.frameAnimation.addOnNextTasks(cb);
+    }
+    // 褰撳叏閮ㄦ暟鎹粯鍒跺畬鎴愭椂锛屾竻绌烘暟鎹�
+    else {
+      that.factorDatas = undefined;
+      that.frameAnimation.addOnNextTasks(undefined);
+    }
+
+    this.start();
+  },
+
+  /*******************************鍔ㄧ敾浠诲姟閫昏緫 -start ******************************/
+  start: function () {
+    sector.clearSector();
+    this.frameAnimation.start();
+  },
+  changeSpeed: function (speed) {
+    this.frameAnimation.changeSpeed(speed);
+  },
+  pause: function () {
+    this.frameAnimation.pause();
+  },
+  stop: function () {
+    this.factorDatas = undefined;
+    this.frameAnimation.addOnNextTasks(undefined);
+    this.frameAnimation.stop();
+  },
+  setOnStopCallback: function (callback) {
+    this.frameAnimation.setOnStopCallback(
+      function () {
+        this._clearMap();
+        callback();
+      }.bind(this)
+    );
+  },
+  runStatus: function () {
+    return this.frameAnimation.runStatus();
+  },
+  /**
+   * 璁剧疆鏄惁寮�鍚姩鎬佺粯鍒堕�熷害
+   */
+  setDynamicSpeed: function (b, sec) {
+    if (b) {
+      this.frameAnimation.dynamicSpeed = true;
+      this.frameAnimation.taskPeriod = sec;
+    } else {
+      this.frameAnimation.dynamicSpeed = false;
+      this.frameAnimation.taskPeriod = sec;
+    }
+  },
+  /*******************************鍔ㄧ敾浠诲姟閫昏緫 -end ******************************/
+
+  _clearMap: function () {
+    var list = [];
+    for (const key in this.moveViews) {
+      list.push(this.moveViews[key]);
+    }
+    map.remove(list);
+    this.moveViews = {};
+    sector.clearSector();
+  },
+  _setPosition: function (pos, time, status) {
+    // eslint-disable-next-line no-undef
+    var lnglat = new AMap.LngLat(...pos);
+    if (this.moveViews['text'] == undefined) {
+      var url;
+      switch (this.vehicleType) {
+        case 0:
+          url = './asset/mipmap/car_driving.png';
+          break;
+        case 1:
+          url = './asset/mipmap/car_driving.png';
+          break;
+        case 2:
+          url = './asset/mipmap/boat_driving.png';
+          break;
+        default:
+          url = './asset/mipmap/car_driving.png';
+          break;
+      }
+      // var url = "./asset/mipmap/car_offline.png";
+      // switch (status) {
+      //   case 0:
+      //     url = "./asset/mipmap/car_offline.png";
+      //     break;
+      //   case 1:
+      //     url = "./asset/mipmap/car_stop.png";
+      //     break;
+      //   case 2:
+      //     url = "./asset/mipmap/car_driving.png";
+      //     break;
+      //   default:
+      //     url = "./asset/mipmap/car_stop.png";
+      //     break;
+      // }
+
+      var icon = new AMap.Icon({
+        size: new AMap.Size(60, 30),
+        imageSize: new AMap.Size(54, 21),
+        image: url
+        // imageOffset: new AMap.Pixel(-16, -16) // 鐩稿浜庡熀鐐圭殑鍋忕Щ浣嶇疆
+      });
+
+      var car = new AMap.Marker({
+        icon: icon,
+        position: lnglat, // 鍩虹偣浣嶇疆
+        offset: new AMap.Pixel(-20, -15) // 鐩稿浜庡熀鐐圭殑鍋忕Щ浣嶇疆
+      });
+
+      MapUtil._map.add(car);
+      this.moveViews['car'] = car;
+
+      // 鏃堕棿
+      var text = new AMap.Text({
+        text: time,
+        position: lnglat,
+        offset: new AMap.Pixel(5, 20),
+        style: {
+          'font-size': '13px',
+          'text-align': 'center',
+          color: 'black',
+          'background-color': 'white',
+          // 'text-shadow': 'black 2px 2px 2px',
+          'border-radius': '2px',
+          border: '0px',
+          padding: '0 4px'
+        }
+      });
+      MapUtil._map.add(text);
+      this.moveViews['text'] = text;
+    } else {
+      MapUtil.setCenter(lnglat);
+      this.moveViews['car'].setPosition(lnglat);
+      this.moveViews['text'].setPosition(lnglat);
+      this.moveViews['text'].setText(time);
+    }
+  },
+  _setStatus: function (status) {},
+
+  /**
+   * 鏍规嵁鍧愭爣鐐广�佽窛绂诲拰瑙掑害锛屽緱鍒板彟涓�涓潗鏍囩偣
+   * @param {*} pos
+   * @param {*} distance 绫�
+   * @param {*} angle
+   */
+  _getLatLon: function (pos, distance, angle) {
+    var Ea = 6378137; //   璧ら亾鍗婂緞
+    var Eb = 6356725; //   鏋佸崐寰�
+    var dx = distance * Math.sin((angle * Math.PI) / 180);
+    var dy = distance * Math.cos((angle * Math.PI) / 180);
+    var ec = Eb + ((Ea - Eb) * (90.0 - pos[1])) / 90.0;
+    var ed = ec * Math.cos((pos[1] * Math.PI) / 180);
+    var lng = ((dx / ed + (pos[0] * Math.PI) / 180.0) * 180.0) / Math.PI;
+    var lat = ((dy / ec + (pos[1] * Math.PI) / 180.0) * 180.0) / Math.PI;
+    return [lng, lat];
+  },
+
+  /**
+   * 鏍规嵁涓や釜鍧愭爣鐐硅幏寰楃浉瀵硅搴�
+   * @param {*} sPos
+   * @param {*} ePos
+   */
+  _getAngle: function (sPos, ePos) {
+    var lng_a = sPos[0];
+    var lat_a = sPos[1];
+    var lng_b = ePos[0];
+    var lat_b = ePos[1];
+
+    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;
+  }
+};
+
+export { MapAnimation };
diff --git a/src/utils/map/dialog.js b/src/utils/map/dialog.js
index 54292bd..443f87b 100644
--- a/src/utils/map/dialog.js
+++ b/src/utils/map/dialog.js
@@ -1,6 +1,7 @@
-import { factorName } from '../../constant/factor-name';
-import { factorUnit } from '../../constant/factor-unit';
-import { windDir } from '../../constant/wind-dir';
+import { factorName } from '@/constant/factor-name';
+import { factorUnit } from '@/constant/factor-unit';
+import { windDir } from '@/constant/wind-dir';
+import { map } from './index_old';
 
 export const DialogUtil = {
   show: true,
@@ -152,7 +153,8 @@
     // 瀹氫箟椤堕儴鏍囬
     var top = document.createElement('div');
     // top.className = "info-top";
-    top.className = 'ff-content ff-content-top-left ff-content-small-borderless-t info-top';
+    top.className =
+      'ff-content ff-content-top-left ff-content-small-borderless-t info-top';
     var top_b = document.createElement('div');
     top_b.className = 'ff-border-bottom';
     var top_t = document.createElement('div');
@@ -246,13 +248,13 @@
     return info;
   },
 
-  openNewWindow(factorDatas, i, map, position, onClose) {
+  openNewWindow(factorDatas, i, onClose) {
     if (!this.show) return;
     const window = this.createInfoWindow(factorDatas, i, onClose);
-    window.open(map, position);
+    window.open(map, factorDatas.lnglats_GD[i]);
   },
 
-  openNewWindow2(factorData, map, position, onClose) {
+  openNewWindow2(factorData, position, onClose) {
     if (!this.show) return;
     const window = this.createInfoWindow2(factorData, onClose);
     window.open(map, position);
diff --git a/src/utils/map/marks.js b/src/utils/map/marks.js
index 94144d2..0d3c21d 100644
--- a/src/utils/map/marks.js
+++ b/src/utils/map/marks.js
@@ -52,17 +52,8 @@
     });
     massMarks.on('click', (event) => {
       const i = event.data.id;
-      // 1. 缁樺埗鎵囧舰鍖哄煙
-      sector.drawSector(fDatas, i);
-
-      // 2. 缁樺埗瀵硅瘽妗�
-      DialogUtil.openNewWindow(fDatas, i, map, lnglats[i], () => {
-        // 绉婚櫎鎵囧舰鍖哄煙
-        // clearSector3();
-      });
-
       // 3. 鑷畾涔夌偣鍑讳簨浠�
-      onClick();
+      onClick(i);
     });
     // eslint-disable-next-line no-undef
     var marker = new AMap.Marker({
diff --git a/src/utils/map/sector.js b/src/utils/map/sector.js
index 846eff2..f2aa490 100644
--- a/src/utils/map/sector.js
+++ b/src/utils/map/sector.js
@@ -1,25 +1,25 @@
 import { map, object3Dlayer } from './index_old';
 import calculate from './calculate';
+import imgLocation from '@/assets/mipmap/location.png';
 
 var _defaultDeg = 30,
   _sector = undefined,
   _sectorViews = {};
 
-function clearSector() {
-  var list = [];
-  for (const key in _sectorViews) {
-    list.push(_sectorViews[key]);
-  }
-  if (list.length > 0) {
-    map.remove(list);
-    _sectorViews = {};
-  }
-  if (_sector) {
-    object3Dlayer.remove(_sector);
-  }
-}
-
 export default {
+  clearSector() {
+    var list = [];
+    for (const key in _sectorViews) {
+      list.push(_sectorViews[key]);
+    }
+    if (list.length > 0) {
+      map.remove(list);
+      _sectorViews = {};
+    }
+    if (_sector) {
+      object3Dlayer.remove(_sector);
+    }
+  },
   drawSector(fDatas, i) {
     const lnglat = fDatas.lnglats_GD[i];
     let windDir = fDatas.factor[17].datas[i].factorData;
@@ -31,7 +31,7 @@
       return;
     }
     if (_sector != undefined) {
-      clearSector();
+      this.clearSector();
     }
 
     // eslint-disable-next-line no-undef
@@ -126,7 +126,7 @@
       styles: [
         {
           icon: {
-            img: './asset/mipmap/location.png',
+            img: imgLocation,
             size: [16, 16], //鍙鍖哄煙鐨勫ぇ灏�
             ancher: [8, 16], //閿氱偣
             fitZoom: 18, //鏈�鍚堥�傜殑绾у埆
@@ -152,7 +152,7 @@
       styles: [
         {
           icon: {
-            img: './asset/mipmap/location.png',
+            img: imgLocation,
             size: [16, 16], //鍙鍖哄煙鐨勫ぇ灏�
             ancher: [8, 16], //閿氱偣
             fitZoom: 18, //鏈�鍚堥�傜殑绾у埆
@@ -182,7 +182,7 @@
       styles: [
         {
           icon: {
-            img: './asset/mipmap/location.png',
+            img: imgLocation,
             size: [16, 16], //鍙鍖哄煙鐨勫ぇ灏�
             ancher: [8, 16], //閿氱偣
             fitZoom: 18, //鏈�鍚堥�傜殑绾у埆
@@ -207,7 +207,7 @@
       styles: [
         {
           icon: {
-            img: './asset/mipmap/location.png',
+            img: imgLocation,
             size: [16, 16], //鍙鍖哄煙鐨勫ぇ灏�
             ancher: [8, 16], //閿氱偣
             fitZoom: 18, //鏈�鍚堥�傜殑绾у埆
diff --git a/src/views/historymode/HistoryMode.vue b/src/views/historymode/HistoryMode.vue
index fc56796..817a150 100644
--- a/src/views/historymode/HistoryMode.vue
+++ b/src/views/historymode/HistoryMode.vue
@@ -20,23 +20,36 @@
     </el-row>
     <TrendAnalysis
       class="trend-analysis"
+      :locate-index="locateIndex"
+      @chart-click="handelIndexChange"
       :factor-datas="factorDatas"
+      :device-type="deviceType"
     ></TrendAnalysis>
+    <DataSheet
+      class="data-sheet"
+      :locate-index="locateIndex"
+      @table-click="handelIndexChange"
+      :factor-datas="factorDatas"
+      :device-type="deviceType"
+    ></DataSheet>
   </div>
 </template>
 
 <script>
 import Layer from '@/utils/map/3dLayer';
 import marks from '@/utils/map/marks';
+import sector from '@/utils/map/sector';
+import { DialogUtil } from '@/utils/map/dialog';
 import monitorDataApi from '@/api/monitorDataApi';
 import { useFetchData } from '@/composables/fetchData';
 import moment from 'moment';
 import { TYPE0 } from '@/constant/device-type';
 import { FactorDatas } from '@/model/FactorDatas';
 import TrendAnalysis from './component/TrendAnalysis.vue';
+import DataSheet from './component/DataSheet.vue';
 
 export default {
-  components: { TrendAnalysis },
+  components: { TrendAnalysis, DataSheet },
   setup() {
     const { loading, fetchData } = useFetchData(10000);
     return { loading, fetchData };
@@ -55,7 +68,9 @@
       setCenter: true,
       // 缁樺埗妯″紡锛�0锛氳嚜鍔ㄦā寮忥紝鑷姩璁$畻褰撳墠鏁版嵁鐨勮寖鍥达紝缁樺埗鍚堥�傜殑姣斾緥锛�1锛氭墜鍔ㄦā寮忥紝鏍规嵁椤甸潰璁剧疆鐨勭粯鍥捐寖鍥磋繘琛岀粯鍒�
       drawMode: 0,
-      searchTime: []
+      searchTime: [],
+      // 褰撳墠閫変腑楂樹寒鐨勬暟鎹偣绱㈠紩
+      locateIndex: undefined
     };
   },
   watch: {
@@ -66,6 +81,11 @@
     }
   },
   methods: {
+    // 鐩戝惉鎶樼嚎鍥惧拰琛ㄦ牸鐨勭偣鍑讳簨浠�
+    handelIndexChange(index) {
+      this.locateIndex = index;
+      this.drawSector(index);
+    },
     draw() {
       // todo 鍒锋柊鍥句緥
       const factor = this.factorDatas.factor[this.factorType];
@@ -85,13 +105,24 @@
       // }
     },
     drawMassMarks(e) {
-      marks.drawMassMarks(this.factorDatas, e, () => {
+      marks.drawMassMarks(this.factorDatas, e, (index) => {
         // 鏌ヨ鑼冨洿鍐呯殑鐩戞祴绔欑偣
         // SceneUtil.searchByCoordinate(lnglat[0], lnglat[1], distance);
         // 3. 瓒嬪娍鍥捐烦杞畾浣�
         // const progress = FChart.locate(lineChart.chart, lineChart.option, i, _factor.factorName);
         // 4. 琛ㄦ牸鏁版嵁璺宠浆瀹氫綅
         // Table.locate(i);
+        this.drawSector(index);
+        this.locateIndex = index;
+      });
+    },
+    drawSector(index) {
+      // 1. 缁樺埗鎵囧舰鍖哄煙
+      sector.drawSector(this.factorDatas, index);
+      // 2. 缁樺埗瀵硅瘽妗�
+      DialogUtil.openNewWindow(this.factorDatas, index, () => {
+        // 绉婚櫎鎵囧舰鍖哄煙
+        // clearSector3();
       });
     },
     onFetchData(type, data) {
@@ -155,4 +186,10 @@
   left: 0;
   bottom: 2px;
 }
+
+.data-sheet {
+  position: absolute;
+  right: 0;
+  top: 0;
+}
 </style>
diff --git a/src/views/historymode/component/DataSheet.vue b/src/views/historymode/component/DataSheet.vue
index e69de29..9d2cfbe 100644
--- a/src/views/historymode/component/DataSheet.vue
+++ b/src/views/historymode/component/DataSheet.vue
@@ -0,0 +1,77 @@
+<template>
+  <el-row class="wrap">
+    <el-col span="2" class="flex-col">
+      <el-row justify="end">
+        <CardButton
+          name="璧拌埅鏁版嵁璇︽儏"
+          direction="left"
+          @click="() => (show = !show)"
+        ></CardButton>
+      </el-row>
+      <el-row class="flex-col">
+        <DataSummary
+          v-show="show"
+          :loading="loading"
+          :factor-datas="factorDatas"
+          :select-factor-type="selectFactorType"
+        ></DataSummary>
+      </el-row>
+    </el-col>
+    <el-col v-show="show" span="10">
+      <el-row align="bottom">
+        <DataTable
+          :loading="loading"
+          :select-factor-type="selectFactorType"
+          :factor-datas="factorDatas"
+          :device-type="deviceType"
+          :locate-index="locateIndex"
+          @table-click="handleTableClick"
+        ></DataTable>
+        <FactorCheckbox
+          direction="left"
+          vertical
+          borderless-direction="r"
+          v-model="selectFactorType"
+          :device-type="deviceType"
+        ></FactorCheckbox>
+      </el-row>
+    </el-col>
+  </el-row>
+</template>
+<script>
+import { TYPE0 } from '@/constant/device-type';
+import { FactorDatas } from '@/model/FactorDatas';
+
+export default {
+  props: {
+    loading: Boolean,
+    factorDatas: FactorDatas,
+    deviceType: {
+      type: String,
+      // type0: 杞﹁浇鎴栨棤浜烘満; type1:鏃犱汉鑸�
+      default: TYPE0
+    },
+    // 褰撳墠閫変腑楂樹寒鐨勬暟鎹偣绱㈠紩
+    locateIndex: Number
+  },
+  data() {
+    return {
+      selectFactorType: ['1', '2', '3'],
+      show: false
+    };
+  },
+  emits: ['tableClick'],
+  methods: {
+    handleTableClick(index) {
+      this.$emit('tableClick', index);
+    }
+  }
+};
+</script>
+<style scoped>
+.flex-col {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+</style>
diff --git a/src/views/historymode/component/TrendAnalysis.vue b/src/views/historymode/component/TrendAnalysis.vue
index 7960195..06be06b 100644
--- a/src/views/historymode/component/TrendAnalysis.vue
+++ b/src/views/historymode/component/TrendAnalysis.vue
@@ -3,10 +3,12 @@
     <Transition name="">
       <el-col v-show="show" span="10">
         <FactorCheckbox
+          v-model="selectFactorType"
           :device-type="deviceType"
-          @change="(e) => (selectFactorType = e)"
         ></FactorCheckbox>
         <LineChart
+          :locate-index="locateIndex"
+          @chart-click="handleChartClick"
           :factor-datas="factorDatas"
           :select-factor-type="selectFactorType"
         ></LineChart>
@@ -32,13 +34,21 @@
     deviceType: {
       type: String
     },
-    factorDatas: FactorDatas
+    factorDatas: FactorDatas,
+    // 褰撳墠閫変腑楂樹寒鐨勬暟鎹偣绱㈠紩
+    locateIndex: Number
   },
   data() {
     return {
       selectFactorType: ['1'],
       show: true
     };
+  },
+  emits: ['chartClick'],
+  methods: {
+    handleChartClick(index) {
+      this.$emit('chartClick', index);
+    }
   }
 };
 </script>

--
Gitblit v1.9.3