/**
|
* 定位hooks,提供定位相关功能
|
* - 获取位置
|
* - 位置监听
|
* - 地址解析
|
* - 距离计算
|
*/
|
export default function useLocation () {
|
// 当前位置信息
|
const location = ref(null)
|
|
// 定位状态
|
const isLocating = ref(false)
|
|
// 是否正在监听位置
|
const isWatching = ref(false)
|
|
// 定位错误信息
|
const error = ref(null)
|
|
// 历史位置
|
const historyLocations = ref([])
|
|
// 监听位置的定时器ID
|
let watchId = null
|
|
/**
|
* 获取当前位置
|
* @param options 定位选项
|
*/
|
const getLocation = (options = {}) => {
|
isLocating.value = true
|
error.value = null
|
|
const defaultOptions = {
|
type: "gcj02",
|
altitude: false,
|
isHighAccuracy: false
|
}
|
|
const finalOptions = { ...defaultOptions, ...options }
|
|
return new Promise((resolve, reject) => {
|
uni.getLocation({
|
type: finalOptions.type,
|
altitude: finalOptions.altitude,
|
isHighAccuracy: finalOptions.isHighAccuracy,
|
highAccuracyExpireTime: finalOptions.highAccuracyExpireTime,
|
success: res => {
|
// 更新当前位置
|
const locationData = {
|
...res,
|
timestamp: Date.now()
|
}
|
|
location.value = locationData
|
|
// 添加到历史记录
|
historyLocations.value.push(locationData)
|
|
// 只保留最近的20条记录
|
if (historyLocations.value.length > 20) {
|
historyLocations.value.shift()
|
}
|
|
finalOptions.success && finalOptions.success(res)
|
resolve(locationData)
|
},
|
fail: err => {
|
error.value = err
|
finalOptions.fail && finalOptions.fail(err)
|
reject(err)
|
},
|
complete: () => {
|
isLocating.value = false
|
finalOptions.complete && finalOptions.complete()
|
}
|
})
|
})
|
}
|
|
/**
|
* 使用地理编码获取地址信息
|
* @param latitude 纬度
|
* @param longitude 经度
|
*/
|
const getAddress = (latitude, longitude) => {
|
return new Promise((resolve, reject) => {
|
// #ifdef APP-PLUS
|
uni.request({
|
url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=YOUR_KEY`,
|
success: res => {
|
if (res.data && res.data.status === 0) {
|
const addressComponent = res.data.result.address_component
|
const formattedAddress =
|
res.data.result.formatted_addresses.recommend
|
|
const addressInfo = {
|
nation: addressComponent.nation,
|
province: addressComponent.province,
|
city: addressComponent.city,
|
district: addressComponent.district,
|
street: addressComponent.street,
|
streetNum: addressComponent.street_number,
|
poiName:
|
res.data.result.poi_count > 0
|
? res.data.result.pois[0].title
|
: "",
|
cityCode: res.data.result.ad_info.city_code
|
}
|
|
if (location.value) {
|
location.value.address = addressInfo
|
location.value.formatted = formattedAddress
|
}
|
|
resolve(addressInfo)
|
} else {
|
reject(new Error("获取地址信息失败"))
|
}
|
},
|
fail: err => {
|
reject(err)
|
}
|
})
|
// #endif
|
|
// #ifndef APP-PLUS
|
// 其他平台可以使用uni.getLocation的geocode参数获取(仅App和微信小程序支持)
|
// 或者使用其他地图服务的API
|
reject(new Error("当前平台不支持地址解析"))
|
// #endif
|
})
|
}
|
|
/**
|
* 停止监听位置
|
*/
|
const stopWatchLocation = () => {
|
if (watchId !== null) {
|
clearInterval(watchId)
|
watchId = null
|
}
|
|
isWatching.value = false
|
}
|
|
/**
|
* 开始监听位置变化
|
* @param options 定位选项
|
* @param interval 监听间隔,单位毫秒
|
*/
|
const watchLocation = (options = {}, interval = 5000) => {
|
// 已经在监听,先停止
|
if (isWatching.value) {
|
stopWatchLocation()
|
}
|
|
isWatching.value = true
|
|
// 首次定位
|
getLocation(options).catch(err => {
|
console.error("监听位置首次定位失败", err)
|
})
|
|
// 定时获取位置
|
watchId = window.setInterval(() => {
|
if (isWatching.value) {
|
getLocation(options).catch(err => {
|
console.error("监听位置更新失败", err)
|
})
|
}
|
}, interval)
|
|
return watchId
|
}
|
|
/**
|
* 计算两点间距离(米)
|
* @param lat1 第一个点的纬度
|
* @param lon1 第一个点的经度
|
* @param lat2 第二个点的纬度
|
* @param lon2 第二个点的经度
|
* @returns 距离,单位:米
|
*/
|
const calculateDistance = (lat1, lon1, lat2, lon2) => {
|
const R = 6371000 // 地球半径,单位米
|
const dLat = ((lat2 - lat1) * Math.PI) / 180
|
const dLon = ((lon2 - lon1) * Math.PI) / 180
|
|
const a =
|
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
Math.cos((lat1 * Math.PI) / 180) *
|
Math.cos((lat2 * Math.PI) / 180) *
|
Math.sin(dLon / 2) *
|
Math.sin(dLon / 2)
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
const distance = R * c
|
|
return distance
|
}
|
|
/**
|
* 获取当前位置到目标位置的距离
|
* @param targetLat 目标位置纬度
|
* @param targetLon 目标位置经度
|
* @returns 距离,单位:米,如果当前没有位置信息则返回-1
|
*/
|
const getDistanceFromCurrent = (targetLat, targetLon) => {
|
if (!location.value) {
|
return -1
|
}
|
|
return calculateDistance(
|
location.value.latitude,
|
location.value.longitude,
|
targetLat,
|
targetLon
|
)
|
}
|
|
/**
|
* 格式化距离显示
|
* @param distance 距离,单位:米
|
* @returns 格式化后的距离字符串
|
*/
|
const formatDistance = distance => {
|
if (distance < 0) {
|
return "未知距离"
|
} else if (distance < 1000) {
|
return `${Math.round(distance)}米`
|
} else {
|
return `${(distance / 1000).toFixed(1)}公里`
|
}
|
}
|
|
/**
|
* 打开导航
|
* @param latitude 目标纬度
|
* @param longitude 目标经度
|
* @param name 目标名称
|
* @param address 目标地址
|
*/
|
const openLocation = (latitude, longitude, name = "", address = "") => {
|
return new Promise((resolve, reject) => {
|
uni.openLocation({
|
latitude,
|
longitude,
|
name,
|
address,
|
success: () => resolve(),
|
fail: err => reject(err)
|
})
|
})
|
}
|
|
/**
|
* 选择位置
|
*/
|
const chooseLocation = () => {
|
return new Promise((resolve, reject) => {
|
uni.chooseLocation({
|
success: res => {
|
// 更新当前位置
|
if (res.latitude && res.longitude) {
|
const locationData = {
|
latitude: res.latitude,
|
longitude: res.longitude,
|
accuracy: 0,
|
verticalAccuracy: 0,
|
horizontalAccuracy: 0,
|
altitude: 0,
|
speed: 0,
|
timestamp: Date.now(),
|
address: {
|
province: "",
|
city: "",
|
district: "",
|
street: "",
|
poiName: res.name
|
},
|
formatted: res.address
|
}
|
|
location.value = locationData
|
}
|
|
resolve(res)
|
},
|
fail: err => reject(err)
|
})
|
})
|
}
|
|
// 自动清理
|
onUnmounted(() => {
|
stopWatchLocation()
|
})
|
|
return {
|
// 状态
|
location,
|
isLocating,
|
isWatching,
|
error,
|
historyLocations,
|
|
// 方法
|
getLocation,
|
getAddress,
|
watchLocation,
|
stopWatchLocation,
|
calculateDistance,
|
getDistanceFromCurrent,
|
formatDistance,
|
openLocation,
|
chooseLocation
|
}
|
}
|