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

---
 src/utils/chart/chart-option.js                           |    2 
 src/views/sourcetrace/SourceTrace.vue                     |  188 ++++++-
 src/api/index.js                                          |    8 
 src/views/sourcetrace/component/PollutedExceptionItem.vue |  237 ++++++++++
 src/views/sourcetrace/component/PollutedClueItem.vue      |  309 +++++---------
 src/views/sourcetrace/component/ClueRecordItem.vue        |   96 ++++
 src/views/realtimemode/RealtimeMode.vue                   |    8 
 src/views/sourcetrace/SourceTrace copy.vue                |  345 +++++++++++++++
 src/components/scene/SceneTable.vue                       |    1 
 src/views/sourcetrace/UnderwayAdvice.vue                  |   98 ++-
 10 files changed, 1,007 insertions(+), 285 deletions(-)

diff --git a/src/api/index.js b/src/api/index.js
index 1792861..efcd5a1 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -13,10 +13,10 @@
 }
 
 if (debug) {
-  // ip1 = 'http://192.168.0.103:8084/';
-  ip1 = 'http://localhost:8084/';
-  // ws = `192.168.0.103:9031`;
-  ws = `localhost:9031`;
+  ip1 = 'http://192.168.0.103:8084/';
+  // ip1 = 'http://localhost:8084/';
+  ws = `192.168.0.103:9031`;
+  // ws = `localhost:9031`;
 }
 
 const $http = axios.create({
diff --git a/src/components/scene/SceneTable.vue b/src/components/scene/SceneTable.vue
index 2333209..f88bf65 100644
--- a/src/components/scene/SceneTable.vue
+++ b/src/components/scene/SceneTable.vue
@@ -29,6 +29,7 @@
         <div>{{ parseInt(row.length) + '绫�' }}</div>
       </template>
     </el-table-column>
+    <slot></slot>
     <!-- <el-table-column
       prop="districtName"
       label="鍖哄幙"
diff --git a/src/utils/chart/chart-option.js b/src/utils/chart/chart-option.js
index 5d9269a..346c602 100644
--- a/src/utils/chart/chart-option.js
+++ b/src/utils/chart/chart-option.js
@@ -137,7 +137,7 @@
       left: '12%',
       right: '2%',
       top: '7%',
-      bottom: '20%'
+      bottom: '30%'
     },
     legend: {
       show: false
diff --git a/src/views/realtimemode/RealtimeMode.vue b/src/views/realtimemode/RealtimeMode.vue
index c72f699..6935614 100644
--- a/src/views/realtimemode/RealtimeMode.vue
+++ b/src/views/realtimemode/RealtimeMode.vue
@@ -29,7 +29,7 @@
       class="source-trace"
       v-model:factorType="factorType"
     ></SourceTrace>
-    <UnderwayAdvice></UnderwayAdvice>
+    <!-- <PollutedClueItem></PollutedClueItem> -->
   </div>
 </template>
 
@@ -45,6 +45,7 @@
 import DeviceChange from './component/DeviceChange.vue';
 import SourceTrace from '@/views/sourcetrace/SourceTrace.vue';
 import UnderwayAdvice from '@/views/sourcetrace/UnderwayAdvice.vue';
+import PollutedClueItem from '@/views/sourcetrace/component/PollutedClueItem.vue';
 import { realTimeMapAnimation } from '@/utils/map/animation';
 import {
   fetchHistoryData,
@@ -67,7 +68,8 @@
     RealTimeTrend,
     DeviceChange,
     SourceTrace,
-    UnderwayAdvice
+    UnderwayAdvice,
+    PollutedClueItem
   },
   setup() {
     const { loading, fetchData } = useFetchData(10000);
@@ -142,7 +144,7 @@
                 // startTime: '2024-12-13 16:35:00',
                 // startTime: '2024-11-27 11:50:41', // Pm, 涓窛绂诲伐鍦�
                 // startTime: '2024-08-30 15:28:00', // voc 杩戣窛绂绘苯淇�
-                startTime: '2024-07-23 15:22:00',
+                startTime: '2024-07-23 15:21:30',
                 // startTime: '2024-07-23 14:39:00',
                 endTime: '2025-01-16 11:51:41',
                 page,
diff --git a/src/views/sourcetrace/SourceTrace copy.vue b/src/views/sourcetrace/SourceTrace copy.vue
new file mode 100644
index 0000000..b0d6844
--- /dev/null
+++ b/src/views/sourcetrace/SourceTrace copy.vue
@@ -0,0 +1,345 @@
+<template>
+  <el-row>
+    <el-col span="2">
+      <CardButton
+        direction="left"
+        name="鍔ㄦ�佹函婧�"
+        @click="() => (show = !show)"
+      ></CardButton>
+    </el-col>
+    <el-col v-show="show" span="10">
+      <BaseCard>
+        <template #content>
+          <el-scrollbar ref="scrollbarRef" class="scrollbar">
+            <div
+              ref="scrollContentRef"
+              style="display: flex; width: fit-content"
+            >
+              <TransitionGroup name="list">
+                <div v-for="(item, index) in streams" :key="index">
+                  <PollutedExceptionItem
+                    :item="item"
+                    @showMarksAndPolygon="showMarksAndPolygon"
+                  ></PollutedExceptionItem>
+                </div>
+              </TransitionGroup>
+            </div>
+          </el-scrollbar>
+        </template>
+      </BaseCard>
+    </el-col>
+  </el-row>
+</template>
+<script setup>
+/**
+ * 鍔ㄦ�佹函婧愪俊鎭鐞�
+ * 閫氳繃websocket鏂瑰紡鎺ユ敹鍚庣鎺ㄩ�佺殑寮傚父淇℃伅骞跺睍绀�
+ */
+import { reactive, ref, onMounted, onUnmounted, unref } from 'vue';
+import websocket from '@/api/websocket';
+import sector from '@/utils/map/sector';
+import mapUtil from '@/utils/map/util';
+import { sceneTypes, sceneIcon } from '@/constant/scene-types';
+import marks from '@/utils/map/marks';
+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 PollutedExceptionItem from './component/PollutedExceptionItem.vue';
+
+const props = defineProps({
+  factorType: String
+});
+
+const emits = defineEmits(['update:factorType']);
+
+const height = `30vh`;
+const width = `60vh`;
+
+const show = ref(false);
+const scrollContentRef = ref();
+const scrollbarRef = ref();
+
+function scrollToBottom() {
+  const h1 = scrollContentRef.value.clientHeight + 100;
+  setTimeout(() => {
+    scrollbarRef.value.setScrollTop(h1);
+  }, 100);
+}
+
+function scrollToTop() {
+  setTimeout(() => {
+    scrollbarRef.value.setScrollTop(0);
+  }, 100);
+}
+
+const streams = reactive([]);
+
+const inputVal = ref('');
+const handleSend = () => {
+  websocket.send(inputVal.value);
+};
+
+let showFirstClueTask;
+function dealMsg(data) {
+  const { type, content } = websocketMsgParser.parseMsg(data);
+
+  // 姹℃煋绾跨储 PollutedClue
+  if (type == '1') {
+    const obj = reactive(JSON.parse(content));
+    // obj.showMore = true;
+    obj.showMore = false;
+    console.log('姹℃煋绾跨储: ', obj);
+
+    if (streams.length == 0) {
+      streams.push(obj);
+    } else {
+      // streams.forEach((s) => {
+      //   showMarksAndPolygon(s);
+      // });
+      hideAll();
+      streams.unshift(obj);
+    }
+    show.value = true;
+
+    // scrollToBottom();
+    scrollToTop();
+    // drawPolygon(obj.pollutedArea);
+    parseChartData(obj);
+
+    if (showFirstClueTask) {
+      clearTimeout(showFirstClueTask);
+    }
+    showFirstClueTask = setTimeout(() => {
+      showMarksAndPolygon(obj);
+    }, 1000);
+  }
+}
+
+onMounted(() => {
+  websocket.registerReceiveEvent(dealMsg);
+});
+onUnmounted(() => {
+  websocket.removeReceiveEvent(dealMsg);
+  sector.clearSectorPt();
+  mapUtil.clearMap();
+  // if (layer != undefined) {
+  //   mapUtil.removeViews(layer);
+  //   layer = undefined;
+  // }
+});
+
+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)) {
+      map.remove(polygonMap.get(item.guid));
+    }
+  }
+}
+
+// 缁樺埗姹℃煋鍖哄煙锛堥珮寰峰湴鍥剧殑澶氳竟褰㈠璞★紝閫氳繃vue鐨剅eactive鍖呰涓哄搷搴斿紡瀵硅薄鍚庯紝鏃犳硶姝g‘鍦ㄥ湴鍥句腑浣跨敤锛�
+const polygonMap = new Map();
+function drawPolygon(item) {
+  const pollutedArea = item.pollutedArea;
+  if (polygonMap.has(item.guid)) {
+    map.add(polygonMap.get(item.guid));
+  } else {
+    const bounds = pollutedArea.polygon.map((v) => [v.first, v.second]);
+    // eslint-disable-next-line no-undef
+    const pollutedAreaPolygon = new AMap.Polygon({
+      map: map, //鏄剧ず璇ヨ鐩栫墿鐨勫湴鍥惧璞�
+      strokeWeight: 2, //杞粨绾垮搴�
+      path: bounds, //澶氳竟褰㈣疆寤撶嚎鐨勮妭鐐瑰潗鏍囨暟缁�
+      fillOpacity: 0, //澶氳竟褰㈠~鍏呴�忔槑搴�
+      fillColor: '#CCF3FF', //澶氳竟褰㈠~鍏呴鑹�
+      strokeColor: '#02ffea', //绾挎潯棰滆壊
+      // strokeColor: '#0552f7', //绾挎潯棰滆壊
+      strokeStyle: 'dashed',
+      zIndex: 9
+    });
+    polygonMap.set(item.guid, pollutedAreaPolygon);
+  }
+  // map.setFitView(polygonMap.get(item.guid));
+}
+
+function parseChartData(obj) {
+  // console.log('鎶樼嚎鍥撅細start');
+  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 + ''
+      }
+    ]);
+    // console.log('鎶樼嚎鍥撅細', obj._chartOptions);
+  });
+}
+
+/******************************************************************************************************************** */
+
+/**
+ * 娣诲姞涓�鏉″伐浣滄祦淇℃伅
+ * @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>
+:deep(.el-statistic) {
+  --el-statistic-title-color: rgb(215, 215, 215);
+  --el-statistic-content-color: white;
+}
+
+:deep(.el-text.el-text--primary) {
+  --el-text-color: white;
+}
+
+:deep(.el-link) {
+  --el-link-text-color: #23dad1;
+}
+
+.scrollbar {
+  min-width: 300px;
+  max-width: 60vw;
+  /* height: 35vh; */
+  /* color: #02ffea; */
+}
+
+.clue-card {
+  padding: 0 4px;
+  /* 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>
+<style>
+.list-move, /* 瀵圭Щ鍔ㄤ腑鐨勫厓绱犲簲鐢ㄧ殑杩囨浮 */
+.list-enter-active,
+.list-leave-active {
+  transition: all 0.5s ease;
+}
+
+.list-enter-from,
+.list-leave-to {
+  opacity: 0;
+  transform: translateX(-30px);
+}
+
+/* 纭繚灏嗙寮�鐨勫厓绱犱粠甯冨眬娴佷腑鍒犻櫎
+  浠ヤ究鑳藉姝g‘鍦拌绠楃Щ鍔ㄧ殑鍔ㄧ敾銆� */
+.list-leave-active {
+  position: absolute;
+}
+</style>
diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue
index 0a122c8..d4c97a6 100644
--- a/src/views/sourcetrace/SourceTrace.vue
+++ b/src/views/sourcetrace/SourceTrace.vue
@@ -1,33 +1,54 @@
 <template>
   <el-row>
-    <el-col span="2">
-      <CardButton
-        direction="left"
-        name="鍔ㄦ�佹函婧�"
-        @click="() => (show = !show)"
-      ></CardButton>
+    <el-col span="2" class="flex-col">
+      <el-row justify="end">
+        <CardButton
+          direction="left"
+          name="鍔ㄦ�佹函婧�"
+          @click="() => (show = !show)"
+        ></CardButton>
+      </el-row>
+      <el-row class="flex-col">
+        <PollutedExceptionItem
+          :item="selectedException"
+          @showMarksAndPolygon="showMarksAndPolygon"
+        ></PollutedExceptionItem>
+      </el-row>
     </el-col>
     <el-col v-show="show" span="10">
       <BaseCard>
         <template #content>
+          <el-checkbox-group v-model="selectedMsgTypes" size="default" :min="1">
+            <el-space>
+              <el-checkbox value="1">寮傚父鍒囩墖</el-checkbox>
+              <el-checkbox value="2">姹℃煋绾跨储</el-checkbox>
+            </el-space>
+          </el-checkbox-group>
           <el-scrollbar ref="scrollbarRef" class="scrollbar">
-            <div
+            <!-- <div
               ref="scrollContentRef"
               style="display: flex; width: fit-content"
-            >
+            > -->
             <TransitionGroup name="list">
-              <div v-for="(item, index) in streams" :key="index">
-                <PollutedClueItem
+              <div
+                v-for="item in filterStreams"
+                :key="item.guid ? item.guid : item.time"
+              >
+                <ClueRecordItem
                   :item="item"
-                  @showMarksAndPolygon="showMarksAndPolygon"
-                ></PollutedClueItem>
+                  @open="handleOpen(item)"
+                ></ClueRecordItem>
               </div>
             </TransitionGroup>
-            </div>
+            <!-- </div> -->
           </el-scrollbar>
         </template>
       </BaseCard>
     </el-col>
+    <PollutedClueItem
+      v-model="clueDialog"
+      :item="selectedClue"
+    ></PollutedClueItem>
   </el-row>
 </template>
 <script setup>
@@ -35,7 +56,9 @@
  * 鍔ㄦ�佹函婧愪俊鎭鐞�
  * 閫氳繃websocket鏂瑰紡鎺ユ敹鍚庣鎺ㄩ�佺殑寮傚父淇℃伅骞跺睍绀�
  */
-import { reactive, ref, onMounted, onUnmounted, unref } from 'vue';
+import { reactive, ref, onMounted, onUnmounted, computed, watch } from 'vue';
+import moment from 'moment';
+
 import websocket from '@/api/websocket';
 import sector from '@/utils/map/sector';
 import mapUtil from '@/utils/map/util';
@@ -46,7 +69,9 @@
 import factorDataParser from '@/utils/chart/factor-data-parser';
 import websocketMsgParser from '@/views/sourcetrace/websocketMsgParser.js';
 
-import PollutedClueItem from './component/PollutedClueItem.vue';
+import PollutedExceptionItem from './component/PollutedExceptionItem.vue';
+import ClueRecordItem from './component/ClueRecordItem.vue';
+import PollutedClueItem from '@/views/sourcetrace/component/PollutedClueItem.vue';
 
 const props = defineProps({
   factorType: String
@@ -58,8 +83,13 @@
 const width = `60vh`;
 
 const show = ref(false);
+const clueDialog = ref(false);
 const scrollContentRef = ref();
 const scrollbarRef = ref();
+
+const selectedException = ref();
+const selectedClue = ref();
+const selectedMsgTypes = ref(['1', '2']);
 
 function scrollToBottom() {
   const h1 = scrollContentRef.value.clientHeight + 100;
@@ -75,6 +105,11 @@
 }
 
 const streams = reactive([]);
+const filterStreams = computed(() => {
+  return streams.filter((v) => {
+    return selectedMsgTypes.value.indexOf(v._type) != -1;
+  });
+});
 
 const inputVal = ref('');
 const handleSend = () => {
@@ -88,32 +123,60 @@
   // 姹℃煋绾跨储 PollutedClue
   if (type == '1') {
     const obj = reactive(JSON.parse(content));
+    obj._type = type;
     // obj.showMore = true;
     obj.showMore = false;
-    console.log('姹℃煋绾跨储: ', obj);
+    console.log('姹℃煋寮傚父鍒囩墖: ', obj);
 
-    if (streams.length == 0) {
-      streams.push(obj);
-    } else {
-      // streams.forEach((s) => {
-      //   showMarksAndPolygon(s);
-      // });
-      hideAll();
-      streams.unshift(obj);
-    }
+    // if (streams.length == 0) {
+    //   streams.push(obj);
+    // } else {
+    //   // streams.forEach((s) => {
+    //   //   showMarksAndPolygon(s);
+    //   // });
+    //   // hideAll();
+    //   streams.unshift(obj);
+    // }
+    addNewMsg(obj);
     show.value = true;
 
     // scrollToBottom();
-    scrollToTop();
+    // scrollToTop();
     // drawPolygon(obj.pollutedArea);
     parseChartData(obj);
 
-    if (showFirstClueTask) {
-      clearTimeout(showFirstClueTask);
-    }
-    showFirstClueTask = setTimeout(() => {
-      showMarksAndPolygon(obj);
+    // if (showFirstClueTask) {
+    //   clearTimeout(showFirstClueTask);
+    // }
+    // showFirstClueTask = setTimeout(() => {
+    //   showMarksAndPolygon(obj);
+    // }, 1000);
+  } else if (type == '2') {
+    const obj = JSON.parse(content);
+    obj._type = type;
+    console.log('姹℃煋绾跨储缁撴灉: ', obj);
+    obj._timestr = timeFormatter(obj.time);
+    // streams.unshift(obj);
+    addNewMsg(obj);
+  }
+}
+
+// 鎸夌収涓�瀹氭椂闂撮棿闅旀柊澧炰竴鏉℃秷鎭�
+let addNewMsgTask;
+const leftMsgList = [];
+function addNewMsg(msg, inside) {
+  if (!addNewMsgTask && (leftMsgList.length == 0 || inside)) {
+    streams.splice(0, 0, msg);
+    addNewMsgTask = setTimeout(() => {
+      clearTimeout(addNewMsgTask);
+      addNewMsgTask = undefined;
+      if (leftMsgList.length > 0) {
+        const next = leftMsgList.shift();
+        addNewMsg(next, true);
+      }
     }, 1000);
+  } else {
+    leftMsgList.push(msg);
   }
 }
 
@@ -129,6 +192,32 @@
   //   layer = undefined;
   // }
 });
+
+watch(clueDialog, (nV, oV) => {
+  if (nV != oV && !nV) {
+    selectedClue.value._selected = false;
+  }
+});
+
+function handleOpen(item) {
+  switch (item._type) {
+    case '1':
+      if (selectedException.value) {
+        selectedException.value._selected = false;
+      }
+      showMarksAndPolygon(item);
+      selectedException.value = item;
+      break;
+    case '2':
+      if (selectedClue.value) {
+        selectedClue.value._selected = false;
+      }
+      selectedClue.value = item;
+      clueDialog.value = true;
+      break;
+  }
+  item._selected = true;
+}
 
 function hideAll() {
   streams.forEach((s) => {
@@ -148,6 +237,7 @@
   } else {
     if (polygonMap.has(item.guid)) {
       map.remove(polygonMap.get(item.guid));
+      selectedException.value._selected = false;
     }
   }
 }
@@ -190,6 +280,10 @@
     ]);
     // console.log('鎶樼嚎鍥撅細', obj._chartOptions);
   });
