1、单独抽离封装
// 腾讯地图SDK引入(需提前下载qqmap-wx-jssdk.min.js文件)
// 注意:使用前需在微信公众平台配置request合法域名https://apis.map.qq.com
var QQMapWX = require('@/libs/qqmap-wx-jssdk.min.js')
// 高德地图SDK引入(需提前下载amap-wx.130.js文件)
// 注意:使用前需配置高德域名白名单https://restapi.amap.com
import amap from '@/libs/amap-wx.130.js';
/**
* 地图服务配置常量
* 建议通过构建工具注入环境变量,避免硬编码
* AMAP_KEY:高德开发者密钥(需到lbs.amap.com申请)
* QQMAP_KEY:腾讯地图开发者密钥(需到lbs.qq.com申请)
*/
const MAP_CONFIG = {
AMAP_KEY: '', // 您的高德地图Key
QQMAP_KEY: '' // 您的腾讯地图Key
}
/**
* 定位服务核心类
* 功能:封装高德/腾讯地图的定位和逆地理编码能力
* 特点:
* 1. 双地图服务支持(自动根据配置初始化)
* 2. 完整的权限控制流程
* 3. 统一的数据格式返回
*/
export default class LocationService {
constructor() {
this.amapPlugin = null // 高德地图实例
this.qqmapsdk = null // 腾讯地图实例
this._initSDK() // 自动初始化SDK
}
/**
* 初始化地图SDK(私有方法)
* 注意:会根据MAP_CONFIG配置自动初始化可用SDK
* 高德地图需额外配置Geocoder插件用于逆地理编码
*/
_initSDK() {
// 高德地图初始化(需同时满足SDK存在且配置了有效KEY)
if (typeof amap !== 'undefined' && MAP_CONFIG.AMAP_KEY) {
this.amapPlugin = new amap.AMapWX({
key: MAP_CONFIG.AMAP_KEY,
// plugin: ['AMap.Geocoder'] // 必须添加地理编码插件
})
}
// 腾讯地图初始化(需同时满足SDK存在且配置了有效KEY)
if (typeof QQMapWX !== 'undefined' && MAP_CONFIG.QQMAP_KEY) {
this.qqmapsdk = new QQMapWX({
key: MAP_CONFIG.QQMAP_KEY
})
}
}
/**
* 统一权限检查流程(私有方法)
* 三阶段处理:
* 1. 检查已有权限
* 2. 无权限时请求授权(每次拒绝后下次仍会触发系统弹窗)
* 3. 授权被拒时引导用户去设置页
*/
async _checkPermission() {
return new Promise((resolve, reject) => {
uni.getSetting({
success: (res) => {
// 已有权限直接通过
if (res.authSetting['scope.userLocation'] === true) {
resolve(true)
} else {
// 1. 先请求授权
// uni.authorize:用于首次请求权限,弹窗询问用户是否允许获取位置
uni.authorize({
scope: 'scope.userLocation',
success: () => resolve(true),
fail: (err) => {
// 引导去设置页(不阻止后续再次触发系统弹窗)
this._showPermissionModal().finally(() => {
reject(new Error('PERMISSION_DENIED'))
})
}
})
}
},
fail: reject
})
})
}
/**
* 显示权限引导弹窗(私有方法)
* 增强功能:用户从设置页返回后自动尝试获取定位
*/
_showPermissionModal() {
return new Promise((resolve) => {
uni.showModal({
title: '权限提示',
content: '需要位置权限才能获取定位信息',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (settingRes) => {
// 设置页返回后检查权限状态
if (settingRes.authSetting['scope.userLocation']) {
resolve(true) // 权限已开启
} else {
resolve(false) // 用户未开启权限
}
},
fail: () => resolve(false)
})
} else {
resolve(false)
}
}
})
})
}
/**
* 获取基础坐标(核心方法)
* 增强功能:支持从设置页返回后自动获取坐标
* @param {Object} options 配置项
* - altitude: 是否获取海拔高度(默认false)
* - highAccuracy: 是否启用高精度模式(耗电增加)
* @returns {Promise} 包含经纬度等信息的Promise
*/
async getCoordinates(options = {}) {
try {
// 检查权限(包含引导设置流程)
const hasPermission = await this._checkPermission()
if (!hasPermission) throw new Error('PERMISSION_DENIED')
return new Promise((resolve, reject) => {
// 2. 授权成功后获取高精度坐标
// uni.getLocation:实际获取坐标,需在权限通过后调用
uni.getLocation({
// wgs84:国际标准GPS坐标系(原始经纬度数据)
// gcj02:中国国测局坐标系(高德/腾讯地图专用)
type: 'gcj02',
altitude: !!options.altitude,
isHighAccuracy: !!options.highAccuracy, //高精度模式(isHighAccuracy:true)会增加功耗,建议按需使用
success: (res) => {
resolve({
longitude: res.longitude,
latitude: res.latitude,
altitude: res.altitude || null,
accuracy: res.accuracy,
verticalAccuracy: res.verticalAccuracy || null
})
},
fail: (err) => {
console.log('err-----授权', err)
// 特殊处理用户从设置页返回的情况
if (err.errMsg.includes('auth deny')) {
this.getCoordinates(options).then(resolve).catch(reject)
} else {
reject((err))
}
}
})
})
} catch (err) {
throw (err)
}
}
/**
* 获取高德地图地址信息(核心方法)
* 流程:获取坐标 -> 调用高德逆地理编码接口
* 注意:需提前配置高德域名白名单
*/
async getAMapAddress(options = {}) {
if (!this.amapPlugin) {
throw new Error('AMAP_SDK_NOT_INIT')
}
try {
// 先获取基础坐标
const coords = await this.getCoordinates(options)
return new Promise((resolve, reject) => {
// 调用高德逆地理编码接口
this.amapPlugin.getRegeo({
location: `${coords.longitude},${coords.latitude}`,
success: (data) => {
resolve(this._formatAMapData(data, coords))
},
fail: (err) => reject(err)
})
})
} catch (err) {
throw (err)
}
}
/**
* 获取腾讯地图地址信息(核心方法)
* 流程:获取坐标 -> 调用腾讯逆地理编码接口
* 注意:需提前配置腾讯地图域名白名单
*/
async getQQMapAddress(options = {}) {
if (!this.qqmapsdk) {
throw new Error('QQMAP_SDK_NOT_INIT')
}
try {
// 先获取基础坐标
const coords = await this.getCoordinates(options)
return new Promise((resolve, reject) => {
// 调用腾讯逆地理编码接口
this.qqmapsdk.reverseGeocoder({
location: {
latitude: coords.latitude,
longitude: coords.longitude
},
get_poi: options.includePOI || false, // 是否返回周边POI信息
success: (res) => {
resolve(this._formatQQMapData(res, coords))
},
fail: (err) => reject(err)
})
})
} catch (err) {
throw (err)
}
}
// 新增:通过坐标获取高德地址信息
async getAMapByCoordinates(lng, lat, options = {}) {
if (!this.amapPlugin) throw new Error('AMAP_SDK_NOT_INIT')
return new Promise((resolve, reject) => {
this.amapPlugin.getRegeo({
location: `${lng},${lat}`,
success: (data) => resolve(this._formatAMapData(data, {
longitude: lng,
latitude: lat
})),
fail: reject
})
})
}
// 新增:通过坐标获取腾讯地址信息
async getQQMapByCoordinates(lng, lat, options = {}) {
if (!this.qqmapsdk) throw new Error('QQMAP_SDK_NOT_INIT')
return new Promise((resolve, reject) => {
this.qqmapsdk.reverseGeocoder({
location: {
latitude: lat,
longitude: lng
},
get_poi: options.includePOI || false,
success: (res) => resolve(this._formatQQMapData(res, {
longitude: lng,
latitude: lat
})),
fail: reject
})
})
}
/**
* 格式化高德地图返回数据(私有方法)
* 统一数据格式,方便业务层使用
*/
_formatAMapData(data, coords) {
console.log('高德地图返回数据', data)
const firstResult = data[0] || {}
return firstResult
//也可以根据具体数据自定义返回
// return {
// coordinates: coords, // 原始坐标信息
// formattedAddress: firstResult.address || '', // 完整地址
// country: firstResult.country || '',
// province: firstResult.province || '',
// city: firstResult.city || '',
// district: firstResult.district || '',
// street: firstResult.street || '',
// streetNumber: firstResult.streetNumber || '',
// cityCode: firstResult.citycode || '', // 城市编码
// adCode: firstResult.adcode || '', // 区域编码
// poiList: firstResult.poiList || [] // 周边POI列表
// }
}
/**
* 格式化腾讯地图返回数据(私有方法)
* 统一数据格式,方便业务层使用
*/
_formatQQMapData(data, coords) {
console.log('腾讯地图返回数据-', data)
const address = data.result || {}
return address
//也可以根据具体数据自定义返回
// return {
// coordinates: coords, // 原始坐标信息
// formattedAddress: address.address || '', // 完整地址
// country: address.address_component?.nation || '',
// province: address.address_component?.province || '',
// city: address.address_component?.city || '',
// district: address.address_component?.district || '',
// street: address.address_component?.street || '',
// streetNumber: address.add
// }
}
}
2、使用示例
import LocationService from '@/utils/location.js'
async getAMapAddress() {
try {
// 仅仅获取经纬度
const result = await new LocationService().getCoordinates()
// 根据高德逆地理编码接口,获取当前经纬度对应的具体地址
const result = await new LocationService().getAMapAddress()
// 根据腾讯逆地理编码接口,获取当前经纬度对应的具体地址
const result = await new LocationService().getQQMapAddress()
// 根据高德逆地理编码接口,获取传入的经纬度对应的具体地址
const result = await new LocationService().getAMapByCoordinates('', '')
// 根据腾讯逆地理编码接口,获取传入的经纬度对应的具体地址
const result = await new LocationService().getQQMapByCoordinates('', '')
console.log(result, '经纬度||地址')
} catch (err) {
console.log(err)
}
}
3、前置条件和配置
具体可以查看另一篇文章:
https://blog.csdn.net/weixin_41549971/article/details/149837996?spm=1011.2415.3001.5331
4、效果弹框