vue3+amap-jsapi-loader参考自如链家相寓,根据项目坐标展示定位地图,点击地图的项目高亮,查询项目后选中并高亮地图
·


安装地图依赖 npm i @amap/amap-jsapi-loader
<template>
<div class="map-project-container">
<!-- 左侧地图区域 -->
<div class="map-area">
<div ref="mapContainer" class="map-container"></div>
<!-- 搜索框 -->
<div class="search-box">
<input v-model="searchKeyword" type="text" placeholder="搜索项目名称" @keyup.enter="handleSearch" />
<button @click="handleSearch" class="search-btn">
<i class="el-icon-search"></i>
</button>
</div>
</div>
<!-- 右侧项目列表区域 -->
<div class="project-list-area">
<div class="list-header">
<h3>项目列表</h3>
<span class="count">共找到 {{ filteredProjects.length }} 个项目</span>
</div>
<div class="project-cards">
<div v-for="project in filteredProjects" :key="project.id" class="project-card"
:class="{ 'selected': selectedProjectIds.includes(project.id) }" @click="handleProjectClick(project)">
<h4>{{ project.name }}</h4>
</div>
<div v-if="filteredProjects.length === 0" class="no-data">
暂无项目数据
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
import AMapLoader from '@amap/amap-jsapi-loader';
// 地图相关
const mapContainer = ref(null);
const map = ref(null);
const AMap = ref(null);
const markers = ref([]);
const mapReady = ref(false);
// 搜索相关
const searchKeyword = ref('');
// 项目数据相关
const allProjects = ref([]);
const selectedProjectIds = ref([]);
const currentMapBounds = ref(null);
// 计算属性:根据当前地图范围过滤项目
const filteredProjects = computed(() => {
if (!currentMapBounds.value) {
return allProjects.value;
}
return allProjects.value.filter(project => {
const { longitude, latitude } = project;
const { minLng, minLat, maxLng, maxLat } = currentMapBounds.value;
return longitude >= minLng &&
longitude <= maxLng &&
latitude >= minLat &&
latitude <= maxLat;
});
});
// 初始化模拟数据
const initMockData = () => {
// 生成一些模拟项目数据,分布在北京不同区域
const mockProjects = [
{ id: 1, name: '北京环球金融中心', longitude: 116.466, latitude: 39.910 },
{ id: 2, name: '北京国贸中心', longitude: 116.464, latitude: 39.909 },
{ id: 3, name: '北京财富中心', longitude: 116.468, latitude: 39.911 },
{ id: 4, name: '北京嘉里中心', longitude: 116.465, latitude: 39.912 },
{ id: 5, name: '北京华贸中心', longitude: 116.470, latitude: 39.915 },
{ id: 6, name: '北京金地中心', longitude: 116.462, latitude: 39.908 },
{ id: 7, name: '北京远洋国际中心', longitude: 116.472, latitude: 39.913 },
{ id: 8, name: '北京建外SOHO', longitude: 116.467, latitude: 39.907 },
{ id: 9, name: '北京三里屯SOHO', longitude: 116.455, latitude: 39.940 },
{ id: 10, name: '北京朝阳公园SOHO', longitude: 116.480, latitude: 39.930 },
{ id: 11, name: '北京中关村软件园', longitude: 116.298, latitude: 40.049 },
{ id: 12, name: '北京上地信息产业基地', longitude: 116.308, latitude: 40.058 },
{ id: 13, name: '北京金融街', longitude: 116.366, latitude: 39.913 },
{ id: 14, name: '北京丽泽商务区', longitude: 116.326, latitude: 39.858 },
{ id: 15, name: '北京望京SOHO', longitude: 116.476, latitude: 39.992 },
];
allProjects.value = mockProjects;
};
// 初始化地图
const initMap = async () => {
try {
// 加载高德地图
AMap.value = await AMapLoader.load({
key: 'e412c60656c1c8f9e4a5bbf261084dc5', // 高德地图API密钥
version: '2.0',
plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.Marker'],
});
// 创建地图实例,以北京为中心
map.value = new AMap.value.Map(mapContainer.value, {
zoom: 12,
center: [116.407, 39.904], // 北京中心坐标
viewMode: '2D',
features: ['bg', 'road', 'building', 'point'],
resizeEnable: true
});
// 添加工具条和比例尺
map.value.addControl(new AMap.value.ToolBar());
map.value.addControl(new AMap.value.Scale());
// 监听地图移动和缩放事件
map.value.on('moveend', handleMapMove);
map.value.on('zoomend', handleMapMove);
mapReady.value = true;
// 初始化地图边界
updateMapBounds();
// 创建地图标记
createMarkers();
} catch (error) {
console.error('地图初始化失败:', error);
}
};
// 更新地图边界
const updateMapBounds = () => {
if (!map.value) return;
const bounds = map.value.getBounds();
const southWest = bounds.getSouthWest();
const northEast = bounds.getNorthEast();
currentMapBounds.value = {
minLng: southWest.lng,
minLat: southWest.lat,
maxLng: northEast.lng,
maxLat: northEast.lat
};
};
// 处理地图移动事件
const handleMapMove = () => {
updateMapBounds();
createMarkers();
};
// 创建地图标记
const createMarkers = () => {
if (!mapReady.value || !map.value) return;
// 清除现有标记
clearMarkers();
// 为当前视图内的项目创建标记
filteredProjects.value.forEach(project => {
const marker = new AMap.value.Marker({
position: [project.longitude, project.latitude],
title: project.name,
map: map.value,
icon: new AMap.value.Icon({
size: new AMap.value.Size(30, 30),
image: selectedProjectIds.value.includes(project.id)
? '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png'
: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
imageSize: new AMap.value.Size(30, 30)
})
});
// 标记点击事件
marker.on('click', () => {
handleProjectClick(project);
});
markers.value.push(marker);
});
};
// 清除所有标记
const clearMarkers = () => {
if (map.value && markers.value.length > 0) {
markers.value.forEach(marker => map.value.remove(marker));
markers.value = [];
}
};
// 处理项目点击事件
const handleProjectClick = (project) => {
// 切换选中状态
const index = selectedProjectIds.value.indexOf(project.id);
if (index > -1) {
selectedProjectIds.value.splice(index, 1);
} else {
selectedProjectIds.value.push(project.id);
}
// 更新地图标记
createMarkers();
};
// 处理搜索事件
const handleSearch = () => {
if (!searchKeyword.value.trim()) return;
// 查找匹配的项目
const matchedProject = allProjects.value.find(project =>
project.name.includes(searchKeyword.value.trim())
);
if (matchedProject) {
// 定位到项目位置
map.value.setCenter([matchedProject.longitude, matchedProject.latitude]);
map.value.setZoom(15);
// 选中该项目
selectedProjectIds.value = [matchedProject.id];
// 更新地图标记
setTimeout(() => {
createMarkers();
}, 300);
} else {
alert('未找到匹配的项目');
}
};
// 组件挂载时初始化
onMounted(() => {
initMockData();
initMap();
});
// 组件卸载前清理
onBeforeUnmount(() => {
if (map.value) {
map.value.destroy();
map.value = null;
mapReady.value = false;
}
});
</script>
<style scoped>
.map-project-container {
display: flex;
width: 100%;
height: 100vh;
overflow: hidden;
}
/* 左侧地图区域 */
.map-area {
flex: 2;
position: relative;
height: 100%;
background-color: #f5f5f5;
}
.map-container {
width: 100%;
height: 100%;
}
.search-box {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
width: 400px;
height: 40px;
background-color: white;
border-radius: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
padding: 0 20px;
z-index: 100;
}
.search-box input {
flex: 1;
height: 100%;
border: none;
outline: none;
font-size: 14px;
}
.search-btn {
background: none;
border: none;
cursor: pointer;
color: #666;
font-size: 16px;
}
/* 右侧项目列表区域 */
.project-list-area {
flex: 1;
height: 100%;
background-color: white;
border-left: 1px solid #e0e0e0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.list-header {
padding: 20px;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
}
.list-header h3 {
margin: 0;
font-size: 18px;
color: #333;
}
.count {
font-size: 14px;
color: #999;
}
.project-cards {
flex: 1;
overflow-y: auto;
padding: 10px;
}
.project-card {
padding: 15px;
margin-bottom: 10px;
background-color: #f9f9f9;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
}
.project-card:hover {
background-color: #f0f0f0;
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.project-card.selected {
background-color: #fff0f0;
border-color: #ff4d4f;
}
.project-card h4 {
margin: 0;
font-size: 16px;
color: #333;
}
.no-data {
text-align: center;
padding: 40px 0;
color: #999;
font-size: 14px;
}
/* 滚动条样式 */
.project-cards::-webkit-scrollbar {
width: 6px;
}
.project-cards::-webkit-scrollbar-track {
background: #f1f1f1;
}
.project-cards::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
.project-cards::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
更多推荐
所有评论(0)