824 lines
22 KiB
Vue
824 lines
22 KiB
Vue
![]() |
<template>
|
|||
|
<div class="map-container">
|
|||
|
<!-- 地图容器 -->
|
|||
|
<div id="container"></div>
|
|||
|
|
|||
|
<!-- 地图工具栏 -->
|
|||
|
<div class="map-tools">
|
|||
|
<el-button-group>
|
|||
|
<el-button size="mini" type="primary" icon="el-icon-plus" @click="startBoundaryEdit" :disabled="isEditing">新增区域</el-button>
|
|||
|
<el-button size="mini" type="warning" icon="el-icon-edit" @click="startAreaBoundaryEdit" :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-group>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 编辑工具栏 -->
|
|||
|
<div class="boundary-tools" v-if="isEditing">
|
|||
|
<el-alert type="info" :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 { SubAreaType, SubAreaStatus } from "@/utils/enums";
|
|||
|
import { debounce } from "@/utils/index";
|
|||
|
import globalConfig from "@/utils/config/globalConfig";
|
|||
|
// 区域样式配置
|
|||
|
const AREA_STYLES = {
|
|||
|
[SubAreaType.PARKING]: {
|
|||
|
strokeColor: '#1890ff',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#1890ff',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'solid'
|
|||
|
},
|
|||
|
[SubAreaType.NO_PARKING]: {
|
|||
|
strokeColor: '#ff4d4f',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#ff4d4f',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'solid'
|
|||
|
},
|
|||
|
[SubAreaType.NO_RIDE]: {
|
|||
|
strokeColor: '#faad14',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#faad14',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'solid'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 高亮样式
|
|||
|
const HIGHLIGHT_STYLES = {
|
|||
|
hover: {
|
|||
|
strokeWeight: 3,
|
|||
|
fillOpacity: 0.5
|
|||
|
},
|
|||
|
selected: {
|
|||
|
strokeWeight: 4,
|
|||
|
fillOpacity: 0.6
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 图标配置
|
|||
|
const MARKER_ICONS = {
|
|||
|
[SubAreaType.PARKING]: 'https://lxnapi.ccttiot.com/FqcYf6ecsnbC0OT6YYAF5npgu-kh',
|
|||
|
[SubAreaType.NO_PARKING]: 'https://lxnapi.ccttiot.com/FjKE5PWbnEnZUq3k-wVIvV4lv8Ab',
|
|||
|
[SubAreaType.NO_RIDE]: 'https://lxnapi.ccttiot.com/FmX1diEPPbFYe1vcUfKp6qbKzzh2'
|
|||
|
};
|
|||
|
|
|||
|
// 标签样式配置
|
|||
|
const LABEL_STYLES = {
|
|||
|
[SubAreaType.PARKING]: {
|
|||
|
backgroundColor: '#1890ff',
|
|||
|
color: '#fff'
|
|||
|
},
|
|||
|
[SubAreaType.NO_PARKING]: {
|
|||
|
backgroundColor: '#ff4d4f',
|
|||
|
color: '#fff'
|
|||
|
},
|
|||
|
[SubAreaType.NO_RIDE]: {
|
|||
|
backgroundColor: '#faad14',
|
|||
|
color: '#fff'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
export default {
|
|||
|
name: 'AreaMap',
|
|||
|
|
|||
|
props: {
|
|||
|
// 运营区域信息
|
|||
|
area: {
|
|||
|
type: Object,
|
|||
|
required: true
|
|||
|
},
|
|||
|
// 子区域列表
|
|||
|
areaSubList: {
|
|||
|
type: Array,
|
|||
|
default: () => []
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
data() {
|
|||
|
return {
|
|||
|
map: null,
|
|||
|
AMap: null,
|
|||
|
mapStyle: 'normal',
|
|||
|
showLabels: true,
|
|||
|
isEditing: false,
|
|||
|
editingPolygon: null,
|
|||
|
editSubArea: null,
|
|||
|
isEditingArea: false,
|
|||
|
polygonEditor: null,
|
|||
|
overlays: {
|
|||
|
polygons: new Map(),
|
|||
|
markers: new Map(),
|
|||
|
labels: new Map()
|
|||
|
},
|
|||
|
currentInfoWindow: null,
|
|||
|
highlightedAreaId: null, // 当前鼠标悬停的区域ID
|
|||
|
selectedAreaId: null // 当前选中的区域ID
|
|||
|
};
|
|||
|
},
|
|||
|
|
|||
|
watch: {
|
|||
|
// 监听区域数据变化
|
|||
|
area: {
|
|||
|
handler: debounce(function() {
|
|||
|
this.renderAreaBoundary();
|
|||
|
}, 300),
|
|||
|
deep: true
|
|||
|
},
|
|||
|
// 监听子区域列表变化
|
|||
|
areaSubList: {
|
|||
|
handler: debounce(function() {
|
|||
|
this.renderSubAreas();
|
|||
|
}, 300),
|
|||
|
deep: true
|
|||
|
},
|
|||
|
// 监听高亮状态变化
|
|||
|
highlightedAreaId: {
|
|||
|
handler(newVal, oldVal) {
|
|||
|
if (oldVal) {
|
|||
|
this.updateAreaStyle(oldVal);
|
|||
|
}
|
|||
|
if (newVal) {
|
|||
|
this.updateAreaStyle(newVal);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 监听选中状态变化
|
|||
|
selectedAreaId: {
|
|||
|
handler(newVal, oldVal) {
|
|||
|
if (oldVal) {
|
|||
|
this.updateAreaStyle(oldVal);
|
|||
|
}
|
|||
|
if (newVal) {
|
|||
|
this.updateAreaStyle(newVal);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
async mounted() {
|
|||
|
await this.initMap();
|
|||
|
this.renderAll();
|
|||
|
},
|
|||
|
|
|||
|
beforeDestroy() {
|
|||
|
this.destroyMap();
|
|||
|
},
|
|||
|
|
|||
|
methods: {
|
|||
|
// 初始化地图
|
|||
|
async initMap() {
|
|||
|
try {
|
|||
|
this.AMap = await AMapLoader.load({
|
|||
|
key: globalConfig.aMap.key,
|
|||
|
version: '2.0',
|
|||
|
plugins: ['AMap.PolygonEditor', 'AMap.InfoWindow']
|
|||
|
});
|
|||
|
|
|||
|
// 使用 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.map.on('click', () => {
|
|||
|
if (this.currentInfoWindow) {
|
|||
|
this.currentInfoWindow.close();
|
|||
|
}
|
|||
|
if (!this.isEditing) {
|
|||
|
this.selectedAreaId = null;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 初始化多边形编辑器
|
|||
|
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);
|
|||
|
this.$message.error('地图加载失败');
|
|||
|
}
|
|||
|
},
|
|||
|
handlePolygonAdd({target}) {
|
|||
|
console.log("handlePolygonAdd", target);
|
|||
|
this.editingPolygon = target;
|
|||
|
},
|
|||
|
handlePolygonEnd({target}) {
|
|||
|
console.log("handlePolygonEnd", target);
|
|||
|
this.editingPolygon = target;
|
|||
|
},
|
|||
|
// 渲染所有内容
|
|||
|
renderAll() {
|
|||
|
this.clearEditor();
|
|||
|
this.clearHighlight();
|
|||
|
this.clearOverlays();
|
|||
|
this.renderAreaBoundary();
|
|||
|
this.renderSubAreas();
|
|||
|
},
|
|||
|
|
|||
|
// 清除编辑器
|
|||
|
clearEditor() {
|
|||
|
|
|||
|
// 关闭编辑器
|
|||
|
if (this.polygonEditor) {
|
|||
|
// 清除编辑器的目标对象
|
|||
|
this.polygonEditor.setTarget();
|
|||
|
}
|
|||
|
|
|||
|
// 移除编辑中的多边形
|
|||
|
if (this.editingPolygon) {
|
|||
|
this.map.remove(this.editingPolygon);
|
|||
|
this.editingPolygon = null;
|
|||
|
}
|
|||
|
|
|||
|
},
|
|||
|
|
|||
|
// 渲染运营区边界
|
|||
|
renderAreaBoundary() {
|
|||
|
if (!this.map || !this.area.boundaryStr) {
|
|||
|
return;
|
|||
|
};
|
|||
|
|
|||
|
try {
|
|||
|
const boundary = JSON.parse(this.area.boundaryStr);
|
|||
|
const polygon = new this.AMap.Polygon({
|
|||
|
path: boundary,
|
|||
|
strokeColor: '#2b8cbe',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#ccebc5',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'dashed',
|
|||
|
strokeDasharray: [5, 5]
|
|||
|
});
|
|||
|
|
|||
|
this.map.add(polygon);
|
|||
|
this.overlays.polygons.set('area', polygon);
|
|||
|
} catch (error) {
|
|||
|
console.error('渲染运营区边界失败:', error);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 渲染子区域
|
|||
|
renderSubAreas() {
|
|||
|
if (!this.map || !this.areaSubList.length) return;
|
|||
|
|
|||
|
this.clearSubAreas();
|
|||
|
|
|||
|
this.areaSubList.forEach(subArea => {
|
|||
|
if (subArea.status === SubAreaStatus.DISABLED) return;
|
|||
|
|
|||
|
try {
|
|||
|
// 渲染边界
|
|||
|
if (subArea.boundaryStr) {
|
|||
|
const boundary = JSON.parse(subArea.boundaryStr);
|
|||
|
const polygon = new this.AMap.Polygon({
|
|||
|
path: boundary,
|
|||
|
...AREA_STYLES[subArea.type],
|
|||
|
extData: {
|
|||
|
id: subArea.id,
|
|||
|
type: subArea.type
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 添加事件监听
|
|||
|
polygon.on('mouseover', () => {
|
|||
|
if (!this.isEditing) {
|
|||
|
this.highlightedAreaId = subArea.id;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
polygon.on('mouseout', () => {
|
|||
|
if (!this.isEditing) {
|
|||
|
this.highlightedAreaId = null;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
polygon.on('click', (e) => {
|
|||
|
if (!this.isEditing) {
|
|||
|
// 如果点击的是当前选中的区域,不做任何操作
|
|||
|
if (this.selectedAreaId !== subArea.id) {
|
|||
|
this.selectedAreaId = subArea.id;
|
|||
|
this.showInfoWindow(subArea);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
this.map.add(polygon);
|
|||
|
this.overlays.polygons.set(subArea.id, polygon);
|
|||
|
}
|
|||
|
|
|||
|
// 渲染标记点
|
|||
|
const marker = new this.AMap.Marker({
|
|||
|
position: [subArea.longitude, subArea.latitude],
|
|||
|
icon: new this.AMap.Icon({
|
|||
|
image: MARKER_ICONS[subArea.type],
|
|||
|
size: new this.AMap.Size(25, 36),
|
|||
|
imageSize: new this.AMap.Size(25, 36)
|
|||
|
}),
|
|||
|
offset: new this.AMap.Pixel(-12.5, -36)
|
|||
|
});
|
|||
|
|
|||
|
marker.on('click', (e) => {
|
|||
|
if (!this.isEditing) {
|
|||
|
this.showInfoWindow(subArea);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
this.map.add(marker);
|
|||
|
this.overlays.markers.set(subArea.id, marker);
|
|||
|
|
|||
|
// 渲染文字标签
|
|||
|
const label = new this.AMap.Text({
|
|||
|
text: subArea.name,
|
|||
|
position: [subArea.longitude, subArea.latitude],
|
|||
|
offset: new this.AMap.Pixel(0, -60),
|
|||
|
anchor: 'center',
|
|||
|
cursor: 'pointer',
|
|||
|
style: {
|
|||
|
...LABEL_STYLES[subArea.type],
|
|||
|
border: 'none',
|
|||
|
fontSize: '12px',
|
|||
|
padding: '2px 8px',
|
|||
|
borderRadius: '4px',
|
|||
|
whiteSpace: 'nowrap',
|
|||
|
textAlign: 'center',
|
|||
|
position: 'absolute',
|
|||
|
transform: 'translateX(-50%)',
|
|||
|
minWidth: '50px',
|
|||
|
display: 'flex',
|
|||
|
justifyContent: 'center',
|
|||
|
alignItems: 'center'
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
label.on('click', (e) => {
|
|||
|
if (!this.isEditing) {
|
|||
|
this.showInfoWindow(subArea);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
this.map.add(label);
|
|||
|
this.overlays.labels.set(subArea.id, label);
|
|||
|
label.setMap(this.showLabels ? this.map : null);
|
|||
|
} catch (error) {
|
|||
|
console.error(`渲染子区域失败 [${subArea.id}]:`, error);
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 显示信息窗体
|
|||
|
showInfoWindow(subArea, focus = false) {
|
|||
|
console.log("showInfoWindow", this.isEditing);
|
|||
|
if (this.currentInfoWindow) {
|
|||
|
this.currentInfoWindow.close();
|
|||
|
}
|
|||
|
|
|||
|
const position = [subArea.longitude, subArea.latitude];
|
|||
|
|
|||
|
// 聚焦效果
|
|||
|
if (focus) {
|
|||
|
const polygon = this.overlays.polygons.get(subArea.id);
|
|||
|
if (polygon) {
|
|||
|
const bounds = polygon.getBounds();
|
|||
|
const padding = [150, 300, 150, 300];
|
|||
|
this.map.setBounds(bounds, false, padding);
|
|||
|
} else {
|
|||
|
this.map.setZoomAndCenter(15, position, false, 300);
|
|||
|
}
|
|||
|
} else {
|
|||
|
this.map.setCenter(position);
|
|||
|
}
|
|||
|
|
|||
|
// 创建自定义内容,使用Vue的事件处理方式
|
|||
|
const content = document.createElement('div');
|
|||
|
content.className = 'map-info-window';
|
|||
|
content.innerHTML = `
|
|||
|
<h4>${subArea.name}</h4>
|
|||
|
<div class="info-actions">
|
|||
|
<a class="info-action" data-action="edit">编辑信息</a>
|
|||
|
<a class="info-action" data-action="boundary">修改边界</a>
|
|||
|
<a class="info-action" data-action="delete">删除</a>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
|
|||
|
// 直接绑定事件
|
|||
|
content.addEventListener('click', (e) => {
|
|||
|
const target = e.target;
|
|||
|
if (target.classList.contains('info-action')) {
|
|||
|
const action = target.getAttribute('data-action');
|
|||
|
this.handleInfoAction(action, subArea);
|
|||
|
if (this.currentInfoWindow) {
|
|||
|
this.currentInfoWindow.close();
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
this.currentInfoWindow = new this.AMap.InfoWindow({
|
|||
|
content: content,
|
|||
|
offset: new this.AMap.Pixel(0, -30),
|
|||
|
closeWhenClickMap: true
|
|||
|
});
|
|||
|
|
|||
|
// 添加关闭事件监听
|
|||
|
this.currentInfoWindow.on('close', () => {
|
|||
|
if (!this.isEditing) {
|
|||
|
this.selectedAreaId = null;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
this.currentInfoWindow.open(this.map, position);
|
|||
|
},
|
|||
|
|
|||
|
// 处理信息窗体操作
|
|||
|
handleInfoAction(action, subArea) {
|
|||
|
console.log("action", action);
|
|||
|
switch (action) {
|
|||
|
case 'edit':
|
|||
|
this.$emit('edit', subArea);
|
|||
|
break;
|
|||
|
case 'boundary':
|
|||
|
this.startBoundaryEdit(subArea);
|
|||
|
break;
|
|||
|
case 'delete':
|
|||
|
this.$emit('delete', subArea.id);
|
|||
|
break;
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 计算多边形中心点
|
|||
|
calculateCenter(path) {
|
|||
|
if (!path || path.length === 0) return null;
|
|||
|
|
|||
|
let total = path.length;
|
|||
|
let X = 0, Y = 0, Z = 0;
|
|||
|
|
|||
|
path.forEach(point => {
|
|||
|
let lng = (point[0] * Math.PI) / 180;
|
|||
|
let lat = (point[1] * Math.PI) / 180;
|
|||
|
|
|||
|
let x = Math.cos(lat) * Math.cos(lng);
|
|||
|
let y = Math.cos(lat) * Math.sin(lng);
|
|||
|
let z = Math.sin(lat);
|
|||
|
|
|||
|
X += x;
|
|||
|
Y += y;
|
|||
|
Z += z;
|
|||
|
});
|
|||
|
|
|||
|
X = X / total;
|
|||
|
Y = Y / total;
|
|||
|
Z = Z / total;
|
|||
|
|
|||
|
let Lng = Math.atan2(Y, X);
|
|||
|
let Hyp = Math.sqrt(X * X + Y * Y);
|
|||
|
let Lat = Math.atan2(Z, Hyp);
|
|||
|
|
|||
|
return [
|
|||
|
(Lng * 180) / Math.PI,
|
|||
|
(Lat * 180) / Math.PI
|
|||
|
];
|
|||
|
},
|
|||
|
|
|||
|
// 开始边界编辑
|
|||
|
startBoundaryEdit(subArea) {
|
|||
|
|
|||
|
this.clearEditor();
|
|||
|
|
|||
|
if (subArea && subArea.boundaryStr) {
|
|||
|
console.log("subArea", subArea);
|
|||
|
try {
|
|||
|
const boundary = JSON.parse(subArea.boundaryStr);
|
|||
|
this.editingPolygon = new this.AMap.Polygon({
|
|||
|
path: boundary,
|
|||
|
...AREA_STYLES[subArea.type]
|
|||
|
});
|
|||
|
this.map.add(this.editingPolygon);
|
|||
|
this.polygonEditor.setTarget(this.editingPolygon);
|
|||
|
} catch (error) {
|
|||
|
console.error('加载边界数据失败:', error);
|
|||
|
this.$message.error('加载边界数据失败');
|
|||
|
}
|
|||
|
}
|
|||
|
this.polygonEditor.open();
|
|||
|
|
|||
|
this.startEditArea();
|
|||
|
},
|
|||
|
|
|||
|
// 保存边界
|
|||
|
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;
|
|||
|
}
|
|||
|
console.log(boundary);
|
|||
|
|
|||
|
if (this.isEditingArea) {
|
|||
|
// 更新运营区边界
|
|||
|
this.$emit('update-area-boundary', {
|
|||
|
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', {
|
|||
|
area: this.editSubArea,
|
|||
|
boundary: JSON.stringify(boundary),
|
|||
|
longitude: center[0],
|
|||
|
latitude: center[1]
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
this.cancelEdit();
|
|||
|
},
|
|||
|
|
|||
|
// 取消编辑
|
|||
|
cancelEdit() {
|
|||
|
|
|||
|
// 重置编辑状态
|
|||
|
this.isEditing = false;
|
|||
|
this.editSubArea = null;
|
|||
|
this.isEditingArea = false;
|
|||
|
|
|||
|
// 移除地图事件监听
|
|||
|
this.map.clearEvents('click');
|
|||
|
this.map.clearEvents('rightclick');
|
|||
|
|
|||
|
// 重新渲染所有区域,确保显示正常
|
|||
|
this.renderAll();
|
|||
|
},
|
|||
|
|
|||
|
// 工具方法
|
|||
|
setFitView() {
|
|||
|
this.map.setFitView(null, false, [150, 60, 100, 60]);
|
|||
|
},
|
|||
|
|
|||
|
// 切换地图样式
|
|||
|
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]);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
toggleLabels() {
|
|||
|
this.showLabels = !this.showLabels;
|
|||
|
this.overlays.labels.forEach(label => {
|
|||
|
label.setMap(this.showLabels ? this.map : null);
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 清理方法优化
|
|||
|
clearOverlays() {
|
|||
|
Object.values(this.overlays).forEach(overlayMap => {
|
|||
|
overlayMap.forEach(overlay => {
|
|||
|
this.map.remove(overlay);
|
|||
|
});
|
|||
|
overlayMap.clear();
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
clearSubAreas() {
|
|||
|
['polygons', 'markers', 'labels'].forEach(type => {
|
|||
|
this.overlays[type].forEach((overlay, key) => {
|
|||
|
if (key !== 'area') {
|
|||
|
this.map.remove(overlay);
|
|||
|
this.overlays[type].delete(key);
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
},
|
|||
|
|
|||
|
// 更新区域样式
|
|||
|
updateAreaStyle(areaId) {
|
|||
|
const polygon = this.overlays.polygons.get(areaId);
|
|||
|
if (!polygon) return;
|
|||
|
|
|||
|
const type = polygon.getExtData().type;
|
|||
|
let style = { ...AREA_STYLES[type] };
|
|||
|
|
|||
|
// 根据状态叠加高亮样式
|
|||
|
if (areaId === this.selectedAreaId) {
|
|||
|
style = { ...style, ...HIGHLIGHT_STYLES.selected };
|
|||
|
} else if (areaId === this.highlightedAreaId) {
|
|||
|
style = { ...style, ...HIGHLIGHT_STYLES.hover };
|
|||
|
}
|
|||
|
|
|||
|
polygon.setOptions(style);
|
|||
|
},
|
|||
|
|
|||
|
// 清除所有高亮状态
|
|||
|
clearHighlight() {
|
|||
|
this.highlightedAreaId = null;
|
|||
|
this.selectedAreaId = null;
|
|||
|
},
|
|||
|
|
|||
|
destroyMap() {
|
|||
|
if (this.map) {
|
|||
|
this.clearHighlight();
|
|||
|
this.clearOverlays();
|
|||
|
this.map.destroy();
|
|||
|
this.map = null;
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
// 开始编辑运营区边界
|
|||
|
startAreaBoundaryEdit() {
|
|||
|
|
|||
|
this.clearEditor();
|
|||
|
|
|||
|
if (this.area.boundaryStr) {
|
|||
|
try {
|
|||
|
const boundary = JSON.parse(this.area.boundaryStr);
|
|||
|
this.editingPolygon = new this.AMap.Polygon({
|
|||
|
path: boundary,
|
|||
|
strokeColor: '#2b8cbe',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#ccebc5',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'dashed',
|
|||
|
strokeDasharray: [5, 5]
|
|||
|
});
|
|||
|
this.map.add(this.editingPolygon);
|
|||
|
this.polygonEditor.setTarget(this.editingPolygon);
|
|||
|
} catch (error) {
|
|||
|
console.error('加载运营区边界数据失败:', error);
|
|||
|
this.$message.error('加载运营区边界数据失败');
|
|||
|
}
|
|||
|
} else {
|
|||
|
// 创建一个新的运营区多边形
|
|||
|
this.editingPolygon = new this.AMap.Polygon({
|
|||
|
path: [],
|
|||
|
strokeColor: '#2b8cbe',
|
|||
|
strokeWeight: 2,
|
|||
|
fillColor: '#ccebc5',
|
|||
|
fillOpacity: 0.3,
|
|||
|
strokeStyle: 'dashed',
|
|||
|
strokeDasharray: [5, 5]
|
|||
|
});
|
|||
|
|
|||
|
this.map.add(this.editingPolygon);
|
|||
|
this.polygonEditor.addAdsorbPolygons(this.editingPolygon)
|
|||
|
}
|
|||
|
|
|||
|
this.polygonEditor.open();
|
|||
|
this.startEditArea();
|
|||
|
},
|
|||
|
// 开始绘制(不管是那种边界)
|
|||
|
startEditArea() {
|
|||
|
this.isEditing = true;
|
|||
|
this.isEditingArea = true;
|
|||
|
|
|||
|
// 关闭当前信息窗口
|
|||
|
if (this.currentInfoWindow) {
|
|||
|
this.currentInfoWindow.close();
|
|||
|
}
|
|||
|
|
|||
|
},
|
|||
|
// 设置为新建模式
|
|||
|
setAddMode() {
|
|||
|
// 添加地图点击事件监听,用于绘制多边形
|
|||
|
const clickHandler = (e) => {
|
|||
|
const path = this.editingPolygon.getPath();
|
|||
|
path.push([e.lnglat.getLng(), e.lnglat.getLat()]);
|
|||
|
this.editingPolygon.setPath(path);
|
|||
|
};
|
|||
|
|
|||
|
// 添加右键点击事件监听,用于完成绘制
|
|||
|
const rightClickHandler = () => {
|
|||
|
this.map.off('click', clickHandler);
|
|||
|
this.map.off('rightclick', rightClickHandler);
|
|||
|
|
|||
|
if (this.editingPolygon.getPath().length < 3) {
|
|||
|
this.$message.warning('请至少绘制3个点形成多边形');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this.polygonEditor.setTarget(this.editingPolygon);
|
|||
|
this.polygonEditor.open();
|
|||
|
};
|
|||
|
|
|||
|
this.map.on('click', clickHandler);
|
|||
|
this.map.on('rightclick', rightClickHandler);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
</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;
|
|||
|
z-index: 100;
|
|||
|
}
|
|||
|
|
|||
|
.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">
|
|||
|
|
|||
|
// 信息窗体样式
|
|||
|
.map-info-window {
|
|||
|
padding: 5px;
|
|||
|
font-size: 12px;
|
|||
|
|
|||
|
h4 {
|
|||
|
margin: 0 0 10px;
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
|
|||
|
.info-actions {
|
|||
|
display: flex;
|
|||
|
gap: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.info-action {
|
|||
|
color: #409eff;
|
|||
|
cursor: pointer;
|
|||
|
text-decoration: none;
|
|||
|
|
|||
|
&:hover {
|
|||
|
color: #66b1ff;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|