From d6e6f8b5b31e132e4597eb531168d3e88f3bda72 Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期五, 04 七月 2025 17:26:49 +0800 Subject: [PATCH] 2025.7.4 动态溯源 --- src/views/sourcetrace/SourceTrace.vue | 378 +++++++++++++++++++++++++++++++++-------------------- 1 files changed, 232 insertions(+), 146 deletions(-) diff --git a/src/views/sourcetrace/SourceTrace.vue b/src/views/sourcetrace/SourceTrace.vue index d4c97a6..19012f9 100644 --- a/src/views/sourcetrace/SourceTrace.vue +++ b/src/views/sourcetrace/SourceTrace.vue @@ -1,11 +1,11 @@ <template> <el-row> - <el-col span="2" class="flex-col"> + <el-col v-if="direction == 'right'" span="2" class="flex-col"> <el-row justify="end"> <CardButton direction="left" name="鍔ㄦ�佹函婧�" - @click="() => (show = !show)" + @click="handleDisplayChange" ></CardButton> </el-row> <el-row class="flex-col"> @@ -18,17 +18,30 @@ <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-row + justify="space-between" + align="middle" + style="border-bottom: 1px solid white" + > + <!-- 鏁版嵁鍒囩墖绛涢�夋潯浠� --> + <SourceTraceFilter + v-model:data-slice="selectedMsgTypes" + v-model:factor-type="selectedFactorTypes" + :factor-options="factorOptions" + v-model:scene-type="selectedSceneTypes" + :scene-options="sceneOptions" + ></SourceTraceFilter> + <!-- <el-divider direction="vertical"></el-divider> --> + <!-- 鏁版嵁鍒囩墖缁熻 --> + <div style="border-left: 1px solid white" class="p-l-8"> + <el-space direction="vertical"> + <el-text type="primary">婧簮锛歿{ countMsg1.type1 }}鏉�</el-text> + <el-text type="primary">绾跨储锛歿{ countMsg1.type2 }}鏉�</el-text> + <el-text type="primary">鎻愰啋锛歿{ countMsg1.type3 }}鏉�</el-text> + </el-space> + </div> + </el-row> <el-scrollbar ref="scrollbarRef" class="scrollbar"> - <!-- <div - ref="scrollContentRef" - style="display: flex; width: fit-content" - > --> <TransitionGroup name="list"> <div v-for="item in filterStreams" @@ -40,10 +53,24 @@ ></ClueRecordItem> </div> </TransitionGroup> - <!-- </div> --> </el-scrollbar> </template> </BaseCard> + </el-col> + <el-col v-if="direction == 'left'" span="2" class="flex-col"> + <el-row justify="start"> + <CardButton + direction="right" + name="鍔ㄦ�佹函婧�" + @click="handleDisplayChange" + ></CardButton> + </el-row> + <el-row class="flex-col"> + <PollutedExceptionItem + :item="selectedException" + @showMarksAndPolygon="showMarksAndPolygon" + ></PollutedExceptionItem> + </el-row> </el-col> <PollutedClueItem v-model="clueDialog" @@ -60,6 +87,7 @@ import moment from 'moment'; import websocket from '@/api/websocket'; +import dataAnalysisApi from '@/api/dataAnalysisApi'; import sector from '@/utils/map/sector'; import mapUtil from '@/utils/map/util'; import { sceneTypes, sceneIcon } from '@/constant/scene-types'; @@ -72,15 +100,29 @@ import PollutedExceptionItem from './component/PollutedExceptionItem.vue'; import ClueRecordItem from './component/ClueRecordItem.vue'; import PollutedClueItem from '@/views/sourcetrace/component/PollutedClueItem.vue'; +import SourceTraceFilter from '@/views/sourcetrace/component/SourceTraceFilter.vue'; + +const NO_SCENE = 'no_scene'; + +const MODE_REALTIME = 'realtime'; +const MODE_HISTORY = 'history'; const props = defineProps({ - factorType: String + direction: { + type: String, + default: 'left' + }, + factorType: String, + // 妯″紡锛宺ealtime锛氬疄鏃舵ā寮忥紱history锛氬巻鍙叉暟鎹ā寮� + mode: { + type: String, + default: 'realtime' + }, + missionCode: String, + deviceCode: String }); const emits = defineEmits(['update:factorType']); - -const height = `30vh`; -const width = `60vh`; const show = ref(false); const clueDialog = ref(false); @@ -89,7 +131,22 @@ const selectedException = ref(); const selectedClue = ref(); -const selectedMsgTypes = ref(['1', '2']); +const selectedMsgTypes = ref(['1', '2', '3']); +const selectedFactorTypes = ref([]); +const factorOptions = ref([]); +const selectedSceneTypes = ref([]); +const sceneOptions = ref([]); + +function handleDisplayChange() { + show.value = !show.value; + if ( + !show.value && + selectedException.value && + selectedException.value.showMore + ) { + showMarksAndPolygon(selectedException.value); + } +} function scrollToBottom() { const h1 = scrollContentRef.value.clientHeight + 100; @@ -104,11 +161,60 @@ }, 100); } -const streams = reactive([]); +const streams = ref([]); const filterStreams = computed(() => { - return streams.filter((v) => { - return selectedMsgTypes.value.indexOf(v._type) != -1; + return streams.value.filter((v) => { + // 鍒ゆ柇娑堟伅绫诲瀷鏄惁閫変腑 + const b1 = selectedMsgTypes.value.indexOf(v._type) != -1; + let b2, b3; + switch (v._type) { + case '1': + case '3': + // 鍒ゆ柇鐩戞祴鍥犲瓙绫诲瀷鏄惁閫変腑 + b2 = selectedFactorTypes.value.indexOf(v.pollutedData.factorId) != -1; + // 鍒ゆ柇鍦烘櫙绫诲瀷鏄惁閫変腑 + if (v.pollutedSource.sceneList.length == 0) { + b3 = selectedSceneTypes.value.indexOf(NO_SCENE) != -1; + } else { + b3 = + v.pollutedSource.sceneList.findIndex( + (v) => selectedSceneTypes.value.indexOf(v.typeId) != -1 + ) != -1; + } + break; + case '2': + b2 = true; + b3 = + v.sortedSceneList.findIndex( + (v) => selectedSceneTypes.value.indexOf(v.first.typeId) != -1 + ) != -1; + break; + } + + return b1 && b2 && b3; }); +}); +// 缁熻鍚勭被鍨嬫秷鎭殑鏁伴噺 +const countMsg1 = computed(() => { + const count = { + type1: 0, + type2: 0, + type3: 0 + }; + streams.value.forEach((v) => { + switch (v._type) { + case '1': + count.type1++; + break; + case '2': + count.type2++; + break; + case '3': + count.type3++; + break; + } + }); + return count; }); const inputVal = ref(''); @@ -116,27 +222,21 @@ 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)); + const obj = reactive(JSON.parse(content)); + if (obj.deviceCode == props.deviceCode) { obj._type = type; - // obj.showMore = true; + dealObj(obj); + } +} + +function dealObj(obj) { + // 姹℃煋绾跨储 PollutedClue + if (obj._type == '1') { obj.showMore = false; console.log('姹℃煋寮傚父鍒囩墖: ', obj); - // if (streams.length == 0) { - // streams.push(obj); - // } else { - // // streams.forEach((s) => { - // // showMarksAndPolygon(s); - // // }); - // // hideAll(); - // streams.unshift(obj); - // } addNewMsg(obj); show.value = true; @@ -144,20 +244,61 @@ // scrollToTop(); // drawPolygon(obj.pollutedArea); parseChartData(obj); - - // if (showFirstClueTask) { - // clearTimeout(showFirstClueTask); - // } - // showFirstClueTask = setTimeout(() => { - // showMarksAndPolygon(obj); - // }, 1000); - } else if (type == '2') { - const obj = JSON.parse(content); - obj._type = type; + } else if (obj._type == '2') { + // const obj = JSON.parse(content); + // obj._type = type; console.log('姹℃煋绾跨储缁撴灉: ', obj); obj._timestr = timeFormatter(obj.time); - // streams.unshift(obj); addNewMsg(obj); + } else if (obj._type == '3') { + console.log('姹℃煋鎻愰啋鍒囩墖: ', obj); + addNewMsg(obj); + parseChartData(obj); + } + + optionsFilte(obj); +} + +// 瀵规暟鎹繘琛岀瓫閫夛紝鍖呮嫭鐩戞祴鍥犲瓙鍜屽満鏅被鍨� +function optionsFilte(objData) { + switch (objData._type) { + 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); + } + // 绛涢�夊満鏅被鍨� + if (objData.pollutedSource.sceneList.length == 0) { + // 鑻ユ病鏈夋壘鍒伴闄╂簮鏃讹紝灏嗚鍒嗙被璁惧畾涓簄ull + if (sceneOptions.value.findIndex((v) => v.value == NO_SCENE) == -1) { + sceneOptions.value.push({ + label: '鏃�', + value: NO_SCENE + }); + selectedSceneTypes.value.push(NO_SCENE); + } + } else { + objData.pollutedSource.sceneList.forEach((s) => { + if (sceneOptions.value.findIndex((v) => v.value == s.typeId) == -1) { + sceneOptions.value.push({ + label: s.type, + value: s.typeId + }); + selectedSceneTypes.value.push(s.typeId); + } + }); + } + // case '2': + // break; } } @@ -166,7 +307,7 @@ const leftMsgList = []; function addNewMsg(msg, inside) { if (!addNewMsgTask && (leftMsgList.length == 0 || inside)) { - streams.splice(0, 0, msg); + streams.value.splice(0, 0, msg); addNewMsgTask = setTimeout(() => { clearTimeout(addNewMsgTask); addNewMsgTask = undefined; @@ -180,8 +321,32 @@ } } +function fetchPollutionTraceHistory() { + dataAnalysisApi.fetchPollutionTraceHistory(props.missionCode).then((res) => { + const objList = JSON.parse(res.data); + objList.forEach((obj) => { + obj._type = obj.msgType + ''; + if (obj._type == '1') { + obj.showMore = false; + show.value = true; + parseChartData(obj); + } else if (obj._type == '2') { + obj._timestr = timeFormatter(obj.time); + } else if (obj._type == '3') { + parseChartData(obj); + } + optionsFilte(obj); + }); + streams.value = objList; + }); +} + onMounted(() => { - websocket.registerReceiveEvent(dealMsg); + if (props.mode == MODE_REALTIME) { + websocket.registerReceiveEvent(dealMsg); + } else if (props.missionCode) { + fetchPollutionTraceHistory(); + } }); onUnmounted(() => { websocket.removeReceiveEvent(dealMsg); @@ -199,9 +364,19 @@ } }); +watch( + () => props.missionCode, + (nV, oV) => { + if (nV != oV) { + fetchPollutionTraceHistory(); + } + } +); + function handleOpen(item) { switch (item._type) { case '1': + case '3': if (selectedException.value) { selectedException.value._selected = false; } @@ -220,7 +395,7 @@ } function hideAll() { - streams.forEach((s) => { + streams.value.forEach((s) => { if (polygonMap.has(s.guid)) { s.showMore = false; map.remove(polygonMap.get(s.guid)); @@ -269,6 +444,12 @@ function parseChartData(obj) { // console.log('鎶樼嚎鍥撅細start'); + obj.pollutedData._startTime = moment(obj.pollutedData.startTime).format( + 'HH:mm:ss' + ); + obj.pollutedData._endTime = moment(obj.pollutedData.endTime).format( + 'HH:mm:ss' + ); const factorDatas = new FactorDatas(); factorDatas.setData(obj.pollutedData.historyDataList, 0, () => { obj._chartOptions = factorDataParser.parseData(factorDatas, [ @@ -284,101 +465,6 @@ function timeFormatter(time) { return moment(time).format('YYYY-MM-DD HH:mm:ss'); -} - -/******************************************************************************************************************** */ - -/** - * 娣诲姞涓�鏉″伐浣滄祦淇℃伅 - * @param {*} data - */ -const putWorkStream = (data) => { - data.substring(); - const obj = JSON.parse(data); - console.log('sourcetrace: ', obj); - - obj._statusStr = exceptionStatus(obj.status); - - if (streams.length == 0) { - streams.push(obj); - } else { - const index = streams.findIndex((v) => { - return v.guid == obj.guid; - }); - if (index != -1) { - const old = streams[index]; - obj.showMore = old.showMore; - old.relatedSceneList.forEach((s) => { - const index = obj.relatedSceneList.findIndex((v) => { - return v.guid == s.guid; - }); - if (index == -1) { - obj.relatedSceneList.push(s); - } - }); - streams.splice(index, 1, obj); - } else { - streams.unshift(obj); - } - - show.value = true; - } - - // scrollToBottom(); - scrollToTop(); - - const excObj = streams.find((v) => { - return v.factorId + '' == props.factorType; - }); - if (excObj) { - drawSector(excObj); - } -}; - -function exceptionStatus(value) { - switch (value) { - case 1: - return '寮傚父鍙戠敓涓�'; - case 2: - return '寮傚父宸茬粨鏉�'; - default: - return '寮傚父宸茬粨鏉�'; - } -} - -let lastDrawObjGuid; -function drawSector(exceptionObj) { - emits('update:factorType', exceptionObj.factorId + ''); - setTimeout(() => { - if (exceptionObj.guid != lastDrawObjGuid) { - sector.clearSectorPt(); - lastDrawObjGuid = exceptionObj.guid; - } - // 1. 缁樺埗鏂版墖褰㈠尯鍩� - const datavo = exceptionObj.midData; - const factorDatas = new FactorDatas(); - factorDatas.setData([datavo], 0, () => { - const pr = sector.drawSectorPt(factorDatas, 0); - // 璋冩暣瑙嗚灞呬腑鏄剧ず - // mapUtil.setCenter(pr.p); - drawMarks(exceptionObj.relatedSceneList); - }); - }, 1000); -} - -let layer = undefined; -function drawMarks(sceneList) { - if (layer != undefined) { - mapUtil.removeViews(layer); - // layer = undefined; - } - if (sceneList.length != 0) { - const icons = []; - sceneList.forEach((s) => { - icons.push(sceneIcon(s.typeId)); - }); - layer = marks.createLabelMarks(icons, sceneList, true); - } } </script> <style scoped> @@ -413,7 +499,7 @@ .scrollbar { width: 400px; /* max-width: 60vw; */ - height: 45vh; + height: 65vh; /* color: #02ffea; */ padding-right: 10px; } -- Gitblit v1.9.3