2025-10-21 18:44:42 +08:00

728 lines
22 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>
<div class="reststop-detail-page" :style="{ paddingBottom: `${safeHeight}px` }">
<!-- 休息站基本信息 -->
<div class="reststop-header">
<div class="reststop-title">{{ reststopInfo.reststopName }}</div>
<div class="reststop-location">{{ reststopInfo.location }}</div>
<div class="status-badges">
<div class="status-badge" :class="[
reststopInfo.doorLockStatus === 'unlocked' ? 'badge-available' : 'badge-occupied'
]">
{{ getDoorLockStatusText(reststopInfo.doorLockStatus) }}
</div>
<div class="status-badge" :class="[
getCleaningBadgeClass(reststopInfo.cleaningStatus)
]">
{{ getCleaningStatusText(reststopInfo.cleaningStatus) }}
</div>
</div>
</div>
<!-- 订单信息 -->
<div class="order-section" v-if="reststopInfo.orderStatus && reststopInfo.orderStatus !== 'cancelled'">
<div class="section-title">订单信息</div>
<div class="order-card">
<div class="order-item">
<div class="order-label">订单状态</div>
<div class="order-value" :class="[
reststopInfo.orderStatus === 'active' ? 'order-active' :
(reststopInfo.orderStatus === 'completed' ? 'order-completed' : 'order-cancelled')
]">
{{ getOrderStatusText(reststopInfo.orderStatus) }}
</div>
</div>
<div class="order-item">
<div class="order-label">下单时间</div>
<div class="order-value">{{ formatDateTime(reststopInfo.orderTime) }}</div>
</div>
<div class="order-item">
<div class="order-label">剩余时间</div>
<div class="order-value remaining-time">
{{ reststopInfo.remainingTime || '已结束' }}
</div>
</div>
<div class="order-item" v-if="reststopInfo.orderStatus === 'active'">
<div class="order-label">续费状态</div>
<div class="order-value" :class="[
reststopInfo.renewalStatus === 'available' ? 'renewal-available' : 'renewal-unavailable'
]">
{{ getRenewalStatusText(reststopInfo.renewalStatus) }}
</div>
</div>
</div>
</div>
<!-- 设备状态 -->
<div class="device-section">
<div class="section-title">设备状态</div>
<div class="device-grid">
<div class="device-item">
<div class="device-icon">🔒</div>
<div class="device-info">
<div class="device-name">门锁</div>
<div class="device-status" :class="[
reststopInfo.doorLockStatus === 'unlocked' ? 'status-unlocked' : 'status-locked'
]">
{{ reststopInfo.doorLockStatus === 'unlocked' ? '未锁定' : '已锁定' }}
</div>
</div>
</div>
<div class="device-item">
<div class="device-icon">🧹</div>
<div class="device-info">
<div class="device-name">保洁</div>
<div class="device-status" :class="[
reststopInfo.cleaningStatus === 'clean' ? 'device-clean' :
(reststopInfo.cleaningStatus === 'cleaning' ? 'device-cleaning' : 'device-dirty')
]">
{{ getCleaningStatusText(reststopInfo.cleaningStatus) }}
</div>
</div>
</div>
</div>
</div>
<!-- 评价信息 -->
<div class="rating-section" v-if="reststopInfo.rating">
<div class="section-title">用户评价</div>
<div class="rating-card">
<div class="rating-score">
<div class="score-number">{{ reststopInfo.rating }}</div>
<div class="score-stars">
<span class="star" v-for="i in 5" :key="i"
:class="[i <= reststopInfo.rating ? 'star-filled' : 'star-empty']"></span>
</div>
</div>
<div class="rating-desc">用户综合评分</div>
</div>
</div>
<!-- 联系信息 -->
<div class="contact-section">
<div class="section-title">联系信息</div>
<div class="contact-card">
<div class="contact-item" @click="makePhoneCall">
<div class="contact-icon">📞</div>
<div class="contact-info">
<div class="contact-label">应急联系</div>
<div class="contact-value">{{ reststopInfo.emergencyContact }}</div>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-section">
<div class="action-btn" :class="[
reststopInfo.orderStatus === 'active' ? 'action-active' :
(reststopInfo.doorLockStatus === 'locked' ? 'action-occupied' :
(reststopInfo.cleaningStatus === 'cleaning' ? 'action-cleaning' : 'action-normal'))
]" @click="handleMainAction">
{{ getMainActionText() }}
</div>
<div class="secondary-btn" v-if="reststopInfo.orderStatus === 'active'" @click="handleRenewal">
续费使用
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
safeHeight: "",
reststopId: "",
reststopInfo: {},
defaultCover: 'https://picsum.photos/seed/reststop-detail/400/300.jpg'
};
},
onLoad(query) {
let systemInfo = uni.getSystemInfoSync();
let height = systemInfo.safeAreaInsets.bottom;
this.safeHeight = Number(height);
if (query.id) {
this.reststopId = query.id;
this.loadReststopDetail();
}
},
methods: {
// 加载休息站详情
async loadReststopDetail() {
try {
uni.showLoading({
title: '加载中...',
mask: true
});
// 这里调用实际接口获取休息站详情
// const res = await this.$api.getReststopDetail({
// reststopId: this.reststopId
// });
// 模拟数据
const mockData = {
id: this.reststopId,
reststopName: "昆明服务区A区-01号休息站",
location: "昆明服务区A区东侧靠近便利店",
doorLockStatus: "unlocked",
orderStatus: "active",
orderTime: "2024-03-20 14:30:00",
remainingTime: "45分钟",
renewalStatus: "available",
cleaningStatus: "clean",
emergencyContact: "13800138000",
rating: 4.8,
facilities: [
"空调", "WiFi", "充电插座", "饮水机", "独立卫浴"
],
description: "舒适便捷的共享休息空间,配备完善的基础设施,为您提供安静的休息环境。"
};
this.reststopInfo = mockData;
uni.hideLoading();
} catch (error) {
console.error('加载休息站详情失败:', error);
uni.hideLoading();
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
});
}
},
// 拨打电话
makePhoneCall() {
uni.makePhoneCall({
phoneNumber: this.reststopInfo.emergencyContact
});
},
// 处理主要操作
handleMainAction() {
if (this.reststopInfo.orderStatus === 'active') {
uni.showToast({
title: '您有正在进行的订单',
icon: 'none'
});
return;
}
if (this.reststopInfo.doorLockStatus === 'locked') {
uni.showToast({
title: '当前休息站被占用',
icon: 'none'
});
return;
}
if (this.reststopInfo.cleaningStatus === 'cleaning') {
uni.showToast({
title: '休息站正在清洁中',
icon: 'none'
});
return;
}
// 跳转到下单页面
uni.showModal({
title: '确认下单',
content: '确定要使用此休息站吗?',
success: (res) => {
if (res.confirm) {
this.createOrder();
}
}
});
},
// 处理续费
handleRenewal() {
if (this.reststopInfo.renewalStatus !== 'available') {
uni.showToast({
title: '当前不可续费',
icon: 'none'
});
return;
}
uni.showModal({
title: '续费确认',
content: '确定要续费使用此休息站吗?',
success: (res) => {
if (res.confirm) {
this.renewOrder();
}
}
});
},
// 创建订单
createOrder() {
uni.showLoading({
title: '下单中...',
mask: true
});
setTimeout(() => {
uni.hideLoading();
this.reststopInfo.orderStatus = 'active';
this.reststopInfo.orderTime = new Date().toISOString();
this.reststopInfo.remainingTime = '120分钟';
this.reststopInfo.doorLockStatus = 'locked';
uni.showToast({
title: '下单成功',
icon: 'success'
});
}, 2000);
},
// 续费订单
renewOrder() {
uni.showLoading({
title: '续费中...',
mask: true
});
setTimeout(() => {
uni.hideLoading();
this.reststopInfo.remainingTime = '240分钟';
uni.showToast({
title: '续费成功',
icon: 'success'
});
}, 2000);
},
// 格式化日期时间
formatDateTime(dateTime) {
if (!dateTime) return '';
try {
const date = new Date(dateTime);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
} catch (error) {
return dateTime;
}
},
// 获取门锁状态文本
getDoorLockStatusText(status) {
const statusMap = {
'unlocked': '可使用',
'locked': '使用中'
};
return statusMap[status] || '未知';
},
// 获取保洁状态文本
getCleaningStatusText(status) {
const statusMap = {
'clean': '已清洁',
'cleaning': '清洁中',
'dirty': '待清洁'
};
return statusMap[status] || '未知';
},
// 获取订单状态文本
getOrderStatusText(status) {
const statusMap = {
'active': '进行中',
'completed': '已完成',
'cancelled': '已取消'
};
return statusMap[status] || '无订单';
},
// 获取续费状态文本
getRenewalStatusText(status) {
const statusMap = {
'available': '可续费',
'unavailable': '不可续费'
};
return statusMap[status] || '未知';
},
// 获取保洁徽章样式类
getCleaningBadgeClass(status) {
const classMap = {
'clean': 'badge-clean',
'cleaning': 'badge-cleaning',
'dirty': 'badge-dirty'
};
return classMap[status] || 'badge-default';
},
// 获取主要操作按钮文本
getMainActionText() {
if (this.reststopInfo.orderStatus === 'active') return '使用中';
if (this.reststopInfo.doorLockStatus === 'locked') return '被占用';
if (this.reststopInfo.cleaningStatus === 'cleaning') return '清洁中';
return '立即使用';
}
}
};
</script>
<style scoped lang="less">
.reststop-detail-page {
width: 100vw;
min-height: 100vh;
background: #f8fafc;
overflow-y: scroll;
padding-bottom: 180rpx;
.reststop-header {
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
padding: 32rpx;
color: white;
.reststop-title {
font-size: 30rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.reststop-location {
font-size: 24rpx;
margin-bottom: 16rpx;
}
.status-badges {
display: flex;
gap: 16rpx;
.status-badge {
padding: 12rpx 12rpx 0;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
&.badge-available {
background: rgba(34, 197, 94, 0.2);
color: white;
}
&.badge-occupied {
background: rgba(245, 158, 11, 0.2);
color: white;
}
&.badge-clean {
background: rgba(16, 185, 129, 0.2);
color: white;
}
&.badge-cleaning {
background: rgba(59, 130, 246, 0.2);
color: white;
}
&.badge-dirty {
background: rgba(239, 68, 68, 0.2);
color: white;
}
}
}
}
.order-section,
.device-section,
.rating-section,
.contact-section {
margin: 24rpx;
background: white;
border-radius: 24rpx;
padding: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
.section-title {
font-size: 28rpx;
font-weight: 600;
color: #1f2937;
margin-bottom: 24rpx;
position: relative;
padding-left: 16rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 8rpx;
bottom: 8rpx;
width: 6rpx;
background: #22c55e;
border-radius: 3rpx;
}
}
}
.contact-section {
margin: 24rpx 24rpx calc(24rpx + env(safe-area-inset-bottom) + 24rpx + 64rpx + 56rpx);
}
.order-card {
.order-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.order-label {
font-size: 24rpx;
color: #6b7280;
}
.order-value {
font-size: 24rpx;
color: #374151;
font-weight: 500;
&.remaining-time {
color: #f59e0b;
font-weight: 600;
}
&.order-active {
color: #10b981;
}
&.order-completed {
color: #6b7280;
}
&.renewal-available {
color: #10b981;
}
&.renewal-unavailable {
color: #ef4444;
}
}
}
}
.device-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24rpx;
.device-item {
display: flex;
align-items: center;
padding: 16rpx;
background: #f9fafb;
border-radius: 16rpx;
.device-icon {
font-size: 40rpx;
margin-right: 16rpx;
}
.device-info {
.device-name {
font-size: 24rpx;
color: #374151;
font-weight: 500;
margin-bottom: 4rpx;
}
.device-status {
font-size: 24rpx;
&.status-unlocked {
color: #10b981;
}
&.status-locked {
color: #f59e0b;
}
&.device-clean {
color: #10b981;
}
&.device-cleaning {
color: #3b82f6;
}
&.device-dirty {
color: #ef4444;
}
}
}
}
}
.rating-card {
text-align: center;
.rating-score {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
.score-number {
font-size: 40rpx;
font-weight: 700;
color: #f59e0b;
margin-right: 16rpx;
}
.score-stars {
.star {
font-size: 28rpx;
margin: 0 4rpx;
&.star-filled {
color: #f59e0b;
}
&.star-empty {
color: #e5e7eb;
}
}
}
}
.rating-desc {
font-size: 24rpx;
color: #6b7280;
}
}
.contact-card {
.contact-item {
display: flex;
align-items: center;
padding: 24rpx;
background: #f9fafb;
border-radius: 16rpx;
transition: all 0.3s ease;
&:active {
background: #f3f4f6;
}
.contact-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.contact-info {
flex: 1;
.contact-label {
font-size: 24rpx;
color: #6b7280;
margin-bottom: 4rpx;
}
.contact-value {
font-size: 24rpx;
color: #374151;
font-weight: 500;
}
}
.contact-arrow {
font-size: 24rpx;
color: #9ca3af;
}
}
}
.action-section {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: white;
border-top: 1rpx solid #f0f0f0;
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
z-index: 100;
.action-btn {
width: 100%;
padding: 16rpx;
border-radius: 32rpx;
font-size: 28rpx;
font-weight: 600;
text-align: center;
color: white;
transition: all 0.3s ease;
margin-bottom: 16rpx;
box-sizing: border-box;
&.action-normal {
background: linear-gradient(135deg, #22c55e, #16a34a);
&:active {
opacity: 0.9;
}
}
&.action-active {
background: linear-gradient(135deg, #f59e0b, #d97706);
&:active {
opacity: 0.9;
}
}
&.action-occupied,
&.action-cleaning {
background: linear-gradient(135deg, #9ca3af, #6b7280);
&:active {
opacity: 0.9;
}
}
}
.secondary-btn {
width: 100%;
padding: 16rpx;
border-radius: 32rpx;
font-size: 28rpx;
font-weight: 500;
text-align: center;
background: #f3f4f6;
color: #374151;
transition: all 0.3s ease;
box-sizing: border-box;
&:active {
background: #e5e7eb;
}
}
}
}
.reststop-detail-page::-webkit-scrollbar {
display: none;
}
</style>