智慧教务系统
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.
 
 
 
 
 
 

456 lines
10 KiB

<!--订单列表内容组件-->
<template>
<view class="order-list-card">
<!-- 操作按钮区域 -->
<view class="action-header" v-if="orderList && orderList.length > 0">
<view class="add-order-btn" @click.stop="handleAddOrder">
<text class="add-icon">+</text>
<text class="add-text">新增订单</text>
</view>
</view>
<!-- 订单列表 -->
<scroll-view
class="order-list"
v-if="orderList && orderList.length > 0"
scroll-y="true"
:enhanced="true"
style="height: 70vh;"
>
<view
class="order-item"
v-for="(order, index) in orderList"
:key="order && order.id ? order.id : `order-${index}`"
:class="{ 'pending-payment': order && order.status === 'pending' }"
@click.stop="order ? handleOrderClick(order) : null"
v-if="order"
>
<view class="order-header">
<view class="order-info">
<view class="order-no">订单号:{{ order.order_no || 'N/A' }}</view>
<view class="order-time" v-if="order.create_time">
{{ formatTime(order.create_time) }}
</view>
</view>
<view :class="['order-status',getStatusClass(order && order.status)]">
{{ getStatusText(order && order.status) }}
</view>
</view>
<!-- 订单内容 -->
<view class="order-content">
<view class="product-info" v-if="order.product_name">
<view class="product-name">{{ order.product_name }}</view>
<view class="product-specs" v-if="order.product_specs">
{{ order.product_specs }}
</view>
</view>
<view class="order-details">
<!-- 价格信息 -->
<view class="detail-row" v-if="order.total_amount">
<text class="detail-label">订单金额:</text>
<text class="detail-value price">¥{{ order.total_amount }}</text>
</view>
<view class="detail-row" v-if="order.paid_amount !== undefined">
<text class="detail-label">已付金额:</text>
<text class="detail-value paid">¥{{ order.paid_amount }}</text>
</view>
<view class="detail-row" v-if="order.unpaid_amount !== undefined">
<text class="detail-label">未付金额:</text>
<text class="detail-value unpaid">¥{{ order.unpaid_amount }}</text>
</view>
<!-- 其他信息 -->
<view class="detail-row" v-if="order.payment_method">
<text class="detail-label">支付方式:</text>
<text class="detail-value">{{ order.payment_method }}</text>
</view>
<view class="detail-row" v-if="order.salesperson_name">
<text class="detail-label">销售顾问:</text>
<text class="detail-value">{{ order.salesperson_name }}</text>
</view>
<view class="detail-row" v-if="order.course_count">
<text class="detail-label">课时数量:</text>
<text class="detail-value">{{ order.course_count }}节</text>
</view>
</view>
<!-- 备注信息 -->
<view class="order-remark">
<view class="remark-label">备注:</view>
<view class="remark-content">{{ order.remark }}</view>
</view>
</view>
</view>
</scroll-view>
<!-- 空状态 -->
<view class="empty-state" v-else>
<view class="empty-icon">📋</view>
<view class="empty-text">暂无订单记录</view>
<view class="empty-tip">客户还未产生任何订单</view>
<view class="empty-add-btn" @click.stop="handleAddOrder">
<text>新增订单</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'OrderListCard',
props: {
// 订单列表数据
orderList: {
type: Array,
default: () => []
}
},
methods: {
// 查看订单详情
viewOrderDetail(order) {
this.$emit('view-detail', order)
},
// 新增订单
handleAddOrder() {
this.$emit('add-order')
},
// 订单点击处理(根据状态决定是查看详情还是支付)
handleOrderClick(order) {
console.log('点击订单:', order)
// 防护检查:确保order对象存在
if (!order) {
console.warn('订单数据不存在,无法处理点击事件')
return
}
if (order.status === 'pending') {
// 未支付订单,触发支付
this.$emit('pay-order', order)
} else {
// 已支付订单,查看详情
this.$emit('view-detail', order)
}
},
// 获取状态样式类
getStatusClass(status) {
if (!status) return 'status-default'
const statusMap = {
'pending': 'status-pending',
'paid': 'status-paid',
'partial': 'status-partial',
'cancelled': 'status-cancelled',
'completed': 'status-completed',
'refunded': 'status-refunded'
}
return statusMap[status] || 'status-default'
},
// 获取状态文本
getStatusText(status) {
if (!status) return '未知状态'
const statusMap = {
'pending': '待支付',
'paid': '已支付',
'partial': '部分支付',
'cancelled': '已取消',
'completed': '已完成',
'refunded': '已退款'
}
return statusMap[status] || '未知状态'
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return ''
try {
const date = new Date(timeStr)
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')}`
} catch (e) {
return timeStr
}
}
}
}
</script>
<style lang="scss" scoped>
.order-list-card {
padding: 0;
}
.order-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.order-item {
background: #3A3A3A;
border-radius: 16rpx;
padding: 32rpx;
border: 1px solid #404040;
transition: all 0.3s ease;
position: relative;
&:active {
background: #4A4A4A;
}
&.pending-payment {
border-color: #FFC107;
background: rgba(255, 193, 7, 0.05);
&::after {
position: absolute;
bottom: 16rpx;
right: 16rpx;
background: #FFC107;
color: #000000;
font-size: 24rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
z-index: 10;
}
}
}
.order-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24rpx;
padding-bottom: 20rpx;
border-bottom: 1px solid #404040;
}
.order-info {
flex: 1;
}
.order-no {
font-size: 30rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.order-time {
font-size: 24rpx;
color: #999999;
}
.order-status {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
&.status-pending {
background: rgba(255, 193, 7, 0.2);
color: #FFC107;
}
&.status-paid {
background: rgba(76, 175, 80, 0.2);
color: #4CAF50;
}
&.status-partial {
background: rgba(255, 152, 0, 0.2);
color: #FF9800;
}
&.status-cancelled {
background: rgba(158, 158, 158, 0.2);
color: #9E9E9E;
}
&.status-completed {
background: rgba(41, 211, 180, 0.2);
color: #29D3B4;
}
&.status-refunded {
background: rgba(244, 67, 54, 0.2);
color: #F44336;
}
&.status-default {
background: rgba(158, 158, 158, 0.2);
color: #9E9E9E;
}
}
.order-content {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.product-info {
padding: 20rpx;
background: rgba(41, 211, 180, 0.05);
border-radius: 12rpx;
border-left: 4rpx solid #29D3B4;
}
.product-name {
font-size: 28rpx;
font-weight: 600;
color: #ffffff;
margin-bottom: 8rpx;
}
.product-specs {
font-size: 24rpx;
color: #cccccc;
}
.order-details {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.detail-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.detail-label {
font-size: 26rpx;
color: #999999;
min-width: 140rpx;
}
.detail-value {
font-size: 26rpx;
color: #ffffff;
flex: 1;
text-align: right;
&.price {
color: #FFC107;
font-weight: 600;
font-size: 28rpx;
}
&.paid {
color: #4CAF50;
font-weight: 600;
}
&.unpaid {
color: #F44336;
font-weight: 600;
}
}
.order-remark {
padding: 20rpx;
background: rgba(255, 255, 255, 0.05);
border-radius: 12rpx;
}
.remark-label {
font-size: 24rpx;
color: #999999;
margin-bottom: 12rpx;
}
.remark-content {
font-size: 26rpx;
color: #cccccc;
line-height: 1.5;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
text-align: center;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 32rpx;
opacity: 0.6;
}
.empty-text {
font-size: 32rpx;
color: #ffffff;
margin-bottom: 16rpx;
font-weight: 500;
}
.empty-tip {
font-size: 26rpx;
color: #999999;
line-height: 1.4;
margin-bottom: 32rpx;
}
.empty-add-btn {
background: #29D3B4;
border-radius: 30rpx;
padding: 16rpx 40rpx;
color: #ffffff;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
&:active {
background: #1fb396;
}
}
.action-header {
display: flex;
justify-content: flex-end;
margin-bottom: 24rpx;
}
.add-order-btn {
display: flex;
align-items: center;
background: #29D3B4;
border-radius: 30rpx;
padding: 12rpx 24rpx;
color: #ffffff;
font-size: 26rpx;
font-weight: 500;
transition: all 0.3s ease;
&:active {
background: #1fb396;
}
}
.add-icon {
font-size: 32rpx;
font-weight: bold;
margin-right: 8rpx;
line-height: 1;
}
.add-text {
font-size: 26rpx;
}
</style>