1.高德地图JS API

本文中组件基于高德地图JS API实现,调用需在高德开放平台注册Web端应用,获取Key安全密钥,详细使用见官方文档

2.基础地图与点标记(Marker)

使用官方推荐的AmapLoader来JS API并进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const key = "Your key" //高德开放平台申请的Key
const secrectKey = "Your secrect key" //高德开放平台申请的安全密钥
const loadMap = async () => {
window._AMapSecurityConfig = {
securityJsCode: secrectKey,
};

const AMap = await AMapLoader.load({
key: key,
version: '2.0',
plugins: ['AMap.Scale', 'AMap.Marker'],
});

map = new AMap.Map('map_container', {
viewMode: '2D',
zoom: 11, //地图级别,用于控制缩放,级别越高地图放大程度越高
center: [116.397428, 39.90923], //地图中心
});
};

onMounted(loadMap);

绘制地图并标点的Vue组件

效果展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<script setup>
import { onMounted, onUnmounted, watch } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';

// 接收父组件传来的 props
const props = defineProps({
center: { //地图中心
type: Array,
required: true,
},
zoom: { //地图层级
type: Number,
default: 11,
},
marker: { // 点标记对象,接收内含两个元素的Number数组
type: Object, // 例:[118.136928,24.421953]
default: null,
},
});

const key = import.meta.env.VITE_GAODE_API_KEY;
const secrectKey = import.meta.env.VITE_GAODE_API_SECRECT_KEY;

let map = null;
let marker = null;

const loadMap = async () => {
window._AMapSecurityConfig = {
securityJsCode: secrectKey,
};

const AMap = await AMapLoader.load({
key: key,
version: '2.0',
plugins: ['AMap.Scale', 'AMap.Marker'],
});

map = new AMap.Map('map_container', {
viewMode: '3D',
zoom: props.zoom,
center: props.center,
});

if (props.marker) {
marker = new AMap.Marker({
position: props.marker.position,
title: props.marker.title || '',
});
map.add(marker);
}
};

// 监听中心点变化
watch(
() => props.center,
(newCenter) => {
if (map) {
map.setCenter(newCenter);
if (marker) marker.setPosition(newCenter);
}
}
);

// 监听缩放变化
watch(
() => props.zoom,
(newZoom) => {
if (map && newZoom) map.setZoom(newZoom);
}
);

// 监听 marker 变化
watch(
() => props.marker,
(newMarker) => {
if (map) {
if (marker) map.remove(marker);
if (newMarker) {
marker = new AMap.Marker({
position: newMarker.position,
title: newMarker.title || '',
});
map.add(marker);
}
}
},
{ deep: true }
);

onMounted(loadMap);

onUnmounted(() => {
map?.destroy();
});
</script>

<template>
<div id="map_container"></div>
</template>

<style scoped>
#map_container {
width: 100%;
height: 300px;
}
</style>

2.输入提示与 POI 搜索

通过输入提示插件AMap.AutoComplete和 POI 搜索插件AMap.PlaceSearch,可实现输入提示与兴趣点显示功能

支持点击标点、输入提示及POI搜索的Vue组件

效果展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<script setup>
import { onMounted, onUnmounted} from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';

const key = import.meta.env.VITE_GAODE_API_KEY;
const secrectKey = import.meta.env.VITE_GAODE_API_SECRECT_KEY;

