Files
todo-frontend/src/pages/order/list.vue
2026-03-23 15:03:37 +00:00

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>