From 16eb4bd55a4fd61ddd7a171b1a07378c45d1665b Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期四, 05 六月 2025 13:59:28 +0800
Subject: [PATCH] 动态溯源(待完成)

---
 src/views/sourcetrace/SourceTrace.vue    |   87 +++++++++++------
 src/utils/factor/data.js                 |   17 ++
 src/components/device/DeviceManage.vue   |   16 +--
 src/api/index.js                         |    8 
 src/components.d.ts                      |    2 
 src/utils/map/line.js                    |    3 
 src/views/realtimemode/RealtimeMode.vue  |   29 ++++-
 src/styles/elementUI.scss                |   13 ++
 src/components/scene/SceneTable.vue      |   22 ++--
 src/views/sourcetrace/UnderwayAdvice.vue |   83 ++++++++++++++-
 10 files changed, 205 insertions(+), 75 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index 73e7919..c83bec6 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -13,10 +13,10 @@
 }
 
 if (debug) {
-  // ip1 = 'http://192.168.0.110:8084/';
-  ip1 = 'http://localhost:8084/';
-  // ws = `192.168.0.110:9031`;
-  ws = `localhost:9031`;
+  ip1 = 'http://192.168.0.110:8084/';
+  // ip1 = 'http://localhost:8084/';
+  ws = `192.168.0.110:9031`;
+  // ws = `localhost:9031`;
 }
 
 const $http = axios.create({
diff --git a/src/components.d.ts b/src/components.d.ts
index 75ed0a7..aefd8b5 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -31,6 +31,7 @@
     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']
@@ -48,6 +49,7 @@
     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']
diff --git a/src/components/device/DeviceManage.vue b/src/components/device/DeviceManage.vue
index 889e1b8..f7872c8 100644
--- a/src/components/device/DeviceManage.vue
+++ b/src/components/device/DeviceManage.vue
@@ -7,11 +7,7 @@
   >
     璁惧绠$悊
   </el-button> -->
-  <CardDialog
-    :model-value="modelValue"
-    @changed="handleChange"
-    title="璧拌埅璁惧绠$悊"
-  >
+  <CardDialog v-bind="$attrs" title="璧拌埅璁惧绠$悊">
     <el-row class="device-table">
       <el-col :span="20">
         <el-table
@@ -88,9 +84,9 @@
     return { loading, fetchData };
   },
   props: {
-    modelValue: Boolean
+    // modelValue: Boolean
   },
-  emits: ['update:modelValue'],
+  // emits: ['update:modelValue'],
   data() {
     return {
       dialogVisible: false,
@@ -105,9 +101,9 @@
     }
   },
   methods: {
-    handleChange(value) {
-      this.$emit('update:modelValue', value);
-    },
+    // handleChange(value) {
+    //   this.$emit('update:modelValue', value);
+    // },
     deleteDevice(row) {
       this.onConfirm = () => {
         this.deviceStore.deleteDevice(row.deviceCode);
diff --git a/src/components/scene/SceneTable.vue b/src/components/scene/SceneTable.vue
index 3957132..2333209 100644
--- a/src/components/scene/SceneTable.vue
+++ b/src/components/scene/SceneTable.vue
@@ -49,7 +49,7 @@
   </el-table>
 </template>
 <script setup>
-import { ref, computed, watch } from 'vue';
+import { ref, computed, watch, onUnmounted } from 'vue';
 import { sceneTypes, sceneIcon } from '@/constant/scene-types';
 import MapUtil from '@/utils/map/util';
 import marks from '@/utils/map/marks';
@@ -86,14 +86,15 @@
   }
 });
 
+onUnmounted(() => {
+  removeLayer();
+});
+
 watch(showSceneList, (nV, oV) => {
   if (nV && props.showMarks) {
     drawMarks(nV);
   } else {
-    if (layer != undefined) {
-      MapUtil.removeViews(layer);
-      layer = undefined;
-    }
+    removeLayer();
   }
 });
 
@@ -103,20 +104,21 @@
     if (showSceneList.value && nV) {
       drawMarks(showSceneList.value);
     } else {
-      if (layer != undefined) {
-        MapUtil.removeViews(layer);
-        layer = undefined;
-      }
+      removeLayer();
     }
   },
   { immediate: true }
 );
 
