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

1327 lines
36 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.
<!-- 配送端首页 - UTS Android 兼容 -->
<template>
<view class="delivery-container">
<!-- 头部状态栏 -->
<view class="header">
<view class="driver-info">
<image :src="driverInfo.avatar_url || '/static/default-avatar.png'" class="avatar" mode="aspectFit" />
<view class="driver-details">
<text class="driver-name">{{ driverInfo.real_name }}</text>
<text class="work-status" :class="getWorkStatusClass()">{{ getWorkStatusText() }}</text>
</view>
</view>
<view class="status-switch">
<switch :checked="isOnline" @change="toggleWorkStatus" color="#4CAF50" />
<text class="switch-label">{{ isOnline ? '在线接单' : '离线休息' }}</text>
</view>
</view>
<!-- 今日统计 -->
<view class="stats-section">
<text class="section-title">今日数据</text>
<view class="stats-grid">
<view class="stat-item">
<text class="stat-value">{{ todayStats.completed_orders }}</text>
<text class="stat-label">完成订单</text>
</view>
<view class="stat-item">
<text class="stat-value">¥{{ todayStats.total_earning }}</text>
<text class="stat-label">总收入</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ todayStats.total_distance }}km</text>
<text class="stat-label">配送距离</text>
</view>
<view class="stat-item">
<text class="stat-value">{{ todayStats.avg_rating }}</text>
<text class="stat-label">平均评分</text>
</view>
</view>
</view>
<!-- 当前配送任务 -->
<view v-if="currentTask" class="current-task-section">
<text class="section-title">当前任务</text>
<view class="task-card">
<view class="task-header">
<text class="task-id">订单号: {{ currentTask.order_no }}</text>
<text class="task-status" :class="getTaskStatusClass(currentTask.status)">{{ getTaskStatusText(currentTask.status) }}</text>
</view>
<view class="task-addresses">
<view class="address-item">
<text class="address-icon">📍</text>
<view class="address-info">
<text class="address-label">取货地址</text>
<text class="address-text">{{ currentTask.pickup_address.detail }}</text>
<text class="contact-info">联系人: {{ currentTask.pickup_contact.name }} {{ currentTask.pickup_contact.phone }}</text>
</view>
</view>
<view class="address-line"></view>
<view class="address-item">
<text class="address-icon">🏠</text>
<view class="address-info">
<text class="address-label">收货地址</text>
<text class="address-text">{{ currentTask.delivery_address.detail }}</text>
<text class="contact-info">联系人: {{ currentTask.delivery_contact.name }} {{ currentTask.delivery_contact.phone }}</text>
</view>
</view>
</view>
<view class="task-details">
<text class="task-info">配送费: ¥{{ currentTask.delivery_fee }}</text>
<text class="task-info">预计距离: {{ currentTask.distance }}km</text>
<text class="task-info">预计时间: {{ currentTask.estimated_time }}分钟</text>
</view>
<view class="task-actions">
<!-- 根据状态显示不同的操作按钮 -->
<button v-if="currentTask.status === 1" class="action-btn primary" @click="acceptTask">接受任务</button>
<button v-if="currentTask.status === 2" class="action-btn primary" @click="startPickup">开始取货</button>
<button v-if="currentTask.status === 3" class="action-btn primary" @click="confirmPickup">确认取货</button>
<button v-if="currentTask.status === 4" class="action-btn primary" @click="startDelivery">开始配送</button>
<button v-if="currentTask.status === 5" class="action-btn primary" @click="showConfirmDeliveryDialog">确认送达</button>
<button class="action-btn secondary" @click="contactCustomer">联系客户</button>
<button class="action-btn secondary" @click="viewNavigation">查看导航</button>
<button class="action-btn secondary" @click="viewOrderDetail">查看详情</button>
</view>
</view>
</view>
<!-- 可接取订单 -->
<view v-if="!currentTask && isOnline" class="available-orders-section">
<view class="section-header">
<text class="section-title">附近订单</text>
<view class="section-header-actions">
<text class="refresh-btn" @click="refreshOrders">🔄 刷新</text>
</view>
</view>
<view v-if="availableOrders.length === 0" class="empty-orders">
<text class="empty-text">暂无可接取订单</text>
<text class="empty-subtitle">请保持在线状态,有新订单会自动推送</text>
</view>
<view v-for="(order, index) in availableOrders" :key="order.id">
<view v-if="index < 5" class="order-card">
<view class="order-header">
<text class="order-id">{{ order.order_no }}</text>
<text class="order-fee">¥{{ order.delivery_fee }}</text>
</view>
<view class="order-route">
<view class="route-item">
<text class="route-icon">📍</text>
<text class="route-text">{{ order.pickup_address.area || order.pickup_address.detail }}</text>
</view>
<text class="route-arrow">→</text>
<view class="route-item">
<text class="route-icon">🏠</text>
<text class="route-text">{{ order.delivery_address.area || order.delivery_address.detail }}</text>
</view>
</view>
<view class="order-info">
<text class="info-item">距离: {{ order.distance }}km</text>
<text class="info-item">预计: {{ order.estimated_time }}分钟</text>
<text class="info-item">下单: {{ formatTime(order.created_at) }}</text>
</view>
<view class="order-actions">
<button class="order-btn accept" @click="acceptOrder(order.id)">接受订单</button>
<button class="order-btn detail" @click="viewOrderDetail(order.id)">查看详情</button>
</view>
</view>
</view>
<!-- 超过5个展示订单就有个加载更多 -->
<view v-if="availableOrders.length > 5" class="view-all-footer" @click="goToAllOrders">
<text class="view-all-text">查看全部订单 (共 {{ availableOrders.length }} 个待接订单) ➜</text>
</view>
</view>
<!-- 历史记录快捷入口 -->
<view class="quick-actions-section">
<text class="section-title">快捷功能</text>
<view class="actions-grid">
<view class="action-item" @click="goToOrderHistory">
<text class="action-icon">📋</text>
<text class="action-text">历史订单</text>
</view>
<view class="action-item" @click="goToEarnings">
<text class="action-icon">💰</text>
<text class="action-text">收入明细</text>
</view>
<view class="action-item" @click="goToProfile">
<text class="action-icon">👤</text>
<text class="action-text">个人资料</text>
</view>
<view class="action-item" @click="goToSettings">
<text class="action-icon">⚙️</text>
<text class="action-text">设置</text>
</view>
</view>
</view>
</view>
</template>
<script lang="uts">
import type {
DeliveryDriverType,
DeliveryTaskType
} from '@/types/mall-types.uts'
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
import { getCurrentUserId, getCurrentUser } from '@/utils/store.uts'
type TodayStatsType = {
completed_orders: number
total_earning: string
total_distance: number
avg_rating: number
}
type AddressInfoType = {
detail: string
area: string
}
type ContactInfoType = {
name: string
phone: string
}
type CurrentTaskType = {
id: string
order_no: string
status: number
pickup_address: AddressInfoType
delivery_address: AddressInfoType
pickup_contact: ContactInfoType
delivery_contact: ContactInfoType
delivery_fee: number
distance: number
estimated_time: number
created_at: string
}
type AvailableOrderType = {
id: string
order_no: string
pickup_address: AddressInfoType
delivery_address: AddressInfoType
delivery_fee: number
distance: number
estimated_time: number
created_at: string
}
export default {
data() {
return {
isOnline: true,
// 防抖:记录上次刷新时间,避免 onShow 导致的频繁 refresh
lastRefreshAt: 0,
// 控制是否启用自动刷新onShow——默认关闭避免频繁或意外刷新
enableAutoRefresh: false,
driverInfo: {
id: '',
user_id: '',
real_name: '配送员',
id_card: '',
driver_license: '',
vehicle_type: 1,
vehicle_number: '',
work_status: 1,
current_location: null,
service_areas: [],
rating: 5.0,
total_orders: 0,
auth_status: 1,
created_at: '',
updated_at: ''
} as DeliveryDriverType,
todayStats: {
completed_orders: 0,
total_earning: '0.00',
total_distance: 0,
avg_rating: 5.0
} as TodayStatsType,
currentTask: null as CurrentTaskType | null,
availableOrders: [] as Array<AvailableOrderType>
}
},
async onLoad() {
// 确保 userProfile 已加载,以便 getCurrentUserId 能返回正确值
try {
await getCurrentUser()
} catch (e) {
console.warn('getCurrentUser failed on onLoad', e)
}
await this.loadDriverInfo()
await this.loadTodayStats()
await this.loadCurrentTask()
await this.loadAvailableOrders()
},
async onShow() {
// 自动刷新已被禁用enableAutoRefresh = false以避免页面抖动。
// 如需临时启用,可在控制台设置 `this.enableAutoRefresh = true`。
if (!this.enableAutoRefresh) {
console.log('onShow: auto refresh disabled')
return
}
const now = Date.now()
if (this.lastRefreshAt && (now - this.lastRefreshAt < 5000)) {
console.log('onShow: skipped refresh (debounced)')
return
}
this.lastRefreshAt = now
await this.refreshData()
},
methods: {
// 加载配送员信息
async loadDriverInfo() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadDriverInfo - proceeding')
const userId = getCurrentUserId()
if (!userId) return
// 先按 user_id 查询userId 可能是 ak_users.id 或 auth.users.id
let res = await supa.from('ml_delivery_drivers').select('*').eq('user_id', userId).limit(1).execute()
console.log('loadDriverInfo: try user_id=', userId, 'res=', res)
if (!(res && (res.data instanceof Array) && res.data.length > 0)) {
// 回退:尝试从 ak_users 表根据 auth_id 查出 ak_users.id
const akRes = await supa.from('ak_users').select('id').eq('auth_id', userId).limit(1).execute()
console.log('loadDriverInfo: ak_users lookup by auth_id=', userId, 'akRes=', akRes)
let akId = ''
if (akRes && Array.isArray(akRes.data) && akRes.data.length > 0) {
akId = (akRes.data[0] as any).id
}
if (akId) {
res = await supa.from('ml_delivery_drivers').select('*').eq('user_id', akId).limit(1).execute()
console.log('loadDriverInfo: retry user_id with akId=', akId, 'res=', res)
}
}
if (res && (res.data instanceof Array) && res.data.length > 0) {
const data = res.data[0] as DeliveryDriverType
this.driverInfo = Object.assign(this.driverInfo, data)
// 同步工作状态到本地变量
this.isOnline = (this.driverInfo.work_status === 1)
}
} catch (e) {
console.error('loadDriverInfo error', e)
}
},
async loadTodayStats() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadTodayStats - proceeding')
const driverId = this.driverInfo.id || null
if (!driverId) return
const start = new Date()
start.setHours(0,0,0,0)
const end = new Date()
end.setHours(23,59,59,999)
const res = await supa.from('ml_delivery_tasks')
.select('id,delivery_fee,distance,created_at,status')
.eq('driver_id', driverId)
.gte('created_at', start.toISOString())
.lte('created_at', end.toISOString())
.execute()
if (res && res.data) {
const rows = res.data as Array<any>
const completed = rows.filter(r => r.status >= 5).length
const earning = rows.reduce((s, r) => s + (Number(r.delivery_fee) || 0), 0)
const distance = rows.reduce((s, r) => s + (Number(r.distance) || 0), 0)
this.todayStats = {
completed_orders: completed,
total_earning: earning.toFixed(2),
total_distance: Number(distance.toFixed(2)),
avg_rating: this.driverInfo.rating || 0
}
}
} catch (e) {
console.error('loadTodayStats error', e)
}
},
async loadCurrentTask() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadCurrentTask - proceeding')
const driverId = this.driverInfo.id || null
if (!driverId) {
this.currentTask = null
return
}
const res = await supa.from('ml_delivery_tasks')
.select('*')
.eq('driver_id', driverId)
.lt('status', 5)
.order('created_at', { ascending: false })
.limit(1)
.execute()
console.log('loadCurrentTask: driverId=', driverId, 'res=', res)
if (res && Array.isArray(res.data) && res.data.length > 0) {
this.currentTask = this._transformTask(res.data[0])
} else {
this.currentTask = null
}
} catch (e) {
console.error('loadCurrentTask error', e)
}
},
async loadAvailableOrders() {
// 如果当前不在线或已有任务,直接清空并返回
if (!this.isOnline || this.currentTask) {
this.availableOrders = []
return
}
// 在加载过程中先清空,避免显示过期或闪现的数据
this.availableOrders = []
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadAvailableOrders - proceeding')
console.log('loadAvailableOrders: supa session=', supa.getSession && supa.getSession())
console.log('loadAvailableOrders: getCurrentUserId=', getCurrentUserId())
const res = await supa.from('ml_delivery_tasks')
.select('*')
.is('driver_id', 'null')
.eq('status', 1)
.range(0, 19)
.execute()
console.log('loadAvailableOrders: query result=', res)
if (res && Array.isArray(res.data)) {
const fetched = (res.data as Array<any>).map((r:any) => this._transformTask(r))
// 再次检查 currentTask避免并发情况下短暂展示可接单
if (this.currentTask) {
this.availableOrders = []
} else {
this.availableOrders = fetched
}
}
} catch (e) {
console.error('loadAvailableOrders error', e)
this.availableOrders = []
}
},
// 将 DB 行转换为页面期望的结构
_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 } }
}
const detail = obj.detail || obj.address || obj.full_address || obj.address_detail || obj.name || ''
const area = (obj.city || obj.district || obj.area || '')
return { detail, area }
}
const parseContact = (c: any) => {
if (!c) return { name: '', phone: '' }
let obj = c
if (typeof c === 'string') {
try { obj = JSON.parse(c) } catch (e) { obj = { name: c } }
}
return { name: obj.name || obj.contact_name || obj.receiver_name || '', phone: obj.phone || obj.mobile || obj.contact_phone || '' }
}
return {
id: task.id,
order_id: task.order_id || task.orderId || task.orderId || '',
order_no: task.order_no || task.orderNo || task.trade_no || '',
status: Number(task.status) || 1,
pickup_address: parseAddress(task.pickup_address),
delivery_address: parseAddress(task.delivery_address),
pickup_contact: parseContact(task.pickup_contact),
delivery_contact: parseContact(task.delivery_contact),
delivery_fee: Number(task.delivery_fee) || 0,
distance: Number(task.distance) || 0,
estimated_time: Number(task.estimated_time) || 0,
created_at: task.created_at || task.createdAt || ''
}
},
// 刷新数据
async refreshData() {
await this.loadTodayStats()
await this.loadCurrentTask()
await this.loadAvailableOrders()
},
// 刷新订单列表
refreshOrders() {
this.loadAvailableOrders()
uni.showToast({
title: '刷新成功',
icon: 'success'
})
},
// 切换工作状态
toggleWorkStatus(event: UniSwitchChangeEvent) {
const targetStatus = event.detail.value
// 检查是否有当前任务,不允许离线
if (!targetStatus && this.currentTask != null) {
// 1. 先同步 UI 状态为 false (由于用户已经拨动了开关)
this.isOnline = false
// 2. 弹出警告
uni.showModal({
title: '无法下线',
content: '您当前有正在进行的任务,请完成后再下线。',
showCancel: false,
success: (_) => {
// 3. 用户点击确定后或立即强制回弹开关为 true
this.$nextTick(() => {
this.isOnline = true
})
}
})
// 4. 冗余保障:如果 Modal 没及时回弹,延时强制重置
setTimeout(() => {
this.isOnline = true
}, 300)
return
}
this.isOnline = targetStatus
if (this.isOnline) {
this.startWork()
} else {
this.stopWork()
}
},
// 开始工作
async startWork() {
const driverId = this.driverInfo.id
if (driverId != '') {
try {
await supa.from('ml_delivery_drivers').update({ work_status: 1 } as any).eq('id', driverId).execute()
} catch (e) {
console.error('startWork update failed', e)
}
}
this.loadAvailableOrders()
uni.showToast({
title: '已上线接单',
icon: 'success'
})
},
// 停止工作
async stopWork() {
const driverId = this.driverInfo.id
if (driverId != '') {
try {
await supa.from('ml_delivery_drivers').update({ work_status: 0 } as any).eq('id', driverId).execute()
} catch (e) {
console.error('stopWork update failed', e)
}
}
this.availableOrders = []
uni.showToast({
title: '已下线休息',
icon: 'none'
})
},
// 获取工作状态样式
getWorkStatusClass(): string {
return this.isOnline ? 'status-online' : 'status-offline'
},
// 获取工作状态文本
getWorkStatusText(): string {
return this.isOnline ? '在线中' : '已离线'
},
// 获取任务状态样式
getTaskStatusClass(status: number): string {
switch (status) {
case 1: return 'task-pending'
case 2: return 'task-accepted'
case 3: return 'task-picking'
case 4: return 'task-picked'
case 5: return 'task-delivering'
default: return 'task-default'
}
},
// 获取任务状态文本
getTaskStatusText(status: number): string {
switch (status) {
case 1: return '待接取'
case 2: return '已接取'
case 3: return '取货中'
case 4: return '已取货'
case 5: return '配送中'
default: return '未知状态'
}
},
// 格式化时间
formatTime(timeStr: string): string {
const date = new Date(timeStr)
const now = new Date()
const diff = now.getTime() - date.getTime()
const minutes = Math.floor(diff / (1000 * 60))
if (minutes < 60) {
return `${minutes}分钟前`
} else {
return `${Math.floor(minutes / 60)}小时前`
}
},
// 任务操作方法
async acceptTask() {
if (!this.currentTask) return
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in acceptTask - proceeding')
const driverId = this.driverInfo.id || null
if (!driverId) throw new Error('无配送员ID')
const res = await supa.from('ml_delivery_tasks').update({ driver_id: driverId, status: 2 }).eq('id', this.currentTask.id).execute()
if (res && !res.error) {
this.currentTask.status = 2
uni.showToast({ title: '任务已接受', icon: 'success' })
}
} catch (e) {
console.error('acceptTask error', e)
uni.showToast({ title: '接受任务失败', icon: 'none' })
}
},
async startPickup() {
if (!this.currentTask) return
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in startPickup - proceeding')
const res = await supa.from('ml_delivery_tasks').update({ status: 3 }).eq('id', this.currentTask.id).execute()
if (res && !res.error) {
this.currentTask.status = 3
uni.showToast({ title: '开始取货', icon: 'success' })
}
} catch (e) {
console.error('startPickup error', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
async confirmPickup() {
if (!this.currentTask) return
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in confirmPickup - proceeding')
const res = await supa.from('ml_delivery_tasks').update({ status: 4, pickup_time: new Date().toISOString() }).eq('id', this.currentTask.id).execute()
if (res && !res.error) {
this.currentTask.status = 4
uni.showToast({ title: '取货完成', icon: 'success' })
}
} catch (e) {
console.error('confirmPickup error', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
async startDelivery() {
if (!this.currentTask) return
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in startDelivery - proceeding')
const res = await supa.from('ml_delivery_tasks').update({ status: 5 }).eq('id', this.currentTask.id).execute()
if (res && !res.error) {
this.currentTask.status = 5
uni.showToast({ title: '开始配送', icon: 'success' })
}
} catch (e) {
console.error('startDelivery error', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
// 显示确认送达弹框
showConfirmDeliveryDialog() {
uni.showModal({
title: '确认送达',
content: '确认商品已送到顾客手中?',
success: (res) => {
if (res.confirm) {
this.confirmDelivery()
}
}
})
},
async confirmDelivery() {
if (!this.currentTask) return
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in confirmDelivery - proceeding')
const res = await supa.from('ml_delivery_tasks').update({ status: 6, delivered_time: new Date().toISOString() }).eq('id', this.currentTask.id).execute()
if (res && !res.error) {
const completedOrder = { ...this.currentTask }
uni.setStorageSync('completed_order_for_history', completedOrder)
// 同步更新 ml_orders 的状态,确保两个表状态一致
try {
// 尝试使用 currentTask.order_id由 _transformTask 提供)
const orderId = (this.currentTask as any).order_id || ''
if (orderId) {
const upRes: any = await supa.from('ml_orders').update({ order_status: 5 }).eq('id', orderId).execute()
console.log('confirmDelivery: ml_orders update res=', upRes)
if (!upRes || upRes.error) console.warn('confirmDelivery: ml_orders update failed', upRes)
} else {
// 如无 order_id回退读取任务行以查找 order_id
const tRes: any = await supa.from('ml_delivery_tasks').select('order_id').eq('id', this.currentTask.id).limit(1).execute()
if (tRes && Array.isArray(tRes.data) && tRes.data.length > 0) {
const oid = tRes.data[0].order_id
if (oid) {
const upRes2: any = await supa.from('ml_orders').update({ order_status: 5 }).eq('id', oid).execute()
console.log('confirmDelivery: ml_orders update (fallback) res=', upRes2)
if (!upRes2 || upRes2.error) console.warn('confirmDelivery: ml_orders update (fallback) failed', upRes2)
}
}
}
} catch (syncErr) {
console.warn('confirmDelivery: failed to sync ml_orders status', syncErr)
}
uni.showToast({ title: '配送完成', icon: 'success' })
this.currentTask = null
this.loadAvailableOrders()
}
} catch (e) {
console.error('confirmDelivery error', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
contactCustomer() {
if (this.currentTask) {
uni.makePhoneCall({
phoneNumber: this.currentTask.delivery_contact.phone
})
}
},
viewNavigation() {
// TODO: 打开地图导航
uni.showToast({
title: '打开导航',
icon: 'none'
})
},
// 查看订单详情(跳转到 order-detail 页面)
viewOrderDetail(orderId?: string,status?:number) {
if (orderId && status) {
uni.navigateTo({
url: `/pages/mall/delivery/order-detail?id=${orderId}&status=${status}` // ✅ 强制为 1
})
} else if (this.currentTask) {
uni.navigateTo({
url: `/pages/mall/delivery/order-detail?id=${this.currentTask.id}&status=${this.currentTask.status}`
})
}else{
uni.navigateTo({
url: `/pages/mall/delivery/order-detail?id=${orderId}&status=1` // ✅ 强制为 1
})
}
},
// 订单操作方法
async acceptOrder(orderId: string) {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in acceptOrder - proceeding')
const driverId = this.driverInfo.id || null
if (!driverId) throw new Error('无配送员ID')
const res = await supa.from('ml_delivery_tasks').update({ driver_id: driverId, status: 2 }).eq('id', orderId).execute()
if (res && !res.error) {
uni.showToast({ title: '订单已接受', icon: 'success' })
// 同步更新 ml_orders 状态为已接取2
try {
// orderId 这里是 ml_delivery_tasks.idtask id需要先获取 order_id
const tRes: any = await supa.from('ml_delivery_tasks').select('order_id').eq('id', orderId).limit(1).execute()
if (tRes && Array.isArray(tRes.data) && tRes.data.length > 0) {
const oid = tRes.data[0].order_id
if (oid) {
const upRes: any = await supa.from('ml_orders').update({ order_status: 2 }).eq('id', oid).execute()
console.log('acceptOrder: ml_orders update res=', upRes)
if (!upRes || upRes.error) console.warn('acceptOrder: ml_orders update failed', upRes)
}
}
} catch (syncErr) {
console.warn('acceptOrder: failed to sync ml_orders status', syncErr)
}
await this.loadCurrentTask()
await this.loadAvailableOrders()
}
} catch (e) {
console.error('acceptOrder error', e)
uni.showToast({ title: '接受订单失败', icon: 'none' })
}
},
// 导航方法
goToOrderHistory() {
uni.navigateTo({
url: '/pages/mall/delivery/order-history'
})
},
// 跳转到“全部可接订单”页面
goToAllOrders() {
uni.navigateTo({
url: '/pages/mall/delivery/all'
})
},
goToEarnings() {
uni.navigateTo({
url: '/pages/mall/delivery/earnings'
})
},
goToProfile() {
uni.navigateTo({
url: '/pages/mall/delivery/profile'
})
},
goToSettings() {
uni.navigateTo({
url: '/pages/mall/delivery/settings'
})
}
}
}
</script>
<style>
/* ... 保持原有 style 部分不变 ... */
.delivery-container {
background-color: #f8f9fa;
min-height: 100vh;
padding-bottom: 40rpx;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.header {
background-color: #fff;
padding: 20rpx 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #e9ecef;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.driver-info {
display: flex;
align-items: center;
}
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
margin-right: 20rpx;
border: 2rpx solid #dee2e6;
}
.driver-details {
display: flex;
flex-direction: column;
justify-content: center;
}
.driver-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.work-status {
font-size: 24rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-weight: 500;
}
.status-online {
background-color: #E8F5E8;
color: #4CAF50;
}
.status-offline {
background-color: #FFF3E0;
color: #FF9800;
}
.status-switch {
display: flex;
align-items: center;
gap: 10rpx;
}
.switch-label {
font-size: 22rpx;
color: #666;
}
/* 今日统计 */
.stats-section {
background-color: #fff;
margin: 20rpx;
padding: 20rpx 30rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
text-align: center;
}
.section-header-actions {
display: flex;
gap: 12rpx;
align-items: center;
}
.more-btn {
color: #1976d2;
font-size: 24rpx;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15rpx;
justify-items: center;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 15rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
min-width: 120rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: bold;
color: #4CAF50;
margin-bottom: 10rpx;
line-height: 1.2;
}
.stat-label {
font-size: 24rpx;
color: #666;
text-align: center;
}
/* 当前任务 */
.current-task-section {
background-color: #fff;
margin: 20rpx;
padding: 20rpx 30rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
text-align: center;
}
.task-card {
border: 1rpx solid #e9ecef;
border-radius: 12rpx;
padding: 20rpx;
background-color: #ffffff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #f8f9fa;
}
.task-id {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.task-status {
font-size: 24rpx;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-weight: 500;
}
.task-accepted {
background-color: #E3F2FD;
color: #1976D2;
}
.task-picking {
background-color: #FFF3E0;
color: #F57C00;
}
.task-delivering {
background-color: #E8F5E8;
color: #388E3C;
}
.task-addresses {
margin-bottom: 20rpx;
}
.address-item {
display: flex;
align-items: flex-start;
margin-bottom: 15rpx;
padding: 10rpx 0;
border-bottom: 1rpx dashed #e9ecef;
}
.address-icon {
font-size: 28rpx;
margin-right: 15rpx;
margin-top: 5rpx;
color: #666;
}
.address-info {
display: flex;
flex-direction: column;
flex: 1;
}
.address-label {
font-size: 24rpx;
color: #666;
margin-bottom: 8rpx;
font-weight: 500;
}
.address-text {
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
word-break: break-all;
}
.contact-info {
font-size: 24rpx;
color: #666;
font-weight: 500;
}
.address-line {
width: 2rpx;
height: 30rpx;
background-color: #ddd;
margin: 10rpx 0 10rpx 14rpx;
}
.task-details {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
padding: 15rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
font-size: 24rpx;
color: #666;
}
.task-info {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
font-size: 24rpx;
color: #666;
margin: 0 5rpx;
}
.task-actions {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
margin-top: 10rpx;
}
.action-btn {
flex: 1;
height: 80rpx;
border-radius: 8rpx;
font-size: 28rpx;
border: none;
font-weight: 500;
padding: 0 10rpx;
box-sizing: border-box;
}
.primary {
background-color: #4CAF50;
color: #fff;
}
.secondary {
background-color: #f0f0f0;
color: #333;
border: 1rpx solid #ddd;
}
/* 可接取订单 */
.available-orders-section {
margin: 20rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.refresh-btn {
font-size: 26rpx;
color: #4CAF50;
padding: 8rpx 16rpx;
background-color: #e8f5e8;
border-radius: 12rpx;
font-weight: 500;
}
.empty-orders {
background-color: #fff;
padding: 40rpx 30rpx;
border-radius: 16rpx;
text-align: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-bottom: 15rpx;
}
.empty-subtitle {
font-size: 24rpx;
color: #ccc;
}
.order-card {
background-color: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 15rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
border: 1rpx solid #e9ecef;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #f8f9fa;
}
.order-id {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.order-fee {
font-size: 32rpx;
color: #4CAF50;
font-weight: bold;
}
.order-route {
display: flex;
align-items: center;
margin-bottom: 15rpx;
padding: 10rpx 0;
border-bottom: 1rpx solid #f8f9fa;
}
.route-item {
display: flex;
align-items: center;
flex: 1;
}
.route-icon {
font-size: 24rpx;
margin-right: 8rpx;
color: #666;
}
.route-text {
font-size: 26rpx;
color: #333;
word-break: break-all;
}
.route-arrow {
font-size: 24rpx;
color: #999;
margin: 0 15rpx;
}
.order-info {
display: flex;
justify-content: space-between;
margin-bottom: 15rpx;
padding: 10rpx 0;
border-bottom: 1rpx solid #f8f9fa;
font-size: 22rpx;
color: #666;
}
.info-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
font-size: 22rpx;
color: #666;
margin: 0 5rpx;
}
.order-actions {
display: flex;
gap: 10rpx;
margin-top: 10rpx;
}
.order-btn {
flex: 1;
height: 70rpx;
border-radius: 8rpx;
font-size: 26rpx;
border: none;
font-weight: 500;
padding: 0 10rpx;
box-sizing: border-box;
}
.accept {
background-color: #4CAF50;
color: #fff;
}
.detail {
background-color: #f0f0f0;
color: #333;
border: 1rpx solid #ddd;
}
/* 加载更多订单入口样式 */
.view-all-footer {
background-color: #ffffff;
padding: 24rpx;
border-radius: 12rpx;
margin: 10rpx 0 30rpx;
display: flex;
justify-content: center;
align-items: center;
border: 1rpx dashed #4CAF50;
box-shadow: 0 4rpx 12rpx rgba(76, 175, 80, 0.1);
}
.view-all-text {
font-size: 26rpx;
color: #4CAF50;
font-weight: bold;
}
/* 历史记录快捷入口 */
.quick-actions-section {
background-color: #fff;
margin: 20rpx;
padding: 20rpx 30rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.actions-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
justify-items: center;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
min-width: 120rpx;
cursor: pointer;
transition: background-color 0.2s;
}
.action-item:hover {
background-color: #e8f5e8;
}
.action-icon {
font-size: 48rpx;
margin-bottom: 15rpx;
color: #666;
}
.action-text {
font-size: 24rpx;
color: #333;
text-align: center;
font-weight: 500;
}
</style>