1122 lines
31 KiB
Vue
1122 lines
31 KiB
Vue
<template>
|
|
<view class="order-detail-container">
|
|
<!-- 顶部标题 -->
|
|
<view class="page-header">
|
|
<view class="header-title">{{ getOrderStatusText() }}</view>
|
|
<view class="header-desc">{{ getStatusDesc() }}</view>
|
|
</view>
|
|
|
|
<!-- 订单信息卡片 -->
|
|
<view class="info-card">
|
|
<view class="info-row">
|
|
<view class="info-col">
|
|
<view class="info-value-wrapper">
|
|
<text class="info-value-large">{{ getUsedTimeDisplay().number }}</text>
|
|
<text class="info-value-unit">{{ getUsedTimeDisplay().unit }}</text>
|
|
</view>
|
|
<view class="info-label">{{ getUsedTimeLabel() }}</view>
|
|
</view>
|
|
<view class="info-col">
|
|
<view class="info-value-wrapper">
|
|
<text class="info-value-large">{{ getOrderFee() }}</text>
|
|
<text class="info-value-unit">{{ $t('unit.yuan') }}</text>
|
|
</view>
|
|
<view class="info-label">{{ $t('order.totalAmount') }}</view>
|
|
</view>
|
|
</view>
|
|
<view class="fee-rule">
|
|
{{ feeRuleText }}
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 租借信息卡片 -->
|
|
<view class="rent-card">
|
|
<view class="rent-title">{{ $t('order.rentInfo') }}</view>
|
|
<view class="rent-item">
|
|
<view class="rent-label">{{ $t('order.orderNo') }}</view>
|
|
<view class="rent-value">{{ orderInfo.orderNo || '-' }}</view>
|
|
</view>
|
|
<view class="rent-item">
|
|
<view class="rent-label">{{ $t('order.fanNo') }}</view>
|
|
<view class="rent-value">{{ deviceId || '-' }}</view>
|
|
</view>
|
|
<view class="rent-item">
|
|
<view class="rent-label">{{ $t('order.rentTime') }}</view>
|
|
<view class="rent-value">{{ orderInfo.startTime || '-' }}</view>
|
|
</view>
|
|
<view class="rent-item">
|
|
<view class="rent-label">{{ $t('order.rentLocation') }}</view>
|
|
<view class="rent-value">{{ orderInfo.positionName || '-' }}</view>
|
|
</view>
|
|
<view class="rent-item">
|
|
<view class="rent-label">{{ $t('order.rentMethod') }}</view>
|
|
<view class="rent-value">{{ getPayWayText() }}</view>
|
|
</view>
|
|
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.endTime">
|
|
<view class="rent-label">{{ $t('order.returnTime') }}</view>
|
|
<view class="rent-value">{{ orderInfo.endTime }}</view>
|
|
</view>
|
|
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.returnPosition">
|
|
<view class="rent-label">{{ $t('order.returnLocation') }}</view>
|
|
<view class="rent-value">{{ orderInfo.returnPosition || '-' }}</view>
|
|
</view>
|
|
<view class="rent-paid" v-if="isOrderCompleted()">
|
|
<text class="paid-label">{{ $t('order.paid') }}</text>
|
|
<text class="paid-value">{{ orderInfo.currentFee || orderInfo.payAmount || '10' }}</text>
|
|
<text class="paid-unit">{{ $t('unit.yuan') }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 底部操作栏 -->
|
|
<view class="bottom-bar">
|
|
<!-- 使用中状态 -->
|
|
<template v-if="orderInfo.orderStatus === 'in_used'">
|
|
<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 v-if="!showExpressAction" class="countdown-btn">
|
|
{{ formatCountdown(countdownRemaining) }}{{ $t('order.canExpressReturn') }}
|
|
</view>
|
|
<view v-if="showExpressAction" class="action-btn secondary" @click="expressRetrunOrder">
|
|
{{ $t('order.pauseBilling') }}
|
|
</view>
|
|
<view v-if="showExpressAction" class="action-btn primary" @click="quickReturn">
|
|
{{ $t('order.quickReturn') }}
|
|
</view>
|
|
</template>
|
|
|
|
<!-- 已完成状态 -->
|
|
<template v-if="isOrderCompleted()">
|
|
<view class="bottom-icon-btn" @click="handleWithdraw" v-if="!orderInfo.isWithdrawn && orderInfo.refundAmount > 0">
|
|
<image src="/static/suggess.png" class="icon" mode="aspectFit"></image>
|
|
<text>{{ $t('order.feeAppeal') }}</text>
|
|
</view>
|
|
<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="action-btn primary" @click="rentAgain">
|
|
{{ $t('order.rentAgain') }}
|
|
</view>
|
|
</template>
|
|
|
|
<!-- 待支付状态 -->
|
|
<template v-if="orderInfo.orderStatus === 'waiting_for_payment'">
|
|
<view class="action-btn secondary" @click="handleCancelOrder">{{ $t('order.cancelOrder') }}</view>
|
|
<view class="action-btn primary" @click="handlePayment">{{ $t('order.payNow') }}</view>
|
|
</template>
|
|
|
|
<!-- 已取消状态 -->
|
|
<template v-if="orderInfo.orderStatus === 'order_cancelled'">
|
|
<view class="action-btn primary full-width" @click="goToHome">{{ $t('order.backToHome') }}</view>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
queryById,
|
|
cancelOrder
|
|
} from '@/config/api/order.js'
|
|
import {
|
|
getSystemConfig
|
|
} from '@/config/api/system.js'
|
|
import {
|
|
URL
|
|
} from "@/config/url.js"
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
deviceId: '',
|
|
orderInfo: {
|
|
orderId: '',
|
|
orderNo: '',
|
|
startTime: '',
|
|
endTime: '',
|
|
usedTime: '0分钟',
|
|
currentFee: '0.00',
|
|
orderStatus: '',
|
|
payWay: '',
|
|
depositAmount: '',
|
|
packageTime: '',
|
|
packagePrice: '',
|
|
payAmount: '0.00',
|
|
phone: '',
|
|
refundAmount: '0.00',
|
|
withdrawStatus: 'waiting',
|
|
isWithdrawn: false,
|
|
positionName: '',
|
|
returnPosition: '',
|
|
expressReturnStart: null
|
|
},
|
|
timer: null,
|
|
statusCheckTimer: null,
|
|
maxStatusChecks: 30,
|
|
currentStatusChecks: 0,
|
|
statusCheckInterval: 5000,
|
|
isPageActive: false,
|
|
expressThresholdSeconds: 180,
|
|
countdownRemaining: 0,
|
|
showExpressAction: false,
|
|
countdownTimer: null,
|
|
feeRuleText: '5.0元/60分钟 前15分钟内归还免费 不足60分钟按60分钟计费 封顶99元 持续计费至99元视为买断'
|
|
}
|
|
},
|
|
onLoad(options) {
|
|
console.log('订单详情页加载,参数:', JSON.stringify(options))
|
|
|
|
// 设置页面标题
|
|
uni.setNavigationBarTitle({
|
|
title: this.$t('order.orderDetail')
|
|
})
|
|
|
|
this.isPageActive = true
|
|
|
|
// 从缓存读取通知内容(计费规则)
|
|
try {
|
|
const cachedNotice = uni.getStorageSync('noticeContent')
|
|
if (cachedNotice) {
|
|
console.log('从缓存读取计费规则:', cachedNotice)
|
|
this.feeRuleText = cachedNotice
|
|
}
|
|
} catch (e) {
|
|
console.warn('读取缓存通知内容失败:', e)
|
|
}
|
|
|
|
this.orderInfo.orderId = options.orderId || ''
|
|
this.deviceId = options.deviceNo || options.deviceId || ''
|
|
|
|
if (!this.orderInfo.orderId && this.deviceId) {
|
|
this.getOrderByDevice()
|
|
} else if (this.orderInfo.orderId) {
|
|
this.getOrderDetails()
|
|
} else {
|
|
uni.showToast({
|
|
title: '缺少订单信息',
|
|
icon: 'none'
|
|
})
|
|
setTimeout(() => {
|
|
this.goToHome()
|
|
}, 1500)
|
|
}
|
|
|
|
// 注册全局订单完成事件监听器
|
|
uni.$on('orderCompleted', this.handleOrderCompleted)
|
|
|
|
// 获取系统配置
|
|
// this.loadSystemConfig().then(() => {
|
|
// if (this.orderInfo.orderStatus === 'in_used') {
|
|
// this.startExpressCountdown()
|
|
// }
|
|
// })
|
|
},
|
|
onShow() {
|
|
this.isPageActive = true
|
|
if (this.orderInfo.orderStatus === 'in_used') {
|
|
this.startExpressCountdown()
|
|
}
|
|
},
|
|
onHide() {
|
|
console.log('订单详情页隐藏,清理资源')
|
|
this.isPageActive = false
|
|
this.clearTimer()
|
|
this.clearStatusCheckTimer()
|
|
this.clearExpressCountdown()
|
|
this.removeFromOrderMonitor()
|
|
},
|
|
onUnload() {
|
|
console.log('订单详情页卸载,清理所有资源')
|
|
this.isPageActive = false
|
|
this.clearTimer()
|
|
this.clearStatusCheckTimer()
|
|
this.clearExpressCountdown()
|
|
this.removeFromOrderMonitor()
|
|
uni.$off('orderCompleted', this.handleOrderCompleted)
|
|
},
|
|
methods: {
|
|
// 判断订单是否已完成
|
|
isOrderCompleted() {
|
|
return this.orderInfo.orderStatus === 'used_done' ||
|
|
this.orderInfo.orderStatus === 'used_down'
|
|
},
|
|
|
|
// 获取订单状态文字
|
|
getOrderStatusText() {
|
|
const statusMap = {
|
|
'waiting_for_payment': this.$t('order.waitingForPayment'),
|
|
'payment_in_progress': this.$t('order.paymentInProgress'),
|
|
'payment_successful': this.$t('order.paymentSuccess'),
|
|
'in_used': this.$t('order.inUse'),
|
|
'payment_failed': this.$t('order.paymentFailed'),
|
|
'order_cancelled': this.$t('order.cancelled'),
|
|
'used_done': this.$t('order.finished'),
|
|
'used_down': this.$t('order.finished')
|
|
}
|
|
return statusMap[this.orderInfo.orderStatus] || this.$t('order.orderDetail')
|
|
},
|
|
|
|
// 获取状态描述
|
|
getStatusDesc() {
|
|
const descMap = {
|
|
'waiting_for_payment': this.$t('order.pleasePaySoon'),
|
|
'in_used': this.$t('order.pleaseReturnInTime'),
|
|
'used_done': this.$t('order.returnedThankYou'),
|
|
'used_down': this.$t('order.returnedThankYou'),
|
|
'order_cancelled': this.$t('order.orderCancelled'),
|
|
'payment_failed': this.$t('order.paymentFailedRetry')
|
|
}
|
|
return descMap[this.orderInfo.orderStatus] || ''
|
|
},
|
|
|
|
// 获取状态图标样式
|
|
getStatusIconClass() {
|
|
const classMap = {
|
|
'waiting_for_payment': 'waiting',
|
|
'in_used': 'using',
|
|
'used_done': 'success',
|
|
'used_down': 'success',
|
|
'order_cancelled': 'cancelled',
|
|
'payment_failed': 'failed'
|
|
}
|
|
return classMap[this.orderInfo.orderStatus] || 'default'
|
|
},
|
|
|
|
// 获取退款状态文本
|
|
getWithdrawStatusText() {
|
|
const statusMap = {
|
|
'waiting': '待申请',
|
|
'processing': '处理中',
|
|
'success': '已退款',
|
|
'failed': '退款失败'
|
|
}
|
|
return statusMap[this.orderInfo.withdrawStatus] || '待申请'
|
|
},
|
|
|
|
// 获取支付方式文本
|
|
getPayWayText() {
|
|
const payWayMap = {
|
|
'wx_score_pay': this.$t('order.depositFree'),
|
|
'wx_member_pay': this.$t('order.memberOrder'),
|
|
'wx_pay': this.$t('order.depositPay')
|
|
}
|
|
return payWayMap[this.orderInfo.payWay] || this.$t('order.depositFree')
|
|
},
|
|
|
|
// 格式化倒计时(显示为 HH:MM:SS 格式)
|
|
formatCountdown(totalSeconds) {
|
|
if (typeof totalSeconds !== 'number' || totalSeconds < 0) return '00:00:00'
|
|
const hours = Math.floor(totalSeconds / 3600)
|
|
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
const seconds = totalSeconds % 60
|
|
const pad = (n) => n.toString().padStart(2, '0')
|
|
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
|
|
},
|
|
|
|
// 联系客服
|
|
contactService() {
|
|
uni.navigateTo({
|
|
url: '/pages/help/index'
|
|
})
|
|
},
|
|
|
|
// 快速归还
|
|
quickReturn() {
|
|
// 刷新订单状态,检查是否已归还
|
|
this.checkReturnStatus()
|
|
},
|
|
|
|
// 再次租借
|
|
rentAgain() {
|
|
uni.reLaunch({
|
|
url: '/pages/index/index'
|
|
})
|
|
},
|
|
|
|
// 获取使用时长显示信息
|
|
getUsedTimeDisplay() {
|
|
let usedTime = this.orderInfo.usedTime
|
|
|
|
// 如果 usedTime 为空,通过开始时间和结束时间计算
|
|
if (usedTime === '0分钟' && this.orderInfo.startTime && this.orderInfo.endTime) {
|
|
const startMs = this.parseStartTimeToMs(this.orderInfo.startTime)
|
|
const endMs = this.parseStartTimeToMs(this.orderInfo.endTime)
|
|
|
|
if (!isNaN(startMs) && !isNaN(endMs)) {
|
|
const diffMs = endMs - startMs
|
|
const totalMinutes = Math.floor(diffMs / (1000 * 60))
|
|
|
|
// 格式化为 "X小时X分钟" 或 "X分钟"
|
|
if (totalMinutes >= 60) {
|
|
const hours = Math.floor(totalMinutes / 60)
|
|
const minutes = totalMinutes % 60
|
|
usedTime = minutes > 0 ? `${hours}小时${minutes}分钟` : `${hours}小时`
|
|
} else {
|
|
usedTime = `${totalMinutes}分钟`
|
|
}
|
|
}
|
|
}
|
|
|
|
// 如果还是没有值,使用默认值
|
|
if (!usedTime) {
|
|
usedTime = '0分钟'
|
|
}
|
|
|
|
// 解析时长字符串,例如 "1小时5分钟" 或 "5分钟"
|
|
const hourMatch = usedTime.match(/(\d+)小时/)
|
|
const minuteMatch = usedTime.match(/(\d+)分钟/)
|
|
|
|
let displayNumber = ''
|
|
let displayUnit = ''
|
|
|
|
if (hourMatch && minuteMatch) {
|
|
// 有小时也有分钟,如 "1小时5分钟"
|
|
displayNumber = `${hourMatch[1]}`
|
|
displayUnit = `小时${minuteMatch[1]}分钟`
|
|
} else if (hourMatch) {
|
|
// 只有小时,如 "1小时"
|
|
displayNumber = hourMatch[1]
|
|
displayUnit = '小时'
|
|
} else if (minuteMatch) {
|
|
// 只有分钟,如 "5分钟"
|
|
displayNumber = minuteMatch[1]
|
|
displayUnit = '分钟'
|
|
} else {
|
|
// 默认情况
|
|
displayNumber = '0'
|
|
displayUnit = '分钟'
|
|
}
|
|
|
|
return {
|
|
number: displayNumber,
|
|
unit: displayUnit
|
|
}
|
|
},
|
|
|
|
// 获取使用时长标签文本
|
|
getUsedTimeLabel() {
|
|
// 使用中状态显示"已使用",已完成状态显示"使用时长"
|
|
return this.orderInfo.orderStatus === 'in_used' ? this.$t('order.used') : this.$t('order.duration')
|
|
},
|
|
|
|
// 获取订单费用(不含单位)
|
|
getOrderFee() {
|
|
const fee = this.orderInfo.currentFee || this.orderInfo.payAmount || '0'
|
|
// 移除可能的"元"字符
|
|
return String(fee).replace(/[元¥]/g, '')
|
|
},
|
|
|
|
// 加载系统配置
|
|
async loadSystemConfig() {
|
|
try {
|
|
// 优先使用订单数据中的 expressReturnStart(小时转秒)
|
|
if (this.orderInfo.expressReturnStart && typeof this.orderInfo.expressReturnStart === 'number' && this.orderInfo.expressReturnStart > 0) {
|
|
this.expressThresholdSeconds = this.orderInfo.expressReturnStart * 3600
|
|
console.log('使用订单配置的快递归还阈值:', this.orderInfo.expressReturnStart, '小时 =>', this.expressThresholdSeconds, '秒')
|
|
} else {
|
|
// 如果订单数据中没有,则使用系统配置
|
|
const res = await getSystemConfig()
|
|
if (res && res.code === 200 && res.data && typeof res.data.expressReturnCountdownSeconds === 'number') {
|
|
const seconds = res.data.expressReturnCountdownSeconds
|
|
if (seconds > 0) {
|
|
this.expressThresholdSeconds = seconds
|
|
console.log('使用系统配置的快递归还阈值:', seconds, '秒')
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.orderInfo.orderStatus === 'in_used' && this.orderInfo.startTime) {
|
|
this.recomputeExpressCountdownFromStartTime()
|
|
}
|
|
} catch (e) {
|
|
console.log('加载系统配置失败:', e)
|
|
}
|
|
},
|
|
|
|
// 启动快递归还倒计时
|
|
startExpressCountdown() {
|
|
this.clearExpressCountdown()
|
|
this.recomputeExpressCountdownFromStartTime()
|
|
if (this.showExpressAction) return
|
|
this.countdownTimer = setInterval(() => {
|
|
if (!this.isPageActive) {
|
|
this.clearExpressCountdown()
|
|
return
|
|
}
|
|
if (this.orderInfo.orderStatus !== 'in_used') {
|
|
this.clearExpressCountdown()
|
|
return
|
|
}
|
|
this.recomputeExpressCountdownFromStartTime()
|
|
if (this.showExpressAction) {
|
|
this.clearExpressCountdown()
|
|
}
|
|
}, 1000)
|
|
},
|
|
|
|
// 清除倒计时
|
|
clearExpressCountdown() {
|
|
if (this.countdownTimer) {
|
|
clearInterval(this.countdownTimer)
|
|
this.countdownTimer = null
|
|
}
|
|
},
|
|
|
|
// 解析开始时间
|
|
parseStartTimeToMs(timeStr) {
|
|
if (!timeStr) return NaN
|
|
if (typeof timeStr === 'number') {
|
|
return timeStr < 1e12 ? timeStr * 1000 : timeStr
|
|
}
|
|
let normalized = String(timeStr).trim()
|
|
normalized = normalized.replace('T', ' ').replace(/\.\d+Z?$/, '')
|
|
const candidates = [
|
|
normalized,
|
|
normalized.replace(/-/g, '/')
|
|
]
|
|
for (let i = 0; i < candidates.length; i++) {
|
|
const ts = Date.parse(candidates[i])
|
|
if (!isNaN(ts)) return ts
|
|
}
|
|
const m = normalized.match(/(\d{4})[\/-](\d{1,2})[\/-](\d{1,2})\s+(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?/)
|
|
if (m) {
|
|
const y = parseInt(m[1])
|
|
const mon = parseInt(m[2]) - 1
|
|
const d = parseInt(m[3])
|
|
const h = parseInt(m[4])
|
|
const min = parseInt(m[5])
|
|
const s = m[6] ? parseInt(m[6]) : 0
|
|
return new Date(y, mon, d, h, min, s).getTime()
|
|
}
|
|
const num = Number(normalized)
|
|
if (!isNaN(num)) {
|
|
return num < 1e12 ? num * 1000 : num
|
|
}
|
|
return NaN
|
|
},
|
|
|
|
// 重新计算倒计时
|
|
recomputeExpressCountdownFromStartTime() {
|
|
if (this.orderInfo.orderStatus !== 'in_used') {
|
|
this.showExpressAction = false
|
|
this.countdownRemaining = 0
|
|
return
|
|
}
|
|
const startMs = this.parseStartTimeToMs(this.orderInfo.startTime)
|
|
if (isNaN(startMs)) {
|
|
this.showExpressAction = false
|
|
this.countdownRemaining = 0
|
|
return
|
|
}
|
|
const nowMs = Date.now()
|
|
const elapsedSeconds = Math.max(0, Math.floor((nowMs - startMs) / 1000))
|
|
const remaining = this.expressThresholdSeconds - elapsedSeconds
|
|
if (remaining <= 0) {
|
|
this.countdownRemaining = 0
|
|
this.showExpressAction = true
|
|
} else {
|
|
this.countdownRemaining = remaining
|
|
this.showExpressAction = false
|
|
}
|
|
},
|
|
|
|
// 从订单监控服务中移除当前订单
|
|
removeFromOrderMonitor() {
|
|
if (this.orderInfo.orderId && this.$orderMonitor) {
|
|
try {
|
|
this.$orderMonitor.removeOrder({
|
|
orderId: this.orderInfo.orderId
|
|
})
|
|
console.log('订单已从监控队列移除:', this.orderInfo.orderId)
|
|
} catch (error) {
|
|
console.error('从监控队列移除订单失败:', error)
|
|
}
|
|
}
|
|
},
|
|
|
|
// 处理订单完成事件
|
|
handleOrderCompleted(orderData) {
|
|
console.log('收到订单完成事件:', orderData)
|
|
if (orderData.orderId === this.orderInfo.orderId || orderData.orderNo === this.orderInfo.orderNo) {
|
|
// 刷新订单详情
|
|
this.getOrderDetails()
|
|
}
|
|
},
|
|
|
|
// 获取订单详情
|
|
async getOrderDetails() {
|
|
if (!this.isPageActive) {
|
|
console.log('页面已不活跃,跳过订单详情请求')
|
|
return
|
|
}
|
|
|
|
try {
|
|
if (!this.orderInfo.orderId) {
|
|
throw new Error(this.$t('order.orderIdRequired'))
|
|
}
|
|
|
|
const result = await queryById(this.orderInfo.orderId)
|
|
|
|
if (result.code === 200 && result.data) {
|
|
const orderData = result.data
|
|
console.log('订单数据:', orderData)
|
|
|
|
// 更新订单信息
|
|
this.updateOrderInfo(orderData)
|
|
|
|
// 只有使用中的订单才启动定时器和监控
|
|
if (this.orderInfo.orderStatus === 'in_used') {
|
|
this.startTimer()
|
|
this.startStatusCheckTimer()
|
|
this.startExpressCountdown()
|
|
|
|
uni.setStorageSync('activeOrderId', this.orderInfo.orderId)
|
|
|
|
// 添加到监控队列
|
|
try {
|
|
if (this.$orderMonitor) {
|
|
this.$orderMonitor.removeOrder({
|
|
orderId: this.orderInfo.orderId
|
|
})
|
|
this.$orderMonitor.addOrder({
|
|
orderId: this.orderInfo.orderId
|
|
}, 'detail')
|
|
console.log('订单已添加到监控队列:', this.orderInfo.orderId)
|
|
}
|
|
} catch (error) {
|
|
console.error('添加订单到监控队列失败:', error)
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(result.msg || '获取订单详情失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('获取订单详情错误:', error)
|
|
uni.showToast({
|
|
title: error.message || '获取订单信息失败',
|
|
icon: 'none'
|
|
})
|
|
setTimeout(() => {
|
|
this.goToHome()
|
|
}, 1500)
|
|
} finally {
|
|
uni.hideLoading()
|
|
}
|
|
},
|
|
|
|
// 更新订单信息
|
|
updateOrderInfo(orderData) {
|
|
this.orderInfo.usedTime = orderData.usedTime || '0分钟'
|
|
this.orderInfo.currentFee = orderData.currentFee || orderData.actualDeviceAmount || orderData.payAmount || '0.00'
|
|
this.orderInfo.orderStatus = orderData.orderStatus || ''
|
|
this.orderInfo.payWay = orderData.payWay || ''
|
|
this.orderInfo.startTime = orderData.startTime || orderData.createTime || ''
|
|
this.orderInfo.endTime = orderData.endTime || ''
|
|
this.orderInfo.depositAmount = orderData.depositAmount || ''
|
|
this.orderInfo.packageTime = orderData.packageTime || ''
|
|
this.orderInfo.packagePrice = orderData.packagePrice || ''
|
|
this.orderInfo.payAmount = orderData.payAmount || '0.00'
|
|
this.orderInfo.phone = orderData.phone || ''
|
|
this.orderInfo.orderNo = orderData.orderNo || ''
|
|
this.orderInfo.refundAmount = orderData.residueAmount || '0.00'
|
|
this.orderInfo.withdrawStatus = orderData.withdrawStatus || 'waiting'
|
|
this.orderInfo.isWithdrawn = orderData.withdrawStatus === 'success'
|
|
this.orderInfo.positionName = orderData.positionName || orderData.positionLocation || ''
|
|
this.orderInfo.returnPosition = orderData.returnPosition || orderData.positionName || orderData.positionLocation || ''
|
|
|
|
// 保存快递归还开始时间(小时为单位)
|
|
this.orderInfo.expressReturnStart = orderData.expressReturnStart || null
|
|
|
|
// 如果有有效的 expressReturnStart,立即更新倒计时阈值(小时转秒)
|
|
if (this.orderInfo.expressReturnStart && typeof this.orderInfo.expressReturnStart === 'number' && this.orderInfo.expressReturnStart > 0) {
|
|
this.expressThresholdSeconds = this.orderInfo.expressReturnStart * 3600
|
|
console.log('从订单数据更新快递归还阈值:', this.orderInfo.expressReturnStart, '小时 =>', this.expressThresholdSeconds, '秒')
|
|
}
|
|
|
|
if (orderData.deviceNo && !this.deviceId) {
|
|
this.deviceId = orderData.deviceNo
|
|
}
|
|
},
|
|
|
|
// 更新使用时长的定时器
|
|
startTimer() {
|
|
this.clearTimer()
|
|
this.timer = setInterval(() => {
|
|
if (this.isPageActive) {
|
|
console.log('执行定时更新订单信息')
|
|
this.getOrderDetails()
|
|
} else {
|
|
console.log('页面已不活跃,停止计时器')
|
|
this.clearTimer()
|
|
}
|
|
}, 60000)
|
|
console.log('已启动使用时长更新计时器')
|
|
},
|
|
|
|
// 清除定时器
|
|
clearTimer() {
|
|
if (this.timer) {
|
|
clearInterval(this.timer)
|
|
this.timer = null
|
|
console.log('已清除使用时长更新计时器')
|
|
}
|
|
},
|
|
|
|
// 清除状态检查定时器
|
|
clearStatusCheckTimer() {
|
|
if (this.statusCheckTimer) {
|
|
clearInterval(this.statusCheckTimer)
|
|
this.statusCheckTimer = null
|
|
console.log('已清除归还状态检查计时器')
|
|
}
|
|
},
|
|
|
|
// 开始状态检查定时器
|
|
startStatusCheckTimer() {
|
|
this.currentStatusChecks = 0
|
|
this.clearStatusCheckTimer()
|
|
|
|
this.statusCheckTimer = setInterval(() => {
|
|
if (this.isPageActive) {
|
|
this.currentStatusChecks++
|
|
console.log(`执行归还状态检查 (${this.currentStatusChecks}/${this.maxStatusChecks})`)
|
|
this.checkReturnStatus()
|
|
|
|
if (this.currentStatusChecks >= this.maxStatusChecks) {
|
|
this.clearStatusCheckTimer()
|
|
uni.showToast({
|
|
title: '请手动刷新查看归还状态',
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
}
|
|
} else {
|
|
console.log('页面已不活跃,停止状态检查计时器')
|
|
this.clearStatusCheckTimer()
|
|
}
|
|
}, this.statusCheckInterval)
|
|
|
|
console.log('已启动归还状态检查计时器')
|
|
},
|
|
|
|
// 通过设备号查询使用中的订单
|
|
async getOrderByDevice() {
|
|
try {
|
|
if (!this.deviceId) {
|
|
throw new Error('设备号不能为空')
|
|
}
|
|
|
|
const inUseRes = await uni.request({
|
|
url: `${URL || 'http://127.0.0.1:8080'}/app/order/inUse`,
|
|
method: 'GET',
|
|
header: {
|
|
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
|
'Clientid': uni.getStorageSync('client_id')
|
|
}
|
|
})
|
|
|
|
console.log('通过设备号查询订单结果:', JSON.stringify(inUseRes))
|
|
|
|
if (inUseRes.statusCode === 200 && inUseRes.data.code === 200 && inUseRes.data.data) {
|
|
const inUseOrder = inUseRes.data.data
|
|
console.log('使用中的订单:', inUseOrder)
|
|
|
|
this.orderInfo.orderId = inUseOrder.orderId
|
|
this.orderInfo.orderStatus = inUseOrder.orderStatus || ''
|
|
this.orderInfo.payWay = inUseOrder.payWay || ''
|
|
this.orderInfo.startTime = inUseOrder.startTime || ''
|
|
|
|
this.getOrderDetails()
|
|
} else {
|
|
throw new Error('未找到使用中的订单')
|
|
}
|
|
} catch (error) {
|
|
console.error('通过设备号查询订单失败:', error)
|
|
uni.showToast({
|
|
title: error.message || '获取订单信息失败',
|
|
icon: 'none'
|
|
})
|
|
setTimeout(() => {
|
|
this.goToHome()
|
|
}, 1500)
|
|
} finally {
|
|
uni.hideLoading()
|
|
}
|
|
},
|
|
|
|
// 检查归还状态
|
|
async checkReturnStatus() {
|
|
try {
|
|
if (this.isPageActive) {
|
|
await this.getOrderDetails()
|
|
}
|
|
} catch (error) {
|
|
console.error('检查归还状态失败:', error)
|
|
}
|
|
},
|
|
|
|
// 快递归还
|
|
expressRetrunOrder() {
|
|
uni.navigateTo({
|
|
url: `/pages/expressReturn/addExpressReturn?orderId=${this.orderInfo.orderId}`
|
|
})
|
|
},
|
|
|
|
// 取消订单
|
|
handleCancelOrder() {
|
|
uni.showModal({
|
|
title: this.$t('order.confirmCancel'),
|
|
content: this.$t('order.confirmCancelContent'),
|
|
success: async (res) => {
|
|
if (res.confirm) {
|
|
try {
|
|
uni.showLoading({
|
|
title: this.$t('common.processing')
|
|
})
|
|
const result = await cancelOrder({
|
|
orderId: this.orderInfo.orderId
|
|
})
|
|
if (result.code === 200) {
|
|
uni.hideLoading()
|
|
uni.showToast({
|
|
title: this.$t('order.cancelSuccess'),
|
|
icon: 'success'
|
|
})
|
|
await this.getOrderDetails()
|
|
} else {
|
|
throw new Error(result.msg || this.$t('order.cancelFailed'))
|
|
}
|
|
} catch (error) {
|
|
uni.hideLoading()
|
|
uni.showToast({
|
|
title: error.message || this.$t('order.cancelFailed'),
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
// 立即支付
|
|
handlePayment() {
|
|
uni.navigateTo({
|
|
url: `/pages/order/payment?orderId=${this.orderInfo.orderId}`
|
|
})
|
|
},
|
|
|
|
// 申请退款
|
|
async handleWithdraw() {
|
|
try {
|
|
uni.showLoading({
|
|
title: '处理中'
|
|
})
|
|
|
|
const res = await uni.request({
|
|
url: `${URL || 'http://127.0.0.1:8080'}/app/withdraw/add/${this.orderInfo.orderNo}`,
|
|
method: 'GET',
|
|
header: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
|
'Clientid': uni.getStorageSync('client_id')
|
|
}
|
|
})
|
|
|
|
if (res.statusCode === 200 && res.data.code === 200) {
|
|
uni.showToast({
|
|
title: this.$t('order.refundSuccess'),
|
|
icon: 'success'
|
|
})
|
|
|
|
this.orderInfo.withdrawStatus = 'processing'
|
|
this.orderInfo.isWithdrawn = true
|
|
|
|
setTimeout(() => {
|
|
this.getOrderDetails()
|
|
}, 1500)
|
|
} else {
|
|
throw new Error(res.data.msg || '退款申请失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('退款申请错误:', error)
|
|
uni.showToast({
|
|
title: error.message || this.$t('order.refundFailed'),
|
|
icon: 'none'
|
|
})
|
|
} finally {
|
|
uni.hideLoading()
|
|
}
|
|
},
|
|
|
|
// 返回首页
|
|
goToHome() {
|
|
uni.reLaunch({
|
|
url: '/pages/index/index'
|
|
})
|
|
},
|
|
|
|
// 格式化时间
|
|
formatTime(minutes) {
|
|
if (!minutes) return ''
|
|
const mins = parseInt(minutes)
|
|
if (mins < 60) {
|
|
return `${mins}分钟`
|
|
} else {
|
|
const hours = Math.floor(mins / 60)
|
|
const remainingMins = mins % 60
|
|
return remainingMins > 0 ? `${hours}小时${remainingMins}分钟` : `${hours}小时`
|
|
}
|
|
},
|
|
|
|
// 格式化倒计时
|
|
formatHms(totalSeconds) {
|
|
if (typeof totalSeconds !== 'number' || totalSeconds < 0) return '00:00:00'
|
|
const hours = Math.floor(totalSeconds / 3600)
|
|
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
const seconds = totalSeconds % 60
|
|
const pad = (n) => n.toString().padStart(2, '0')
|
|
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.order-detail-container {
|
|
min-height: 100vh;
|
|
background: #f7f8fa;
|
|
padding: 30rpx;
|
|
padding-bottom: 180rpx;
|
|
box-sizing: border-box;
|
|
|
|
// 顶部标题
|
|
.page-header {
|
|
margin-bottom: 30rpx;
|
|
|
|
.header-title {
|
|
font-size: 48rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.header-desc {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
// 订单信息卡片
|
|
.info-card {
|
|
background: #fff;
|
|
border-radius: 20rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
|
|
|
.info-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 20rpx;
|
|
|
|
.info-col {
|
|
flex: 1;
|
|
text-align: center;
|
|
|
|
.info-value-wrapper {
|
|
display: flex;
|
|
align-items: baseline;
|
|
justify-content: center;
|
|
margin-bottom: 8rpx;
|
|
|
|
.info-value-large {
|
|
font-size: 48rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
line-height: 1;
|
|
}
|
|
|
|
.info-value-unit {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
margin-left: 4rpx;
|
|
}
|
|
}
|
|
|
|
.info-label {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
|
|
.fee-rule {
|
|
background: #E8F5E9;
|
|
border-radius: 8rpx;
|
|
padding: 16rpx;
|
|
font-size: 24rpx;
|
|
color: #4CAF50;
|
|
line-height: 1.6;
|
|
}
|
|
}
|
|
|
|
// 租借信息卡片
|
|
.rent-card {
|
|
background: #fff;
|
|
border-radius: 20rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
|
|
|
.rent-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.rent-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
&:last-of-type {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.rent-label {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.rent-value {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
text-align: right;
|
|
max-width: 400rpx;
|
|
}
|
|
}
|
|
|
|
.rent-paid {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: baseline;
|
|
padding-top: 20rpx;
|
|
margin-top: 10rpx;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
|
|
.paid-label {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
margin-right: 8rpx;
|
|
}
|
|
|
|
.paid-value {
|
|
font-size: 48rpx;
|
|
font-weight: bold;
|
|
color: #ff6b6b;
|
|
}
|
|
|
|
.paid-unit {
|
|
font-size: 28rpx;
|
|
color: #ff6b6b;
|
|
margin-left: 4rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 底部操作栏
|
|
.bottom-bar {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
padding: 20rpx 30rpx;
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
background: #fff;
|
|
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
z-index: 10;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
|
|
.bottom-icon-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 100rpx;
|
|
|
|
.icon {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
text {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
&:active {
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
.countdown-btn {
|
|
flex: 1;
|
|
height: 88rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 28rpx;
|
|
color: #07c160;
|
|
background: #E8F5E9;
|
|
border-radius: 44rpx;
|
|
border: 2rpx solid #07c160;
|
|
}
|
|
|
|
.action-btn {
|
|
height: 88rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 30rpx;
|
|
border-radius: 44rpx;
|
|
padding: 0 40rpx;
|
|
white-space: nowrap;
|
|
|
|
&.full-width {
|
|
flex: 1;
|
|
}
|
|
|
|
&.primary {
|
|
background: #07c160;
|
|
color: #fff;
|
|
flex: 1;
|
|
|
|
&:active {
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
|
|
&.secondary {
|
|
background: #f5f5f5;
|
|
color: #333;
|
|
border: 1rpx solid #e0e0e0;
|
|
|
|
&:active {
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|