From d6e6f8b5b31e132e4597eb531168d3e88f3bda72 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 04 七月 2025 17:26:49 +0800
Subject: [PATCH] 2025.7.4 动态溯源

---
 src/styles/animation.scss                                 |    0 
 src/views/sourcetrace/SourceTrace.vue                     |  211 ++++-----
 src/views/historymode/HistoryMode.vue                     |   15 
 src/components/monitor/WeatherData.vue                    |   54 +-
 src/components/monitor/FactorTrend.vue                    |   21 
 src/components.d.ts                                       |  171 ++++----
 src/views/sourcetrace/component/ClueRecordItem.vue        |   19 
 src/assets/logo.svg                                       |    5 
 src/views/realtimemode/RealtimeMode.vue                   |    7 
 src/views/realtimemode/component/DashBoard.vue            |   74 ++-
 src/components/monitor/FactorLegend.vue                   |  116 +++--
 /dev/null                                                 |   87 ----
 src/api/dataAnalysisApi.js                                |    6 
 src/components/monitor/FactorRadio.vue                    |   61 ++
 src/assets/wdr.svg                                        |    6 
 src/components/monitor/VehicleData.vue                    |   18 
 src/views/sourcetrace/component/PollutedExceptionItem.vue |   66 --
 src/components/monitor/FactorIconText.vue                 |   72 +++
 src/components/monitor/WeatherData_Old.vue                |  106 +++++
 src/styles/index.scss                                     |    4 
 src/views/realtimemode/component/DashBoard_Old.vue        |   78 +++
 21 files changed, 737 insertions(+), 460 deletions(-)

diff --git a/src/api/dataAnalysisApi.js b/src/api/dataAnalysisApi.js
index 9b57b3d..fe9e1b0 100644
--- a/src/api/dataAnalysisApi.js
+++ b/src/api/dataAnalysisApi.js
@@ -10,5 +10,11 @@
     return $http
       .get(`air/analysis/pollution/trace`, { params: { missionCode } })
       .then((res) => res.data);
+  },
+
+  fetchPollutionTraceHistory(missionCode) {
+    return $http
+      .get(`air/analysis/pollution/trace/history`, { params: { missionCode } })
+      .then((res) => res.data);
   }
 };
