更新记录
1.0.202605031(2026-05-03)
- 修正已知问题
1.0.20260504(2026-05-03)
- 优化README
1.0.20260503(2026-05-03)
- 三端插件发布
平台兼容性
uni-app(5.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | √ | × | × | × | × | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(5.0)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | √ | - |
cms-mpnav 三端地图导航插件
cms-mpnav 是一个基于 UTS 的 App 端地图导航唤起插件,支持 Android、iOS、HarmonyOS 三端调用第三方地图或系统地图进行路线导航。
- 支持 Android、iOS、HarmonyOS。
- 支持高德地图、百度地图、腾讯地图。
- iOS 支持苹果地图兜底。
- HarmonyOS 支持花瓣地图兜底。
- Android 在未指定目标地图时优先打开已安装的第三方地图,无可用第三方地图时尝试系统
geo导航。 - 支持驾车、步行、公交、骑行导航类型。
- 支持
gcj02、wgs84、bd09坐标传入,插件内部会按目标地图自动转换坐标。 - 提供地图安装检测和可用地图列表查询能力。
- 插件支持uniapp & uniappx
平台支持
| 平台 | 支持地图 |
|---|---|
| Android | 高德地图、百度地图、腾讯地图 |
| iOS | 高德地图、百度地图、腾讯地图、苹果地图 |
| HarmonyOS | 高德地图、百度地图、腾讯地图、花瓣地图 |
说明:第三方地图是否能被唤起,取决于用户设备是否已安装对应地图 App,以及系统是否允许当前应用拉起目标 App。
引入插件
在插件市场点击“试用”或“购买”后导入项目,然后制作自定义基座包进行真机测试。
import {
getAvailableMaps,
isMapInstalled,
openNavigation
} from '@/uni_modules/cms-mpnav'
也可以一次性引入:
import * as mpnav from '@/uni_modules/cms-mpnav'
API 说明
| 方法名 | 描述 |
|---|---|
| openNavigation(options) | 唤起地图进行导航 |
| getAvailableMaps(): AvailableMapInfo[] | 获取当前系统已安装地图 |
| isMapInstalled(mapId) | 检测地图是否安装 |
openNavigation(options)
打开地图导航。
openNavigation(options: OpenNavigationOptions): Promise<boolean>
返回值:
| 返回值 | 说明 |
|---|---|
Promise resolve(true) |
已成功发起地图唤起请求 |
Promise reject({ code, msg }) |
参数错误、目标地图未安装、当前平台不支持目标地图或唤起失败 |
注意:
resolve(true)表示已经向系统发起打开地图请求,不代表用户一定完成了导航。
OpenNavigationOptions
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
destination |
MpnavPoint |
是 | - | 终点坐标 |
origin |
MpnavPoint \| null |
否 | 不传 | 起点坐标,不传时由目标地图自行使用当前位置 |
coordType |
'gcj02' \| 'wgs84' \| 'bd09' |
否 | gcj02 |
传入坐标的坐标系 |
navType |
'drive' 驾车 \| 'walk' 步行 \| 'bus' 公交 \| 'bike' 骑行 |
否 | drive |
导航方式 |
targetMap |
'amap' 高德 \| 'baidu' 百度 \| 'tencent' 腾讯 \| 'apple' 苹果 \| 'petal' 花瓣 |
否 | 自动选择 | 指定目标地图 |
success |
() => void |
否 | - | 成功发起导航时回调 |
fail |
(code: number, msg: string) => void |
否 | - | 调用失败回调,code 为错误码,msg 为中文错误信息 |
complete |
() => void |
否 | - | 调用结束回调,成功失败都会执行 |
MpnavPoint
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
lat |
number |
是 | 纬度,范围 -90 到 90 |
lng |
number |
是 | 经度,范围 -180 到 180 |
title |
string \| null |
否 | 地点名称,会展示在目标地图中 |
targetMap 可选值
| 值 | 地图 | Android | iOS | HarmonyOS |
|---|---|---|---|---|
amap |
高德地图 | 支持 | 支持 | 支持 |
baidu |
百度地图 | 支持 | 支持 | 支持 |
tencent |
腾讯地图 | 支持 | 支持 | 支持 |
apple |
苹果地图 | 不支持 | 支持 | 不支持 |
petal |
花瓣地图 | 不支持 | 不支持 | 支持 |
不传 targetMap 时:
- Android:优先使用已安装的高德、百度、腾讯地图
- iOS:优先使用已安装的高德、百度、腾讯地图;都不可用时使用苹果地图。
- HarmonyOS:优先使用已安装的高德、百度、腾讯地图;都不可用时使用花瓣地图。
getAvailableMaps()
获取当前设备可用地图列表。
getAvailableMaps(): AvailableMapInfo[]
AvailableMapInfo
| 参数 | 类型 | 说明 |
|---|---|---|
mapId |
string |
地图标识,例如 amap、baidu、tencent、apple、petal |
name |
string |
地图名称 |
scheme |
string |
地图唤起协议 |
packageName |
string \| null |
Android 包名,非 Android 平台可能为 null |
isInstalled |
boolean |
是否可用 |
isMapInstalled(mapId)
判断指定地图在当前平台是否可用。
isMapInstalled(mapId: string): boolean
示例:
console.log(isMapInstalled('amap'))
console.log(isMapInstalled('tencent'))
uniapp完整示例
<template>
<view class="page">
<view class="card">
<view class="title">cms-mpnav 地图导航示例</view>
<view class="desc">选择地图、起点和终点后发起导航。起点默认当前定位,终点默认正弘城。</view>
<view class="section">
<view class="section-title">地图选择</view>
<view v-if="availableMaps.length > 0" class="map-list">
<view
v-for="item in availableMaps"
:key="item.mapId"
class="map-item"
:class="[item.mapId, { active: selectedMapId === item.mapId }]"
@click="handleSelectMap(item.mapId)"
>
<view class="map-icon">{{ item.shortName }}</view>
<view class="map-info">
<view class="map-name">{{ item.name }}</view>
<view class="map-status">{{ item.isInstalled ? "已安装" : "未安装" }}</view>
</view>
</view>
</view>
<view v-else class="empty-text">当前平台暂无可展示地图。</view>
<button class="btn" @click="refreshMaps">刷新地图列表</button>
</view>
<view class="section">
<view class="section-title">起点</view>
<view class="location-card">
<block v-if="origin.title">
<view class="location-title">{{ origin.title || "当前位置" }}</view>
<view class="location-coord">{{ formatPoint(origin) }}</view>
</block>
<block v-else>
当前位置
</block>
</view>
<view class="btn-row">
<button class="btn half" @click="useCurrentLocation">使用当前位置</button>
<button class="btn half" @click="chooseOrigin">选择起点</button>
</view>
</view>
<view class="section">
<view class="section-title">终点</view>
<view class="location-card">
<view class="location-title">{{ destination.title }}</view>
<view class="location-coord">{{ formatPoint(destination) }}</view>
</view>
<button class="btn" @click="chooseDestination">选择终点</button>
</view>
<view class="section">
<view class="section-title">导航方式</view>
<view class="nav-list">
<view
v-for="item in navTypeOptions"
:key="item.value"
class="nav-item"
:class="{ active: navType === item.value }"
@click="navType = item.value"
>
{{ item.label }}
</view>
</view>
</view>
<button class="btn primary" @click="openSelectedNavigation">开始导航</button>
<view class="result">{{ result }}</view>
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from "vue";
import {
isMapInstalled,
openNavigation
} from "@/uni_modules/cms-mpnav";
const defaultDestination = {
lat: 34.795836,
lng: 113.680473,
title: "正弘城"
};
const defaultOrigin = {
lat: 34.789401,
lng: 113.685214,
title: "郑州市动物园"
};
const availableMaps = ref([]);
const selectedMapId = ref("");
const navType = ref("drive");
const result = ref("等待操作");
const origin = ref({
...defaultOrigin
});
const destination = ref({
...defaultDestination
});
const navTypeOptions = [
{ label: "驾车", value: "drive" },
{ label: "步行", value: "walk" },
{ label: "公交", value: "bus" },
{ label: "骑行", value: "bike" }
];
const supportedMapMeta = {
amap: { mapId: "amap", name: "高德地图", shortName: "高" },
baidu: { mapId: "baidu", name: "百度地图", shortName: "百" },
tencent: { mapId: "tencent", name: "腾讯地图", shortName: "腾" },
apple: { mapId: "apple", name: "苹果地图", shortName: "苹" },
petal: { mapId: "petal", name: "花瓣地图", shortName: "花" }
};
function setResult(text) {
result.value = text;
}
function formatPoint(point) {
return `${Number(point.lat).toFixed(6)}, ${Number(point.lng).toFixed(6)}`;
}
function getPlatformSupportedMapIds() {
const systemInfo = uni.getSystemInfoSync();
const platform = String(systemInfo.platform || "").toLowerCase();
if (platform == "android") {
return ["amap", "baidu", "tencent"];
}
if (platform == "ios") {
return ["amap", "baidu", "tencent", "apple"];
}
if (platform.indexOf("harmony") >= 0 || platform.indexOf("ohos") >= 0) {
return ["amap", "baidu", "tencent", "petal"];
}
return [];
}
function refreshMaps() {
const mapIds = getPlatformSupportedMapIds();
const maps = mapIds.map((mapId) => {
const meta = supportedMapMeta[mapId];
let installed = false;
try {
installed = isMapInstalled(mapId);
} catch (err) {
installed = false;
}
return {
mapId: meta.mapId,
name: meta.name,
shortName: meta.shortName,
isInstalled: installed
};
});
availableMaps.value = maps;
if (maps.length > 0) {
const currentSelected = maps.find((item) => item.mapId === selectedMapId.value);
if (!currentSelected) {
selectedMapId.value = maps[0].mapId;
}
setResult(`当前平台支持地图:${maps.map(item => item.name).join("、")}`);
return;
}
selectedMapId.value = "";
setResult("当前平台暂无可用地图范围");
}
function handleSelectMap(mapId) {
selectedMapId.value = mapId;
const target = availableMaps.value.find((item) => item.mapId === mapId);
if (!isMapInstalled(mapId)) {
setResult(`未安装${target ? target.name : mapId},调用导航时会进入 fail 回调`);
return;
}
setResult(`当前选择地图:${target ? target.name : mapId}`);
}
function useCurrentLocation() {
origin.value.title = "";
origin.value.lat = "";
origin.value.lng = "";
}
function choosePoint(onSuccess) {
uni.chooseLocation({
success(res) {
onSuccess({
lat: Number(res.latitude),
lng: Number(res.longitude),
title: res.name || res.address || "已选择位置"
});
},
fail(err) {
console.error("chooseLocation failed:", err);
setResult("选择位置失败");
}
});
}
function chooseOrigin() {
choosePoint((point) => {
origin.value = point;
setResult(`已选择起点:${point.title}`);
});
}
function chooseDestination() {
choosePoint((point) => {
destination.value = point;
setResult(`已选择终点:${point.title}`);
});
}
async function openSelectedNavigation() {
let params = {
coordType: "gcj02",
destination: destination.value,
navType: navType.value,
targetMap: selectedMapId.value || null,
success() {
setResult("success 回调:已发起导航");
},
fail(code, msg) {
setResult(`fail 回调:${code},${msg}`);
},
complete() {
console.log("openNavigation complete");
}
};
if (origin.value.title) {
params.origin = origin.value;
}
try {
await openNavigation(params);
console.log("openNavigation Promise resolved");
} catch (err) {
console.error("openNavigation failed:", err);
}
}
onMounted(() => {
refreshMaps();
});
</script>
<style>
.page {
min-height: 100vh;
padding: 20rpx;
box-sizing: border-box;
background: linear-gradient(180deg, #eef4ff 0%, #f8fafc 100%);
}
.card {
padding: 22rpx;
border-radius: 18rpx;
background: #ffffff;
box-shadow: 0 12rpx 48rpx rgba(15, 23, 42, 0.08);
}
.title {
font-size: 32rpx;
font-weight: 700;
color: #0f172a;
}
.desc {
margin-top: 8rpx;
color: #475569;
font-size: 22rpx;
line-height: 1.5;
}
.section {
margin-top: 20rpx;
}
.section-title {
margin-bottom: 10rpx;
font-size: 26rpx;
font-weight: 600;
color: #1e293b;
}
.map-list {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.map-item {
display: flex;
align-items: center;
padding: 14rpx;
border: 2rpx solid #dbe4f0;
border-radius: 14rpx;
background: #f8fafc;
}
.map-item.active {
border-color: #1677ff;
background: #eef6ff;
}
.map-icon {
width: 56rpx;
height: 56rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ffffff;
font-size: 24rpx;
font-weight: 700;
}
.map-item.amap .map-icon {
background: #1677ff;
}
.map-item.baidu .map-icon {
background: #d93026;
}
.map-item.tencent .map-icon {
background: #19be6b;
}
.map-item.apple .map-icon {
background: #111111;
}
.map-item.petal .map-icon {
background: #ff6a00;
}
.map-info {
margin-left: 14rpx;
}
.map-name {
font-size: 24rpx;
color: #0f172a;
font-weight: 600;
}
.map-status {
margin-top: 4rpx;
font-size: 20rpx;
color: #64748b;
}
.empty-text {
padding: 14rpx;
border-radius: 14rpx;
background: #f8fafc;
color: #64748b;
font-size: 20rpx;
}
.location-card {
padding: 14rpx;
border-radius: 14rpx;
background: #f8fafc;
border: 2rpx solid #e2e8f0;
}
.location-title {
font-size: 24rpx;
color: #0f172a;
font-weight: 600;
}
.location-coord {
margin-top: 4rpx;
font-size: 20rpx;
color: #64748b;
}
.nav-list {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
}
.nav-item {
padding: 10rpx 18rpx;
border-radius: 999rpx;
border: 2rpx solid #dbe4f0;
background: #f8fafc;
color: #334155;
font-size: 22rpx;
flex: 1;
text-align: center;
}
.nav-item.active {
border-color: #1677ff;
background: #1677ff;
color: #ffffff;
}
.btn {
margin-top: 10rpx;
transform: scale(0.96);
transform-origin: center top;
}
.btn-row {
display: flex;
gap: 10rpx;
}
.half {
flex: 1;
}
.primary {
background: #1677ff;
color: #ffffff;
}
.result {
margin-top: 20rpx;
padding: 16rpx;
border-radius: 12rpx;
background: #0f172a;
color: #e2e8f0;
font-size: 20rpx;
line-height: 1.5;
word-break: break-all;
white-space: pre-wrap;
}
</style>
坐标系说明
请根据你业务中保存的坐标类型正确传入 coordType。
| 坐标系 | 说明 | 常见来源 |
|---|---|---|
gcj02 |
国测局坐标 | 高德地图、腾讯地图、uni.getLocation 设置 type: 'gcj02' |
wgs84 |
GPS 原始坐标 | GPS 设备、部分海外地图数据、uni.getLocation 设置 type: 'wgs84' |
bd09 |
百度坐标 | 百度地图、百度定位 |
如果坐标系传错,地图中显示的位置会出现偏移。
错误码
openNavigation 参数校验失败或唤起失败时会进入 fail(code, msg) 回调,并返回 Promise reject({ code, msg })。
| 错误码 | 说明 |
|---|---|
9001001 |
终点坐标无效,请检查 destination.lat、destination.lng |
9001002 |
起点坐标无效,请检查 origin.lat、origin.lng |
9001003 |
targetMap 参数无效 |
9001004 |
当前设备没有可用地图,且系统地图打开失败 |
9001005 |
当前平台不支持该地图 |
9001006 |
未安装目标地图,请下载安装后使用 |
9001007 |
打开目标地图失败,请稍后重试 |
常见问题
为什么真机没有反应?
请确认已经制作并运行自定义基座。UTS 原生能力需要在 App 真机环境中测试,普通浏览器、H5、小程序环境不支持。
为什么会进入 fail 回调或 Promise reject?
常见原因:
- 用户设备未安装指定地图 App。
- 当前平台不支持该地图,例如 Android 指定
apple、iOS 指定petal。 - 传入的经纬度无效。
- 系统限制当前应用打开外部 App。
建议先调用 isMapInstalled(mapId) 或 getAvailableMaps() 判断可用地图,再展示对应入口。
坐标为什么偏移?
大概率是 coordType 传错。插件会根据 coordType 做坐标转换,请确保传入坐标与声明的坐标系一致。
是否需要申请地图开放平台 Key?
本插件是唤起用户设备上已安装的地图 App 导航,不是在 App 内渲染地图,因此基础导航唤起通常不需要集成地图 SDK Key。部分地图平台可能对来源字段、referer、应用标识有自己的规则,如你的应用有更严格的上架或合规要求,请以对应地图开放平台说明为准。
需要帮助?有其他插件需求?联系我

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 3018
赞赏 7
下载 11767756
赞赏 1911
赞赏
京公网安备:11010802035340号