+}
+
+function timeFormatter(time) {
+  return moment(time).format('YYYY-MM-DD HH:mm:ss');
 }
 
 /******************************************************************************************************************** */
@@ -301,11 +395,27 @@
   --el-link-text-color: #23dad1;
 }
 
+.el-checkbox {
+  --el-checkbox-text-color: white;
+  --main-color: #23dad1;
+  --el-checkbox-checked-text-color: var(--main-color);
+  --el-checkbox-checked-input-border-color: var(--main-color);
+  --el-checkbox-checked-bg-color: var(--main-color);
+  --el-checkbox-input-border-color-hover: var(--main-color);
+
+  --el-checkbox-disabled-checked-input-fill: var(--main-color);
+  --el-checkbox-disabled-checked-input-border-color: var(--main-color);
+  --el-checkbox-disabled-checked-icon-color: white;
+  margin-right: 6px;
+  /* height: initial; */
+}
+
 .scrollbar {
-  min-width: 300px;
-  max-width: 60vw;
-  /* height: 35vh; */
+  width: 400px;
+  /* max-width: 60vw; */
+  height: 45vh;
   /* color: #02ffea; */
+  padding-right: 10px;
 }
 
 .clue-card {
@@ -323,18 +433,24 @@
   padding: 0px 1px;
   margin-bottom: 4px;
 }
