ly-front/src/views/mapControl/index.vue

495 lines
14 KiB
Vue
Raw Normal View History

2025-03-31 15:26:29 +00:00
<template>
2025-06-16 14:22:14 +00:00
<div class="HomeMap">
2025-06-19 15:41:07 +00:00
<div class="zoom-level" @click="soundAndMenu">
<img :src="zoomLevelImage" alt="" />
</div>
2025-06-22 09:42:09 +00:00
<div class="btn-container" v-if="closeBtnVisible">
<el-button @click="handleClickClose()">返回</el-button>
</div>
2025-06-16 14:22:14 +00:00
<div id="map" ref="olMap" class="map-container"></div>
2025-06-22 09:42:09 +00:00
<div class="pointingNorth">
<img src="@/assets/img/user.png" alt="" @click="userOpen" />
<img src="@/assets/img/setup.png" alt="" @click="setupOpen" />
<img src="@/assets/img/query.png" alt="" @click="queryOpen" />
2025-06-19 15:41:07 +00:00
</div>
<div class="configuration">
<div class="refresh" @click="refreshClick">
2025-06-19 15:41:07 +00:00
<img src="@/assets/img/refresh.png" alt="" />
</div>
<div class="refresh" @click="positionClick">
2025-06-19 15:41:07 +00:00
<img src="@/assets/img/icon_postions.png" alt="" />
</div>
<div class="refresh" @click="modelClick">
2025-06-19 15:41:07 +00:00
<img src="@/assets/img/icon_model.png" alt="" />
</div>
2025-04-13 08:23:44 +00:00
</div>
2025-06-22 09:42:09 +00:00
<el-dialog
title="设置"
:visible.sync="dialogVisible"
width="50%"
:before-close="handleClose"
append-to-body
class="custom-class"
>
<div class="block">
<span class="demonstration">预警声音设置</span>
<el-slider v-model="value2"></el-slider>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="determine"> </el-button>
</span>
</el-dialog>
<el-dialog
title="查询"
:visible.sync="dialogQueryVisible"
width="90%"
:before-close="handleClose"
append-to-body
class="custom-table"
>
<AlertDialog />
</el-dialog>
2025-03-31 15:26:29 +00:00
</div>
</template>
<script>
import { mapGetters } from "vuex";
2025-06-16 14:22:14 +00:00
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import { fromLonLat } from "ol/proj";
import XYZ from "ol/source/XYZ";
2025-06-22 09:42:09 +00:00
import { Feature, Overlay } from "ol";
import { Point, MultiPolygon } from "ol/geom";
import { Vector as VectorLayer } from "ol/layer";
import { Vector as VectorSource } from "ol/source";
import { Style, Fill, Stroke, Circle } from "ol/style";
import GeoJSON from "ol/format/GeoJSON";
import AlertDialog from "../menuData/AlertDialog.vue";
import { devPositionList } from "@/api/position.js";
2025-03-31 15:26:29 +00:00
export default {
name: "HomeMap",
2025-06-22 09:42:09 +00:00
components: {
AlertDialog
},
2025-03-31 15:26:29 +00:00
data() {
return {
2025-06-16 14:22:14 +00:00
map: null,
2025-06-19 15:41:07 +00:00
zoomLevel: null,
isZoomedIn: true,
isZoomedOut: false,
2025-06-22 09:42:09 +00:00
showPermissionPrompt: false,
dialogVisible: false,
value2: 0,
currentBaseMap: "gaode",
dialogQueryVisible: false,
closeBtnVisible: false
2025-03-31 15:26:29 +00:00
};
},
created() {
2025-06-16 14:22:14 +00:00
// 清空控制台(可选)
2025-03-31 15:26:29 +00:00
// console.clear();
},
mounted() {
2025-06-16 14:22:14 +00:00
this.initMap();
2025-03-31 15:26:29 +00:00
},
computed: {
2025-06-22 09:42:09 +00:00
...mapGetters(["statePage", "cityCode", "checkFlag"]),
2025-06-19 15:41:07 +00:00
zoomLevelImage() {
return this.isZoomedIn
? require("@/assets/img/sound.png")
: require("@/assets/img/mute.png");
}
2025-03-31 15:26:29 +00:00
},
2025-06-22 09:42:09 +00:00
watch: {
checkFlag: {
handler(newVal) {
this.closeBtnVisible = newVal;
this.dialogQueryVisible = false;
},
deep: true
}
},
2025-03-31 15:26:29 +00:00
methods: {
2025-06-22 09:42:09 +00:00
handleClickClose() {
this.$store.commit("SET_CHECKFLAG", false);
this.$store.commit("SET_CLEARTRAJECTORY", true);
},
initPositon() {
let positionIds = {
pageNum: 1,
pageSize: 10000
};
devPositionList(positionIds).then((devRes) => {
if (devRes.code === 0) {
let positionRes = localStorage.getItem("PositionIds");
this.positionList = devRes.data.items;
const matchedList = this.positionList.filter((item) =>
positionRes.includes(item.id)
);
this.loadGeoJson(matchedList);
}
});
},
// 加载 GeoJSON 数据并绘制
loadGeoJson(value) {
// 清空已有图层
this.clearLayer();
// 创建矢量源
const source = new VectorSource();
// 解析 GeoJSON 数据
const geojsonFormat = new GeoJSON();
value.forEach((item) => {
// 解析 regionJson 为 OpenLayers 特征
const feature = geojsonFormat.readFeature(JSON.parse(item.regionJson), {
dataProjection: "EPSG:4326", // 输入数据为 WGS84
featureProjection: "EPSG:3857" // 转换为地图投影
});
// 设置多边形样式:透明蓝色填充,蓝色边框
feature.setStyle(
new Style({
fill: new Fill({
color: "rgba(0, 0, 255, 0.3)" // 透明蓝色填充
}),
stroke: new Stroke({
color: "#0000FF", // 蓝色边框
width: 2
})
})
);
// 添加多边形到源
source.addFeature(feature);
// 添加中心点
const centerFeature = new Feature({
geometry: new Point(fromLonLat([item.lon, item.lat]))
});
// 设置中心点样式:红色圆点
centerFeature.setStyle(
new Style({
image: new Circle({
radius: 6,
fill: new Fill({
color: "#FF0000" // 红色填充
}),
stroke: new Stroke({
color: "#FFFFFF", // 白色边框
width: 1
})
})
})
);
// 添加中心点到源
source.addFeature(centerFeature);
});
// 创建矢量图层
this.vectorLayer = new VectorLayer({
source: source,
zIndex: 10 // 设置图层优先级
});
// 添加到父组件的地图
window.olMap.addLayer(this.vectorLayer);
// 跳转到 GeoJSON 数据区域
if (source.getFeatures().length > 0) {
const extent = source.getExtent(); // 获取所有特征的边界框
window.olMap.getView().fit(extent, {
padding: [50, 50, 50, 50], // 四周留白(像素)
maxZoom: 15, // 最大缩放级别
duration: 500 // 动画持续时间(毫秒)
});
}
},
// 清空图层
clearLayer() {
if (this.vectorLayer) {
window.olMap.removeLayer(this.vectorLayer);
this.vectorLayer = null;
}
},
refreshClick() {
// 刷新页面
window.location.reload();
},
positionClick() {
console.log("位置点击");
this.$store.commit("SET_POSITIONPOINT", true);
},
modelClick() {
// 切换底图
const newBaseMap = this.currentBaseMap === "gaode" ? "tianditu" : "gaode";
this.map.getLayers().forEach((layer) => {
const name = layer.get("name");
if (name === "gaode" || name === "tianditu") {
layer.setVisible(name === newBaseMap);
}
});
this.currentBaseMap = newBaseMap;
},
2025-06-19 15:41:07 +00:00
soundAndMenu() {
this.isZoomedIn = !this.isZoomedIn;
this.$store.commit("SET_ISZOOMEDIN", this.isZoomedIn);
2025-06-19 15:41:07 +00:00
},
2025-06-16 14:22:14 +00:00
initMap() {
// 初始化 OpenLayers 地图
this.map = new Map({
target: this.$refs.olMap,
layers: [
new TileLayer({
name: "gaode", // 图层名称
2025-06-16 14:22:14 +00:00
visible: true,
source: new XYZ({
visible: true,
url: "http://webrd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=2&scale=1&style=8"
})
}),
new TileLayer({
2025-06-22 09:42:09 +00:00
name: "tianditu", // 图层名称
visible: false,
source: new XYZ({
2025-06-22 09:42:09 +00:00
url: "http://webrd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=7",
layer: "img",
style: "default",
matrixSet: "c",
format: "tiles",
tileLoadFunction: function (imageTile, src) {
// 使用滤镜 将白色修改为深色
const img = new Image();
// img.crossOrigin = ''
// 设置图片不从缓存取,从缓存取可能会出现跨域,导致加载失败
img.setAttribute("crossOrigin", "anonymous");
img.onload = function () {
const canvas = document.createElement("canvas");
const w = img.width;
const h = img.height;
canvas.width = w;
canvas.height = h;
const context = canvas.getContext("2d");
context.filter =
"grayscale(98%) invert(100%) sepia(20%) hue-rotate(180deg) saturate(1600%) brightness(80%) contrast(90%)";
context.drawImage(img, 0, 0, w, h, 0, 0, w, h);
imageTile.getImage().src = canvas.toDataURL("image/png");
};
img.src = src;
}
}),
2025-06-22 09:42:09 +00:00
format: new GeoJSON()
2025-06-16 14:22:14 +00:00
})
],
view: new View({
projection: "EPSG:3857",
center: fromLonLat([117.337103, 39.040924]), // 经纬度转投影坐标
2025-06-16 14:22:14 +00:00
zoom: 10,
minZoom: 3,
maxZoom: 18,
constrainRotation: false // 允许旋转
})
2025-03-31 15:26:29 +00:00
});
2025-06-16 14:22:14 +00:00
// 保存地图实例到全局(与 Mars3D 的 window.marsMap 类似)
window.olMap = this.map;
// 处理地图加载完成后的逻辑
this.onMapLoad(this.map);
// 绑定点击事件
this.map.on("click", this.mapClickHandler);
// 绑定缩放/移动结束事件
this.map.getView().on("change:resolution", this.mapZoomHandler);
this.map.getView().on("change:center", this.mapZoomHandler);
2025-03-31 15:26:29 +00:00
},
2025-06-16 14:22:14 +00:00
mapClickHandler(event) {
// 处理地图点击事件
console.log("地图点击:", event);
2025-04-01 16:12:21 +00:00
},
2025-06-16 14:22:14 +00:00
mapZoomHandler() {
// 获取当前缩放等级
this.zoomLevel = Math.round(this.map.getView().getZoom());
2025-04-01 16:12:21 +00:00
},
2025-03-31 15:26:29 +00:00
onMapLoad(map) {
2025-06-24 16:28:53 +00:00
setTimeout(() => {
this.initPositon();
}, 2000);
2025-06-16 14:22:14 +00:00
// 地图加载完成后的处理
const camera = window.mapConfig || {};
2025-04-12 15:15:32 +00:00
if (camera.isCamera) {
2025-06-16 14:22:14 +00:00
// 设置视角约束
map
.getView()
.setCenter(
fromLonLat([camera.cameraLon || 0, camera.cameraLat || 0])
);
map.getView().setMinZoom(3); // 最小缩放等级
map.getView().setMaxZoom(18); // 最大缩放等级
2025-04-12 15:15:32 +00:00
}
2025-04-13 08:23:44 +00:00
},
2025-06-22 09:42:09 +00:00
handleClose() {
this.dialogVisible = false;
this.dialogQueryVisible = false;
},
userOpen() {
this.$confirm("是否退出登录?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$router.push({ name: "login" });
localStorage.removeItem("setToken");
localStorage.removeItem("expires");
localStorage.removeItem("userId");
localStorage.removeItem("isAdmin");
localStorage.removeItem("PositionIds");
})
.catch(() => {
this.$message({
type: "info",
message: "已取消退出登录"
});
});
},
setupOpen() {
this.dialogVisible = true;
const media = this.$refs.uavAudio;
const savedVolume = localStorage.getItem("soundValue");
if (savedVolume !== null) {
this.value2 = Number(savedVolume);
} else {
this.value2 = media.volume * 100;
}
localStorage.setItem("soundValue", this.value2);
},
determine() {
// 确定时保存音量到 localStorage
localStorage.setItem("soundValue", this.value2);
this.$store.commit("SET_SILDERVALUE", this.value2);
this.dialogVisible = false; // 可选:关闭弹窗
},
queryOpen() {
this.dialogQueryVisible = true;
},
headdenForm(value, type) {
let params = {};
if (type === "search") {
params = JSON.parse(JSON.stringify(this.$refs.myForm.ruleForm));
if (params.dateRange && params.dateRange.length === 2) {
params.strartDate = moment(params.dateRange[0]).format("YYYY-MM-DD");
params.endDate = moment(params.dateRange[1]).format("YYYY-MM-DD");
}
this.$delete(params, "dateRange");
params.pageNum = this.paginationParam.currentPage;
params.pageSize = this.paginationParam.size;
alarmList(params).then((res) => {
if (res.code === 0) {
this.tableData = res.data.items.map((item) => {
const { isWhitelist, duration, frequency, ...rest } = item;
return {
...rest,
isattacked: item.isattacked ? "是" : "否",
duration: String(item.duration),
frequency: String(item.frequency)
};
});
this.paginationParam.total = res.data.total;
}
});
}
},
handleSizeChange(value) {
console.log(value);
this.paginationParam.size = value;
this.headdenForm({}, "search");
},
handlePageChange(value) {
console.log(value);
this.paginationParam.currentPage = value;
this.headdenForm({}, "search");
2025-03-31 15:26:29 +00:00
}
}
};
</script>
2025-06-16 14:22:14 +00:00
2025-03-31 15:26:29 +00:00
<style scoped lang="scss">
2025-06-16 14:22:14 +00:00
.HomeMap {
2025-03-31 15:26:29 +00:00
width: 100%;
height: 100%;
2025-06-16 14:22:14 +00:00
position: relative;
2025-04-01 16:12:21 +00:00
.zoom-level {
position: absolute;
2025-06-19 15:41:07 +00:00
top: 60px;
right: 40px;
2025-06-16 14:22:14 +00:00
z-index: 1000;
2025-04-01 16:12:21 +00:00
color: #fff;
2025-06-19 15:41:07 +00:00
img {
width: 48px;
height: 48px;
}
}
2025-06-22 09:42:09 +00:00
.btn-container {
position: absolute;
top: 70px;
right: 100px;
z-index: 1000;
color: #fff;
}
2025-06-19 15:41:07 +00:00
.configuration {
position: absolute;
bottom: 32px;
right: 40px;
z-index: 1000;
color: #fff;
2025-06-24 15:33:44 +00:00
width: 50px;
height: 200px;
2025-06-19 15:41:07 +00:00
background-color: rgba(209, 242, 255, 0.9);
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
.refresh {
2025-06-24 15:33:44 +00:00
width: 100%;
height: 30%;
line-height: 60px;
2025-06-19 15:41:07 +00:00
img {
width: 32px;
height: 32px;
2025-06-24 15:33:44 +00:00
margin-top: 30%;
2025-06-19 15:41:07 +00:00
}
}
2025-04-01 16:12:21 +00:00
}
2025-03-31 15:26:29 +00:00
2025-06-16 14:22:14 +00:00
.map-container {
2025-03-31 15:26:29 +00:00
width: 100%;
height: 100%;
}
2025-06-16 14:22:14 +00:00
2025-04-13 08:23:44 +00:00
.pointingNorth {
2025-06-19 15:41:07 +00:00
width: 236px;
height: 52px;
2025-04-13 08:23:44 +00:00
position: absolute;
2025-06-19 15:41:07 +00:00
bottom: 37px;
left: 50%;
transform: translateX(-50%);
2025-06-16 14:22:14 +00:00
z-index: 1000;
2025-06-19 15:41:07 +00:00
display: flex;
align-items: center;
justify-content: space-between;
2025-04-24 12:55:18 +00:00
img {
2025-06-19 15:41:07 +00:00
width: 52px;
height: 52px;
2025-04-24 12:55:18 +00:00
}
2025-04-13 08:23:44 +00:00
}
2025-03-31 15:26:29 +00:00
}
</style>