-function drawMarks(sceneList) {
+function removeLayer() {
   if (layer != undefined) {
     MapUtil.removeViews(layer);
     layer = undefined;
   }
+}
+
+function drawMarks(sceneList) {
+  removeLayer();
   if (sceneList.length != 0) {
     const icons = [];
     sceneList.forEach((s) => {
diff --git a/src/styles/elementUI.scss b/src/styles/elementUI.scss
index 96cfa18..ff482e2 100644
--- a/src/styles/elementUI.scss
+++ b/src/styles/elementUI.scss
@@ -32,3 +32,16 @@
 .el-button-custom:focus-visible {
   outline: 0px solid var(--el-button-outline-color);
 }
+
+// .el-statistic {
+//   --el-statistic-title-color: rgb(215, 215, 215);
+//   --el-statistic-content-color: white;
+// }
+
+// .el-text {
+//   --el-text-color: white;
+// }
+
+// .el-link {
+//   --el-link-text-color: #23dad1;
+// }
diff --git a/src/utils/factor/data.js b/src/utils/factor/data.js
index 4f47e41..ab85787 100644
--- a/src/utils/factor/data.js
+++ b/src/utils/factor/data.js
@@ -182,6 +182,7 @@
 
 var fetchingTask;
 var isFetching;
+var pause = false;
 /**
  * 寮�鍚疄鏃舵暟鎹惊鐜幏鍙�
  * @param {Object} params
@@ -200,12 +201,12 @@
     interval = 10 * 1000;
   }
   if (_interval) {
-    interval = _interval
+    interval = _interval;
   }
   // 寮�濮嬪惊鐜换鍔�
   clearFetchingTask();
   fetchingTask = setInterval(() => {
-    if (isFetching) {
+    if (isFetching || pause) {
       return;
     }
     isFetching = true;
@@ -237,6 +238,11 @@
   }, interval);
 }
 
+function pauseTask() {
+  pause = !pause;
+  return pause;
+}
+
 /**
  * 娓呯悊鍘嗗彶瀹炴椂鏁版嵁鑾峰彇浠诲姟
  */
@@ -248,4 +254,9 @@
   }
 }
 
-export { fetchHistoryData, startLoopFetchRealTimeData, clearFetchingTask };
+export {
+  fetchHistoryData,
+  startLoopFetchRealTimeData,
+  clearFetchingTask,
+  pauseTask
+};
diff --git a/src/utils/map/line.js b/src/utils/map/line.js
index bb32b78..195e797 100644
--- a/src/utils/map/line.js
+++ b/src/utils/map/line.js
@@ -22,7 +22,8 @@
 
 function drawDirection(path) {
   const polyline = newPolyline(path, '#02ffea');
-  map.add(polyline)
+  map.add(polyline);
+  return polyline;
 }
 
 export default {
diff --git a/src/views/realtimemode/RealtimeMode.vue b/src/views/realtimemode/RealtimeMode.vue
index 7ebb66b..5691c14 100644
--- a/src/views/realtimemode/RealtimeMode.vue
+++ b/src/views/realtimemode/RealtimeMode.vue
@@ -5,9 +5,9 @@
       <el-button
         type="primary"
         class="p-events-auto el-button-custom"
-        @click="clearFetchingTask"
+        @click="pauseTask"
       >
-        鍋滄
+        {{ pause ? '缁х画' : '鏆傚仠' }}
       </el-button>
     </el-row>
     <el-row class="m-t-2">
@@ -49,7 +49,8 @@
 import {
   fetchHistoryData,
   startLoopFetchRealTimeData,
-  clearFetchingTask
+  clearFetchingTask,
+  pauseTask
 } from '@/utils/factor/data';
 import thirdPartyDataApi from '@/api/thirdPartyDataApi';
 import websocket from '@/api/websocket';
@@ -61,7 +62,13 @@
 // const mode = 'product';
 
 export default {
-  components: { DashBoard, RealTimeTrend, DeviceChange, SourceTrace, UnderwayAdvice },
+  components: {
+    DashBoard,
+    RealTimeTrend,
+    DeviceChange,
+    SourceTrace,
+    UnderwayAdvice
+  },
   setup() {
     const { loading, fetchData } = useFetchData(10000);
     return { loading, fetchData };
@@ -76,7 +83,8 @@
       // 鏂拌幏鍙栫殑鐩戞祴鏁版嵁
       factorDatas: new FactorDatas(),
       // 鍏ㄩ儴鐩戞祴鏁版嵁
-      allFactorDatas: new FactorDatas()
+      allFactorDatas: new FactorDatas(),
+      pause: false
     };
   },
   watch: {
@@ -127,10 +135,12 @@
           mode == 'debug'
             ? {
                 deviceCode: this.deviceCode,
-                startTime: '2025-01-16 11:34:00',
-                endTime: '2025-01-16 11:35:00',
+                // startTime: '2025-01-16 11:34:00',
+                // endTime: '2025-01-16 11:35:00',
+                startTime: '2024-11-27 11:50:41',
+                endTime: '2025-01-16 11:51:41',
                 page,
-                perPage: 100
+                perPage: 10
               }
             : {
                 deviceCode: this.deviceCode,
@@ -145,6 +155,9 @@
         });
       });
     },
+    pauseTask() {
+      this.pause = pauseTask();
+    },
     clearFetchingTask() {
       clearFetchingTask();
     },
diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue
index 0ce544e..725f2d6 100644
--- a/src/views/sourcetrace/SourceTrace.vue
+++ b/src/views/sourcetrace/SourceTrace.vue
@@ -18,14 +18,17 @@
           <!-- <div>
             <el-text type="primary" size="large" tag="b">鍔ㄦ�佹函婧�</el-text>
           </div> -->
-          <el-scrollbar ref="scrollbarRef" :height="height" class="scrollbar">
-            <el-row ref="scrollContentRef">
-              <div
+          <el-scrollbar ref="scrollbarRef" class="scrollbar">
+            <div
+              ref="scrollContentRef"
+              style="display: flex; width: fit-content"
+            >
+              <el-scrollbar
                 v-for="(item, index) in streams"
                 :key="index"
                 class="clue-card"
               >
-                <el-row>
+                <el-row justify="space-between">
                   <!-- <el-tag v-if="index == 0" type="danger">鏈�鏂�</el-tag> -->
                   <el-text type="primary">{{
                     '绾跨储鏃堕棿锛�' +
@@ -33,6 +36,13 @@
                     ' - ' +
                     item.pollutedData.endTime
                   }}</el-text>
+                  <el-link
+                    type="primary"
+                    :underline="true"
+                    @click="showMarksAndPolygon(item)"
+                  >
+                    {{ item.showMore ? '鏀惰捣寮傚父' : '瀹氫綅寮傚父' }}
+                  </el-link>
                 </el-row>
                 <div>
                   <el-text type="primary">
@@ -46,7 +56,7 @@
                     }}
                   </el-text>
                 </div>
