<template>
|
<view class="history-page" :class="{ leaving: isLeaving }">
|
<guide-nav-bar title="历史订单" @back="goBack" />
|
|
<view class="tabs">
|
<view
|
v-for="item in tabs"
|
:key="item.key"
|
class="tab"
|
:class="{ active: current === item.key }"
|
@tap="switchTab(item.key)"
|
>
|
{{ item.text }}
|
</view>
|
</view>
|
|
<scroll-view class="order-list" scroll-y>
|
<view v-for="order in displayOrders" :key="order.id" class="order-row" @tap="openOrder(order.id)">
|
<view class="order-main">
|
<view class="order-head">
|
<text class="service-text">{{ order.serviceText }}</text>
|
<text class="scenic-text">{{ order.scenicShortName }}</text>
|
</view>
|
<view class="order-time">接单时间 {{ order.receiveTime }}</view>
|
</view>
|
<view class="order-side">
|
<view class="order-status" :class="order.statusClass">{{ order.statusLabel }}</view>
|
<view class="people-box">
|
<text class="people-value">{{ order.quantity }}</text>
|
<text class="people-unit">人</text>
|
</view>
|
</view>
|
</view>
|
<view v-if="!displayOrders.length" class="empty-text">暂无历史订单</view>
|
</scroll-view>
|
</view>
|
</template>
|
|
<script>
|
import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
|
import { getGuideOrders } from '../../services/order'
|
import { formatDuration } from '../../utils/format'
|
import { backOrHome, routeTo } from '../../utils/router'
|
|
export default {
|
components: {
|
GuideNavBar
|
},
|
data() {
|
return {
|
current: 'all',
|
tabs: [
|
{ key: 'all', text: '全部讲解' },
|
{ key: 'accepted', text: '接单中' },
|
{ key: 'serving', text: '讲解中' },
|
{ key: 'completed', text: '已完成' }
|
],
|
orders: [],
|
isLeaving: false
|
}
|
},
|
computed: {
|
displayOrders() {
|
return this.normalizeOrders(this.orders)
|
}
|
},
|
onShow() {
|
this.loadOrders()
|
},
|
methods: {
|
goBack() {
|
if (this.isLeaving) {
|
return
|
}
|
this.isLeaving = true
|
setTimeout(() => {
|
backOrHome()
|
}, 280)
|
},
|
switchTab(key) {
|
this.current = key
|
this.loadOrders()
|
},
|
loadOrders() {
|
getGuideOrders(this.current).then((orders) => {
|
this.orders = orders
|
})
|
},
|
matchStatus(order, key) {
|
if (key === 'accepted') {
|
return ['pending', 'accepted'].includes(order.status)
|
}
|
if (key === 'serving') {
|
return ['arrived', 'serving'].includes(order.status)
|
}
|
if (key === 'completed') {
|
return ['completed'].includes(order.status)
|
}
|
return true
|
},
|
normalizeOrders(orders = []) {
|
return orders.map((order) => {
|
const serviceName = order.serviceName || '精讲'
|
const serviceText = serviceName.replace('拼团 · ', '').replace('包团 · ', '')
|
return {
|
...order,
|
serviceText: `${serviceText} ${formatDuration(order.durationMinutes || 30)}`,
|
scenicShortName: (order.scenicName || '青城山').replace('景区', ''),
|
receiveTime: order.receiveTime || '2026-06-08 16:34:33',
|
statusLabel: this.statusLabel(order.status),
|
statusClass: this.statusClass(order.status)
|
}
|
})
|
},
|
statusLabel(status) {
|
const map = {
|
pending: '接单中',
|
accepted: '接单中',
|
arrived: '讲解中',
|
serving: '讲解中',
|
completed: '已完成',
|
canceled: '已取消'
|
}
|
return map[status] || '接单中'
|
},
|
statusClass(status) {
|
if (['pending', 'accepted'].includes(status)) {
|
return 'status-accepted'
|
}
|
if (['arrived', 'serving'].includes(status)) {
|
return 'status-serving'
|
}
|
if (status === 'canceled') {
|
return 'status-canceled'
|
}
|
return 'status-completed'
|
},
|
openOrder(id) {
|
routeTo(`/pages/order-detail/index?id=${id}`)
|
}
|
}
|
}
|
</script>
|
|
<style>
|
.history-page {
|
display: flex;
|
flex-direction: column;
|
min-height: 100vh;
|
background: #ffffff;
|
animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
will-change: transform, opacity;
|
}
|
|
.history-page.leaving {
|
animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
|
}
|
|
@keyframes pageSlideIn {
|
from {
|
opacity: 0.35;
|
transform: translateX(72rpx);
|
}
|
|
to {
|
opacity: 1;
|
transform: translateX(0);
|
}
|
}
|
|
@keyframes pageSlideOut {
|
from {
|
opacity: 1;
|
transform: translateX(0);
|
}
|
|
to {
|
opacity: 0.2;
|
transform: translateX(260rpx);
|
}
|
}
|
|
.tabs {
|
display: grid;
|
grid-template-columns: repeat(4, 1fr);
|
height: 92rpx;
|
border-bottom: 1rpx solid #d9d9d9;
|
background: #ffffff;
|
}
|
|
.tab {
|
position: relative;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
height: 92rpx;
|
color: #334155;
|
font-size: 26rpx;
|
font-weight: 600;
|
white-space: nowrap;
|
}
|
|
.tab.active {
|
color: #1685ff;
|
}
|
|
.tab.active::after {
|
position: absolute;
|
left: 22rpx;
|
right: 22rpx;
|
bottom: 0;
|
height: 8rpx;
|
border-radius: 999rpx 999rpx 0 0;
|
background: #1685ff;
|
content: '';
|
}
|
|
.order-list {
|
flex: 1;
|
min-height: 0;
|
background: #f7f9fc;
|
}
|
|
.order-row {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
min-height: 176rpx;
|
margin: 18rpx 24rpx 0;
|
padding: 0 28rpx 0 30rpx;
|
border: 1rpx solid #edf1f5;
|
border-radius: 12rpx;
|
background: #ffffff;
|
}
|
|
.order-main {
|
min-width: 0;
|
}
|
|
.order-head {
|
display: flex;
|
align-items: center;
|
min-width: 0;
|
}
|
|
.service-text {
|
color: #253b5f;
|
font-size: 30rpx;
|
font-weight: 800;
|
white-space: nowrap;
|
}
|
|
.scenic-text {
|
margin-left: 24rpx;
|
padding: 6rpx 14rpx;
|
border-radius: 999rpx;
|
color: #1f3b5b;
|
background: #eef6ff;
|
font-size: 28rpx;
|
font-weight: 700;
|
white-space: nowrap;
|
}
|
|
.order-time {
|
margin-top: 26rpx;
|
color: #64748b;
|
font-size: 25rpx;
|
line-height: 1.2;
|
white-space: nowrap;
|
}
|
|
.order-side {
|
flex-shrink: 0;
|
min-width: 116rpx;
|
text-align: right;
|
}
|
|
.order-status {
|
display: inline-flex;
|
align-items: center;
|
justify-content: center;
|
min-width: 104rpx;
|
height: 48rpx;
|
padding: 0 16rpx;
|
border-radius: 999rpx;
|
font-size: 24rpx;
|
font-weight: 800;
|
line-height: 48rpx;
|
}
|
|
.order-status.status-accepted {
|
color: #1677ff;
|
background: #eaf4ff;
|
}
|
|
.order-status.status-serving {
|
color: #d97706;
|
background: #fff4dc;
|
}
|
|
.order-status.status-completed {
|
color: #16a34a;
|
background: #eafaf0;
|
}
|
|
.order-status.status-canceled {
|
color: #e95b55;
|
background: #fff1f0;
|
}
|
|
.people-box {
|
margin-top: 28rpx;
|
color: #0f172a;
|
line-height: 1.2;
|
}
|
|
.people-value {
|
font-size: 34rpx;
|
font-weight: 800;
|
}
|
|
.people-unit {
|
margin-left: 4rpx;
|
color: #64748b;
|
font-size: 24rpx;
|
font-weight: 600;
|
}
|
|
.empty-text {
|
padding-top: 180rpx;
|
color: #667085;
|
text-align: center;
|
font-size: 28rpx;
|
}
|
</style>
|