373 lines
7.4 KiB
Vue
373 lines
7.4 KiB
Vue
<template>
|
|
<view class="page">
|
|
<!-- 筛选栏 -->
|
|
<view class="filter-bar">
|
|
<view
|
|
class="filter-item"
|
|
:class="{ active: status === null }"
|
|
@click="filterStatus(null)"
|
|
>
|
|
全部
|
|
</view>
|
|
<view
|
|
class="filter-item"
|
|
:class="{ active: status === 1 }"
|
|
@click="filterStatus(1)"
|
|
>
|
|
已完成
|
|
</view>
|
|
<view
|
|
class="filter-item"
|
|
:class="{ active: status === 2 }"
|
|
@click="filterStatus(2)"
|
|
>
|
|
已取消
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 订单列表 -->
|
|
<view class="order-list">
|
|
<view
|
|
v-for="order in orders"
|
|
:key="order.orderId"
|
|
class="order-card"
|
|
@click="viewDetail(order)"
|
|
>
|
|
<view class="order-header">
|
|
<text class="order-no">{{ order.orderNo }}</text>
|
|
<text class="order-status" :class="getStatusClass(order.status)">
|
|
{{ getStatusText(order.status) }}
|
|
</text>
|
|
</view>
|
|
|
|
<view class="order-customer">
|
|
<text class="customer-name">{{ order.customerName || '散客' }}</text>
|
|
<text class="customer-phone">{{ order.customerPhone || '-' }}</text>
|
|
</view>
|
|
|
|
<view class="order-items">
|
|
<text class="items-label">商品明细</text>
|
|
<text class="items-count">{{ getItemCount(order.orderId) }}种商品</text>
|
|
</view>
|
|
|
|
<view class="order-amount">
|
|
<view class="amount-row">
|
|
<text class="amount-label">原价</text>
|
|
<text class="amount-value">¥{{ order.totalAmount }}</text>
|
|
</view>
|
|
<view class="amount-row">
|
|
<text class="amount-label">优惠</text>
|
|
<text class="amount-value discount">-¥{{ order.discountAmount }}</text>
|
|
</view>
|
|
<view class="amount-row actual">
|
|
<text class="amount-label">实付</text>
|
|
<text class="amount-value">¥{{ order.actualAmount }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="order-footer">
|
|
<text class="order-time">{{ formatTime(order.createdAt) }}</text>
|
|
<text class="operator">{{ order.operatorName }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="orders.length === 0" class="empty">
|
|
<text>暂无订单</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 加载更多 -->
|
|
<view v-if="orders.length > 0" class="load-more">
|
|
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import orderApi from '@/api/order'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
status: null,
|
|
orders: [],
|
|
orderItemsMap: {},
|
|
page: 1,
|
|
pageSize: 20,
|
|
hasMore: true,
|
|
loading: false
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.loadOrders()
|
|
},
|
|
onReachBottom() {
|
|
if (this.hasMore && !this.loading) {
|
|
this.page++
|
|
this.loadOrders()
|
|
}
|
|
},
|
|
onPullDownRefresh() {
|
|
this.page = 1
|
|
this.loadOrders().then(() => {
|
|
uni.stopPullDownRefresh()
|
|
})
|
|
},
|
|
methods: {
|
|
async loadOrders() {
|
|
if (this.loading) return
|
|
this.loading = true
|
|
|
|
try {
|
|
const res = await orderApi.getOrders({
|
|
status: this.status,
|
|
page: this.page,
|
|
pageSize: this.pageSize
|
|
})
|
|
|
|
const list = res.records || []
|
|
|
|
// 加载每个订单的明细
|
|
for (const order of list) {
|
|
try {
|
|
const detail = await orderApi.getOrderDetail(order.orderId)
|
|
this.$set(this.orderItemsMap, order.orderId, detail.items || [])
|
|
} catch (e) {
|
|
console.error(e)
|
|
}
|
|
}
|
|
|
|
if (this.page === 1) {
|
|
this.orders = list
|
|
} else {
|
|
this.orders = [...this.orders, ...list]
|
|
}
|
|
this.hasMore = list.length >= this.pageSize
|
|
} catch (e) {
|
|
console.error(e)
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
filterStatus(status) {
|
|
this.status = status
|
|
this.page = 1
|
|
this.loadOrders()
|
|
},
|
|
viewDetail(order) {
|
|
// 跳转到订单详情页或显示详情弹窗
|
|
uni.showModal({
|
|
title: '订单详情',
|
|
content: `订单号: ${order.orderNo}\n原价: ¥${order.totalAmount}\n优惠: ¥${order.discountAmount}\n实付: ¥${order.actualAmount}`,
|
|
showCancel: false
|
|
})
|
|
},
|
|
getItemCount(orderId) {
|
|
const items = this.orderItemsMap[orderId]
|
|
return items ? items.length : 0
|
|
},
|
|
getStatusClass(status) {
|
|
const map = {
|
|
1: 'status-success',
|
|
2: 'status-cancel',
|
|
3: 'status-refunding',
|
|
4: 'status-refunded'
|
|
}
|
|
return map[status] || ''
|
|
},
|
|
getStatusText(status) {
|
|
const map = {
|
|
1: '已完成',
|
|
2: '已取消',
|
|
3: '退款中',
|
|
4: '已退款'
|
|
}
|
|
return map[status] || '未知'
|
|
},
|
|
formatTime(time) {
|
|
if (!time) return '-'
|
|
return time.substring(0, 16).replace('T', ' ')
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.page {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.filter-bar {
|
|
display: flex;
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 16rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.filter-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 12rpx;
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.filter-item.active {
|
|
background: #3cc51f;
|
|
color: #fff;
|
|
}
|
|
|
|
.order-card {
|
|
background: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.order-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.order-no {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.order-status {
|
|
font-size: 24rpx;
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 4rpx;
|
|
}
|
|
|
|
.status-success {
|
|
background: #f6ffed;
|
|
color: #52c41a;
|
|
}
|
|
|
|
.status-cancel {
|
|
background: #fff1f0;
|
|
color: #ff4d4f;
|
|
}
|
|
|
|
.status-refunding {
|
|
background: #fff7e6;
|
|
color: #fa8c16;
|
|
}
|
|
|
|
.status-refunded {
|
|
background: #f9f0ff;
|
|
color: #722ed1;
|
|
}
|
|
|
|
.order-customer {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.customer-name {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.customer-phone {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.order-items {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 16rpx 0;
|
|
border-top: 1rpx solid #f5f5f5;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.items-label {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.items-count {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.order-amount {
|
|
padding: 16rpx 0;
|
|
}
|
|
|
|
.amount-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 8rpx 0;
|
|
}
|
|
|
|
.amount-label {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.amount-value {
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.amount-value.discount {
|
|
color: #52c41a;
|
|
}
|
|
|
|
.amount-row.actual {
|
|
border-top: 1rpx dashed #ddd;
|
|
padding-top: 16rpx;
|
|
margin-top: 8rpx;
|
|
}
|
|
|
|
.amount-row.actual .amount-label {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.amount-row.actual .amount-value {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #ff4d4f;
|
|
}
|
|
|
|
.order-footer {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding-top: 16rpx;
|
|
border-top: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.order-time {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.operator {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.empty {
|
|
padding: 100rpx;
|
|
text-align: center;
|
|
color: #999;
|
|
}
|
|
|
|
.load-more {
|
|
padding: 20rpx;
|
|
text-align: center;
|
|
color: #999;
|
|
font-size: 24rpx;
|
|
}
|
|
</style>
|