fix:修复bug;新增订单列表页面订单监控

This commit is contained in:
2025-12-30 17:26:16 +08:00
parent 9d4e229312
commit be01fb211e
13 changed files with 400 additions and 125 deletions
+25 -1
View File
@@ -111,7 +111,8 @@
onMounted
} from 'vue'
import {
onLoad
onLoad,
onUnload
} from '@dcloudio/uni-app'
import {
getDeviceInfo,
@@ -170,6 +171,21 @@
await fetchDeviceInfo()
})
// 页面卸载时设置默认启动路径为首页(仅在非下单流程时生效)
onUnload(() => {
// 如果是下单流程跳转(在提交订单时设置了标记),则本次不设置启动路径
const skipSetLaunchPathOnce = uni.getStorageSync('skipSetLaunchPathOnce')
if (skipSetLaunchPathOnce) {
console.log('下单流程离开设备详情页,本次不设置启动路径')
uni.removeStorageSync('skipSetLaunchPathOnce')
return
}
// 正常离开设备详情页(比如返回、关闭小程序)时,记录启动路径为首页
uni.setStorageSync('launchPath', '/pages/index/index')
console.log('设备详情页卸载,已设置启动路径为首页')
})
const checkUserPhone = async () => {
try {
const userInfoRes = await getUserInfo()
@@ -278,6 +294,7 @@
// 检查登录状态和订单
const fetchDeviceInfo = async () => {
console.log(deviceId.value);
const res = await getDeviceInfo(deviceId.value)
if (res.code == 200) {
deviceInfo.value = res.data.device || {}
@@ -504,6 +521,13 @@
const order = rentResult.data
console.log('订单信息', order);
// 标记:本次是从设备详情页发起的下单流程,离开页面时不设置启动路径
try {
uni.setStorageSync('skipSetLaunchPathOnce', true)
} catch (e) {
console.warn('设置 skipSetLaunchPathOnce 失败:', e)
}
if (payWay == 'wx-pay') {
// 当支付方式为押金支付时
uni.hideLoading()
+1 -1
View File
@@ -152,7 +152,7 @@
const status = statusTabs[currentTab.value].status;
const params = {
pageNum: currentPage.value,
pageSize: pageSize.value
pageSize: pageSize.value,
};
if (status) {
params.status = status;
+99 -89
View File
@@ -7,24 +7,24 @@
</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 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>
<!-- 内容区域 -->
<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" :bannerImages="bannerImages"
:hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" />
<!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
:enableMarkers="true" :bannerImages="bannerImages"
:hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup" @relocate="handleRelocate"
@scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" />
<!-- 地图加载状态 -->
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
@@ -101,7 +101,7 @@
</view>
<!-- 使用指南居中弹出ref 控制 open/close -->
<uv-popup ref="guidePopup" mode="center" :overlay="true" :closeOnClickOverlay="false"
<uv-popup ref="guidePopup" mode="center" :overlay="true" :closeOnClickOverlay="false"
:safeAreaInsetBottom="false">
<view class="guide-popup">
<view class="guide-header">
@@ -318,7 +318,7 @@
'Content-Language': languageCode
},
data: {
type: 'wx_user_type' // 微信小程序用户端
type: 'wx_user_type' // 微信小程序用户端
}
})
@@ -366,8 +366,9 @@
'Content-Language': languageCode
},
data: {
appPlatform: 'wechat', // 微信平台
appType: 'user' // 用户端
appPlatform: 'wechat', // 微信平台
appType: 'user' ,// 用户端
pictureLocation:'home_banner'
}
})
@@ -567,94 +568,94 @@
console.warn('清理旧缓存失败:', e);
}
// 开发环境测试距离计算
if (process.env.NODE_ENV === 'development') {
testDistanceCalculation()
}
// 并行加载公告和广告(不依赖定位)
await Promise.all([
getNoticeText(),
getBannerImages()
])
// 开发环境测试距离计算
if (process.env.NODE_ENV === 'development') {
testDistanceCalculation()
}
// 1. 先获取用户位置
await getUserLocationAndAddress()
// 并行加载公告和广告(不依赖定位)
await Promise.all([
getNoticeText(),
getBannerImages()
])
// 2. 加载场地列表(依赖定位)
await loadPositions()
// 1. 先获取用户位置
await getUserLocationAndAddress()
// 3. 查询活动并显示弹窗
await checkActiveActivity()
// 2. 加载场地列表(依赖定位)
await loadPositions()
// 3. 查询活动并显示弹窗
await checkActiveActivity()
} catch (error) {
console.error('初始化失败:', error)
uni.showToast({
title: $t('home.getLocationFailed'),
icon: 'none'
})
uni.showToast({
title: $t('home.getLocationFailed'),
icon: 'none'
})
} finally {
isLoading.value = false
}
}
const getUserLocationAndAddress = async () => {
// 使用腾讯地图SDK获取位置(若失败抛出,由调用方统一处理)
const location = await getUserLocation()
// 使用腾讯地图SDK获取位置(若失败抛出,由调用方统一处理)
const location = await getUserLocation()
if (!location?.longitude || !location?.latitude) {
throw new Error('invalid location result')
}
if (!location?.longitude || !location?.latitude) {
throw new Error('invalid location result')
}
// 保存用户位置
userLocation.value = {
longitude: location.longitude,
latitude: location.latitude
}
console.log(userLocation.value);
// 将经纬度写入本地缓存(基础信息)
try {
uni.setStorageSync('userLocation', {
// 保存用户位置
userLocation.value = {
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 {
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) {
// 忽略地址信息错误,使用基础定位信息
}
return userLocation.value
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 {
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) {
// 忽略地址信息错误,使用基础定位信息
}
return userLocation.value
}
const loadPositions = async () => {
@@ -934,7 +935,16 @@
})
})
let deviceNo = getQueryString(scanResult.path, 'deviceNo')
console.log(scanResult);
let deviceNo;
if (scanResult.scanType=='"QR_CODE"') {
deviceNo = getQueryString(scanResult.result, 'deviceNo')
} else {
deviceNo = getQueryString(scanResult.path, 'deviceNo')
}
if (!deviceNo) {
uni.showToast({
+2 -1
View File
@@ -186,7 +186,8 @@ import {
},
data: {
appPlatform: 'wechat', // 微信平台
appType: 'user' // 用户端
appType: 'user' ,// 用户端
pictureLocation:'userProfile_banner'
}
})
+150 -15
View File
@@ -80,8 +80,25 @@
</view>
</view>
<!-- 转为自用提示 - 在使用中状态时显示 -->
<view v-if="orderInfo.orderStatus === 'in_used'" class="convert-tip" @click="handleConvertToOwned">
{{ $t('order.convertToOwn') }}
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<!-- 支付成功状态 -->
<template v-if="orderInfo.orderStatus === 'payment_successful'">
<view class="bottom-icon-btn" @click="contactService">
<image src="/static/customer-service.png" class="icon" mode="aspectFit"></image>
<text>{{ $t('user.customerService') }}</text>
</view>
<view class="bottom-icon-btn" @click="handleDeviceNoEject">
<image src="/static/complaint.png" class="icon" mode="aspectFit"></image>
<text>{{ $t('order.deviceNoEject') }}</text>
</view>
</template>
<!-- 使用中状态 -->
<template v-if="orderInfo.orderStatus === 'in_used'">
<view class="bottom-icon-btn" @click="contactService">
@@ -104,6 +121,11 @@
<view v-else class="action-btn primary" @click="quickReturn">
{{ $t('order.quickReturn') }}
</view>
<!-- 宝未弹出按钮 - 放在最后 -->
<view class="bottom-icon-btn" @click="handleDeviceNoEject">
<image src="/static/complaint.png" class="icon" mode="aspectFit"></image>
<text>{{ $t('order.deviceNoEject') }}</text>
</view>
</template>
<!-- 已完成状态 -->
@@ -140,8 +162,13 @@
import { onLoad, onShow, onHide, onUnload } from '@dcloudio/uni-app'
import {
queryById,
cancelOrder
cancelOrder,
reportDeviceNoEject,
convertToOwned
} from '@/config/api/order.js'
import {
addUserFeedback
} from '@/config/api/feedback.js'
import {
getSystemConfig
} from '@/config/api/system.js'
@@ -497,18 +524,11 @@
}
}
// 从订单监控服务中移除当前订单
// 从订单详情页角度不再主动移除订单监控
// 订单监控的生命周期完全交给全局 orderMonitor,在订单真正完成时自动移除
const removeFromOrderMonitor = () => {
if (orderInfo.value.orderId && $orderMonitor) {
try {
$orderMonitor.removeOrder({
orderId: orderInfo.value.orderId
})
console.log('订单已从监控队列移除:', orderInfo.value.orderId)
} catch (error) {
console.error('从监控队列移除订单失败:', error)
}
}
// 保留函数以兼容已有调用,但不再从全局监控中删除订单
console.log('removeFromOrderMonitor 调用:已不再从全局监控中移除订单,交由 orderMonitor 在订单完成时处理')
}
// 处理订单完成事件
@@ -691,7 +711,7 @@
// 更新订单信息
updateOrderInfo(orderData)
// 只有使用中的订单才启动定时器和监控
// 只有使用中的订单才启动本页的计时器逻辑
if (orderInfo.value.orderStatus === 'in_used') {
startTimer()
startStatusCheckTimer()
@@ -699,10 +719,13 @@
if (orderInfo.value.isSupportExpressReturn !== 'no') {
startExpressCountdown()
}
}
// 无论当前订单状态如何,只要进入订单详情页,就把当前订单交给全局订单监控
if (orderInfo.value.orderId) {
uni.setStorageSync('activeOrderId', orderInfo.value.orderId)
// 添加到监控队列
// 添加/更新到监控队列
try {
if ($orderMonitor) {
$orderMonitor.removeOrder({
@@ -711,7 +734,9 @@
$orderMonitor.addOrder({
orderId: orderInfo.value.orderId
}, 'detail')
console.log('订单已添加到监控队列:', orderInfo.value.orderId)
console.log('订单已添加到监控队列(无论当前状态):', orderInfo.value.orderId, '当前状态:', orderInfo.value.orderStatus)
} else {
console.warn('$orderMonitor 未定义,无法添加订单到监控队列')
}
} catch (error) {
console.error('添加订单到监控队列失败:', error)
@@ -880,6 +905,98 @@
})
}
// 处理"宝未弹出"反馈
const handleDeviceNoEject = () => {
uni.showModal({
title: $t('order.deviceNoEjectTitle'),
content: $t('order.deviceNoEjectConfirm'),
confirmText: $t('common.confirm'),
cancelText: $t('common.cancel'),
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: $t('common.submitting')
})
// 调用反馈API提交工单
const feedbackData = {
type: 'device_issue',
content: `订单号:${orderInfo.value.orderNo}\n设备号:${deviceId.value}\n问题:充电宝未弹出`,
phone: orderInfo.value.phone || '',
orderId: orderInfo.value.orderId,
orderNo: orderInfo.value.orderNo
}
const result = await addUserFeedback(feedbackData)
if (result.code === 200) {
uni.hideLoading()
uni.showToast({
title: $t('order.deviceNoEjectSuccess'),
icon: 'success',
duration: 3000
})
} else {
throw new Error(result.msg || $t('order.deviceNoEjectFailed'))
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || $t('order.deviceNoEjectFailed'),
icon: 'none',
duration: 2000
})
}
}
}
})
}
// 处理"转为自用"
const handleConvertToOwned = () => {
uni.showModal({
title: $t('order.convertToOwnTitle'),
content: $t('order.convertToOwnConfirm'),
confirmText: $t('common.confirm'),
cancelText: $t('common.cancel'),
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: $t('common.processing')
})
const result = await convertToOwned(orderInfo.value.orderId)
if (result.code === 200) {
uni.hideLoading()
uni.showToast({
title: $t('order.convertToOwnSuccess'),
icon: 'success',
duration: 2000
})
// 刷新订单详情
setTimeout(() => {
getOrderDetails()
}, 2000)
} else {
throw new Error(result.msg || $t('order.convertToOwnFailed'))
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || $t('order.convertToOwnFailed'),
icon: 'none',
duration: 2000
})
}
}
}
})
}
// 生命周期钩子
onLoad((options) => {
console.log('订单详情页加载,参数:', JSON.stringify(options))
@@ -1029,6 +1146,24 @@
}
}
// 转为自用提示
.convert-tip {
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: calc(120rpx + env(safe-area-inset-bottom));
color: #000;
text-align: center;
padding: 20rpx 30rpx;
border-radius: 12rpx;
font-size: 26rpx;
z-index: 9;
&:active {
opacity: 0.85;
}
}
// 租借信息卡片
.rent-card {
background: #fff;
+25 -8
View File
@@ -35,7 +35,8 @@
import {
ref,
reactive,
onMounted
onMounted,
onUnmounted
} from 'vue';
import OrderItemCard from '../../components/OrderItemCard.vue';
import {
@@ -60,13 +61,6 @@
const { t: $t } = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('order.myOrders')
})
})
// 初始化状态
const currentTab = ref(0);
const orderList = ref([]);
@@ -228,6 +222,29 @@
}
};
// 处理订单完成事件
const handleOrderCompleted = (orderData) => {
console.log('订单列表页收到订单完成事件:', orderData)
// 刷新订单列表,根据当前选中的标签刷新对应状态的订单
const statusList = orderStatusTabs[currentTab.value].status[0]
loadOrderList(statusList)
}
// 设置页面标题并监听订单完成事件
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('order.myOrders')
})
// 监听订单完成事件
uni.$on('orderCompleted', handleOrderCompleted)
})
// 页面卸载时移除事件监听
onUnmounted(() => {
uni.$off('orderCompleted', handleOrderCompleted)
})
// 同步订单状态
const getOrderStatus = async (order) => {
try {
+2 -2
View File
@@ -93,7 +93,7 @@
} catch (e) {}
uni.showToast({ title: $t('waiting.rentFailed'), icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
uni.reLaunch({ url: '/pages/index/index' })
}, 800)
}
@@ -131,7 +131,7 @@
stopAllTimers()
uni.showToast({ title: $t('waiting.timeout'), icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
uni.reLaunch({ url: '/pages/index/index' })
}, 800)
}, 60000)
}