electripper-v2-ui/src/views/bst/areaSub/components/AreaMap.vue
2025-04-28 16:34:03 +08:00

323 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="map-container">
<!-- 地图容器 -->
<div id="container"></div>
<!-- 地址搜索组件 -->
<address-search :map="map" :AMap="AMap" v-if="map && enableEdit" />
<!-- 地图工具栏 -->
<div class="map-tools">
<el-button-group>
<el-button size="mini" id="add-sub" type="primary" icon="el-icon-plus" @click="startBoundaryEdit(null)" v-if="enableEdit" :disabled="isEditing">子区域</el-button>
<el-button size="mini" id="area-edit" type="warning" icon="el-icon-edit" @click="startAreaBoundaryEdit" v-if="enableEdit" :disabled="isEditing">电子围栏</el-button>
<el-button size="mini" icon="el-icon-full-screen" @click="setFitView">全局查看</el-button>
<el-button size="mini" icon="el-icon-picture" @click="toggleMapStyle">切换样式</el-button>
<el-button size="mini" icon="el-icon-document" @click="toggleLabels">{{ showLabels ? '隐藏' : '显示' }}标签</el-button>
<el-button size="mini" icon="el-icon-video-play" v-if="locationLogList && locationLogList.length > 0" @click="togglePlaybackPanel">{{ isPlaybackVisible ? '隐藏' : '显示' }}轨迹控制台</el-button>
</el-button-group>
</div>
<!-- 轨迹回放控制面板 -->
<playback-panel
v-show="isPlaybackVisible && locationLogList.length > 0"
:current-log="currentLog"
:current-speed="currentSpeed"
:max-index="sortedLocationLogs.length - 1"
:is-playing="isPlaying"
:playback-speed="playbackSpeed"
:current-index="currentLogIndex"
@slider-change="handleSliderChange"
@toggle-play="togglePlay"
@speed-change="setPlaybackSpeed"
/>
<!-- 编辑工具栏 -->
<div class="boundary-tools" v-if="isEditing">
<el-alert type="warning" :closable="false">
<template slot="title">
<div>{{ editSubArea != null ? '编辑' : '新增' }}提示</div>
<div>1. 左键点击添加边界点</div>
<div>2. 点击顶点可删除该点</div>
<div>3. 双击完成绘制</div>
</template>
</el-alert>
<div class="tool-buttons">
<el-button type="success" size="mini" @click="saveBoundary">保存</el-button>
<el-button size="mini" @click="cancelEdit">取消</el-button>
</div>
</div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import globalConfig from "@/utils/config/globalConfig";
import { mapMethods } from "@/views/bst/areaSub/components/AreaMap";
import PlaybackPanel from '@/views/bst/areaSub/components/PlaybackPanel.vue';
import AddressSearch from '@/views/bst/areaSub/components/AddressSearch.vue';
import OperationArea from './mixins/OperationArea';
import SubArea from './mixins/SubArea';
import LocationLog from './mixins/LocationLog';
export default {
name: 'AreaMap',
components: {
PlaybackPanel,
AddressSearch
},
mixins: [OperationArea, SubArea, LocationLog],
dicts: ['device_status', 'device_lock_status', 'device_quality'],
props: {
// 运营区域信息
area: {
type: Object,
default: () => {}
},
// 子区域列表
areaSubList: {
type: Array,
default: () => []
},
// 定位日志列表
locationLogList: {
type: Array,
default: () => []
},
// 开启编辑
enableEdit: {
type: Boolean,
default: true
}
},
data() {
return {
map: null,
AMap: null,
mapStyle: 'normal',
showLabels: true,
isEditing: false,
editingPolygon: null,
isEditingArea: false,
polygonEditor: null
};
},
async mounted() {
await this.initMap();
this.renderAll();
},
beforeDestroy() {
this.pausePlayback();
if (this.currentPolylines.length > 0) {
this.currentPolylines.forEach(polyline => {
this.map.remove(polyline);
});
}
if (this.currentDeviceMarker) {
if (this.currentDeviceMarker.snLabel) {
this.map.remove(this.currentDeviceMarker.snLabel);
}
this.map.remove(this.currentDeviceMarker);
}
this.destroyMap();
},
methods: {
...mapMethods,
// 初始化地图
async initMap() {
try {
this.AMap = await AMapLoader.load({
key: globalConfig.aMap.key,
version: '2.0',
plugins: [
'AMap.PolygonEditor',
'AMap.InfoWindow',
'AMap.AutoComplete',
'AMap.PlaceSearch'
],
});
// 使用 area 的经纬度作为中心点
const center = [
this.area?.longitude || 120.35218, // 如果没有经度,使用默认值
this.area?.latitude || 26.944335 // 如果没有纬度,使用默认值
];
this.map = new this.AMap.Map('container', {
zoom: 13,
center: center,
touchZoom: false,
dragEnable: true,
contextMenu: true
});
// 初始化多边形编辑器
this.polygonEditor = new this.AMap.PolygonEditor(this.map);
// 绑定编辑器事件
this.polygonEditor.on('add', this.handlePolygonAdd);
this.polygonEditor.on('end', this.handlePolygonEnd);
} catch (error) {
console.error('地图初始化失败:', error);
}
},
handlePolygonAdd({target}) {
this.editingPolygon = target;
},
handlePolygonEnd({target}) {
this.editingPolygon = target;
},
// 渲染所有内容
renderAll() {
this.clearEditor();
this.renderAreaBoundary();
this.renderSubAreas();
this.initPlayback();
},
// 清除编辑器
clearEditor() {
// 关闭编辑器
if (this.polygonEditor) {
// 清除编辑器的目标对象
this.polygonEditor.setTarget();
}
// 移除编辑中的多边形
if (this.editingPolygon) {
this.map.remove(this.editingPolygon);
this.editingPolygon = null
}
},
// 保存边界
saveBoundary() {
if (!this.editingPolygon) {
this.$message.warning('请先绘制边界');
return;
}
const path = this.editingPolygon.getPath();
const boundary = path.map(point => [point.getLng(), point.getLat()]);
const center = this.calculateCenter(boundary);
if (!center) {
this.$message.error('计算中心点失败');
return;
}
if (this.isEditingArea) {
// 更新运营区边界
this.$emit('update-area-boundary', {
area: this.area,
boundary: JSON.stringify(boundary),
longitude: center[0],
latitude: center[1]
});
} else {
if (this.editSubArea == null) {
// 新增子区域
this.$emit('add-boundary', {
boundary: JSON.stringify(boundary),
longitude: center[0],
latitude: center[1]
});
} else {
// 更新子区域边界
this.$emit('update-boundary', {
areaSub: this.editSubArea,
boundary: JSON.stringify(boundary),
longitude: center[0],
latitude: center[1]
});
}
}
},
// 取消编辑
cancelEdit() {
// 重置编辑状态
this.isEditing = false;
this.isEditingArea = false;
// 移除地图事件监听
this.map.clearEvents('click');
this.map.clearEvents('rightclick');
// 重新渲染所有区域,确保显示正常
this.clearEditor();
if (this.isEditingArea) {
this.renderAreaBoundary();
} else {
this.renderSubAreas();
}
},
// 工具方法
setFitView() {
this.map.setFitView();
},
// 切换地图样式
toggleMapStyle() {
if (this.mapStyle === 'normal') {
// 切换到卫星图
this.mapStyle = 'satellite';
const satelliteLayer = new this.AMap.TileLayer.Satellite();
const roadNetLayer = new this.AMap.TileLayer.RoadNet();
this.map.setLayers([satelliteLayer, roadNetLayer]);
} else {
// 切换到普通地图
this.mapStyle = 'normal';
const normalLayer = new this.AMap.TileLayer();
this.map.setLayers([normalLayer]);
}
},
destroyMap() {
if (this.map) {
this.clearHighlight();
this.clearOverlays();
this.map.destroy();
this.map = null;
}
},
}
};
</script>
<style lang="scss" scoped>
.map-container {
position: relative;
width: 100%;
height: 100%;
#container {
width: 100%;
height: 100%;
}
.map-tools {
position: absolute;
top: 15px;
right: 15px;
}
.boundary-tools {
position: absolute;
top: 15px;
left: 15px;
z-index: 100;
width: 200px;
.tool-buttons {
margin-top: 10px;
text-align: left;
}
}
}
</style>
<style lang="scss">
@import "@/views/bst/areaSub/components/AreaMap.scss";
</style>