+.flex-col {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
 </style>
 <style>
 .list-move, /* 瀵圭Щ鍔ㄤ腑鐨勫厓绱犲簲鐢ㄧ殑杩囨浮 */
 .list-enter-active,
 .list-leave-active {
+  /* transition: all 0.8s cubic-bezier(0.55, 0, 0.1, 1); */
   transition: all 0.5s ease;
 }
 
 .list-enter-from,
 .list-leave-to {
   opacity: 0;
-  transform: translateX(30px);
+  transform: scaleY(0.01) translate(300px, 0);
 }
 
 /* 纭繚灏嗙寮�鐨勫厓绱犱粠甯冨眬娴佷腑鍒犻櫎
@@ -342,4 +458,4 @@
 .list-leave-active {
   position: absolute;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/sourcetrace/UnderwayAdvice.vue b/src/views/sourcetrace/UnderwayAdvice.vue
index b8eaf03..9615793 100644
--- a/src/views/sourcetrace/UnderwayAdvice.vue
+++ b/src/views/sourcetrace/UnderwayAdvice.vue
@@ -26,42 +26,45 @@
           </el-text>
         </el-row>
       </template> -->
-      <TransitionGroup name="list">
-        <div v-for="(item, index) in analysisResultList" :key="index">
-          <template v-if="index == 0">
-            <el-row justify="space-between">
-              <el-text size="small">{{ item._timestr }}</el-text>
-              <el-tag type="danger" effect="dark">鏈�鏂扮嚎绱�</el-tag>
-            </el-row>
-            <el-space>
-              <el-icon color="#F56C6C" :size="40"
-                ><WarnTriangleFilled
-              /></el-icon>
-              <el-text>
-                {{ item.advice }}
-              </el-text>
-            </el-space>
-            <el-row justify="space-between">
-              <el-link type="primary" :underline="true" @click="showPolyline">
-                {{ lineShow ? '鏀惰捣璺嚎' : '瀹氫綅璺嚎' }}
-              </el-link>
-              <el-text size="small">
-                鎺ㄨ崘璺嚎鎬婚暱{{ item.direction.distance }}绫�
-              </el-text>
-            </el-row>
-            <el-divider>鍘嗗彶绾跨储</el-divider>
-          </template>
-          <template v-else>
-            <el-row>
-              <el-text size="small">{{ item._timestr }}</el-text>
-            </el-row>
-            <el-space>
-              <!-- <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon> -->
-              <el-text>
-                {{ item.advice }}
-              </el-text>
-            </el-space>
-            <!-- <el-row justify="space-between">
+      <el-button icon="Plus" @click="addAdvice"></el-button>
+      <el-button icon="Minus" @click="removeAdvice"></el-button>
+      <el-scrollbar height="200">
+        <TransitionGroup name="list">
+          <div v-for="(item, index) in analysisResultList" :key="index">
+            <template v-if="index == 0">
+              <el-row justify="space-between">
+                <el-text size="small">{{ item._timestr }}</el-text>
+                <el-tag type="danger" effect="dark">鏈�鏂扮嚎绱�</el-tag>
+              </el-row>
+              <el-space>
+                <el-icon color="#F56C6C" :size="40"
+                  ><WarnTriangleFilled
+                /></el-icon>
+                <el-text>
+                  {{ item.advice }}
+                </el-text>
+              </el-space>
+              <el-row justify="space-between">
+                <el-link type="primary" :underline="true" @click="showPolyline">
+                  {{ lineShow ? '鏀惰捣璺嚎' : '瀹氫綅璺嚎' }}
+                </el-link>
+                <el-text size="small">
+                  鎺ㄨ崘璺嚎鎬婚暱{{ item.direction.distance }}绫�
+                </el-text>
+              </el-row>
+              <el-divider>鍘嗗彶绾跨储</el-divider>
+            </template>
+            <template v-else>
+              <el-row>
+                <el-text size="small">{{ item._timestr }}</el-text>
+              </el-row>
+              <el-space>
+                <!-- <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon> -->
+                <el-text>
+                  {{ item.advice }}
+                </el-text>
+              </el-space>
+              <!-- <el-row justify="space-between">
               <el-link type="primary" :underline="true" @click="showPolyline">
                 {{ lineShow ? '鏀惰捣璺嚎' : '瀹氫綅璺嚎' }}
               </el-link>
@@ -69,10 +72,11 @@
                 鎺ㄨ崘璺嚎鎬婚暱{{ item.direction.distance }}绫�
               </el-text>
             </el-row> -->
-            <el-divider></el-divider>
-          </template>
-        </div>
-      </TransitionGroup>
+              <el-divider></el-divider>
+            </template>
+          </div>
+        </TransitionGroup>
+      </el-scrollbar>
     </template>
     <template #footer> </template>
   </CardDialog>
@@ -144,6 +148,14 @@
 function timeFormatter(time) {
   return moment(time).format('YYYY-MM-DD HH:mm:ss');
 }
+
+function addAdvice() {
+  analysisResultList.unshift(analysisResultList[0]);
+}
+
+function removeAdvice() {
+  analysisResultList.splice(0, 1);
+}
 </script>
 <style scoped>
 :deep(.el-text) {
@@ -155,7 +167,7 @@
   /* color: #ffd82a; */
 }
 </style>
