campus-errand/miniapp/pages/order/my-taken.vue
18631081161 681d2b5fe8
All checks were successful
continuous-integration/drone/push Build is passing
提现
2026-04-02 16:55:18 +08:00

414 lines
9.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="my-taken-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<view class="nav-back" @click="goBack">
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
</view>
<text class="navbar-title">我的接单</text>
<view class="nav-placeholder"></view>
</view>
</view>
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 筛选栏吸顶 -->
<view class="filter-sticky" :style="{ top: (statusBarHeight + 44) + 'px' }">
<!-- 状态筛选标签 -->
<view class="tab-bar">
<scroll-view class="tab-scroll" scroll-x>
<view
class="tab-item"
v-for="tab in statusTabs"
:key="tab.value"
:class="{ active: currentStatus === tab.value }"
@click="switchStatus(tab.value)"
>{{ tab.label }}</view>
</scroll-view>
</view>
<!-- 类型筛选标签 -->
<view class="type-bar">
<scroll-view class="type-scroll" scroll-x>
<view
class="type-item"
v-for="tab in typeTabs"
:key="tab.value"
:class="{ active: currentType === tab.value }"
@click="switchType(tab.value)"
>{{ tab.label }}</view>
</scroll-view>
</view>
</view>
<!-- 订单列表 -->
<view class="order-list">
<view v-if="loading" class="loading-tip">加载中...</view>
<view v-else-if="filteredOrders.length === 0" class="empty-tip">暂无接单</view>
<view
v-for="order in filteredOrders"
:key="order.id"
class="order-card"
>
<view class="card-header">
<text class="order-type-tag">{{ getTypeLabel(order.orderType) }}</text>
<text class="order-status" :class="getStatusClass(order.status)">{{ getStatusLabel(order.status) }}</text>
</view>
<view class="card-body">
<view class="info-row">
<text class="info-label">订单编号</text>
<text class="info-value">{{ order.orderNo }}</text>
</view>
<view class="info-row" v-if="order.itemName">
<text class="info-label">物品</text>
<text class="info-value">{{ order.itemName }}</text>
</view>
<view class="info-row" v-if="order.deliveryLocation">
<text class="info-label">送达地点</text>
<text class="info-value">{{ order.deliveryLocation }}</text>
</view>
<view class="info-row">
<text class="info-label">跑腿佣金</text>
<text class="info-value price">¥{{ order.commission }}</text>
</view>
<view class="info-row">
<text class="info-label">接单时间</text>
<text class="info-value">{{ formatTime(order.acceptedAt) }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-footer">
<!-- 进行中:完成订单 + 查看详情 + 联系单主 -->
<template v-if="order.status === 'InProgress'">
<view class="btn btn-primary" @click="goComplete(order)">
<text>完成订单</text>
</view>
<view class="btn btn-contact" @click="goChat(order)">
<text>联系单主</text>
</view>
<view class="btn btn-detail" @click="goDetail(order)">
<text>查看详情</text>
</view>
</template>
<!-- 已完成:查看详情 + 联系单主 -->
<template v-else-if="order.status === 'Completed'">
<view class="btn btn-contact" @click="goChat(order)">
<text>联系单主</text>
</view>
<view class="btn btn-detail" @click="goDetail(order)">
<text>查看详情</text>
</view>
</template>
<!-- 其他状态:查看详情 -->
<template v-else>
<view class="btn btn-detail" @click="goDetail(order)">
<text>查看详情</text>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import { getMyTaken } from '../../utils/api'
export default {
data() {
return {
statusTabs: [
{ label: '全部', value: '' },
{ label: '进行中', value: 'InProgress' },
{ label: '待确认', value: 'WaitConfirm' },
{ label: '已完成', value: 'Completed' },
{ label: '已取消', value: 'Cancelled' }
],
typeTabs: [
{ label: '全部', value: '' },
{ label: '代取', value: 'Pickup' },
{ label: '代送', value: 'Delivery' },
{ label: '万能帮', value: 'Help' },
{ label: '代购', value: 'Purchase' },
{ label: '美食街', value: 'Food' }
],
currentStatus: '',
currentType: '',
orders: [],
loading: false,
statusBarHeight: 0
}
},
computed: {
filteredOrders() {
return this.orders.filter(o => {
if (this.currentStatus) {
const statuses = this.currentStatus.split(',')
if (!statuses.includes(o.status)) return false
}
if (this.currentType && o.orderType !== this.currentType) return false
return true
})
}
},
onLoad(options) {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
if (options.status) {
this.currentStatus = options.status
}
},
onShow() {
this.loadOrders()
},
methods: {
goBack() { uni.navigateBack() },
/** 加载我的接单 */
async loadOrders() {
this.loading = true
try {
const res = await getMyTaken({})
this.orders = res?.items || res || []
} catch (e) {
this.orders = []
} finally {
this.loading = false
}
},
switchStatus(value) { this.currentStatus = value },
switchType(value) { this.currentType = value },
getTypeLabel(type) {
const map = { Pickup: '代取', Delivery: '代送', Help: '万能帮', Purchase: '代购', Food: '美食街' }
return map[type] || type
},
getStatusLabel(status) {
const map = { InProgress: '进行中', Completed: '已完成', Cancelled: '已取消', WaitConfirm: '待确认', Appealing: '申诉中' }
return map[status] || status
},
getStatusClass(status) {
const map = { InProgress: 'status-progress', Completed: 'status-done', Cancelled: 'status-cancel', WaitConfirm: 'status-confirm', Appealing: 'status-appeal' }
return map[status] || ''
},
formatTime(dateStr) {
if (!dateStr) return '-'
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
const pad = (n) => String(n).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
},
/** 跳转完成订单确认页 */
goComplete(order) {
uni.navigateTo({ url: `/pages/order/complete-order?id=${order.id}` })
},
/** 跳转聊天页 */
goChat(order) {
uni.navigateTo({ url: `/pages/message/chat?orderId=${order.id}` })
},
/** 跳转订单详情 */
goDetail(order) {
uni.navigateTo({ url: `/pages/order/order-detail?id=${order.id}` })
}
}
}
</script>
<style scoped>
/* 自定义导航栏 */
.custom-navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
background: #FFB700;
}
.navbar-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
}
.nav-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.navbar-title {
font-size: 34rpx;
font-weight: bold;
color: #363636;
}
.nav-placeholder {
width: 60rpx;
}
.my-taken-page {
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 40rpx;
}
/* 筛选栏吸顶容器 */
.filter-sticky {
position: sticky;
top: 0;
z-index: 99;
background-color: #ffffff;
}
.tab-bar {
padding: 16rpx 24rpx;
}
.tab-scroll {
white-space: nowrap;
}
.tab-item {
display: inline-block;
padding: 10rpx 24rpx;
margin-right: 12rpx;
font-size: 26rpx;
color: #666666;
border-radius: 28rpx;
background-color: #f5f5f5;
}
.tab-item.active {
background-color: #FFB700;
color: #ffffff;
}
.type-bar {
padding: 12rpx 24rpx;
border-top: 1rpx solid #f0f0f0;
}
.type-scroll {
white-space: nowrap;
}
.type-item {
display: inline-block;
padding: 8rpx 20rpx;
margin-right: 12rpx;
font-size: 24rpx;
color: #999999;
border-radius: 24rpx;
border: 1rpx solid #e0e0e0;
}
.type-item.active {
color: #FFB700;
border-color: #FFB700;
}
.order-list {
padding: 16rpx 24rpx;
}
.loading-tip, .empty-tip {
text-align: center;
color: #999999;
font-size: 28rpx;
padding: 80rpx 0;
}
.order-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.order-type-tag {
font-size: 24rpx;
color: #ffffff;
background-color: #FFB700;
padding: 4rpx 16rpx;
border-radius: 8rpx;
}
.order-status { font-size: 24rpx; font-weight: 500; }
.status-progress { color: #FFB700; }
.status-done { color: #52c41a; }
.status-cancel { color: #999999; }
.status-confirm { color: #ff9900; }
.status-appeal { color: #e64340; }
.card-body { margin-bottom: 16rpx; }
.info-row {
display: flex;
padding: 6rpx 0;
}
.info-label {
font-size: 26rpx;
color: #999999;
width: 140rpx;
flex-shrink: 0;
}
.info-value {
font-size: 26rpx;
color: #333333;
flex: 1;
}
.info-value.price {
color: #e64340;
font-weight: bold;
}
.card-footer {
display: flex;
justify-content: flex-end;
gap: 16rpx;
padding-top: 16rpx;
border-top: 1rpx solid #f0f0f0;
flex-wrap: wrap;
}
.btn {
padding: 12rpx 28rpx;
border-radius: 32rpx;
font-size: 26rpx;
}
.btn text { font-size: 26rpx; }
.btn-primary { background-color: #FAD146; }
.btn-primary text { color: #333333; }
.btn-detail { border: 1rpx solid #FFB700; }
.btn-detail text { color: #FFB700; }
.btn-contact { border: 1rpx solid #52c41a; }
.btn-contact text { color: #52c41a; }
</style>