diff --git a/src/assets/logo.svg b/src/assets/logo.svg
index 7565660..43c9cba 100644
--- a/src/assets/logo.svg
+++ b/src/assets/logo.svg
@@ -1 +1,4 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69">
+  <path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883" />
+  <path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e" />
+</svg>
\ No newline at end of file
diff --git a/src/assets/wdr.svg b/src/assets/wdr.svg
new file mode 100644
index 0000000..a3ec37f
--- /dev/null
+++ b/src/assets/wdr.svg
@@ -0,0 +1,6 @@
+<svg t="1751598713381" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4696"
+  width="30" height="30">
+  <path
+    d="M973.824 278.186667l-136.533333-85.333334A34.133333 34.133333 0 0 0 785.066667 221.866667V273.066667H318.122667L88.064 108.885333a34.133333 34.133333 0 0 0-51.541333 40.277334L99.669333 307.2l-63.146666 158.037333A34.133333 34.133333 0 0 0 68.266667 512a34.133333 34.133333 0 0 0 19.797333-6.485333L318.122667 341.333333H477.866667v307.2h-68.266667v341.333334h68.266667v-273.066667h68.266666v273.066667h68.266667V648.533333h-68.266667v-307.2h238.933334v51.2a34.133333 34.133333 0 0 0 17.749333 29.696 34.133333 34.133333 0 0 0 16.384 4.437334 34.133333 34.133333 0 0 0 18.090667-5.12l136.533333-85.333334a34.133333 34.133333 0 0 0 0-58.026666z"
+    p-id="4697" fill="#CCD1D7"></path>
+</svg>
\ No newline at end of file
diff --git a/src/components.d.ts b/src/components.d.ts
index 9231243..0bdadcd 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -7,92 +7,93 @@
 
 declare module 'vue' {
   export interface GlobalComponents {
-    BaseCard: typeof import('./components/BaseCard.vue')['default']
-    BaseMap: typeof import('./components/map/BaseMap.vue')['default']
-    CardButton: typeof import('./components/CardButton.vue')['default']
-    CardDialog: typeof import('./components/CardDialog.vue')['default']
-    'CardDialog copy': typeof import('./components/CardDialog copy.vue')['default']
-    CheckButton: typeof import('./components/common/CheckButton.vue')['default']
-    ConfigManage: typeof import('./components/map/ConfigManage.vue')['default']
-    copy: typeof import('./components/CardDialog copy.vue')['default']
-    CoreHeader: typeof import('./components/core/CoreHeader.vue')['default']
-    CoreMenu: typeof import('./components/core/CoreMenu.vue')['default']
-    DataSummary: typeof import('./components/monitor/DataSummary.vue')['default']
-    DataTable: typeof import('./components/monitor/DataTable.vue')['default']
-    DescriptionsList: typeof import('./components/list/DescriptionsList.vue')['default']
-    DescriptionsListItem: typeof import('./components/list/DescriptionsListItem.vue')['default']
-    DeviceCreate: typeof import('./components/device/DeviceCreate.vue')['default']
-    DeviceManage: typeof import('./components/device/DeviceManage.vue')['default']
-    ElButton: typeof import('element-plus/es')['ElButton']
-    ElCascader: typeof import('element-plus/es')['ElCascader']
-    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
-    ElCol: typeof import('element-plus/es')['ElCol']
-    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
-    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElDialog: typeof import('element-plus/es')['ElDialog']
-    ElDivider: typeof import('element-plus/es')['ElDivider']
-    ElDropdown: typeof import('element-plus/es')['ElDropdown']
-    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
-    ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
-    ElForm: typeof import('element-plus/es')['ElForm']
-    ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElInput: typeof import('element-plus/es')['ElInput']
-    ElLink: typeof import('element-plus/es')['ElLink']
-    ElOption: typeof import('element-plus/es')['ElOption']
-    ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElPopover: typeof import('element-plus/es')['ElPopover']
-    ElRadio: typeof import('element-plus/es')['ElRadio']
-    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
-    ElRow: typeof import('element-plus/es')['ElRow']
-    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
-    ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElSlider: typeof import('element-plus/es')['ElSlider']
-    ElSpace: typeof import('element-plus/es')['ElSpace']
-    ElStatistic: typeof import('element-plus/es')['ElStatistic']
-    ElSwitch: typeof import('element-plus/es')['ElSwitch']
-    ElTable: typeof import('element-plus/es')['ElTable']
-    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
-    ElTag: typeof import('element-plus/es')['ElTag']
-    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']
-    FactorTrend: typeof import('./components/monitor/FactorTrend.vue')['default']
-    GaugeChart: typeof import('./components/chart/GaugeChart.vue')['default']
-    GridSearch: typeof import('./components/grid/GridSearch.vue')['default']
-    HistoricalTrajectory: typeof import('./components/animation/HistoricalTrajectory.vue')['default']
-    MapLocate: typeof import('./components/map/MapLocate.vue')['default']
-    MapScene: typeof import('./components/map/MapScene.vue')['default']
-    MapToolbox: typeof import('./components/map/MapToolbox.vue')['default']
-    MessageBox: typeof import('./components/MessageBox.vue')['default']
-    MissionEdit: typeof import('./components/mission/MissionEdit.vue')['default']
-    MissionImport: typeof import('./components/mission/MissionImport.vue')['default']
-    MissionManage: typeof import('./components/mission/MissionManage.vue')['default']
-    OptionDevice: typeof import('./components/search/OptionDevice.vue')['default']
-    OptionLocation: typeof import('./components/search/OptionLocation.vue')['default']
-    OptionLocation2: typeof import('./components/search/OptionLocation2.vue')['default']
-    OptionMission: typeof import('./components/search/OptionMission.vue')['default']
-    OptionPollutionDegree: typeof import('./components/search/OptionPollutionDegree.vue')['default']
-    OptionTime: typeof import('./components/search/OptionTime.vue')['default']
-    OptionType: typeof import('./components/search/OptionType.vue')['default']
-    ProgressLineChart: typeof import('./components/chart/ProgressLineChart.vue')['default']
-    RealTimeLineChart: typeof import('./components/chart/RealTimeLineChart.vue')['default']
-    RouterLink: typeof import('vue-router')['RouterLink']
-    RouterView: typeof import('vue-router')['RouterView']
-    SceneSearch: typeof import('./components/scene/SceneSearch.vue')['default']
-    SceneTable: typeof import('./components/scene/SceneTable.vue')['default']
-    SearchBar: typeof import('./components/search/SearchBar.vue')['default']
-    SliderBar: typeof import('./components/SliderBar.vue')['default']
-    TrajectoryState: typeof import('./components/animation/TrajectoryState.vue')['default']
-    VehicleData: typeof import('./components/monitor/VehicleData.vue')['default']
-    WeatherData: typeof import('./components/monitor/WeatherData.vue')['default']
-    WeatherDataCopy: typeof import('./components/monitor/WeatherData-copy.vue')['default']
+    BaseCard: (typeof import('./components/BaseCard.vue'))['default'];
+    BaseMap: (typeof import('./components/map/BaseMap.vue'))['default'];
+    CardButton: (typeof import('./components/CardButton.vue'))['default'];
+    CardDialog: (typeof import('./components/CardDialog.vue'))['default'];
+    'CardDialog copy': (typeof import('./components/CardDialog copy.vue'))['default'];
+    CheckButton: (typeof import('./components/common/CheckButton.vue'))['default'];
+    ConfigManage: (typeof import('./components/map/ConfigManage.vue'))['default'];
+    copy: (typeof import('./components/monitor/WeatherData_Old.vue'))['default'];
+    CoreHeader: (typeof import('./components/core/CoreHeader.vue'))['default'];
+    CoreMenu: (typeof import('./components/core/CoreMenu.vue'))['default'];
+    DataSummary: (typeof import('./components/monitor/DataSummary.vue'))['default'];
+    DataTable: (typeof import('./components/monitor/DataTable.vue'))['default'];
+    DescriptionsList: (typeof import('./components/list/DescriptionsList.vue'))['default'];
+    DescriptionsListItem: (typeof import('./components/list/DescriptionsListItem.vue'))['default'];
+    DeviceCreate: (typeof import('./components/device/DeviceCreate.vue'))['default'];
+    DeviceManage: (typeof import('./components/device/DeviceManage.vue'))['default'];
+    ElButton: (typeof import('element-plus/es'))['ElButton'];
+    ElCascader: (typeof import('element-plus/es'))['ElCascader'];
+    ElCheckbox: (typeof import('element-plus/es'))['ElCheckbox'];
+    ElCheckboxGroup: (typeof import('element-plus/es'))['ElCheckboxGroup'];
+    ElCol: (typeof import('element-plus/es'))['ElCol'];
+    ElConfigProvider: (typeof import('element-plus/es'))['ElConfigProvider'];
+    ElDatePicker: (typeof import('element-plus/es'))['ElDatePicker'];
+    ElDialog: (typeof import('element-plus/es'))['ElDialog'];
+    ElDivider: (typeof import('element-plus/es'))['ElDivider'];
+    ElDropdown: (typeof import('element-plus/es'))['ElDropdown'];
+    ElDropdownItem: (typeof import('element-plus/es'))['ElDropdownItem'];
+    ElDropdownMenu: (typeof import('element-plus/es'))['ElDropdownMenu'];
+    ElForm: (typeof import('element-plus/es'))['ElForm'];
+    ElFormItem: (typeof import('element-plus/es'))['ElFormItem'];
+    ElIcon: (typeof import('element-plus/es'))['ElIcon'];
+    ElInput: (typeof import('element-plus/es'))['ElInput'];
+    ElLink: (typeof import('element-plus/es'))['ElLink'];
+    ElOption: (typeof import('element-plus/es'))['ElOption'];
+    ElPagination: (typeof import('element-plus/es'))['ElPagination'];
+    ElPopover: (typeof import('element-plus/es'))['ElPopover'];
+    ElRadio: (typeof import('element-plus/es'))['ElRadio'];
+    ElRadioGroup: (typeof import('element-plus/es'))['ElRadioGroup'];
+    ElRow: (typeof import('element-plus/es'))['ElRow'];
+    ElScrollbar: (typeof import('element-plus/es'))['ElScrollbar'];
+    ElSelect: (typeof import('element-plus/es'))['ElSelect'];
+    ElSlider: (typeof import('element-plus/es'))['ElSlider'];
+    ElSpace: (typeof import('element-plus/es'))['ElSpace'];
+    ElStatistic: (typeof import('element-plus/es'))['ElStatistic'];
+    ElSwitch: (typeof import('element-plus/es'))['ElSwitch'];
+    ElTable: (typeof import('element-plus/es'))['ElTable'];
+    ElTableColumn: (typeof import('element-plus/es'))['ElTableColumn'];
+    ElTabPane: (typeof import('element-plus/es'))['ElTabPane'];
+    ElTabs: (typeof import('element-plus/es'))['ElTabs'];
+    ElTag: (typeof import('element-plus/es'))['ElTag'];
+    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'];
+    FactorTrend: (typeof import('./components/monitor/FactorTrend.vue'))['default'];
+    GaugeChart: (typeof import('./components/chart/GaugeChart.vue'))['default'];
+    GridSearch: (typeof import('./components/grid/GridSearch.vue'))['default'];
+    HistoricalTrajectory: (typeof import('./components/animation/HistoricalTrajectory.vue'))['default'];
+    MapLocate: (typeof import('./components/map/MapLocate.vue'))['default'];
+    MapScene: (typeof import('./components/map/MapScene.vue'))['default'];
+    MapToolbox: (typeof import('./components/map/MapToolbox.vue'))['default'];
+    MessageBox: (typeof import('./components/MessageBox.vue'))['default'];
+    MissionEdit: (typeof import('./components/mission/MissionEdit.vue'))['default'];
+    MissionImport: (typeof import('./components/mission/MissionImport.vue'))['default'];
+    MissionManage: (typeof import('./components/mission/MissionManage.vue'))['default'];
+    OptionDevice: (typeof import('./components/search/OptionDevice.vue'))['default'];
+    OptionLocation: (typeof import('./components/search/OptionLocation.vue'))['default'];
+    OptionLocation2: (typeof import('./components/search/OptionLocation2.vue'))['default'];
+    OptionMission: (typeof import('./components/search/OptionMission.vue'))['default'];
+    OptionPollutionDegree: (typeof import('./components/search/OptionPollutionDegree.vue'))['default'];
+    OptionTime: (typeof import('./components/search/OptionTime.vue'))['default'];
+    OptionType: (typeof import('./components/search/OptionType.vue'))['default'];
+    ProgressLineChart: (typeof import('./components/chart/ProgressLineChart.vue'))['default'];
+    RealTimeLineChart: (typeof import('./components/chart/RealTimeLineChart.vue'))['default'];
+    RouterLink: (typeof import('vue-router'))['RouterLink'];
+    RouterView: (typeof import('vue-router'))['RouterView'];
+    SceneSearch: (typeof import('./components/scene/SceneSearch.vue'))['default'];
+    SceneTable: (typeof import('./components/scene/SceneTable.vue'))['default'];
+    SearchBar: (typeof import('./components/search/SearchBar.vue'))['default'];
+    SliderBar: (typeof import('./components/SliderBar.vue'))['default'];
+    TrajectoryState: (typeof import('./components/animation/TrajectoryState.vue'))['default'];
+    VehicleData: (typeof import('./components/monitor/VehicleData.vue'))['default'];
+    WeatherData: (typeof import('./components/monitor/WeatherData.vue'))['default'];
+    WeatherData_Old: (typeof import('./components/monitor/WeatherData_Old.vue'))['default'];
+    WeatherDataCopy: (typeof import('./components/monitor/WeatherData-copy.vue'))['default'];
   }
   export interface ComponentCustomProperties {
-    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+    vLoading: (typeof import('element-plus/es'))['ElLoadingDirective'];
   }
 }