-<style>
+<!-- <style>
 .list-move, /* 瀵圭Щ鍔ㄤ腑鐨勫厓绱犲簲鐢ㄧ殑杩囨浮 */
 .list-enter-active,
 .list-leave-active {
@@ -165,7 +177,7 @@
 .list-enter-from,
 .list-leave-to {
   opacity: 0;
-  transform: translateX(30px);
+  transform: translateX(-30px);
 }
 
 /* 纭繚灏嗙寮�鐨勫厓绱犱粠甯冨眬娴佷腑鍒犻櫎
@@ -173,4 +185,4 @@
 .list-leave-active {
   position: absolute;
 }
-</style>
+</style> -->
diff --git a/src/views/sourcetrace/component/ClueRecordItem.vue b/src/views/sourcetrace/component/ClueRecordItem.vue
new file mode 100644
index 0000000..28f2918
--- /dev/null
+++ b/src/views/sourcetrace/component/ClueRecordItem.vue
@@ -0,0 +1,96 @@
+<template>
+  <div :class="'wrapper' + (item._selected ? ' wrapper-select' : '')">
+    <div v-if="item._type == '1'">
+      <el-row justify="space-between">
+        <el-space>
+          <el-tag v-if="noWarn" type="info" effect="dark" size="small"
+            >寮傚父</el-tag
+          >
+          <el-tag v-else type="warning" effect="dark" size="small">寮傚父</el-tag>
+          <el-text type="primary">{{
+            item.pollutedData.startTime + ' - ' + item.pollutedData.endTime
+          }}</el-text>
+        </el-space>
+        <el-link type="primary" @click="emits('open', item)"> 璇︽儏 </el-link>
+      </el-row>
+      <el-col :span="24">
+        <el-text type="primary">{{
+          item.pollutedData.factorName +
+          formatException(item.pollutedData.exceptionType) +
+          '锛�' +
+          formatDistanceType(item.pollutedArea.distanceType)
+        }}</el-text>
+        <el-text :type="noWarn ? 'primary' : 'warning'">
+          {{
+            item.pollutedSource.sceneList.length == 0
+              ? '鏈壘鍒板彲鐤戞薄鏌撴簮'
+              : '鎵惧埌' + item.pollutedSource.sceneList.length + '涓彲鐤戞薄鏌撴簮'
+          }}
+        </el-text>
+      </el-col>
+      <!-- <el-col :span="2"> </el-col> -->
+    </div>
+    <div v-else-if="item._type == '2'">
+      <el-row justify="space-between">
+        <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>
+    </div>
+  </div>
+</template>
+<script setup>
+import { computed } from 'vue';
+
+const props = defineProps({
+  item: Object
+});
+
+const emits = defineEmits(['open']);
+
+const noWarn = computed(() => {
+  return props.item && props.item.pollutedSource.sceneList.length == 0;
+});
+
+function formatException(value) {
+  switch (value) {
+    case 4:
+      return '閲忕骇绐佸彉';
+    case 9:
+      return '蹇�熶笂鍗�';
+    default:
+      return '閲忕骇绐佸彉';
+  }
+}
+
+function formatDistanceType(value) {
+  switch (value) {
+    case 'TYPE1':
+      return '50绫充互鍐�';
+    case 'TYPE2':
+      return '50绫� - 500绫充互鍐�';
+    case 'TYPE3':
+      return '50绫� - 1鍏噷浠ュ唴';
+    case 'TYPE4':
+      return '50绫� - 2鍏噷浠ュ唴';
+
+    default:
+      break;
+  }
+}
+</script>
+<style scoped>
+.wrapper {
+  margin-bottom: 8px;
+  border-bottom: 1px dashed rgba(253, 253, 253, 0.651);
+}
+.wrapper-select {
+  background: linear-gradient(
+    rgba(228, 228, 228, 0.274),
+    rgba(64, 165, 248, 0.089)
+  );
+}
+.no-warning {
+  color: var(--el-text-color-disabled) !important;
+}
+</style>
diff --git a/src/views/sourcetrace/component/PollutedClueItem.vue b/src/views/sourcetrace/component/PollutedClueItem.vue
index 0bf7086..784771b 100644
--- a/src/views/sourcetrace/component/PollutedClueItem.vue
+++ b/src/views/sourcetrace/component/PollutedClueItem.vue
@@ -1,222 +1,135 @@
 <template>
-  <!-- <BaseCard>
-    <template #content> -->
-  <el-scrollbar class="clue-card">
-    <el-row justify="space-between">
-      <!-- <el-tag v-if="index == 0" type="danger">鏈�鏂�</el-tag> -->
-      <el-text type="primary">{{
-        '鍒囩墖鏃堕棿锛�' +
-        item.pollutedData.startTime +
-        ' - ' +
-        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">
-        姹℃煋鍖哄煙锛歿{ item.pollutedArea.address }}
-      </el-text>
-    </div>
-    <div>
-      <el-text type="primary">
-        婧簮璺濈锛歿{ formatDistanceType(item.pollutedArea.distanceType) }}
-      </el-text>
-    </div>
-    <div>
-      <el-text type="primary">
-        寮傚父绫诲瀷锛歿{ item.pollutedData.exception }}
-      </el-text>
-    </div>
-    <el-row style="border-top: 1px solid white">
-      <el-col :span="6">
-        <el-statistic title="绐佸彉鍥犲瓙" :value="item.pollutedData.factorName" />
-      </el-col>
-      <el-col :span="6">
-        <el-statistic
-          v-if="item.pollutedData.exceptionType == 4"
-          title="鍙樺寲骞呭害"
-          :value="formatPercentage(item.pollutedData.avgPer)"
-        />
-        <el-statistic
-          v-else-if="item.pollutedData.exceptionType == 9"
-          title="鍙樺寲閫熺巼"
-          :value="formatPercentage(item.pollutedData.avgPer)"
-        />
-      </el-col>
-      <el-col :span="6">
-        <el-statistic title="鍙戠敓娆℃暟" :value="item.pollutedData.times" />
-      </el-col>
-      <el-col :span="6">
-        <el-statistic
-          title="骞冲潎椋庨��"
-          :value="item.pollutedData.windSpeed"
-          suffix="m/s"
-        />
-      </el-col>
-    </el-row>
-    <el-row justify="space-between">
-      <!-- <el-link
-                    type="primary"
-                    underline
-                    @click="showMarksAndPolygon(item)"
-                  >
-                    {{
-                      (item.showMore ? '鏀惰捣婧簮鍦烘櫙' : '鏌ョ湅婧簮鍦烘櫙') +
-                      '锛�' +
-                      item.pollutedSource.sceneList.length +
-                      '锛�'
-                    }}
-                  </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"
-    ></RealTimeLineChart>
-    <!-- </div> -->
-    <div class="border-dashed">
-      <el-text type="" tag="mark">
-        {{ item.pollutedSource.conclusion }}
-      </el-text>
-    </div>
-    <SceneTable
-      :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> -->
+  <CardDialog
+    :model-value="modelValue"
+    @update:model-value="handleDialogShow"
+    title="姹℃煋绾跨储"
+    draggable
+    :modal="false"
+    width="400px"
+  >
+    <template #default>
+      <template v-if="item">
+        <el-row>
+          <el-text type="primary" size="small">{{ item._timestr }}</el-text>
+        </el-row>
+        <el-space>
+          <el-icon color="#F56C6C" :size="40"><WarnTriangleFilled /></el-icon>
+          <el-text type="primary">
+            {{ item.advice }}
+          </el-text>
+        </el-space>
+        <el-row justify="space-between">
+          <el-text type="primary" size="small">
+            鎺ㄨ崘璺嚎鎬婚暱{{ item.direction.distance }}绫�
+          </el-text>
+          <el-link type="primary" size="small" @click="showPolyline">
+            {{ lineShow ? '鏀惰捣璺嚎' : '瀹氫綅璺嚎' }}
+          </el-link>
+        </el-row>
+        <SceneTable
+          :show-marks="lineShow"
+          :scene-list="formatSceneList(item.sortedSceneList)"
+        >
+          <el-table-column prop="_count" width="42" label="婧簮娆℃暟" />
+        </SceneTable>
+      </template>
+    </template>
+    <template #footer> </template>
+  </CardDialog>
 </template>
 <script setup>
-import { ref } from 'vue';
+import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
+
+import { sceneTypes, sceneIcon } from '@/constant/scene-types';
+import mapLine from '@/utils/map/line';
+import mapUtil from '@/utils/map/util';
+import marks from '@/utils/map/marks';
 
 const props = defineProps({
+  modelValue: Boolean,
   item: Object
 });
 
-const emits = defineEmits(['showMarksAndPolygon']);
+const lineShow = ref(true);
+// 涓婁竴鏉″鑸矾绾�
+let lastPolyline = undefined;
+// 涓婁竴涓鑸洰鐨勫湴
+// let lastDestination = undefined;
+// let layer = undefined;
 
-function showMarksAndPolygon(item) {
-  emits('showMarksAndPolygon', item);
-}
+const emits = defineEmits(['update:modelValue']);
 
-function formatPercentage(value) {
-  return Math.round(value * 100) + '%';
-}
+watch(
+  () => [props.item, props.modelValue],
+  (nV, oV) => {
+    if (nV[0] != oV[0]) {
+      const polyline = mapLine.drawDirection(
+        nV[0].direction.paths.map((v) => [v.first, v.second])
+      );
+      // polylineList.unshift(polyline);
+      if (lastPolyline) {
+        mapUtil.removeViews(lastPolyline);
+      }
+      lastPolyline = polyline;
+      lineShow.value = true;
+    } else if (nV[1]) {
+      showPolyline(true);
+    }
+  }
+);
 
-function formatDistanceType(value) {
-  switch (value) {
-    case 'TYPE1':
-      return '50绫�';
-    case 'TYPE2':
-      return '50绫� - 500绫�';
-    case 'TYPE3':
-      return '50绫� - 1鍏噷';
-    case 'TYPE4':
-      return '50绫� - 2鍏噷';
+function showPolyline(show) {
+  if (typeof show === 'boolean') {
+    lineShow.value = show;
+  } else {
+    lineShow.value = !lineShow.value;
+  }
 
-    default:
-      break;
+  if (lineShow.value && lastPolyline) {
+    mapUtil.addViews(lastPolyline);
+  } else {
+    mapUtil.removeViews(lastPolyline);
   }
 }
 
-function formatChangeRate() {
-  
+// function removeLayer() {
+//   if (layer != undefined) {
+//     mapUtil.removeViews(layer);
+//     layer = undefined;
+//   }
+// }
+
+// function drawMarks(sceneList) {
+//   removeLayer();
+//   if (sceneList.length != 0) {
+//     const icons = [];
+//     sceneList.forEach((s) => {
+//       icons.push(sceneIcon(s.typeId));
+//     });
+//     layer = marks.createLabelMarks(icons, sceneList, false);
+//   }
+// }
+
+function formatSceneList(sceneList) {
+  return sceneList.map((v) => {
+    return {
+      ...v.first,
+      _count: v.second
+    };
+  });
+}
+
+function handleDialogShow(value) {
+  showPolyline(value);
+  emits('update:modelValue', value);
 }
 </script>
 <style scoped>
-:deep(.el-statistic) {
-  --el-statistic-title-color: rgb(215, 215, 215);
-  --el-statistic-content-color: white;
-}
-
-:deep(.el-text.el-text--primary) {
+/* :deep(.el-text.el-text--primary) {
   --el-text-color: white;
 }
 
 :deep(.el-link) {
   --el-link-text-color: #23dad1;
-}
-
-.scrollbar {
-  min-width: 300px;
-  max-width: 60vw;
-  /* height: 35vh; */
-  /* color: #02ffea; */
-}
-
-.clue-card {
-  padding: 0 4px;
-  /* 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/component/PollutedExceptionItem.vue b/src/views/sourcetrace/component/PollutedExceptionItem.vue
new file mode 100644
index 0000000..e7defcc
--- /dev/null
+++ b/src/views/sourcetrace/component/PollutedExceptionItem.vue
@@ -0,0 +1,237 @@
+<template>
+  <!-- <CardDialog
+    :model-value="modelValue"
+    @update:model-value="handleDialogShow"
+    title="姹℃煋寮傚父"
+    draggable
+    :modal="false"
+  >
+    <template #default> -->
+  <BaseCard v-if="item" v-show="item.showMore">
+    <template #content>
+      <el-scrollbar class="clue-card">
+        <el-row justify="space-between">
+          <!-- <el-tag v-if="index == 0" type="danger">鏈�鏂�</el-tag> -->
+          <el-text type="primary">{{
+            '鍒囩墖鏃堕棿锛�' +
+            item.pollutedData.startTime +
+            ' - ' +
+            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">
+            姹℃煋鍖哄煙锛歿{ item.pollutedArea.address }}
+          </el-text>
+        </div>
+        <div>
+          <el-text type="primary">
+            婧簮璺濈锛歿{ formatDistanceType(item.pollutedArea.distanceType) }}
+          </el-text>
+        </div>
+        <div>
+          <el-text type="primary">
+            寮傚父绫诲瀷锛歿{ item.pollutedData.exception }}
+          </el-text>
+        </div>
+        <el-row style="border-top: 1px solid white">
+          <el-col :span="6">
+            <el-statistic
+              title="绐佸彉鍥犲瓙"
+              :value="item.pollutedData.factorName"
+            />
+          </el-col>
+          <el-col :span="6">
+            <el-statistic
+              v-if="item.pollutedData.exceptionType == 4"
+              title="鍙樺寲骞呭害"
+              :value="formatPercentage(item.pollutedData.avgPer)"
+            />
+            <el-statistic
+              v-else-if="item.pollutedData.exceptionType == 9"
+              title="鍙樺寲閫熺巼"
+              :value="formatChangeRate(item.pollutedData.avgRate)"
+              suffix=""
+            />
+          </el-col>
+          <el-col :span="6">
+            <el-statistic title="鍙戠敓娆℃暟" :value="item.pollutedData.times" />
+          </el-col>
+          <el-col :span="6">
+            <el-statistic
+              title="骞冲潎椋庨��"
+              :value="item.pollutedData.windSpeed"
+              suffix="m/s"
+            />
+          </el-col>
+        </el-row>
+        <el-row justify="space-between">
+          <!-- <el-link
+                    type="primary"
+                    underline
+                    @click="showMarksAndPolygon(item)"
+                  >
+                    {{
+                      (item.showMore ? '鏀惰捣婧簮鍦烘櫙' : '鏌ョ湅婧簮鍦烘櫙') +
+                      '锛�' +
+                      item.pollutedSource.sceneList.length +
+                      '锛�'
+                    }}
+                  </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"
+        ></RealTimeLineChart>
+        <!-- </div> -->
+        <div class="border-dashed">
+          <el-text type="" tag="mark">
+            {{ item.pollutedSource.conclusion }}
+          </el-text>
+        </div>
+        <SceneTable
+          :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>
+  <!-- </template>
+  </CardDialog> -->
+</template>
+<script setup>
+import { ref } from 'vue';
+
+const props = defineProps({
+  modelValue: Boolean,
+  item: Object
+});
+
+const emits = defineEmits(['showMarksAndPolygon', 'update:modelValue']);
+
+function showMarksAndPolygon(item) {
+  emits('showMarksAndPolygon', item);
+}
+
+function formatPercentage(value) {
+  return Math.round(value * 100) + '%';
+}
+
+function formatDistanceType(value) {
+  switch (value) {
+    case 'TYPE1':
+      return '50绫�';
+    case 'TYPE2':
+      return '50绫� - 500绫�';
+    case 'TYPE3':
+      return '50绫� - 1鍏噷';
+    case 'TYPE4':
+      return '50绫� - 2鍏噷';
+
+    default:
+      break;
+  }
+}
+
+function formatChangeRate(value) {
+  return Math.round(value * 100) / 100 + '';
+}
+</script>
+<style scoped>
+:deep(.el-statistic) {
+  --el-statistic-title-color: rgb(215, 215, 215);
+  --el-statistic-content-color: white;
+}
+
+:deep(.el-text.el-text--primary) {
+  --el-text-color: white;
+}
+
+:deep(.el-link) {
+  --el-link-text-color: #23dad1;
+}
+
+.scrollbar {
+  min-width: 300px;
+  max-width: 60vw;
+  /* height: 35vh; */
+  /* color: #02ffea; */
+}
+
+.clue-card {
+  padding: 0 4px;
+  /* 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>

--
Gitblit v1.9.3