-                <el-row>
+                <el-row style="border-top: 1px solid white">
                   <el-col :span="6">
                     <el-statistic
                       title="绐佸彉鍥犲瓙"
@@ -86,13 +96,6 @@
                       '锛�'
                     }}
                   </el-link> -->
-                  <el-link
-                    type="primary"
-                    underline
-                    @click="showMarksAndPolygon(item)"
-                  >
-                    {{ item.showMore ? '鏀惰捣寮傚父' : '瀹氫綅寮傚父' }}
-                  </el-link>
                 </el-row>
                 <div style="width: 320px; height: 140px">
                   <RealTimeLineChart
@@ -100,6 +103,11 @@
                     :key="index1"
                     :model-value="item1"
                   ></RealTimeLineChart>
+                </div>
+                <div class="border-dashed">
+                  <el-text type="" tag="mark">
+                    {{ item.pollutedSource.conclusion }}
+                  </el-text>
                 </div>
                 <SceneTable
                   :show-marks="item.showMore"
@@ -156,8 +164,8 @@
                   :scene-list="item.relatedSceneList"
                 ></SceneTable>
                 <el-divider /> -->
-              </div>
-            </el-row>
+              </el-scrollbar>
+            </div>
           </el-scrollbar>
         </template>
       </BaseCard>
