323 lines
8.8 KiB
Vue
323 lines
8.8 KiB
Vue
<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>
|