diff --git a/src/components/monitor/FactorIconText.vue b/src/components/monitor/FactorIconText.vue
new file mode 100644
index 0000000..0fbaede
--- /dev/null
+++ b/src/components/monitor/FactorIconText.vue
@@ -0,0 +1,72 @@
+<template>
+  <el-row justify="start" class="wrap">
+    <el-col :span="6">
+      <el-space direction="vertical" :size="0">
+        <font-awesome-icon v-if="faIcon" :icon="faIcon" class="fa-icon-size" />
+        <component v-if="elIcon" class="el-icon-size" :is="elIcon"></component>
+        <img v-if="img" :src="img" class="icon-size" />
+        <el-text class="factor-text">{{ label }}</el-text>
+      </el-space>
+    </el-col>
+    <el-col :span="18">
+      <el-space direction="vertical" :size="0">
+        <div>
+          <el-text class="factor-number">{{ value }}</el-text>
+          <el-text class="factor-unit"
+            ><sub>{{ unit }}</sub></el-text
+          >
+        </div>
+        <el-text class="factor-text">{{ des }}</el-text>
+      </el-space>
+      <!-- <div >
+        <el-text class="factor-number">{{ value }}</el-text>
+        <el-text class="factor-unit"
+          ><sub>{{ unit }}</sub></el-text
+        >
+      </div> -->
+    </el-col>
+  </el-row>
+</template>
+<script setup>
+defineProps({
+  faIcon: String,
+  elIcon: String,
+  img: String,
+  svg: String,
+  label: String,
+  value: String || Number,
+  unit: String,
+  des: String
+});
+</script>
+<style scoped>
+.wrap {
+  /* border: 1px solid white; */
+  width: 120px;
+}
+.factor-text {
+  color: whitesmoke;
+  font-size: 9px;
+}
+.factor-number {
+  color: whitesmoke;
+  font-size: 20px;
+  line-height: 30px;
+  padding-left: 2px;
+}
+.factor-unit {
+  color: whitesmoke;
+  font-size: 12px;
+  margin-left: 2px;
+}
+
+.fa-icon-size {
+  width: 26px;
+  height: 26px;
+  padding: 2px;
+}
+.el-icon-size {
+  width: 30px;
+  height: 30px;
+}
+</style>
diff --git a/src/components/monitor/FactorLegend.vue b/src/components/monitor/FactorLegend.vue
index bcd447d..caba203 100644
--- a/src/components/monitor/FactorLegend.vue
+++ b/src/components/monitor/FactorLegend.vue
@@ -1,44 +1,66 @@
 <template>
-  <BaseCard>
+  <transition
+    name="el-zoom-in-left"
+    @before-enter="onBeforeEnter"
+    @after-leave="onAfterLeave"
+  >
+    <BaseCard v-show="show">
+      <template #content>
+        <el-row justify="space-between" align="middle">
+          <el-row align="middle">
+            <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" />
+            <span>璧拌埅鍥句緥</span>
+            <span v-if="factor.factorName">锛坽{ factor.factorName }}锛�</span>
+          </el-row>
+          <span @click="show = !show" class="btn-show">
+            <el-icon v-if="show"><ArrowLeftBold /></el-icon>
+            <el-icon v-else><ArrowRightBold /></el-icon>
+          </span>
+        </el-row>
+        <div
+          v-for="(item, index) in legends"
+          :key="index"
+          class="flexbox align-items margin-top"
+        >
+          <div
+            class="rectangle"
+            :style="'background-color: ' + item.color"
+          ></div>
+          <el-row v-if="item.max">
+            <span class="w-40 text-right">{{ item.min }}</span>
+            <span class="w-20 text-center">~</span>
+            <span class="w-40 text-right">{{ item.max }}</span>
+            <span class="w-60 m-l-8">{{ item.unit }}</span>
+          </el-row>
+          <el-row v-else>
+            <span class="w-40 text-right"></span>
+            <span class="w-20 text-center">></span>
+            <span class="w-40 text-right">{{ item.min }}</span>
+            <span class="w-60 m-l-8">{{ item.unit }}</span>
+          </el-row>
+        </div>
+        <div>
+          鍒囨崲缁樺浘妯″紡锛�
+          <el-switch
+            v-model="legendType"
+            width="60"
+            inline-prompt
+            style=""
+            active-text="鍔ㄦ��"
+            inactive-text="鏍囧噯"
+            @change="handleChange"
+          />
+        </div>
+      </template>
+    </BaseCard>
+  </transition>
+  <BaseCard v-show="btnShow">
     <template #content>
-      <el-row justify="space-between" align="middle">
-        <el-row align="middle">
-          <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" />
-          <span>璧拌埅鍥句緥</span>
-        </el-row>
-        <span>{{ factor.factorName }}</span>
-      </el-row>
-      <div
-        v-for="(item, index) in legends"
-        :key="index"
-        class="flexbox align-items margin-top"
-      >
-        <div class="rectangle" :style="'background-color: ' + item.color"></div>
-        <el-row v-if="item.max">
-          <span class="w-40 text-right">{{ item.min }}</span>
-          <span class="w-20 text-center">~</span>
-          <span class="w-40 text-right">{{ item.max }}</span>
-          <span class="w-60 m-l-8">{{ item.unit }}</span>
-        </el-row>
-        <el-row v-else>
-          <span class="w-40 text-right"></span>
-          <span class="w-20 text-center">></span>
-          <span class="w-40 text-right">{{ item.min }}</span>
-          <span class="w-60 m-l-8">{{ item.unit }}</span>
-        </el-row>
-      </div>
-      <div>
-        鍒囨崲缁樺浘妯″紡锛�
-        <el-switch
-          v-model="legendType"
-          width="60"
-          inline-prompt
-          style=""
-          active-text="鍔ㄦ��"
-          inactive-text="鏍囧噯"
-          @change="handleChange"
-        />
-      </div>
+      <span @click="show = !show" class="btn-show">
+        <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" />
+        <el-icon v-if="show"><ArrowLeftBold /></el-icon>
+        <el-icon v-else><ArrowRightBold /></el-icon>
+      </span>
     </template>
   </BaseCard>
 </template>
@@ -60,7 +82,9 @@
     return {
       // 缁樺浘妯″紡锛宖alse: 鏍囧噯妯″紡锛泃rue锛氬姩鎬佹ā寮�
       legendType: false,
-      legends: []
+      legends: [],
+      show: true,
+      btnShow: false
     };
   },
   watch: {
@@ -149,6 +173,12 @@
           this.factor.max
         );
       });
+    },
+    onBeforeEnter() {
+      this.btnShow = false;
+    },
+    onAfterLeave() {
+      this.btnShow = true;
     }
   }
 };
@@ -166,4 +196,10 @@
   --el-switch-on-color: #1f9956;
   --el-switch-off-color: #8b8b8b;
 }
+.btn-show {
+  cursor: pointer;
+}
+.btn-show:hover {
+  color: #23dad1;
+}
 </style>
diff --git a/src/components/monitor/FactorRadio.vue b/src/components/monitor/FactorRadio.vue
index e95e72d..e133e8e 100644
--- a/src/components/monitor/FactorRadio.vue
+++ b/src/components/monitor/FactorRadio.vue
@@ -1,11 +1,32 @@
 <template>
-  <BaseCard>
+  <transition
+    name="el-zoom-in-left"
+    @before-enter="onBeforeEnter"
+    @after-leave="onAfterLeave"
+  >
+    <BaseCard v-show="show" direction="left">
+      <template #content>
+        <!-- <el-collapse-transition> -->
+        <el-radio-group v-model="radio" size="default" @change="handleChange">
+          <el-radio v-for="(item, i) in options" :key="i" :value="item.value">{{
+            item.label
+          }}</el-radio>
+        </el-radio-group>
+        <!-- </el-collapse-transition> -->
+        <span @click="show = !show" class="btn-show">
+          <el-icon v-if="show"><ArrowLeftBold /></el-icon>
+          <el-icon v-else><ArrowRightBold /></el-icon>
+        </span>
+      </template>
+    </BaseCard>
+  </transition>
+  <BaseCard v-show="btnShow">
     <template #content>