@@ -178,9 +186,7 @@
 import { map, onMapMounted } from '@/utils/map/index_old';
 import { FactorDatas } from '@/model/FactorDatas';
 import factorDataParser from '@/utils/chart/factor-data-parser';
-import websocketMsgParser from "@/views/sourcetrace/websocketMsgParser.js";
-
-
+import websocketMsgParser from '@/views/sourcetrace/websocketMsgParser.js';
 
 const props = defineProps({
   factorType: String
@@ -188,7 +194,7 @@
 
 const emits = defineEmits(['update:factorType']);
 
-const height = `48vh`;
+const height = `30vh`;
 const width = `60vh`;
 
 const show = ref(false);
@@ -217,7 +223,7 @@
 
 let showFirstClueTask;
 function dealMsg(data) {
-  const {type, content} = websocketMsgParser.parseMsg(data)
+  const { type, content } = websocketMsgParser.parseMsg(data);
 
   // 姹℃煋绾跨储 PollutedClue
   if (type == '1') {
@@ -229,12 +235,13 @@
     if (streams.length == 0) {
       streams.push(obj);
     } else {
-      streams.forEach((s) => {
-        s.showMore = false;
-      });
+      // streams.forEach((s) => {
+      //   showMarksAndPolygon(s);
+      // });
+      hideAll();
       streams.unshift(obj);
-      show.value = true;
     }
+    show.value = true;
 
     // scrollToBottom();
     scrollToTop();
@@ -263,9 +270,20 @@
   // }
 });
 
+function hideAll() {
+  streams.forEach((s) => {
+    if (polygonMap.has(s.guid)) {
+      s.showMore = false;
+      map.remove(polygonMap.get(s.guid));
+    }
+  });
+}
+
 function showMarksAndPolygon(item) {
   item.showMore = !item.showMore;
   if (item.showMore) {
+    hideAll();
+    item.showMore = true;
     drawPolygon(item);
   } else {
     if (polygonMap.has(item.guid)) {
@@ -296,11 +314,11 @@
     });
     polygonMap.set(item.guid, pollutedAreaPolygon);
   }
-  map.setFitView(polygonMap.get(item.guid));
+  // map.setFitView(polygonMap.get(item.guid));
 }
 
 function parseChartData(obj) {
-  console.log('鎶樼嚎鍥撅細start');
+  // console.log('鎶樼嚎鍥撅細start');
   const factorDatas = new FactorDatas();
   factorDatas.setData(obj.pollutedData.dataVoList, 0, () => {
     obj._chartOptions = factorDataParser.parseData(factorDatas, [
@@ -310,7 +328,7 @@
         value: obj.pollutedData.factorId + ''
       }
     ]);
-    console.log('鎶樼嚎鍥撅細', obj._chartOptions);
+    // console.log('鎶樼嚎鍥撅細', obj._chartOptions);
   });
 }
 
@@ -435,7 +453,7 @@
   --el-statistic-content-color: white;
 }
 
