修复地图定位bug
This commit is contained in:
@@ -19,15 +19,12 @@
|
|||||||
<cover-view class="map-side-controls" v-if="!props.hideControls && !props.hideMapOverlays">
|
<cover-view class="map-side-controls" v-if="!props.hideControls && !props.hideMapOverlays">
|
||||||
<cover-view class="side-btn locate" @tap="handleRelocate">
|
<cover-view class="side-btn locate" @tap="handleRelocate">
|
||||||
<cover-image class="side-icon" src="/static/location.png"></cover-image>
|
<cover-image class="side-icon" src="/static/location.png"></cover-image>
|
||||||
<!-- <cover-view class="side-text">定位</cover-view> -->
|
|
||||||
</cover-view>
|
</cover-view>
|
||||||
<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>
|
||||||
<!-- <cover-view class="side-text">客服</cover-view> -->
|
|
||||||
</cover-view>
|
</cover-view>
|
||||||
<cover-view class="side-btn search" @tap="handleSearch">
|
<cover-view class="side-btn search" @tap="handleSearch">
|
||||||
<cover-image class="side-icon" src="/static/other_device.png"></cover-image>
|
<cover-image class="side-icon" src="/static/other_device.png"></cover-image>
|
||||||
<!-- <cover-view class="side-text">搜索</cover-view> -->
|
|
||||||
</cover-view>
|
</cover-view>
|
||||||
|
|
||||||
</cover-view>
|
</cover-view>
|
||||||
@@ -180,7 +177,6 @@
|
|||||||
// 移动地图到指定位置(直接更新地图中心坐标)
|
// 移动地图到指定位置(直接更新地图中心坐标)
|
||||||
const moveToLocation = (location) => {
|
const moveToLocation = (location) => {
|
||||||
if (!location || !location.longitude || !location.latitude) {
|
if (!location || !location.longitude || !location.latitude) {
|
||||||
console.warn('moveToLocation: 无效的位置参数', location)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +185,6 @@
|
|||||||
latitude: Number(location.latitude)
|
latitude: Number(location.latitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('移动地图到:', newCenter)
|
|
||||||
|
|
||||||
// 直接更新地图中心,触发地图组件的视图更新
|
// 直接更新地图中心,触发地图组件的视图更新
|
||||||
mapCenter.value = newCenter
|
mapCenter.value = newCenter
|
||||||
|
|
||||||
@@ -207,7 +201,6 @@
|
|||||||
oldLocation.latitude !== newLocation.latitude
|
oldLocation.latitude !== newLocation.latitude
|
||||||
|
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
console.log('用户位置变化:', newLocation)
|
|
||||||
mapCenter.value = {
|
mapCenter.value = {
|
||||||
longitude: newLocation.longitude,
|
longitude: newLocation.longitude,
|
||||||
latitude: newLocation.latitude
|
latitude: newLocation.latitude
|
||||||
@@ -234,25 +227,15 @@
|
|||||||
|
|
||||||
// 地图区域变化事件(带防抖优化)
|
// 地图区域变化事件(带防抖优化)
|
||||||
const onMapRegionChange = (e) => {
|
const onMapRegionChange = (e) => {
|
||||||
console.log('regionchange事件:', e)
|
|
||||||
|
|
||||||
// 只处理结束事件
|
// 只处理结束事件
|
||||||
if (!e || e.type !== 'end') {
|
if (!e || e.type !== 'end') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取事件原因
|
|
||||||
// 微信小程序:e.causedBy 可能的值:
|
|
||||||
// - 'gesture': 手势拖动
|
|
||||||
// - 'scale': 缩放
|
|
||||||
// - 'update': 调用更新接口(如 setCenterOffset 等)
|
|
||||||
const causedBy = e.causedBy || e.detail?.causedBy
|
const causedBy = e.causedBy || e.detail?.causedBy
|
||||||
|
|
||||||
console.log('地图变化原因:', causedBy, '完整事件:', e)
|
if (causedBy === 'gesture' || causedBy === 'scale' || causedBy === 'drag'||causedBy==='update') {
|
||||||
|
|
||||||
// 只在用户手动操作(拖动或缩放)结束时触发查询
|
|
||||||
// 排除程序调用(update)导致的变化,避免定位按钮触发多余查询
|
|
||||||
if (causedBy === 'gesture' || causedBy === 'scale' || causedBy === 'drag') {
|
|
||||||
// 清除之前的定时器
|
// 清除之前的定时器
|
||||||
if (regionChangeTimer) {
|
if (regionChangeTimer) {
|
||||||
clearTimeout(regionChangeTimer)
|
clearTimeout(regionChangeTimer)
|
||||||
@@ -269,14 +252,13 @@
|
|||||||
latitude: Number(centerLocation.latitude)
|
latitude: Number(centerLocation.latitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapCenter.value = newCenter
|
mapCenter.value = newCenter;
|
||||||
console.log('地图中心变化,触发查询:', newCenter, '原因:', causedBy)
|
|
||||||
|
|
||||||
// 触发父组件查询新位置的场地
|
// 触发父组件查询新位置的场地
|
||||||
emit('mapCenterChange', newCenter)
|
emit('mapCenterChange', newCenter)
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
console.warn('未能从事件中获取中心点位置,尝试使用API获取')
|
|
||||||
// 兜底方案:如果事件中没有centerLocation,才使用API获取
|
// 兜底方案:如果事件中没有centerLocation,才使用API获取
|
||||||
regionChangeTimer = setTimeout(() => {
|
regionChangeTimer = setTimeout(() => {
|
||||||
if (mapContext.value) {
|
if (mapContext.value) {
|
||||||
@@ -288,7 +270,6 @@
|
|||||||
latitude: res.latitude
|
latitude: res.latitude
|
||||||
}
|
}
|
||||||
mapCenter.value = newCenter
|
mapCenter.value = newCenter
|
||||||
console.log('地图中心变化(API获取):', newCenter, '原因:', causedBy)
|
|
||||||
emit('mapCenterChange', newCenter)
|
emit('mapCenterChange', newCenter)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -299,8 +280,6 @@
|
|||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('跳过查询,原因:', causedBy)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,7 +484,7 @@ const handleSearch = () => {
|
|||||||
|
|
||||||
.center-marker-icon {
|
.center-marker-icon {
|
||||||
width: 60rpx;
|
width: 60rpx;
|
||||||
height: 80rpx;
|
height: 60rpx;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
// export const URL = "https://my.gxfs123.com/api" //正式服务器
|
// export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用
|
||||||
|
// export const URL = "https://manager.fdzpower.com/api" //正式服务器
|
||||||
export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
|
export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
|
||||||
// export const URL = "http://192.168.5.149:8080" //本地调试
|
// export const URL = "http://192.168.5.149:8080" //本地调试
|
||||||
// export const URL = "http://127.0.0.1:8080" //本地调试
|
// export const URL = "http://127.0.0.1:8080" //本地调试
|
||||||
|
|||||||
@@ -298,6 +298,15 @@ export default {
|
|||||||
authCodeFailed: 'Auth failed'
|
authCodeFailed: 'Auth failed'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
permission: {
|
||||||
|
locationTitle: 'Location Permission',
|
||||||
|
locationNeed: 'We need your location to show nearby devices. Please enable location access in “Settings > Permissions”.',
|
||||||
|
locationDenied: 'Location access is disabled. You can enable it later in “Settings > Permissions” to continue.',
|
||||||
|
goToSettings: 'Open Settings',
|
||||||
|
later: 'Not now',
|
||||||
|
gotIt: 'Got it'
|
||||||
|
},
|
||||||
|
|
||||||
payment: {
|
payment: {
|
||||||
paymentAmount: 'Amount',
|
paymentAmount: 'Amount',
|
||||||
paymentMethod: 'Method',
|
paymentMethod: 'Method',
|
||||||
|
|||||||
@@ -298,6 +298,15 @@ export default {
|
|||||||
authCodeFailed: '获取授权码失败'
|
authCodeFailed: '获取授权码失败'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
permission: {
|
||||||
|
locationTitle: '位置信息授权',
|
||||||
|
locationNeed: '需要获取您的位置信息以展示附近设备,请在“设置-权限管理”中开启定位权限。',
|
||||||
|
locationDenied: '未授权定位,将无法展示附近设备。您可以稍后在“设置-权限管理”中重新开启定位权限。',
|
||||||
|
goToSettings: '去设置',
|
||||||
|
later: '暂不',
|
||||||
|
gotIt: '知道了'
|
||||||
|
},
|
||||||
|
|
||||||
payment: {
|
payment: {
|
||||||
paymentAmount: '支付金额',
|
paymentAmount: '支付金额',
|
||||||
paymentMethod: '支付方式',
|
paymentMethod: '支付方式',
|
||||||
|
|||||||
@@ -384,9 +384,12 @@
|
|||||||
|
|
||||||
// 获取价格单位文本
|
// 获取价格单位文本
|
||||||
const getPriceUnit = () => {
|
const getPriceUnit = () => {
|
||||||
|
console.log(deviceInfo.value);
|
||||||
// 按分钟计费
|
// 按分钟计费
|
||||||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||||||
return '分钟'
|
return '分钟'
|
||||||
|
}else if(deviceInfo.value && deviceFeeConfig.value.hourPrice == '0.5'){
|
||||||
|
return '30分钟'
|
||||||
}
|
}
|
||||||
// 按小时计费(默认)
|
// 按小时计费(默认)
|
||||||
return $t('time.hour')
|
return $t('time.hour')
|
||||||
@@ -417,7 +420,7 @@
|
|||||||
|
|
||||||
// 按小时计费
|
// 按小时计费
|
||||||
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1)
|
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1)
|
||||||
const unitPrice = maxHourPrice * hourPrice
|
const unitPrice = maxHourPrice
|
||||||
return unitPrice.toFixed(2)
|
return unitPrice.toFixed(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,11 +442,7 @@
|
|||||||
|
|
||||||
// 按小时计费
|
// 按小时计费
|
||||||
const unitMinutes = getBillingUnitMinutes()
|
const unitMinutes = getBillingUnitMinutes()
|
||||||
if (unitMinutes === 30) {
|
return deviceInfo.value.remark;
|
||||||
return `${unitPrice}元/${unitMinutes}分钟,${maxHourPrice}元/小时`
|
|
||||||
} else {
|
|
||||||
return `${maxHourPrice}元/小时`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成详细说明文本
|
// 生成详细说明文本
|
||||||
@@ -454,11 +453,11 @@
|
|||||||
|
|
||||||
// 按分钟计费
|
// 按分钟计费
|
||||||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||||||
return `前${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按小时计费
|
// 按小时计费
|
||||||
return `前${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交租借订单
|
// 提交租借订单
|
||||||
|
|||||||
+43
-24
@@ -4,13 +4,13 @@
|
|||||||
<!-- 问题类型选择 -->
|
<!-- 问题类型选择 -->
|
||||||
<view class="type-section">
|
<view class="type-section">
|
||||||
<view class="section-title">{{ $t('feedback.issueType') }}</view>
|
<view class="section-title">{{ $t('feedback.issueType') }}</view>
|
||||||
<view class="type-grid">
|
<view class="type-grid">
|
||||||
<view v-for="(type, index) in types" :key="index" class="type-item"
|
<view v-for="(type, index) in types" :key="index" class="type-item"
|
||||||
:class="{ active: selectedType === index }" @click="selectType(index)">
|
:class="{ active: selectedType === index }" @click="selectType(index)">
|
||||||
{{ $t(type) }}
|
{{ $t(type) }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 问题描述 -->
|
<!-- 问题描述 -->
|
||||||
<view class="description-section">
|
<view class="description-section">
|
||||||
@@ -23,23 +23,23 @@
|
|||||||
<!-- 图片上传 -->
|
<!-- 图片上传 -->
|
||||||
<view class="upload-section">
|
<view class="upload-section">
|
||||||
<view class="section-title">{{ $t('feedback.imageUpload') }}</view>
|
<view class="section-title">{{ $t('feedback.imageUpload') }}</view>
|
||||||
<view class="upload-grid">
|
<view class="upload-grid">
|
||||||
<view class="upload-item" v-for="(img, index) in images" :key="index">
|
<view class="upload-item" v-for="(img, index) in images" :key="index">
|
||||||
<image :src="img" mode="aspectFill" />
|
<image :src="img" mode="aspectFill" />
|
||||||
<view class="delete-btn" @click="deleteImage(index)">×</view>
|
<view class="delete-btn" @click="deleteImage(index)">×</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
|
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
|
||||||
<text class="plus">+</text>
|
<text class="plus">+</text>
|
||||||
<text class="tip">{{ $t('feedback.uploadImage') }}</text>
|
<text class="tip">{{ $t('feedback.uploadImage') }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 联系方式 -->
|
<!-- 联系方式 -->
|
||||||
<view class="contact-section">
|
<view class="contact-section">
|
||||||
<view class="section-title">{{ $t('feedback.contactInfo') }}</view>
|
<view class="section-title">{{ $t('feedback.contactInfo') }}</view>
|
||||||
<input class="contact-input" v-model="contact" :placeholder="$t('feedback.contactPlaceholder')" type="number"
|
<input class="contact-input" v-model="contact" :placeholder="$t('feedback.contactPlaceholder')"
|
||||||
maxlength="11" name="contact" />
|
type="number" maxlength="11" name="contact" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
@@ -55,6 +55,9 @@
|
|||||||
ref,
|
ref,
|
||||||
onMounted
|
onMounted
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
|
import {
|
||||||
|
onLoad
|
||||||
|
} from "@dcloudio/uni-app"
|
||||||
import {
|
import {
|
||||||
URL,
|
URL,
|
||||||
appid
|
appid
|
||||||
@@ -65,9 +68,12 @@
|
|||||||
import {
|
import {
|
||||||
addUserFeedback
|
addUserFeedback
|
||||||
} from '../../config/api/feedback'
|
} from '../../config/api/feedback'
|
||||||
import { useI18n } from '@/utils/i18n.js'
|
import {
|
||||||
|
useI18n
|
||||||
const { t: $t } = useI18n()
|
} from '@/utils/i18n.js'
|
||||||
|
const {
|
||||||
|
t: $t
|
||||||
|
} = useI18n()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
@@ -75,6 +81,12 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
if (uni.getStorageSync("userInfo").phone) {
|
||||||
|
contact.value = uni.getStorageSync('userInfo').phone;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const types = ref(['feedback.deviceFault', 'feedback.chargingIssue', 'feedback.usageSuggestion', 'feedback.other'])
|
const types = ref(['feedback.deviceFault', 'feedback.chargingIssue', 'feedback.usageSuggestion', 'feedback.other'])
|
||||||
const selectedType = ref(-1)
|
const selectedType = ref(-1)
|
||||||
@@ -89,6 +101,7 @@
|
|||||||
selectedType.value = index
|
selectedType.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const chooseImage = () => {
|
const chooseImage = () => {
|
||||||
uni.chooseImage({
|
uni.chooseImage({
|
||||||
count: 3 - images.value.length,
|
count: 3 - images.value.length,
|
||||||
@@ -109,7 +122,10 @@
|
|||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
images.value.splice(idx, 1)
|
images.value.splice(idx, 1)
|
||||||
}
|
}
|
||||||
uni.showToast({ title: '图片上传失败', icon: 'none' })
|
uni.showToast({
|
||||||
|
title: '图片上传失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,11 +161,12 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.value[selectedType.value] === 'feedback.deviceFault' || types.value[selectedType.value] === 'feedback.chargingIssue') {
|
if (types.value[selectedType.value] === 'feedback.deviceFault' || types.value[selectedType.value] ===
|
||||||
paramsType.value = 'complain'
|
'feedback.chargingIssue') {
|
||||||
} else {
|
paramsType.value = 'complain'
|
||||||
paramsType.value = 'suggestion'
|
} else {
|
||||||
}
|
paramsType.value = 'suggestion'
|
||||||
|
}
|
||||||
|
|
||||||
// 构建反馈数据
|
// 构建反馈数据
|
||||||
const feedbackData = {
|
const feedbackData = {
|
||||||
@@ -172,7 +189,8 @@
|
|||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 兼容后端返回 { code: 200 } 或 HTTP 200 情况
|
// 兼容后端返回 { code: 200 } 或 HTTP 200 情况
|
||||||
if ((res.statusCode === 200) && ((res.data && res.data.code === 200) || res.data === true || res.data?.success === true)) {
|
if ((res.statusCode === 200) && ((res.data && res.data.code === 200) || res.data ===
|
||||||
|
true || res.data?.success === true)) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: $t('feedback.submitSuccess'),
|
title: $t('feedback.submitSuccess'),
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
@@ -183,7 +201,8 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: (res.data && (res.data.msg || res.data.message)) || $t('feedback.submitFailed'),
|
title: (res.data && (res.data.msg || res.data.message)) || $t(
|
||||||
|
'feedback.submitFailed'),
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
+247
-254
@@ -1,41 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="container fullscreen">
|
<view class="container fullscreen">
|
||||||
<!-- 自定义导航栏 -->
|
<!-- 自定义导航栏 -->
|
||||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||||
<view class="navbar-content" :style="{ height: navBarHeight + 'px' }">
|
<view class="navbar-content" :style="{ height: navBarHeight + 'px' }">
|
||||||
<text class="navbar-title">{{ $t('home.title') }}</text>
|
<text class="navbar-title">{{ $t('home.title') }}</text>
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 顶部信息区域(通知、招商等) -->
|
|
||||||
<view class="top-info-section" :style="{ top: (statusBarHeight + navBarHeight) + 'px' }">
|
|
||||||
<!-- 通知栏 -->
|
|
||||||
<view class="notice-wrapper" v-if="noticeText" @click="openNoticePopup">
|
|
||||||
<uv-notice-bar :text="noticeText" :speed="50" :show-icon="true" color="#07c160" bg-color="#E8F8EF"
|
|
||||||
icon="volume"></uv-notice-bar>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 内容区域 -->
|
|
||||||
<view class="main-content" :style="{ paddingTop: (statusBarHeight + navBarHeight + noticeHeight) + 'px' }">
|
|
||||||
<!-- 全屏地图组件 -->
|
|
||||||
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
|
|
||||||
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
|
|
||||||
:enableMarkers="true"
|
|
||||||
:hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup"
|
|
||||||
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
|
|
||||||
@mapCenterChange="onMapCenterChange" />
|
|
||||||
|
|
||||||
<!-- 地图加载状态 -->
|
|
||||||
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
|
|
||||||
<view class="loading-content">
|
|
||||||
<view class="loading-spinner"></view>
|
|
||||||
<text>{{ $t('common.loadingLocation') }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 底部操作栏:附近设备 / 扫码使用 / 我的 -->
|
<!-- 顶部信息区域(通知、招商等) -->
|
||||||
|
<view class="top-info-section" :style="{ top: (statusBarHeight + navBarHeight) + 'px' }">
|
||||||
|
<!-- 通知栏 -->
|
||||||
|
<view class="notice-wrapper" v-if="noticeText" @click="openNoticePopup">
|
||||||
|
<uv-notice-bar :text="noticeText" :speed="50" :show-icon="true" color="#07c160" bg-color="#E8F8EF"
|
||||||
|
icon="volume"></uv-notice-bar>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="main-content" :style="{ paddingTop: (statusBarHeight + navBarHeight + noticeHeight) + 'px' }">
|
||||||
|
<!-- 全屏地图组件 -->
|
||||||
|
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
|
||||||
|
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
|
||||||
|
:enableMarkers="true" :hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup"
|
||||||
|
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
|
||||||
|
@mapCenterChange="onMapCenterChange" />
|
||||||
|
|
||||||
|
<!-- 地图加载状态 -->
|
||||||
|
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
|
||||||
|
<view class="loading-content">
|
||||||
|
<view class="loading-spinner"></view>
|
||||||
|
<text>{{ $t('common.loadingLocation') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作栏:附近设备 / 扫码使用 / 我的 -->
|
||||||
<view class="bottom-actions">
|
<view class="bottom-actions">
|
||||||
<!-- <view class="action-btn secondary small btn-nearby" @click="showLocationList">
|
<!-- <view class="action-btn secondary small btn-nearby" @click="showLocationList">
|
||||||
<view class="icon-wrap">
|
<view class="icon-wrap">
|
||||||
@@ -67,16 +66,9 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 场地列表弹窗组件 -->
|
<!-- 场地列表弹窗组件 -->
|
||||||
<LocationListSheet
|
<LocationListSheet :show="showLocationPopup" :expanded="isExpanded" :positions="filteredPositions"
|
||||||
:show="showLocationPopup"
|
:isLoading="isLoading" :title="$t('home.nearbyDeviceLocation')" @close="hideLocationList"
|
||||||
:expanded="isExpanded"
|
@select="selectPositionFromPopup" @navigate="navigateToPosition" />
|
||||||
:positions="filteredPositions"
|
|
||||||
:isLoading="isLoading"
|
|
||||||
:title="$t('home.nearbyDeviceLocation')"
|
|
||||||
@close="hideLocationList"
|
|
||||||
@select="selectPositionFromPopup"
|
|
||||||
@navigate="navigateToPosition"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态 -->
|
||||||
<view class="loading-overlay" v-if="isLoading">
|
<view class="loading-overlay" v-if="isLoading">
|
||||||
@@ -107,90 +99,82 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 使用指南:居中弹出(ref 控制 open/close) -->
|
<!-- 使用指南:居中弹出(ref 控制 open/close) -->
|
||||||
<uv-popup ref="guidePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false">
|
<uv-popup ref="guidePopup" mode="center" :overlay="true" :closeOnClickOverlay="false"
|
||||||
<view class="guide-popup">
|
:safeAreaInsetBottom="false">
|
||||||
<view class="guide-header">
|
<view class="guide-popup">
|
||||||
<text class="guide-title">{{ $t('guide.title') }}</text>
|
<view class="guide-header">
|
||||||
<!-- <view class="guide-close" @click="closeGuidePopup">
|
<text class="guide-title">{{ $t('guide.title') }}</text>
|
||||||
|
<!-- <view class="guide-close" @click="closeGuidePopup">
|
||||||
<uv-icon name="close" size="20"></uv-icon>
|
<uv-icon name="close" size="20"></uv-icon>
|
||||||
</view> -->
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
<view class="guide-content">
|
<view class="guide-content">
|
||||||
<view class="guide-step" v-for="(step, idx) in guideSteps" :key="idx">
|
<view class="guide-step" v-for="(step, idx) in guideSteps" :key="idx">
|
||||||
<view class="step-index">{{ idx + 1 }}</view>
|
<view class="step-index">{{ idx + 1 }}</view>
|
||||||
<view class="step-info">
|
<view class="step-info">
|
||||||
<view class="step-title">{{ $t('guide.step' + (idx + 1) + 'Title') }}</view>
|
<view class="step-title">{{ $t('guide.step' + (idx + 1) + 'Title') }}</view>
|
||||||
<view class="step-desc">{{ $t('guide.step' + (idx + 1) + 'Desc') }}</view>
|
<view class="step-desc">{{ $t('guide.step' + (idx + 1) + 'Desc') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<view class="guide-actions">
|
<view class="guide-actions">
|
||||||
<!-- <view class="primary-btn" @click="closeGuidePopup"></view> -->
|
<!-- <view class="primary-btn" @click="closeGuidePopup"></view> -->
|
||||||
<view class="primary-btn" @click="closeGuidePopup">
|
<view class="primary-btn" @click="closeGuidePopup">
|
||||||
<uv-icon name="close" size="20"></uv-icon>
|
<uv-icon name="close" size="20"></uv-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</uv-popup>
|
||||||
</uv-popup>
|
|
||||||
|
<!-- 通知详情弹窗:居中弹出 -->
|
||||||
|
<uv-popup ref="noticePopup" mode="center" :overlay="true" :closeOnClickOverlay="true"
|
||||||
|
:safeAreaInsetBottom="false">
|
||||||
|
<view class="notice-popup">
|
||||||
|
<view class="notice-header">
|
||||||
|
<text class="notice-title">{{ $t('home.noticeTitle') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="notice-content">
|
||||||
|
<text class="notice-text">{{ noticeText }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 通知详情弹窗:居中弹出 -->
|
|
||||||
<uv-popup ref="noticePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="true" :safeAreaInsetBottom="false">
|
|
||||||
<view class="notice-popup">
|
|
||||||
<view class="notice-header">
|
|
||||||
<text class="notice-title">{{ $t('home.noticeTitle') }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="notice-content">
|
|
||||||
<text class="notice-text">{{ noticeText }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="notice-actions">
|
<view class="notice-actions">
|
||||||
<view class="primary-btn" @click="closeNoticePopup">
|
<view class="primary-btn" @click="closeNoticePopup">
|
||||||
<uv-icon name="close" size="20"></uv-icon>
|
<uv-icon name="close" size="20"></uv-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</uv-popup>
|
||||||
</uv-popup>
|
|
||||||
|
|
||||||
<!-- 活动弹窗:居中弹出,支持多个活动轮播 -->
|
<!-- 活动弹窗:居中弹出,支持多个活动轮播 -->
|
||||||
<uv-popup ref="activityPopup" mode="center" round="16" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false" :customStyle="{ background: 'transparent' }">
|
<uv-popup ref="activityPopup" mode="center" round="16" :overlay="true" :closeOnClickOverlay="false"
|
||||||
<view class="activity-popup" v-if="activityList && activityList.length > 0">
|
:safeAreaInsetBottom="false" :customStyle="{ background: 'transparent' }">
|
||||||
<!-- 轮播图 -->
|
<view class="activity-popup" v-if="activityList && activityList.length > 0">
|
||||||
<swiper
|
<!-- 轮播图 -->
|
||||||
class="activity-swiper"
|
<swiper class="activity-swiper" :indicator-dots="activityList.length > 1" :autoplay="false"
|
||||||
:indicator-dots="activityList.length > 1"
|
:circular="false" @change="onActivitySwiperChange" :current="currentActivityIndex">
|
||||||
:autoplay="false"
|
<swiper-item v-for="(activity, index) in activityList" :key="index">
|
||||||
:circular="false"
|
<view class="activity-poster-wrapper">
|
||||||
@change="onActivitySwiperChange"
|
<image :src="activity.coverImageUrl || activity.imageUrl || activity.posterUrl"
|
||||||
:current="currentActivityIndex"
|
mode="aspectFit" class="activity-poster"></image>
|
||||||
>
|
</view>
|
||||||
<swiper-item v-for="(activity, index) in activityList" :key="index">
|
</swiper-item>
|
||||||
<view class="activity-poster-wrapper">
|
</swiper>
|
||||||
<image
|
|
||||||
:src="activity.coverImageUrl || activity.imageUrl || activity.posterUrl"
|
<!-- 自定义指示器 -->
|
||||||
mode="aspectFit"
|
<view class="activity-indicators" v-if="activityList.length > 1">
|
||||||
class="activity-poster"
|
<view v-for="(item, index) in activityList" :key="index" class="indicator-dot"
|
||||||
></image>
|
:class="{ active: index === currentActivityIndex }"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 关闭按钮 -->
|
||||||
|
<view class="activity-close-btn" @click="closeActivityPopup">
|
||||||
|
<text class="close-text">{{ $t('common.close') }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</swiper-item>
|
</uv-popup>
|
||||||
</swiper>
|
</view>
|
||||||
|
|
||||||
<!-- 自定义指示器 -->
|
|
||||||
<view class="activity-indicators" v-if="activityList.length > 1">
|
|
||||||
<view
|
|
||||||
v-for="(item, index) in activityList"
|
|
||||||
:key="index"
|
|
||||||
class="indicator-dot"
|
|
||||||
:class="{ active: index === currentActivityIndex }"
|
|
||||||
></view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 关闭按钮 -->
|
|
||||||
<view class="activity-close-btn" @click="closeActivityPopup">
|
|
||||||
<text class="close-text">{{ $t('common.close') }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</uv-popup>
|
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -230,7 +214,9 @@
|
|||||||
// 注意:从 pages/index/ 目录访问 components/ 需要使用 ../../components/ 路径
|
// 注意:从 pages/index/ 目录访问 components/ 需要使用 ../../components/ 路径
|
||||||
import MapComponent from '../../components/MapComponent.vue'
|
import MapComponent from '../../components/MapComponent.vue'
|
||||||
import LocationListSheet from '../../components/LocationListSheet.vue'
|
import LocationListSheet from '../../components/LocationListSheet.vue'
|
||||||
import { useI18n } from '../../utils/i18n.js'
|
import {
|
||||||
|
useI18n
|
||||||
|
} from '../../utils/i18n.js'
|
||||||
|
|
||||||
// 开启右上角分享菜单(仅 mp-weixin 有效)
|
// 开启右上角分享菜单(仅 mp-weixin 有效)
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
@@ -240,7 +226,9 @@
|
|||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
const { t: $t } = useI18n()
|
const {
|
||||||
|
t: $t
|
||||||
|
} = useI18n()
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const searchKeyword = ref('')
|
const searchKeyword = ref('')
|
||||||
@@ -251,26 +239,37 @@
|
|||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
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 isRelocating = ref(false) // 防抖标志:是否正在重新定位
|
||||||
const showGuidePopup = ref(false) // 使用指南弹窗显示状态
|
const showGuidePopup = ref(false) // 使用指南弹窗显示状态
|
||||||
const showNoticePopup = ref(false) // 通知详情弹窗显示状态
|
const showNoticePopup = ref(false) // 通知详情弹窗显示状态
|
||||||
const showActivityPopup = ref(false) // 活动弹窗显示状态
|
const showActivityPopup = ref(false) // 活动弹窗显示状态
|
||||||
const activityList = ref([]) // 活动列表
|
const activityList = ref([]) // 活动列表
|
||||||
const currentActivityIndex = ref(0) // 当前活动索引
|
const currentActivityIndex = ref(0) // 当前活动索引
|
||||||
const hasShownActivityThisSession = ref(false) // 本次会话是否已显示过活动(内存变量)
|
const hasShownActivityThisSession = ref(false) // 本次会话是否已显示过活动(内存变量)
|
||||||
|
|
||||||
// 导航栏高度相关
|
// 导航栏高度相关
|
||||||
const statusBarHeight = ref(0)
|
const statusBarHeight = ref(0)
|
||||||
const navBarHeight = ref(44) // 默认导航栏内容高度
|
const navBarHeight = ref(44) // 默认导航栏内容高度
|
||||||
const noticeHeight = ref(0) // 通知栏高度
|
const noticeHeight = ref(0) // 通知栏高度
|
||||||
|
|
||||||
// 使用指南步骤
|
// 使用指南步骤
|
||||||
const guideSteps = ref([
|
const guideSteps = ref([{
|
||||||
{ title: '扫码使用', desc: '找到附近设备,扫描设备上的二维码' },
|
title: '扫码使用',
|
||||||
{ title: '免押金支付', desc: '无需支付押金,使用支付分免押即可完成租借' },
|
desc: '找到附近设备,扫描设备上的二维码'
|
||||||
{ title: '开始使用', desc: '设备自动解锁,风扇弹出后取出即可开始使用' },
|
},
|
||||||
{ title: '归还设备', desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单' }
|
{
|
||||||
|
title: '免押金支付',
|
||||||
|
desc: '无需支付押金,使用支付分免押即可完成租借'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始使用',
|
||||||
|
desc: '设备自动解锁,风扇弹出后取出即可开始使用'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '归还设备',
|
||||||
|
desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单'
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const redirectToLogin = () => {
|
const redirectToLogin = () => {
|
||||||
@@ -376,11 +375,11 @@ const statusBarHeight = ref(0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 组件引用
|
// 组件引用
|
||||||
const mapRef = ref(null)
|
const mapRef = ref(null)
|
||||||
const guidePopup = ref(null)
|
const guidePopup = ref(null)
|
||||||
const noticePopup = ref(null)
|
const noticePopup = ref(null)
|
||||||
const activityPopup = ref(null)
|
const activityPopup = ref(null)
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
const searchPlaceholder = computed(() => {
|
const searchPlaceholder = computed(() => {
|
||||||
@@ -446,90 +445,82 @@ const activityPopup = ref(null)
|
|||||||
await getNoticeText();
|
await getNoticeText();
|
||||||
|
|
||||||
// 1. 先获取用户位置
|
// 1. 先获取用户位置
|
||||||
await getUserLocationAndAddress()
|
await getUserLocationAndAddress()
|
||||||
|
|
||||||
// 2. 加载场地列表
|
// 2. 加载场地列表(依赖定位)
|
||||||
await loadPositions()
|
await loadPositions()
|
||||||
|
|
||||||
// 3. 查询活动并显示弹窗
|
// 3. 查询活动并显示弹窗
|
||||||
await checkActiveActivity()
|
await checkActiveActivity()
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('初始化失败:', error)
|
console.error('初始化失败:', error)
|
||||||
// 即使失败也要加载场地列表
|
uni.showToast({
|
||||||
await loadPositions()
|
title: $t('home.getLocationFailed'),
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserLocationAndAddress = async () => {
|
const getUserLocationAndAddress = async () => {
|
||||||
try {
|
// 使用腾讯地图SDK获取位置(若失败抛出,由调用方统一处理)
|
||||||
// 使用腾讯地图SDK获取位置
|
const location = await getUserLocation()
|
||||||
const location = await getUserLocation()
|
|
||||||
|
|
||||||
// 保存用户位置
|
if (!location?.longitude || !location?.latitude) {
|
||||||
userLocation.value = {
|
throw new Error('invalid location result')
|
||||||
longitude: location.longitude,
|
}
|
||||||
latitude: location.latitude
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(userLocation.value);
|
// 保存用户位置
|
||||||
// 将经纬度写入本地缓存(基础信息)
|
userLocation.value = {
|
||||||
|
longitude: location.longitude,
|
||||||
|
latitude: location.latitude
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(userLocation.value);
|
||||||
|
// 将经纬度写入本地缓存(基础信息)
|
||||||
|
try {
|
||||||
|
uni.setStorageSync('userLocation', {
|
||||||
|
longitude: location.longitude,
|
||||||
|
latitude: location.latitude
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('缓存基础定位信息失败:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只在首次初始化时设置标记
|
||||||
|
if (!isLocationInitialized.value) {
|
||||||
|
isLocationInitialized.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取详细地址信息
|
||||||
|
try {
|
||||||
|
const addressResult = await getRegeo(location.longitude, location.latitude)
|
||||||
|
if (addressResult.success) {
|
||||||
|
const addressInfo = addressResult.data
|
||||||
|
userLocation.value.address = addressInfo.formatted_address
|
||||||
|
userLocation.value.city = addressInfo.addressComponent.city
|
||||||
|
userLocation.value.district = addressInfo.addressComponent.district
|
||||||
|
|
||||||
|
// 更新本地缓存,包含地址信息
|
||||||
try {
|
try {
|
||||||
uni.setStorageSync('userLocation', {
|
uni.setStorageSync('userLocation', {
|
||||||
longitude: location.longitude,
|
longitude: userLocation.value.longitude,
|
||||||
latitude: location.latitude
|
latitude: userLocation.value.latitude,
|
||||||
|
address: userLocation.value.address,
|
||||||
|
city: userLocation.value.city,
|
||||||
|
district: userLocation.value.district
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('缓存基础定位信息失败:', e)
|
console.warn('缓存带地址的定位信息失败:', e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只在首次初始化时设置标记
|
|
||||||
if (!isLocationInitialized.value) {
|
|
||||||
isLocationInitialized.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取详细地址信息
|
|
||||||
try {
|
|
||||||
const addressResult = await getRegeo(location.longitude, location.latitude)
|
|
||||||
if (addressResult.success) {
|
|
||||||
const addressInfo = addressResult.data
|
|
||||||
userLocation.value.address = addressInfo.formatted_address
|
|
||||||
userLocation.value.city = addressInfo.addressComponent.city
|
|
||||||
userLocation.value.district = addressInfo.addressComponent.district
|
|
||||||
|
|
||||||
// 更新本地缓存,包含地址信息
|
|
||||||
try {
|
|
||||||
uni.setStorageSync('userLocation', {
|
|
||||||
longitude: userLocation.value.longitude,
|
|
||||||
latitude: userLocation.value.latitude,
|
|
||||||
address: userLocation.value.address,
|
|
||||||
city: userLocation.value.city,
|
|
||||||
district: userLocation.value.district
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('缓存带地址的定位信息失败:', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// 忽略地址信息错误,使用基础定位信息
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待地图组件响应位置变化后,重新加载场地列表
|
|
||||||
setTimeout(async () => {
|
|
||||||
await loadPositions()
|
|
||||||
|
|
||||||
uni.hideLoading()
|
|
||||||
}, 800)
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取位置失败:', error)
|
|
||||||
uni.showToast({
|
|
||||||
title: $t('home.getLocationFailed'),
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 忽略地址信息错误,使用基础定位信息
|
||||||
|
}
|
||||||
|
|
||||||
|
return userLocation.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadPositions = async () => {
|
const loadPositions = async () => {
|
||||||
@@ -723,7 +714,6 @@ const activityPopup = ref(null)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
@@ -830,16 +820,16 @@ const activityPopup = ref(null)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (inUseRes.statusCode === 401 || inUseRes.data?.code === 401 || inUseRes.data?.code === 40101) {
|
if (inUseRes.statusCode === 401 || inUseRes.data?.code === 401 || inUseRes.data?.code === 40101) {
|
||||||
redirectToLogin()
|
redirectToLogin()
|
||||||
return
|
return
|
||||||
} else if (inUseRes.statusCode == 200 && inUseRes.data.code == 200 && inUseRes.data.data) {
|
} else if (inUseRes.statusCode == 200 && inUseRes.data.code == 200 && inUseRes.data.data) {
|
||||||
const inUseOrder = inUseRes.data.data
|
const inUseOrder = inUseRes.data.data
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有待支付订单
|
// 检查是否有待支付订单
|
||||||
const orderRes = await uni.request({
|
const orderRes = await uni.request({
|
||||||
@@ -921,57 +911,57 @@ const activityPopup = ref(null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用指南弹窗控制
|
// 使用指南弹窗控制
|
||||||
const openPopup = () => {
|
const openPopup = () => {
|
||||||
try {
|
try {
|
||||||
showGuidePopup.value = true
|
showGuidePopup.value = true
|
||||||
guidePopup.value && typeof guidePopup.value.open === 'function' && guidePopup.value.open()
|
guidePopup.value && typeof guidePopup.value.open === 'function' && guidePopup.value.open()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeGuidePopup = () => {
|
const closeGuidePopup = () => {
|
||||||
try {
|
try {
|
||||||
showGuidePopup.value = false
|
showGuidePopup.value = false
|
||||||
guidePopup.value && typeof guidePopup.value.close === 'function' && guidePopup.value.close()
|
guidePopup.value && typeof guidePopup.value.close === 'function' && guidePopup.value.close()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知弹窗控制
|
// 通知弹窗控制
|
||||||
const openNoticePopup = () => {
|
const openNoticePopup = () => {
|
||||||
try {
|
try {
|
||||||
showNoticePopup.value = true
|
showNoticePopup.value = true
|
||||||
noticePopup.value && typeof noticePopup.value.open === 'function' && noticePopup.value.open()
|
noticePopup.value && typeof noticePopup.value.open === 'function' && noticePopup.value.open()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeNoticePopup = () => {
|
const closeNoticePopup = () => {
|
||||||
try {
|
try {
|
||||||
showNoticePopup.value = false
|
showNoticePopup.value = false
|
||||||
noticePopup.value && typeof noticePopup.value.close === 'function' && noticePopup.value.close()
|
noticePopup.value && typeof noticePopup.value.close === 'function' && noticePopup.value.close()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 活动弹窗控制
|
// 活动弹窗控制
|
||||||
const openActivityPopup = () => {
|
const openActivityPopup = () => {
|
||||||
try {
|
try {
|
||||||
showActivityPopup.value = true
|
showActivityPopup.value = true
|
||||||
activityPopup.value && typeof activityPopup.value.open === 'function' && activityPopup.value.open()
|
activityPopup.value && typeof activityPopup.value.open === 'function' && activityPopup.value.open()
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeActivityPopup = () => {
|
const closeActivityPopup = () => {
|
||||||
try {
|
try {
|
||||||
showActivityPopup.value = false
|
showActivityPopup.value = false
|
||||||
activityPopup.value && typeof activityPopup.value.close === 'function' && activityPopup.value.close()
|
activityPopup.value && typeof activityPopup.value.close === 'function' && activityPopup.value.close()
|
||||||
// 重置索引
|
// 重置索引
|
||||||
currentActivityIndex.value = 0
|
currentActivityIndex.value = 0
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 活动轮播图切换
|
// 活动轮播图切换
|
||||||
const onActivitySwiperChange = (e) => {
|
const onActivitySwiperChange = (e) => {
|
||||||
currentActivityIndex.value = e.detail.current
|
currentActivityIndex.value = e.detail.current
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -1040,7 +1030,8 @@ const onActivitySwiperChange = (e) => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-bottom: 180rpx; /* 为底部按钮留出空间 */
|
padding-bottom: 180rpx;
|
||||||
|
/* 为底部按钮留出空间 */
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1509,7 +1500,8 @@ const onActivitySwiperChange = (e) => {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 180rpx; /* 为底部按钮留出空间 */
|
bottom: 180rpx;
|
||||||
|
/* 为底部按钮留出空间 */
|
||||||
background: #f6f7fb;
|
background: #f6f7fb;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1659,6 +1651,7 @@ const onActivitySwiperChange = (e) => {
|
|||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
padding: 24rpx 24rpx 16rpx;
|
padding: 24rpx 24rpx 16rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
// padding-bottom:100rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guide-header {
|
.guide-header {
|
||||||
@@ -1732,7 +1725,7 @@ const onActivitySwiperChange = (e) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.guide-actions {
|
.guide-actions {
|
||||||
margin-top:20rpx;
|
margin-top: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-btn {
|
.primary-btn {
|
||||||
@@ -1752,8 +1745,8 @@ const onActivitySwiperChange = (e) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.primary-label{
|
.primary-label {
|
||||||
color:#ffffff;
|
color: #ffffff;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-53
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="search-page">
|
<view class="search-page">
|
||||||
<view class="map-wrap">
|
<view class="map-wrap">
|
||||||
<MapComponent :userLocation="userLocation" :filteredPositions="filteredPositions"
|
<MapComponent :userLocation="userLocation" :filteredPositions="filteredPositions" :enableMarkers="true"
|
||||||
:enableMarkers="true" :customHeight="'100%'" :hideControls="true" :fullWidth="true"
|
:customHeight="'100%'" :hideControls="true" :fullWidth="true" @mapCenterChange="onMapCenterChange"
|
||||||
@mapCenterChange="onMapCenterChange" @relocate="init" @markerTap="goToPositionDetail" />
|
@relocate="init" @markerTap="goToPositionDetail" />
|
||||||
<!-- 定位按钮 -->
|
<!-- 定位按钮 -->
|
||||||
<view class="relocate-btn" @click="init">
|
<view class="relocate-btn" @click="init">
|
||||||
<image src="/static/location.png" class="relocate-icon" mode="aspectFit"></image>
|
<image src="/static/location.png" class="relocate-icon" mode="aspectFit"></image>
|
||||||
@@ -12,15 +12,19 @@
|
|||||||
<view class="list-wrap">
|
<view class="list-wrap">
|
||||||
<view class="panel">
|
<view class="panel">
|
||||||
<view class="filter-tabs">
|
<view class="filter-tabs">
|
||||||
<view class="tab" :class="{ active: activeTab === 'rent' }" @click="setTab('rent')">{{ $t('location.rent') }}</view>
|
<view class="tab" :class="{ active: activeTab === 'rent' }" @click="setTab('rent')">
|
||||||
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">{{ $t('location.return') }}</view>
|
{{ $t('location.rent') }}</view>
|
||||||
|
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">
|
||||||
|
{{ $t('location.return') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view class="list-scroll" scroll-y="true">
|
<scroll-view class="list-scroll" scroll-y="true">
|
||||||
<view class="card" :class="{ available: isRentable(item), invalid: !isValidCoordinate(item.latitude, item.longitude) }"
|
<view class="card"
|
||||||
|
:class="{ available: isRentable(item), invalid: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||||
v-for="(item, index) in filteredPositions" :key="item.positionId || index"
|
v-for="(item, index) in filteredPositions" :key="item.positionId || index"
|
||||||
@click="goToPositionDetail(item)">
|
@click="goToPositionDetail(item)">
|
||||||
<view class="thumb">
|
<view class="thumb">
|
||||||
<image v-if="item.deviceImg" :src="item.deviceImg" class="thumb-img" mode="aspectFill"></image>
|
<image v-if="item.deviceImg" :src="item.deviceImg" class="thumb-img" mode="aspectFill">
|
||||||
|
</image>
|
||||||
<image v-else src="/static/device-info.png" class="thumb-img" mode="aspectFit"></image>
|
<image v-else src="/static/device-info.png" class="thumb-img" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="info">
|
<view class="info">
|
||||||
@@ -31,41 +35,44 @@
|
|||||||
<view class="row sub" v-if="item.location">
|
<view class="row sub" v-if="item.location">
|
||||||
<text class="addr">{{ item.location }}</text>
|
<text class="addr">{{ item.location }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
|
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
|
||||||
<text class="time">{{ $t('location.businessHours') }}{{ item.workTime }}</text>
|
<text class="time">{{ $t('location.businessHours') }}{{ item.workTime }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="row meta" v-if="!isValidCoordinate(item.latitude, item.longitude)">
|
<view class="row meta" v-if="!isValidCoordinate(item.latitude, item.longitude)">
|
||||||
<text class="time" style="color: #ff6b6b;">{{ $t('location.coordinateError') }}</text>
|
<text class="time" style="color: #ff6b6b;">{{ $t('location.coordinateError') }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="row meta" v-if="item.availablePowerBankCount !== undefined && item.availablePowerBankCount !== null">
|
<view class="row meta"
|
||||||
<text class="time">可租借:{{ item.availablePowerBankCount }} 台</text>
|
v-if="item.availablePowerBankCount !== undefined && item.availablePowerBankCount !== null">
|
||||||
</view>
|
<text class="time">可租借:{{ item.availablePowerBankCount }} 台</text>
|
||||||
<view class="row meta" v-if="item.availableEmptyGridCount !== undefined && item.availableEmptyGridCount !== null">
|
</view>
|
||||||
<text class="time">可归还:{{ item.availableEmptyGridCount }} 个空位</text>
|
<view class="row meta"
|
||||||
</view>
|
v-if="item.availableEmptyGridCount !== undefined && item.availableEmptyGridCount !== null">
|
||||||
<view class="row meta remark-info" v-if="item.remark">
|
<text class="time">可归还:{{ item.availableEmptyGridCount }} 个空位</text>
|
||||||
<text class="time">💰 {{ item.remark }}</text>
|
</view>
|
||||||
</view>
|
<view class="row meta remark-info" v-if="item.remark">
|
||||||
<view class="tags">
|
<text class="time">💰 {{ item.remark }}</text>
|
||||||
<view class="tag rent" v-if="isRentable(item)">{{ $t('location.rent') }}</view>
|
</view>
|
||||||
<view class="tag return" v-if="isReturnable(item)">{{ $t('location.return') }}</view>
|
<view class="tags">
|
||||||
</view>
|
<view class="tag rent" v-if="isRentable(item)">{{ $t('location.rent') }}</view>
|
||||||
|
<view class="tag return" v-if="isReturnable(item)">{{ $t('location.return') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="actions">
|
<view class="actions">
|
||||||
|
|
||||||
<view class="nav"
|
<view class="nav" :class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||||
:class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
|
|
||||||
@click.stop="navigateToPosition(item)">
|
@click.stop="navigateToPosition(item)">
|
||||||
<image src="/static/luxian.png" class="action-icon" mode="aspectFit"></image>
|
<image src="/static/luxian.png" class="action-icon" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="distance" v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">{{ item.distance }}</view>
|
<view class="distance"
|
||||||
|
v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">
|
||||||
|
{{ item.distance }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
|
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
|
||||||
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||||
<text class="empty-text">{{ $t('home.noNearbyDevice') }}</text>
|
<text class="empty-text">{{ $t('home.noNearbyDevice') }}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -88,15 +95,25 @@
|
|||||||
getRegeo,
|
getRegeo,
|
||||||
calculateDistanceSync
|
calculateDistanceSync
|
||||||
} from '../../utils/mapUtils.js'
|
} from '../../utils/mapUtils.js'
|
||||||
import { useI18n } from '@/utils/i18n.js'
|
import {
|
||||||
|
useI18n
|
||||||
|
} from '@/utils/i18n.js'
|
||||||
|
|
||||||
const { t: $t } = useI18n()
|
const {
|
||||||
|
t: $t
|
||||||
|
} = useI18n()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: $t('search.title')
|
title: $t('search.title')
|
||||||
})
|
})
|
||||||
|
// uni.showLoading({
|
||||||
|
// title:'11111',
|
||||||
|
// mask:true
|
||||||
|
// })
|
||||||
init()
|
init()
|
||||||
|
// uni.hideLoading();
|
||||||
})
|
})
|
||||||
|
|
||||||
const userLocation = ref(null)
|
const userLocation = ref(null)
|
||||||
@@ -199,7 +216,8 @@
|
|||||||
return {
|
return {
|
||||||
...transformed,
|
...transformed,
|
||||||
canRent: activeTab.value === 'rent' ? true : (device.availablePowerBankCount > 0),
|
canRent: activeTab.value === 'rent' ? true : (device.availablePowerBankCount > 0),
|
||||||
canReturn: activeTab.value === 'return' ? true : (device.availableEmptyGridCount > 0)
|
canReturn: activeTab.value === 'return' ? true : (device.availableEmptyGridCount >
|
||||||
|
0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -217,24 +235,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
uni.showLoading({
|
||||||
const loc = await getUserLocation()
|
mask: true
|
||||||
userLocation.value = {
|
})
|
||||||
longitude: loc.longitude,
|
try {
|
||||||
latitude: loc.latitude
|
const loc = await getUserLocation()
|
||||||
}
|
if (!loc?.longitude || !loc?.latitude) {
|
||||||
await loadPositions(userLocation.value)
|
throw new Error('invalid location result')
|
||||||
} catch (e) {
|
|
||||||
await loadPositions(userLocation.value || {
|
|
||||||
longitude: 0,
|
|
||||||
latitude: 0
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
}
|
||||||
|
userLocation.value = {
|
||||||
|
longitude: loc.longitude,
|
||||||
|
latitude: loc.latitude
|
||||||
|
}
|
||||||
|
await loadPositions(userLocation.value)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('初始化定位失败:', e)
|
||||||
|
userLocation.value = null
|
||||||
|
positionList.value = []
|
||||||
|
filteredPositions.value = []
|
||||||
|
uni.showToast({
|
||||||
|
title: $t('home.getLocationFailed'),
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
uni.hideLoading();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onMapCenterChange = (center) => {
|
const onMapCenterChange = (center) => {
|
||||||
if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') {
|
if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') {
|
||||||
@@ -276,7 +305,6 @@
|
|||||||
url: `/pages/position/detail?positionId=${position.positionId}`
|
url: `/pages/position/detail?positionId=${position.positionId}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -427,6 +455,7 @@
|
|||||||
|
|
||||||
.addr {
|
.addr {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
|
line-clamp: 1;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -44,12 +44,12 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item" v-if="userInfo.balanceAmount !== undefined">
|
<!-- <view class="form-item" v-if="userInfo.balanceAmount !== undefined">
|
||||||
<view class="label">{{ $t('userProfile.balance') }}</view>
|
<view class="label">{{ $t('userProfile.balance') }}</view>
|
||||||
<view class="value">
|
<view class="value">
|
||||||
<text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text>
|
<text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.9 KiB |
+145
-22
@@ -1,5 +1,41 @@
|
|||||||
// 地图工具函数 - 内联腾讯地图SDK核心代码
|
// 地图工具函数 - 内联腾讯地图SDK核心代码
|
||||||
|
|
||||||
|
const DEFAULT_LOCALE = 'zh-CN'
|
||||||
|
const permissionTexts = {
|
||||||
|
'zh-CN': {
|
||||||
|
locationTitle: '位置信息授权',
|
||||||
|
locationNeed: '需要获取您的位置信息以展示附近设备,请在“设置-权限管理”中开启定位权限。',
|
||||||
|
locationDenied: '未授权定位,将无法展示附近设备。您可以稍后在“设置-权限管理”中重新开启定位权限。',
|
||||||
|
goToSettings: '去设置',
|
||||||
|
later: '暂不',
|
||||||
|
gotIt: '知道了'
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
locationTitle: 'Location Permission',
|
||||||
|
locationNeed: 'We need your location to show nearby devices. Please enable location in “Settings > Permissions”.',
|
||||||
|
locationDenied: 'Location access is disabled. You can re-enable it later in “Settings > Permissions”.',
|
||||||
|
goToSettings: 'Set',
|
||||||
|
later: 'Skip',
|
||||||
|
gotIt: 'OK'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCurrentLocale = () => {
|
||||||
|
try {
|
||||||
|
const saved = uni.getStorageSync('language')
|
||||||
|
if (saved && permissionTexts[saved]) {
|
||||||
|
return saved
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return DEFAULT_LOCALE
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPermissionText = (key) => {
|
||||||
|
const locale = getCurrentLocale()
|
||||||
|
const texts = permissionTexts[locale] || permissionTexts[DEFAULT_LOCALE]
|
||||||
|
return texts[key] || permissionTexts[DEFAULT_LOCALE][key] || ''
|
||||||
|
}
|
||||||
|
|
||||||
// 腾讯地图Key
|
// 腾讯地图Key
|
||||||
const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35';
|
const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35';
|
||||||
|
|
||||||
@@ -408,16 +444,120 @@ function getQQMapInstance() {
|
|||||||
return qqmapInstance || initQQMap();
|
return qqmapInstance || initQQMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户位置(使用微信的接口获取位置)
|
// 获取用户位置(统一处理小程序定位授权 && 实际定位)
|
||||||
function getUserLocation() {
|
function getUserLocation() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
wx.getLocation({
|
// 小程序端优先通过 getSetting 判断权限状态
|
||||||
|
// 这里只考虑 mp-weixin 场景,其它平台回退到直接调用 getLocation
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.getSetting({
|
||||||
|
success: (settingRes) => {
|
||||||
|
const authSetting = settingRes.authSetting || {};
|
||||||
|
// 兼容多种定位 scope(微信近几次版本变更比较多)
|
||||||
|
const hasUserLocation = Object.prototype.hasOwnProperty.call(authSetting, 'scope.userLocation');
|
||||||
|
const userLocationAuth = authSetting['scope.userLocation'];
|
||||||
|
const fuzzyLocationAuth = authSetting['scope.userFuzzyLocation'];
|
||||||
|
const bgLocationAuth = authSetting['scope.userLocationBackground'];
|
||||||
|
|
||||||
|
// 已明确拒绝定位
|
||||||
|
if (
|
||||||
|
userLocationAuth === false ||
|
||||||
|
fuzzyLocationAuth === false ||
|
||||||
|
bgLocationAuth === false
|
||||||
|
) {
|
||||||
|
uni.showModal({
|
||||||
|
title: getPermissionText('locationTitle'),
|
||||||
|
content: getPermissionText('locationNeed'),
|
||||||
|
confirmText: getPermissionText('goToSettings'),
|
||||||
|
cancelText: getPermissionText('later'),
|
||||||
|
success: (modalRes) => {
|
||||||
|
if (modalRes.confirm) {
|
||||||
|
uni.openSetting({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reject({
|
||||||
|
code: 'LOCATION_DENIED',
|
||||||
|
errMsg: 'user denied location permission'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doGetLocation = () => {
|
||||||
|
wx.getLocation({
|
||||||
|
type: 'gcj02',
|
||||||
|
success: (res) => {
|
||||||
|
const longitude = parseFloat(res.longitude.toFixed(5));
|
||||||
|
const latitude = parseFloat(res.latitude.toFixed(5));
|
||||||
|
console.log('地址获取成功');
|
||||||
|
resolve({
|
||||||
|
longitude,
|
||||||
|
latitude
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('获取位置失败:', error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 未显式授权时,主动申请一次权限(老版本 scope 为 scope.userLocation)
|
||||||
|
if (!hasUserLocation || userLocationAuth === undefined) {
|
||||||
|
uni.authorize({
|
||||||
|
scope: 'scope.userLocation',
|
||||||
|
success: () => {
|
||||||
|
doGetLocation();
|
||||||
|
},
|
||||||
|
fail: (authErr) => {
|
||||||
|
console.error('定位授权失败:', authErr);
|
||||||
|
uni.showModal({
|
||||||
|
title: getPermissionText('locationTitle'),
|
||||||
|
content: getPermissionText('locationDenied'),
|
||||||
|
confirmText: getPermissionText('gotIt'),
|
||||||
|
showCancel: false
|
||||||
|
});
|
||||||
|
reject({
|
||||||
|
code: 'LOCATION_AUTH_FAIL',
|
||||||
|
errMsg: authErr.errMsg || 'authorize location fail'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 已授权,直接获取定位
|
||||||
|
doGetLocation();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('获取授权设置失败,直接尝试定位:', err);
|
||||||
|
wx.getLocation({
|
||||||
|
type: 'gcj02',
|
||||||
|
success: (res) => {
|
||||||
|
const longitude = parseFloat(res.longitude.toFixed(5));
|
||||||
|
const latitude = parseFloat(res.latitude.toFixed(5));
|
||||||
|
console.log('地址获取成功');
|
||||||
|
resolve({
|
||||||
|
longitude,
|
||||||
|
latitude
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('获取位置失败:', error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 非微信小程序平台:使用 uni.getLocation 做一个尽量兼容的兜底
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
uni.getLocation({
|
||||||
type: 'gcj02',
|
type: 'gcj02',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// 对经度和纬度进行四舍五入,保留小数点后五位
|
|
||||||
const longitude = parseFloat(res.longitude.toFixed(5));
|
const longitude = parseFloat(res.longitude.toFixed(5));
|
||||||
const latitude = parseFloat(res.latitude.toFixed(5));
|
const latitude = parseFloat(res.latitude.toFixed(5));
|
||||||
|
console.log('地址获取成功');
|
||||||
resolve({
|
resolve({
|
||||||
longitude,
|
longitude,
|
||||||
latitude
|
latitude
|
||||||
@@ -428,26 +568,9 @@ function getUserLocation() {
|
|||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// #endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//TODO : 修改getUserLocation函数,使其返回Promise(暂时弃用)
|
|
||||||
// function getUserLocation() {
|
|
||||||
// return new Promise((resolve, reject) => {
|
|
||||||
// wx.getLocation({
|
|
||||||
// type: 'gcj02',
|
|
||||||
// success: (res) => {
|
|
||||||
// resolve({
|
|
||||||
// longitude: res.longitude,
|
|
||||||
// latitude: res.latitude
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// fail: (error) => {
|
|
||||||
// console.error('获取位置失败:', error);
|
|
||||||
// reject(error);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 逆地理编码 - 根据经纬度获取地址信息
|
// 逆地理编码 - 根据经纬度获取地址信息
|
||||||
function getRegeo(longitude, latitude) {
|
function getRegeo(longitude, latitude) {
|
||||||
|
|||||||
Reference in New Issue
Block a user