569 lines
15 KiB
Vue
569 lines
15 KiB
Vue
<template>
|
||
<view class="order-detail-page">
|
||
<!-- 自定义导航栏 -->
|
||
<view class="custom-nav safe-area-top">
|
||
<view class="nav-content">
|
||
<view class="nav-btn" @click="goBack">
|
||
<uni-icons type="left" size="22" color="#1E293B"></uni-icons>
|
||
</view>
|
||
<text class="nav-title">订单详情</text>
|
||
<view class="nav-btn"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="page-content" v-if="order">
|
||
<!-- 订单状态 -->
|
||
<view class="status-section" :class="order.status">
|
||
<view class="status-icon">
|
||
<uni-icons :type="statusIcon" size="48" color="#FFFFFF"></uni-icons>
|
||
</view>
|
||
<text class="status-text">{{statusText}}</text>
|
||
<text class="status-desc">{{statusDesc}}</text>
|
||
</view>
|
||
|
||
<!-- 订单信息 -->
|
||
<view class="section-card">
|
||
<view class="section-title">订单信息</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="info-label">订单编号</text>
|
||
<text class="info-value">{{order.orderNo}}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">创建时间</text>
|
||
<text class="info-value">{{formatDateTime(order.createTime)}}</text>
|
||
</view>
|
||
<view class="info-item" v-if="order.payTime">
|
||
<text class="info-label">支付时间</text>
|
||
<text class="info-value">{{formatDateTime(order.payTime)}}</text>
|
||
</view>
|
||
<view class="info-item" v-if="order.cancelTime">
|
||
<text class="info-label">取消时间</text>
|
||
<text class="info-value">{{formatDateTime(order.cancelTime)}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 套餐信息 -->
|
||
<view class="section-card" v-if="order.subscriptionPlan">
|
||
<view class="section-title">套餐信息</view>
|
||
<view class="plan-card">
|
||
<text class="plan-name">{{order.subscriptionPlan.name}}</text>
|
||
<text class="plan-desc">{{order.subscriptionPlan.description}}</text>
|
||
<view class="plan-resources">
|
||
<view class="resource-item">
|
||
<uni-icons type="home-filled" size="16" color="#667eea"></uni-icons>
|
||
<text>{{order.subscriptionPlan.maxApartments}} 栋公寓</text>
|
||
</view>
|
||
<view class="resource-item">
|
||
<uni-icons type="shop-filled" size="16" color="#667eea"></uni-icons>
|
||
<text>{{order.subscriptionPlan.maxRooms}} 个房间</text>
|
||
</view>
|
||
<view class="resource-item">
|
||
<uni-icons type="person-filled" size="16" color="#667eea"></uni-icons>
|
||
<text>{{order.subscriptionPlan.maxUsers}} 个用户</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 费用明细 -->
|
||
<view class="section-card">
|
||
<view class="section-title">费用明细</view>
|
||
<view class="price-list">
|
||
<view class="price-row">
|
||
<text class="row-label">套餐单价</text>
|
||
<text class="row-value">¥{{order.unitPrice || order.subscriptionPlan?.monthlyPrice || 0}}/月</text>
|
||
</view>
|
||
<view class="price-row">
|
||
<text class="row-label">购买时长</text>
|
||
<text class="row-value">{{order.months}} 个月</text>
|
||
</view>
|
||
<view class="price-row">
|
||
<text class="row-label">基础费用</text>
|
||
<text class="row-value">¥{{order.amount}}</text>
|
||
</view>
|
||
<view class="price-row" v-if="order.discountAmount > 0">
|
||
<text class="row-label">优惠金额</text>
|
||
<text class="row-value discount">-¥{{order.discountAmount}}</text>
|
||
</view>
|
||
<view class="price-row total">
|
||
<text class="row-label">实付金额</text>
|
||
<text class="row-value total-price">¥{{order.actualAmount}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付信息 -->
|
||
<view class="section-card" v-if="order.status === 'paid'">
|
||
<view class="section-title">支付信息</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="info-label">支付方式</text>
|
||
<text class="info-value">{{getPaymentMethodText(order.paymentMethod)}}</text>
|
||
</view>
|
||
<view class="info-item" v-if="order.transactionId">
|
||
<text class="info-label">交易号</text>
|
||
<text class="info-value">{{order.transactionId}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 有效期 -->
|
||
<view class="section-card" v-if="order.status === 'paid' && order.startDate">
|
||
<view class="section-title">有效期</view>
|
||
<view class="validity-info">
|
||
<view class="validity-item">
|
||
<text class="validity-label">开始时间</text>
|
||
<text class="validity-value">{{formatDate(order.startDate)}}</text>
|
||
</view>
|
||
<view class="validity-arrow">
|
||
<uni-icons type="right" size="20" color="#94A3B8"></uni-icons>
|
||
</view>
|
||
<view class="validity-item">
|
||
<text class="validity-label">结束时间</text>
|
||
<text class="validity-value">{{formatDate(order.endDate)}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="safe-area-bottom" style="height: 140rpx;"></view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<view class="bottom-bar safe-area-bottom" v-if="order && order.status === 'pending'">
|
||
<button class="action-btn cancel" @click="cancelOrder">取消订单</button>
|
||
<button class="action-btn pay" @click="payOrder">立即支付</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { billingApi } from '@/api/index.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
orderId: null,
|
||
order: null
|
||
}
|
||
},
|
||
computed: {
|
||
statusIcon() {
|
||
const map = {
|
||
pending: 'info-filled',
|
||
paid: 'checkmarkempty',
|
||
cancelled: 'closeempty'
|
||
}
|
||
return map[this.order?.status] || 'info-filled'
|
||
},
|
||
statusText() {
|
||
const map = {
|
||
pending: '待支付',
|
||
paid: '已支付',
|
||
cancelled: '已取消'
|
||
}
|
||
return map[this.order?.status] || '未知状态'
|
||
},
|
||
statusDesc() {
|
||
const map = {
|
||
pending: '请在24小时内完成支付,否则订单将自动取消',
|
||
paid: '订单已支付成功,套餐已生效',
|
||
cancelled: '订单已取消,如有疑问请联系客服'
|
||
}
|
||
return map[this.order?.status] || ''
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
this.orderId = options.id
|
||
if (this.orderId) {
|
||
this.loadOrderDetail()
|
||
}
|
||
},
|
||
methods: {
|
||
async loadOrderDetail() {
|
||
try {
|
||
uni.showLoading({ title: '加载中...' })
|
||
const res = await billingApi.getOrderDetail(this.orderId)
|
||
uni.hideLoading()
|
||
|
||
if (res.code === 200) {
|
||
this.order = res.data
|
||
} else {
|
||
uni.showToast({ title: res.message || '加载失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('加载订单详情失败:', error)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
}
|
||
},
|
||
|
||
getPaymentMethodText(method) {
|
||
const map = {
|
||
alipay: '支付宝',
|
||
wechat: '微信支付',
|
||
bank: '银行转账'
|
||
}
|
||
return map[method] || method || '-'
|
||
},
|
||
|
||
formatDateTime(dateTime) {
|
||
if (!dateTime) return '-'
|
||
const date = new Date(dateTime)
|
||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||
},
|
||
|
||
formatDate(date) {
|
||
if (!date) return '-'
|
||
return new Date(date).toLocaleDateString('zh-CN')
|
||
},
|
||
|
||
cancelOrder() {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要取消该订单吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
uni.showLoading({ title: '取消中...' })
|
||
const result = await billingApi.cancelOrder(this.orderId)
|
||
uni.hideLoading()
|
||
|
||
if (result.code === 200) {
|
||
uni.showToast({ title: '已取消', icon: 'success' })
|
||
this.loadOrderDetail()
|
||
} else {
|
||
uni.showToast({ title: result.message || '取消失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('取消订单失败:', error)
|
||
uni.showToast({ title: '取消失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
payOrder() {
|
||
uni.showModal({
|
||
title: '支付确认',
|
||
content: `确认支付订单 #${this.order.orderNo},金额 ¥${this.order.actualAmount}?`,
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
uni.showLoading({ title: '支付中...' })
|
||
const result = await billingApi.payOrder(this.orderId)
|
||
uni.hideLoading()
|
||
|
||
if (result.code === 200) {
|
||
uni.showToast({
|
||
title: '支付成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
this.loadOrderDetail()
|
||
} else {
|
||
uni.showToast({ title: result.message || '支付失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('支付失败:', error)
|
||
uni.showToast({ title: '支付失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.order-detail-page {
|
||
min-height: 100vh;
|
||
background: #F8FAFC;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 导航栏 */
|
||
.custom-nav {
|
||
background: #FFFFFF;
|
||
border-bottom: 2rpx solid #F1F5F9;
|
||
}
|
||
|
||
.nav-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 20rpx 32rpx;
|
||
}
|
||
|
||
.nav-btn {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #1E293B;
|
||
}
|
||
|
||
/* 页面内容 */
|
||
.page-content {
|
||
flex: 1;
|
||
padding: 24rpx 32rpx;
|
||
}
|
||
|
||
/* 状态区域 */
|
||
.status-section {
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 48rpx;
|
||
margin-bottom: 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
}
|
||
|
||
.status-section.pending {
|
||
background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
|
||
}
|
||
|
||
.status-section.paid {
|
||
background: linear-gradient(135deg, #D1FAE5 0%, #A7F3D0 100%);
|
||
}
|
||
|
||
.status-section.cancelled {
|
||
background: linear-gradient(135deg, #F3F4F6 0%, #E5E7EB 100%);
|
||
}
|
||
|
||
.status-icon {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.status-section.pending .status-icon {
|
||
background: #F59E0B;
|
||
}
|
||
|
||
.status-section.paid .status-icon {
|
||
background: #10B981;
|
||
}
|
||
|
||
.status-section.cancelled .status-icon {
|
||
background: #9CA3AF;
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.status-desc {
|
||
font-size: 26rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
/* 区块卡片 */
|
||
.section-card {
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 28rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
/* 信息列表 */
|
||
.info-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 28rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 28rpx;
|
||
color: #1E293B;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 套餐卡片 */
|
||
.plan-card {
|
||
background: #F8FAFC;
|
||
border-radius: 16rpx;
|
||
padding: 28rpx;
|
||
}
|
||
|
||
.plan-name {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.plan-desc {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #64748B;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.plan-resources {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.resource-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
font-size: 26rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
/* 费用明细 */
|
||
.price-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.price-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.price-row.total {
|
||
margin-top: 20rpx;
|
||
padding-top: 20rpx;
|
||
border-top: 2rpx solid #F1F5F9;
|
||
}
|
||
|
||
.row-label {
|
||
font-size: 28rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
.row-value {
|
||
font-size: 28rpx;
|
||
color: #1E293B;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.row-value.discount {
|
||
color: #67C23A;
|
||
}
|
||
|
||
.row-value.total-price {
|
||
font-size: 40rpx;
|
||
color: #F56C6C;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* 有效期 */
|
||
.validity-info {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.validity-item {
|
||
flex: 1;
|
||
background: #F8FAFC;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.validity-label {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
color: #64748B;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.validity-value {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #1E293B;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.validity-arrow {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: #FFFFFF;
|
||
border-top: 2rpx solid #F1F5F9;
|
||
padding: 24rpx 32rpx;
|
||
display: flex;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
padding: 28rpx;
|
||
border-radius: 16rpx;
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
border: none;
|
||
}
|
||
|
||
.action-btn.cancel {
|
||
background: #F3F4F6;
|
||
color: #64748B;
|
||
}
|
||
|
||
.action-btn.pay {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #FFFFFF;
|
||
}
|
||
</style>
|