[APM32F4] 在 APM32F427 上移植 LVGL 的新方法

核心内容摘要

如何用Python打造专属大麦抢票工具?从入门到精通的实战指南
AI日报 - 2026年03月02日

CasRel模型一键部署教程:基于星图GPU平台的快速环境搭建

在前端开发中地图组件是非常常见的需求尤其是地址选择、经纬度获取这类场景。

本文将基于 Vue3 高德地图 JS API

0详细讲解如何封装一个功能完整、易用性强的地图地址选择组件包含地址搜索、地图点击选点、经纬度双向绑定等核心功能。

组件核心功能概览基础地图初始化支持自定义初始经纬度、缩放级别地址搜索地理编码输入地址自动定位并标记地图点击选点点击地图任意位置获取经纬度并标记反地理编码可选扩展通过经纬度反查具体地址自定义标记图标支持自定义标记点的样式和尺寸经纬度双向通信向父组件实时传递选中的经纬度完善的异常处理和生命周期管理前置准备在使用该组件前你需要完成以下准备工作

注册高德地图开发者账号获取Web 端开发者 Key和安全密钥securityJsCode

安装依赖npm install amap/amap-jsapi-loader --save

将代码中的mapConfig里的key和securityJsCode替换为你自己的const mapConfig { key: 你的高德Key, securityJsCode: 你的安全密钥, version:

0, plugins: [AMap.Scale, AMap.Geocoder, AMap.Marker] };功能详解与

使用方法组件基础使用父组件中引入这是最基础的使用方式直接引入组件并设置初始参数template div stylewidth: 100%; height: 600px; !-- 地图组件 -- MapSelector :initLng

1

397428 :initLat

3

