修改页面逻辑
This commit is contained in:
360
pages/mall/delivery/all.uvue
Normal file
360
pages/mall/delivery/all.uvue
Normal file
@@ -0,0 +1,360 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user