6/23 更改高德地图组件,统一入口

This commit is contained in:
Zy
2026-06-23 17:07:48 +08:00
parent 826bf3c782
commit 33d35c8d70
30 changed files with 643 additions and 757 deletions

334
src/components/Map/GD.vue Normal file
View File

@@ -0,0 +1,334 @@
<script setup lang="ts">
import AMapLoader from '@amap/amap-jsapi-loader';
import { getVillageByIdAPI } from '@/api/system/community';
import { onUnmounted, ref, watch, onMounted, nextTick } from 'vue';
// 密钥配置
const autoloader = import.meta.env.VITE_GCJ02_KEY_GD;
const VITE_GCJ02_JS_CODE_GD = import.meta.env.VITE_GCJ02_JS_CODE_GD;
window._AMapSecurityConfig = { securityJsCode: VITE_GCJ02_JS_CODE_GD };
// 全局实例
const AMAP = ref<any>(null);
const map = ref<any>(null);
const mapReady = ref(false);
const maploading = ref(false);
const markers = ref<any[]>([]);
const mouseTool = ref<any>(null);
const AddMarkered = new Map<string, any>();
let drawnPolylines: any[] = [];
// Props & Emits
const props = defineProps({
disabled: {
type: Boolean,
default: false
},
clickOrDraw: {
type: Boolean,
default: false
},
smallScroll: {
type: Boolean,
default: false
}
});
const emits = defineEmits<{
clickPoint: [value: number[]];
}>();
// 对外暴露方法
defineExpose({
addMarker_only,
clearLocation,
addMarker_more,
clearAll,
getAllDrawLines,
renderSavedPath,
clearMapMarker
});
const village = localStorage.getItem('villageid');
// ===================== 工具函数 =====================
/** 地图完整可用校验 */
function isMapAvailable() {
return mapReady.value && map.value && AMAP.value;
}
/** 统一适配视野,无动画 + 留白 + 最大缩放限制 */
function fitMapView(overlays: any[]) {
if (!isMapAvailable() || overlays.length === 0) return;
// 第二个参数true关闭动画留白80px最大缩放18级
console.log(props.smallScroll);
map.value.setFitView(overlays, props.smallScroll);
}
// ===================== 渲染等待队列(响应式) =====================
type WaitMarkerItem = { angular: number[]; pointName: string };
type WaitMoreMarkerItem = { angular: number[]; pointName: string; pointId: string };
const waitQueue = ref<{
onlyPoints: WaitMarkerItem[];
point: WaitMoreMarkerItem[];
routes: string[];
clear: boolean;
}>({
onlyPoints: [],
point: [],
routes: [],
clear: false
});
// 地图就绪后批量执行队列任务
watch(mapReady, async (ready) => {
if (!ready || !isMapAvailable()) return;
await nextTick();
// 先执行清空
if (waitQueue.value.clear) {
map.value.clearMap();
AddMarkered.clear();
markers.value = [];
drawnPolylines = [];
waitQueue.value.clear = false;
}
// 批量多点标记
waitQueue.value.point.forEach((item) => addMarker_more(item.angular, item.pointName, item.pointId));
waitQueue.value.point = [];
// 单点标记
waitQueue.value.onlyPoints.forEach((item) => addMarker_only(item.angular, item.pointName));
waitQueue.value.onlyPoints = [];
// 路线回显
waitQueue.value.routes.forEach((pathStr) => renderSavedPath(pathStr));
waitQueue.value.routes = [];
});
// ===================== 单点标记 =====================
function addMarker_only(angular: number[], pointName: string) {
if (!isMapAvailable()) {
waitQueue.value.onlyPoints.push({ angular, pointName });
return;
}
clearLocation();
const marker = new AMAP.value.Marker({
position: angular,
map: map.value,
title: pointName || '巡检点',
content: `
<div style="display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;background:rgba(26,117,255,0.3);">
<div style="width:20px;height:20px;border-radius:50%;line-height:20px;background:#1a75ff;color:#fff;font-size:20px;text-align:center;">1</div>
</div>`
});
markers.value = [marker];
fitMapView(markers.value);
return marker;
}
/** 清空单点标记 */
function clearLocation() {
if (!isMapAvailable() || markers.value.length === 0) return;
markers.value.forEach((item) => map.value.remove(item));
markers.value = [];
}
// ===================== 多点标记(可增删) =====================
function addMarker_more(angular: number[], pointName: string, pointId: string) {
if (!isMapAvailable()) {
waitQueue.value.point.push({ angular, pointName, pointId });
return;
}
// 存在则删除
if (AddMarkered.has(pointId)) {
map.value.remove(AddMarkered.get(pointId));
AddMarkered.delete(pointId);
// 删除后重新适配视野
fitMapView(Array.from(AddMarkered.values()));
return;
}
const index = AddMarkered.size + 1;
const marker = new AMAP.value.Marker({
position: angular,
map: map.value,
title: pointName || '巡检点',
content: `
<div style="display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;background:rgba(26,117,255,0.3);">
<div style="width:20px;height:20px;border-radius:50%;line-height:20px;background:#1a75ff;color:#fff;font-size:20px;text-align:center;">${index}</div>
</div>`
});
AddMarkered.set(pointId, marker);
markers.value.push(marker);
fitMapView(Array.from(AddMarkered.values()));
return marker;
}
// ===================== 绘制路线 =====================
function MapDrawLine() {
if (!isMapAvailable()) return;
mouseTool.value = new AMAP.value.MouseTool(map.value);
mouseTool.value.on('draw', (e: any) => {
if (!e.obj) return;
// 只保留一条线
if (drawnPolylines.length > 0) map.value.remove(drawnPolylines[0]);
drawnPolylines = [e.obj];
initDrawTool();
});
initDrawTool();
}
function initDrawTool() {
if (!mouseTool.value || !isMapAvailable()) return;
mouseTool.value.close(false);
mouseTool.value.polyline({
strokeColor: '#3366FF',
strokeOpacity: 1,
strokeWeight: 3,
strokeStyle: 'solid'
});
}
/** 获取路线坐标数组 */
function getAllDrawLines(): number[][] {
if (!isMapAvailable() || drawnPolylines.length === 0) return [];
const path = drawnPolylines[0].getPath();
return path.map((p: any) => [p.lng, p.lat]);
}
/** 回显历史路线 */
function renderSavedPath(pathStr: string) {
if (!isMapAvailable()) {
waitQueue.value.routes.push(pathStr);
return;
}
try {
const path = JSON.parse(pathStr);
if (!Array.isArray(path) || path.length === 0) return;
// 清空旧线
drawnPolylines.forEach((line) => map.value.remove(line));
drawnPolylines = [];
const polyline = new AMAP.value.Polyline({
path,
strokeColor: '#3366FF',
strokeOpacity: 1,
strokeWeight: 3,
strokeStyle: 'solid',
map: map.value
});
drawnPolylines.push(polyline);
fitMapView([polyline]);
} catch (err) {
console.error('路线解析失败', err);
}
}
/** 清空绘图工具+路线 */
function clearAll() {
if (!isMapAvailable() || !mouseTool.value) return;
mouseTool.value.close(true);
drawnPolylines.forEach((line) => map.value.removeOverlay(line));
drawnPolylines = [];
initDrawTool();
}
/** 清空地图所有覆盖物(标记+路线) */
function clearMapMarker() {
if (isMapAvailable()) {
map.value.clearMap();
AddMarkered.clear();
markers.value = [];
drawnPolylines = [];
} else {
waitQueue.value.clear = true;
}
}
/** 地图点击拾取坐标 */
function handleMap() {
if (!isMapAvailable()) return;
map.value.on('click', (e: any) => {
const { lng, lat } = e.lnglat;
emits('clickPoint', [lng, lat]);
});
}
// ===================== 地图初始化 =====================
onMounted(async () => {
maploading.value = true;
try {
const AMap = await AMapLoader.load({
key: autoloader,
version: '2.0',
plugins: ['AMap.Scale', 'AMap.Marker', 'AMap.Geocoder', 'AMap.MouseTool']
});
AMAP.value = AMap;
// 获取村庄城市定位
const villageInfo = await getVillageByIdAPI(village);
const city = villageInfo.data.city.replaceAll('/', '');
// 创建地图,全局关闭动画,解决移动缓慢
map.value = new AMap.Map('GD-Map', {
viewMode: '3D',
zoom: 11,
center: [116.397428, 39.90923]
});
// 城市地理编码定位
const geocoder = new AMap.Geocoder({ city });
geocoder.getLocation(city, (status: string, result: any) => {
if (status === 'complete' && result.info === 'OK') {
map.value.setCenter(result.geocodes[0].location.toArray());
}
});
mapReady.value = true;
// 禁用状态不开启绘制/点击
if (!props.disabled) {
props.clickOrDraw ? handleMap() : MapDrawLine();
}
} catch (err) {
console.error('高德地图加载失败', err);
} finally {
maploading.value = false;
}
});
// ===================== 销毁清理 =====================
onUnmounted(() => {
// 销毁地图实例
map.value?.destroy();
// 清空所有缓存队列
waitQueue.value.onlyPoints = [];
waitQueue.value.point = [];
waitQueue.value.routes = [];
waitQueue.value.clear = false;
// 清空覆盖物缓存
markers.value = [];
drawnPolylines = [];
AddMarkered.clear();
mouseTool.value = null;
AMAP.value = null;
map.value = null;
mapReady.value = false;
});
</script>
<template>
<div v-loading="maploading" class="map" id="GD-Map"></div>
</template>
<style scoped lang="scss">
.map {
width: 100%;
min-height: 450px;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
margin-top: 10px;
}
</style>

View File

@@ -0,0 +1,9 @@
<script setup lang="ts"></script>
<template>
<div>
<!-- 天地图-->
</div>
</template>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,14 @@
<script setup name="MapIndex" lang="ts">
import TDT from './TDT.vue';
import GD from './GD.vue';
const isGD = import.meta.env.VITE_APP_MAP_TYPE === 'GCJ02';
</script>
<template>
<div>
<!-- 天地图 -->
<TDT v-if="!isGD"></TDT>
<!-- 高德地图-->
<GD v-else></GD>
</div>
</template>
<style scoped lang="scss"></style>