Files
medical-mall/pages/mall/delivery/all.uvue
not-like-juvenile c803a77c8f 修改页面逻辑
2026-02-03 12:01:10 +08:00

361 lines
8.0 KiB
Plaintext
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="all-orders-container">
<!-- 头部标题栏 -->
<view class="page-header">
<view class="back-btn" @click="goBack">
<text class="back-icon"></text>
<text class="back-text">返回</text>
</view>
<text class="page-title">全部待接订单</text>
<view class="refresh-action" @click="loadOrders">
<text class="refresh-icon">🔄</text>
</view>
</view>
<!-- 订单列表区 -->
<scroll-view class="order-list-scroll" scroll-y="true" refresher-enabled="true" :refresher-triggered="isRefreshing" @refresherrefresh="onRefresh">
<view v-if="orders.length === 0" class="empty-state">
<image src="/static/images/no-order.png" class="empty-img" mode="aspectFit" />
<text class="empty-text">附近暂时没有待接订单</text>
</view>
<view v-for="order in orders" :key="order.id" class="order-card">
<view class="order-fee-center">
<text class="order-fee-text">¥{{ order.delivery_fee }}</text>
</view>
<view class="order-route-vertical">
<view class="route-point">
<text class="route-icon-pink">📍</text>
<text class="route-text-main">{{ order.pickup_address.detail || order.pickup_address.area }}</text>
</view>
<text class="route-arrow-down">↓</text>
<view class="route-point">
<text class="route-icon-home">🏠</text>
<text class="route-text-main">{{ order.delivery_address.detail || order.delivery_address.area }}</text>
</view>
</view>
<view class="order-meta-info">
<text class="meta-label">距离: {{ order.distance }}km</text>
<text class="meta-label">预计: {{ order.estimated_time }}分钟</text>
<text class="meta-label">下单: {{ order.created_at }}</text>
</view>
<view class="order-actions-stack">
<button class="order-btn-full accept" @click="acceptOrder(order.id)">接受订单</button>
<button class="order-btn-full detail" @click="viewOrderDetail(order.id)">查看详情</button>
</view>
</view>
<view v-if="orders.length > 0" class="list-bottom">
<text class="bottom-text">已加载全部订单</text>
</view>
</scroll-view>
</view>
</template>
<script lang="uts">
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
import { getCurrentUserId } from '@/utils/store.uts'
export default {
data() {
return {
orders: [] as any[],
isRefreshing: false,
driverId: ''
}
},
async onLoad() {
await this.getDriverId()
await this.loadOrders()
},
methods: {
goBack() {
uni.navigateBack()
},
async getDriverId() {
try {
await supaReady
const userId = getCurrentUserId()
if (!userId) return
const res = await supa.from('ml_delivery_drivers').select('id').eq('user_id', userId).limit(1).execute()
if (res && Array.isArray(res.data) && res.data.length > 0) {
this.driverId = res.data[0].id
}
} catch (e) {
console.error('getDriverId error', e)
}
},
async loadOrders() {
this.isRefreshing = true
try {
await supaReady
const res = await supa.from('ml_delivery_tasks')
.select('*')
.is('driver_id', 'null')
.eq('status', 1)
.order('created_at', { ascending: false })
.execute()
if (res && Array.isArray(res.data)) {
this.orders = res.data.map((r: any) => this._transformTask(r))
}
} catch (e) {
console.error('loadAllOrders error', e)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.isRefreshing = false
}
},
onRefresh() {
this.loadOrders()
},
viewOrderDetail(orderId: string) {
uni.navigateTo({
url: `/pages/mall/delivery/order-detail?id=${orderId}&status=1`
})
},
_transformTask(task: any) {
const parseAddress = (a: any) => {
if (!a) return { detail: '', area: '' }
let obj = a
if (typeof a === 'string') {
try { obj = JSON.parse(a) } catch (e) { obj = { detail: a } }
}
return {
detail: obj.detail || obj.address || '',
area: obj.area || obj.district || obj.city || '未知区域'
}
}
return {
id: task.id,
order_no: task.order_no || '无编号',
delivery_fee: Number(task.delivery_fee) || 0,
pickup_address: parseAddress(task.pickup_address),
delivery_address: parseAddress(task.delivery_address),
distance: Number(task.distance) || 0,
estimated_time: Number(task.estimated_time) || 0,
created_at: task.created_at
}
},
async acceptOrder(taskId: string) {
if (!this.driverId) {
uni.showToast({ title: '未找到配送员身份', icon: 'none' })
return
}
uni.showLoading({ title: '正抢单中...' })
try {
// 抢单逻辑:更新 driver_id 和状态
const res = await supa.from('ml_delivery_tasks')
.update({ driver_id: this.driverId, status: 2 })
.eq('id', taskId)
.is('driver_id', 'null') // 并发保护
.execute()
if (res && Array.isArray(res.data) && res.data.length > 0) {
// 同步订单状态
const orderId = (res.data[0] as any).order_id
if (orderId) {
await supa.from('ml_orders').update({ order_status: 2 }).eq('id', orderId).execute()
}
uni.hideLoading()
uni.showToast({ title: '接单成功!', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/mall/delivery/index' })
}, 1500)
} else {
throw new Error('订单已被抢走')
}
} catch (e) {
uni.hideLoading()
uni.showToast({ title: '抢单失败,可能已被其他配送员接取', icon: 'none' })
this.loadOrders()
}
}
}
}
</script>
<style scoped>
.all-orders-container {
background-color: #f7f8fa;
height: 100vh;
display: flex;
flex-direction: column;
}
.page-header {
background-color: #ffffff;
padding: 20rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #eee;
}
.back-btn {
display: flex;
align-items: center;
padding: 10rpx 0;
}
.back-icon {
font-size: 40rpx;
color: #333;
}
.back-text {
font-size: 28rpx;
margin-left: 5rpx;
}
.page-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.refresh-action {
padding: 10rpx;
}
.order-list-scroll {
flex: 1;
overflow: hidden;
}
.order-card {
background-color: #ffffff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
}
.order-fee-center {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx 0;
margin-bottom: 30rpx;
border-bottom: 1rpx dashed #eee;
}
.order-fee-text {
font-size: 48rpx;
font-weight: bold;
color: #4CAF50;
text-align: center;
}
.order-route-vertical {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20rpx;
}
.route-point {
display: flex;
flex-direction: column;
align-items: center;
gap: 10rpx;
}
.route-icon-pink {
font-size: 32rpx;
}
.route-icon-home {
font-size: 32rpx;
}
.route-text-main {
font-size: 28rpx;
color: #333;
font-weight: 500;
text-align: center;
}
.route-arrow-down {
font-size: 24rpx;
color: #999;
margin: 10rpx 0;
}
.order-meta-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
margin-bottom: 30rpx;
}
.meta-label {
font-size: 24rpx;
color: #999;
}
.order-actions-stack {
display: flex;
flex-direction: column;
gap: 15rpx;
}
.order-btn-full {
width: 100%;
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8rpx;
font-size: 28rpx;
border: none;
font-weight: 500;
}
.order-btn-full.accept {
background-color: #4CAF50;
color: #fff;
}
.order-btn-full.detail {
background-color: #f0f0f0;
color: #333;
border: 1rpx solid #ddd;
}
.empty-state {
padding-top: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.empty-img {
width: 240rpx;
height: 240rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.list-bottom {
padding: 40rpx;
text-align: center;
}
.bottom-text {
font-size: 24rpx;
color: #ccc;
}
</style>