feat: 添加订单支付和成功页面,更新订单状态逻辑

新增了订单支付页面和支付成功页面,分别用于处理用户的支付流程和展示支付结果。同时,更新了订单状态的逻辑,确保在订单列表中正确显示各个状态。调整了设备租借逻辑,优化了扫码处理流程,提升用户体验。
This commit is contained in:
8vd8
2025-04-10 14:18:43 +08:00
parent aebd6c0459
commit 3fecd77739
53 changed files with 2049 additions and 702 deletions
+6 -10
View File
@@ -219,6 +219,9 @@
title: '处理中'
})
// 获取选中的套餐信息
const selectedPkg = this.packages[this.selectedPackage]
// 调用设备租借接口
const rentResult = await rentPowerBank(this.deviceId, this.phoneNumber)
if (rentResult.code !== 200) {
@@ -230,17 +233,10 @@
uni.hideLoading()
uni.showToast({
title: '租借成功',
icon: 'success'
// 跳转到订单支付页面,传递订单ID和套餐信息
uni.redirectTo({
url: `/pages/order/payment?orderId=${order.orderId}&packageTime=${selectedPkg.time}&packagePrice=${selectedPkg.price}`
})
// 跳转到订单页面
setTimeout(() => {
uni.redirectTo({
url: `/pages/order/index?orderId=${order.orderId}`
})
}, 1500)
} catch (error) {
uni.hideLoading()
uni.showToast({
-387
View File
@@ -1,387 +0,0 @@
<template>
<view class="return-container">
<!-- 订单信息卡片 -->
<view class="order-card">
<view class="order-header">
<text class="title">使用中</text>
<text class="order-no">订单号{{ orderInfo.orderId }}</text>
</view>
<view class="device-info">
<text class="device-name">共享风扇</text>
<text class="device-id">设备号{{ deviceNo }}</text>
</view>
<view class="time-info">
<view class="time-item">
<text class="label">开始时间</text>
<text class="value">{{ orderInfo.createTime }}</text>
</view>
<view class="time-item">
<text class="label">已使用时长</text>
<text class="value highlight">{{ orderInfo.usedTime }}</text>
</view>
<view class="time-item">
<text class="label">当前费用</text>
<text class="value">{{ orderInfo.currentFee }}</text>
</view>
</view>
</view>
<!-- 归还说明 -->
<view class="notice-card">
<view class="notice-title">归还说明</view>
<view class="notice-list">
<view class="notice-item">
<view class="dot"></view>
<text>请确保设备完好无损</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>请在指定区域内归还设备</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>归还后押金将自动退还</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<button class="unlock-btn" @click="handleUnlock" :disabled="unlocking">
{{ unlocking ? '归还中...' : '归还设备' }}
</button>
</view>
</view>
</template>
<script>
import { queryHasOrder, overOrderById } from '@/config/user.js'
export default {
data() {
return {
deviceNo: '',
orderInfo: {
orderId: '',
startTime: '',
usedTime: '',
currentFee: '0.00'
},
unlocking: false,
timer: null
}
},
onLoad(options) {
this.deviceNo = options.deviceNo
this.getActiveOrder()
},
onUnload() {
this.clearTimer()
},
methods: {
// 获取进行中的订单信息
async getActiveOrder() {
try {
uni.showLoading({ title: '加载中' })
const result = await queryHasOrder(this.deviceNo)
if (result.code === 200 && result.data && result.data.length > 0) {
const orderData = result.data[0]
this.orderInfo = {
orderId: orderData.orderId,
startTime: this.formatTime(orderData.createTime),
usedTime: this.calculateUsedTime(orderData.createTime),
currentFee: orderData.amount || '0.00'
}
this.startTimer()
} else {
uni.showToast({
title: '没有进行中的订单',
icon: 'none'
})
// 如果没有进行中的订单,返回首页
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1500)
}
} catch (error) {
console.error('获取订单信息失败:', error)
uni.showToast({
title: '获取订单信息失败',
icon: 'none'
})
} finally {
uni.hideLoading()
}
},
// 处理归还请求
async handleUnlock() {
if (this.unlocking) return
try {
this.unlocking = true
uni.showLoading({ title: '归还中' })
// 发送结束订单请求
const result = await overOrderById(this.orderInfo.orderId)
if (result.code === 200) {
uni.showToast({
title: '归还成功',
icon: 'success'
})
// 归还成功后,跳转到订单页面
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/index'
})
}, 1500)
} else {
throw new Error(result.msg || '归还失败')
}
} catch (error) {
uni.showToast({
title: error.message || '归还失败,请重试',
icon: 'none'
})
} finally {
this.unlocking = false
uni.hideLoading()
}
},
// 格式化时间
formatTime(date) {
// 检查是否是字符串格式的日期(如 "Mon Apr 07 16:36:35 CST 2025"
if (typeof date === 'string' && date.match(/\w{3}\s\w{3}\s\d{2}\s\d{2}:\d{2}:\d{2}\s\w{3}\s\d{4}/)) {
// 直接使用字符串创建Date对象,JS会自动解析这种标准格式
date = new Date(date);
} else if (!(date instanceof Date)) {
// 如果不是Date对象,尝试创建一个
date = new Date(date);
}
// 检查日期是否有效
if (isNaN(date.getTime())) {
console.error('无效的日期格式:', date);
return '无效日期';
}
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hour = date.getHours().toString().padStart(2, '0')
const minute = date.getMinutes().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
},
// 计算使用时长
calculateUsedTime(startTime) {
// 检查是否是字符串格式的日期(如 "Mon Apr 07 16:36:35 CST 2025"
let start;
if (typeof startTime === 'string') {
// 尝试直接创建Date对象,JS会自动解析标准格式
start = new Date(startTime);
// 检查日期是否有效
if (isNaN(start.getTime())) {
console.error('无效的日期格式:', startTime);
return '0小时0分钟';
}
} else if (startTime instanceof Date) {
start = startTime;
} else {
console.error('无效的日期类型:', startTime);
return '0小时0分钟';
}
const now = new Date()
const diffMs = now - start
const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60))
return `${diffHours}小时${diffMinutes}分钟`
},
// 更新使用时长
startTimer() {
this.timer = setInterval(() => {
// 更新使用时长
if (this.orderInfo.orderId) {
// 直接使用原始的createTime计算使用时长
this.orderInfo.usedTime = this.calculateUsedTime(this.orderInfo.startTime)
}
}, 60000) // 每分钟更新一次
},
clearTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
}
</script>
<style lang="scss" scoped>
.return-container {
min-height: 100vh;
background: #f8f8f8;
padding: 30rpx;
padding-bottom: 180rpx;
box-sizing: border-box;
.order-card {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.title {
font-size: 36rpx;
font-weight: 600;
color: #1976D2;
}
.order-no {
font-size: 24rpx;
color: #999;
}
}
.device-info {
margin-bottom: 30rpx;
.device-name {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-right: 20rpx;
}
.device-id {
font-size: 24rpx;
color: #666;
}
}
.time-info {
.time-item {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 28rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #333;
&.highlight {
color: #1976D2;
font-weight: 500;
}
}
}
}
}
.notice-card {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
.notice-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
.notice-list {
.notice-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.dot {
width: 12rpx;
height: 12rpx;
background: #1976D2;
border-radius: 50%;
margin-right: 16rpx;
}
text {
font-size: 28rpx;
color: #666;
line-height: 1.5;
}
}
}
}
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
display: flex;
justify-content: center;
box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04);
.unlock-btn {
width: 80%;
height: 88rpx;
background: linear-gradient(135deg, #1976D2, #42A5F5);
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
border: none;
&:active {
transform: scale(0.98);
}
&[disabled] {
background: #ccc;
transform: none;
}
}
}
}
</style>