90923 :initZoom11 coordChangehandleCoordChange / !-- 显示选中的经纬度 -- div 选中的经纬度, /div /div /template script setup import { ref } from vue; import MapSelector from ./components/MapSelector.vue; const selectedLng ref(

116.

; const selectedLat ref(

39.

; // 接收子组件传递的经纬度 const handleCoordChange (coord) { selectedLng.value coord.lng; selectedLat.value coord.lat; console.log(当前选中的经纬度, coord); }; /script参数说明initLng初始经度默认

1

397428initLat初始纬度默认

3

90923initZoom初始缩放级别默认 11markerIcon自定义标记图标地址可选markerSize标记图标尺寸默认 [40, 40]coordChange监听经纬度变化的事件地址搜索功能组件内置了地址搜索框使用方式非常简单在搜索框中输入地址 / 地点名称如 “北京市朝阳区天安门”点击 “搜索” 按钮组件会自动执行地理编码将地址转换为经纬度将地图中心定位到该位置并缩放至 15 级在该位置添加标记点通过coordChange事件向父组件传递经纬度若搜索失败如地址不存在会在搜索框下方显示错误提示点击 “清空” 按钮可清空搜索框内容和错误提示地图点击选点功能这是组件的核心交互功能之一点击地图上任意位置组件会自动获取点击位置的经纬度移除原有标记点在点击位置添加新标记通过coordChange事件向父组件实时传递新的经纬度在控制台打印点击位置的经纬度便于调试自定义标记图标如果你想替换默认的标记图标只需在父组件中传递markerIcon和markerSize参数MapSelector :initLng

1

397428 :initLat

3

90923 markerIcon/static/images/marker.png :markerSize[50, 50] coordChangehandleCoordChange /markerIcon传入图标文件的路径支持相对路径 / 绝对路径 / CDN 地址markerSize传入数组 [宽度高度]设置图标的显示尺寸调用组件暴露的方法高级用法组件通过defineExpose暴露了多个方法和数据你可以在父组件中通过ref调用template div stylewidth: 100%; height: 600px; MapSelector refmapRef :initLng

1

397428 :initLat

3

90923 coordChangehandleCoordChange / !-- 手动控制按钮 -- div stylemargin-top: 10px; button clicksetCustomCoord手动设置经纬度/button button clickreverseGeocodeTest反查地址/button button clickremoveMarker移除标记/button /div /div /template script setup import { ref } from vue; import MapSelector from ./components/MapSelector.vue; const mapRef ref(null); //

手动设置经纬度 const setCustomCoord async () { // 等待地图加载完成 await new Promise(resolve setTimeout(resolve,

); // 调用组件暴露的 setCoord 方法 mapRef.value.setCoord(

1

123456,

30.

; }; //

反地理编码通过经纬度查地址 const reverseGeocodeTest async () { await new Promise(resolve setTimeout(resolve,

); const result await mapRef.value.reverseGeocode(

1

397428,

39.

; console.log(反查地址结果, result); // 包含省市区等详细信息 }; //

移除标记点 const removeMarker () { mapRef.value.removeMarker(); }; const handleCoordChange (coord) { console.log(经纬度变化, coord); }; /script常用暴露方法说明setCoord(lng, lat)手动设置经纬度并添加标记geocode(address)手动执行地理编码地址转经纬度reverseGeocode(lng, lat)手动执行反地理编码经纬度转地址addMarker(lng, lat, title)添加标记点removeMarker()移除标记点selectedLng/selectedLat获取当前选中的经纬度监听初始参数变化组件内置了watch监听当父组件修改initLng/initLat时地图会自动更新标记位置和中心template div button clickchangeInitCoord修改初始经纬度/button MapSelector :initLnginitLng :initLatinitLat coordChangehandleCoordChange / /div /template script setup import { ref } from vue; import MapSelector from ./components/MapSelector.vue; const initLng ref(

116.

; const initLat ref(

39.

; // 修改初始经纬度 const changeInitCoord () { initLng.value

1

473701; initLat.value

3

230416; // 上海经纬度 }; const handleCoordChange (coord) { console.log(coord); }; /script组件核心优化点说明生命周期管理在onUnmounted中销毁地图实例、移除事件监听避免内存泄漏异常处理所有核心方法都有 try/catch 包裹地图加载完成后才执行操作标记点管理添加新标记前先移除旧标记避免重复标记兼容性使用 2D 地图模式兼容更多浏览器和设备事件防抖搜索和清空按钮添加了事件阻止避免冒泡

总结该组件基于 Vue3 高德地图 JS API

0 开发封装了地址搜索、地图选点、经纬度传递等核心功能开箱即用。

使用时需先替换高德地图 Key 和安全密钥父组件可通过 Props 配置初始参数通过coordChange事件接收经纬度。

组件暴露了丰富的方法如setCoord、reverseGeocode支持高级自定义操作满足不同场景需求。

源码template div classmap-wrapper div classsearch-container input v-modelsearchKeyword typetext placeholder请输入地址/地点名称 classsearch-input / button clickhandleSearch classsearch-btn搜索/button button clickclearSearch classclear-btn清空/button /div div idcontainer classmap-container/div div v-ifsearchError classerror-tip/div /div /template script setup import { ref, onMounted, onUnmounted, defineExpose, defineProps, computed, watch, defineEmits } from vue; import AMapLoader from amap/amap-jsapi-loader; //

定义自定义事件用于向父组件传递经纬度 const emit defineEmits([coordChange]); // 定义Props const props defineProps({ initLng: { type: Number, default:

1

397428 }, initLat: { type: Number, default:

3

90923 }, initZoom: { type: Number, default: 11 }, markerIcon: { type: String, default: }, markerSize: { type: Array, default: () [40, 40] } }); // 计算初始中心点 const initCenter computed(() [props.initLng, props.initLat]); // 响应式数据 const map ref(null); const geocoder ref(null); const marker ref(null); const mapLoaded ref(false); const searchKeyword ref(); const searchResult ref(null); const searchError ref(); // 新增存储选中的经纬度 const selectedLng ref(props.initLng); const selectedLat ref(props.initLat); // 地图配置 const mapConfig { key: , // 替换为你的key securityJsCode: , // 替换为你的安全密钥 version:

0, plugins: [AMap.Scale, AMap.Geocoder, AMap.Marker] // 显式声明Marker插件 }; // 移除标记 const removeMarker () { if (marker.value map.value) { map.value.remove(marker.value); marker.value null; } }; // 添加标记核心修复 const addMarker (lng, lat, title 标记点) { // 前置校验确保地图和AMap对象存在 if (!map.value || !window.AMap) { console.error(地图实例或AMap未初始化); return null; } // 先移除已有标记 removeMarker(); try { // 标记基础配置 const markerOptions { position: new window.AMap.LngLat(lng, lat), // 显式创建LngLat对象 title: title, anchor: bottom-center, zIndex: 9999, // 强制最高层级 offset: new window.AMap.Pixel(0,

// 调整锚点偏移避免标记被遮挡 }; // 自定义图标兼容默认图标 if (props.markerIcon) { markerOptions.icon new window.AMap.Icon({ size: new window.AMap.Size(...props.markerSize), image: props.markerIcon, imageSize: new window.AMap.Size(...props.markerSize) }); } else { // 强制使用高德默认图标兜底 markerOptions.icon new window.AMap.Icon({ size: new window.AMap.Size(32,

, image: https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png, imageSize: new window.AMap.Size(32,

}); } // 创建并添加标记 marker.value new window.AMap.Marker(markerOptions); map.value.add(marker.value); // 调试信息 console.log(标记添加成功, { lng, lat, title }); return marker.value; } catch (e) { console.error(创建标记失败, e); return null; } }; //

新增处理地图点击事件获取经纬度 const handleMapClick (e) { if (!mapLoaded.value) return; // 获取点击位置的经纬度 const lng e.lnglat.getLng(); const lat e.lnglat.getLat(); // 更新选中的经纬度 selectedLng.value lng; selectedLat.value lat; // 在点击位置添加标记 addMarker(lng, lat, 选中位置); // 触发自定义事件向父组件传递经纬度 emit(coordChange, { lng, lat }); console.log(地图点击位置, { lng, lat }); }; // 初始化地图核心修复 const initMap async () { try { //

设置安全密钥 window._AMapSecurityConfig { securityJsCode: mapConfig.securityJsCode }; //

加载AMap核心 const AMap await AMapLoader.load({ key: mapConfig.key, version: mapConfig.version, plugins: mapConfig.plugins }); //

挂载到window关键 window.AMap AMap; //

创建地图实例 map.value new AMap.Map(container, { viewMode: 2D, // 先改用2D模式3D可能有兼容性问题 zoom: props.initZoom, center: initCenter.value, resizeEnable: true // 开启自适应 }); //

添加比例尺 map.value.addControl(new AMap.Scale()); //

创建地理编码实例 geocoder.value new AMap.Geocoder({ radius: 1000, extensions: all }); //

监听地图加载完成事件关键 map.value.on(complete, () { mapLoaded.value true; // 地图加载完成后立即添加初始标记 addMarker(props.initLng, props.initLat, 初始位置); console.log(地图加载完成初始标记已添加); //

新增绑定地图点击事件 map.value.on(click, handleMapClick); }); } catch (e) { console.error(地图初始化失败, e); } }; // 地理编码 const geocode async (address) { if (!mapLoaded.value || !geocoder.value) throw new Error(地图未加载完成); return new Promise((resolve, reject) { geocoder.value.getLocation(address, (status, result) { if (status complete result.geocodes.length

{ const { lng, lat } result.geocodes[0].location; const formattedAddress result.geocodes[0].formattedAddress; // 更新选中的经纬度 selectedLng.value lng; selectedLat.value lat; map.value.setCenter([lng, lat]); map.value.setZoom(

; addMarker(lng, lat, formattedAddress); // 搜索结果添加标记 // 触发自定义事件 emit(coordChange, { lng, lat }); resolve({ lng, lat, address: formattedAddress }); } else { reject(new Error(地理编码失败${result.info || 地址不存在})); } }); }); }; // 反地理编码 const reverseGeocode async (lng, lat) { if (!mapLoaded.value || !geocoder.value) throw new Error(地图未加载完成); return new Promise((resolve, reject) { geocoder.value.getAddress([lng, lat], (status, result) { if (status complete result.regeocode) { const { formatted_address: address, addressComponent } result.regeocode; // 更新选中的经纬度 selectedLng.value lng; selectedLat.value lat; map.value.setCenter([lng, lat]); map.value.setZoom(

; addMarker(lng, lat, address); // 反编码结果添加标记 // 触发自定义事件 emit(coordChange, { lng, lat }); resolve({ address, province: addressComponent.province, city: addressComponent.city, district: addressComponent.district }); } else { reject(new Error(反地理编码失败${result.info || 坐标无效})); } }); }); }; // 搜索/清空逻辑 const handleSearch async () { if (window.event) { window.event.preventDefault(); window.event.stopPropagation(); } searchError.value ; if (!searchKeyword.value.trim()) { searchError.value 请输入地址; return; } try { searchResult.value await geocode(searchKeyword.value.trim()); } catch (error) { searchError.value error.message; } }; const clearSearch () { if (window.event) { window.event.preventDefault(); window.event.stopPropagation(); } searchKeyword.value ; searchResult.value null; searchError.value ; }; // 监听props变化更新标记位置 watch([() props.initLng, () props.initLat], ([newLng, newLat]) { if (mapLoaded.value) { selectedLng.value newLng; selectedLat.value newLat; addMarker(newLng, newLat, 初始位置); map.value.setCenter([newLng, newLat]); // 触发自定义事件 emit(coordChange, { lng: newLng, lat: newLat }); } }); // 生命周期 onMounted(() { // 确保DOM渲染完成后初始化地图 setTimeout(initMap,

; }); onUnmounted(() { // 移除点击事件监听 if (map.value) { map.value.off(click, handleMapClick); } removeMarker(); if (map.value) map.value.destroy(); map.value null; geocoder.value null; marker.value null; mapLoaded.value false; }); // 暴露方法和数据 defineExpose({ geocode, reverseGeocode, addMarker, removeMarker, map, mapLoaded, // 暴露选中的经纬度 selectedLng, selectedLat, // 暴露手动设置经纬度的方法 setCoord: (lng, lat) { selectedLng.value lng; selectedLat.value lat; addMarker(lng, lat, 手动设置位置); emit(coordChange, { lng, lat }); } }); /script style scoped .map-wrapper { width: 100%; height: 100%; position: relative; box-sizing: border-box; } .search-container { z-index: 1000; position: absolute; top: 10px; left: 10px; display: flex; gap: 10px; background: #fff; padding: 8px; border-radius: 4px; box-shadow: 0 2px 10px rgba(0,0,0,

0.

; } .search-input { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; outline: none; width: 200px; } .search-btn { background: #1677ff; color: white; border: none; padding: 0 12px; border-radius: 4px; cursor: pointer; } .clear-btn { background: #f5f5f5; color: #666; border: none; padding: 0 12px; border-radius: 4px; cursor: pointer; } /* 新增经纬度信息显示样式 */ .coord-info { z-index: 1000; position: absolute; top: 10px; right: 10px; background: #fff; padding: 6px 12px; border-radius: 4px; box-shadow: 0 2px 10px rgba(0,0,0,

0.

; font-size: 12px; color: #333; } .map-container { width: 100%; height: 100%; z-index: 1; } .error-tip { position: absolute; top: 70px; left: 10px; z-index: 1000; background: #fff2f0; color: #f5222d; padding: 8px 12px; border-radius: 4px; border: 1px solid #ffccc7; font-size: 12px; } /style

菠萝蜜在线观看高清电视剧-菠萝蜜在线观看高清电视剧应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123