feat:首页地图新增设备标记点

This commit is contained in:
2025-10-24 17:08:33 +08:00
parent 179be8f8b0
commit fba9261840
3 changed files with 218 additions and 133 deletions
+111 -87
View File
@@ -4,12 +4,18 @@
<view class="map-wrapper">
<!-- 使用小程序原生地图组件 -->
<map id="map" class="native-map" :longitude="mapCenter.longitude" :latitude="mapCenter.latitude"
:markers="mapMarkers" :scale="mapZoom" :show-location="true" @regionchange="onMapRegionChange"
:markers="mapMarkers" :scale="mapZoom" :show-location="false" @regionchange="onMapRegionChange"
@markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated" @error="onMapError">
<!-- 覆盖在地图上的可点击控件使用 cover-view 以兼容小程序原生组件层级 -->
<cover-view class="index-swiper" v-if="!props.hideControls" @tap="handleJoinTap">
<cover-image src="/static/index_swiper.png" class="index-swiper-img" mode="aspectFit"></cover-image>
</cover-view>
<!-- 地图中心固定定位图标 -->
<cover-view class="center-location-marker">
<cover-image src="/static/location-icon.png" class="center-marker-icon"></cover-image>
</cover-view>
<cover-view class="map-side-controls" v-if="!props.hideControls">
<cover-view class="side-btn service" @tap="handleService">
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image>
@@ -24,7 +30,6 @@
<!-- <cover-view class="side-text">定位</cover-view> -->
</cover-view>
</cover-view>
<!-- 使用原生 marker 方案渲染中心指示 regionchange 同步到地图中心 -->
</map>
<!-- 地图加载状态 -->
@@ -144,28 +149,14 @@
!(latitude === 0 && longitude === 0) // 排除 0,0 这种无效坐标
}
// 防抖定时器
let regionChangeTimer = null
// 方法
const updateMapMarkers = () => {
const markers = []
// 中心 marker(始终存在,使用传入中心坐标或 userLocation
const centerLng = Number(mapCenter.value.longitude || (props.userLocation && props.userLocation.longitude))
const centerLat = Number(mapCenter.value.latitude || (props.userLocation && props.userLocation.latitude))
if (!isNaN(centerLng) && !isNaN(centerLat) && isValidCoordinate(centerLat, centerLng)) {
markers.push({
id: 999999, // 固定 id 作为中心点
latitude: centerLat,
longitude: centerLng,
iconPath: '/static/location-icon.png',
width: 30,
height: 40,
anchor: {
x: 0.5,
y: 1
} // 图钉尖端对准坐标
})
}
// 可选:周边位置点
// 只添加周边场地位置点,中心定位图标改用固定的cover-view显示
if (props.enableMarkers && props.filteredPositions && props.filteredPositions.length > 0) {
props.filteredPositions.forEach((pos, index) => {
if (pos.longitude && pos.latitude && isValidCoordinate(pos.latitude, pos.longitude)) {
@@ -175,9 +166,9 @@
id: index + 1,
latitude: lat,
longitude: lng,
iconPath: '/static/map.png',
width: 30,
height: 30,
iconPath: '/static/markes_fdz.png',
width: 40,
height: 40,
callout: {
content: pos.name,
fontSize: 12,
@@ -195,45 +186,43 @@
isLoading.value = false
}
// 移动地图到指定位置(使用 includePoints 以避免 mapid 错误,并兼容不同基础库
// 移动地图到指定位置(直接更新地图中心坐标
const moveToLocation = (location) => {
if (!location || !location.longitude || !location.latitude) return
if (!mapContext.value) return
if (!location || !location.longitude || !location.latitude) {
console.warn('moveToLocation: 无效的位置参数', location)
return
}
try {
mapContext.value.includePoints({
points: [{
const newCenter = {
longitude: Number(location.longitude),
latitude: Number(location.latitude)
}],
padding: [60, 60, 60, 60],
success: () => {
console.log('地图已移动到指定位置(includePoints)')
},
fail: (err) => {
console.warn('includePoints 失败,尝试 moveToLocation():', err)
// 回退尝试(不传参,部分基础库仅支持移动到用户当前位置)
try {
mapContext.value.moveToLocation()
} catch (e) {
console.error('moveToLocation 回退失败:', e)
}
}
})
} catch (e) {
console.error('移动地图异常:', e)
}
console.log('移动地图到:', newCenter)
// 直接更新地图中心,触发地图组件的视图更新
mapCenter.value = newCenter
// 更新标记点
updateMapMarkers()
}
// 监听用户位置变化
watch(() => props.userLocation, (newLocation) => {
watch(() => props.userLocation, (newLocation, oldLocation) => {
if (newLocation && newLocation.longitude && newLocation.latitude) {
// 检查位置是否真的变化了(避免重复更新)
const isChanged = !oldLocation ||
oldLocation.longitude !== newLocation.longitude ||
oldLocation.latitude !== newLocation.latitude
if (isChanged) {
console.log('用户位置变化:', newLocation)
mapCenter.value = {
longitude: newLocation.longitude,
latitude: newLocation.latitude
}
updateMapMarkers()
moveToLocation(newLocation)
}
}
}, {
immediate: true,
@@ -252,25 +241,75 @@
isLoading.value = false
}
// 地图区域变化事件
// 地图区域变化事件(带防抖优化)
const onMapRegionChange = (e) => {
// 在手势或缩放结束时更新中心坐标
if (e && e.type === 'end') {
console.log('regionchange事件:', e)
// 只处理结束事件
if (!e || e.type !== 'end') {
return
}
// 获取事件原因
// 微信小程序:e.causedBy 可能的值:
// - 'gesture': 手势拖动
// - 'scale': 缩放
// - 'update': 调用更新接口(如 setCenterOffset 等)
const causedBy = e.causedBy || e.detail?.causedBy
console.log('地图变化原因:', causedBy, '完整事件:', e)
// 只在用户手动操作(拖动或缩放)结束时触发查询
// 排除程序调用(update)导致的变化,避免定位按钮触发多余查询
if (causedBy === 'gesture' || causedBy === 'scale' || causedBy === 'drag') {
// 清除之前的定时器
if (regionChangeTimer) {
clearTimeout(regionChangeTimer)
}
// 直接从事件对象中获取最新的中心点位置
const centerLocation = e.detail?.centerLocation || e.centerLocation
if (centerLocation && centerLocation.longitude && centerLocation.latitude) {
// 防抖:500ms后执行查询
regionChangeTimer = setTimeout(() => {
const newCenter = {
longitude: Number(centerLocation.longitude),
latitude: Number(centerLocation.latitude)
}
mapCenter.value = newCenter
console.log('地图中心变化,触发查询:', newCenter, '原因:', causedBy)
// 触发父组件查询新位置的场地
emit('mapCenterChange', newCenter)
}, 500)
} else {
console.warn('未能从事件中获取中心点位置,尝试使用API获取')
// 兜底方案:如果事件中没有centerLocation,才使用API获取
regionChangeTimer = setTimeout(() => {
if (mapContext.value) {
mapContext.value.getCenterLocation({
success: (res) => {
if (res && res.longitude && res.latitude) {
mapCenter.value = {
const newCenter = {
longitude: res.longitude,
latitude: res.latitude
}
// 更新中心 marker 位置
updateMapMarkers()
emit('mapCenterChange', mapCenter.value)
mapCenter.value = newCenter
console.log('地图中心变化(API获取):', newCenter, '原因:', causedBy)
emit('mapCenterChange', newCenter)
}
},
fail: (err) => {
console.error('获取地图中心失败:', err)
}
})
}
}, 500)
}
} else {
console.log('跳过查询,原因:', causedBy)
}
}
@@ -278,16 +317,7 @@
const onMapMarkerTap = (e) => {
const markerId = e.detail?.markerId || e.markerId
// 中心点标记
if (markerId === 999999) {
uni.showToast({
title: '这是您的当前位置',
icon: 'none'
})
return
}
// 查找对应的位置信息
// 查找对应的场地位置信息
if (props.filteredPositions && props.filteredPositions.length > 0) {
const position = props.filteredPositions[markerId - 1]
if (position) {
@@ -369,6 +399,10 @@ const handleSearch = () => {
onUnmounted(() => {
// 清理工作
if (regionChangeTimer) {
clearTimeout(regionChangeTimer)
regionChangeTimer = null
}
mapContext.value = null
})
@@ -464,33 +498,23 @@ const handleSearch = () => {
}
}
/* 地图中心图钉(cover-view,避免使用 transform 以兼容小程序 */
.center-pin {
/* 地图中心定位图标(固定在屏幕中心 */
.center-location-marker {
position: absolute;
left: 50%;
top: 50%;
z-index: 16;
width: 0;
height: 0;
z-index: 100;
width: 60rpx;
height: 80rpx;
margin-left: -30rpx;
margin-top: -80rpx;
pointer-events: none;
}
.center-pin .pin-img {
position: absolute;
width: 48rpx;
height: 64rpx;
/* 使图钉尖端对准中心点:X 向左偏半宽,Y 向上偏高度-阴影间距 */
margin-left: -24rpx;
margin-top: -68rpx;
}
.center-pin .pin-shadow {
position: absolute;
width: 28rpx;
height: 8rpx;
border-radius: 10rpx;
background: rgba(0, 0, 0, 0.18);
margin-left: -14rpx;
margin-top: -8rpx;
.center-marker-icon {
width: 60rpx;
height: 80rpx;
display: block;
}
.map-side-controls {
+83 -22
View File
@@ -9,6 +9,7 @@
<!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
:enableMarkers="true"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" />
@@ -175,6 +176,7 @@
const showPhoneAuthPopup = ref(false)
const isLocationInitialized = ref(false)
const showLocationPopup = ref(false)
const isRelocating = ref(false) // 防抖标志:是否正在重新定位
// 使用指南步骤
const guideSteps = ref([
@@ -184,13 +186,6 @@
{ title: '归还设备', desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单' }
])
// 使用指南步骤
// 使用指南已取消
// 滚动通知内容
// const noticeText = ref('消费规则:每小时5元,不足1小时按1小时计费,最高24小时封顶,请爱护设备,使用后请及时归还')
const redirectToLogin = () => {
try {
const pages = getCurrentPages()
@@ -423,14 +418,25 @@
}
const loadPositionsByCenter = async (center) => {
if (!center || !center.longitude || !center.latitude) {
console.warn('loadPositionsByCenter: 无效的中心点', center)
return
}
console.log('根据地图中心加载场地:', center)
try {
// 使用原有接口获取所有场地
// 使用原有接口获取所有场地,传入中心点经纬度
const res = await uni.request({
url: `${URL}/device/position/app/list`,
method: 'GET',
header: {
'Authorization': "Bearer " + uni.getStorageSync('token'),
'Clientid': uni.getStorageSync('client_id')
},
data: {
latitude: center.latitude,
longitude: center.longitude
}
})
@@ -438,7 +444,10 @@
redirectToLogin()
return
} else if (res.statusCode === 200 && res.data.code === 200) {
positionList.value = res.data.rows || []
const rows = res.data.rows || []
console.log('加载到场地数量:', rows.length)
positionList.value = rows
// 基于地图中心计算距离
calculateDistances(center)
@@ -448,6 +457,8 @@
return !item.distanceInMeters || item.distanceInMeters <= maxDistanceInMeters
})
console.log('过滤后场地数量:', filteredPositions.value.length)
} else {
console.error('根据地图中心加载场地失败:', res.data.msg)
positionList.value = []
@@ -460,34 +471,80 @@
}
const handleRelocate = async () => {
// 防抖:如果正在定位中,直接返回
if (isRelocating.value) {
console.log('正在定位中,请勿重复点击')
return
}
try {
isRelocating.value = true
uni.showLoading({
title: '定位中...'
title: '重新定位中...',
mask: true
})
// 重新获取用户真实位置(不使用缓存)
const loc = await getUserLocation()
const center = {
const newLocation = {
longitude: Number(loc.longitude),
latitude: Number(loc.latitude)
}
userLocation.value = center
try {
uni.setStorageSync('userLocation', center)
} catch (_) {}
if (mapRef.value && typeof mapRef.value.moveToLocation === 'function') {
mapRef.value.moveToLocation(center)
console.log('重新定位成功,新位置:', newLocation)
console.log('当前位置:', userLocation.value)
// 更新用户位置(触发地图组件的watch,自动移动地图)
userLocation.value = {
...newLocation
}
await loadPositionsByCenter(center)
// 保存到本地缓存
try {
uni.setStorageSync('userLocation', newLocation)
} catch (e) {
console.warn('缓存位置失败:', e)
}
// 确保地图移动到新位置
if (mapRef.value && typeof mapRef.value.moveToLocation === 'function') {
mapRef.value.moveToLocation(newLocation)
}
// 延迟一下,等待地图移动完成后再查询场地
await new Promise(resolve => setTimeout(resolve, 300))
// 加载新位置的场地
await loadPositionsByCenter(newLocation)
uni.hideLoading()
uni.showToast({
title: '定位失败',
icon: 'none'
title: '定位成功',
icon: 'success',
duration: 1500
})
} catch (e) {
console.error('定位失败:', e)
uni.hideLoading()
uni.showToast({
title: e.errMsg || '定位失败,请检查定位权限',
icon: 'none',
duration: 2000
})
} finally {
uni.hideLoading()
// 1秒后解除防抖锁定
setTimeout(() => {
isRelocating.value = false
}, 1000)
}
}
const onMapCenterChange = (center) => {
console.log('onMapCenterChange 被调用,中心点:', center)
if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') {
userLocation.value = {
longitude: Number(center.longitude),
@@ -496,8 +553,12 @@
try {
uni.setStorageSync('userLocation', userLocation.value)
} catch (_) {}
}
// 调用加载场地方法
loadPositionsByCenter(center)
} else {
console.warn('onMapCenterChange: 无效的中心点', center)
}
}
const selectPosition = (position) => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB