Vue3實(shí)現(xiàn)地圖選點(diǎn)組件的示例代碼
Vue3地圖選點(diǎn)組件

實(shí)現(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']);
// 中心點(diǎn)位置
let location: any = reactive([116.4074, 39.9042]);
// 地圖縮放比例
const chooseZoom = 15;
// 搜索關(guān)鍵字
const suggestionKeyWord = ref('');
// 搜索建議列表
let suggestionData = reactive([]);
// 地圖實(shí)例
let map: any;
// 輸入建議實(shí)例
let autoComplete = reactive({});
// 選中的建議
let chooseSuggestion = reactive<any>({});
// 地圖中心標(biāo)記點(diǎn)
let centerMarker = reactive({});
// poi檢索實(shí)例
let placeSearch = reactive({});
// poi檢索的數(shù)據(jù)
const poiData = ref<Poi[]>([]);
// 選中的數(shù)據(jù)
const chooseIndex = ref<any>(null);
// 是否是點(diǎn)擊poi列表移動(dòng)地圖
let isSelMove = false;
// 圖標(biāo)是否顯示跳動(dòng)動(dòng)畫
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', // 申請(qǐng)好的Web端開發(fā)者Key,首次調(diào)用 load 時(shí)必填
version: '2.0', // 指定要加載的 JSAPI 的版本,缺省時(shí)默認(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,
});
// 輸入建議實(shí)例
autoComplete = new AMap.AutoComplete({
city: '全國(guó)',
});
// marker實(shí)例
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檢索實(shí)例
placeSearch = new AMap.PlaceSearch({
type: '', // poi檢索興趣點(diǎn)類別
pageSize: 30, // poi檢索每頁(yè)數(shù)量
pageIndex: 1,
extensions: 'all',
});
// 地圖加載完成事件
map.on('complete', () => {
chooseIndex.value = null;
const center = map.getCenter();
searchNearBy(center.lat, center.lng, true);
});
// 地圖移動(dòng)結(jié)束事件
map.on('moveend', () => {
const center = map.getCenter();
addMarker(center.lng, center.lat);
if (isSelMove) {
// poi列表點(diǎn)擊的移動(dòng)
isSelMove = false;
} else {
// 拖動(dòng)或搜索建議的移動(dòng)
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)地址庫(kù)-地址拼接省市區(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: 點(diǎn)擊選擇
* @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 = () => {
// 地圖中心點(diǎn)
// 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)緯度后重新請(qǐng)求三級(jí)地址
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)跳動(dòng)動(dòng)畫 */
.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實(shí)現(xiàn)地圖選點(diǎn)組件的示例代碼的文章就介紹到這了,更多相關(guān)Vue3地圖選點(diǎn)組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+elementUi中的table實(shí)現(xiàn)跨頁(yè)多選功能(示例詳解)
最近在開發(fā)工業(yè)品超市的后臺(tái)系統(tǒng),遇到一個(gè)需求,就是實(shí)現(xiàn)在一個(gè)table表格中多選數(shù)據(jù),在網(wǎng)上查了好多,有些方法真的是無(wú)語(yǔ),下面通過(guò)本文給大家分享vue+elementUi中的table實(shí)現(xiàn)跨頁(yè)多選功能,感興趣的朋友跟隨小編一起看看吧2024-05-05
vue?使用el-table循環(huán)生成表格的過(guò)程
這篇文章主要介紹了vue?使用el-table循環(huán)生成表格的過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue路由跳轉(zhuǎn)時(shí)判斷用戶是否登錄功能的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇vue路由跳轉(zhuǎn)時(shí)判斷用戶是否登錄功能的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
vue中axios的get請(qǐng)求和post請(qǐng)求的傳參方式、攔截器示例代碼
Post是向服務(wù)器提交數(shù)據(jù)的一種請(qǐng)求,get是向服務(wù)器發(fā)索取數(shù)據(jù)的一種請(qǐng)求,post在真正接受數(shù)據(jù)之前會(huì)先將請(qǐng)求頭發(fā)送給服務(wù)器進(jìn)行確認(rèn),然后才真正發(fā)送數(shù)據(jù),本文給大家介紹vue中axios的get請(qǐng)求和post請(qǐng)求的傳參方式、攔截器示例代碼,感興趣的朋友一起看看吧2023-10-10
vue 解決computed修改data數(shù)據(jù)的問(wèn)題
今天小編就為大家分享一篇vue 解決computed修改data數(shù)據(jù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11

