HomeLease/pages/myOrder/OrderDetail.vue
2025-08-25 14:12:27 +08:00

469 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.

<script setup>
import { ref, onMounted } from 'vue'
import { getOrderDetail } from '@/api/order/myOrder.js'
import { onLoad } from '@dcloudio/uni-app'
const orderDetail = ref(null)
const loading = ref(false)
const orderId = ref('')
// 页面加载时获取参数
onLoad(options => {
if (options.id) {
orderId.value = options.id
loadOrderDetail()
} else {
uni.showToast({
title: '订单ID不能为空',
icon: 'none',
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
})
/**
* 订单状态/物流状态映射工具
* @param {string} type - 'status'(订单状态) 或 'logistic'(物流状态)
* @param {string|number} value - 状态值(数字或中文)
* @returns {string|number} 转换后的值
*/
function mapOrderStatus(type, value) {
const maps = {
status: {
1: '未支付',
2: '支付成功',
3: '退款',
未支付: '1',
支付成功: '2',
退款: '3',
},
logistic: {
1: '未签收',
2: '已签收',
3: '已归还',
未签收: '1',
已签收: '2',
已归还: '3',
},
}
return maps[type]?.[value] || '未知状态'
}
/**
* 获取订单状态样式类
* @param {string} status - 状态值
* @returns {string} 样式类名
*/
function getStatusClass(status) {
const statusMap = {
1: 'status-pending',
2: 'status-success',
3: 'status-refund',
}
return statusMap[status] || 'status-unknown'
}
/**
* 获取物流状态样式类
* @param {string} status - 状态值
* @returns {string} 样式类名
*/
function getLogisticClass(status) {
const statusMap = {
1: 'logistic-pending',
2: 'logistic-success',
3: 'logistic-returned',
}
return statusMap[status] || 'logistic-unknown'
}
/**
* 格式化时间
* @param {string} timeStr - 时间字符串
* @returns {string} 格式化后的时间
*/
function formatTime(timeStr) {
if (!timeStr) return '--'
try {
// 处理iOS兼容性问题将 "yyyy-MM-dd HH:mm:ss" 转换为 "yyyy/MM/dd HH:mm:ss"
let normalizedTimeStr = timeStr
if (typeof timeStr === 'string' && timeStr.includes('-') && timeStr.includes(' ')) {
// 将 "2027-08-22 17:17:01" 转换为 "2027/08/22 17:17:01"
normalizedTimeStr = timeStr.replace(/-/g, '/')
}
const date = new Date(normalizedTimeStr)
// 检查日期是否有效
if (isNaN(date.getTime())) {
return timeStr
}
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
} catch (error) {
console.error('时间格式化错误:', error, '原始时间:', timeStr)
return timeStr
}
}
/**
* 加载订单详情
*/
const loadOrderDetail = async () => {
if (!orderId.value) return
try {
loading.value = true
const res = await getOrderDetail(orderId.value)
if (res.code === 200 && res.data) {
orderDetail.value = res.data
console.log('订单详情:', orderDetail.value)
} else {
throw new Error(res.msg || '获取订单详情失败')
}
} catch (error) {
console.error('获取订单详情失败:', error)
uni.showToast({
title: error.message || '获取订单详情失败',
icon: 'none',
})
orderDetail.value = null
} finally {
loading.value = false
}
}
</script>
<template>
<view class="container">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<text class="loading-text">正在加载订单详情...</text>
</view>
<!-- 订单详情内容 -->
<view v-else-if="orderDetail" class="detail-content">
<!-- 订单状态卡片 -->
<view class="status-card">
<view class="status-header">
<text class="status-title">订单状态</text>
<text :class="getStatusClass(orderDetail.status)" class="status-value">
{{ mapOrderStatus('status', orderDetail.status) }}
</text>
</view>
<view class="status-info">
<text class="order-number">订单号:{{ orderDetail.orderNumber }}</text>
<text class="create-time">创建时间:{{ formatTime(orderDetail.createTime) }}</text>
</view>
</view>
<!-- 设备信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">设备信息</text>
</view>
<view class="card-content">
<view class="info-item">
<text class="label">设备类型:</text>
<text class="value">{{ orderDetail.typeName }}</text>
</view>
<view class="info-item">
<text class="label">租赁套餐:</text>
<text class="value">{{ orderDetail.suitName }}</text>
</view>
<view class="info-item">
<text class="label">租赁天数:</text>
<text class="value">{{ orderDetail.suitDay }}天</text>
</view>
<view class="info-item">
<text class="label">租赁时间:</text>
<text class="value">{{ formatTime(orderDetail.leaseTime) }}</text>
</view>
<view class="info-item">
<text class="label">到期时间:</text>
<text class="value">{{ formatTime(orderDetail.expirationTime) }}</text>
</view>
</view>
</view>
<!-- 用户信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">用户信息</text>
</view>
<view class="card-content">
<view class="info-item">
<text class="label">用户姓名:</text>
<text class="value">{{ orderDetail.name || '--' }}</text>
</view>
<view class="info-item">
<text class="label">联系电话:</text>
<text class="value">{{ orderDetail.phone || '--' }}</text>
</view>
<view class="info-item">
<text class="label">安装地址:</text>
<text class="value">{{ orderDetail.address || '--' }}</text>
</view>
<view v-if="orderDetail.detailed" class="info-item">
<text class="label">详细地址:</text>
<text class="value">{{ orderDetail.detailed }}</text>
</view>
</view>
</view>
<!-- 支付信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">支付信息</text>
</view>
<view class="card-content">
<view class="info-item">
<text class="label">订单金额:</text>
<text class="value amount">¥{{ orderDetail.amount?.toFixed(2) || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">支付状态:</text>
<text :class="getStatusClass(orderDetail.status)" class="value">
{{ mapOrderStatus('status', orderDetail.status) }}
</text>
</view>
<view class="info-item">
<text class="label">物流状态:</text>
<text :class="getLogisticClass(orderDetail.logisticStatus)" class="value">
{{ mapOrderStatus('logistic', orderDetail.logisticStatus) }}
</text>
</view>
<view v-if="orderDetail.payId" class="info-item">
<text class="label">支付单号:</text>
<text class="value">{{ orderDetail.payId }}</text>
</view>
</view>
</view>
<!-- 其他信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">其他信息</text>
</view>
<view class="card-content">
<view class="info-item">
<text class="label">应用名称:</text>
<text class="value">{{ orderDetail.appName || '--' }}</text>
</view>
<view class="info-item">
<text class="label">是否续租:</text>
<text class="value">{{ orderDetail.renew ? '是' : '否' }}</text>
</view>
<view v-if="orderDetail.remark" class="info-item">
<text class="label">备注信息:</text>
<text class="value">{{ orderDetail.remark }}</text>
</view>
</view>
</view>
</view>
<!-- 错误状态 -->
<view v-else class="error-container">
<view class="error-icon">❌</view>
<text class="error-text">订单详情加载失败</text>
<button class="retry-btn" @click="loadOrderDetail">重新加载</button>
</view>
</view>
</template>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20rpx;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 400rpx;
.loading-text {
font-size: 28rpx;
color: #666;
}
}
.error-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 400rpx;
.error-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.error-text {
font-size: 28rpx;
color: #999;
margin-bottom: 30rpx;
}
.retry-btn {
background-color: #007aff;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 20rpx 40rpx;
font-size: 28rpx;
}
}
.detail-content {
.status-card {
background: linear-gradient(135deg, #f15a04 0%, #f15a04 100%);
border-radius: 16rpx;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
color: #fff;
.status-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.status-title {
font-size: 32rpx;
font-weight: 600;
}
.status-value {
font-size: 26rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
background-color: rgba(255, 255, 255, 0.2);
font-weight: 500;
&.status-pending {
background-color: rgba(250, 140, 22, 0.8);
}
&.status-success {
background-color: rgba(82, 196, 26, 0.8);
}
&.status-refund {
background-color: rgba(255, 77, 79, 0.8);
}
}
}
.status-info {
.order-number {
display: block;
font-size: 26rpx;
margin-bottom: 10rpx;
opacity: 0.9;
}
.create-time {
display: block;
font-size: 24rpx;
opacity: 0.8;
}
}
}
.info-card {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
.card-title {
background-color: #f8f9fa;
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.title-text {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
}
.card-content {
padding: 30rpx;
.info-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 28rpx;
color: #666;
min-width: 160rpx;
flex-shrink: 0;
}
.value {
font-size: 28rpx;
color: #333;
flex: 1;
text-align: right;
word-break: break-all;
&.amount {
font-size: 32rpx;
color: #f15a04;
font-weight: bold;
}
&.status-pending {
color: #fa8c16;
}
&.status-success {
color: #52c41a;
}
&.status-refund {
color: #ff4d4f;
}
&.logistic-pending {
color: #fa8c16;
}
&.logistic-success {
color: #52c41a;
}
&.logistic-returned {
color: #1890ff;
}
}
}
}
}
}
</style>