Files
uni-fans-score/pages/order/payment.vue
T

583 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="payment-container">
<!-- 订单状态 -->
<view class="status-card">
<view class="status-icon" :class="orderStatus.class"></view>
<view class="status-text">{{ orderStatus.text }}</view>
<view class="status-desc">{{ orderStatus.desc }}</view>
</view>
<!-- 订单信息 -->
<view class="order-card">
<view class="card-title">订单信息</view>
<view class="info-item">
<text class="label">订单号</text>
<text class="value">{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">设备号</text>
<text class="value">{{ orderInfo.deviceNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">创建时间</text>
<text class="value">{{ orderInfo.createTime || '-' }}</text>
</view>
<view class="info-item">
<text class="label">联系电话</text>
<text class="value">{{ orderInfo.phone || '-' }}</text>
</view>
</view>
<!-- 费用信息 -->
<view class="price-card">
<view class="card-title">费用信息</view>
<view class="price-item">
<text class="label">押金</text>
<text class="value">{{ orderInfo.deposit || '99.00' }}</text>
</view>
<view class="price-item">
<text class="label">套餐</text>
<text class="value">{{ packageInfo.price }}/{{ packageInfo.time }}小时</text>
</view>
<view class="price-item total">
<text class="label">合计</text>
<text class="value">{{ totalAmount }}</text>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<view class="total-amount">
<text>合计</text>
<text class="amount">{{ totalAmount }}</text>
</view>
<view class="pay-btn" @click="handlePayment">立即支付</view>
</view>
</view>
</template>
<script>
import { queryById } from '@/config/user.js'
import { getDeviceInfo } from '@/config/user.js'
import { updateOrderPackage } from '@/config/user.js'
import { updateUserBalance } from '@/config/user.js'
import {
URL
}from"@/config/url.js"
export default {
data() {
return {
orderId: null,
deviceNo: null,
orderInfo: {},
packageInfo: {
time: '',
price: '0.00'
},
deviceInfo: null,
passedTotalAmount: null,
passedDepositAmount: null,
orderStatus: {
text: '等待支付',
desc: '请在15分钟内完成支付',
class: 'waiting'
}
}
},
computed: {
totalAmount() {
if (this.passedTotalAmount !== null) {
return parseFloat(this.passedTotalAmount).toFixed(2);
}
const deposit = parseFloat(this.orderInfo.deposit || this.passedDepositAmount || 99)
const packagePrice = parseFloat(this.packageInfo.price || 0)
return (deposit + packagePrice).toFixed(2)
},
// 计算每小时的价格
hourlyRate() {
const price = parseFloat(this.packageInfo.price || 0);
let time = parseFloat(this.packageInfo.time || 1);
// 如果时间单位不是小时(例如分钟),需要转换
if (this.packageInfo.time && this.packageInfo.time.includes('分钟')) {
time = time / 60; // 将分钟转换为小时
} else if (this.packageInfo.time && this.packageInfo.time.includes('次')) {
// 按次计费时,暂时显示单次价格
return price.toFixed(2);
}
// 避免除以零
if (time <= 0) time = 1;
// 计算每小时价格
return (price / time).toFixed(2);
}
},
onLoad(options) {
if (options && options.orderId) {
this.orderId = options.orderId
if (options.totalAmount) {
this.passedTotalAmount = options.totalAmount;
}
if (options.depositAmount) {
this.passedDepositAmount = options.depositAmount;
}
// 如果URL中包含了feeConfig参数,保存它
if (options.feeConfig) {
try {
console.log('从URL获取到feeConfig:', options.feeConfig)
const feeConfigStr = decodeURIComponent(options.feeConfig)
// 创建一个临时的deviceInfo对象保存feeConfig
this.deviceInfo = { feeConfig: feeConfigStr }
} catch (e) {
console.error('解析URL中的feeConfig失败:', e)
}
}
this.loadOrderInfo()
} else {
uni.showToast({
title: '订单信息不存在',
icon: 'none'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/index/index'
})
}, 1500)
}
},
methods: {
// 加载订单信息
async loadOrderInfo() {
try {
uni.showLoading({
title: '加载中'
})
const res = await queryById(this.orderId)
if (res.code === 200 && res.data) {
const orderData = res.data
// 处理创建时间,确保显示的是格式化后的时间
let formattedTime;
try {
// 如果orderData.createTime存在并且是有效的日期字符串/时间戳,则格式化它
if (orderData.createTime) {
formattedTime = this.formatTime(new Date(orderData.createTime));
} else {
// 如果createTime不存在,使用当前时间作为创建时间
formattedTime = this.formatTime(new Date());
}
} catch (e) {
console.error('时间格式化错误:', e);
formattedTime = this.formatTime(new Date());
}
this.orderInfo = {
orderNo: orderData.orderNo || orderData.orderId,
deviceNo: orderData.deviceNo,
createTime: formattedTime,
phone: orderData.phone,
deposit: this.passedDepositAmount || orderData.depositAmount || '99.00',
}
// 直接从订单数据中获取套餐信息
if (orderData.packageTime && orderData.packagePrice) {
// 将分钟转换为小时
const timeInHours = (parseFloat(orderData.packageTime) / 60).toFixed(1);
this.packageInfo = {
time: timeInHours.toString(),
price: orderData.packagePrice.toString()
}
}
// 获取设备信息(但不再用于设置套餐信息)
this.deviceNo = orderData.deviceNo;
await this.loadDeviceInfo();
} else {
throw new Error('获取订单信息失败')
}
uni.hideLoading()
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '获取订单信息失败',
icon: 'none'
})
}
},
// 加载设备信息
async loadDeviceInfo() {
if (!this.deviceNo) return;
try {
const res = await getDeviceInfo(this.deviceNo);
if (res.code === 200 && res.data) {
this.deviceInfo = res.data.device;
// 设置存款金额
if (this.deviceInfo && this.deviceInfo.depositAmount) {
this.orderInfo.deposit = this.deviceInfo.depositAmount;
}
}
} catch (error) {
console.error('获取设备信息失败:', error);
}
},
// 处理支付
async handlePayment() {
try {
uni.showLoading({
title: '处理中'
})
// 调用后端创建微信支付订单接口
const res = await uni.request({
url: `${URL || 'http://127.0.0.1:8080'}/app/wx-payment/create/${this.orderInfo.orderNo}`,
method: 'GET',
header: {
'Authorization': "Bearer " + uni.getStorageSync('token'),
'Clientid': uni.getStorageSync('client_id')
}
})
if (res.statusCode === 200 && res.data.code === 200) {
const payParams = res.data.data
// 调用微信支付
await uni.requestPayment({
...payParams,
success: async () => {
uni.showToast({
title: '支付成功',
icon: 'success'
});
// 更新用户余额
try {
await updateUserBalance(this.orderId);
} catch (error) {
console.warn('更新用户余额失败:', error);
}
// 支付成功后直接跳转到订单页面,不再轮询
setTimeout(() => {
uni.redirectTo({
url: `/pages/order/index?orderId=${this.orderId}`
});
}, 1500);
},
fail: (err) => {
console.error('支付失败:', err)
throw new Error('支付失败,请重试')
}
})
} else {
throw new Error(res.data.msg || '创建支付订单失败')
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '支付失败',
icon: 'none'
})
}
},
// 发送租借指令
async sendRentCommand() {
try {
uni.showLoading({
title: '处理中'
})
// 调用发送租借指令的接口
const res = await this.sendRentRequest()
if (res.code === 200) {
uni.hideLoading()
uni.showToast({
title: '租借成功',
icon: 'success'
})
// 跳转到订单列表页面
setTimeout(() => {
uni.redirectTo({
url: `/pages/order/index?orderId=${this.orderId}`
})
}, 1500)
} else {
throw new Error(res.msg || '租借失败')
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '租借失败',
icon: 'none'
})
}
},
// 发送租借请求
sendRentRequest() {
return new Promise((resolve, reject) => {
uni.request({
url: `${URL || 'http://127.0.0.1:8080'}/app/device/sendRentCommand`,
method: 'POST',
data: {
orderId: this.orderId
},
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': "Bearer " + uni.getStorageSync('token'),
'Clientid': uni.getStorageSync('client_id')
},
success(res) {
if (res.statusCode === 200) {
resolve(res.data)
} else {
reject(new Error('请求失败'))
}
},
fail(err) {
reject(err)
}
})
})
},
// 格式化时间
formatTime(date) {
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}`
},
// 检查订单状态(单次查询,不轮询)
async checkOrderStatus() {
try {
const res = await uni.request({
url: `${URL || 'http://127.0.0.1:8080'}/app/wx-payment/status/${this.orderInfo.orderNo}`,
method: 'GET',
header: {
'Authorization': "Bearer " + uni.getStorageSync('token'),
'Clientid': uni.getStorageSync('client_id')
}
});
if (res.statusCode === 200 && res.data.code === 200) {
return res.data.data;
} else {
throw new Error('查询订单状态失败');
}
} catch (error) {
console.error('查询订单状态错误:', error);
return null;
}
},
}
}
</script>
<style lang="scss" scoped>
.payment-container {
min-height: 100vh;
background: #f8f8f8;
padding: 30rpx;
padding-bottom: 180rpx;
box-sizing: border-box;
.status-card {
background: #fff;
border-radius: 24rpx;
padding: 40rpx 30rpx;
margin-bottom: 30rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.status-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background: #f5f5f5;
margin-bottom: 20rpx;
&.waiting {
background: #FFF9C4;
}
&.success {
background: #E8F5E9;
}
&.failed {
background: #FFEBEE;
}
}
.status-text {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 10rpx;
}
.status-desc {
font-size: 28rpx;
color: #999;
}
}
.order-card, .price-card {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
position: relative;
padding-left: 20rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 8rpx;
height: 32rpx;
background: #1976D2;
border-radius: 4rpx;
}
}
.info-item, .price-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.label {
font-size: 28rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #333;
}
&.total {
margin-top: 10rpx;
padding-top: 30rpx;
border-top: 1px solid #f5f5f5;
.label, .value {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.value {
color: #FF5722;
}
}
}
}
.wechat-tip {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
display: flex;
align-items: center;
.method-icon {
width: 48rpx;
height: 48rpx;
margin-right: 20rpx;
&.wechat {
background: url('../../static/images/wechat.svg') no-repeat center/contain;
}
}
.method-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
}
.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;
align-items: center;
justify-content: space-between;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
.total-amount {
font-size: 28rpx;
color: #666;
.amount {
font-size: 36rpx;
font-weight: 600;
color: #FF5722;
margin-left: 10rpx;
}
}
.pay-btn {
background: #1976D2;
color: #fff;
font-size: 32rpx;
font-weight: 600;
padding: 20rpx 60rpx;
border-radius: 100rpx;
border: none;
&:active {
opacity: 0.9;
}
}
}
}
</style>