708 lines
15 KiB
Vue
708 lines
15 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 顶部设备信息 -->
|
||
<view class="device-header">
|
||
<view class="device-status-card" :class="deviceStatus.class">
|
||
<view class="status-indicator"></view>
|
||
<text class="status-text">{{ deviceStatus.text }}</text>
|
||
</view>
|
||
<view class="device-title">
|
||
<text class="name">共享风扇</text>
|
||
<view class="device-meta">
|
||
<text class="id-label">设备号:</text>
|
||
<text class="id-value">{{ deviceId }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 设备信息卡片 -->
|
||
<view class="card device-info-card">
|
||
<view class="card-row">
|
||
<view class="card-item">
|
||
<view class="item-icon location-icon">
|
||
<!-- <uni-icons type="location" size="24" color="#fff"></uni-icons> -->
|
||
<image src="/static/images/location-map.svg" mode="aspectFill"
|
||
style="width: 45rpx;height: 45rpx;"></image>
|
||
</view>
|
||
<view class="item-content">
|
||
<text class="item-label">当前位置</text>
|
||
<text class="item-value">{{ deviceLocation }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="card-item">
|
||
<view class="item-icon battery-icon" :class="{ 'battery-low': batteryLevel < 20 }">
|
||
<image src="/static/images/Electricity.svg" mode="aspectFill"
|
||
style="width: 45rpx;height: 45rpx;"></image>
|
||
</view>
|
||
<view class="item-content">
|
||
<text class="item-label">电池电量</text>
|
||
<text class="item-value">{{ batteryLevel }}%</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 计费规则 -->
|
||
<view class="card pricing-card">
|
||
<view class="card-header">
|
||
<text class="card-title">计费规则</text>
|
||
</view>
|
||
|
||
<view class="pricing-banner">
|
||
<view class="pricing-main">
|
||
<text class="price">¥5.00</text>
|
||
<text class="unit">/小时</text>
|
||
</view>
|
||
<text class="cap-price">封顶 ¥99</text>
|
||
</view>
|
||
|
||
<view class="pricing-rules">
|
||
<view class="rule-item">
|
||
<view class="rule-dot"></view>
|
||
<text class="rule-text">前15分钟内归还<text class="highlight">免费</text></text>
|
||
</view>
|
||
<view class="rule-item">
|
||
<view class="rule-dot"></view>
|
||
<text class="rule-text">不足60分钟按60分钟计费</text>
|
||
</view>
|
||
<view class="rule-item">
|
||
<view class="rule-dot"></view>
|
||
<text class="rule-text">持续计费至99元视为买断</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 手机号输入 -->
|
||
<view class="card phone-card" v-if="!hasActiveOrder">
|
||
<view class="card-header">
|
||
<text class="card-title">联系方式</text>
|
||
</view>
|
||
<view class="phone-input-container">
|
||
<view class="input-wrapper">
|
||
<text class="prefix">+86</text>
|
||
<input type="number" class="phone-input" maxlength="11" placeholder="请输入手机号码"
|
||
v-model="phoneNumber" />
|
||
</view>
|
||
<!-- <text class="phone-tip">租借将发送验证码至此号码,请确保可正常接收短信</text> -->
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 使用须知 -->
|
||
<view class="card notice-card">
|
||
<view class="card-header">
|
||
<text class="card-title">使用须知</text>
|
||
</view>
|
||
<view class="notice-items">
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">请在使用前检查设备是否完好</text>
|
||
</view>
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">请在指定区域内使用设备</text>
|
||
</view>
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">归还时请确保设备完好,避免损坏</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部操作区 -->
|
||
<view class="footer">
|
||
<view class="wechat-credit">
|
||
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="wx-icon"></image>
|
||
<view class="credit-text">
|
||
<text>微信支付分</text>
|
||
<text class="credit-divider">|</text>
|
||
<text class="credit-score">支付分200分及以上优享</text>
|
||
</view>
|
||
</view>
|
||
|
||
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }" @click="handleRent">
|
||
<text>{{ hasActiveOrder ? '归还设备' : '免押金租借' }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
reactive,
|
||
onMounted
|
||
} from 'vue'
|
||
import {
|
||
onLoad
|
||
} from '@dcloudio/uni-app'
|
||
import {
|
||
getDeviceInfo,
|
||
rentPowerBank,
|
||
getOrderByOrderNoScore,
|
||
getOrderByOrderNoScorePayStatus
|
||
} from '@/config/user.js'
|
||
import {
|
||
URL
|
||
} from "@/config/url.js"
|
||
import {
|
||
initiateWeChatScorePayment
|
||
} from '@/util/index.js'
|
||
|
||
// 响应式状态
|
||
const deviceInfo = ref({})
|
||
const deviceId = ref('')
|
||
const deviceLocation = ref('一号教学楼大厅')
|
||
const batteryLevel = ref(95)
|
||
const hasActiveOrder = ref(false)
|
||
const deviceStatus = reactive({
|
||
text: '可使用',
|
||
class: 'available'
|
||
})
|
||
const isLoggedIn = ref(true)
|
||
const phoneNumber = ref('')
|
||
|
||
// 生命周期 onLoad 钩子
|
||
onLoad((options) => {
|
||
deviceId.value = options.deviceNo
|
||
checkOrderStatus()
|
||
console.log(options.deviceNo)
|
||
fetchDeviceInfo()
|
||
})
|
||
|
||
// 检查登录状态和订单
|
||
const fetchDeviceInfo = async () => {
|
||
const res = await getDeviceInfo(deviceId.value)
|
||
if (res.code == 200) {
|
||
deviceInfo.value = res.data.device || {}
|
||
|
||
// 更新设备位置信息
|
||
if (deviceInfo.value.deviceLocation) {
|
||
deviceLocation.value = deviceInfo.value.deviceLocation
|
||
} else if (res.data.position && res.data.position.name) {
|
||
deviceLocation.value = res.data.position.name
|
||
}
|
||
|
||
// 更新设备状态
|
||
if (deviceInfo.value.status) {
|
||
if (deviceInfo.value.status === 'online') {
|
||
deviceStatus.text = '可使用'
|
||
deviceStatus.class = 'available'
|
||
} else if (deviceInfo.value.status === 'offline') {
|
||
deviceStatus.text = '离线'
|
||
deviceStatus.class = 'offline'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示登录提示
|
||
const showLoginTip = () => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '请先登录后再操作',
|
||
confirmText: '去登录',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({
|
||
url: '/pages/login/index'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 检查订单状态
|
||
const checkOrderStatus = async () => {
|
||
try {
|
||
// 调用接口检查是否有进行中的订单
|
||
const result = await uni.$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=${deviceId.value}`
|
||
})
|
||
} else if (order.status === 'in_used') {
|
||
// 如果有正在进行的订单,跳转到归还页面,带上设备ID
|
||
uni.redirectTo({
|
||
url: `/pages/device/return?deviceId=${deviceId.value}`
|
||
})
|
||
}
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: '订单状态查询失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 处理租借操作
|
||
const handleRent = () => {
|
||
if (!isLoggedIn.value) {
|
||
showLoginTip()
|
||
return
|
||
}
|
||
|
||
// 添加手机号验证
|
||
if (!phoneNumber.value) {
|
||
uni.showToast({
|
||
title: '请输入手机号码',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 验证手机号格式
|
||
if (!/^1[3-9]\d{9}$/.test(phoneNumber.value)) {
|
||
uni.showToast({
|
||
title: '请输入正确的手机号码',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 直接提交订单,不显示确认对话框
|
||
submitRentOrder()
|
||
}
|
||
|
||
// 提交租借订单
|
||
const submitRentOrder = async () => {
|
||
try {
|
||
uni.showLoading({
|
||
title: '处理中'
|
||
})
|
||
|
||
// 调用设备租借接口
|
||
const rentResult = await rentPowerBank(deviceId.value, phoneNumber.value)
|
||
if (rentResult.code !== 200) {
|
||
throw new Error(rentResult.msg || '设备租借失败')
|
||
}
|
||
|
||
// 获取后端返回的订单信息
|
||
const order = rentResult.data
|
||
|
||
// 获取支付分所需参数
|
||
const res = await getOrderByOrderNoScore(order.orderNo);
|
||
console.log(res);
|
||
|
||
uni.hideLoading()
|
||
|
||
if (res && res.code === 200) {
|
||
try {
|
||
// 调用微信支付分小程序
|
||
const payResult = await initiateWeChatScorePayment(res);
|
||
|
||
// 支付成功后的逻辑处理 - 可以根据业务需求决定是否跳转或刷新页面
|
||
if (payResult.errCode == '0') {
|
||
const res = await getOrderByOrderNoScorePayStatus(order.orderNo);
|
||
// 用户完成了支付流程,可以查询订单状态或跳转到订单页
|
||
uni.showToast({
|
||
title: '设备租借成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
setTimeout(() => {
|
||
// 延迟跳转到租用中页面或订单页
|
||
uni.redirectTo({
|
||
url: '/pages/order/index'
|
||
});
|
||
}, 1500);
|
||
}
|
||
// 用户取消等其他情况,不做特殊处理
|
||
} catch (payError) {
|
||
uni.showToast({
|
||
title: '支付分调用失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: res?.msg || '获取支付参数失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
|
||
console.log(order);
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: error.message || '租借失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.container {
|
||
min-height: 100vh;
|
||
background-color: #f5f7fa;
|
||
padding: 30rpx 30rpx 240rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 顶部设备信息
|
||
.device-header {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-bottom: 30rpx;
|
||
|
||
.device-status-card {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.status-indicator {
|
||
width: 20rpx;
|
||
height: 20rpx;
|
||
border-radius: 50%;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
&.available {
|
||
.status-indicator {
|
||
background-color: #10c469;
|
||
box-shadow: 0 0 10rpx rgba(16, 196, 105, 0.5);
|
||
}
|
||
|
||
.status-text {
|
||
color: #10c469;
|
||
}
|
||
}
|
||
|
||
&.offline {
|
||
.status-indicator {
|
||
background-color: #9a9a9a;
|
||
}
|
||
|
||
.status-text {
|
||
color: #9a9a9a;
|
||
}
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.device-title {
|
||
.name {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.device-meta {
|
||
margin-top: 10rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.id-label {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.id-value {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 卡片通用样式
|
||
.card {
|
||
background-color: #fff;
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.03);
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
|
||
.card-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设备信息卡
|
||
.device-info-card {
|
||
.card-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.card-item {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
|
||
.item-icon {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 12rpx;
|
||
margin-right: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 30rpx;
|
||
color: #fff;
|
||
|
||
&.location-icon {
|
||
background: linear-gradient(135deg, #40c9ff, #32a5ff);
|
||
|
||
// &::before {
|
||
// content: "\e900"; // 使用字体图标,需要自行替换
|
||
// }
|
||
}
|
||
|
||
&.battery-icon {
|
||
background: linear-gradient(135deg, #33db92, #10c469);
|
||
|
||
// &::before {
|
||
// content: "\e901"; // 使用字体图标,需要自行替换
|
||
// }
|
||
|
||
&.battery-low {
|
||
background: linear-gradient(135deg, #ff7676, #f54f4f);
|
||
}
|
||
}
|
||
}
|
||
|
||
.item-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.item-label {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.item-value {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计费规则
|
||
.pricing-card {
|
||
.pricing-banner {
|
||
background: linear-gradient(to right, #f8f9ff, #e8f0ff);
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.pricing-main {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
|
||
.price {
|
||
font-size: 60rpx;
|
||
font-weight: bold;
|
||
color: #ff6b6b;
|
||
}
|
||
|
||
.unit {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
margin-left: 4rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
}
|
||
|
||
.cap-price {
|
||
margin-top: 10rpx;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
background-color: rgba(255, 107, 107, 0.1);
|
||
padding: 6rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
}
|
||
|
||
.pricing-rules {
|
||
.rule-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.rule-dot {
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
border-radius: 50%;
|
||
background-color: #ff6b6b;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.rule-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
|
||
.highlight {
|
||
color: #ff6b6b;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 手机号输入
|
||
.phone-card {
|
||
.phone-input-container {
|
||
.input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 88rpx;
|
||
background-color: #f5f7fa;
|
||
border-radius: 16rpx;
|
||
padding: 0 24rpx;
|
||
|
||
.prefix {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-right: 16rpx;
|
||
padding-right: 16rpx;
|
||
border-right: 1px solid #e0e0e0;
|
||
}
|
||
|
||
.phone-input {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
padding-left: 10rpx;
|
||
}
|
||
}
|
||
|
||
.phone-tip {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 16rpx;
|
||
display: block;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 使用须知
|
||
.notice-card {
|
||
.notice-items {
|
||
.notice-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 16rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.notice-dot {
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
border-radius: 50%;
|
||
background-color: #32a5ff;
|
||
margin-right: 16rpx;
|
||
margin-top: 12rpx;
|
||
}
|
||
|
||
.notice-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部操作区
|
||
.footer {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #fff;
|
||
padding: 20rpx 30rpx;
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||
z-index: 100;
|
||
display: flex;
|
||
flex-direction: column;
|
||
// 添加一个变量来保存footer高度,方便管理和确保一致性
|
||
--footer-height: 180rpx;
|
||
|
||
.wechat-credit {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.wx-icon {
|
||
width: 50rpx;
|
||
height: 40rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.credit-text {
|
||
font-size: 24rpx;
|
||
color: #07c160;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.credit-divider {
|
||
margin: 0 10rpx;
|
||
}
|
||
|
||
.credit-score {
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.rent-button {
|
||
height: 92rpx;
|
||
border-radius: 46rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
width: 90%;
|
||
|
||
&.return-button {
|
||
background: linear-gradient(135deg, #FF9800, #FFB74D);
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
</style> |