Files
uni-fans-score/pages/device/detail.vue
T
8vd8 41b409c327 feat:
取消了订单轮询
 更新押金提现及订单查询功能
 归还成功之后不会有归还成功的弹窗出现
 提现的判断目前判断押金状态和订单状态

修改了提现API的参数名称,从订单ID更改为订单号,并新增了根据设备号和状态查询订单列表的功能。同时,优化了用户在提现过程中的错误提示,确保用户能够获得更清晰的反馈。更新了多个页面的逻辑,提升了整体用户体验。
2025-04-18 18:04:21 +08:00

573 lines
12 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="detail-container">
<!-- 设备状态卡片 -->
<view class="device-card">
<view class="device-header">
<view class="device-title">
<text class="name">共享风扇</text>
<text class="id">设备号{{ deviceId }}</text>
</view>
<view class="status" :class="deviceStatus.class">{{ deviceStatus.text }}</view>
</view>
<view class="device-info">
<view class="info-item">
<text class="label">设备位置</text>
<text class="value">{{ deviceLocation }}</text>
</view>
<view class="info-item">
<text class="label">电池电量</text>
<text class="value">{{ batteryLevel }}%</text>
</view>
</view>
</view>
<!-- 套餐选择 -->
<view class="package-section" v-if="!hasActiveOrder">
<view class="section-title">选择套餐</view>
<view class="package-list">
<view v-for="(pkg, index) in packages" :key="index" class="package-item"
:class="{ active: selectedPackage === index }" @click="selectPackage(index)">
<view class="package-content">
<text class="time">{{ pkg.time }}</text>
<text class="price">{{ pkg.price }}</text>
</view>
<text class="unit-price">{{ pkg.unitPrice }}/小时</text>
</view>
</view>
</view>
<!-- 手机号输入 -->
<view class="phone-section" v-if="!hasActiveOrder">
<view class="section-title">联系方式</view>
<view class="phone-input-wrap">
<input
type="number"
class="phone-input"
maxlength="11"
placeholder="请输入手机号码"
v-model="phoneNumber"
/>
</view>
</view>
<!-- 使用说明 -->
<view class="notice-section">
<view class="section-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">
<view class="price-info" v-if="!hasActiveOrder">
<text class="deposit-text">押金</text>
<text class="deposit-amount">99</text>
</view>
<button class="action-btn" :class="hasActiveOrder ? 'return' : 'rent'" @click="handleRent">
{{ hasActiveOrder ? '归还设备' : '立即租借' }}
</button>
</view>
</view>
</template>
<script>
import {
getDeviceInfo,
rentPowerBank,
updateOrderPackage
} from '@/config/user.js'
export default {
data() {
return {
deviceInfo: {},
deviceId: '',
deviceLocation: '一号教学楼大厅',
batteryLevel: 95,
hasActiveOrder: false,
deviceStatus: {
text: '可使用',
class: 'available'
},
selectedPackage: 1,
packages: [{
time: '1小时',
price: '2.00',
unitPrice: '2.00'
},
{
time: '4小时',
price: '6.00',
unitPrice: '1.50'
},
{
time: '12小时',
price: '15.00',
unitPrice: '1.25'
}
],
isLoggedIn: true,
phoneNumber: ''
}
},
onLoad(options) {
// console.log(options);
this.deviceId = options.deviceNo
this.checkOrderStatus() // 新增状态检查
console.log(options.deviceNo);
this.getDeviceInfo()
},
methods: {
// 检查登录状态和订单
async getDeviceInfo() {
const res = await getDeviceInfo(this.deviceId);
if (res.code == 200) {
this.deviceInfo = res.data
}
},
// 显示登录提示
showLoginTip() {
uni.showModal({
title: '提示',
content: '请先登录后再操作',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: '/pages/login/index'
})
}
}
})
},
selectPackage(index) {
this.selectedPackage = index;
},
// 检查订单状态
async checkOrderStatus() {
try {
// 调用接口检查是否有进行中的订单
const result = await this.$api.checkActiveOrder()
if (result.hasOrder) {
const order = result.order; // 假设后端返回 order 对象
// 检查订单状态
if (order.status === 'waiting_for_payment') {
// 跳转支付页面,带上订单ID
uni.redirectTo({
url: `/pages/order/payment?orderId=${order.orderId}&deviceId=${this.deviceId}`
})
}else if(order.status === 'in_used'){
// 如果有正在进行的订单,跳转到归还页面,带上设备ID
uni.redirectTo({
url: `/pages/device/return?deviceId=${this.deviceId}`
})
}
}
} catch (error) {
uni.showToast({
title: '订单状态查询失败',
icon: 'none'
})
}
},
// 处理租借操作
handleRent() {
if (!this.isLoggedIn) {
this.showLoginTip()
return
}
// 添加手机号验证
if (!this.phoneNumber) {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
// 验证手机号格式
if (!/^1[3-9]\d{9}$/.test(this.phoneNumber)) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
const selectedPkg = this.packages[this.selectedPackage]
uni.showModal({
title: '确认租借',
content: `确认支付押金¥99.00及${selectedPkg.time}套餐费用¥${selectedPkg.price}`,
success: (res) => {
if (res.confirm) {
this.submitRentOrder()
}
}
})
},
// 提交租借订单
async submitRentOrder() {
try {
uni.showLoading({
title: '处理中'
})
// 获取选中的套餐信息
const selectedPkg = this.packages[this.selectedPackage]
// 调用设备租借接口
const rentResult = await rentPowerBank(this.deviceId, this.phoneNumber)
if (rentResult.code !== 200) {
throw new Error(rentResult.msg || '设备租借失败')
}
// 获取后端返回的订单信息
const order = rentResult.data
// --- 新增:立即更新订单套餐信息 ---
try {
let packageTimeMinutes = 0;
if (selectedPkg.time.includes('小时')) {
packageTimeMinutes = parseInt(selectedPkg.time) * 60;
} else if (selectedPkg.time.includes('分钟')) {
packageTimeMinutes = parseInt(selectedPkg.time);
} else {
packageTimeMinutes = parseInt(selectedPkg.time) * 60; // 默认按小时处理
}
const updateRes = await updateOrderPackage({
orderId: order.orderId,
packageTime: packageTimeMinutes,
packagePrice: parseFloat(selectedPkg.price)
});
if (updateRes.code !== 200) {
console.warn("更新订单套餐信息失败:", updateRes.msg);
// 这里可以选择是否提示用户或阻止流程,当前不阻止
} else {
console.log("订单套餐信息已提前更新");
}
} catch (updateError) {
console.error("更新订单套餐信息时出错:", updateError);
// 即使更新失败,也继续尝试跳转支付,让用户完成支付
}
// --- 更新结束 ---
// --- 新增:计算总金额 ---
const deposit = 99.00; // 固定押金
const packagePrice = parseFloat(selectedPkg.price);
const totalAmount = (deposit + packagePrice).toFixed(2);
// --- 计算结束 ---
uni.hideLoading()
// 跳转到订单支付页面,传递订单ID、套餐信息和总金额
uni.redirectTo({
url: `/pages/order/payment?orderId=${order.orderId}&packageTime=${selectedPkg.time}&packagePrice=${selectedPkg.price}&totalAmount=${totalAmount}`
})
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '租借失败,请重试',
icon: 'none'
})
}
}
}
}
</script>
<style lang="scss" scoped>
.detail-container {
min-height: 100vh;
background: #f8f8f8;
padding: 30rpx;
padding-bottom: 180rpx;
box-sizing: border-box;
.device-card {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.device-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.device-title {
.name {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-right: 20rpx;
}
.id {
font-size: 24rpx;
color: #999;
}
}
.status {
padding: 8rpx 24rpx;
border-radius: 24rpx;
font-size: 24rpx;
&.available {
background: #E8F5E9;
color: #4CAF50;
}
&.in-use {
background: #E3F2FD;
color: #1976D2;
}
}
}
.device-info {
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 28rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #333;
}
}
}
}
.package-section {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
.package-list {
display: flex;
justify-content: space-between;
.package-item {
flex: 1;
margin: 0 10rpx;
padding: 24rpx;
background: #F5F5F5;
border-radius: 16rpx;
text-align: center;
transition: all 0.3s;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
&.active {
background: #E3F2FD;
border: 2rpx solid #1976D2;
.package-content {
.time,
.price {
color: #1976D2;
}
}
}
.package-content {
margin-bottom: 8rpx;
.time {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
display: block;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
}
.unit-price {
font-size: 24rpx;
color: #999;
}
}
}
}
.phone-section {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
.phone-input-wrap {
.phone-input {
width: 100%;
height: 88rpx;
background: #F5F5F5;
border-radius: 16rpx;
padding: 0 30rpx;
font-size: 28rpx;
box-sizing: border-box;
&::placeholder {
color: #999;
}
}
}
}
.notice-section {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.section-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;
align-items: center;
justify-content: space-between;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
.price-info {
.deposit-text {
font-size: 28rpx;
color: #666;
}
.deposit-amount {
font-size: 36rpx;
font-weight: 600;
color: #FF9800;
}
}
.action-btn {
flex: 1;
margin-left: 30rpx;
height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
border: none;
&.rent {
background: linear-gradient(135deg, #1976D2, #42A5F5);
color: #fff;
}
&.return {
background: linear-gradient(135deg, #FF9800, #FFB74D);
color: #fff;
}
&:active {
transform: scale(0.98);
}
}
}
}
</style>