Vue3實現(xiàn)地圖選點組件的示例代碼
更新時間:2024年01月04日 11:51:26 作者:liyfn
這篇文章主要為大家詳細介紹了Vue3實現(xiàn)地圖選點組件的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
Vue3地圖選點組件

實現(xiàn)代碼
<template>
<div style="width: 100%; height: 500px">
<div class="search-container">
<el-autocomplete
v-model="suggestionKeyWord"
class="search-container__input"
clearable
:fetch-suggestions="searchSuggestions"
placeholder="輸入關(guān)鍵字搜索"
@select="onSuggestionChoose"
>
<template #default="{ item }">
<div class="value">{{ item.name }}</div>
<span class="link">{{ item.address }}</span>
</template>
</el-autocomplete>
<el-button type="primary" class="search-container__button" @click="doneMap"> 確定 </el-button>
</div>
<div class="map-body">
<div id="container" class="map-body__left"></div>
<img :class="iconClass" :src="markerSrc" alt="" />
<!-- poi數(shù)據(jù) -->
<div class="map-body__right ele-map-picker-poi-list">
<div
v-for="(poi, index) in poiData"
:key="index"
:class="[
'ele-map-picker-poi-item',
{ 'ele-map-picker-poi-item-active': index === chooseIndex },
]"
@click="choose(index)"
>
<el-icon class="ele-map-picker-poi-item-icon el-icon-location-outline"
><Location
/></el-icon>
<!-- <icon-ep-location class="ele-map-picker-poi-item-icon el-icon-location-outline" /> -->
<div class="ele-map-picker-poi-item-title">{{ poi.name }}</div>
<div v-if="poi.address" class="ele-map-picker-poi-item-address">
{{ poi.address }}
</div>
<el-icon v-if="index === chooseIndex" class="ele-map-picker-poi-item-check"
><Check
/></el-icon>
<!-- <icon-park-check-small
v-if="index === chooseIndex"
class="ele-map-picker-poi-item-check"
/> -->
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';
import markerSrc from '@/assets/images/location.png';
import type { Poi } from './type';
// const props = defineProps({});
const emit = defineEmits(['done-map']);
// 中心點位置
let location: any = reactive([116.4074, 39.9042]);
// 地圖縮放比例
const chooseZoom = 15;
// 搜索關(guān)鍵字
const suggestionKeyWord = ref('');
// 搜索建議列表
let suggestionData = reactive([]);
// 地圖實例
let map: any;
// 輸入建議實例
let autoComplete = reactive({});
// 選中的建議
let chooseSuggestion = reactive<any>({});
// 地圖中心標(biāo)記點
let centerMarker = reactive({});
// poi檢索實例
let placeSearch = reactive({});
// poi檢索的數(shù)據(jù)
const poiData = ref<Poi[]>([]);
// 選中的數(shù)據(jù)
const chooseIndex = ref<any>(null);
// 是否是點擊poi列表移動地圖
let isSelMove = false;
// 圖標(biāo)是否顯示跳動動畫
const showIconAnim = ref(false);
const iconClass = computed(() => {
return ['ele-map-picker-main-icon', { 'ele-map-picker-anim-bounce': showIconAnim.value }];
});
/**
* @description: 初始化地圖
* @param {*} local
* @return {*}
*/
const initMap = (local: any) => {
AMapLoader.load({
key: 'xxxxxxxxxxxxx', // 申請好的Web端開發(fā)者Key,首次調(diào)用 load 時必填
version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時默認(rèn)為 1.4.15
plugins: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.AutoComplete'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
map = new AMap.Map('container', {
zoom: chooseZoom,
center: location,
});
// 輸入建議實例
autoComplete = new AMap.AutoComplete({
city: '全國',
});
// marker實例
centerMarker = new AMap.Marker({
icon: new AMap.Icon({
image: markerSrc,
size: new AMap.Size(26, 36.5),
imageSize: new AMap.Size(26, 36.5),
}),
offset: new AMap.Pixel(-13, -36.5),
});
addMarker(location[0], location[1]);
// 獲取poi檢索實例
placeSearch = new AMap.PlaceSearch({
type: '', // poi檢索興趣點類別
pageSize: 30, // poi檢索每頁數(shù)量
pageIndex: 1,
extensions: 'all',
});
// 地圖加載完成事件
map.on('complete', () => {
chooseIndex.value = null;
const center = map.getCenter();
searchNearBy(center.lat, center.lng, true);
});
// 地圖移動結(jié)束事件
map.on('moveend', () => {
const center = map.getCenter();
addMarker(center.lng, center.lat);
if (isSelMove) {
// poi列表點擊的移動
isSelMove = false;
} else {
// 拖動或搜索建議的移動
showIconAnim.value = false;
nextTick(() => {
setTimeout(() => {
showIconAnim.value = true;
}, 0);
});
searchNearBy(center.lat, center.lng);
}
});
});
};
/**
* @description: poi檢索
* @param {*} lat
* @param {*} lng
* @param {*} force
* @return {*}
*/
const searchNearBy = (lat: any, lng: any) => {
if (!placeSearch) {
return;
}
// this.poiLoading = true;
placeSearch.searchNearBy('', [lng, lat], 1000, (status: any, result: any) => {
// this.poiLoading = false;
if (status === 'complete') {
const data = result.poiList.pois.filter((p: any) => p.location !== undefined);
if (chooseSuggestion) {
// 如果選中的搜索建議不在poi列表中則添加
if (data.length === 0 || data[0].name !== chooseSuggestion.name) {
data.unshift({ ...chooseSuggestion });
}
chooseSuggestion = null;
} else {
chooseIndex.value = null;
}
poiData.value = data;
// v3.17 標(biāo)準(zhǔn)地址庫-地址拼接省市區(qū)
poiData.value.forEach((item) => {
item.pname = item.pname || '';
item.cityname = item.cityname || '';
item.adname = item.adname || '';
item.address = item.address || '';
item.address = `${item.pname}${item.cityname}${item.adname}${item.address}`;
});
}
});
};
/**
* @description: poi列表選中
* @param {*} index
* @return {*}
*/
const choose = (index: number) => {
chooseIndex.value = index;
isSelMove = true;
// this.showIconAnim = false;
// nextTick(() => {
// setTimeout(() => {
// this.showIconAnim = true;
// }, 0);
// });
const point = poiData.value[index].location;
map.setZoomAndCenter(chooseZoom, [point.lng, point.lat]);
};
/**
* @description: 添加marker
* @param {*} lng
* @param {*} lat
* @return {*}
*/
const addMarker = (lng: string, lat: string) => {
// centerMarker.setMap(map);
centerMarker.setPosition([lng, lat]);
map.add(centerMarker);
};
/**
* @description: 獲取搜索數(shù)據(jù)
* @param {*} keywords
* @param {*} callback
* @return {*}
*/
const searchSuggestions = (keywords: string, callback: any) => {
if (!keywords) {
return callback(suggestionData);
}
autoComplete.search(keywords, (status: any, result: any) => {
if (status === 'complete') {
suggestionData = result.tips.filter((item) => item.location);
suggestionData.forEach((item: any) => {
item.address = item.address || '';
item.district = item.district || '';
item.address = `${item.district}${item.address}`;
});
callback(suggestionData);
}
});
};
/**
* @description: 點擊選擇
* @param {*} item
* @return {*}
*/
const onSuggestionChoose = (item: any) => {
suggestionKeyWord.value = item.name;
chooseSuggestion = item;
chooseIndex.value = 0;
const point = item.location;
if (point) {
map.setZoomAndCenter(chooseZoom, [point.lng, point.lat]);
addMarker(point.lng, point.lat);
}
};
/**
* @description: 確定
* @return {*}
*/
const doneMap = () => {
// 地圖中心點
// const center = { ...map.getCenter() };
// getByLatLng({ lat: center.lat, lng: center.lng }).then((res) => {
// // console.log('接口獲取的值', res);
// if (res.result) {
// location = {
// country: res.result?.country?.i18nName,
// province: res.result?.province?.i18nName || '',
// city: res.result?.city?.i18nName,
// district: res.result?.district?.i18nName,
// address: res.result.raw?.formattedAddress,
// lat: center.lat,
// lng: center.lng,
// };
// }
// // 選中則取高德地圖返回的address
// if (chooseIndex.value || chooseIndex.value === 0) {
// location.address = poiData.value[chooseIndex.value].address || '';
// }
// suggestionKeyWord.value = '';
// emit('done-map', location);
// });
// TODO 由于數(shù)據(jù)規(guī)范性,需獲取經(jīng)緯度后重新請求三級地址
if (chooseIndex.value || chooseIndex.value === 0) {
location.address = poiData.value[chooseIndex.value].address || '';
}
console.log('選中的地址', location);
suggestionKeyWord.value = '';
emit('done-map', location);
};
onMounted(() => {
setTimeout(() => {
initMap(location);
}, 200);
});
</script>
<style scoped lang="scss">
#container {
margin: 0;
padding: 0;
width: 100%;
height: calc(100% - 50px);
}
.search-container {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
:deep(.el-autocomplete) {
width: 80%;
}
}
.map-body {
display: flex;
height: 450px;
&__left {
width: 70% !important;
height: 100% !important;
}
&__right {
flex: 1;
}
}
/* 地圖圖標(biāo)跳動動畫 */
.ele-map-picker-anim-bounce {
animation: elePickerAnimBounce 500ms;
animation-direction: alternate;
}
@keyframes elePickerAnimBounce {
0%,
60%,
75%,
90%,
to {
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0%,
to {
transform: translate3d(0, 0, 0);
}
25% {
transform: translate3d(0, -10px, 0);
}
50% {
transform: translate3d(0, -20px, 0);
}
75% {
transform: translate3d(0, -10px, 0);
}
}
.ele-map-picker-main-icon {
width: 26px;
position: absolute;
left: 50%;
bottom: 50%;
margin-left: -13px;
}
/* poi列表 */
.ele-map-picker-poi-list {
overflow: auto;
width: 300px;
}
.ele-map-picker-poi-item {
position: relative;
padding: 8px 30px 8px 44px;
border-bottom: 1px solid hsl(0deg 0% 60% / 15%);
cursor: pointer;
}
.ele-map-picker-poi-item:hover {
background-color: hsl(0deg 0% 60% / 5%);
}
.ele-map-picker-poi-item-icon {
position: absolute;
top: 50%;
left: 14px;
transform: translateY(-50%);
font-size: 20px;
opacity: 0.4;
}
.ele-map-picker-poi-item-title {
font-size: 14px;
}
.ele-map-picker-poi-item-address {
margin-top: 2px;
font-size: 12px;
opacity: 0.6;
}
.ele-map-picker-poi-item .ele-map-picker-poi-item-check {
position: absolute;
top: 50%;
right: 7px;
display: none;
font-size: 16px;
color: #3b74ff;
transform: translateY(-50%);
}
.ele-map-picker-poi-item-active .ele-map-picker-poi-item-check {
display: block;
}
</style>
<style lang="scss">
.map-body {
.amap-icon {
display: none;
}
}
</style>到此這篇關(guān)于Vue3實現(xiàn)地圖選點組件的示例代碼的文章就介紹到這了,更多相關(guān)Vue3地圖選點組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+elementUi中的table實現(xiàn)跨頁多選功能(示例詳解)
最近在開發(fā)工業(yè)品超市的后臺系統(tǒng),遇到一個需求,就是實現(xiàn)在一個table表格中多選數(shù)據(jù),在網(wǎng)上查了好多,有些方法真的是無語,下面通過本文給大家分享vue+elementUi中的table實現(xiàn)跨頁多選功能,感興趣的朋友跟隨小編一起看看吧2024-05-05
vue路由跳轉(zhuǎn)時判斷用戶是否登錄功能的實現(xiàn)
下面小編就為大家?guī)硪黄獀ue路由跳轉(zhuǎn)時判斷用戶是否登錄功能的實現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
vue中axios的get請求和post請求的傳參方式、攔截器示例代碼
Post是向服務(wù)器提交數(shù)據(jù)的一種請求,get是向服務(wù)器發(fā)索取數(shù)據(jù)的一種請求,post在真正接受數(shù)據(jù)之前會先將請求頭發(fā)送給服務(wù)器進行確認(rèn),然后才真正發(fā)送數(shù)據(jù),本文給大家介紹vue中axios的get請求和post請求的傳參方式、攔截器示例代碼,感興趣的朋友一起看看吧2023-10-10
vue 解決computed修改data數(shù)據(jù)的問題
今天小編就為大家分享一篇vue 解決computed修改data數(shù)據(jù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11