-:deep(.el-text) {
+:deep(.el-text.el-text--primary) {
   --el-text-color: white;
 }
 
@@ -446,14 +464,23 @@
 .scrollbar {
   min-width: 300px;
   max-width: 60vw;
+  /* height: 35vh; */
   /* color: #02ffea; */
 }
 
 .clue-card {
   padding: 0 4px;
-  margin-right: 2px;
-  width: 320px;
-  border: 1px solid white;
+  /* margin-right: 2px; */
+  width: 340px;
+  height: 35vh;
+  border-right: 1px solid white;
   border-radius: 2px;
 }
+
+.border-dashed {
+  /* border: 1px dashed white; */
+  border: 1px dashed #ffbc58;
+  padding: 0px 1px;
+  margin-bottom: 4px;
+}
 </style>
diff --git a/src/views/sourcetrace/UnderwayAdvice.vue b/src/views/sourcetrace/UnderwayAdvice.vue
index 8c2b68d..183ba28 100644
--- a/src/views/sourcetrace/UnderwayAdvice.vue
+++ b/src/views/sourcetrace/UnderwayAdvice.vue
@@ -1,29 +1,57 @@
 <template>
   <CardDialog
     v-model="dialogVisible"
-    title="璧拌埅璺嚎鎺ㄨ崘"
+    title="璧拌埅鏅鸿兘鍒嗘瀽"
     draggable
     :modal="false"
     width="400px"
   >
-    <template #default> </template>
+    <template #default>
+      <template v-if="latestResult">
+        <el-row>
+          <el-text size="small">{{ latestResult._timestr }}</el-text>
+        </el-row>
+        <el-space>
+          <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon>
+          <el-text>
+            {{ latestResult.advice }}
+          </el-text>
+        </el-space>
+        <el-row justify="end">
+          <el-link type="primary" :underline="true" @click="showPolyline">
+            {{ lineShow ? '鏀惰捣璺嚎' : '瀹氫綅璺嚎' }}
+          </el-link>
+          <el-text size="small">
+            鎺ㄨ崘璺嚎鎬婚暱{{ latestResult.direction.distance }}绫�
+          </el-text>
+        </el-row>
+      </template>
+    </template>
     <template #footer> </template>
   </CardDialog>
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
+import moment from 'moment';
+import { ref, onMounted, onUnmounted, reactive } from 'vue';
 import websocket from '@/api/websocket';
 import websocketMsgParser from '@/views/sourcetrace/websocketMsgParser.js';
 import mapLine from '@/utils/map/line';
+import mapUtil from '@/utils/map/util';
 
-const dialogVisible = ref(true);
+const dialogVisible = ref(false);
+const lineShow = ref(true);
+const latestResult = ref();
+let latestPolyline = undefined;
+const analysisResultList = reactive([]);
+const polylineList = [];
 
 onMounted(() => {
   websocket.registerReceiveEvent(dealMsg);
 });
 onUnmounted(() => {
   websocket.removeReceiveEvent(dealMsg);
+  showPolyline(false);
 });
 
 function dealMsg(data) {
@@ -32,13 +60,50 @@
   if (type == '2') {
     const obj = JSON.parse(content);
     console.log('姹℃煋鍒嗘瀽缁撴灉: ', obj);
+    obj._timestr = timeFormatter(obj.time);
+    analysisResultList.unshift(obj);
+    latestResult.value = obj;
 
-    obj.sortedSceneList;
-    obj.time;
-    obj.advice;
-    obj.direction;
+    // obj.sortedSceneList;
+    // obj.time;
+    // obj.advice;
+    // obj.direction;
 
-    mapLine.drawDirection(obj.direction.paths.map((v) => [v.first, v.second]));
+    const polyline = mapLine.drawDirection(
+      obj.direction.paths.map((v) => [v.first, v.second])
+    );
+    polylineList.unshift(polyline);
+    if (latestPolyline) {
+      mapUtil.removeViews(latestPolyline);
+    }
+    latestPolyline = polyline;
+    dialogVisible.value = true;
   }
 }
+
+function showPolyline(show) {
+  if (typeof show === 'boolean') {
+    lineShow.value = show;
+  } else {
+    lineShow.value = !lineShow.value;
+  }
+  if (lineShow.value) {
+    mapUtil.addViews(latestPolyline);
+  } else {
+    mapUtil.removeViews(latestPolyline);
+  }
+}
+
+function timeFormatter(time) {
+  return moment(time).format('YYYY-MM-DD HH:mm:ss');
+}
 </script>
+<style scoped>
+:deep(.el-text) {
+  --el-text-color: white;
+}
+
+:deep(.el-link) {
+  --el-link-text-color: #23dad1;
+}
+</style>

--
Gitblit v1.9.3