const props = defineProps({
latitude: {
type: String,
required: false
},
longitude: {
type: String,
required: false
}
})
let AMapObj = null;
let map = null;
let preMarker = null;
let hasPre = false;
let searchText = ref('')
const position_Lng = ref(116.12912)
const position_Lat = ref(39.727705)
const hasMarker = ref(false) //创建时是否添加了Marker
const signClick = (e) => {
position_Lng.value = e.lnglat.getLng();
position_Lat.value = e.lnglat.getLat();
console.log(position_Lat.value +" " +position_Lng.value)
const marker = new AMapObj.Marker({
position: e.lnglat,
title: "仓库",
});
if(!hasMarker.value)hasMarker.value = true;
if(!hasPre){
hasPre = true;
}else{
preMarker.setMap(null)
}
preMarker = marker
map.add(marker);
}
const bindEvent = () =>{
map.on('click', signClick);
}
const loadMap = async () => {
window._AMapSecurityConfig = {
securityJsCode: secrectKey,
};

const AMap = await AMapLoader.load({
key: key,
version: '2.0',
plugins: ['AMap.Scale', 'AMap.Marker'],
});
AMapObj = AMap
map = new AMap.Map('map_container', {
viewMode: '2D',
zoom: 11,
center: [116.397428, 39.90923],
});
if(props.latitude && props.longitude){
const lat = parseFloat(props.latitude);
const lng = parseFloat(props.longitude);
console.log({lat, lng})
hasMarker.value = true
const marker = new AMapObj.Marker({
position: [lng, lat],
title: '仓库',
});
preMarker = marker
map.add(marker);
}
AMap.plugin(["AMap.AutoComplete", "AMap.PlaceSearch"], function () {
var autoOptions = {
//城市,默认全国
city: "北京",
//使用联想输入的 input 的 id
input: "searchInputId",
citylimit: false,
};
var autocomplete = new AMap.AutoComplete(autoOptions);
var placeSearch = new AMap.PlaceSearch({
city: "北京",
map: map,
});
autocomplete.on("select", function (e) {
placeSearch.search(e.poi.name);
});
});
bindEvent()
};

onMounted(loadMap);

onUnmounted(() => {
map?.destroy();
});
defineExpose({
position_Lat,position_Lng, hasMarker
})

</script>

<template>
<div id="map_container"></div>
<div class="map-wrapper">
<div id="search" style="position: absolute;top: 0;height: 40px;left: 100px;width: 400px;z-index: 999">
<div id="search_icon">
<el-icon style="font-size: 24px;"><Search /></el-icon>
</div>
<el-input v-model="searchText" placeholder="请输入搜索关键字" id="searchInputId"/>
</div>
<div id="amap"></div>
</div>
</template>

<style scoped>
#map_container {
width: 100%;
height: 300px;
}
#search {
display: flex;
}
#search_icon{
margin-top: 8px;
}
</style>


3.折线 Polyline

AMap.Polyline可以绘制折线,并设置宽度,描边颜色、线条样式等属性。
创建折线需输入折线路径,由一个节点坐标数组组成

1
2
3
4
5
6
7
//配置折线路径
var path = [
new AMap.LngLat(116.368904, 39.913423),
new AMap.LngLat(116.382122, 39.901176),
new AMap.LngLat(116.387271, 39.912501),
new AMap.LngLat(116.398258, 39.9046),
];

通过AMap.Polyline创建折线实例并添加至地图:

1
2
3
4
5
6
7
var polyline = new AMap.Polyline({
path: path,
strokeWeight: 2, //线条宽度
strokeColor: "red", //线条颜色
lineJoin: "round", //折线拐点连接处样式
});
map.add(polyline);

显示实时位置并与起点连线的Vue组件

效果展示:

组件接收

  • startPoint 起点坐标
  • nowPoint 当前坐标
  • endPoint 终点坐标

在起点与当前位置间绘制折线,并可监听当前位置的更新并重绘折线,当前位置图标需自行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<script setup>
import { onMounted, onUnmounted, watch } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';
import Icon from '@/assets/YourNowPointIcon.png'; // 当前位置图标需自行更改

let map = null;
let marker = null;
let nowMarker = null;
let polyline = null;
// 接收父组件传来的 props
const props = defineProps({
zoom: {
type: Number,
default: 13,
},
marker: {
type: Object,
default: null,
},
startPoint: {
type: Array,
required: true,
},
endPoint: {
type: Array,
required: true,
},
nowPoint: {
type: Array,
required: true,
},
});

// 获取环境变量中的高德 API key
const key = import.meta.env.VITE_GAODE_API_KEY;
const secrectKey = import.meta.env.VITE_GAODE_API_SECRECT_KEY;

