Files
uni-fans-score/pages/device/detail.vue
T

669 lines
14 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="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
} from '@/config/user.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()
// 跳转到订单支付页面
// uni.redirectTo({
// url: `/pages/order/payment?orderId=${order.orderId}&deviceId=${deviceId.value}&noDeposit=true`
// })
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>