electripper-v2-ui/src/views/bst/areaSub/components/AreaMap.vue

323 lines
8.8 KiB
Vue
Raw Normal View History

2025-03-15 18:38:27 +08:00
<template>
<div class="map-container">
<!-- 地图容器 -->
<div id="container"></div>
2025-04-26 18:41:39 +08:00
<!-- 地址搜索组件 -->
<address-search :map="map" :AMap="AMap" v-if="map && enableEdit" />
2025-03-15 18:38:27 +08:00
<!-- 地图工具栏 -->
<div class="map-tools">
<el-button-group>
2025-04-28 16:34:03 +08:00
<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>
2025-03-15 18:38:27 +08:00
<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>
2025-04-26 18:41:39 +08:00
<el-button size="mini" icon="el-icon-document" @click="toggleLabels">{{ showLabels ? '隐藏' : '显示' }}标签</el-button>
2025-04-08 16:18:38 +08:00
<el-button size="mini" icon="el-icon-video-play" v-if="locationLogList && locationLogList.length > 0" @click="togglePlaybackPanel">{{ isPlaybackVisible ? '隐藏' : '显示' }}轨迹控制台</el-button>
2025-03-15 18:38:27 +08:00
</el-button-group>
</div>
2025-04-03 20:54:17 +08:00
<!-- 轨迹回放控制面板 -->
2025-04-10 19:56:40 +08:00
<playback-panel
2025-04-11 15:35:32 +08:00
v-show="isPlaybackVisible && locationLogList.length > 0"
2025-04-10 19:56:40 +08:00
:current-log="currentLog"
:current-speed="currentSpeed"
:max-index="sortedLocationLogs.length - 1"
:is-playing="isPlaying"
:playback-speed="playbackSpeed"
2025-04-11 15:35:32 +08:00
:current-index="currentLogIndex"
2025-04-10 19:56:40 +08:00
@slider-change="handleSliderChange"
@toggle-play="togglePlay"
@speed-change="setPlaybackSpeed"
/>
2025-04-03 20:54:17 +08:00
2025-03-15 18:38:27 +08:00
<!-- 编辑工具栏 -->
<div class="boundary-tools" v-if="isEditing">
2025-04-10 14:07:21 +08:00
<el-alert type="warning" :closable="false">
2025-03-15 18:38:27 +08:00
<template slot="title">
<div>{{ editSubArea != null ? '编辑' : '新增' }}提示</div>
<div>1. 左键点击添加边界点</div>
2025-03-17 14:35:07 +08:00
<div>2. 点击顶点可删除该点</div>
<div>3. 双击完成绘制</div>
2025-03-15 18:38:27 +08:00
</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";
2025-04-26 18:41:39 +08:00
import { mapMethods } from "@/views/bst/areaSub/components/AreaMap";
2025-04-11 15:35:32 +08:00
import PlaybackPanel from '@/views/bst/areaSub/components/PlaybackPanel.vue';
2025-04-26 18:41:39 +08:00
import AddressSearch from '@/views/bst/areaSub/components/AddressSearch.vue';
import OperationArea from './mixins/OperationArea';
import SubArea from './mixins/SubArea';
import LocationLog from './mixins/LocationLog';
2025-03-15 18:38:27 +08:00
export default {
name: 'AreaMap',
2025-04-10 19:56:40 +08:00
components: {
2025-04-26 18:41:39 +08:00
PlaybackPanel,
AddressSearch
2025-04-10 19:56:40 +08:00
},
2025-04-26 18:41:39 +08:00
mixins: [OperationArea, SubArea, LocationLog],
2025-04-03 20:54:17 +08:00
dicts: ['device_status', 'device_lock_status', 'device_quality'],
2025-03-15 18:38:27 +08:00
props: {
// 运营区域信息
area: {
type: Object,
2025-04-03 20:54:17 +08:00
default: () => {}
2025-03-15 18:38:27 +08:00
},
// 子区域列表
areaSubList: {
type: Array,
default: () => []
2025-04-03 20:54:17 +08:00
},
// 定位日志列表
locationLogList: {
type: Array,
default: () => []
},
// 开启编辑
enableEdit: {
type: Boolean,
default: true
2025-03-15 18:38:27 +08:00
}
},
data() {
return {
map: null,
AMap: null,
mapStyle: 'normal',
showLabels: true,
isEditing: false,
editingPolygon: null,
isEditingArea: false,
2025-04-26 18:41:39 +08:00
polygonEditor: null
2025-03-15 18:38:27 +08:00
};
},
async mounted() {
await this.initMap();
this.renderAll();
},
beforeDestroy() {
2025-04-03 20:54:17 +08:00
this.pausePlayback();
2025-04-12 18:00:14 +08:00
if (this.currentPolylines.length > 0) {
this.currentPolylines.forEach(polyline => {
this.map.remove(polyline);
});
2025-04-03 20:54:17 +08:00
}
if (this.currentDeviceMarker) {
if (this.currentDeviceMarker.snLabel) {
this.map.remove(this.currentDeviceMarker.snLabel);
}
this.map.remove(this.currentDeviceMarker);
}
2025-03-15 18:38:27 +08:00
this.destroyMap();
},
methods: {
2025-04-26 18:41:39 +08:00
...mapMethods,
2025-03-15 18:38:27 +08:00
// 初始化地图
async initMap() {
try {
this.AMap = await AMapLoader.load({
key: globalConfig.aMap.key,
version: '2.0',
2025-04-26 18:41:39 +08:00
plugins: [
'AMap.PolygonEditor',
'AMap.InfoWindow',
'AMap.AutoComplete',
'AMap.PlaceSearch'
],
2025-03-15 18:38:27 +08:00
});
// 使用 area 的经纬度作为中心点
const center = [
2025-04-03 20:54:17 +08:00
this.area?.longitude || 120.35218, // 如果没有经度,使用默认值
this.area?.latitude || 26.944335 // 如果没有纬度,使用默认值
2025-03-15 18:38:27 +08:00
];
2025-04-12 18:00:14 +08:00
2025-03-15 18:38:27 +08:00
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);
2025-04-12 18:00:14 +08:00
2025-03-15 18:38:27 +08:00
// 绑定编辑器事件
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();
2025-04-03 20:54:17 +08:00
this.initPlayback();
2025-03-15 18:38:27 +08:00
},
// 清除编辑器
clearEditor() {
// 关闭编辑器
if (this.polygonEditor) {
// 清除编辑器的目标对象
this.polygonEditor.setTarget();
}
// 移除编辑中的多边形
if (this.editingPolygon) {
this.map.remove(this.editingPolygon);
2025-04-26 18:41:39 +08:00
this.editingPolygon = null
2025-03-15 18:38:27 +08:00
}
},
// 保存边界
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);
2025-04-12 18:00:14 +08:00
2025-03-15 18:38:27 +08:00
if (!center) {
this.$message.error('计算中心点失败');
return;
}
if (this.isEditingArea) {
// 更新运营区边界
this.$emit('update-area-boundary', {
2025-03-17 14:35:07 +08:00
area: this.area,
2025-03-15 18:38:27 +08:00
boundary: JSON.stringify(boundary),
longitude: center[0],
latitude: center[1]
});
} else {
2025-03-17 14:35:07 +08:00
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]
});
}
2025-03-15 18:38:27 +08:00
}
},
// 取消编辑
cancelEdit() {
// 重置编辑状态
this.isEditing = false;
this.isEditingArea = false;
2025-04-12 18:00:14 +08:00
2025-03-15 18:38:27 +08:00
// 移除地图事件监听
this.map.clearEvents('click');
this.map.clearEvents('rightclick');
// 重新渲染所有区域,确保显示正常
2025-04-26 18:41:39 +08:00
this.clearEditor();
if (this.isEditingArea) {
this.renderAreaBoundary();
} else {
this.renderSubAreas();
}
2025-03-15 18:38:27 +08:00
},
// 工具方法
setFitView() {
2025-03-17 14:35:07 +08:00
this.map.setFitView();
2025-03-15 18:38:27 +08:00
},
// 切换地图样式
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;
}
}
}
2025-04-03 20:54:17 +08:00
</style>
<style lang="scss">
@import "@/views/bst/areaSub/components/AreaMap.scss";
2025-03-15 18:38:27 +08:00
</style>