-      <el-radio-group v-model="radio" size="default" @change="handleChange">
-        <el-radio v-for="(item, i) in options" :key="i" :value="item.value">{{
-          item.label
-        }}</el-radio>
-      </el-radio-group>
+      <el-row @click="show = !show" class="btn-show">
+        <font-awesome-icon icon="fa-check-circle" />
+        <el-icon v-if="show"><ArrowLeftBold /></el-icon>
+        <el-icon v-else><ArrowRightBold /></el-icon>
+      </el-row>
     </template>
   </BaseCard>
 </template>
@@ -30,7 +51,9 @@
   emits: ['change', 'update:modelValue'],
   data() {
     return {
-      radio: defaultOptions(TYPE0).value
+      radio: defaultOptions(TYPE0).value,
+      show: true,
+      btnShow: false
     };
   },
   computed: {
@@ -42,12 +65,12 @@
     deviceType(nV, oV) {
       if (nV != oV) {
         this.radio = this.options[0].value;
-        this.$emit('update:modelValue', this.radio)
+        this.$emit('update:modelValue', this.radio);
       }
     },
-    modelValue(nV, oV){
+    modelValue(nV, oV) {
       if (nV != oV) {
-        this.radio = nV
+        this.radio = nV;
       }
     }
   },
@@ -55,17 +78,31 @@
     handleChange(value) {
       const item = this.options.find((v) => v.value == value);
       this.$emit('change', item.value, item);
-      this.$emit('update:modelValue', item.value)
+      this.$emit('update:modelValue', item.value);
       // todo 鍦板浘3d鍥惧儚鍒囨崲灞曠ず鐩戞祴鍥犲瓙
+    },
+    onBeforeEnter() {
+      this.btnShow = false;
+    },
+    onAfterLeave() {
+      this.btnShow = true;
     }
   }
 };
 </script>
 <style scoped>
-.el-radio {
+:deep(.el-radio) {
   --el-radio-text-color: white;
   --el-color-primary: #23dad1;
   margin-right: 10px;
   height: initial;
 }
+
+.btn-show {
+  cursor: pointer;
+  padding: 4px 0;
+}
+.btn-show:hover {
+  color: #23dad1;
+}
 </style>
diff --git a/src/components/monitor/FactorTrend.vue b/src/components/monitor/FactorTrend.vue
index 8256d81..babe6f1 100644
--- a/src/components/monitor/FactorTrend.vue
+++ b/src/components/monitor/FactorTrend.vue
@@ -1,7 +1,8 @@
 <template>
   <BaseCard size="medium" direction="left">
     <template #content>
-      <el-scrollbar height="calc(49vh - var(--bevel-length-2))" always>
+      <DashBoard ref="dashBoardRef" :factor-datas="factorDatas"></DashBoard>
+      <el-scrollbar :height="height" always>
         <div v-for="item in seriesList" :key="item.key">
           <el-row
             v-show="selectFactorType.includes(item.series.key)"
@@ -34,8 +35,10 @@
 import { checkboxOptions } from '@/constant/checkbox-options';
 import { factorName } from '@/constant/factor-name';
 import { factorUnit } from '@/constant/factor-unit';
+import DashBoard from '@/views/realtimemode/component/DashBoard.vue';
 
 export default {
+  components: { DashBoard },
   props: {
     loading: Boolean,
     factorDatas: FactorDatas,
@@ -56,16 +59,13 @@
   },
   data() {
     return {
+      height: 'calc(99vh - var(--bevel-length-2))',
       xAxis: [],
       allSeries: new Map(),
       seriesList: []
     };
   },
-  computed: {
-    // factorTypes() {
-    //   return checkboxOptions(this.deviceType);
-    // }
-  },
+  computed: {},
   watch: {
     factorDatas: {
       handler() {
@@ -165,7 +165,16 @@
     getUnit(label) {
       // fixeme 2024.5.15 淇CO灞曠ず鍗曚綅鍜屽師濮嬫暟鎹笉涓�鑷撮棶棰�
       return label == 'CO' ? '渭g/m鲁' : factorUnit[label].unit;
+    },
+    calcHeight() {
+      const h1 = this.$refs.dashBoardRef
+        ? this.$refs.dashBoardRef.$el.offsetHeight
+        : 0;
+      this.height = `calc(98vh - var(--bevel-length-2) - ${h1}px)`;
     }
+  },
+  mounted() {
+    this.calcHeight();
   }
 };
 </script>
diff --git a/src/components/monitor/VehicleData.vue b/src/components/monitor/VehicleData.vue
index 7f7ff70..cef00bd 100644
--- a/src/components/monitor/VehicleData.vue
+++ b/src/components/monitor/VehicleData.vue
@@ -1,11 +1,25 @@
 <template>
-  <el-row justify="center" class="wrap">
+  <el-row justify="start" class="wrap">
+    <!-- <el-space direction="vertical" :size="0">
+      <el-icon size="30"><Stopwatch /></el-icon>
+      <el-text class="speed-text">SPD</el-text>
+    </el-space>
+    <div>
+      <el-text class="speed-number">{{ speed }}</el-text>
+      <el-text class="speed-unit"><sub>km/h</sub></el-text>
+    </div> -->
+    <FactorIconText
+      elIcon="Odometer"
+      label="SPD"
+      :value="speed"
+      unit="km/h"
+    ></FactorIconText>
     <!-- <el-form :inline="true">
       <el-form-item label="杞﹂�燂細" class="tag-2">
         {{ speed }}km/h
       </el-form-item>
     </el-form> -->
-    <GaugeChart name="杞﹂��" :value="speed"></GaugeChart>
+    <!-- <GaugeChart name="杞﹂��" :value="speed"></GaugeChart> -->
   </el-row>
 </template>
 <script>
diff --git a/src/components/monitor/WeatherData-copy.vue b/src/components/monitor/WeatherData-copy.vue
deleted file mode 100644
index 6d53a7e..0000000
--- a/src/components/monitor/WeatherData-copy.vue
+++ /dev/null
@@ -1,87 +0,0 @@
-<template>
-  <el-row class="wrap">
-    <el-form :inline="true" class="form">
-      <el-form-item label="娓╁害锛�" class="w-tag">
-        {{ temprature }}
-      </el-form-item>
-      <el-form-item label="婀垮害锛�" class="w-tag">
-        {{ humidity }}
-      </el-form-item>
-    </el-form>
-    <!-- <div class="w-tag">{{ temprature }}</div>
-    <div class="w-tag">{{ humidity }}</div> -->
-  </el-row>
-  <el-row class="wrap">
-    <el-form :inline="true" class="form">
-      <el-form-item label="椋庡悜锛�" class="w-tag">
-        {{ windDirection }}
-      </el-form-item>
-      <el-form-item label="椋庨�燂細" class="w-tag">
-        {{ windSpeed }}
-      </el-form-item>
-    </el-form>
-    <!-- <div class="w-tag">{{ windDirection }}</div>
-    <div class="w-tag">{{ windSpeed }}</div> -->
-  </el-row>
-</template>
-<script>
-import { FactorDatas } from '@/model/FactorDatas';
-
-export default {
-  props: {
-    loading: Boolean,
-    factorDatas: FactorDatas
-  },
-  data() {
-    return {
-      temprature: '--鈩�',
-      humidity: '--%',
-      windDirection: '--',
-      windSpeed: '--m/s',
-    };
-  },
-  watch: {
-    factorDatas: {
-      handler(nV) {
-        this.temprature = this.lastOne(nV, '8') + '鈩�';
-        this.humidity = this.lastOne(nV, '9') + '%';
-        this.windDirection = this.lastOne(nV, '17');
-        this.windSpeed = this.lastOne(nV, '16') + 'm/s';
-      },
-      deep: true
-    }
-  },
-  methods: {
-    lastOne(factorDatas, key) {
-      const f = factorDatas.factor[key];
-      if (f) {
-        const lastIndex = f.datas.length - 1;
-        return factorDatas.factor[key].datas[lastIndex].factorData;
-      } else {
-        return '--';
-      }
-    }
-  }
-};
-</script>
-<style scoped>
-.w-tag {
-  padding: 2px 4px;
-  /* background-color: green; */
-  border: 1px solid white;
-  border-radius: 2px;
-  -moz-border-radius: 25px;
-  width: 130px;
-  /* Old Firefox */
-}
-
-.form {
-  display: flex;
-  gap: 4px;
-}
-
-.el-form-item {
-  margin-bottom: 4px;
-  margin-right: 0px !important;
-}
-</style>
diff --git a/src/components/monitor/WeatherData.vue b/src/components/monitor/WeatherData.vue
index af32269..c6614c1 100644
--- a/src/components/monitor/WeatherData.vue
+++ b/src/components/monitor/WeatherData.vue
@@ -1,32 +1,38 @@
 <template>
-  <el-row class="wrap">
-    <el-form :inline="true" class="form">
-      <el-form-item label="娓╁害锛�" class="w-tag">
-        {{ temprature }}鈩�
-      </el-form-item>
-      <el-form-item label="婀垮害锛�" class="w-tag">
-        {{ humidity }}%
-      </el-form-item>
-    </el-form>
-    <!-- <div class="w-tag">{{ temprature }}</div>
-    <div class="w-tag">{{ humidity }}</div> -->
-  </el-row>
-  <el-row class="wrap">
-    <el-form :inline="true" class="form">
-      <el-form-item label="椋庡悜锛�" class="w-tag">
-        {{ _windDir }}
-      </el-form-item>
-      <el-form-item label="椋庨�燂細" class="w-tag">
-        {{ windSpeed }}m/s
-      </el-form-item>
-    </el-form>
-    <!-- <div class="w-tag">{{ windDirection }}</div>
-    <div class="w-tag">{{ windSpeed }}</div> -->
-  </el-row>
+  <div>
+    <el-row justify="space-between">
+      <FactorIconText
+        faIcon="fa-solid fa-temperature-half"
+        label="TEMP"
+        :value="temprature"
+        unit="鈩�"
+      ></FactorIconText>
+      <FactorIconText
+        faIcon="fa-droplet"
+        label="HUM"
+        :value="humidity"
+        unit="%"
+      ></FactorIconText>
+    </el-row>
+    <el-row justify="space-between" class="m-t-4">
+      <FactorIconText
+        img="/src/assets/wdr.svg"
+        label="WDR"
+        :value="_windDir"
+      ></FactorIconText>
+      <FactorIconText
+        faIcon="fa-wind"
+        label="WS"
+        :value="windSpeed"
+        unit="m/s"
+      ></FactorIconText>
+    </el-row>
+  </div>
 </template>
 <script>
 import { FactorDatas } from '@/model/FactorDatas';
 import { windDir } from '@/constant/wind-dir';
+import FactorIconText from './FactorIconText.vue';
 
 export default {
   props: {
diff --git a/src/components/monitor/WeatherData_Old.vue b/src/components/monitor/WeatherData_Old.vue
new file mode 100644
index 0000000..98acf23
--- /dev/null
+++ b/src/components/monitor/WeatherData_Old.vue
@@ -0,0 +1,106 @@
+<template>
+  <div>
+    <el-row class="wrap">
+      <el-form :inline="true" class="form">
+        <el-form-item label="娓╁害锛�" class="w-tag">
+          {{ temprature }}鈩�
+        </el-form-item>
+        <el-form-item label="婀垮害锛�" class="w-tag">
+          {{ humidity }}%
+        </el-form-item>
+      </el-form>
+      <!-- <div class="w-tag">{{ temprature }}</div>
+    <div class="w-tag">{{ humidity }}</div> -->
+    </el-row>
+    <el-row class="wrap">
+      <el-form :inline="true" class="form">
+        <el-form-item label="椋庡悜锛�" class="w-tag">
+          {{ _windDir }}
+        </el-form-item>
+        <el-form-item label="椋庨�燂細" class="w-tag">
+          {{ windSpeed }}m/s
+        </el-form-item>
+      </el-form>
+      <!-- <div class="w-tag">{{ windDirection }}</div>
+    <div class="w-tag">{{ windSpeed }}</div> -->
+    </el-row>
+  </div>
+</template>
+<script>
+import { FactorDatas } from '@/model/FactorDatas';
+import { windDir } from '@/constant/wind-dir';
+
+export default {
+  props: {
+    loading: Boolean,
+    factorDatas: FactorDatas,
+    temprature: {
+      type: String,
+      default: '--'
+    },
+    humidity: {
+      type: String,
+      default: '--'
+    },
+    windDirection: {
+      type: String,
+      default: '--'
+    },
+    windSpeed: {
+      type: String,
+      default: '--'
+    }
+  },
+  data() {
+    return {};
+  },
+  watch: {
+    factorDatas: {
+      handler(nV) {
+        // this.temprature = this.lastOne(nV, '8') + '鈩�';
+        // this.humidity = this.lastOne(nV, '9') + '%';
+        // this.windDirection = this.lastOne(nV, '17');
+        // this.windSpeed = this.lastOne(nV, '16') + 'm/s';
+      },
+      deep: true
+    }
+  },
+  computed: {
+    _windDir() {
+      return windDir(this.windDirection);
+    }
+  },
+  methods: {
+    lastOne(factorDatas, key) {
+      const f = factorDatas.factor[key];
+      if (f) {
+        const lastIndex = f.datas.length - 1;
+        return factorDatas.factor[key].datas[lastIndex].factorData;
+      } else {
+        return '--';
+      }
+    }
+  }
+};
+</script>
+<style scoped>
+.w-tag {
+  padding: 2px 4px;
+  /* background-color: green; */
+  border: 1px solid white;
+  border-radius: 2px;
+  -moz-border-radius: 25px;
+  width: 130px;
+  /* Old Firefox */
+}
+
+.form {
+  display: flex;
+  gap: 4px;
+}
+
+.el-form-item {
+  margin-bottom: 4px;
+  margin-right: 0px !important;
+}
+</style>
diff --git a/src/styles/animation.scss b/src/styles/animation.scss
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/styles/animation.scss
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 23285cd..7f53a8b 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,2 +1,6 @@
 @use './base.scss';
 @use './elementUI.scss';
+
+body {
+  font-family: fantasy;
+}
diff --git a/src/views/historymode/HistoryMode.vue b/src/views/historymode/HistoryMode.vue
index 2ced9f5..5e72264 100644
--- a/src/views/historymode/HistoryMode.vue
+++ b/src/views/historymode/HistoryMode.vue
@@ -28,7 +28,6 @@
         :factor="factorDatas.factor[factorType]"
         @change="handleLegendTypeChange"
       ></FactorLegend>
-      <!-- <SourceTrace></SourceTrace> -->
     </el-row>
     <el-row class="historical" justify="center">
       <HistoricalTrajectory
@@ -53,6 +52,13 @@
       :device-type="deviceType"
       :device-code="deviceCode"
     ></DataSheet>
+    <SourceTrace
+      class="source-trace"
+      v-model:factorType="factorType"
+      direction="right"
+      mode="history"
+      :mission-code="missionCode"
+    ></SourceTrace>
   </div>
 </template>
 
@@ -87,6 +93,7 @@
   },
   data() {
     return {
+      missionCode: undefined,
       // 鐩戞祴璁惧绫诲瀷
       deviceType: TYPE0,
       // 鐩戞祴璁惧缂栧彿
@@ -227,6 +234,7 @@
       this.deviceType = deviceType;
       this.deviceCode = deviceCode;
       this.mission = mission;
+      this.missionCode = mission.missionCode;
       let startTime, endTime;
       if (timeArray && timeArray.length == 2) {
         startTime = moment(timeArray[0]).format('YYYY-MM-DD HH:mm:ss');
@@ -307,4 +315,9 @@
   left: 0;
   right: 0;
 }
+.source-trace {
+  position: absolute;
+  right: 0;
+  bottom: 0px;
+}
 </style>
diff --git a/src/views/realtimemode/RealtimeMode.vue b/src/views/realtimemode/RealtimeMode.vue
index 6935614..9eb4352 100644
--- a/src/views/realtimemode/RealtimeMode.vue
+++ b/src/views/realtimemode/RealtimeMode.vue
@@ -19,7 +19,7 @@
       </el-col>
       <el-col span="1"> </el-col>
     </el-row>
-    <DashBoard class="dash-board" :factor-datas="factorDatas"></DashBoard>
+    <!-- <DashBoard class="dash-board" :factor-datas="factorDatas"></DashBoard> -->
     <RealTimeTrend
       class="real-time-trend"
       :factor-datas="factorDatas"
@@ -28,6 +28,7 @@
     <SourceTrace
       class="source-trace"
       v-model:factorType="factorType"
+      :deviceCode="deviceCode"
     ></SourceTrace>
     <!-- <PollutedClueItem></PollutedClueItem> -->
   </div>
@@ -219,7 +220,7 @@
 <style scoped>
 .dash-board {
   position: absolute;
-  left: 0;
+  right: 0;
   bottom: 0px;
 }
 .real-time-trend {
@@ -229,7 +230,7 @@
 }
 .source-trace {
   position: absolute;
-  right: 0;
+  left: 0;
   bottom: 0px;
 }
 </style>
diff --git a/src/views/realtimemode/component/DashBoard.vue b/src/views/realtimemode/component/DashBoard.vue
index 30c87e6..00e6ccc 100644
--- a/src/views/realtimemode/component/DashBoard.vue
+++ b/src/views/realtimemode/component/DashBoard.vue
@@ -1,26 +1,42 @@
 <template>
-  <el-row class="wrap">
-    <el-col v-show="show" span="10">
-      <BaseCard>
-        <template #content>
-          <WeatherData
-            :factor-datas="factorDatas"
-            :temprature="temprature"
-            :humidity="humidity"
-            :wind-direction="windDirection"
-            :wind-speed="windSpeed"
-          ></WeatherData>
-          <VehicleData :factor-datas="factorDatas" :speed="speed"></VehicleData>
-          <el-row justify="center">
-            <div class="tag-time">鏃堕棿锛歿{ time }}</div>
-          </el-row>
-        </template>
-      </BaseCard>
-    </el-col>
-    <el-col span="2">
-      <CardButton name="瀹炴椂鐩戞祴" @click="() => (show = !show)"></CardButton>
-    </el-col>
-  </el-row>
+  <div>
+    <!-- <el-row justify="end">
+      <el-text class="tag-time" size="default">
+        <el-icon><Timer /></el-icon>
+        {{ time }}
+      </el-text>
+    </el-row> -->
+    <el-row class="dash-wrap">
+      <el-col :span="8">
+        <FactorIconText
+          elIcon="Timer"
+          label="TIME"
+          :value="hms"
+          :des="date"
+        ></FactorIconText>
+        <VehicleData
+          :factor-datas="factorDatas"
+          :speed="speed"
+          class="m-t-4"
+        ></VehicleData>
+        <!-- <el-text class="tag-time" size="default">
+          <el-icon><Timer /></el-icon>
+          {{ time }}
+        </el-text> -->
+      </el-col>
+      <el-col :span="16">
+        <WeatherData
+          :factor-datas="factorDatas"
+          :temprature="temprature"
+          :humidity="humidity"
+          :wind-direction="windDirection"
+          :wind-speed="windSpeed"
+        ></WeatherData>
+      </el-col>
+    </el-row>
+    <!-- <el-row justify="center"> -->
+    <!-- </el-row> -->
+  </div>
 </template>
 <script>
 import { FactorDatas } from '@/model/FactorDatas';
@@ -41,7 +57,9 @@
       windDirection: '--',
       windSpeed: '--',
       speed: '0',
-      time: '----/--/--'
+      time: '----/--/--',
+      date: '----/--/--',
+      hms: '--:--:--'
     };
   },
   methods: {
@@ -61,6 +79,8 @@
       this.windSpeed = this.getFactorData(factorDatas, index, '16') + '';
       this.speed = this.getFactorData(factorDatas, index, '14', 1);
       this.time = factorDatas.times[index];
+      this.date = this.time.split(' ')[0];
+      this.hms = this.time.split(' ')[1];
     }
   },
   mounted() {
@@ -69,10 +89,14 @@
 };
 </script>
 <style scoped>
+.dash-wrap {
+  border-bottom: 1px solid rgba(255, 255, 255, 0.445);
+}
 .tag-time {
+  color: rgba(255, 255, 255, 0.815);
   padding: 2px 4px;
-  border: 1px solid white;
+  /* border: 1px solid white;
   border-radius: 2px;
-  -moz-border-radius: 25px;
+  -moz-border-radius: 25px; */
 }
 </style>
diff --git a/src/views/realtimemode/component/DashBoard_Old.vue b/src/views/realtimemode/component/DashBoard_Old.vue
new file mode 100644
index 0000000..30c87e6
--- /dev/null
+++ b/src/views/realtimemode/component/DashBoard_Old.vue
@@ -0,0 +1,78 @@
+<template>
+  <el-row class="wrap">
+    <el-col v-show="show" span="10">
+      <BaseCard>
+        <template #content>
+          <WeatherData
+            :factor-datas="factorDatas"
+            :temprature="temprature"
+            :humidity="humidity"
+            :wind-direction="windDirection"
+            :wind-speed="windSpeed"
+          ></WeatherData>
+          <VehicleData :factor-datas="factorDatas" :speed="speed"></VehicleData>
+          <el-row justify="center">
+            <div class="tag-time">鏃堕棿锛歿{ time }}</div>
+          </el-row>
+        </template>
+      </BaseCard>
+    </el-col>
+    <el-col span="2">
+      <CardButton name="瀹炴椂鐩戞祴" @click="() => (show = !show)"></CardButton>
+    </el-col>
+  </el-row>
+</template>
+<script>
+import { FactorDatas } from '@/model/FactorDatas';
+import { realTimeMapAnimation } from '@/utils/map/animation';
+
+export default {
+  props: {
+    deviceType: {
+      type: String
+    },
+    factorDatas: FactorDatas
+  },
+  data() {
+    return {
+      show: true,
+      temprature: '--',
+      humidity: '--',
+      windDirection: '--',
+      windSpeed: '--',
+      speed: '0',
+      time: '----/--/--'
+    };
+  },
+  methods: {
+    getFactorData(factorDatas, index, key, scale = 10) {
+      const _factor = factorDatas.factor[key];
+      if (_factor != undefined) {
+        let d = _factor.datas[index].factorData;
+        return Math.round(d * scale) / scale;
+      } else {
+        return '--';
+      }
+    },
+    refresh(factorDatas, index) {
+      this.temprature = this.getFactorData(factorDatas, index, '8') + '';
+      this.humidity = this.getFactorData(factorDatas, index, '9') + '';
+      this.windDirection = this.getFactorData(factorDatas, index, '17') + '';
+      this.windSpeed = this.getFactorData(factorDatas, index, '16') + '';
+      this.speed = this.getFactorData(factorDatas, index, '14', 1);
+      this.time = factorDatas.times[index];
+    }
+  },
+  mounted() {
+    realTimeMapAnimation.setOnEachFrameCallback(this.refresh);
+  }
+};
+</script>
+<style scoped>
+.tag-time {
+  padding: 2px 4px;
+  border: 1px solid white;
+  border-radius: 2px;
+  -moz-border-radius: 25px;
+}
+</style>
diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue
index 0145b9a..19012f9 100644
--- a/src/views/sourcetrace/SourceTrace.vue
+++ b/src/views/sourcetrace/SourceTrace.vue
@@ -1,11 +1,11 @@
 <template>
   <el-row>
-    <el-col span="2" class="flex-col">
+    <el-col v-if="direction == 'right'" span="2" class="flex-col">
       <el-row justify="end">
         <CardButton
           direction="left"
           name="鍔ㄦ�佹函婧�"
-          @click="() => (show = !show)"
+          @click="handleDisplayChange"
         ></CardButton>
       </el-row>
       <el-row class="flex-col">
@@ -57,6 +57,21 @@
         </template>
       </BaseCard>
     </el-col>
+    <el-col v-if="direction == 'left'" span="2" class="flex-col">
+      <el-row justify="start">
+        <CardButton
+          direction="right"
+          name="鍔ㄦ�佹函婧�"
+          @click="handleDisplayChange"
+        ></CardButton>
+      </el-row>
+      <el-row class="flex-col">
+        <PollutedExceptionItem
+          :item="selectedException"
+          @showMarksAndPolygon="showMarksAndPolygon"
+        ></PollutedExceptionItem>
+      </el-row>
+    </el-col>
     <PollutedClueItem
       v-model="clueDialog"
       :item="selectedClue"
@@ -72,6 +87,7 @@
 import moment from 'moment';
 
 import websocket from '@/api/websocket';
+import dataAnalysisApi from '@/api/dataAnalysisApi';
 import sector from '@/utils/map/sector';
 import mapUtil from '@/utils/map/util';
 import { sceneTypes, sceneIcon } from '@/constant/scene-types';
@@ -88,14 +104,25 @@
 
 const NO_SCENE = 'no_scene';
 
+const MODE_REALTIME = 'realtime';
+const MODE_HISTORY = 'history';
+
 const props = defineProps({
-  factorType: String
+  direction: {
+    type: String,
+    default: 'left'
+  },
+  factorType: String,
+  // 妯″紡锛宺ealtime锛氬疄鏃舵ā寮忥紱history锛氬巻鍙叉暟鎹ā寮�
+  mode: {
+    type: String,
+    default: 'realtime'
+  },
+  missionCode: String,
+  deviceCode: String
 });
 
 const emits = defineEmits(['update:factorType']);
-
-const height = `30vh`;
-const width = `60vh`;
 
 const show = ref(false);
 const clueDialog = ref(false);
@@ -110,6 +137,17 @@
 const selectedSceneTypes = ref([]);
 const sceneOptions = ref([]);
 
+function handleDisplayChange() {
+  show.value = !show.value;
+  if (
+    !show.value &&
+    selectedException.value &&
+    selectedException.value.showMore
+  ) {
+    showMarksAndPolygon(selectedException.value);
+  }
+}
+
 function scrollToBottom() {
   const h1 = scrollContentRef.value.clientHeight + 100;
   setTimeout(() => {
@@ -123,9 +161,9 @@
   }, 100);
 }
 
-const streams = reactive([]);
+const streams = ref([]);
 const filterStreams = computed(() => {
-  return streams.filter((v) => {
+  return streams.value.filter((v) => {
     // 鍒ゆ柇娑堟伅绫诲瀷鏄惁閫変腑
     const b1 = selectedMsgTypes.value.indexOf(v._type) != -1;
     let b2, b3;
@@ -163,7 +201,7 @@
     type2: 0,
     type3: 0
   };
-  streams.forEach((v) => {
+  streams.value.forEach((v) => {
     switch (v._type) {
       case '1':
         count.type1++;
@@ -187,10 +225,15 @@
 function dealMsg(data) {
   const { type, content } = websocketMsgParser.parseMsg(data);
   const obj = reactive(JSON.parse(content));
-  obj._type = type;
+  if (obj.deviceCode == props.deviceCode) {
+    obj._type = type;
+    dealObj(obj);
+  }
+}
 
+function dealObj(obj) {
   // 姹℃煋绾跨储 PollutedClue
-  if (type == '1') {
+  if (obj._type == '1') {
     obj.showMore = false;
     console.log('姹℃煋寮傚父鍒囩墖: ', obj);
 
@@ -201,13 +244,13 @@
     // scrollToTop();
     // drawPolygon(obj.pollutedArea);
     parseChartData(obj);
-  } else if (type == '2') {
+  } else if (obj._type == '2') {
     // const obj = JSON.parse(content);
     // obj._type = type;
     console.log('姹℃煋绾跨储缁撴灉: ', obj);
     obj._timestr = timeFormatter(obj.time);
     addNewMsg(obj);
-  } else if (type == '3') {
+  } else if (obj._type == '3') {
     console.log('姹℃煋鎻愰啋鍒囩墖: ', obj);
     addNewMsg(obj);
     parseChartData(obj);
@@ -264,7 +307,7 @@
 const leftMsgList = [];
 function addNewMsg(msg, inside) {
   if (!addNewMsgTask && (leftMsgList.length == 0 || inside)) {
-    streams.splice(0, 0, msg);
+    streams.value.splice(0, 0, msg);
     addNewMsgTask = setTimeout(() => {
       clearTimeout(addNewMsgTask);
       addNewMsgTask = undefined;
@@ -278,8 +321,32 @@
   }
 }
 
+function fetchPollutionTraceHistory() {
+  dataAnalysisApi.fetchPollutionTraceHistory(props.missionCode).then((res) => {
+    const objList = JSON.parse(res.data);
+    objList.forEach((obj) => {
+      obj._type = obj.msgType + '';
+      if (obj._type == '1') {
+        obj.showMore = false;
+        show.value = true;
+        parseChartData(obj);
+      } else if (obj._type == '2') {
+        obj._timestr = timeFormatter(obj.time);
+      } else if (obj._type == '3') {
+        parseChartData(obj);
+      }
+      optionsFilte(obj);
+    });
+    streams.value = objList;
+  });
+}
+
 onMounted(() => {
-  websocket.registerReceiveEvent(dealMsg);
+  if (props.mode == MODE_REALTIME) {
+    websocket.registerReceiveEvent(dealMsg);
+  } else if (props.missionCode) {
+    fetchPollutionTraceHistory();
+  }
 });
 onUnmounted(() => {
   websocket.removeReceiveEvent(dealMsg);
@@ -296,6 +363,15 @@
     selectedClue.value._selected = false;
   }
 });
+
+watch(
+  () => props.missionCode,
+  (nV, oV) => {
+    if (nV != oV) {
+      fetchPollutionTraceHistory();
+    }
+  }
+);
 
 function handleOpen(item) {
   switch (item._type) {
@@ -319,7 +395,7 @@
 }
 
 function hideAll() {
-  streams.forEach((s) => {
+  streams.value.forEach((s) => {
     if (polygonMap.has(s.guid)) {
       s.showMore = false;
       map.remove(polygonMap.get(s.guid));
@@ -368,6 +444,12 @@
 
 function parseChartData(obj) {
   // console.log('鎶樼嚎鍥撅細start');
+  obj.pollutedData._startTime = moment(obj.pollutedData.startTime).format(
+    'HH:mm:ss'
+  );
+  obj.pollutedData._endTime = moment(obj.pollutedData.endTime).format(
+    'HH:mm:ss'
+  );
   const factorDatas = new FactorDatas();
   factorDatas.setData(obj.pollutedData.historyDataList, 0, () => {
     obj._chartOptions = factorDataParser.parseData(factorDatas, [
@@ -383,101 +465,6 @@
 
 function timeFormatter(time) {
   return moment(time).format('YYYY-MM-DD HH:mm:ss');
-}
-
-/******************************************************************************************************************** */
-
-/**
- * 娣诲姞涓�鏉″伐浣滄祦淇℃伅
- * @param {*} data
- */
-const putWorkStream = (data) => {
-  data.substring();
-  const obj = JSON.parse(data);
-  console.log('sourcetrace: ', obj);
-
-  obj._statusStr = exceptionStatus(obj.status);
-
-  if (streams.length == 0) {
-    streams.push(obj);
-  } else {
-    const index = streams.findIndex((v) => {
-      return v.guid == obj.guid;
-    });
-    if (index != -1) {
-      const old = streams[index];
-      obj.showMore = old.showMore;
-      old.relatedSceneList.forEach((s) => {
-        const index = obj.relatedSceneList.findIndex((v) => {
-          return v.guid == s.guid;
-        });
-        if (index == -1) {
-          obj.relatedSceneList.push(s);
-        }
-      });
-      streams.splice(index, 1, obj);
-    } else {
-      streams.unshift(obj);
-    }
-
-    show.value = true;
-  }
-
-  // scrollToBottom();
-  scrollToTop();
-
-  const excObj = streams.find((v) => {
-    return v.factorId + '' == props.factorType;
-  });
-  if (excObj) {
-    drawSector(excObj);
-  }
-};
-
-function exceptionStatus(value) {
-  switch (value) {
-    case 1:
-      return '寮傚父鍙戠敓涓�';
-    case 2:
-      return '寮傚父宸茬粨鏉�';
-    default:
-      return '寮傚父宸茬粨鏉�';
-  }
-}
-
-let lastDrawObjGuid;
-function drawSector(exceptionObj) {
-  emits('update:factorType', exceptionObj.factorId + '');
-  setTimeout(() => {
-    if (exceptionObj.guid != lastDrawObjGuid) {
-      sector.clearSectorPt();
-      lastDrawObjGuid = exceptionObj.guid;
-    }
-    // 1. 缁樺埗鏂版墖褰㈠尯鍩�
-    const datavo = exceptionObj.midData;
-    const factorDatas = new FactorDatas();
-    factorDatas.setData([datavo], 0, () => {
-      const pr = sector.drawSectorPt(factorDatas, 0);
-      // 璋冩暣瑙嗚灞呬腑鏄剧ず
-      // mapUtil.setCenter(pr.p);
-      drawMarks(exceptionObj.relatedSceneList);
-    });
-  }, 1000);
-}
-
-let layer = undefined;
-function drawMarks(sceneList) {
-  if (layer != undefined) {
-    mapUtil.removeViews(layer);
-    // layer = undefined;
-  }
-  if (sceneList.length != 0) {
-    const icons = [];
-    sceneList.forEach((s) => {
-      icons.push(sceneIcon(s.typeId));
-    });
-    layer = marks.createLabelMarks(icons, sceneList, true);
-  }
 }
 </script>
 <style scoped>
@@ -512,7 +499,7 @@
 .scrollbar {
   width: 400px;
   /* max-width: 60vw; */
-  height: 45vh;
+  height: 65vh;
   /* color: #02ffea; */
   padding-right: 10px;
 }
diff --git a/src/views/sourcetrace/component/ClueRecordItem.vue b/src/views/sourcetrace/component/ClueRecordItem.vue
index a8aecbb..5237bb5 100644
--- a/src/views/sourcetrace/component/ClueRecordItem.vue
+++ b/src/views/sourcetrace/component/ClueRecordItem.vue
@@ -12,7 +12,9 @@
             <el-text type="primary" size="default">
               <el-icon><Timer /></el-icon>
               {{
-                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+                item.pollutedData._startTime +
+                ' - ' +
+                item.pollutedData._endTime
               }}
             </el-text>
           </el-space>
@@ -22,6 +24,7 @@
           <el-tag
             effect="plain"
             type="info"
+            style="color: black"
             size="small"
             hit
             round
@@ -64,7 +67,10 @@
         <el-tag type="danger" effect="dark" size="small">绾跨储</el-tag>
         <el-link type="primary" @click="emits('open')"> 璇︽儏 </el-link>
       </el-row>
-      <el-text type="danger">{{ item.advice }}</el-text>
+      <el-space>
+        <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon>
+        <el-text type="primary">{{ item.advice }}</el-text>
+      </el-space>
     </div>
     <el-row v-else-if="item._type == '3'">
       <el-col :span="3">
@@ -76,17 +82,20 @@
             <el-text type="primary" size="default">
               <el-icon><Timer /></el-icon>
               {{
-                item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+                item.pollutedData._startTime +
+                ' - ' +
+                item.pollutedData._endTime
               }}
             </el-text>
           </el-space>
-          <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
+          <!-- <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link> -->
         </el-row>
         <div>
           <el-tag
             effect="plain"
             type="info"
             size="small"
+            style="color: black"
             hit
             round
             class="m-r-4"
@@ -158,7 +167,7 @@
 function formatDistanceType(value) {
   switch (value) {
     case 'TYPE1':
-      return '50绫充互鍐�';
+      return '100绫充互鍐�';
     case 'TYPE2':
       return '50绫� - 500绫充互鍐�';
     case 'TYPE3':
diff --git a/src/views/sourcetrace/component/PollutedExceptionItem.vue b/src/views/sourcetrace/component/PollutedExceptionItem.vue
index 0aeedff..9351fb8 100644
--- a/src/views/sourcetrace/component/PollutedExceptionItem.vue
+++ b/src/views/sourcetrace/component/PollutedExceptionItem.vue
@@ -27,9 +27,9 @@
             <el-icon><Timer /></el-icon>
             {{
               '鍒囩墖鏃舵锛�' +
-              item.pollutedData.startTime +
+              item.pollutedData._startTime +
               ' - ' +
-              item.pollutedData.endTime
+              item.pollutedData._endTime
             }}
           </el-text>
         </div>
@@ -46,7 +46,7 @@
         </div> -->
         <div>
           <el-text type="primary">
-            <el-icon><BellFilled /></el-icon>
+            <el-icon><Bell /></el-icon>
             寮傚父绫诲瀷锛歿{ item.pollutedData.exception }}
           </el-text>
         </div>
@@ -105,7 +105,8 @@
         ></RealTimeLineChart>
         <!-- </div> -->
         <div class="border-dashed">
-          <el-text type="" tag="mark">
+          <el-icon color="#ffbc58" size="20"><WarningFilled /></el-icon>
+          <el-text type="primary" tag="b">
             {{ item.pollutedSource.conclusion }}
           </el-text>
         </div>
@@ -113,57 +114,6 @@
           :show-marks="item.showMore"
           :scene-list="item.pollutedSource.sceneList"
         ></SceneTable>
-
-        <!-- <el-space gap="4">
-                  <el-tag :type="item.status == 1 ? 'danger' : 'info'">{{
-                    item._statusStr
-                  }}</el-tag>
-                  <el-text type="default">{{ item.exception }}</el-text>
-                </el-space>
-                <el-row gap="4">
-                  <el-text type="primary">鍙戠敓鏃堕棿锛�</el-text>
-                  <el-text type="primary">{{
-                    item.startTime + ' 鑷� '
-                  }}</el-text>
-                  <el-text type="primary">{{
-                    item.status == 1 ? '褰撳墠' : item.endTime
-                  }}</el-text>
-                </el-row>
-                <el-row>
-                  <el-col :span="6">
-                    <el-statistic title="鍥犲瓙" :value="item.factorName" />
-                  </el-col>
-                  <el-col :span="6">
-                    <el-statistic title="鍧囧��" :value="item.avg" />
-                  </el-col>
-                  <el-col :span="6">
-                    <el-statistic title="宄板��" :value="item.max" />
-                  </el-col>
-                  <el-col :span="6">
-                    <el-statistic title="璋峰��" :value="item.min" />
-                  </el-col>
-                </el-row>
-                <el-row justify="space-between">
-                  <el-link
-                    type="primary"
-                    @click="item.showMore = !item.showMore"
-                  >
-                    {{
-                      (item.showMore ? '鏀惰捣婧簮鍦烘櫙' : '鏌ョ湅婧簮鍦烘櫙') +
-                      '锛�' +
-                      item.relatedSceneList.length +
-                      '锛�'
-                    }}
-                  </el-link>
-                  <el-link type="primary" @click="drawSector(item)">
-                    鏌ョ湅寮傚父
-                  </el-link>
-                </el-row>
-                <SceneTable
-                  v-show="item.showMore"
-                  :scene-list="item.relatedSceneList"
-                ></SceneTable>
-                <el-divider /> -->
       </el-scrollbar>
     </template>
   </BaseCard>
@@ -233,13 +183,15 @@
   padding: 0 4px;
   /* margin-right: 2px; */
   width: 340px;
-  height: 35vh;
-  border-right: 1px solid white;
+  height: 400px;
+  /* border-right: 1px solid white; */
   border-radius: 2px;
 }
 
 .border-dashed {
   /* border: 1px dashed white; */
+  display: flex;
+  /* align-items: center; */
   border: 1px dashed #ffbc58;
   padding: 0px 1px;
   margin-bottom: 4px;

--
Gitblit v1.9.3