智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

807 lines
19 KiB

<!--学员订单详情页面-->
<template>
<view class="main_box">
<!-- 自定义导航栏 -->
<view class="navbar_section">
<view class="navbar_back" @click="goBack">
<text class="back_icon"></text>
</view>
<view class="navbar_title">订单详情</view>
<view class="navbar_action"></view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading_section">
<view class="loading_text">加载中...</view>
</view>
<!-- 订单详情内容 -->
<view v-else-if="orderDetail" class="detail_content">
<!-- 订单状态 -->
<view class="status_section">
<view class="status_icon" :class="orderDetail.status">
<text class="icon_text">{{ getStatusIcon(orderDetail.status) }}</text>
</view>
<view class="status_info">
<view class="status_text">{{ getStatusText(orderDetail.status) }}</view>
<view class="status_desc">{{ getStatusDesc(orderDetail.status) }}</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order_info_section">
<view class="section_title">订单信息</view>
<view class="info_list">
<view class="info_item">
<text class="label">订单号:</text>
<text class="value">{{ orderDetail.order_no }}</text>
</view>
<view class="info_item">
<text class="label">下单时间:</text>
<text class="value">{{ formatDateTime(orderDetail.create_time) }}</text>
</view>
<view class="info_item" v-if="orderDetail.payment_time">
<text class="label">支付时间:</text>
<text class="value">{{ formatDateTime(orderDetail.payment_time) }}</text>
</view>
<view class="info_item">
<text class="label">订单金额:</text>
<text class="value amount">¥{{ orderDetail.total_amount }}</text>
</view>
<view class="info_item" v-if="orderDetail.payment_method">
<text class="label">支付方式:</text>
<text class="value">{{ orderDetail.payment_method }}</text>
</view>
</view>
</view>
<!-- 课程信息 -->
<view class="course_info_section">
<view class="section_title">课程信息</view>
<view class="course_card">
<view class="course_header">
<view class="course_name">{{ orderDetail.product_name }}</view>
<view class="course_amount">¥{{ orderDetail.total_amount }}</view>
</view>
<view class="course_details">
<view class="detail_item" v-if="orderDetail.product_specs">
<text class="detail_label">课程规格:</text>
<text class="detail_value">{{ orderDetail.product_specs }}</text>
</view>
<view class="detail_item">
<text class="detail_label">购买数量:</text>
<text class="detail_value">{{ orderDetail.quantity }}节</text>
</view>
<view class="detail_item" v-if="orderDetail.course_count">
<text class="detail_label">总课时:</text>
<text class="detail_value">{{ orderDetail.course_count }}节</text>
</view>
<view class="detail_item" v-if="orderDetail.used_count !== undefined">
<text class="detail_label">已使用:</text>
<text class="detail_value">{{ orderDetail.used_count }}节</text>
</view>
<view class="detail_item" v-if="orderDetail.remaining_count !== undefined">
<text class="detail_label">剩余课时:</text>
<text class="detail_value">{{ orderDetail.remaining_count }}节</text>
</view>
</view>
</view>
</view>
<!-- 备注信息 -->
<view class="remark_section" v-if="orderDetail.remark">
<view class="section_title">备注信息</view>
<view class="remark_text">{{ orderDetail.remark }}</view>
</view>
<!-- 退款信息 -->
<view class="refund_section" v-if="showRefundInfo">
<view class="section_title">退款信息</view>
<view class="refund_info">
<view class="info_item">
<text class="label">退款金额:</text>
<text class="value amount">¥{{ orderDetail.refund_amount || orderDetail.total_amount }}</text>
</view>
<view class="info_item" v-if="orderDetail.refund_time">
<text class="label">退款时间:</text>
<text class="value">{{ formatDateTime(orderDetail.refund_time) }}</text>
</view>
<view class="info_item" v-if="orderDetail.cancel_reason">
<text class="label">退款原因:</text>
<text class="value">{{ orderDetail.cancel_reason }}</text>
</view>
</view>
</view>
</view>
<!-- 订单不存在 -->
<view v-else class="empty_section">
<view class="empty_icon">📋</view>
<view class="empty_text">订单不存在</view>
<view class="empty_hint">请检查订单信息或联系客服</view>
</view>
<!-- 操作按钮 -->
<view class="action_section" v-if="orderDetail">
<fui-button
v-if="orderDetail.status === 'pending_payment'"
background="#29d3b4"
@click="payOrder"
>
立即付款
</fui-button>
<fui-button
v-if="orderDetail.status === 'pending_payment'"
background="transparent"
color="#999"
@click="cancelOrder"
>
取消订单
</fui-button>
<fui-button
v-if="canViewContract"
background="#f39c12"
@click="viewContract"
>
查看合同
</fui-button>
<fui-button
v-if="orderDetail.status === 'refunding' || orderDetail.status === 'refunded'"
background="transparent"
color="#f39c12"
@click="viewRefundDetail"
>
查看退款
</fui-button>
</view>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js'
export default {
data() {
return {
orderId: 0,
studentId: 0,
orderDetail: null,
loading: false,
// 状态文本映射
STATUS_TEXT_MAP: {
'pending_payment': '待付款',
'paid': '已付款',
'completed': '已完成',
'cancelled': '已取消',
'refunding': '退款中',
'refunded': '已退款'
},
// 状态描述映射
STATUS_DESC_MAP: {
'pending_payment': '请在规定时间内完成支付',
'paid': '订单已支付成功',
'completed': '订单已完成,感谢您的使用',
'cancelled': '订单已取消',
'refunding': '退款处理中,请耐心等待',
'refunded': '退款已完成'
}
}
},
computed: {
showRefundInfo() {
return this.orderDetail && (this.orderDetail.status === 'refunding' || this.orderDetail.status === 'refunded')
},
canViewContract() {
// 已支付的订单可以查看合同
return this.orderDetail && (this.orderDetail.status === 'paid' || this.orderDetail.status === 'completed')
}
},
onLoad(options) {
console.log('订单详情页面接收参数:', options)
this.orderId = parseInt(options.id) || 0
this.studentId = parseInt(options.student_id) || 0
// 如果没有学员ID,从本地存储获取
if (!this.studentId) {
const userInfo = uni.getStorageSync('userInfo')
if (userInfo && userInfo.id) {
this.studentId = userInfo.id
}
}
console.log('订单详情页面参数确认:', {
orderId: this.orderId,
studentId: this.studentId
})
if (this.orderId && this.studentId) {
// 延迟加载确保页面完全显示
setTimeout(() => {
this.loadOrderDetail()
}, 100)
} else {
console.error('订单详情页面参数错误:', {
orderId: this.orderId,
studentId: this.studentId
})
uni.showToast({
title: '参数错误',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
},
onShow() {
console.log('订单详情页面显示')
},
onReady() {
console.log('订单详情页面准备完成')
},
methods: {
goBack() {
uni.navigateBack()
},
async loadOrderDetail() {
this.loading = true
try {
console.log('获取订单详情,参数:', {
id: this.orderId,
student_id: this.studentId
});
const response = await apiRoute.xy_getStudentOrderDetail({
id: this.orderId,
student_id: this.studentId
})
if (response.code === 1 && response.data) {
this.orderDetail = this.processOrderData(response.data)
} else {
// 如果接口返回失败,尝试从订单列表接口获取数据
console.warn('订单详情接口失败,尝试从列表接口获取:', response.msg)
await this.loadOrderFromList()
}
} catch (error) {
console.error('获取订单详情失败:', error)
// 接口调用失败,尝试从订单列表接口获取数据
await this.loadOrderFromList()
} finally {
this.loading = false
}
},
// 从订单列表接口获取订单数据作为备用
async loadOrderFromList() {
try {
const response = await apiRoute.xy_getStudentOrders({
student_id: this.studentId,
page: 1,
limit: 100
})
if (response.code === 1 && response.data && response.data.data) {
const orders = response.data.data
const targetOrder = orders.find(order => order.id == this.orderId)
if (targetOrder) {
this.orderDetail = this.processOrderData(targetOrder)
} else {
this.showError('订单不存在')
}
} else {
this.showError('获取订单信息失败')
}
} catch (error) {
console.error('从列表获取订单详情失败:', error)
this.showError('获取订单信息失败')
}
},
// 处理订单数据,将后端数据转换为前端需要的格式
processOrderData(rawData) {
return {
id: rawData.id,
order_no: rawData.order_no || rawData.order_number || rawData.payment_id,
product_name: rawData.course_id_name || rawData.course_name || rawData.product_name || '课程订单',
product_specs: rawData.course_specs || rawData.product_specs || '',
quantity: rawData.quantity || 1,
total_amount: rawData.order_amount || rawData.total_amount || rawData.amount || '0.00',
status: this.mapOrderStatus(rawData.order_status || rawData.status),
create_time: rawData.created_at || rawData.create_time,
payment_method: rawData.payment_type ? this.mapPaymentMethod(rawData.payment_type) : '',
payment_time: rawData.payment_time || rawData.paid_at,
refund_time: rawData.refund_time,
refund_amount: rawData.refund_amount,
// 课程相关字段
course_count: rawData.course_count || 0,
used_count: rawData.use_total_hours || rawData.used_count || 0,
remaining_count: rawData.remaining_count || 0,
cancel_reason: rawData.after_sales_reason || rawData.cancel_reason || '',
remark: rawData.remark || ''
}
},
// 映射订单状态
mapOrderStatus(status) {
const statusMap = {
'pending': 'pending_payment',
'paid': 'paid',
'signed': 'completed',
'completed': 'completed',
'transfer': 'cancelled',
'cancelled': 'cancelled',
'refunded': 'refunded',
'refunding': 'refunding'
}
return statusMap[status] || 'pending_payment'
},
// 映射支付方式
mapPaymentMethod(method) {
const methodMap = {
'cash': '现金支付',
'scan_code': '扫码支付',
'subscription': '预约支付',
'wxpay_online': '微信支付',
'wxpay': '微信支付',
'alipay': '支付宝',
'bank': '银行转账'
}
return methodMap[method] || method || ''
},
getStatusText(status) {
return this.STATUS_TEXT_MAP[status] || status
},
getStatusDesc(status) {
return this.STATUS_DESC_MAP[status] || ''
},
getStatusIcon(status) {
const iconMap = {
'pending_payment': '💰',
'paid': '✅',
'completed': '🎉',
'cancelled': '❌',
'refunding': '⏳',
'refunded': '↩️'
}
return iconMap[status] || '📋'
},
formatDateTime(dateString) {
if (!dateString) return ''
const date = new Date(dateString)
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}`
},
showError(message) {
uni.showToast({
title: message,
icon: 'none'
})
},
// 支付订单
payOrder() {
// 跳转回订单列表页面进行支付
uni.navigateBack()
},
// 取消订单
cancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
success: (res) => {
if (res.confirm) {
this.performCancelOrder()
}
}
})
},
async performCancelOrder() {
try {
uni.showLoading({ title: '处理中...' })
// 这里应该调用取消订单的API
// 暂时模拟处理
await new Promise(resolve => setTimeout(resolve, 1000))
uni.hideLoading()
uni.showToast({
title: '取消成功',
icon: 'success'
})
// 刷新订单详情
this.loadOrderDetail()
} catch (error) {
uni.hideLoading()
console.error('取消订单失败:', error)
uni.showToast({
title: '取消失败',
icon: 'none'
})
}
},
// 查看合同
async viewContract() {
try {
uni.showLoading({ title: '获取合同信息...' })
// 查询合同签署记录
const contractResponse = await this.getContractInfo()
uni.hideLoading()
if (contractResponse && contractResponse.contract_id) {
// 跳转到合同详情页面
uni.navigateTo({
url: `/pages-student/contract/detail?contract_id=${contractResponse.contract_id}&student_id=${this.studentId}`
})
} else {
uni.showModal({
title: '提示',
content: '该订单暂无关联合同,请联系客服处理',
showCancel: false
})
}
} catch (error) {
uni.hideLoading()
console.error('获取合同信息失败:', error)
uni.showToast({
title: '获取合同信息失败',
icon: 'none'
})
}
},
// 获取合同信息
async getContractInfo() {
try {
// 调用获取学员合同列表接口
const response = await apiRoute.getStudentContracts({
student_id: this.studentId,
order_id: this.orderId // 如果接口支持按订单筛选
})
if (response.code === 1 && response.data && response.data.length > 0) {
// 返回第一个合同记录
return response.data[0]
}
return null
} catch (error) {
console.error('获取合同信息失败:', error)
return null
}
},
// 查看退款详情
viewRefundDetail() {
const refundInfo = `订单号:${this.orderDetail.order_no}\n退款金额:¥${this.orderDetail.refund_amount || this.orderDetail.total_amount}\n退款时间:${this.orderDetail.refund_time ? this.formatDateTime(this.orderDetail.refund_time) : '处理中'}`
uni.showModal({
title: '退款详情',
content: refundInfo,
showCancel: false
})
}
}
}
</script>
<style lang="less" scoped>
.main_box {
background: #f8f9fa;
min-height: 100vh;
position: relative;
z-index: 1;
}
// 自定义导航栏
.navbar_section {
display: flex;
justify-content: space-between;
align-items: center;
background: #29D3B4;
padding: 40rpx 32rpx 20rpx;
// 小程序端适配状态栏
// #ifdef MP-WEIXIN
padding-top: 80rpx;
// #endif
.navbar_back {
width: 60rpx;
.back_icon {
color: #fff;
font-size: 40rpx;
font-weight: 600;
}
}
.navbar_title {
color: #fff;
font-size: 32rpx;
font-weight: 600;
}
.navbar_action {
width: 60rpx;
}
}
// 加载状态
.loading_section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 80rpx 32rpx;
text-align: center;
.loading_text {
font-size: 28rpx;
color: #666;
}
}
// 订单详情内容
.detail_content {
padding: 0 20rpx 120rpx;
}
// 订单状态
.status_section {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 32rpx;
margin: 20rpx 0;
display: flex;
align-items: center;
.status_icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
.icon_text {
font-size: 32rpx;
}
&.pending_payment {
background: rgba(243, 156, 18, 0.1);
}
&.paid {
background: rgba(52, 152, 219, 0.1);
}
&.completed {
background: rgba(39, 174, 96, 0.1);
}
&.cancelled {
background: rgba(149, 165, 166, 0.1);
}
&.refunded, &.refunding {
background: rgba(231, 76, 60, 0.1);
}
}
.status_info {
flex: 1;
.status_text {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.status_desc {
font-size: 24rpx;
color: #666;
}
}
}
// 通用区块样式
.order_info_section, .course_info_section, .remark_section, .refund_section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin: 20rpx 0;
.section_title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
}
// 信息列表
.info_list {
.info_item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
min-width: 160rpx;
}
.value {
font-size: 26rpx;
color: #333;
flex: 1;
&.amount {
color: #e74c3c;
font-weight: 600;
}
}
}
}
// 课程卡片
.course_card {
.course_header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
.course_name {
flex: 1;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-right: 20rpx;
}
.course_amount {
font-size: 32rpx;
font-weight: 600;
color: #e74c3c;
}
}
.course_details {
.detail_item {
display: flex;
align-items: center;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.detail_label {
font-size: 24rpx;
color: #666;
min-width: 140rpx;
}
.detail_value {
font-size: 24rpx;
color: #333;
}
}
}
}
// 备注文本
.remark_text {
font-size: 26rpx;
color: #666;
line-height: 1.6;
}
// 退款信息
.refund_info {
.info_item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
min-width: 160rpx;
}
.value {
font-size: 26rpx;
color: #333;
flex: 1;
&.amount {
color: #e74c3c;
font-weight: 600;
}
}
}
}
// 空状态
.empty_section {
background: #fff;
border-radius: 16rpx;
padding: 80rpx 32rpx;
text-align: center;
margin: 20rpx;
.empty_icon {
font-size: 80rpx;
margin-bottom: 24rpx;
}
.empty_text {
font-size: 28rpx;
color: #666;
margin-bottom: 12rpx;
}
.empty_hint {
font-size: 24rpx;
color: #999;
}
}
// 操作按钮
.action_section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 20rpx 32rpx;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.1);
display: flex;
gap: 16rpx;
fui-button {
flex: 1;
}
}
</style>