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