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
+129 -105
View File
@@ -4,12 +4,18 @@
<view class="map-wrapper"> <view class="map-wrapper">
<!-- 使用小程序原生地图组件 --> <!-- 使用小程序原生地图组件 -->
<map id="map" class="native-map" :longitude="mapCenter.longitude" :latitude="mapCenter.latitude" <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"> @markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated" @error="onMapError">
<!-- 覆盖在地图上的可点击控件使用 cover-view 以兼容小程序原生组件层级 --> <!-- 覆盖在地图上的可点击控件使用 cover-view 以兼容小程序原生组件层级 -->
<cover-view class="index-swiper" v-if="!props.hideControls" @tap="handleJoinTap"> <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-image src="/static/index_swiper.png" class="index-swiper-img" mode="aspectFit"></cover-image>
</cover-view> </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="map-side-controls" v-if="!props.hideControls">
<cover-view class="side-btn service" @tap="handleService"> <cover-view class="side-btn service" @tap="handleService">
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image> <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 class="side-text">定位</cover-view> -->
</cover-view> </cover-view>
</cover-view> </cover-view>
<!-- 使用原生 marker 方案渲染中心指示 regionchange 同步到地图中心 -->
</map> </map>
<!-- 地图加载状态 --> <!-- 地图加载状态 -->
@@ -144,28 +149,14 @@
!(latitude === 0 && longitude === 0) // 排除 0,0 这种无效坐标 !(latitude === 0 && longitude === 0) // 排除 0,0 这种无效坐标
} }
// 防抖定时器
let regionChangeTimer = null
// 方法 // 方法
const updateMapMarkers = () => { const updateMapMarkers = () => {
const markers = [] const markers = []
// 中心 marker(始终存在,使用传入中心坐标或 userLocation
const centerLng = Number(mapCenter.value.longitude || (props.userLocation && props.userLocation.longitude)) // 只添加周边场地位置点,中心定位图标改用固定的cover-view显示
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
} // 图钉尖端对准坐标
})
}
// 可选:周边位置点
if (props.enableMarkers && props.filteredPositions && props.filteredPositions.length > 0) { if (props.enableMarkers && props.filteredPositions && props.filteredPositions.length > 0) {
props.filteredPositions.forEach((pos, index) => { props.filteredPositions.forEach((pos, index) => {
if (pos.longitude && pos.latitude && isValidCoordinate(pos.latitude, pos.longitude)) { if (pos.longitude && pos.latitude && isValidCoordinate(pos.latitude, pos.longitude)) {
@@ -175,9 +166,9 @@
id: index + 1, id: index + 1,
latitude: lat, latitude: lat,
longitude: lng, longitude: lng,
iconPath: '/static/map.png', iconPath: '/static/markes_fdz.png',
width: 30, width: 40,
height: 30, height: 40,
callout: { callout: {
content: pos.name, content: pos.name,
fontSize: 12, fontSize: 12,
@@ -195,45 +186,43 @@
isLoading.value = false isLoading.value = false
} }
// 移动地图到指定位置(使用 includePoints 以避免 mapid 错误,并兼容不同基础库 // 移动地图到指定位置(直接更新地图中心坐标
const moveToLocation = (location) => { const moveToLocation = (location) => {
if (!location || !location.longitude || !location.latitude) return if (!location || !location.longitude || !location.latitude) {
if (!mapContext.value) return console.warn('moveToLocation: 无效的位置参数', location)
return
try {
mapContext.value.includePoints({
points: [{
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)
} }
const newCenter = {
longitude: Number(location.longitude),
latitude: Number(location.latitude)
}
console.log('移动地图到:', newCenter)
// 直接更新地图中心,触发地图组件的视图更新
mapCenter.value = newCenter
// 更新标记点
updateMapMarkers()
} }
// 监听用户位置变化 // 监听用户位置变化
watch(() => props.userLocation, (newLocation) => { watch(() => props.userLocation, (newLocation, oldLocation) => {
if (newLocation && newLocation.longitude && newLocation.latitude) { if (newLocation && newLocation.longitude && newLocation.latitude) {
mapCenter.value = { // 检查位置是否真的变化了(避免重复更新)
longitude: newLocation.longitude, const isChanged = !oldLocation ||
latitude: newLocation.latitude oldLocation.longitude !== newLocation.longitude ||
oldLocation.latitude !== newLocation.latitude
if (isChanged) {
console.log('用户位置变化:', newLocation)
mapCenter.value = {
longitude: newLocation.longitude,
latitude: newLocation.latitude
}
updateMapMarkers()
} }
updateMapMarkers()
moveToLocation(newLocation)
} }
}, { }, {
immediate: true, immediate: true,
@@ -252,25 +241,75 @@
isLoading.value = false isLoading.value = false
} }
// 地图区域变化事件 // 地图区域变化事件(带防抖优化)
const onMapRegionChange = (e) => { const onMapRegionChange = (e) => {
// 在手势或缩放结束时更新中心坐标 console.log('regionchange事件:', e)
if (e && e.type === 'end') {
if (mapContext.value) { // 只处理结束事件
mapContext.value.getCenterLocation({ if (!e || e.type !== 'end') {
success: (res) => { return
if (res && res.longitude && res.latitude) { }
mapCenter.value = {
longitude: res.longitude, // 获取事件原因
latitude: res.latitude // 微信小程序:e.causedBy 可能的值:
} // - 'gesture': 手势拖动
// 更新中心 marker 位置 // - 'scale': 缩放
updateMapMarkers() // - 'update': 调用更新接口(如 setCenterOffset 等)
emit('mapCenterChange', mapCenter.value) 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) {
const newCenter = {
longitude: res.longitude,
latitude: res.latitude
}
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 onMapMarkerTap = (e) => {
const markerId = e.detail?.markerId || e.markerId const markerId = e.detail?.markerId || e.markerId
// 中心点标记 // 查找对应的场地位置信息
if (markerId === 999999) {
uni.showToast({
title: '这是您的当前位置',
icon: 'none'
})
return
}
// 查找对应的位置信息
if (props.filteredPositions && props.filteredPositions.length > 0) { if (props.filteredPositions && props.filteredPositions.length > 0) {
const position = props.filteredPositions[markerId - 1] const position = props.filteredPositions[markerId - 1]
if (position) { if (position) {
@@ -369,6 +399,10 @@ const handleSearch = () => {
onUnmounted(() => { onUnmounted(() => {
// 清理工作 // 清理工作
if (regionChangeTimer) {
clearTimeout(regionChangeTimer)
regionChangeTimer = null
}
mapContext.value = null mapContext.value = null
}) })
@@ -464,33 +498,23 @@ const handleSearch = () => {
} }
} }
/* 地图中心图钉(cover-view,避免使用 transform 以兼容小程序 */ /* 地图中心定位图标(固定在屏幕中心 */
.center-pin { .center-location-marker {
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
z-index: 16; z-index: 100;
width: 0; width: 60rpx;
height: 0; height: 80rpx;
margin-left: -30rpx;
margin-top: -80rpx;
pointer-events: none;
} }
.center-pin .pin-img { .center-marker-icon {
position: absolute; width: 60rpx;
width: 48rpx; height: 80rpx;
height: 64rpx; display: block;
/* 使图钉尖端对准中心点: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;
} }
.map-side-controls { .map-side-controls {
+89 -28
View File
@@ -6,11 +6,12 @@
icon="volume"></uv-notice-bar> icon="volume"></uv-notice-bar>
</view> </view>
<!-- 全屏地图组件 --> <!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation" <MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword" :positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition" :enableMarkers="true"
@mapCenterChange="onMapCenterChange" /> @relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" />
<!-- 底部操作栏附近设备 / 扫码使用 / 我的 --> <!-- 底部操作栏附近设备 / 扫码使用 / 我的 -->
<view class="bottom-actions"> <view class="bottom-actions">
@@ -175,6 +176,7 @@
const showPhoneAuthPopup = ref(false) const showPhoneAuthPopup = ref(false)
const isLocationInitialized = ref(false) const isLocationInitialized = ref(false)
const showLocationPopup = ref(false) const showLocationPopup = ref(false)
const isRelocating = ref(false) // 防抖标志:是否正在重新定位
// 使用指南步骤 // 使用指南步骤
const guideSteps = ref([ const guideSteps = ref([
@@ -184,13 +186,6 @@
{ title: '归还设备', desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单' } { title: '归还设备', desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单' }
]) ])
// 使用指南步骤
// 使用指南已取消
// 滚动通知内容
// const noticeText = ref('消费规则:每小时5元,不足1小时按1小时计费,最高24小时封顶,请爱护设备,使用后请及时归还')
const redirectToLogin = () => { const redirectToLogin = () => {
try { try {
const pages = getCurrentPages() const pages = getCurrentPages()
@@ -423,14 +418,25 @@
} }
const loadPositionsByCenter = async (center) => { const loadPositionsByCenter = async (center) => {
if (!center || !center.longitude || !center.latitude) {
console.warn('loadPositionsByCenter: 无效的中心点', center)
return
}
console.log('根据地图中心加载场地:', center)
try { try {
// 使用原有接口获取所有场地 // 使用原有接口获取所有场地,传入中心点经纬度
const res = await uni.request({ const res = await uni.request({
url: `${URL}/device/position/app/list`, url: `${URL}/device/position/app/list`,
method: 'GET', method: 'GET',
header: { header: {
'Authorization': "Bearer " + uni.getStorageSync('token'), 'Authorization': "Bearer " + uni.getStorageSync('token'),
'Clientid': uni.getStorageSync('client_id') 'Clientid': uni.getStorageSync('client_id')
},
data: {
latitude: center.latitude,
longitude: center.longitude
} }
}) })
@@ -438,7 +444,10 @@
redirectToLogin() redirectToLogin()
return return
} else if (res.statusCode === 200 && res.data.code === 200) { } 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) calculateDistances(center)
@@ -447,6 +456,8 @@
filteredPositions.value = positionList.value.filter(item => { filteredPositions.value = positionList.value.filter(item => {
return !item.distanceInMeters || item.distanceInMeters <= maxDistanceInMeters return !item.distanceInMeters || item.distanceInMeters <= maxDistanceInMeters
}) })
console.log('过滤后场地数量:', filteredPositions.value.length)
} else { } else {
console.error('根据地图中心加载场地失败:', res.data.msg) console.error('根据地图中心加载场地失败:', res.data.msg)
@@ -460,34 +471,80 @@
} }
const handleRelocate = async () => { const handleRelocate = async () => {
// 防抖:如果正在定位中,直接返回
if (isRelocating.value) {
console.log('正在定位中,请勿重复点击')
return
}
try { try {
isRelocating.value = true
uni.showLoading({ uni.showLoading({
title: '定位中...' title: '重新定位中...',
mask: true
}) })
// 重新获取用户真实位置(不使用缓存)
const loc = await getUserLocation() const loc = await getUserLocation()
const center = { const newLocation = {
longitude: Number(loc.longitude), longitude: Number(loc.longitude),
latitude: Number(loc.latitude) latitude: Number(loc.latitude)
} }
userLocation.value = center
try { console.log('重新定位成功,新位置:', newLocation)
uni.setStorageSync('userLocation', center) console.log('当前位置:', userLocation.value)
} catch (_) {}
if (mapRef.value && typeof mapRef.value.moveToLocation === 'function') { // 更新用户位置(触发地图组件的watch,自动移动地图)
mapRef.value.moveToLocation(center) userLocation.value = {
...newLocation
} }
await loadPositionsByCenter(center)
} catch (e) { // 保存到本地缓存
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({ uni.showToast({
title: '定位失败', title: '定位成功',
icon: 'none' icon: 'success',
duration: 1500
})
} catch (e) {
console.error('定位失败:', e)
uni.hideLoading()
uni.showToast({
title: e.errMsg || '定位失败,请检查定位权限',
icon: 'none',
duration: 2000
}) })
} finally { } finally {
uni.hideLoading() // 1秒后解除防抖锁定
setTimeout(() => {
isRelocating.value = false
}, 1000)
} }
} }
const onMapCenterChange = (center) => { const onMapCenterChange = (center) => {
console.log('onMapCenterChange 被调用,中心点:', center)
if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') { if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') {
userLocation.value = { userLocation.value = {
longitude: Number(center.longitude), longitude: Number(center.longitude),
@@ -496,8 +553,12 @@
try { try {
uni.setStorageSync('userLocation', userLocation.value) uni.setStorageSync('userLocation', userLocation.value)
} catch (_) {} } catch (_) {}
// 调用加载场地方法
loadPositionsByCenter(center)
} else {
console.warn('onMapCenterChange: 无效的中心点', center)
} }
loadPositionsByCenter(center)
} }
const selectPosition = (position) => { const selectPosition = (position) => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB