From 306ef09707d6bcf9ffa67de55f86ab6f4362deee Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 18 七月 2025 10:04:01 +0800
Subject: [PATCH] 2025.7.18 动态溯源-测试版本

---
 src/utils/chart/chart-option.js                           |   34 ++++++
 src/views/sourcetrace/SourceTrace.vue                     |  100 ++++++++++++-------
 src/views/historymode/HistoryMode.vue                     |   10 +
 src/views/sourcetrace/component/PollutedWarnItem.vue      |   41 +++++++
 src/api/index.js                                          |   24 ++--
 src/views/sourcetrace/component/PollutedExceptionItem.vue |   36 +++++--
 src/components.d.ts                                       |    2 
 src/views/sourcetrace/component/ClueRecordItem.vue        |    4 
 src/components/chart/RealTimeLineChart.vue                |   10 +
 9 files changed, 190 insertions(+), 71 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index efcd5a1..fb3b440 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -2,7 +2,7 @@
 import { ElMessage } from 'element-plus';
 
 const openLog = false;
-const debug = true;
+const debug = false;
 
 let ip1 = 'http://47.100.191.150:9029/';
 let ws = `47.100.191.150:9030`;
@@ -30,13 +30,13 @@
   i.interceptors.request.use(
     function (config) {
       // 鍦ㄥ彂閫佽姹備箣鍓嶅仛浜涗粈涔�
-      if (import.meta.env.DEV && openLog) {
-        console.log('==>璇锋眰寮�濮�');
-        console.log(`${config.baseURL}${config.url}`);
-        if (config.data) {
-          console.log('==>璇锋眰鏁版嵁', config.data);
-        }
-      }
+      // if (import.meta.env.DEV && openLog) {
+      //   console.log('==>璇锋眰寮�濮�');
+      //   console.log(`${config.baseURL}${config.url}`);
+      //   if (config.data) {
+      //     console.log('==>璇锋眰鏁版嵁', config.data);
+      //   }
+      // }
       return config;
     },
     function (error) {
@@ -59,8 +59,12 @@
       // 2xx 鑼冨洿鍐呯殑鐘舵�佺爜閮戒細瑙﹀彂璇ュ嚱鏁般��
       // 瀵瑰搷搴旀暟鎹仛鐐逛粈涔�
       if (import.meta.env.DEV && openLog) {
-        console.log(response);
-        console.log('==>璇锋眰缁撴潫');
+        console.log('|------------------------------------------');
+        console.log('|--璇锋眰: ', `${response.request.responseURL}`);
+        if (response.config.data) {
+          console.log('|--鏁版嵁: ', response.config.data);
+        }
+        console.log('|--缁撴灉: ', response.data);
       }
       if (response.status == 200) {
         if (
diff --git a/src/components.d.ts b/src/components.d.ts
index 80dc883..fa422c5 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -14,7 +14,6 @@
     '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']
@@ -39,6 +38,7 @@
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
     ElLink: typeof import('element-plus/es')['ElLink']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
diff --git a/src/components/chart/RealTimeLineChart.vue b/src/components/chart/RealTimeLineChart.vue
index cc72183..bd75435 100644
--- a/src/components/chart/RealTimeLineChart.vue
+++ b/src/components/chart/RealTimeLineChart.vue
@@ -9,7 +9,7 @@
 </template>
 <script>
 import * as echarts from 'echarts';
-import { smallLineOption } from '@/utils/chart/chart-option';
+import { smallLineOption, baseVisualMap } from '@/utils/chart/chart-option';
 
 export default {
   props: {
@@ -31,7 +31,9 @@
     yMinInterval: {
       type: Number,
       default: 1
-    }
+    },
+    // 寮傚父鏁版嵁绱㈠紩鑼冨洿闆嗗悎锛孾[i1,i2], [i3,i4],...]
+    exceptionIndexArr: Array
   },
   data() {
     return {
@@ -52,6 +54,10 @@
       const { xAxis, series } = this.modelValue;
       if (!this.option) {
         this.option = smallLineOption(xAxis, series, this.yMinInterval);
+        if (this.exceptionIndexArr) {
+          const visualMap = baseVisualMap(this.exceptionIndexArr);
+          this.option.visualMap = visualMap;
+        }
       } else {
         this.option.xAxis[0].data = xAxis;
         this.option.series = series;
diff --git a/src/utils/chart/chart-option.js b/src/utils/chart/chart-option.js
index 99337d0..3395e29 100644
--- a/src/utils/chart/chart-option.js
+++ b/src/utils/chart/chart-option.js
@@ -14,6 +14,38 @@
   return fontSize;
 }
 
+function baseVisualMap(area) {
+  const _pieces = [];
+  area.forEach((v, i) => {
+    // if (i == 0) {
+    //   _pieces.push({
+    //     lt: v[0],
+    //     color: 'green'
+    //   });
+    // }
+    _pieces.push({
+      gte: v[0],
+      lte: v[1],
+      color: 'red'
+    });
+  });
+  // const lastOne = area[area.length - 1];
+  // _pieces.push({
+  //   gt: lastOne[1],
+  //   color: 'green'
+  // });
+  return {
+    type: 'piecewise',
+    // type: 'continuous',
+    show: false,
+    dimension: 0,
+    pieces: _pieces,
+    outOfRange: {
+      color: ['#5470c6']
+    }
+  };
+}
+
 // 鎶樼嚎鍥�
 function factorLineOption(_xAxis, _series) {
   var fontSize = fGetChartFontSize();
@@ -301,4 +333,4 @@
   return option;
 }
 
-export { factorLineOption, smallLineOption, gaugeOption };
+export { factorLineOption, smallLineOption, gaugeOption, baseVisualMap };
diff --git a/src/views/historymode/HistoryMode.vue b/src/views/historymode/HistoryMode.vue
index 2865dec..452fc0d 100644
--- a/src/views/historymode/HistoryMode.vue
+++ b/src/views/historymode/HistoryMode.vue
@@ -126,7 +126,7 @@
     factorType(nValue, oValue) {
       if (nValue != oValue && this.status == 0) {
         Layer.clear();
-        this.draw();
+        this.draw(true);
         // this.drawHighlightPollution();
       }
     }
@@ -169,13 +169,17 @@
       done();
       this.draw();
     },
-    draw() {
+    draw(notSetBound) {
       // 鍒锋柊鍥句緥
       const factor = this.factorDatas.factor[this.factorType];
       sector.clearSector();
       // this.drawRoadLine(factor);
       this.drawRoadMap(factor);
       this.drawMassMarks(factor);
+      // 璋冩暣鍦板浘瑙嗚
+      if (!notSetBound) {
+        mapUtil.setBound(this.factorDatas.lnglats_GD);
+      }
     },
     // 缁樺埗3D璧拌璺嚎鍥�
     drawRoadMap(e) {
@@ -190,8 +194,6 @@
       marks.drawMassMarks(this.factorDatas, e, (index) => {
         this.handelIndexChange(index);
       });
-      // 璋冩暣鍦板浘瑙嗚
-      mapUtil.setBound(this.factorDatas.lnglats_GD);
     },
     drawSector(index) {
       // 1. 缁樺埗鏂版墖褰㈠尯鍩�
diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue
index 9cfbd0e..2ef9353 100644
--- a/src/views/sourcetrace/SourceTrace.vue
+++ b/src/views/sourcetrace/SourceTrace.vue
@@ -41,8 +41,8 @@
             <SourceTraceFilter
               v-model:data-slice="selectedMsgTypes"
               v-model:factor-type="selectedFactorTypes"
-              :factor-options="factorOptions"
               v-model:scene-type="selectedSceneTypes"
+              :factor-options="factorOptions"
               :scene-options="sceneOptions"
             ></SourceTraceFilter>
             <!-- <el-divider direction="vertical"></el-divider> -->
@@ -55,7 +55,11 @@
               </el-space>
             </div>
           </el-row>
-          <el-scrollbar ref="scrollbarRef" class="scrollbar">
+          <el-scrollbar
+            ref="scrollbarRef"
+            class="scrollbar"
+            v-loading="loading"
+          >
             <TransitionGroup name="list">
               <div
                 v-for="item in filterStreams"
@@ -154,6 +158,8 @@
 const selectedSceneTypes = ref([]);
 const sceneOptions = ref([]);
 
+const loading = ref(false);
+
 function handleDisplayChange() {
   show.value = !show.value;
   if (
@@ -188,7 +194,11 @@
       case '1':
       case '3':
         // 鍒ゆ柇鐩戞祴鍥犲瓙绫诲瀷鏄惁閫変腑
-        b2 = selectedFactorTypes.value.indexOf(v.pollutedData.factorId) != -1;
+        for (const key in v.pollutedData.statisticMap) {
+          const value = v.pollutedData.statisticMap[key];
+          b2 = b2 || selectedFactorTypes.value.indexOf(value.factorId) != -1;
+        }
+
         // 鍒ゆ柇鍦烘櫙绫诲瀷鏄惁閫変腑
         if (v.pollutedSource.sceneList.length == 0) {
           b3 = selectedSceneTypes.value.indexOf(NO_SCENE) != -1;
@@ -282,17 +292,19 @@
     case '1':
     case '3':
       // 绛涢�夌洃娴嬪洜瀛愮被鍨�
-      if (
-        factorOptions.value.findIndex(
-          (v) => v.value == objData.pollutedData.factorId
-        ) == -1
-      ) {
-        factorOptions.value.push({
-          label: objData.pollutedData.factorName,
-          value: objData.pollutedData.factorId
-        });
-        selectedFactorTypes.value.push(objData.pollutedData.factorId);
+      for (const key in objData.pollutedData.statisticMap) {
+        const value = objData.pollutedData.statisticMap[key];
+        if (
+          factorOptions.value.findIndex((v) => v.value == value.factorId) == -1
+        ) {
+          factorOptions.value.push({
+            label: value.factorName,
+            value: value.factorId
+          });
+          selectedFactorTypes.value.push(value.factorId);
+        }
       }
+
       // 绛涢�夊満鏅被鍨�
       if (objData.pollutedSource.sceneList.length == 0) {
         // 鑻ユ病鏈夋壘鍒伴闄╂簮鏃讹紝灏嗚鍒嗙被璁惧畾涓簄ull
@@ -339,23 +351,27 @@
 }
 
 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;
-  });
+  loading.value = true;
+  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;
+    })
+    .finally(() => (loading.value = false));
 }
 
 onMounted(() => {
@@ -481,13 +497,23 @@
   );
   const factorDatas = new FactorDatas();
   factorDatas.setData(obj.pollutedData.historyDataList, 0, () => {
-    obj._chartOptions = factorDataParser.parseData(factorDatas, [
-      {
-        label: obj.pollutedData.factorName,
-        name: obj.pollutedData.factorName,
-        value: obj.pollutedData.factorId + ''
-      }
-    ]);
+    for (const key in obj.pollutedData.statisticMap) {
+      const value = obj.pollutedData.statisticMap[key];
+      value._chartOptions = factorDataParser.parseData(factorDatas, [
+        {
+          label: value.factorName,
+          name: value.factorName,
+          value: value.factorId + ''
+        }
+      ]);
+    }
+    // obj._chartOptions = factorDataParser.parseData(factorDatas, [
+    //   {
+    //     label: obj.pollutedData.factorName,
+    //     name: obj.pollutedData.factorName,
+    //     value: obj.pollutedData.factorId + ''
+    //   }
+    // ]);
     // console.log('鎶樼嚎鍥撅細', obj._chartOptions);
   });
 }
diff --git a/src/views/sourcetrace/component/ClueRecordItem.vue b/src/views/sourcetrace/component/ClueRecordItem.vue
index 25a1b59..ab8223c 100644
--- a/src/views/sourcetrace/component/ClueRecordItem.vue
+++ b/src/views/sourcetrace/component/ClueRecordItem.vue
@@ -108,7 +108,7 @@
           </el-tag>
           <el-text type="info">{{ item.pollutedData.exception }}</el-text>
         </div>
-        <div v-if="item.pollutedSource.sceneList.length > 0">
+        <!-- <div v-if="item.pollutedSource.sceneList.length > 0">
           <div v-for="s in item.pollutedSource.sceneList" :key="s.guid">
             <img style="width: 24px" :src="sceneIcon(s.typeId)" :alt="s.type" />
             <el-text
@@ -121,7 +121,7 @@
               {{ s.name }}
             </el-text>
           </div>
-        </div>
+        </div> -->
       </el-col>
     </el-row>
   </div>
diff --git a/src/views/sourcetrace/component/PollutedExceptionItem.vue b/src/views/sourcetrace/component/PollutedExceptionItem.vue
index d269ccd..2482d6c 100644
--- a/src/views/sourcetrace/component/PollutedExceptionItem.vue
+++ b/src/views/sourcetrace/component/PollutedExceptionItem.vue
@@ -38,7 +38,10 @@
         <div>
           <el-text type="info">
             <el-icon><MapLocation /></el-icon>
-            {{ '椋庨櫓鍖哄煙锛�' + item.pollutedArea.address }}
+            {{
+              '椋庨櫓鍖哄煙锛�' +
+              (item.pollutedArea.address ? item.pollutedArea.address : '')
+            }}
           </el-text>
         </div>
         <!-- <div>
@@ -52,7 +55,7 @@
             寮傚父绫诲瀷锛歿{ item.pollutedData.exception }}
           </el-text>
         </div>
-        <div v-for="s in item.pollutedData.statisticMap" :key="s">
+        <div v-for="s in item.pollutedData.statisticMap" :key="s.factorId">
           <el-row style="border-top: 1px solid white">
             <el-col :span="6">
               <el-statistic title="绐佸彉鍥犲瓙" :value="s.factorName" />
@@ -77,10 +80,19 @@
               <el-statistic
                 title="骞冲潎椋庨��"
                 :value="item.pollutedData.windSpeed"
+                :precision="1"
                 suffix="m/s"
               />
             </el-col>
           </el-row>
+          <RealTimeLineChart
+            v-for="(item1, index1) in s._chartOptions"
+            :key="index1"
+            :model-value="item1"
+            chart-height="80px"
+            :y-min-interval="20"
+            :exception-index-arr="exceptionIndexArr"
+          ></RealTimeLineChart>
         </div>
         <el-row justify="space-between">
           <!-- <el-link
@@ -97,13 +109,6 @@
                   </el-link> -->
         </el-row>
         <!-- <div style="width: 320px; height: 80px"> -->
-        <RealTimeLineChart
-          v-for="(item1, index1) in item._chartOptions"
-          :key="index1"
-          :model-value="item1"
-          chart-height="80px"
-          :y-min-interval="20"
-        ></RealTimeLineChart>
         <!-- </div> -->
         <div class="border-dashed">
           <el-icon color="#ffbc58" size="20"><WarningFilled /></el-icon>
@@ -122,13 +127,24 @@
   </CardDialog> -->
 </template>
 <script setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 
 const props = defineProps({
   modelValue: Boolean,
   item: Object
 });
 
+const exceptionIndexArr = computed(() => {
+  const indexArr = [];
+  props.item.pollutedData.dataVoList.forEach((e) => {
+    const i = props.item.pollutedData.historyDataList.findIndex(
+      (v) => v.time == e.time
+    );
+    indexArr.push([i - 1 < 0 ? 0 : i - 1, i]);
+  });
+  return indexArr;
+});
+
 const emits = defineEmits(['showMarksAndPolygon', 'update:modelValue']);
 
 function showMarksAndPolygon(item) {
diff --git a/src/views/sourcetrace/component/PollutedWarnItem.vue b/src/views/sourcetrace/component/PollutedWarnItem.vue
index d047320..90a8456 100644
--- a/src/views/sourcetrace/component/PollutedWarnItem.vue
+++ b/src/views/sourcetrace/component/PollutedWarnItem.vue
@@ -30,7 +30,10 @@
         <div>
           <el-text type="info">
             <el-icon><MapLocation /></el-icon>
-            {{ '鎵�鍦ㄥ尯鍩燂細' + item.pollutedArea.address }}
+            {{
+              '鎵�鍦ㄥ尯鍩燂細' +
+              (item.pollutedArea.address ? item.pollutedArea.address : '')
+            }}
           </el-text>
         </div>
         <!-- <div>
@@ -45,11 +48,23 @@
           </el-text>
         </div>
         <el-row style="border-top: 1px solid white"> </el-row>
-        <RealTimeLineChart
+        <div v-for="s in item.pollutedData.statisticMap" :key="s.factorId">
+          <el-row justify="space-between" class="wrap">
+            <div class="flex-col m-r-4">
+              <div class="factor-name">{{ s.factorName }}</div>
+            </div>
+            <RealTimeLineChart
+              v-for="(item1, index1) in s._chartOptions"
+              :key="index1"
+              :model-value="item1"
+            ></RealTimeLineChart>
+          </el-row>
+        </div>
+        <!-- <RealTimeLineChart
           v-for="(item1, index1) in item._chartOptions"
           :key="index1"
           :model-value="item1"
-        ></RealTimeLineChart>
+        ></RealTimeLineChart> -->
       </el-scrollbar>
     </template>
   </BaseCard>
@@ -117,7 +132,7 @@
   padding: 0 4px;
   /* margin-right: 2px; */
   width: 340px;
-  height: 240px;
+  height: 260px;
   /* border-right: 1px solid white; */
   border-radius: 2px;
 }
@@ -130,4 +145,22 @@
   padding: 0px 1px;
   margin-bottom: 4px;
 }
+
+.wrap {
+  border-bottom: 1px solid rgba(255, 255, 255, 0.329);
+}
+
+.flex-col {
+  display: flex;
+  flex-direction: column;
+}
+
+.factor-value {
+  font-weight: 600;
+  font-size: 20px;
+}
+
+.factor-name {
+  color: #23dad1;
+}
 </style>

--
Gitblit v1.9.3