Compare commits

...

2 Commits

Author SHA1 Message Date
guanj
98b9bbce0d Merge branch 'master' of http://192.168.1.22:3000/Web/bigscreenWeb 2025-10-27 13:21:46 +08:00
guanj
da63aa8abe 添加地图 2025-10-27 13:21:31 +08:00
3 changed files with 493 additions and 19 deletions

View File

@@ -20,6 +20,7 @@ interface State {
eventDuration: number; eventDuration: number;
realData: []; //实时数据 realData: []; //实时数据
iframeLoad: boolean; iframeLoad: boolean;
showMap: boolean;
} }
// 初始状态 // 初始状态
@@ -37,13 +38,14 @@ const state: State = {
eventValue: 0.7, eventValue: 0.7,
eventDuration: 5, eventDuration: 5,
realData: [], //实时数据 realData: [], //实时数据
iframeLoad: false, iframeLoad: false,//无锡接线图是否加载完成
showMap: true,//无锡地图显示
}; };
// 定义Mutation类型 // 定义Mutation类型
enum Mutations { enum Mutations {
INCREMENT = "INCREMENT", INCREMENT = "INCREMENT",
IFRAMELOAD = "IFRAMELOAD", SET_STATE= "SET_STATE",
SET_TOKEN = "SET_TOKEN", SET_TOKEN = "SET_TOKEN",
SET_TIME = "SET-TIME", SET_TIME = "SET-TIME",
SET_CONFIG = "SET-CONFIG-TIME", SET_CONFIG = "SET-CONFIG-TIME",
@@ -55,8 +57,9 @@ export default createStore({
[Mutations.INCREMENT](state: State) { [Mutations.INCREMENT](state: State) {
state.count++; state.count++;
}, },
[Mutations.IFRAMELOAD](state: State, data: boolean) { [Mutations.SET_STATE](state: State, data: any) {
state.iframeLoad = data; console.log("🚀 ~ data:", data)
state[data.key] = data.value;
}, },
[Mutations.SET_TOKEN](state: State, data: any) { [Mutations.SET_TOKEN](state: State, data: any) {
window.sessionStorage.setItem("token", data.token); window.sessionStorage.setItem("token", data.token);
@@ -103,8 +106,8 @@ export default createStore({
increment({ commit }) { increment({ commit }) {
commit(Mutations.INCREMENT); commit(Mutations.INCREMENT);
}, },
setIframeLoad({ commit }, data: any) { setStateKey({ commit }, data: any) {
commit(Mutations.IFRAMELOAD,data); commit(Mutations.SET_STATE,data);
}, },
setTimeType({ commit }, data: any) { setTimeType({ commit }, data: any) {
commit(Mutations.SET_TIME, data); commit(Mutations.SET_TIME, data);

View File

@@ -0,0 +1,472 @@
<template>
<div v-loading="loading" element-loading-background="#343849c7">
<div class="iconBox">
<div class="div">
<img src="@/assets/jcd.png" alt="" />
<span>变电站</span>
</div>
<div class="div">
<img src="@/assets/txycyzj.gif" alt="" />
<span>暂降监测点</span>
</div>
</div>
<div class="bmSelect">
<el-select
v-model="value"
@change="setIcon"
placeholder="变电站筛选"
filterable
clearable
size="small"
style="width: 150px"
>
<el-option
v-for="item in siteList"
:key="item.stationName"
:label="item.stationName"
:value="item.stationName"
/>
</el-select>
</div>
<div>
<baidu-map
ref="mapRef"
class="bm-view"
:max-zoom="15"
:min-zoom="10"
:zoom="zoom"
@zoomend="syncCenterAndZoom"
@moveend="checkMapData"
@ready="handler"
:center="center"
:scroll-wheel-zoom="false"
:double-click-zoom="false"
>
<!-- 线-->
<div v-if="zoom > 13">
<bm-polyline
:path="path"
v-for="(path, index) in polyline"
:key="index"
></bm-polyline>
</div>
<!-- 变电站-->
<template v-if="zoom > 13">
<bm-marker
:position="path"
v-for="path in siteList"
:key="path.subId"
:icon="path.icon"
@click="markerClick(path)"
></bm-marker>
</template>
<!-- -->
<div maxZoom="12">
<bm-marker
:position="path"
v-for="path in areaLineInfo"
:key="path.lineId"
:icon="path.icon"
@click="markerClick(path)"
></bm-marker>
</div>
<bm-marker
:position="infoWindowPoint"
:icon="{ url: '1', size: { width: 0, height: 0 } }"
>
<bm-info-window
:show="infoWindowPoint.show"
@close="infoWindowPoint.show = false"
>
<el-descriptions
:title="infoWindowPoint.lineName"
style="min-width: 250px"
:column="1"
border
v-if="infoWindowPoint.lineId"
label-width="90px"
>
<el-descriptions-item label="供电区域">{{
infoWindowPoint.gdName
}}</el-descriptions-item>
<el-descriptions-item label="上级电站">{{
infoWindowPoint.stationName
}}</el-descriptions-item>
<el-descriptions-item label="上级母线">{{
infoWindowPoint.busBarName
}}</el-descriptions-item>
<el-descriptions-item label="暂降次数">{{
infoWindowPoint.eventCount
}}</el-descriptions-item>
<el-descriptions-item
label="用户"
v-if="infoWindowPoint.objName == null"
>/</el-descriptions-item
>
<template v-else>
<el-descriptions-item label="用户">
<div class="descriptionsBox">
<div
v-for="(value, index) in infoWindowPoint.objName.split(
';'
)"
style="white-space: nowrap"
>
{{ value }}
</div>
</div></el-descriptions-item
>
</template>
</el-descriptions>
<el-descriptions
:title="infoWindowPoint.stationName"
:column="1"
v-else
style="min-width: 250px"
border
>
<!-- <el-descriptions-item
:label="index == 0 ? '母线' : ''"
v-for="(value, index) in infoWindowPoint.lineEventDetails"
>
{{ value.busBarName + `-` + value.lineName }}
</el-descriptions-item> -->
<el-descriptions-item label="母线"
><div class="descriptionsBox">
<div
v-for="(value, index) in infoWindowPoint.lineEventDetails"
style="white-space: nowrap"
>
{{ value.busBarName + `-` + value.lineName }}
</div>
</div></el-descriptions-item
>
<el-descriptions-item label="暂降次数">{{
infoWindowPoint.eventCount
}}</el-descriptions-item>
</el-descriptions>
</bm-info-window>
</bm-marker>
</baidu-map>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, nextTick } from "vue";
import { substationCount } from "@/api/statistics/index";
import { useStore } from "vuex";
// import { BmlMarkerClusterer } from "vue-baidu-map-3x";
const store = useStore();
const mapRef = ref<any>(null); // 地图容器的ref
const mapInstance: any = ref(null); // 百度地图实例
const BMapInstance: any = ref(null); // BMap对象
const loading = ref(false);
const dataList: any = ref([]);
const polyline = ref<any>([]);
const zoom = ref(11);
const areaLineInfo = ref<any>([]);
const siteList = ref<any>([]);
const infoWindowPoint = ref<any>({
lng: 0,
lat: 0,
show: false,
});
const center = ref({
lng: 120.12,
lat: 31.55,
});
const value = ref("");
const handler = async ({ BMap, map }: any) => {
if (!BMap.MarkerClusterer) {
// await import("/offline/libs/MarkerClusterer_min.js");
}
mapInstance.value = map;
BMapInstance.value = BMap;
// 监听地图容器的鼠标滚轮事件
const mapDom = mapRef.value.$el; // 获取地图DOM元素
mapDom.addEventListener("mousewheel", handleMapWheel, { passive: false });
};
// 点击变电站\监测点
const markerClick = (e: any) => {
zoom.value = 15;
infoWindowPoint.value = e;
infoWindowPoint.value.show = true;
center.value.lng = 116.404367;
center.value.lat = 39.915421;
setTimeout(() => {
center.value.lng = e.lng;
center.value.lat = e.lat + 0.01;
}, 0);
};
const init = () => {
loading.value = true;
siteList.value = [];
polyline.value = [];
dataList.value = [];
areaLineInfo.value = [];
substationCount({
deptId: store.state.deptId,
type: store.state.timeType,
startTime: store.state.timeValue[0],
endTime: store.state.timeValue[1],
}).then((res: any) => {
dataList.value = res.data;
let data = dataList.value;
let r = 0.0035;
let list = data.filter((item: any) => item.latitude != 0);
list.forEach((item: any) => {
// 变电站图标
item.icon = {
url: new URL("@/assets/jcd.png", import.meta.url).href,
size: {
width: 40,
height: 40,
},
};
if (
item.lineEventDetails?.length > 10 &&
item.lineEventDetails?.length < 100
) {
r = 0.0055;
} else if (item.lineEventDetails.length >= 100) {
r = 0.01055;
}
item.lng = item.longitude;
item.lat = item.latitude;
item.lineEventDetails.forEach((val: any, i: number) => {
val.lng =
item.longitude +
r * Math.cos((2 * Math.PI * i) / item.lineEventDetails.length);
val.lat =
item.latitude +
r * Math.sin((2 * Math.PI * i) / item.lineEventDetails.length);
// 监测点图标
val.icon = {
url: "",
size: {
width: 40,
height: 40,
},
};
val.icon.url = new URL("@/assets/txycyzj.gif", import.meta.url).href;
polyline.value.push([
{
lng: item.lng,
lat: item.lat,
},
{
lng: val.lng,
lat: val.lat,
},
]);
});
areaLineInfo.value.push(...item.lineEventDetails);
});
siteList.value = list;
zoom.value = 12;
setTimeout(() => {
loading.value = false;
}, 0);
});
};
const moveenFlag = ref(true);
const checkMapData = () => {
if (!mapInstance.value || !BMapInstance.value || !moveenFlag.value) return;
// 获取地图容器
const container = mapInstance.value.getContainer();
setTimeout(() => {
try {
// 1. 获取所有图片瓦片
const tiles = Array.from(container.querySelectorAll("img"));
// 2. 检查是否有离线地图瓦片
const hasOfflineTiles = tiles.some((tile: any) => {
// 确保tile是有效的DOM元素
if (!tile || !tile.src) return false;
// 检查是否是离线瓦片
return tile.src.includes("/plugin/offline/tiles/");
});
// 3. 如果没有离线瓦片,回到默认位置
if (!hasOfflineTiles) {
console.warn("当前区域无离线地图数据,将返回默认位置");
// 使用正确的BMap.Point创建方式
const point = new BMapInstance.value.Point(116.404367, 39.915421);
// 平滑移动并设置合适缩放级别
mapInstance.value.panTo(point);
mapInstance.value.setZoom(12);
// zoom.value = 12;
}
} catch (error) {
console.error("地图检测出错:", error);
}
}, 1000); // 适当缩短延迟时间
};
// 处理地图滚轮缩放修正scale导致的坐标偏移
const handleMapWheel = (e: WheelEvent) => {
e.preventDefault();
if (!mapInstance.value || !BMapInstance.value) return;
// 1. 获取当前缩放比例假设你通过scale变量控制需替换为你的实际scale值
const scaleWidth: any = window.sessionStorage.getItem("scaleWidth"); // 你的水平缩放比例
const scaleHeight: any = window.sessionStorage.getItem("scaleheight"); // 你的垂直缩放比例
// 2. 获取地图容器的位置和尺寸原始DOM尺寸未被scale影响
const rect = mapRef.value.$el.getBoundingClientRect();
// 3. 计算鼠标在地图容器内的原始坐标(未修正)
const mouseXRaw = e.clientX - rect.left;
const mouseYRaw = e.clientY - rect.top;
// 4. 修正坐标除以缩放比例得到scale前的原始坐标地图实际识别的坐标
const mouseX = mouseXRaw / scaleWidth;
const mouseY = mouseYRaw / scaleHeight;
// 5. 将修正后的坐标转换为百度地图的经纬度
const point = new BMapInstance.value.Pixel(mouseX, mouseY);
const lngLat = mapInstance.value.pixelToPoint(point); // 像素坐标转经纬度
// 6. 执行缩放(滚轮向上放大,向下缩小)
const zoomDelta = e.deltaY < 0 ? 1 : -1; // 滚轮方向
const newZoom = mapInstance.value.getZoom() + zoomDelta;
if (newZoom < 10 || newZoom > 15) return; // 限制缩放范围(百度地图默认范围)
// 7. 缩放时保持鼠标指向的位置不变(核心:先缩放再移中心点)
mapInstance.value.setZoom(newZoom);
mapInstance.value.setCenter(lngLat); // 让鼠标指向的位置成为新中心
};
const syncCenterAndZoom = (e: any) => {
zoom.value = e.target.getZoom();
checkMapData();
};
//点击过滤位置
const setIcon = (e: string) => {
moveenFlag.value = false;
siteList.value.forEach((item: any) => {
if (item.stationName == e) {
// center.value.lng = item.lng;
// center.value.lat = item.lat + 0.01;
infoWindowPoint.value = item;
infoWindowPoint.value.show = true;
zoom.value = 15;
}
});
setTimeout(() => {
moveenFlag.value = true;
}, 1500);
};
defineExpose({
init,
setIcon,
});
</script>
<style lang="scss" scoped>
@use "@/assets/scss/index.scss";
.flex {
display: flex;
justify-content: center;
align-items: center;
height: 600px;
}
.bm-view {
width: 99%;
height: 980px;
}
.iconBox {
position: absolute;
bottom: 10px;
left: 10px;
z-index: 2000;
width: 110px;
height: 70px;
padding: 10px;
background: #ffffff10 !important;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
font-size: 12px;
.div {
display: flex;
margin-bottom: 5px;
img {
height: 20px;
margin-right: 5px;
}
}
}
:deep(.el-descriptions__title) {
color: #fff;
}
:deep(.el-descriptions__content) {
color: #fff;
// width: 100%;
}
:deep(.el-descriptions__body) {
color: #fff;
background-color: rgb(0 0 0 / 0%) !important;
}
:deep(.el-descriptions__label) {
color: #fff;
// display: inline-block;
width: 80px;
text-align: right; /* 右对齐 */
}
:deep(.BMap_pop .BMap_center) {
background-color: #343849c7;
}
.descriptionsBox {
max-height: 100px;
overflow-y: auto;
}
</style>
<style>
.BMap_cpyCtrl {
display: none;
}
.anchorBL {
display: none;
} /* 地图容器样式 */
.baidu-map-container {
/* 消除可能的缝隙 */
line-height: 0;
font-size: 0;
}
/* .BMap_pop div {
background-color: #343849c7 !important;
} */
.bmSelect {
position: absolute;
right: 10px;
top: 10px;
z-index: 2000;
}
.bm-view {
border: 0;
background-color: transparent;
}
</style>

View File

@@ -1,14 +1,10 @@
<template> <template>
<div class="plan"> <div class="plan">
<!-- src=" http://192.168.1.128:3001/zutai/?id=44368e72a2e594d14ebaf317f0f6ad00&&name=decodeURI(APP测试项目)&&flag=true&&wxqr=true#/" --> <bdMap v-show="store.state.showMap" width="100%" height="100%"></bdMap>
<!-- <iframe
src="http://192.168.1.62:8088/zutai/?id=0944fe372e90daeefd040916a105ac8b&&name=测试组态编辑器&&preview=true#/preview"
width="100%"
height="100%"
frameborder="0"
></iframe> -->
<!-- 添加加载事件监听 --> <!-- 添加加载事件监听 -->
<iframe <iframe
v-if="!store.state.showMap"
:src="iframeSrc" :src="iframeSrc"
width="100%" width="100%"
height="100%" height="100%"
@@ -23,6 +19,7 @@
import { ref, watch, onMounted, onUnmounted } from "vue"; import { ref, watch, onMounted, onUnmounted } from "vue";
import { getActive } from "@/api/manage_wx/index"; import { getActive } from "@/api/manage_wx/index";
import { useStore } from "vuex"; import { useStore } from "vuex";
import bdMap from "./bdMap.vue";
const store = useStore(); const store = useStore();
const props = defineProps<{ const props = defineProps<{
project: { id: string; name: string } | null; project: { id: string; name: string } | null;
@@ -50,6 +47,7 @@ watch(
); );
onMounted(() => { onMounted(() => {
// store.dispatch("setStateKey", { key: "showMap", value: false });
// 监听来自 eventStatistics 组件的消息 // 监听来自 eventStatistics 组件的消息
window.addEventListener("message", handleMessage); window.addEventListener("message", handleMessage);
getActive({}).then((res: any) => { getActive({}).then((res: any) => {
@@ -65,12 +63,13 @@ onMounted(() => {
}); });
// 子页面 // 子页面
const iframe = document.getElementById("iframeLeft"); const iframe = document.getElementById("iframeLeft");
if (iframe) {
// 监听 iframe 加载完成事件 // 监听 iframe 加载完成事件
iframe.addEventListener("load", function () { iframe.addEventListener("load", function () {
// 通知父页面:我已加载完毕 // 通知父页面:我已加载完毕
store.dispatch("setIframeLoad", true); store.dispatch("setStateKey", { key: "iframeLoad", value: true });
}); });
}
}); });
onUnmounted(() => { onUnmounted(() => {