| | |
| | | } |
| | | |
| | | 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({ |
| | |
| | | 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'] |
| | |
| | | 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'] |
| | |
| | | > |
| | | 设备管理 |
| | | </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 |
| | |
| | | return { loading, fetchData }; |
| | | }, |
| | | props: { |
| | | modelValue: Boolean |
| | | // modelValue: Boolean |
| | | }, |
| | | emits: ['update:modelValue'], |
| | | // emits: ['update:modelValue'], |
| | | data() { |
| | | return { |
| | | dialogVisible: false, |
| | |
| | | } |
| | | }, |
| | | methods: { |
| | | handleChange(value) { |
| | | this.$emit('update:modelValue', value); |
| | | }, |
| | | // handleChange(value) { |
| | | // this.$emit('update:modelValue', value); |
| | | // }, |
| | | deleteDevice(row) { |
| | | this.onConfirm = () => { |
| | | this.deviceStore.deleteDevice(row.deviceCode); |
| | |
| | | </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'; |
| | |
| | | } |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | removeLayer(); |
| | | }); |
| | | |
| | | watch(showSceneList, (nV, oV) => { |
| | | if (nV && props.showMarks) { |
| | | drawMarks(nV); |
| | | } else { |
| | | if (layer != undefined) { |
| | | MapUtil.removeViews(layer); |
| | | layer = undefined; |
| | | } |
| | | removeLayer(); |
| | | } |
| | | }); |
| | | |
| | |
| | | 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) => { |
| | |
| | | .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; |
| | | // } |
| | |
| | | |
| | | var fetchingTask; |
| | | var isFetching; |
| | | var pause = false; |
| | | /** |
| | | * 开启实时数据循环获取 |
| | | * @param {Object} params |
| | |
| | | interval = 10 * 1000; |
| | | } |
| | | if (_interval) { |
| | | interval = _interval |
| | | interval = _interval; |
| | | } |
| | | // 开始循环任务 |
| | | clearFetchingTask(); |
| | | fetchingTask = setInterval(() => { |
| | | if (isFetching) { |
| | | if (isFetching || pause) { |
| | | return; |
| | | } |
| | | isFetching = true; |
| | |
| | | }, interval); |
| | | } |
| | | |
| | | function pauseTask() { |
| | | pause = !pause; |
| | | return pause; |
| | | } |
| | | |
| | | /** |
| | | * 清理历史实时数据获取任务 |
| | | */ |
| | |
| | | } |
| | | } |
| | | |
| | | export { fetchHistoryData, startLoopFetchRealTimeData, clearFetchingTask }; |
| | | export { |
| | | fetchHistoryData, |
| | | startLoopFetchRealTimeData, |
| | | clearFetchingTask, |
| | | pauseTask |
| | | }; |
| | |
| | | |
| | | function drawDirection(path) { |
| | | const polyline = newPolyline(path, '#02ffea'); |
| | | map.add(polyline) |
| | | map.add(polyline); |
| | | return polyline; |
| | | } |
| | | |
| | | export default { |
| | |
| | | <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"> |
| | |
| | | import { |
| | | fetchHistoryData, |
| | | startLoopFetchRealTimeData, |
| | | clearFetchingTask |
| | | clearFetchingTask, |
| | | pauseTask |
| | | } from '@/utils/factor/data'; |
| | | import thirdPartyDataApi from '@/api/thirdPartyDataApi'; |
| | | import websocket from '@/api/websocket'; |
| | |
| | | // 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 }; |
| | |
| | | // 新获取的监测数据 |
| | | factorDatas: new FactorDatas(), |
| | | // 全部监测数据 |
| | | allFactorDatas: new FactorDatas() |
| | | allFactorDatas: new FactorDatas(), |
| | | pause: false |
| | | }; |
| | | }, |
| | | watch: { |
| | |
| | | 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, |
| | |
| | | }); |
| | | }); |
| | | }, |
| | | pauseTask() { |
| | | this.pause = pauseTask(); |
| | | }, |
| | | clearFetchingTask() { |
| | | clearFetchingTask(); |
| | | }, |
| | |
| | | <!-- <div> |
| | | <el-text type="primary" size="large" tag="b">动态溯源</el-text> |
| | | </div> --> |
| | | <el-scrollbar ref="scrollbarRef" :height="height" class="scrollbar"> |
| | | <el-row ref="scrollContentRef"> |
| | | <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">{{ |
| | | '线索时间:' + |
| | |
| | | ' - ' + |
| | | 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"> |
| | |
| | | }} |
| | | </el-text> |
| | | </div> |
| | | <el-row> |
| | | <el-row style="border-top: 1px solid white"> |
| | | <el-col :span="6"> |
| | | <el-statistic |
| | | title="突变因子" |
| | |
| | | ')' |
| | | }} |
| | | </el-link> --> |
| | | <el-link |
| | | type="primary" |
| | | underline |
| | | @click="showMarksAndPolygon(item)" |
| | | > |
| | | {{ item.showMore ? '收起异常' : '定位异常' }} |
| | | </el-link> |
| | | </el-row> |
| | | <div style="width: 320px; height: 140px"> |
| | | <RealTimeLineChart |
| | |
| | | :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" |
| | |
| | | :scene-list="item.relatedSceneList" |
| | | ></SceneTable> |
| | | <el-divider /> --> |
| | | </el-scrollbar> |
| | | </div> |
| | | </el-row> |
| | | </el-scrollbar> |
| | | </template> |
| | | </BaseCard> |
| | |
| | | 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 |
| | |
| | | |
| | | const emits = defineEmits(['update:factorType']); |
| | | |
| | | const height = `48vh`; |
| | | const height = `30vh`; |
| | | const width = `60vh`; |
| | | |
| | | const show = ref(false); |
| | |
| | | |
| | | let showFirstClueTask; |
| | | function dealMsg(data) { |
| | | const {type, content} = websocketMsgParser.parseMsg(data) |
| | | const { type, content } = websocketMsgParser.parseMsg(data); |
| | | |
| | | // 污染线索 PollutedClue |
| | | if (type == '1') { |
| | |
| | | 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(); |
| | |
| | | // } |
| | | }); |
| | | |
| | | 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)) { |
| | |
| | | }); |
| | | 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, [ |
| | |
| | | value: obj.pollutedData.factorId + '' |
| | | } |
| | | ]); |
| | | console.log('折线图:', obj._chartOptions); |
| | | // console.log('折线图:', obj._chartOptions); |
| | | }); |
| | | } |
| | | |
| | |
| | | --el-statistic-content-color: white; |
| | | } |
| | | |
| | | :deep(.el-text) { |
| | | :deep(.el-text.el-text--primary) { |
| | | --el-text-color: white; |
| | | } |
| | | |
| | |
| | | .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> |
| | |
| | | <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) { |
| | |
| | | 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> |