更新记录
1.6.4(2026-03-11)
功能基本稳定
1.6.3(2026-02-09)
安卓端增加高德 tts 初始化接口
1.6.2(2026-02-04)
安卓后台定位通知栏文字支持自定义
查看更多平台兼容性
uni-app x(4.87)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 5.0 | 12 | 12 | × |
pow-amapx
开发文档
UTS 语法 UTS API插件 UTS uni-app兼容模式组件 UTS 标准模式组件 Hello UTS
欢迎试用本插件,觉得合适再考虑购买
已支持:
- 高德sdk,鸿蒙+安卓+ios
定位功能:单次定位+持续定位+后台定位,安卓保活地图功能:控制以及绘制线+绘制圆+绘制多边形+绘制marker,地图上的多个marker可以同时显示气泡, 离线地图搜索功能:路线规划接口,地理信息查询,逆地理信息查询,行驶距离测量导航功能:驾车导航、步行导航、骑行导航、货车导航(鸿蒙端高德本身sdk目前不支持骑行/步行导航)TTS功能: ios 安卓 鸿蒙 三端都支持语音播报功能- 关于后台定位,开始持续定位时,会去申请后台定位权限,申请成功后,即使app退到后台,定位也会继续。如果用户没同意后台定位权限。那就得自己做界面提示用户,让用户去设置界面手动去设置允许后台(始终)定位权限了
- 只支持 uniappx,不支持 uniapp
- 三端接口统一,所有功能,只要调一样的接口就行
- 插件体积小,避免超过打包限制
- 努力增加功能中...
- 有未实现的需求的老板可以点上方进入[进入交流群]按钮定制需求,或者随时沟通
- 如果需要支持 uniapp 可以参考本人另一款插件高德定位、地图、导航全功能,简单易用,支持安卓和iOS
使用方式
- 在插件市场导入本插件到工程中(试用也可以)
- 【鸿蒙】把示例工程根目录中的
harmony-configs复制到自己工程根目录 - 【鸿蒙】鸿蒙不用打自定义基座,直接仿照示例工程中的
index.uvue中的getHarmonyAppId()方法,在自己项目中调用getHarmonyAppId()获取鸿蒙的专属appIdentifier - 申请高德地图的key。(鸿蒙要把
专属appIdentifier粘贴到高德地图key的申请页面) - 【鸿蒙,安卓,iOS】参考示例工程的
index.uvue中的initAmapSDK方法,调用插件的setAmapKey方法 - 【安卓】在
manifest.json里面把targetSdkVersion设置到33或者更高 - 【安卓,iOS】打自定义基座(鸿蒙不需要打)
- 【安卓,iOS】运行到自定义基座,【鸿蒙】直接运行
- 【鸿蒙,安卓,iOS】参考示例工程的
index.uvue中的申请定位权限,单次定位,持续定位方法 - 【鸿蒙】鸿蒙平台如果想调用持续后台定位,需要参考示例工程的
App.uvue,在自己工程的App.uvue的onShow调用amapOnAppShow(),在onHide调用amapOnAppHide() - 导航使用方式可以仿照
powamap/pages/index/navi-view.uvue创建一个导航页面,然后仿照powamap/pages/index/navi.uvue来设置导航参数 - 【HBuilderX的bug】【鸿蒙】4.87以及之前的hbx,试用插件鸿蒙端无法运行,不是插件问题,是 hbx 问题,要先这样绕过:把插件中的
项目根目录/uni_modules/pow-amapx/components目录剪切,粘贴到uni_modules之外,让components目录和uni_modules目录平级,变成项目根目录/components,然后就能正常运行了
示例页面
pages/index/index.uvue:入口页,汇总所有功能按钮(定位、搜索、工具、TTS、离线地图等),用于快速验证接口。pages/index/map.uvue:地图组件示例页,包含地图控制、绘制、手势、路径规划、热力图与海量点等。pages/index/navi.uvue:导航参数配置页,设置起终点、导航方式与模式。pages/index/navi-view.uvue:导航展示页,承载导航组件并接收导航回调。pages/index/cruise.uvue:巡航示例页,展示巡航/监听相关功能。pages/index/map-offline-map-controller.uvue:离线地图页(鸿蒙端),承载OfflineMapPage组件展示离线地图管理界面。pages/index/map-setup写法.uvue:地图页面的 vue3 setup 写法。
接口示例
接口功能
<template>
<view class="container">
<scroll-view class="scroll-area" scroll-y="true">
<text class="title">系统功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="jianchadingweiquanxian">检查定位权限</button>
<button type="primary" class="btn" size="mini" @click="jianchahoutaidinweiquanxian">检查和申请后台定位权限</button>
<button type="primary" class="btn" size="mini" @click="jiazailixian">加载离线地图</button>
<!-- #ifdef APP-ANDROID -->
<button type="primary" class="btn" size="mini" @click="shenqingtongzhilanquanxian">申请通知栏权限</button>
<button type="primary" class="btn" size="mini" @click="jianchahuluedianchi">检查忽略电池优化</button>
<button type="primary" class="btn" size="mini" @click="shenqinghuluedianchi">申请忽略电池优化</button>
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<button type="primary" class="btn" @click="reqHarmonyAppId" size='mini'>获取鸿蒙AppId</button>
<!-- #endif -->
<button type="primary" class="btn" size="mini" @click="goToMap">跳转到地图</button>
<button type="primary" class="btn" size="mini" @click="goToNavi">跳转到导航</button>
<button type="primary" class="btn" size="mini" @click="goToCruise">跳转到巡航</button>
</view>
<view class="content">
<text class="switch-label">屏幕常亮:{{keepScreenOn ? '开启' : '关闭'}}</text>
<switch class="switch-item" :checked="keepScreenOn" @change="toggleKeepScreenOn" />
</view>
<text class="title">定位功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="dancidingwei">单次定位</button>
<button type="primary" class="btn" size="mini" @click="chixudingwei">持续定位</button>
<button type="primary" class="btn" size="mini" @click="jieshuchixudingwei">结束持续定位</button>
</view>
<text class="title">搜索功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="searchPoi">关键词搜索</button>
<button type="primary" class="btn" size="mini" @click="searchNearby">周边搜索</button>
</view>
<view class="content">
<button type="primary" class="btn" size="mini" @click="searchGeocode">地理编码</button>
<button type="primary" class="btn" size="mini" @click="searchRegeocode">逆地理编码</button>
</view>
<text class="title">工具功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="calcDistance">计算距离</button>
<button type="primary" class="btn" size="mini" @click="calcArea">计算面积</button>
<button type="primary" class="btn" size="mini" @click="convertCoord">坐标转换</button>
<button type="primary" class="btn" size="mini" @click="calcDriveDistance">驾车距离测量</button>
</view>
<text class="title">TTS语音播报</text>
<view class="content">
<input class="voice-input" placeholder="输入要播放的文字" :value="ttsText" @input="inputTTSText" />
</view>
<view class="content">
<button type="primary" class="btn" size="mini" @click="readTTS">播放</button>
</view>
</scroll-view>
<!-- 结果显示区域 -->
<view class="result-area">
<text class="result-text">{{resultText}}</text>
</view>
</view>
</template>
<script lang="uts" setup>
import {
setAmapKey,
getAmapLocation,
startAmapLocation,
stopAmapLocation,
checkAndRequestLocationPermission,
checkAndRequestBackgroundLocationPermission,
manageOfflineMap,
checkAndRequestNotificationPermission,
setKeepScreenOn,
checkIgnoreBatteryOptimizations,
requestIgnoreBatteryOptimizations,
searchAmapPoisWithKeywords,
searchAmapPoisNearby,
searchAmapGeocode,
searchAmapRegeocode,
amapLineDistance,
amapCaculateArea,
amapCoordFromOtherCoord,
searchAmapDriveDistance,
amapTTS,
speakRu
} from '@/uni_modules/pow-amapx' // 导入插件内的方法
// 当前位置(用于搜索)
let currentLat = ref(39.91054)
let currentLon = ref(116.398111)
let resultText = ref('')
let ttsText = ref('我是文字转语音,支持英语,hello')
let keepScreenOn = ref(false)
// 追加日志并添加时间戳(最新日志在最上方)
const appendLog = (text : string) => {
const now = new Date()
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const ms = now.getMilliseconds().toString().padStart(3, '0')
const timestamp = `${hours}:${minutes}:${seconds}.${ms}`
const logLine = `[${timestamp}] ${text}`
if (resultText.value == '') {
resultText.value = logLine
} else {
resultText.value = logLine + '\n' + resultText.value
}
}
// #ifdef APP-HARMONY
import {
getHarmonyAppId
} from '@/uni_modules/pow-amapx'
const reqHarmonyAppId = () => {
// 获取鸿蒙AppId , 高德后台要填这个字段
let appid = getHarmonyAppId()
console.log(`appid: ${appid}`)
uni.showModal({
title: '鸿蒙AppId',
content: appid,
confirmText: '复制到剪切板',
cancelText: '取消',
success: res => {
if (res.confirm) {
uni.setClipboardData({
data: appid,
success: () => {
uni.showToast({
title: '已复制到剪切板'
})
}
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
// #endif
const initAmapSDK = () => {
/* setTencentKey
接收4个参数,分别是
1. ioskey
2. 安卓key
3. 鸿蒙key
*/
setAmapKey(
'2d3da96f1736d9403288c6ed95a5e85f',
'72566fdc715020e355f355e4d9b7ceb1',
'dc45c88c0fb6dbd5ffb54cd33475ab48')
}
onReady(() => {
uni.setNavigationBarTitle({
title: '高德demo'
})
initAmapSDK()
})
const jianchadingweiquanxian = () => {
console.log('请求定位权限');
checkAndRequestLocationPermission(res => {
console.log(`定位权限状态:${JSON.stringify(res)}`)
uni.showToast({
title: `请求权限回调:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const jianchahoutaidinweiquanxian = () => {
console.log('检查和申请后台定位权限')
checkAndRequestBackgroundLocationPermission(res => {
console.log(`后台定位权限状态:${JSON.stringify(res)}`)
uni.showToast({
title: `后台定位权限:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const jiazailixian = () => {
// #ifdef APP-HARMONY
uni.navigateTo({
url: '/pages/index/map-offline-map-controller'
})
// #endif
// #ifndef APP-HARMONY
manageOfflineMap()
// #endif
}
const shenqingtongzhilanquanxian = () => {
checkAndRequestNotificationPermission(res => {
console.log(`通知栏权限结果:${JSON.stringify(res)}`)
uni.showToast({
title: `通知栏权限:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const jianchahuluedianchi = () => {
checkIgnoreBatteryOptimizations(res => {
console.log(`忽略电池优化状态:${JSON.stringify(res)}`)
uni.showToast({
title: `忽略电池优化:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const shenqinghuluedianchi = () => {
requestIgnoreBatteryOptimizations(res => {
console.log(`申请忽略电池优化结果:${JSON.stringify(res)}`)
uni.showToast({
title: `申请忽略电池优化:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const toggleKeepScreenOn = () => {
keepScreenOn.value = !keepScreenOn.value
setKeepScreenOn(keepScreenOn.value)
uni.showToast({
title: keepScreenOn.value ? '已开启屏幕常亮' : '已关闭屏幕常亮',
icon: 'none'
})
}
const dancidingwei = () => {
uni.showLoading({
title: '正在定位...'
})
appendLog('正在定位...')
getAmapLocation({
needAddress: true, //是否需要返回位置描述
desiredAccuracy: 100, //期望的定位精度(单位米),iOS直接传给desiredAccuracy;安卓根据值选择定位模式 10 以内是仅使用卫星定位,其他情况默认定位类型;鸿蒙传给maxAccuracy
}, res => {
uni.hideLoading()
console.log(` 调用方收到定位结果:${JSON.stringify(res)} , typeof res :${typeof res}`)
// 更新当前位置
const lat = res['lat'] as Number | null
const lon = res['lon'] as Number | null
if (lat != null && lon != null) {
currentLat.value = lat!
currentLon.value = lon!
}
// 显示结果
const address = res['address']
const city = res['city']
let text = `定位成功! 经度: ${lon}, 纬度: ${lat}`
if (city != null) text += `, 城市: ${city}`
if (address != null) text += `, 地址: ${address}`
appendLog(text)
uni.showToast({
title: `定位成功`,
icon: 'success'
})
})
}
const chixudingwei = () => {
appendLog('开始持续定位...')
const enableBg = true
// #ifdef APP-ANDROID
if (enableBg) {
checkIgnoreBatteryOptimizations(res => {
const enabled = res['enabled'] as boolean | null
if (enabled == null || enabled == false) {
requestIgnoreBatteryOptimizations(() => {})
}
})
}
// #endif
startAmapLocation({
interval: 2000, // 定位间隔,单位毫秒,默认是2000
enableBg: enableBg, // 是否开启后台定位, 默认false
needAddress: true, //是否需要返回位置描述
bgTitle: '定位中', // 后台定位时显示的标题
desiredAccuracy: 100, //期望的定位精度(单位米),iOS直接传给desiredAccuracy;安卓根据值选择定位模式 10 以内是仅使用卫星定位,其他情况默认定位类型;鸿蒙传给maxAccuracy
}, res => {
console.log(`持续定位结果:${JSON.stringify(res)}`)
// 更新当前位置
const lat = res['lat'] as Number | null
const lon = res['lon'] as Number | null
if (lat != null && lon != null) {
currentLat.value = lat!
currentLon.value = lon!
}
// 显示结果
const address = res['address']
let text = `持续定位: 经度: ${lon}, 纬度: ${lat}`
if (address != null) text += `, 地址: ${address}`
appendLog(text)
})
}
const jieshuchixudingwei = () => {
stopAmapLocation({}, res => {
console.log(`停止持续定位结果:${JSON.stringify(res)}`)
appendLog(`停止持续定位: ${JSON.stringify(res)}`)
uni.showToast({
title: `停止持续定位结果:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
// ========== 搜索功能 ==========
const searchPoi = () => {
appendLog('正在搜索POI...')
searchAmapPoisWithKeywords('餐厅', '北京', (res : UTSJSONObject) => {
console.log('POI搜索结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const pois = res['pois'] as Array<UTSJSONObject> | null
if (pois != null && pois.length > 0) {
appendLog(`POI搜索: 找到 ${pois.length} 个结果`)
for (let i = 0; i < Math.min(5, pois.length); i++) {
const poi = pois[i]
appendLog(` ${i + 1}. ${poi['name']}`)
}
} else {
appendLog('POI搜索: 未找到结果')
}
} else {
appendLog(`POI搜索失败: ${errCode}`)
}
})
}
const searchNearby = () => {
appendLog('正在搜索周边...')
searchAmapPoisNearby(currentLat.value, currentLon.value, 1000, (res : UTSJSONObject) => {
console.log('周边搜索结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const pois = res['pois'] as Array<UTSJSONObject> | null
if (pois != null && pois.length > 0) {
appendLog(`周边搜索: 找到 ${pois.length} 个POI`)
for (let i = 0; i < Math.min(5, pois.length); i++) {
const poi = pois[i]
appendLog(` ${i + 1}. ${poi['name']}`)
}
} else {
appendLog('周边搜索: 周边无POI')
}
} else {
appendLog(`周边搜索失败: ${errCode}`)
}
})
}
const searchGeocode = () => {
appendLog('正在进行地理编码...')
searchAmapGeocode('天安门', '北京', (res : UTSJSONObject) => {
console.log('地理编码结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const addressList = res['addressList'] as Array<UTSJSONObject> | null
if (addressList != null && addressList.length > 0) {
const addr = addressList[0]
const lat = addr['lat'] ?? ''
const lon = addr['lon'] ?? ''
const name = addr['name'] ?? ''
appendLog(`地理编码成功: ${name}, 经度: ${lon}, 纬度: ${lat}`)
} else {
appendLog('地理编码: 未找到结果')
}
} else {
appendLog(`地理编码失败: ${errCode}`)
}
})
}
const searchRegeocode = () => {
appendLog('正在进行逆地理编码...')
searchAmapRegeocode(currentLat.value, currentLon.value, (res : UTSJSONObject) => {
console.log('逆地理编码结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const address = res['address'] ?? ''
const city = res['city'] ?? ''
const district = res['district'] ?? ''
appendLog(`逆地理编码成功: ${address}, 城市: ${city}, 区县: ${district}`)
} else {
appendLog(`逆地理编码失败: ${errCode}`)
}
})
}
// ========== 工具功能 ==========
const calcDistance = () => {
// 计算当前位置到天安门的距离
const tiananmenLat = 39.9087
const tiananmenLon = 116.3975
const distance = amapLineDistance(currentLat.value, currentLon.value, tiananmenLat, tiananmenLon)
appendLog(`当前位置到天安门的直线距离: ${distance.toFixed(2)} 米`)
}
const calcArea = () => {
// 计算一个区域的面积(左上角到右下角)
const lat1 = 39.92
const lng1 = 116.38
const lat2 = 39.90
const lng2 = 116.42
const area = amapCaculateArea(lat1, lng1, lat2, lng2)
appendLog(`区域面积: ${area.toFixed(2)} 平方米`)
}
const convertCoord = () => {
// GPS坐标转高德坐标示例
const gpsLat = 39.9087
const gpsLon = 116.3975
const result = amapCoordFromOtherCoord('gps', gpsLat, gpsLon)
const newLat = result['lat']
const newLon = result['lon']
appendLog(`GPS坐标转换: 原坐标: ${gpsLon}, ${gpsLat} -> 高德坐标: ${newLon}, ${newLat}`)
}
const calcDriveDistance = () => {
// 驾车距离测量示例:当前位置到天安门
const tiananmenLat = 39.9087
const tiananmenLon = 116.3975
appendLog('正在测量驾车距离...')
searchAmapDriveDistance(currentLat.value, currentLon.value, tiananmenLat, tiananmenLon, (res : UTSJSONObject) => {
console.log('驾车距离测量结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const distance = res['distance'] as Number | null
const duration = res['duration'] as Number | null
let text = `驾车距离测量成功: 起点(${currentLon.value}, ${currentLat.value}) -> 终点(${tiananmenLon}, ${tiananmenLat})`
if (distance != null) {
text += `, 距离: ${distance}米`
}
if (duration != null) {
const minutes = Math.floor(duration / 60)
const seconds = duration! % 60
text += `, 耗时: ${minutes}分${seconds}秒`
}
appendLog(text)
} else {
appendLog(`驾车距离测量失败: ${res['errMsg'] ?? errCode}`)
}
})
}
const goToMap = () => {
uni.navigateTo({
url: './map'
})
}
const goToNavi = () => {
uni.navigateTo({
url: './navi'
})
}
const goToCruise = () => {
uni.navigateTo({
url: './cruise'
})
}
// ========== TTS 语音播报 ==========
const inputTTSText = (e : InputEvent) => {
ttsText.value = e.detail.value
}
const readTTS = () => {
if (ttsText.value == '') {
uni.showToast({
title: '请输入播放文字',
icon: 'error'
})
return
}
amapTTS(ttsText.value)
appendLog(`正在播放: ${ttsText.value}`)
// setTimeout(() => {
// //测试后台播报,延迟期间把 app退到后台
// amapTTS(ttsText.value)
// appendLog(`正在播放: ${ttsText.value}`)
// }, 5000)
}
</script>
<style lang="scss">
.title {
font-size: 18px;
font-weight: bold;
color: #000;
margin-top: 20px;
margin-left: 10px;
}
.container {
display: flex;
flex-direction: column;
width: 750rpx;
height: 100%;
}
.scroll-area {
flex: 1;
}
.content {
margin-top: 10px;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
flex-wrap: wrap;
}
.btn {
margin-left: 10px;
margin-top: 5px;
}
.switch-label {
margin-left: 10px;
}
.switch-item {
margin-left: 10px;
}
.voice-input {
flex: 1;
border-radius: 4px;
border-width: 1px;
border-style: solid;
border-color: #ccc;
margin-top: 10px;
margin-left: 10px;
margin-right: 10px;
padding: 8px;
font-size: 14px;
}
.result-area {
height: 100px;
background-color: #f5f5f5;
padding: 10px;
overflow: hidden;
}
.result-text {
font-size: 14px;
color: #333;
line-height: 1.6;
}
</style>
地图功能
<template>
<view class="container">
<pow-amapx ref="mapRef" class="native-map" :center='mapCenter' :zoomLevel='mapZoomLevel' @load="onMapLoad"
@mapLoaded="onMapLoaded" @mapRendered="onMapRendered" @markerClick="onMarkerClick"
@markerDrag="onMarkerDrag" @cameraChange="onCameraChange"
@cameraChangeStart="onCameraChangeStart" @massPointClick="onMassPointClick"
@mapClick="onMapClick" @mapLongPress="onMapLongPress"></pow-amapx>
<!-- 按钮区域可滚动 -->
<scroll-view class="controls-area" scroll-y="true">
<!-- 地图控制按钮 -->
<view class="section">
<text class="section-title">地图控制</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="moveCenter">移动中心点</button>
<button class="btn" size="mini" type="primary" @click="zoomIn">放大</button>
<button class="btn" size="mini" type="primary" @click="zoomOut">缩小</button>
<button class="btn" size="mini" type="primary" @click="getMapInfo">获取地图信息</button>
<button class="btn" size="mini" type="warn" @click="locateToCurrent">归位</button>
</view>
<view class="btns">
<button class="btn" size="mini" type="default" @click="setMapTypeNormal">普通地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeSatellite">卫星地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeNight">夜间地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeNavi">导航地图</button>
</view>
<view class="btns">
<button class="btn" size="mini" :type="trafficEnabled ? 'warn' : 'primary'"
@click="toggleTraffic">{{trafficEnabled ? '关闭路况' : '开启路况'}}</button>
<button class="btn" size="mini" :type="compassEnabled ? 'warn' : 'primary'"
@click="toggleCompass">{{compassEnabled ? '隐藏指南针' : '显示指南针'}}</button>
<button class="btn" size="mini" :type="scaleEnabled ? 'warn' : 'primary'"
@click="toggleScale">{{scaleEnabled ? '隐藏比例尺' : '显示比例尺'}}</button>
</view>
<view class="btns">
<button class="btn" size="mini" :type="zoomControlsEnabled ? 'warn' : 'primary'"
@click="toggleZoomControls">{{zoomControlsEnabled ? '隐藏缩放按钮' : '显示缩放按钮'}}</button>
</view>
</view>
<!-- 绘制功能按钮 -->
<view class="section">
<text class="section-title">绘制功能</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="drawPolyline">画折线</button>
<button class="btn" size="mini" type="warn" @click="removePolyline">删除折线</button>
<button class="btn" size="mini" type="primary" @click="drawCircle">画圆形</button>
<button class="btn" size="mini" type="warn" @click="removeCircle">删除圆形</button>
</view>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="drawPolygon">画多边形</button>
<button class="btn" size="mini" type="warn" @click="removePolygon">删除多边形</button>
<button class="btn" size="mini" type="primary" @click="drawTexturedPolyline">画纹理线</button>
<button class="btn" size="mini" type="default" @click="clearAllOverlays">清除所有</button>
</view>
</view>
<!-- Marker 功能按钮 -->
<view class="section">
<text class="section-title">Marker 功能</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="addMarkers">添加Markers</button>
<button class="btn" size="mini" type="warn" @click="removeAllMarkers">删除所有Markers</button>
<button class="btn" size="mini" type="default" @click="zoomToFitMarkers">缩放包含所有Markers</button>
</view>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="addMoveAnimation">点平滑移动</button>
</view>
</view>
<!-- 手势控制按钮 -->
<view class="section">
<text class="section-title">手势控制</text>
<view class="btns">
<button class="btn" size="mini" :type="zoomGestureEnabled ? 'warn' : 'primary'"
@click="toggleZoomGesture">{{zoomGestureEnabled ? '禁止缩放' : '允许缩放'}}</button>
<button class="btn" size="mini" :type="scrollGestureEnabled ? 'warn' : 'primary'"
@click="toggleScrollGesture">{{scrollGestureEnabled ? '禁止拖拽' : '允许拖拽'}}</button>
</view>
<view class="btns">
<button class="btn" size="mini" :type="rotateGestureEnabled ? 'warn' : 'primary'"
@click="toggleRotateGesture">{{rotateGestureEnabled ? '禁止旋转' : '允许旋转'}}</button>
<button class="btn" size="mini" :type="tiltGestureEnabled ? 'warn' : 'primary'"
@click="toggleTiltGesture">{{tiltGestureEnabled ? '禁止倾斜' : '允许倾斜'}}</button>
</view>
</view>
<!-- 位置功能按钮 -->
<view class="section">
<text class="section-title">位置功能</text>
<view class="btns">
<button class="btn" size="mini" :type="myLocationEnabled ? 'warn' : 'primary'"
@click="toggleMyLocation">{{myLocationEnabled ? '隐藏位置' : '显示位置'}}</button>
<button class="btn" size="mini" :type="followMode ? 'warn' : 'primary'"
@click="toggleFollowMode">{{followMode ? '取消跟随' : '跟随模式'}}</button>
</view>
</view>
<!-- 路径规划按钮 -->
<view class="section">
<text class="section-title">路径规划</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="searchDriveRoute">驾车路径规划</button>
<button class="btn" size="mini" type="primary" @click="searchRideRoute">骑行路径规划</button>
<button class="btn" size="mini" type="primary" @click="searchWalkRoute">步行路径规划</button>
<button class="btn" size="mini" type="warn" @click="clearRoutePolyline">清除规划路线</button>
</view>
</view>
<!-- 热力图按钮 -->
<view class="section">
<text class="section-title">热力图</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="showHeatMap">显示热力图</button>
<button class="btn" size="mini" type="warn" @click="hideHeatMap">隐藏热力图</button>
</view>
</view>
<!-- 海量点图层按钮 -->
<view class="section">
<text class="section-title">海量点图层</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="showMassPoints">显示海量点</button>
<button class="btn" size="mini" type="warn" @click="hideMassPoints">隐藏海量点</button>
</view>
</view>
<!-- 结果显示区域 -->
<view class="result-area">
<text class="result-text">{{resultText}}</text>
</view>
</scroll-view>
</view>
</template>
<script>
import lineTextureData from '../../static/linetexture.json'
import { searchAmapDriveRoute, searchAmapRideRoute, searchAmapWalkRoute, getAmapLocation, getFileLocalPath } from '@/uni_modules/pow-amapx'
export default {
data() {
return {
mapCenter: '116.398111,39.91054',
mapZoomLevel: 12,
resultText: '点击按钮测试地图控制功能...',
trafficEnabled: false,
compassEnabled: false,
scaleEnabled: true,
zoomControlsEnabled: true,
// 手势控制状态
zoomGestureEnabled: true,
scrollGestureEnabled: true,
rotateGestureEnabled: true,
tiltGestureEnabled: true,
// 位置功能状态
myLocationEnabled: false,
followMode: false,
// 热力图状态
heatMapShown: false,
// 海量点图层状态
massPointsShown: false,
// 路径规划起终点
routeStartPoint: { lat: 39.909187, lng: 116.397455 }, // 天安门
routeEndPoint: { lat: 39.984120, lng: 116.307484 }, // 北京大学
routePolylineId: 'route_polyline_001',
// 绘制数据
polylineData: {
id: 'polyline001',
points: [
'116.362209,39.887487',
'116.422897,39.878002',
'116.372105,39.90651',
'116.428945,39.89663'
],
width: 10,
color: '#FF0000',
isDottedLine: false
},
circleData: {
id: 'circle001',
lat: 39.91054,
lng: 116.398111,
radius: 1000,
lineWidth: 5,
strokeColor: '#FF0000',
fillColor: '#50FF0000'
},
polygonData: {
id: 'polygon001',
points: [
'116.404,39.915',
'116.408,39.917',
'116.412,39.919',
'116.412,39.921',
'116.410,39.923',
'116.406,39.923',
'116.404,39.921'
],
lineWidth: 5,
strokeColor: '#0000FF',
fillColor: '#500000FF'
},
texturedPolylineData: {
id: 'texturedPolyline001',
points: [
'116.352209,39.897487',
'116.402897,39.888002',
'116.362105,39.91651',
'116.418945,39.90663',
'116.438945,39.92663'
],
width: 20,
texture: lineTextureData.imageBase64
},
markersData: [
{
id: 'marker001',
lat: 39.909187,
lng: 116.397455,
title: '天安门',
subtitle: '北京市中心',
anchor: 'bottomCenter',
icon: 'https://s11.ax1x.com/2023/03/17/ppGtKhD.png', //支持网络图片和 base64 字符串,免费生成网络图片的地址 imgse.com
draggable: true,
rotate: 45,
iconWidth: 40,
iconHeight: 40,
callout: {
content: '天安门广场',
textColor: '#ffffff',
fontSize: 14,
borderRadius: 8,
borderColor: '#007AFF',
borderWidth: 1,
bgColor: '#007AFF',
padding: 8,
leftIcon: {
url: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
width: 16,
height: 16
}
}
},
{
id: 'marker002',
lat: 39.915,
lng: 116.404,
title: '故宫',
anchor: 'center',
callout: {
content: '故宫博物院',
textColor: '#333333',
fontSize: 12,
borderRadius: 5,
bgColor: '#FFD700',
padding: 5,
rightIcon: {
url: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/dir-via-marker.png',
width: 16,
height: 16
}
}
},
{
id: 'marker003',
lat: 39.905,
lng: 116.410,
title: '圆角图标示例',
anchor: 'center',
icon: 'https://s41.ax1x.com/2026/01/05/pZaq0a9.jpg',
iconWidth: 50,
iconHeight: 50,
iconRadius: 25 // 设置圆角半径,值为图标宽高一半时显示为圆形
}
],
moveAnimationPoints: [
'116.37,39.91',
'116.380298,39.907771',
'116.38,39.90',
'116.385298,39.907771',
'116.40,39.90',
'116.40772,39.909252',
'116.41,39.89',
'116.423857,39.889498',
'116.422312,39.899639',
'116.425273,39.902273'
],
moveAnimationPolylineId: 'move_polyline_001',
moveAnimationMarkerId: 'move_marker_001'
}
},
methods: {
// ========== 地图事件 ==========
onMapLoad() {
console.log('地图组件加载完成1')
this.resultText = '地图组件加载完成'
},
onMapLoaded(e : UniNativeViewEvent) {
console.log('地图已加载')
this.resultText = '地图已加载'
},
onMapRendered(e : UniNativeViewEvent) {
console.log('地图已渲染')
this.resultText = '地图已渲染'
},
// ========== 地图控制 ==========
moveCenter() {
const lat = 39.9 + Math.random() * 0.1
const lon = 116.3 + Math.random() * 0.2
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('updateCenter', `${lon},${lat}`)
}
this.resultText = `中心点移动到: ${lon.toFixed(6)}, ${lat.toFixed(6)}`
},
zoomIn() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const zoom = mapRef.$callMethod('getZoomLevel') as number
const nextZoom = Math.min(20, zoom + 1)
mapRef.$callMethod('updateZoomLevel', nextZoom)
this.resultText = `缩放级别: ${nextZoom.toFixed(1)}`
}
},
zoomOut() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const zoom = mapRef.$callMethod('getZoomLevel') as number
const nextZoom = Math.max(3, zoom - 1)
mapRef.$callMethod('updateZoomLevel', nextZoom)
this.resultText = `缩放级别: ${nextZoom.toFixed(1)}`
}
},
getMapInfo() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const center = mapRef.$callMethod('getCenter') as UTSJSONObject
const zoom = mapRef.$callMethod('getZoomLevel') as number
const lat = center['lat'] as number
const lon = center['lon'] as number
this.resultText = `当前地图信息:\n中心点: ${lon.toFixed(6)}, ${lat.toFixed(6)}\n缩放级别: ${zoom.toFixed(1)}`
}
},
setMapTypeNormal() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 1)
this.resultText = '已切换到普通地图'
}
},
setMapTypeSatellite() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 2)
this.resultText = '已切换到卫星地图'
}
},
setMapTypeNight() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 3)
this.resultText = '已切换到夜间地图'
}
},
setMapTypeNavi() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 4)
this.resultText = '已切换到导航地图'
}
},
toggleTraffic() {
this.trafficEnabled = !this.trafficEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('showTraffic', this.trafficEnabled)
this.resultText = this.trafficEnabled ? '已开启实时路况' : '已关闭实时路况'
}
},
toggleCompass() {
this.compassEnabled = !this.compassEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setCompassEnabled', this.compassEnabled)
this.resultText = this.compassEnabled ? '已显示指南针' : '已隐藏指南针'
}
},
toggleScale() {
this.scaleEnabled = !this.scaleEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setScaleControlsEnabled', this.scaleEnabled)
this.resultText = this.scaleEnabled ? '已显示比例尺' : '已隐藏比例尺'
}
},
toggleZoomControls() {
this.zoomControlsEnabled = !this.zoomControlsEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setZoomControlsEnabled', this.zoomControlsEnabled)
this.resultText = this.zoomControlsEnabled ? '已显示缩放按钮' : '已隐藏缩放按钮'
}
},
// ========== 绘制功能 ==========
drawPolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawPolyline', JSON.stringify(this.polylineData))
this.resultText = '已绘制折线'
}
},
removePolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolyline', '{"id":"polyline001"}')
this.resultText = '已删除折线'
}
},
drawCircle() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawCircle', JSON.stringify(this.circleData))
this.resultText = '已绘制圆形'
}
},
removeCircle() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeCircle', '{"id":"circle001"}')
this.resultText = '已删除圆形'
}
},
drawPolygon() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawPolygon', JSON.stringify(this.polygonData))
this.resultText = '已绘制多边形'
}
},
removePolygon() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolygon', '{"id":"polygon001"}')
this.resultText = '已删除多边形'
}
},
drawTexturedPolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawTexturedPolyline', JSON.stringify(this.texturedPolylineData))
this.resultText = '已绘制纹理折线'
}
},
clearAllOverlays() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeAllPolylines')
mapRef.$callMethod('removeAllCircles')
mapRef.$callMethod('removeAllPolygons')
this.resultText = '已清除所有覆盖物'
}
},
// ========== Marker 功能 ==========
addMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
// 获取本地图片路径的方法,从插件导入使用
getFileLocalPath(
'../../static/ball.png', //资源在 hbx 中的相对路径
(localIconPath : string) => { //获取到的资源原生本地路径
const marker004 = {
id: 'marker004',
lat: 39.925,
lng: 116.395,
title: '本地图标示例',
anchor: 'center',
icon: localIconPath, //只能支持原生用的本地路径
iconWidth: 40,
iconHeight: 40
}
const markersDataWithLocal = [...this.markersData, marker004]
mapRef.$callMethod('addMarkers', JSON.stringify(markersDataWithLocal))
this.resultText = '已添加Markers(marker004使用本地图片)'
},
(err : any) => {
this.resultText = '获取本地图片路径失败: ' + JSON.stringify(err)
}
)
}
},
removeAllMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeAllMarkers')
this.resultText = '已删除所有Markers'
}
},
addMoveAnimation() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('updateZoomLevel', 13)
mapRef.$callMethod('drawPolyline', JSON.stringify({
id: this.moveAnimationPolylineId,
points: this.moveAnimationPoints,
width: 8,
color: '#FF0000',
isDottedLine: false
}))
getFileLocalPath(
'../../static/ball.png',
(localIconPath : string) => {
const options = {
id: this.moveAnimationMarkerId,
points: this.moveAnimationPoints,
duration: 10,
icon: localIconPath,
iconWidth: 40,
iconHeight: 40,
anchor: 'center',
removeMarkerWhenFinish: false
}
mapRef.$callMethod('addMoveAnimation', JSON.stringify(options))
this.resultText = '开始点平滑移动'
},
(err : any) => {
this.resultText = '获取本地图片路径失败: ' + JSON.stringify(err)
}
)
}
},
onMarkerClick(e : UniNativeViewEvent) {
console.log('Marker点击:', JSON.stringify(e.detail))
this.resultText = 'Marker点击: ' + JSON.stringify(e.detail)
},
onMarkerDrag(e : UniNativeViewEvent) {
console.log('Marker拖拽:', JSON.stringify(e.detail))
this.resultText = 'Marker拖拽: ' + JSON.stringify(e.detail)
},
// ========== 海量点点击回调 ==========
onMassPointClick(e : UniNativeViewEvent) {
console.log('海量点点击:', JSON.stringify(e.detail))
// 解析点击的海量点数据
try {
const detailStr = e.detail['detail'] as string
if (detailStr != null) {
const detail = JSON.parseObject(detailStr)
if (detail != null) {
const lat = detail['lat'] as number
const lon = detail['lon'] as number
const name = detail['name'] as string | null
const deviceId = detail['deviceId'] as string | null
let text = `海量点点击:\n经度: ${lon?.toFixed(6)}\n纬度: ${lat?.toFixed(6)}`
if (name != null) text += `\n名称: ${name}`
if (deviceId != null) text += `\n设备ID: ${deviceId}`
this.resultText = text
}
}
} catch (err) {
console.log('解析海量点点击数据失败:', err)
this.resultText = '海量点点击: ' + JSON.stringify(e.detail)
}
},
// ========== 视角改变回调 ==========
onCameraChange(e : UniNativeViewEvent) {
console.log('视角改变完成:', JSON.stringify(e.detail))
// 解析回调数据
try {
const detailStr = e.detail['detail'] as string
if (detailStr != null) {
const detail = JSON.parseObject(detailStr)
if (detail != null) {
const centerLat = detail['centerLat'] as number
const centerLng = detail['centerLng'] as number
const zoomLevel = detail['zoomLevel'] as number
const rotation = detail['rotation'] as number
const pitch = detail['pitch'] as number
this.resultText = `视角改变完成:\n中心点: ${centerLng.toFixed(6)}, ${centerLat.toFixed(6)}\n缩放: ${zoomLevel.toFixed(1)}\n旋转: ${rotation.toFixed(1)}°\n俯仰: ${pitch.toFixed(1)}°`
}
}
} catch (err) {
console.log('解析视角数据失败:', err)
}
},
onCameraChangeStart(e : UniNativeViewEvent) {
console.log('视角开始改变:', JSON.stringify(e.detail))
},
// ========== 地图点击回调 ==========
onMapClick(e : UniNativeViewEvent) {
console.log('地图点击:', JSON.stringify(e.detail))
// 解析点击的坐标数据
try {
const detailStr = e.detail['detail'] as string
if (detailStr != null) {
const detail = JSON.parseObject(detailStr)
if (detail != null) {
const latLng = detail['latLng'] as UTSJSONObject
if (latLng != null) {
const lat = latLng['lat'] as number
const lng = latLng['lng'] as number
this.resultText = `地图点击:\n经度: ${lng.toFixed(6)}\n纬度: ${lat.toFixed(6)}`
}
}
}
} catch (err) {
console.log('解析地图点击数据失败:', err)
this.resultText = '地图点击: ' + JSON.stringify(e.detail)
}
},
// ========== 地图长按回调 ==========
onMapLongPress(e : UniNativeViewEvent) {
console.log('地图长按:', JSON.stringify(e.detail))
// 解析长按的坐标数据
try {
const detailStr = e.detail['detail'] as string
if (detailStr != null) {
const detail = JSON.parseObject(detailStr)
if (detail != null) {
const latLng = detail['latLng'] as UTSJSONObject
if (latLng != null) {
const lat = latLng['lat'] as number
const lng = latLng['lng'] as number
this.resultText = `地图长按:\n经度: ${lng.toFixed(6)}\n纬度: ${lat.toFixed(6)}`
}
}
}
} catch (err) {
console.log('解析地图长按数据失败:', err)
this.resultText = '地图长按: ' + JSON.stringify(e.detail)
}
},
// ========== 缩放以包含 Markers ==========
zoomToFitMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
// 构造参数,包含所有 marker 的坐标
const params = {
markers: this.markersData, // 使用已添加的 markers 数据
paddingTop: 5, // 上边距,单位像素
paddingBottom: 5, // 下边距
paddingLeft: 5, // 左边距
paddingRight: 5, // 右边距
animated: true // 是否使用动画
}
mapRef.$callMethod('setVisibleIncludeMarkers', JSON.stringify(params))
this.resultText = '已缩放地图以包含所有Markers'
}
},
// ========== 手势控制 ==========
toggleZoomGesture() {
this.zoomGestureEnabled = !this.zoomGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setZoomGesturesEnabled', this.zoomGestureEnabled)
this.resultText = this.zoomGestureEnabled ? '已允许缩放手势' : '已禁止缩放手势'
}
},
toggleScrollGesture() {
this.scrollGestureEnabled = !this.scrollGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setScrollGesturesEnabled', this.scrollGestureEnabled)
this.resultText = this.scrollGestureEnabled ? '已允许拖拽手势' : '已禁止拖拽手势'
}
},
toggleRotateGesture() {
this.rotateGestureEnabled = !this.rotateGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setRotateGesturesEnabled', this.rotateGestureEnabled)
this.resultText = this.rotateGestureEnabled ? '已允许旋转手势' : '已禁止旋转手势'
}
},
toggleTiltGesture() {
this.tiltGestureEnabled = !this.tiltGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setTiltGesturesEnabled', this.tiltGestureEnabled)
this.resultText = this.tiltGestureEnabled ? '已允许倾斜手势' : '已禁止倾斜手势'
}
},
// ========== 位置功能 ==========
toggleMyLocation() {
this.myLocationEnabled = !this.myLocationEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const params = {
show: this.myLocationEnabled,
updatingMapCenter: this.followMode
}
mapRef.$callMethod('showMyLocation', JSON.stringify(params))
this.resultText = this.myLocationEnabled ? '已显示自身位置' : '已隐藏自身位置'
}
},
toggleFollowMode() {
this.followMode = !this.followMode
// 如果位置已显示,更新跟随模式
if (this.myLocationEnabled) {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const params = {
show: true,
updatingMapCenter: this.followMode
}
mapRef.$callMethod('showMyLocation', JSON.stringify(params))
}
}
this.resultText = this.followMode ? '已开启跟随模式(地图跟随位置移动)' : '已关闭跟随模式'
},
// ========== 路径规划功能 ==========
logRouteResult(result: UTSJSONObject, routeType: string) {
const errCode = result['errCode'] as number
const errMsg = result['errMsg'] as string
const distance = result['distance'] as number
const duration = result['duration'] as number
const taxiCost = result['taxiCost'] as number
const polyline = result['polyline'] as string[]
const steps = result['steps'] as UTSJSONObject[]
const summary = {
errCode: errCode,
errMsg: errMsg,
distance: distance,
duration: duration,
taxiCost: taxiCost,
polylineCount: polyline != null ? polyline.length : 0,
stepsCount: steps != null ? steps.length : 0
}
console.log(`${routeType}路径规划结果: ` + JSON.stringify(summary))
},
searchDriveRoute() {
this.resultText = '正在搜索驾车路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapDriveRoute(
latStart,
lngStart,
latEnd,
lngEnd,
0, // strategy: 0-速度优先
(result : UTSJSONObject) => {
this.logRouteResult(result, '驾车')
this.handleRouteResult(result, '驾车')
}
)
},
searchRideRoute() {
this.resultText = '正在搜索骑行路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapRideRoute(
latStart,
lngStart,
latEnd,
lngEnd,
(result : UTSJSONObject) => {
this.logRouteResult(result, '骑行')
this.handleRouteResult(result, '骑行')
}
)
},
searchWalkRoute() {
this.resultText = '正在搜索步行路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapWalkRoute(
latStart,
lngStart,
latEnd,
lngEnd,
(result : UTSJSONObject) => {
this.logRouteResult(result, '步行')
this.handleRouteResult(result, '步行')
}
)
},
handleRouteResult(result : UTSJSONObject, routeType : string) {
const errCode = result['errCode'] as number
if (errCode !== 0) {
this.resultText = `${routeType}路径规划失败,错误码: ${errCode}`
return
}
const distance = result['distance'] as number
const duration = result['duration'] as number
const polyline = result['polyline'] as string[]
const taxiCost = result['taxiCost'] as number
// 显示规划结果信息
let info = `${routeType}路径规划成功\n距离: ${(distance / 1000).toFixed(2)} 公里\n预计耗时: ${Math.ceil(duration / 60)} 分钟`
if (taxiCost != null && taxiCost > 0) {
info += `\n预估打车费: ¥${taxiCost.toFixed(0)}`
}
info += `\n路线点数: ${polyline.length}`
this.resultText = info
// 在地图上绘制路线
if (polyline.length > 0) {
this.drawRouteOnMap(polyline, routeType)
}
},
drawRouteOnMap(polyline : string[], routeType : string) {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
// 根据路线类型设置不同颜色
let color = '#3498db' // 默认蓝色
if (routeType === '驾车') {
color = '#e74c3c' // 红色
} else if (routeType === '骑行') {
color = '#2ecc71' // 绿色
} else if (routeType === '步行') {
color = '#9b59b6' // 紫色
}
// 先删除之前的路线和起终点marker
mapRef.$callMethod('removePolyline', JSON.stringify({ id: this.routePolylineId }))
mapRef.$callMethod('removeSomeMarkers', JSON.stringify([{ id: 'route_start_marker' }, { id: 'route_end_marker' }]))
// 绘制新路线
const routeData = {
id: this.routePolylineId,
points: polyline,
width: 12,
color: color,
isDottedLine: false
}
mapRef.$callMethod('drawPolyline', JSON.stringify(routeData))
// 添加起终点marker
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
const routeMarkers = [
{
id: 'route_start_marker',
lat: latStart,
lng: lngStart,
title: '起点',
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/start.png',
iconWidth: 19,
iconHeight: 31,
anchor: 'bottomCenter'
},
{
id: 'route_end_marker',
lat: latEnd,
lng: lngEnd,
title: '终点',
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/end.png',
iconWidth: 19,
iconHeight: 31,
anchor: 'bottomCenter'
}
]
mapRef.$callMethod('addMarkers', JSON.stringify(routeMarkers))
// 延迟设置可视区域,确保 markers 已添加完成后再调整视野
if (polyline.length >= 2) {
setTimeout(() => {
const mapRefInner = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRefInner != null) {
const params = {
markers: routeMarkers,
paddingTop: 80,
paddingBottom: 80,
paddingLeft: 40,
paddingRight: 40,
animated: true
}
mapRefInner.$callMethod('setVisibleIncludeMarkers', JSON.stringify(params))
}
}, 300)
}
},
clearRoutePolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolyline', JSON.stringify({ id: this.routePolylineId }))
mapRef.$callMethod('removeSomeMarkers', JSON.stringify([{ id: 'route_start_marker' }, { id: 'route_end_marker' }]))
this.resultText = '已清除规划路线'
}
},
// ========== 热力图功能 ==========
showHeatMap() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
if (this.heatMapShown) {
this.resultText = '热力图已显示'
return
}
// 生成热力图数据点
const points : UTSJSONObject[] = []
const centerX = 39.909187
const centerY = 116.397455
// 生成多个热力点
for (let i = 0; i < 50; i++) {
const offsetX = (Math.random() - 0.5) * 0.1
const offsetY = (Math.random() - 0.5) * 0.1
const intensity = Math.random()
const point = new UTSJSONObject()
point.set('lat', centerX + offsetX)
point.set('lng', centerY + offsetY)
point.set('intensity', intensity)
points.push(point)
}
// 热力图渐变配置
const gradient = new UTSJSONObject()
gradient.set('colors', ['#0000FF', '#00FF00', '#FFFF00', '#FF0000']) // 蓝→绿→黄→红
gradient.set('startPoints', [0.1, 0.3, 0.6, 1.0])
// 构建热力图参数
const heatMapParams = new UTSJSONObject()
heatMapParams.set('id', 'heatmap_001')
heatMapParams.set('points', points)
heatMapParams.set('gradient', gradient)
mapRef.$callMethod('showHeatMap', JSON.stringify(heatMapParams))
this.heatMapShown = true
this.resultText = '已显示热力图(50个随机点)'
},
hideHeatMap() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
if (!this.heatMapShown) {
this.resultText = '热力图已隐藏'
return
}
mapRef.$callMethod('hideHeatMap', JSON.stringify({ id: 'heatmap_001' }))
this.heatMapShown = false
this.resultText = '已隐藏热力图'
},
// ========== 海量点图层功能 ==========
showMassPoints() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
if (this.massPointsShown) {
this.resultText = '海量点已显示'
return
}
// 生成海量点数据点(带自定义数据,点击时返回)
const points : UTSJSONObject[] = []
const centerX = 39.909187
const centerY = 116.397455
// 生成多个海量点(模拟设备分布,每个点有自定义数据)
for (let i = 0; i < 200; i++) {
const offsetX = (Math.random() - 0.5) * 0.15
const offsetY = (Math.random() - 0.5) * 0.15
const point = new UTSJSONObject()
point.set('lat', centerX + offsetX)
point.set('lon', centerY + offsetY)
// 添加自定义数据,点击时会在回调中返回
point.set('deviceId', `device_${i}`)
point.set('name', `设备${i + 1}`)
point.set('status', i % 3 === 0 ? 'online' : 'offline')
point.set('battery', Math.floor(Math.random() * 100))
points.push(point)
}
// 获取本地图片路径(和 marker004 一样的方式)
getFileLocalPath(
'../../static/ball.png',
(localIconPath : string) => {
// 构建海量点图层参数
const massPointsParams = new UTSJSONObject()
massPointsParams.set('id', 'masspoints_001')
massPointsParams.set('points', points)
massPointsParams.set('zIndex', 1)
massPointsParams.set('visible', true)
// 设置图标(支持本地路径和 http 链接)
massPointsParams.set('icon', localIconPath)
massPointsParams.set('iconWidth', 20)
massPointsParams.set('iconHeight', 20)
massPointsParams.set('anchor', 'center')
mapRef.$callMethod('addMassPoints', JSON.stringify(massPointsParams))
this.massPointsShown = true
this.resultText = '已显示海量点图层(200个随机点,带自定义数据,点击测试回调)'
},
(err : any) => {
this.resultText = '获取本地图片路径失败: ' + JSON.stringify(err)
}
)
},
hideMassPoints() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
if (!this.massPointsShown) {
this.resultText = '海量点已隐藏'
return
}
mapRef.$callMethod('hideMassPoints', JSON.stringify({ id: 'masspoints_001' }))
this.massPointsShown = false
this.resultText = '已隐藏海量点图层'
},
// ========== 归位功能 ==========
locateToCurrent() {
this.resultText = '正在获取当前位置...'
getAmapLocation({
needAddress: true,
desiredAccuracy: 100,
}, (res : UTSJSONObject) => {
const lat = res['lat'] as number | null
const lon = res['lon'] as number | null
if (lat != null && lon != null) {
// 调用地图组件的 updateCenter 方法设置中心点
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('updateCenter', `${lon},${lat}`)
}
const address = res['address'] as string | null
const city = res['city'] as string | null
let text = `已归位到当前位置: ${lon.toFixed(6)}, ${lat.toFixed(6)}`
if (city != null) text += `\n城市: ${city}`
if (address != null) text += `\n地址: ${address}`
this.resultText = text
} else {
this.resultText = '定位失败,请检查定位权限'
}
})
}
}
}
</script>
<style>
.section {
padding: 10rpx 20rpx;
background-color: #f8f8f8;
margin-top: 10rpx;
}
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.btn {
margin-left: 10rpx;
margin-top: 10rpx;
}
.btns {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding-top: 10rpx;
padding-bottom: 10rpx;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.native-map {
height: 750rpx;
width: 750rpx;
}
.controls-area {
flex: 1;
}
.result-area {
flex: 1;
background-color: #fff;
padding: 20rpx;
margin: 10rpx;
border-radius: 10rpx;
border: 1px solid #eee;
}
.result-text {
font-size: 26rpx;
color: #333;
line-height: 1.6;
}
</style>
导航组件方法说明
| 方法名 | 参数 | 说明 |
|---|---|---|
| calculateDriveRoute | JSON字符串 | 规划驾车路线 |
| calculateWalkRoute | JSON字符串 | 规划步行路线 |
| calculateRideRoute | JSON字符串 | 规划骑行路线 |
| calculateTruckRoute | JSON字符串 | 规划货车路线 |
| startGPSNavi | 无 | 开始GPS实时导航 |
| startEmulatorNavi | 无 | 开始模拟导航 |
| stopNavi | 无 | 停止导航 |
| destroy | 无 | 销毁导航组件 |
路线规划参数说明
{
"startLat": 39.909187, // 起点纬度
"startLng": 116.397455, // 起点经度
"endLat": 39.917839, // 终点纬度
"endLng": 116.397029, // 终点经度
"naviType": "drive", // 导航类型: drive/walk/ride/truck
"naviMode": "gps", // 导航模式: gps/emulator
"strategy": 0, // 路线策略(可选)
// 货车专用参数(仅 naviType 为 truck 时有效)
"truckSize": 2, // 车型: 1-微型, 2-轻型, 3-中型, 4-重型
"truckHeight": 2.5, // 车高(米)
"truckWidth": 2.0, // 车宽(米)
"truckLoad": 5.0, // 载重(吨)
"truckWeight": 10.0, // 车重(吨)
"truckAxis": 2 // 轴数
}
导航回调事件说明
| 事件名 | 说明 | 返回数据 |
|---|---|---|
| initNavi | 导航初始化 | { success: boolean } |
| calculateRoute | 路线规划结果 | { success: boolean, status: string } |
| startNavi | 导航开始 | { type: 'GPS'/'EMULATOR' } |
| stopNavi | 导航停止 | { status: string } |
| arriveDestination | 到达目的地 | { status: 'arrived' } |
| naviInfoUpdate | 导航信息更新 | { curRoadName, nextRoadName, remainDistance, remainTime } |
| locationChange | 位置变化 | { lat, lng, speed, bearing } |
| recalculateRoute | 重新规划路线 | { reason: 'yaw'/'congestion' } |
| goBack | 用户点击退出按钮 | { status: string } |
| error | 错误信息 | { message: string } |
| warning | 警告信息 | { message: string } |
平台差异说明
| 功能 | Android | iOS | HarmonyOS |
|---|---|---|---|
| 驾车导航 | ✅ | ✅ | ✅ |
| 步行导航 | ✅ | ✅ | ⚠️ 自动切换为驾车 |
| 骑行导航 | ✅ | ✅ | ⚠️ 自动切换为驾车 |
| 货车导航 | ✅ | ✅ | ✅ |
| GPS实时导航 | ✅ | ✅ | ✅ |
| 模拟导航 | ✅ | ✅ | ✅ |
| 语音播报 | ✅ | ✅ | ✅ |
注意:鸿蒙端 AMap SDK 1.0 版本暂不支持步行和骑行导航,选择这两种类型时会自动切换为驾车导航并给出提示。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 1938
赞赏 0
下载 11415810
赞赏 1873
赞赏
京公网安备:11010802035340号