fix:修复bug
This commit is contained in:
+280
-237
@@ -3,10 +3,27 @@
|
||||
<!-- 地图容器 -->
|
||||
<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"
|
||||
@markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated"
|
||||
@error="onMapError"></map>
|
||||
@error="onMapError">
|
||||
<!-- 覆盖在地图上的可点击控件(使用 cover-view 以兼容小程序原生组件层级) -->
|
||||
<cover-view class="map-side-controls">
|
||||
<cover-view class="side-btn service" @tap="handleService">
|
||||
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image>
|
||||
<!-- <cover-view class="side-text">客服</cover-view> -->
|
||||
</cover-view>
|
||||
<cover-view class="side-btn search" @tap="handleSearch">
|
||||
<cover-image class="side-icon" src="/static/search-icon.png"></cover-image>
|
||||
<!-- <cover-view class="side-text">搜索</cover-view> -->
|
||||
</cover-view>
|
||||
<cover-view class="side-btn locate" @tap="handleRelocate">
|
||||
<cover-image class="side-icon" src="/static/location.png"></cover-image>
|
||||
<!-- <cover-view class="side-text">定位</cover-view> -->
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
<!-- 使用原生 marker 方案渲染中心指示,随 regionchange 同步到地图中心 -->
|
||||
</map>
|
||||
|
||||
|
||||
|
||||
@@ -18,35 +35,19 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 地图控制按钮 -->
|
||||
<view class="map-controls">
|
||||
<view class="control-btn location-control" @tap="handleRelocate">
|
||||
<!-- <image class="control-icon" src="/static/scan-icon.png" mode="aspectFit" /> -->
|
||||
<uv-icon name="map-fill" size="18"></uv-icon>
|
||||
<text style="margin-left: 8rpx;">我的位置</text>
|
||||
</view>
|
||||
<view class="control-btn scan-control main-btn" @tap="handleScan">
|
||||
<image class="control-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||
<text>扫码使用</text>
|
||||
</view>
|
||||
<view class="control-btn list-control" @tap="handleShowList">
|
||||
<image class="control-icon" src="/static/map.png" mode="aspectFit" />
|
||||
<text>附近设备</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
// 导入地图工具函数
|
||||
import {
|
||||
calculateDistanceSync
|
||||
@@ -75,7 +76,7 @@
|
||||
])
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
const props = defineProps({
|
||||
userLocation: {
|
||||
type: Object,
|
||||
default: null
|
||||
@@ -91,7 +92,15 @@
|
||||
searchKeyword: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
noticeText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
enableMarkers: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// Emits
|
||||
@@ -114,91 +123,73 @@
|
||||
const mapContext = ref(null) // 地图上下文
|
||||
|
||||
// 方法
|
||||
const updateMapMarkers = () => {
|
||||
mapMarkers.value = []
|
||||
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)) {
|
||||
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.userLocation) {
|
||||
mapMarkers.value.push({
|
||||
id: 0, // ID必须是数字
|
||||
// iconPath: '/static/scan-icon.png',
|
||||
width: 32,
|
||||
height: 32,
|
||||
latitude: props.userLocation.latitude,
|
||||
longitude: props.userLocation.longitude,
|
||||
title: '我的位置',
|
||||
callout: {
|
||||
content: '我的位置',
|
||||
color: '#ffffff',
|
||||
fontSize: 12,
|
||||
borderRadius: 4,
|
||||
bgColor: '#2196F3',
|
||||
padding: 6,
|
||||
display: 'BYCLICK' // 点击时显示
|
||||
},
|
||||
customCallout: {
|
||||
anchorX: 0,
|
||||
anchorY: 0
|
||||
}
|
||||
})
|
||||
}
|
||||
// 可选:周边位置点
|
||||
if (props.enableMarkers && props.filteredPositions && props.filteredPositions.length > 0) {
|
||||
props.filteredPositions.forEach((pos, index) => {
|
||||
if (pos.longitude && pos.latitude) {
|
||||
const lat = parseFloat(pos.latitude)
|
||||
const lng = parseFloat(pos.longitude)
|
||||
if (lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
||||
markers.push({
|
||||
id: index + 1,
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
width: 24,
|
||||
height: 24,
|
||||
title: pos.name
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 添加位置点标记
|
||||
if (props.filteredPositions && props.filteredPositions.length > 0) {
|
||||
props.filteredPositions.forEach((pos, index) => {
|
||||
if (pos.longitude && pos.latitude) {
|
||||
// 验证纬度值是否在有效范围内
|
||||
const lat = parseFloat(pos.latitude);
|
||||
const lng = parseFloat(pos.longitude);
|
||||
mapMarkers.value = markers
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// 检查纬度是否在-90到90之间,经度是否在-180到180之间
|
||||
if (lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
||||
mapMarkers.value.push({
|
||||
id: index + 1, // ID必须是数字,避免和用户位置的ID冲突
|
||||
// iconPath: '/static/scan-icon.png',
|
||||
width: 30,
|
||||
height: 30,
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
title: pos.name,
|
||||
position: pos, // 存储原始位置数据,用于事件处理
|
||||
callout: {
|
||||
content: pos.name,
|
||||
color: '#333333',
|
||||
fontSize: 12,
|
||||
borderRadius: 4,
|
||||
bgColor: '#ffffff',
|
||||
padding: 6,
|
||||
display: 'BYCLICK' // 点击时显示
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.warn(`忽略无效坐标: ${pos.name}, 纬度=${lat}, 经度=${lng}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 移动地图到指定位置(使用 includePoints 以避免 mapid 错误,并兼容不同基础库)
|
||||
const moveToLocation = (location) => {
|
||||
if (!location || !location.longitude || !location.latitude) return
|
||||
if (!mapContext.value) return
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// 移动地图到指定位置
|
||||
const moveToLocation = (location) => {
|
||||
if (!location || !location.longitude || !location.latitude) return
|
||||
|
||||
if (mapContext.value) {
|
||||
mapContext.value.moveToLocation({
|
||||
longitude: location.longitude,
|
||||
latitude: location.latitude,
|
||||
success: () => {
|
||||
console.log('地图已移动到指定位置')
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('移动地图失败:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听用户位置变化
|
||||
watch(() => props.userLocation, (newLocation) => {
|
||||
@@ -228,25 +219,26 @@
|
||||
}
|
||||
|
||||
// 地图区域变化事件
|
||||
const onMapRegionChange = (e) => {
|
||||
// 当地图区域变化结束时,更新mapCenter
|
||||
if (e.type === 'end' && e.causedBy === 'drag') {
|
||||
// 获取地图中心点
|
||||
if (mapContext.value) {
|
||||
mapContext.value.getCenterLocation({
|
||||
success: (res) => {
|
||||
if (res.longitude && res.latitude) {
|
||||
mapCenter.value = {
|
||||
longitude: res.longitude,
|
||||
latitude: res.latitude
|
||||
}
|
||||
emit('mapCenterChange', mapCenter.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
const onMapRegionChange = (e) => {
|
||||
// 在手势或缩放结束时更新中心坐标
|
||||
if (e && e.type === 'end') {
|
||||
if (mapContext.value) {
|
||||
mapContext.value.getCenterLocation({
|
||||
success: (res) => {
|
||||
if (res && res.longitude && res.latitude) {
|
||||
mapCenter.value = {
|
||||
longitude: res.longitude,
|
||||
latitude: res.latitude
|
||||
}
|
||||
// 更新中心 marker 位置
|
||||
updateMapMarkers()
|
||||
emit('mapCenterChange', mapCenter.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 标记点点击事件
|
||||
const onMapMarkerTap = (e) => {
|
||||
@@ -284,8 +276,55 @@
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
const handleRelocate = () => {
|
||||
emit('relocate')
|
||||
const handleRelocate = () => {
|
||||
// 直接委托父级处理定位并移动地图,避免内部重复弹 loading
|
||||
try {
|
||||
emit('relocate')
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// 选择位置后移动地图并通知父组件刷新附近列表(增强容错与平台兼容)
|
||||
const onSuccess = (res) => {
|
||||
try {
|
||||
const lng = Number(res.longitude)
|
||||
const lat = Number(res.latitude)
|
||||
if (!isNaN(lng) && !isNaN(lat)) {
|
||||
const center = { longitude: lng, latitude: lat }
|
||||
moveToLocation(center)
|
||||
emit('mapCenterChange', center)
|
||||
} else {
|
||||
uni.showToast({ title: '无效的坐标', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('处理选择位置结果异常:', e)
|
||||
}
|
||||
}
|
||||
const onFail = (err) => {
|
||||
console.error('chooseLocation 失败:', err)
|
||||
// 授权被拒时引导开启
|
||||
if (err && (String(err.errMsg || '').includes('auth') || String(err.errMsg || '').includes('deny'))) {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.openSetting && wx.openSetting({})
|
||||
// #endif
|
||||
}
|
||||
// uni.showToast({ title: '无法打开选择位置', icon: 'none' })
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
if (typeof wx !== 'undefined' && wx.chooseLocation) {
|
||||
wx.chooseLocation({ success: onSuccess, fail: onFail })
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
if (uni && typeof uni.chooseLocation === 'function') {
|
||||
uni.chooseLocation({ success: onSuccess, fail: onFail })
|
||||
} else {
|
||||
uni.showToast({ title: '当前环境不支持选择位置', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleService = () => {
|
||||
uni.navigateTo({ url: '/pages/help/index' })
|
||||
}
|
||||
|
||||
const handleScan = () => {
|
||||
@@ -297,19 +336,26 @@
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
// 初始化地图上下文
|
||||
nextTick(() => {
|
||||
// 需要使用nextTick确保地图组件已经渲染
|
||||
mapContext.value = uni.createMapContext('map')
|
||||
updateMapMarkers()
|
||||
onMounted(() => {
|
||||
// 初始化地图上下文
|
||||
nextTick(() => {
|
||||
// 需要使用nextTick确保地图组件已经渲染
|
||||
const inst = getCurrentInstance()
|
||||
const vm = (inst && (inst.proxy || inst)) || undefined
|
||||
try {
|
||||
mapContext.value = uni.createMapContext('map', vm)
|
||||
} catch (e) {
|
||||
// 兼容:如果第二参不被支持,退回单参
|
||||
mapContext.value = uni.createMapContext('map')
|
||||
}
|
||||
updateMapMarkers()
|
||||
|
||||
// 初始化折叠面板
|
||||
if (collapseRef.value) {
|
||||
collapseRef.value.init()
|
||||
}
|
||||
})
|
||||
})
|
||||
// 初始化折叠面板
|
||||
if (collapseRef.value) {
|
||||
collapseRef.value.init()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理工作
|
||||
@@ -340,34 +386,29 @@
|
||||
/* 地图容器 */
|
||||
.map-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
height: 60vh;
|
||||
/* 增加高度 */
|
||||
width: 92%;
|
||||
/* 略微增加宽度 */
|
||||
margin: 10rpx auto 30rpx;
|
||||
/* 调整上下间距,左右自动居中 */
|
||||
border-radius: 24rpx;
|
||||
/* 添加圆角 */
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 94vw;
|
||||
height: 78vh;
|
||||
margin: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
/* 确保圆角生效 */
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
/* 添加阴影效果 */
|
||||
|
||||
.map-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 24rpx;
|
||||
/* 内层也添加圆角 */
|
||||
border-radius: 0;
|
||||
|
||||
.native-map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 24rpx;
|
||||
/* 地图也添加圆角 */
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,42 +447,66 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.map-controls {
|
||||
|
||||
/* 地图中心图钉(cover-view,避免使用 transform 以兼容小程序) */
|
||||
.center-pin {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
bottom: 20rpx;
|
||||
left: 20rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: 16;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
min-width: 120rpx;
|
||||
/* 减小按钮宽度 */
|
||||
height: 70rpx;
|
||||
/* 减小按钮高度 */
|
||||
background: #ffffff;
|
||||
border-radius: 35rpx;
|
||||
display: flex;
|
||||
.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;
|
||||
}
|
||||
|
||||
.map-side-controls {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 300rpx;
|
||||
margin: auto;
|
||||
gap: 12rpx;
|
||||
|
||||
.side-btn {
|
||||
// min-width: 160rpx;
|
||||
margin: auto;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: 36rpx;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
padding: 0 16rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
padding: 0 18rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.control-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
@@ -449,65 +514,39 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.main-btn {
|
||||
min-width: 140rpx;
|
||||
/* 减小主按钮宽度 */
|
||||
height: 80rpx;
|
||||
/* 减小主按钮高度 */
|
||||
box-shadow: 0 6rpx 20rpx rgba(33, 150, 243, 0.4);
|
||||
transform: translateY(-5rpx);
|
||||
|
||||
.control-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-5rpx) scale(0.95);
|
||||
}
|
||||
&.search {
|
||||
border-color: #07c160;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scan-control {
|
||||
background: #2196F3;
|
||||
/* 展示用图标列(与 cover-view 对齐) */
|
||||
.map-side-icons {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.control-icon {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
text {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.list-control {
|
||||
background: #4CAF50;
|
||||
|
||||
.control-icon {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
text {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.location-control {
|
||||
background: #ffffff;
|
||||
.side-btn {
|
||||
min-width: 160rpx;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: 36rpx;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
padding: 0 18rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
|
||||
.control-icon {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
text {
|
||||
.label {
|
||||
margin-left: 8rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,5 +562,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.side-icon{
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user