连接数据库并修改页面

This commit is contained in:
not-like-juvenile
2026-02-02 18:20:22 +08:00
parent 5f856a96c9
commit 8efe6d5e89
22 changed files with 1630 additions and 992 deletions

View File

@@ -95,7 +95,11 @@
<view v-if="!currentTask && isOnline" class="available-orders-section">
<view class="section-header">
<text class="section-title">附近订单</text>
<text class="refresh-btn" @click="refreshOrders">🔄 刷新</text>
<view class="section-header-actions">
<text class="refresh-btn" @click="refreshOrders">🔄 刷新</text>
<!-- 当可接取订单达到上限时显示更多入口 -->
<text v-if="availableOrders && availableOrders.length >= 20" class="more-btn" @click="goToAllOrders">更多 ➜</text>
</view>
</view>
<view v-if="availableOrders.length === 0" class="empty-orders">
@@ -164,6 +168,9 @@
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
@@ -211,6 +218,10 @@
data() {
return {
isOnline: true,
// 防抖:记录上次刷新时间,避免 onShow 导致的频繁 refresh
lastRefreshAt: 0,
// 控制是否启用自动刷新onShow——默认关闭避免频繁或意外刷新
enableAutoRefresh: false,
driverInfo: {
id: '',
@@ -243,101 +254,204 @@
}
},
onLoad() {
this.loadDriverInfo()
this.loadTodayStats()
this.loadCurrentTask()
this.loadAvailableOrders()
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()
},
onShow() {
// 页面显示时刷新数据
this.refreshData()
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: {
// 加载配送员信息
loadDriverInfo() {
// TODO: 调用API获取配送员信息
this.driverInfo.real_name = '张师傅'
this.driverInfo.rating = 4.8
this.driverInfo.total_orders = 1250
},
// 加载今日统计
loadTodayStats() {
// TODO: 调用API获取今日统计
this.todayStats = {
completed_orders: 8,
total_earning: '245.60',
total_distance: 45,
avg_rating: 4.9
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) {
this.driverInfo = Object.assign(this.driverInfo, res.data[0])
}
} catch (e) {
console.error('loadDriverInfo error', e)
}
},
// 加载当前任务
loadCurrentTask() {
// TODO: 调用API获取当前任务
this.currentTask = {
id: '1',
order_no: 'D202501081234',
status: 2, // 👈 设置为“已接取”,以便测试“开始取货”按钮
pickup_address: {
detail: '华强北商业区华强电子世界2楼A205',
area: '华强北'
},
delivery_address: {
detail: '南山区科技园深南大道9999号',
area: '科技园'
},
pickup_contact: {
name: '商家联系人',
phone: '138****5678'
},
delivery_contact: {
name: '张先生',
phone: '139****1234'
},
delivery_fee: 8.5,
distance: 12.5,
estimated_time: 35,
created_at: '2025-01-08T14:30:00Z'
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)
}
},
// 加载可接取订单
loadAvailableOrders() {
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
}
// TODO: 调用API获取附近订单
this.availableOrders = [
{
id: '2',
order_no: 'D202501081235',
pickup_address: {
detail: '福田区购物公园',
area: '购物公园'
},
delivery_address: {
detail: '南山区海岸城',
area: '海岸城'
},
delivery_fee: 12.0,
distance: 8.2,
estimated_time: 25,
created_at: '2025-01-08T15:00:00Z'
// 在加载过程中先清空,避免显示过期或闪现的数据
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_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 || ''
}
},
// 刷新数据
refreshData() {
this.loadTodayStats()
this.loadCurrentTask()
this.loadAvailableOrders()
async refreshData() {
await this.loadTodayStats()
await this.loadCurrentTask()
await this.loadAvailableOrders()
},
// 刷新订单列表
@@ -429,49 +543,71 @@
},
// 任务操作方法
acceptTask() {
// TODO: 调用API接受任务
if (this.currentTask) {
this.currentTask.status = 2 // 更新状态为“已接取”
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' })
}
uni.showToast({
title: '任务已接受',
icon: 'success'
})
},
startPickup() {
// TODO: 调用API开始取货
if (this.currentTask) {
this.currentTask.status = 3 // 更新状态为“取货中”
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' })
}
uni.showToast({
title: '开始取货',
icon: 'success'
})
},
confirmPickup() {
// TODO: 调用API确认取货
if (this.currentTask) {
this.currentTask.status = 5 // 更新状态为“已取货”
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' })
}
uni.showToast({
title: '取货完成',
icon: 'success'
})
},
// startDelivery() {
// // TODO: 调用API开始配送
// if (this.currentTask) {
// this.currentTask.status = 5 // 更新状态为“配送中”
// }
// uni.showToast({
// title: '开始配送',
// icon: 'success'
// })
// },
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() {
@@ -486,22 +622,23 @@
})
},
// 确认送达
confirmDelivery() {
// TODO: 调用API确认送达
if (this.currentTask) {
// 1. 将订单状态更新为“已完成” (假设5表示已完成)
this.currentTask.status = 5;
// 2. 将已完成的任务保存到本地存储,以便历史订单页面可以读取
const completedOrder = {...this.currentTask}; // 创建副本,避免引用问题
uni.setStorageSync('completed_order_for_history', completedOrder);
}
uni.showToast({
title: '配送完成',
icon: 'success'
})
this.currentTask = null
this.loadAvailableOrders()
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)
uni.showToast({ title: '配送完成', icon: 'success' })
this.currentTask = null
this.loadAvailableOrders()
}
} catch (e) {
console.error('confirmDelivery error', e)
uni.showToast({ title: '操作失败', icon: 'none' })
}
},
contactCustomer() {
@@ -538,14 +675,22 @@
},
// 订单操作方法
acceptOrder(orderId: string) {
// TODO: 调用API接受订单
uni.showToast({
title: '订单已接受',
icon: 'success'
})
this.loadCurrentTask()
this.loadAvailableOrders()
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' })
await this.loadCurrentTask()
await this.loadAvailableOrders()
}
} catch (e) {
console.error('acceptOrder error', e)
uni.showToast({ title: '接受订单失败', icon: 'none' })
}
},
// 导航方法
@@ -554,6 +699,13 @@
url: '/pages/mall/delivery/order-history'
})
},
// 跳转到“全部可接订单”页面
goToAllOrders() {
uni.navigateTo({
url: '/pages/mall/delivery/all'
})
},
goToEarnings() {
uni.navigateTo({
@@ -666,6 +818,17 @@
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);