// 转换字符串坐标为数字
const normalizePoint = (point) => {
if (Array.isArray(point)) {
return point.map(p => typeof p === 'string' ? parseFloat(p) : p);
}
return point;
};

const loadMap = async () => {
const start = normalizePoint(props.startPoint);
const now = normalizePoint(props.nowPoint);
const end = normalizePoint(props.endPoint);
console.log(start)
console.log(now)
console.log(end)
window._AMapSecurityConfig = {
securityJsCode: secrectKey,
};

const AMap = await AMapLoader.load({
key: key,
version: '2.0',
plugins: ['AMap.Scale', 'AMap.Marker'],
});

map = new AMap.Map('map_container', {
viewMode: '3D',
zoom: props.zoom,
mapStyle: "amap://styles/macaron",
center: now,
});
console.log("intiCenter:")
console.log(map.getCenter().toJSON())
// 可选 marker
if (props.marker) {
marker = new AMap.Marker({
position: normalizePoint(props.marker.position),
title: props.marker.title || '',
});
map.add(marker);
}

// 起点
map.add(
new AMap.Marker({
position: start || [116.035914, 39.710443],
title: "起点",
})
);

// nowPoint marker
const nowIcon = new AMap.Icon({
size: new AMap.Size(60, 60),
image: Icon,
imageSize: new AMap.Size(60, 60),
});
nowMarker = new AMap.Marker({
position: now || [116.035914, 39.710443],
title: "Now",
icon: nowIcon,
offset: new AMap.Pixel(-30, -30),
});
map.add(nowMarker);

// 终点
const endIcon = new AMap.Icon({
size: new AMap.Size(25, 34),
image: '//a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
imageSize: new AMap.Size(135, 40),
imageOffset: new AMap.Pixel(-95, -3),
});
map.add(
new AMap.Marker({
position: end || [115.981787, 39.693467],
title: "终点",
icon: endIcon,
offset: new AMap.Pixel(-13, -30),
})
);

// 初始轨迹线(起点 -> 当前点)
polyline = new AMap.Polyline({
path: [start, now],
strokeWeight: 2,
strokeColor: "red",
lineJoin: "round",
});
map.add(polyline);
};

// 监听中心点变化(可选)
watch(
() => props.center,
(newCenter) => {
if (map) {
map.setCenter(normalizePoint(newCenter));
if (marker) marker.setPosition(normalizePoint(newCenter));
}
}
);

// 监听缩放变化
watch(
() => props.zoom,
(newZoom) => {
if (map && newZoom) map.setZoom(newZoom);
}
);

// 监听 nowPoint 的变化:更新 UAV marker 和折线路径
watch(
() => props.nowPoint,
(newNowPointRaw) => {
const newNowPoint = normalizePoint(newNowPointRaw);
const start = normalizePoint(props.startPoint);
if (nowMarker && newNowPoint) {
nowMarker.setPosition(newNowPoint);
console.log("centerCheck1:")
console.log(map.getCenter().toJSON())
map?.setCenter(newNowPoint);

console.log("centerCheck2:")
console.log(map.getCenter().toJSON())
// 更新折线
if (polyline) {
polyline.setPath([start, newNowPoint]);
}
}
},
{ deep: true }
);

// 监听 marker 变化
watch(
() => props.marker,
(newMarker) => {
if (map) {
if (marker) map.remove(marker);
if (newMarker) {
marker = new AMap.Marker({
position: normalizePoint(newMarker.position),
title: newMarker.title || '',
});
map.add(marker);
}
}
},
{ deep: true }
);

// 初始化地图
onMounted(loadMap);

// 卸载地图
onUnmounted(() => {
map?.destroy();
});
</script>

<template>
<div id="map_container"></div>
</template>

<style scoped>
#map_container {
width: 60%;
height: 100%;
}
</style>

4.地图显示问题

若出现地图组件不显示问题,可尝试更改全局CSS样式,向index.scss中添加如下代码:

1
2
3
4
.amap-sug-result {
z-index: 9999;
visibility: visible;
}