From d25f80ccddcd60d044e053bf35c243815a5a4190 Mon Sep 17 00:00:00 2001
From: huangzhenbao <17818024429@163.com>
Date: Fri, 22 May 2026 10:16:51 +0800
Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E5=AE=8C=E5=96=84=E5=B1=85?=
=?UTF-8?q?=E5=AE=B6=E6=9C=8D=E5=8A=A1=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pages.delivery.json | 14 +
pages/mall/delivery/home/index.uvue | 11 +-
pages/mall/delivery/orders/checkin.uvue | 2 +-
pages/mall/delivery/orders/detail.uvue | 41 +-
pages/mall/delivery/orders/index.uvue | 33 +-
pages/mall/delivery/service-record/index.uvue | 8 +-
services/deliveryService.uts | 74 +--
services/serviceOrderService.uts | 500 ++++++++++++++++++
types/service-order.uts | 81 +++
utils/deliveryCareUi.uts | 16 +-
10 files changed, 670 insertions(+), 110 deletions(-)
create mode 100644 services/serviceOrderService.uts
create mode 100644 types/service-order.uts
diff --git a/pages.delivery.json b/pages.delivery.json
index 7d0d4a37..22447dbf 100644
--- a/pages.delivery.json
+++ b/pages.delivery.json
@@ -42,6 +42,20 @@
"navigationStyle": "custom"
}
},
+ {
+ "path": "pages/mall/delivery/orders/route",
+ "style": {
+ "navigationBarTitleText": "出发与导航",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/mall/delivery/orders/checkin",
+ "style": {
+ "navigationBarTitleText": "到岗签到",
+ "navigationStyle": "custom"
+ }
+ },
{
"path": "pages/mall/delivery/service-record/index",
"style": {
diff --git a/pages/mall/delivery/home/index.uvue b/pages/mall/delivery/home/index.uvue
index ed538c1f..08309c5d 100644
--- a/pages/mall/delivery/home/index.uvue
+++ b/pages/mall/delivery/home/index.uvue
@@ -39,8 +39,8 @@
服务中
- {{ incomeText }}
- 预计收入
+ {{ dashboard.completedCount }}
+ 已完成
@@ -89,7 +89,7 @@ import { computed, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
import type { DeliveryInfoType } from '@/types/delivery.uts'
-import { acceptServiceOrder, getDeliveryDashboardStats, getDeliveryProfile, markDeparted } from '@/services/deliveryService.uts'
+import { acceptServiceOrder, getDeliveryDashboardStats, getDeliveryProfile } from '@/services/deliveryService.uts'
import { getNextStepText, getPrimaryActionText } from '@/utils/deliveryCareUi.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
@@ -117,7 +117,6 @@ const onlineText = computed((): string => {
if (profile.value.onlineStatus == 'busy') return '忙碌'
return '离线'
})
-const incomeText = computed((): string => '¥' + String(dashboard.value.expectedIncome))
const nextActionText = computed((): string => dashboard.value.nextOrder == null ? '查看详情' : getPrimaryActionText(dashboard.value.nextOrder!.status))
const nextStepText = computed((): string => dashboard.value.nextOrder == null ? '暂无' : getNextStepText(dashboard.value.nextOrder!.status))
const nextStatusText = computed((): string => dashboard.value.nextOrder == null ? '' : dashboard.value.nextOrder!.statusText)
@@ -172,9 +171,7 @@ async function handleOrderAction(orderId: string) {
return
}
if (order.status == 'accepted' || order.status == 'waiting_departure') {
- await markDeparted(orderId)
- uni.showToast({ title: '已标记出发', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + orderId })
return
}
goDetail(orderId)
diff --git a/pages/mall/delivery/orders/checkin.uvue b/pages/mall/delivery/orders/checkin.uvue
index 2cae6157..8f16959a 100644
--- a/pages/mall/delivery/orders/checkin.uvue
+++ b/pages/mall/delivery/orders/checkin.uvue
@@ -127,7 +127,7 @@ async function submitCheckin() {
checkinMode: 'gps'
})
uni.showToast({ title: '签到成功', icon: 'success' })
- uni.navigateTo({ url: '/pages/mall/delivery/orders/execute?id=' + orderId.value })
+ uni.redirectTo({ url: '/pages/mall/delivery/service-record/index?id=' + orderId.value })
} finally {
submitting.value = false
}
diff --git a/pages/mall/delivery/orders/detail.uvue b/pages/mall/delivery/orders/detail.uvue
index 9e0d39e1..b8bd1c83 100644
--- a/pages/mall/delivery/orders/detail.uvue
+++ b/pages/mall/delivery/orders/detail.uvue
@@ -92,15 +92,10 @@ import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uv
import type { DeliveryOrderType } from '@/types/delivery.uts'
import {
acceptServiceOrder,
- checkInServiceOrder,
- completeServiceOrder,
getServiceOrderDetail,
- markArrived,
- markDeparted,
- rejectServiceOrder,
- startServiceOrder
+ rejectServiceOrder
} from '@/services/deliveryService.uts'
-import { getNextStepText, getPrimaryActionText, needsServiceRecord } from '@/utils/deliveryCareUi.uts'
+import { getNextStepText, getPrimaryActionText } from '@/utils/deliveryCareUi.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
import { getDeliveryRouteParam } from '@/utils/deliveryRoute.uts'
@@ -129,7 +124,10 @@ function makePhoneCall(phone: string) {
}
function mockNavigate() {
- uni.showToast({ title: '导航为 mock 占位', icon: 'none' })
+ if (order.value == null) {
+ return
+ }
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + order.value!.id })
}
function goException() {
@@ -164,27 +162,15 @@ async function handlePrimary() {
return
}
if (status == 'accepted' || status == 'waiting_departure') {
- await markDeparted(orderId.value)
- uni.showToast({ title: '已出发', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + orderId.value })
return
}
if (status == 'departed' || status == 'on_the_way') {
- await markArrived(orderId.value)
- uni.showToast({ title: '已到达', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + orderId.value })
return
}
if (status == 'arrived') {
- await checkInServiceOrder(orderId.value, '详情页签到', null)
- uni.showToast({ title: '签到成功', icon: 'success' })
- loadData()
- return
- }
- if (status == 'checked_in') {
- await startServiceOrder(orderId.value)
- uni.showToast({ title: '开始服务', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/checkin?id=' + orderId.value })
return
}
if (status == 'in_service' || status == 'serving' || status == 'completed') {
@@ -192,14 +178,7 @@ async function handlePrimary() {
return
}
if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') {
- if (needsServiceRecord(order.value)) {
- uni.showToast({ title: '请先填写服务记录', icon: 'none' })
- goRecord()
- return
- }
- await completeServiceOrder(orderId.value)
- uni.showToast({ title: '服务已完成', icon: 'success' })
- loadData()
+ uni.showToast({ title: '已完成服务,等待用户验收', icon: 'none' })
return
}
if (status == 'abnormal' || status == 'exception_pending') {
diff --git a/pages/mall/delivery/orders/index.uvue b/pages/mall/delivery/orders/index.uvue
index e19ede4c..28502a78 100644
--- a/pages/mall/delivery/orders/index.uvue
+++ b/pages/mall/delivery/orders/index.uvue
@@ -43,15 +43,10 @@ import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uv
import type { DeliveryOrderStatus, DeliveryOrderType } from '@/types/delivery.uts'
import {
acceptServiceOrder,
- checkInServiceOrder,
- completeServiceOrder,
getHistoryServiceOrders,
getPendingServiceOrders,
getTodayServiceOrders,
- markArrived,
- markDeparted,
- rejectServiceOrder,
- startServiceOrder
+ rejectServiceOrder
} from '@/services/deliveryService.uts'
import { getDeliveryOrderTabs, getPrimaryActionText } from '@/utils/deliveryCareUi.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
@@ -137,27 +132,15 @@ async function handleAction(orderId: string, status: DeliveryOrderStatus) {
return
}
if (status == 'accepted' || status == 'waiting_departure') {
- await markDeparted(orderId)
- uni.showToast({ title: '已标记出发', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + orderId })
return
}
if (status == 'departed' || status == 'on_the_way') {
- await markArrived(orderId)
- uni.showToast({ title: '已标记到达', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/route?id=' + orderId })
return
}
if (status == 'arrived') {
- await checkInServiceOrder(orderId, '已到达并签到', null)
- uni.showToast({ title: '签到成功', icon: 'success' })
- loadData()
- return
- }
- if (status == 'checked_in') {
- await startServiceOrder(orderId)
- uni.showToast({ title: '已开始服务', icon: 'success' })
- loadData()
+ uni.navigateTo({ url: '/pages/mall/delivery/orders/checkin?id=' + orderId })
return
}
if (status == 'in_service' || status == 'serving' || status == 'completed') {
@@ -165,13 +148,7 @@ async function handleAction(orderId: string, status: DeliveryOrderStatus) {
return
}
if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') {
- const result = await completeServiceOrder(orderId)
- if (result == null) {
- uni.showToast({ title: '请先填写服务记录', icon: 'none' })
- return
- }
- uni.showToast({ title: '服务已完成', icon: 'success' })
- loadData()
+ uni.showToast({ title: '已完成服务,等待用户验收', icon: 'none' })
return
}
goDetail(orderId)
diff --git a/pages/mall/delivery/service-record/index.uvue b/pages/mall/delivery/service-record/index.uvue
index 9aecc250..6738f1f7 100644
--- a/pages/mall/delivery/service-record/index.uvue
+++ b/pages/mall/delivery/service-record/index.uvue
@@ -60,7 +60,7 @@ import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
import type { DeliveryOrderType, DeliveryServiceItemType, DeliveryServiceRecordType } from '@/types/delivery.uts'
-import { getServiceOrderDetail, submitServiceRecord } from '@/services/deliveryService.uts'
+import { completeServiceOrder, getServiceOrderDetail, startServiceOrder, submitServiceRecord } from '@/services/deliveryService.uts'
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
import { getDeliveryRouteParam } from '@/utils/deliveryRoute.uts'
@@ -145,6 +145,9 @@ async function submitRecordAction() {
if (!validateRecord()) {
return
}
+ if (order.value != null && order.value.status != 'in_service' && order.value.status != 'serving') {
+ await startServiceOrder(orderId.value)
+ }
const record = {
id: 'record-' + orderId.value,
orderId: orderId.value,
@@ -176,7 +179,8 @@ async function submitRecordAction() {
updatedAt: new Date().toISOString().replace('T', ' ').substring(0, 19)
} as DeliveryServiceRecordType
await submitServiceRecord(orderId.value, record)
- uni.showToast({ title: '服务记录已提交', icon: 'success' })
+ await completeServiceOrder(orderId.value)
+ uni.showToast({ title: '服务记录已提交,等待验收', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/mall/delivery/orders/detail?id=' + orderId.value })
}, 300)
diff --git a/services/deliveryService.uts b/services/deliveryService.uts
index 1033c8be..f31d1403 100644
--- a/services/deliveryService.uts
+++ b/services/deliveryService.uts
@@ -1,4 +1,16 @@
import { getDeliveryProfileByUserId, loginDelivery as loginDeliveryApi } from '@/api/delivery.uts'
+import {
+ acceptOrder as acceptRealServiceOrder,
+ arriveOrder as arriveRealServiceOrder,
+ checkinOrder as checkinRealServiceOrder,
+ departOrder as departRealServiceOrder,
+ finishOrder as finishRealServiceOrder,
+ getDashboard as getRealDashboard,
+ getOrderDetail as getRealServiceOrderDetail,
+ getOrdersByTab as getRealOrdersByTab,
+ saveServiceRecord as saveRealServiceRecord,
+ startService as startRealService
+} from '@/services/serviceOrderService.uts'
import {
acceptCareOrder,
checkInCareOrder,
@@ -47,7 +59,7 @@ export async function getDeliveryProfile(): Promise {
if (!authResult.ok) {
return null
}
- return getDeliveryCareProfile()
+ return authResult.deliveryInfo
}
export async function getDeliveryByUserId(userId: string): Promise {
@@ -72,21 +84,21 @@ export async function getDeliveryDashboard(): Promise {
}
export async function getDeliveryOrders(params: DeliveryOrderQueryType): Promise> {
- if (params.tab == 'pending' || params.tab == 'pending_assignment' || params.tab == 'pending_accept') {
- return getPendingCareOrders()
+ if (params.tab == 'pending' || params.tab == 'pending_assignment') {
+ return await getRealOrdersByTab('pending')
}
if (params.tab == 'history' || params.tab == 'completed' || params.tab == 'archive') {
- return getHistoryCareOrders()
+ return await getRealOrdersByTab('history')
}
- return getTodayCareOrders()
+ return await getRealOrdersByTab('today')
}
export async function getDeliveryOrderDetail(id: string): Promise {
- return getCareOrderDetail(id)
+ return await getRealServiceOrderDetail(id)
}
export async function acceptDeliveryOrder(id: string): Promise {
- return acceptCareOrder(id)
+ return await acceptRealServiceOrder(id)
}
export async function rejectDeliveryOrder(id: string, reason: string): Promise {
@@ -94,27 +106,19 @@ export async function rejectDeliveryOrder(id: string, reason: string): Promise {
- const order = markCareOrderDeparted(id)
- if (order != null) {
- order.lastLocation = location
- }
- return order
+ return await departRealServiceOrder(id, location)
}
export async function arriveOrder(id: string, location: DeliveryLocationType): Promise {
- const order = markCareOrderArrived(id)
- if (order != null) {
- order.lastLocation = location
- }
- return order
+ return await arriveRealServiceOrder(id, location)
}
export async function checkinOrder(id: string, payload: DeliveryCheckinPayloadType): Promise {
- return checkInCareOrder(id, payload.location, payload.note)
+ return await checkinRealServiceOrder(id, payload)
}
export async function startService(id: string): Promise {
- return startCareService(id)
+ return await startRealService(id)
}
export async function saveServiceProgress(id: string, payload: DeliveryProgressPayloadType): Promise {
@@ -194,27 +198,27 @@ export async function updateDeliveryOnlineStatus(status: string): Promise {
- return getDeliveryCareDashboard()
+ return await getRealDashboard()
}
export async function getPendingServiceOrders(): Promise> {
- return getPendingCareOrders()
+ return await getRealOrdersByTab('pending')
}
export async function getTodayServiceOrders(): Promise> {
- return getTodayCareOrders()
+ return await getRealOrdersByTab('today')
}
export async function getHistoryServiceOrders(): Promise> {
- return getHistoryCareOrders()
+ return await getRealOrdersByTab('history')
}
export async function getServiceOrderDetail(orderId: string): Promise {
- return getCareOrderDetail(orderId)
+ return await getRealServiceOrderDetail(orderId)
}
export async function acceptServiceOrder(orderId: string): Promise {
- return acceptCareOrder(orderId)
+ return await acceptRealServiceOrder(orderId)
}
export async function rejectServiceOrder(orderId: string, reason: string): Promise {
@@ -222,27 +226,35 @@ export async function rejectServiceOrder(orderId: string, reason: string): Promi
}
export async function markDeparted(orderId: string): Promise {
- return markCareOrderDeparted(orderId)
+ return await departRealServiceOrder(orderId, null)
}
export async function markArrived(orderId: string): Promise {
- return markCareOrderArrived(orderId)
+ return await arriveRealServiceOrder(orderId, null)
}
export async function checkInServiceOrder(orderId: string, note: string, location: DeliveryLocationType | null = null): Promise {
- return checkInCareOrder(orderId, location, note)
+ if (location == null) {
+ return null
+ }
+ return await checkinRealServiceOrder(orderId, {
+ location,
+ note,
+ photos: [] as Array,
+ checkinMode: 'gps'
+ })
}
export async function startServiceOrder(orderId: string): Promise {
- return startCareService(orderId)
+ return await startRealService(orderId)
}
export async function submitServiceRecord(orderId: string, record: DeliveryServiceRecordType): Promise {
- return submitCareServiceRecord(orderId, record)
+ return await saveRealServiceRecord(orderId, record)
}
export async function completeServiceOrder(orderId: string): Promise {
- return completeCareOrder(orderId)
+ return await finishRealServiceOrder(orderId)
}
export async function submitAbnormalReport(orderId: string, report: DeliveryExceptionPayloadType): Promise {
diff --git a/services/serviceOrderService.uts b/services/serviceOrderService.uts
new file mode 100644
index 00000000..c2193636
--- /dev/null
+++ b/services/serviceOrderService.uts
@@ -0,0 +1,500 @@
+import supa from '@/components/supadb/aksupainstance.uts'
+import { getCurrentUserId } from '@/utils/store.uts'
+import { getDeliveryProfileByUserId } from '@/api/delivery.uts'
+import { getServiceOrderStatusText, normalizeServiceOrderStatus, type ServiceOrderStatus } from '@/types/service-order.uts'
+import type {
+ DeliveryCheckinPayloadType,
+ DeliveryDashboardType,
+ DeliveryLocationType,
+ DeliveryOrderType,
+ DeliveryServiceRecordType
+} from '@/types/delivery.uts'
+
+function nowIso(): string {
+ return new Date().toISOString()
+ }
+
+function buildId(prefix: string): string {
+ return prefix + '-' + String(Date.now()) + '-' + String(Math.floor(Math.random() * 100000)).padStart(5, '0')
+ }
+
+async function getCurrentStaffId(): Promise {
+ const userId = getCurrentUserId()
+ if (userId == '') {
+ return ''
+ }
+ const profile = await getDeliveryProfileByUserId(userId)
+ return profile != null ? profile.id : ''
+ }
+
+async function insertStatusLog(orderId: string, fromStatus: string, toStatus: ServiceOrderStatus, remark: string): Promise {
+ const userId = getCurrentUserId()
+ await supa.from('hss_service_order_status_logs').insert({
+ id: buildId('slog'),
+ order_id: orderId,
+ from_status: fromStatus,
+ to_status: toStatus,
+ operator_id: userId == '' ? null : userId,
+ operator_role: 'delivery',
+ remark,
+ created_at: nowIso()
+ }).execute()
+ }
+
+function emptyOrder(): DeliveryOrderType {
+ return {
+ id: '',
+ orderNo: '',
+ serviceType: '',
+ serviceName: '',
+ serviceCategory: '',
+ serviceItems: [] as Array,
+ elderId: '',
+ elderName: '',
+ elderNameMasked: '',
+ elderGender: '',
+ elderAge: 0,
+ elderPhone: '',
+ elderPhoneMasked: '',
+ fullElderName: '',
+ fullPhone: '',
+ contactRelation: '家属',
+ addressSummary: '',
+ address: '',
+ addressDetail: '',
+ fullAddress: '',
+ latitude: 0,
+ longitude: 0,
+ appointmentTime: '',
+ appointmentStartTime: '',
+ appointmentEndTime: '',
+ duration: 90,
+ estimatedDuration: 90,
+ price: 0,
+ staffIncome: 0,
+ distance: '',
+ actualStartTime: '',
+ actualEndTime: '',
+ status: 'pending_assignment' as any,
+ statusText: '',
+ statusTone: 'warning',
+ riskTags: [] as Array,
+ healthTags: [] as Array,
+ careLevel: '',
+ needFamilyPresent: false,
+ needMaterials: false,
+ remark: '',
+ merchantId: '',
+ merchantName: '',
+ deliveryStaffId: '',
+ deliveryStaffName: '',
+ acceptTime: '',
+ departTime: '',
+ arriveTime: '',
+ checkinTime: '',
+ finishTime: '',
+ cancelReason: '',
+ exceptionType: '',
+ exceptionDesc: '',
+ evidenceList: [] as Array,
+ signatureUrl: '',
+ signatureName: '',
+ satisfactionStatus: '',
+ settlementStatus: '',
+ archiveStatus: '',
+ createdAt: '',
+ updatedAt: '',
+ contactName: '',
+ contactPhone: '',
+ notices: [] as Array,
+ timeline: [] as Array,
+ statusLog: [] as Array,
+ serviceSummary: '',
+ progressNote: '',
+ distanceKm: '',
+ allowCheckinRadiusMeters: 100,
+ lastLocation: null,
+ trackPoints: [] as Array,
+ serviceRecord: null,
+ abnormalReport: null
+ } as DeliveryOrderType
+ }
+
+function safeJsonField(source: any, key: string): string {
+ const plain = JSON.parse(JSON.stringify(source)) as any
+ const value = plain[key]
+ if (value == null) {
+ return ''
+ }
+ return JSON.stringify(value)
+ }
+
+function statusToDeliveryStatus(status: ServiceOrderStatus): string {
+ if (status == 'assigned') return 'pending_assignment'
+ if (status == 'accepted') return 'accepted'
+ if (status == 'departed') return 'departed'
+ if (status == 'arrived') return 'arrived'
+ if (status == 'in_service') return 'in_service'
+ if (status == 'pending_acceptance') return 'pending_acceptance'
+ if (status == 'reviewed' || status == 'accepted_by_user' || status == 'settled') return 'completed'
+ if (status == 'rejected') return 'rejected'
+ if (status == 'cancelled') return 'cancelled'
+ if (status == 'exception') return 'abnormal'
+ return 'pending_assignment'
+ }
+
+function statusTone(status: ServiceOrderStatus): string {
+ if (status == 'pending_acceptance' || status == 'assigned') return 'warning'
+ if (status == 'accepted' || status == 'departed' || status == 'arrived' || status == 'in_service') return 'primary'
+ if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 'success'
+ if (status == 'rejected' || status == 'cancelled' || status == 'exception') return 'danger'
+ return 'warning'
+ }
+
+async function parseDeliveryOrder(orderId: string, item: any): Promise {
+ const order = emptyOrder()
+ const obj = JSON.parse(JSON.stringify(item)) as UTSJSONObject
+ const addressRaw = safeJsonField(item, 'address_snapshot_json')
+ const serviceRaw = safeJsonField(item, 'service_snapshot_json')
+ const addressObj = JSON.parse(addressRaw == '' ? '{}' : addressRaw) as UTSJSONObject
+ const serviceObj = JSON.parse(serviceRaw == '' ? '{}' : serviceRaw) as UTSJSONObject
+ const normalizedStatus = normalizeServiceOrderStatus(obj.getString('status') ?? '')
+ order.id = obj.getString('id') ?? ''
+ order.orderNo = obj.getString('order_no') ?? ''
+ order.serviceType = serviceObj.getString('category') ?? '居家服务'
+ order.serviceName = obj.getString('service_name') ?? ''
+ order.serviceCategory = serviceObj.getString('category') ?? ''
+ order.elderName = obj.getString('recipient_name') ?? ''
+ order.elderNameMasked = order.elderName
+ order.fullElderName = order.elderName
+ order.elderPhone = obj.getString('recipient_phone') ?? ''
+ order.elderPhoneMasked = order.elderPhone
+ order.fullPhone = order.elderPhone
+ order.contactName = obj.getString('contact_name') ?? ''
+ order.contactPhone = obj.getString('contact_phone') ?? ''
+ order.contactRelation = '家属'
+ order.address = addressObj.getString('fullAddress') ?? ''
+ order.addressDetail = addressObj.getString('detailAddress') ?? ''
+ order.fullAddress = order.address
+ order.latitude = addressObj.getNumber('latitude') ?? 0
+ order.longitude = addressObj.getNumber('longitude') ?? 0
+ order.appointmentTime = obj.getString('appointment_time') ?? ''
+ order.appointmentStartTime = order.appointmentTime
+ order.appointmentEndTime = order.appointmentTime
+ order.duration = 90
+ order.estimatedDuration = 90
+ order.price = serviceObj.getNumber('price') ?? 0
+ order.staffIncome = order.price
+ order.status = statusToDeliveryStatus(normalizedStatus) as any
+ order.statusText = getServiceOrderStatusText(normalizedStatus)
+ order.statusTone = statusTone(normalizedStatus)
+ order.remark = obj.getString('remark') ?? ''
+ order.deliveryStaffId = obj.getString('current_staff_id') ?? ''
+ order.acceptTime = obj.getString('accepted_at') ?? ''
+ order.departTime = obj.getString('departed_at') ?? ''
+ order.arriveTime = obj.getString('arrived_at') ?? ''
+ order.actualStartTime = obj.getString('service_started_at') ?? ''
+ order.startServiceTime = order.actualStartTime
+ order.finishTime = obj.getString('completed_at') ?? ''
+ order.createdAt = obj.getString('created_at') ?? ''
+ order.updatedAt = obj.getString('updated_at') ?? ''
+ order.allowCheckinRadiusMeters = 100
+ const logsResponse = await supa.from('hss_service_order_status_logs').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (logsResponse.data != null) {
+ const rawLogs = logsResponse.data as any[]
+ for (let i = 0; i < rawLogs.length; i++) {
+ const logObj = JSON.parse(JSON.stringify(rawLogs[i])) as UTSJSONObject
+ order.timeline.push({
+ id: logObj.getString('id') ?? '',
+ title: getServiceOrderStatusText(normalizeServiceOrderStatus(logObj.getString('to_status') ?? 'created')),
+ time: logObj.getString('created_at') ?? '',
+ description: logObj.getString('remark') ?? ''
+ })
+ }
+ }
+ const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (recordResponse.data != null) {
+ const raw = recordResponse.data as any[]
+ if (raw.length > 0) {
+ const recordObj = JSON.parse(JSON.stringify(raw[0])) as UTSJSONObject
+ order.checkinTime = recordObj.getString('checkin_time') ?? ''
+ order.serviceRecord = {
+ id: recordObj.getString('id') ?? '',
+ orderId: orderId,
+ startTime: recordObj.getString('service_started_at') ?? '',
+ endTime: recordObj.getString('service_finished_at') ?? '',
+ actualDurationMinutes: recordObj.getNumber('actual_duration_minutes') ?? 0,
+ serviceItems: [] as Array,
+ serviceContent: [] as Array,
+ processNote: recordObj.getString('summary') ?? '',
+ elderStatus: '',
+ healthMetrics: { bloodPressure: '', heartRate: '', bloodSugar: '', bloodOxygen: '' },
+ materialsUsed: '',
+ abnormalNote: '',
+ photos: [] as Array,
+ staffRemark: recordObj.getString('remark') ?? '',
+ familyConfirmation: { method: 'none', code: '', signatureName: '', signatureUrl: '', confirmedAt: '' },
+ createdAt: recordObj.getString('created_at') ?? '',
+ updatedAt: recordObj.getString('updated_at') ?? ''
+ } as any
+ }
+ }
+ const evidenceResponse = await supa.from('hss_service_evidence_files').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (evidenceResponse.data != null) {
+ const rawEvidence = evidenceResponse.data as any[]
+ for (let i = 0; i < rawEvidence.length; i++) {
+ const evidenceObj = JSON.parse(JSON.stringify(rawEvidence[i])) as UTSJSONObject
+ order.evidenceList.push({
+ id: evidenceObj.getString('id') ?? '',
+ orderId: orderId,
+ phase: evidenceObj.getString('phase') ?? '',
+ fileType: evidenceObj.getString('file_type') ?? 'image',
+ name: evidenceObj.getString('storage_path') ?? '',
+ url: evidenceObj.getString('file_url') ?? '',
+ localPath: '',
+ status: 'success',
+ progress: 100,
+ retryable: false,
+ createdAt: evidenceObj.getString('created_at') ?? ''
+ })
+ }
+ }
+ return order
+ }
+
+export async function getDashboard(): Promise {
+ const orders = await getOrdersByTab('all')
+ let pending = 0
+ let today = 0
+ let serving = 0
+ let completed = 0
+ let nextOrder: DeliveryOrderType | null = null
+ for (let i = 0; i < orders.length; i++) {
+ const item = orders[i]
+ if (item.status == 'pending_assignment') pending++
+ if (item.status == 'pending_assignment' || item.status == 'accepted' || item.status == 'departed' || item.status == 'arrived' || item.status == 'in_service') today++
+ if (item.status == 'in_service') serving++
+ if (item.status == 'completed' || item.status == 'pending_acceptance') completed++
+ if (nextOrder == null && item.status != 'completed' && item.status != 'cancelled' && item.status != 'abnormal') {
+ nextOrder = item
+ }
+ }
+ return {
+ pendingAssignmentCount: pending,
+ pendingAcceptCount: pending,
+ todayOrderCount: today,
+ pendingDepartCount: 0,
+ servingCount: serving,
+ completedCount: completed,
+ exceptionCount: 0,
+ expectedIncome: 0,
+ onlineStatus: 'online',
+ nextOrder,
+ recentOrders: orders.slice(0, 5)
+ } as DeliveryDashboardType
+ }
+
+export async function getOrdersByTab(tab: string): Promise> {
+ const staffId = await getCurrentStaffId()
+ if (staffId == '') {
+ return [] as Array
+ }
+ const response = await supa.from('hss_service_orders').select('*').eq('current_staff_id', staffId).order('created_at', { ascending: false }).execute()
+ if (response.error != null || response.data == null) {
+ return [] as Array
+ }
+ const rawOrders = response.data as any[]
+ const result = [] as Array
+ for (let i = 0; i < rawOrders.length; i++) {
+ const orderObj = JSON.parse(JSON.stringify(rawOrders[i])) as UTSJSONObject
+ const normalized = normalizeServiceOrderStatus(orderObj.getString('status') ?? '')
+ let matched = true
+ if (tab == 'pending') {
+ matched = normalized == 'assigned'
+ } else if (tab == 'today') {
+ matched = normalized == 'assigned' || normalized == 'accepted' || normalized == 'departed' || normalized == 'arrived' || normalized == 'in_service' || normalized == 'pending_acceptance'
+ } else if (tab == 'history') {
+ matched = normalized == 'pending_acceptance' || normalized == 'accepted_by_user' || normalized == 'reviewed' || normalized == 'settled' || normalized == 'exception' || normalized == 'cancelled'
+ }
+ if (tab == 'all' || matched) {
+ result.push(await parseDeliveryOrder(orderObj.getString('id') ?? '', rawOrders[i]))
+ }
+ }
+ return result
+ }
+
+export async function getOrderDetail(orderId: string): Promise {
+ const response = await supa.from('hss_service_orders').select('*').eq('id', orderId).single().execute()
+ if (response.error != null || response.data == null) {
+ return null
+ }
+ return await parseDeliveryOrder(orderId, response.data)
+ }
+
+async function updateOrderStatus(orderId: string, nextStatus: ServiceOrderStatus, updateData: UTSJSONObject, remark: string): Promise {
+ const current = await getOrderDetail(orderId)
+ if (current == null) {
+ return null
+ }
+ const map = JSON.parse('{}') as UTSJSONObject
+ map.set('status', nextStatus)
+ map.set('updated_at', nowIso())
+ const iterator = updateData.keys()
+ while (iterator.hasNext()) {
+ const key = iterator.next()
+ map.set(key, updateData.get(key))
+ }
+ const response = await supa.from('hss_service_orders').update(map).eq('id', orderId).execute()
+ if (response.error != null) {
+ console.error('updateOrderStatus failed', response.error)
+ return null
+ }
+ await insertStatusLog(orderId, normalizeServiceOrderStatus(current.status as string), nextStatus, remark)
+ return await getOrderDetail(orderId)
+ }
+
+export async function acceptOrder(orderId: string): Promise {
+ const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (assignmentResponse.data != null) {
+ const raw = assignmentResponse.data as any[]
+ if (raw.length > 0) {
+ const assignmentId = JSON.parse(JSON.stringify(raw[0]))['id'] as string
+ await supa.from('hss_service_assignments').update({ status: 'accepted', accepted_at: nowIso(), updated_at: nowIso() }).eq('id', assignmentId).execute()
+ }
+ }
+ const updateData = JSON.parse('{}') as UTSJSONObject
+ updateData.set('accepted_at', nowIso())
+ return await updateOrderStatus(orderId, 'accepted', updateData, '服务人员接单')
+ }
+
+export async function departOrder(orderId: string, location: DeliveryLocationType | null): Promise {
+ const updateData = JSON.parse('{}') as UTSJSONObject
+ updateData.set('departed_at', nowIso())
+ return await updateOrderStatus(orderId, 'departed', updateData, location == null ? '服务人员出发' : '服务人员出发:' + location.address)
+ }
+
+export async function arriveOrder(orderId: string, location: DeliveryLocationType | null): Promise {
+ const updateData = JSON.parse('{}') as UTSJSONObject
+ updateData.set('arrived_at', nowIso())
+ return await updateOrderStatus(orderId, 'arrived', updateData, location == null ? '服务人员到达' : '服务人员到达:' + location.address)
+ }
+
+export async function checkinOrder(orderId: string, payload: DeliveryCheckinPayloadType): Promise {
+ const current = await getOrderDetail(orderId)
+ if (current == null) {
+ return null
+ }
+ let assignmentId = ''
+ const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (assignmentResponse.data != null) {
+ const rawAssignments = assignmentResponse.data as any[]
+ if (rawAssignments.length > 0) {
+ assignmentId = JSON.parse(JSON.stringify(rawAssignments[0]))['id'] as string
+ }
+ }
+ let recordId = buildId('ser')
+ const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (recordResponse.data != null) {
+ const records = recordResponse.data as any[]
+ if (records.length > 0) {
+ recordId = JSON.parse(JSON.stringify(records[0]))['id'] as string
+ await supa.from('hss_service_execution_records').update({
+ checkin_time: nowIso(),
+ checkin_latitude: payload.location.latitude,
+ checkin_longitude: payload.location.longitude,
+ checkin_address: payload.location.address,
+ remark: payload.note,
+ updated_at: nowIso()
+ }).eq('id', recordId).execute()
+ } else {
+ await supa.from('hss_service_execution_records').insert({
+ id: recordId,
+ order_id: orderId,
+ assignment_id: assignmentId,
+ checkin_time: nowIso(),
+ checkin_latitude: payload.location.latitude,
+ checkin_longitude: payload.location.longitude,
+ checkin_address: payload.location.address,
+ remark: payload.note,
+ created_at: nowIso(),
+ updated_at: nowIso()
+ }).execute()
+ }
+ }
+ for (let i = 0; i < payload.photos.length; i++) {
+ await supa.from('hss_service_evidence_files').insert({
+ id: buildId('sef'),
+ order_id: orderId,
+ execution_record_id: recordId,
+ phase: 'checkin',
+ file_type: 'image',
+ storage_path: payload.photos[i],
+ file_url: payload.photos[i],
+ latitude: payload.location.latitude,
+ longitude: payload.location.longitude,
+ captured_at: nowIso(),
+ created_at: nowIso()
+ }).execute()
+ }
+ return current
+ }
+
+export async function startService(orderId: string): Promise {
+ let recordId = ''
+ const recordResponse = await supa.from('hss_service_execution_records').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (recordResponse.data != null) {
+ const records = recordResponse.data as any[]
+ if (records.length > 0) {
+ recordId = JSON.parse(JSON.stringify(records[0]))['id'] as string
+ await supa.from('hss_service_execution_records').update({ service_started_at: nowIso(), updated_at: nowIso() }).eq('id', recordId).execute()
+ }
+ }
+ const updateData = JSON.parse('{}') as UTSJSONObject
+ updateData.set('service_started_at', nowIso())
+ return await updateOrderStatus(orderId, 'in_service', updateData, '开始服务')
+ }
+
+export async function saveServiceRecord(orderId: string, record: DeliveryServiceRecordType): Promise {
+ let assignmentId = ''
+ const assignmentResponse = await supa.from('hss_service_assignments').select('*').eq('order_id', orderId).order('created_at', { ascending: false }).execute()
+ if (assignmentResponse.data != null) {
+ const assignments = assignmentResponse.data as any[]
+ if (assignments.length > 0) {
+ assignmentId = JSON.parse(JSON.stringify(assignments[0]))['id'] as string
+ }
+ }
+ await supa.from('hss_service_execution_records').upsert({
+ id: record.id,
+ order_id: orderId,
+ assignment_id: assignmentId,
+ service_started_at: record.startTime,
+ service_finished_at: record.endTime,
+ actual_duration_minutes: record.actualDurationMinutes,
+ service_items_json: record.serviceItems as any,
+ summary: record.processNote,
+ remark: record.staffRemark,
+ updated_at: nowIso(),
+ created_at: record.createdAt
+ }).execute()
+ for (let i = 0; i < record.photos.length; i++) {
+ await supa.from('hss_service_evidence_files').insert({
+ id: buildId('sef'),
+ order_id: orderId,
+ execution_record_id: record.id,
+ phase: 'service',
+ file_type: 'image',
+ storage_path: record.photos[i],
+ file_url: record.photos[i],
+ captured_at: nowIso(),
+ created_at: nowIso()
+ }).execute()
+ }
+ return await getOrderDetail(orderId)
+ }
+
+export async function finishOrder(orderId: string): Promise {
+ const updateData = JSON.parse('{}') as UTSJSONObject
+ updateData.set('completed_at', nowIso())
+ updateData.set('pending_acceptance_at', nowIso())
+ return await updateOrderStatus(orderId, 'pending_acceptance', updateData, '服务完成,等待用户验收')
+ }
\ No newline at end of file
diff --git a/types/service-order.uts b/types/service-order.uts
new file mode 100644
index 00000000..0d0923d7
--- /dev/null
+++ b/types/service-order.uts
@@ -0,0 +1,81 @@
+export type ServiceOrderStatus =
+ 'created' |
+ 'paid' |
+ 'assigned' |
+ 'accepted' |
+ 'rejected' |
+ 'departed' |
+ 'arrived' |
+ 'in_service' |
+ 'completed' |
+ 'pending_acceptance' |
+ 'accepted_by_user' |
+ 'reviewed' |
+ 'settled' |
+ 'cancelled' |
+ 'exception'
+
+export const SERVICE_ORDER_STATUS_LIST: Array = [
+ 'created',
+ 'paid',
+ 'assigned',
+ 'accepted',
+ 'rejected',
+ 'departed',
+ 'arrived',
+ 'in_service',
+ 'completed',
+ 'pending_acceptance',
+ 'accepted_by_user',
+ 'reviewed',
+ 'settled',
+ 'cancelled',
+ 'exception'
+]
+
+export type ServiceOrderTimelineItemType = {
+ id: string
+ orderId: string
+ fromStatus: string
+ toStatus: ServiceOrderStatus
+ operatorId: string
+ operatorRole: string
+ remark: string
+ createdAt: string
+}
+
+export function getServiceOrderStatusText(status: ServiceOrderStatus): string {
+ if (status == 'created') return '待处理'
+ if (status == 'paid') return '已支付'
+ if (status == 'assigned') return '已派单'
+ if (status == 'accepted') return '已接单'
+ if (status == 'rejected') return '已拒单'
+ if (status == 'departed') return '已出发'
+ if (status == 'arrived') return '已到达'
+ if (status == 'in_service') return '服务中'
+ if (status == 'completed') return '已完成'
+ if (status == 'pending_acceptance') return '待验收'
+ if (status == 'accepted_by_user') return '已验收'
+ if (status == 'reviewed') return '已评价'
+ if (status == 'settled') return '已结算'
+ if (status == 'cancelled') return '已取消'
+ return '异常'
+}
+
+export function normalizeServiceOrderStatus(status: string): ServiceOrderStatus {
+ if (status == 'created' || status == 'submitted') return 'created'
+ if (status == 'paid') return 'paid'
+ if (status == 'assigned' || status == 'pending_dispatch' || status == 'pending_assignment') return 'assigned'
+ if (status == 'accepted' || status == 'pending_accept') return 'accepted'
+ if (status == 'rejected') return 'rejected'
+ if (status == 'departed' || status == 'waiting_departure' || status == 'on_the_way') return 'departed'
+ if (status == 'arrived' || status == 'checked_in') return 'arrived'
+ if (status == 'in_service' || status == 'serving') return 'in_service'
+ if (status == 'completed') return 'completed'
+ if (status == 'pending_acceptance' || status == 'pending_confirm' || status == 'pending_submit') return 'pending_acceptance'
+ if (status == 'accepted_by_user') return 'accepted_by_user'
+ if (status == 'reviewed') return 'reviewed'
+ if (status == 'settled') return 'settled'
+ if (status == 'cancelled') return 'cancelled'
+ return 'exception'
+}
\ No newline at end of file
diff --git a/utils/deliveryCareUi.uts b/utils/deliveryCareUi.uts
index e1e01b5f..405e6d85 100644
--- a/utils/deliveryCareUi.uts
+++ b/utils/deliveryCareUi.uts
@@ -14,16 +14,14 @@ export function getDeliveryOrderTabs(): Array {
}
export function isHistoryStatus(status: DeliveryOrderStatus): boolean {
- return status == 'completed' || status == 'rejected' || status == 'cancelled' || status == 'abnormal' || status == 'terminated' || status == 'archived'
+ return status == 'completed' || status == 'pending_acceptance' || status == 'rejected' || status == 'cancelled' || status == 'abnormal' || status == 'terminated' || status == 'archived'
}
export function getDeliveryStatusLabel(status: DeliveryOrderStatus): string {
if (status == 'pending_assignment' || status == 'pending_accept') return '待接单'
if (status == 'accepted') return '已接单'
- if (status == 'waiting_departure') return '待出发'
if (status == 'departed' || status == 'on_the_way') return '已出发'
if (status == 'arrived') return '已到达'
- if (status == 'checked_in') return '已签到'
if (status == 'in_service' || status == 'serving') return '服务中'
if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') return '待确认'
if (status == 'completed') return '已完成'
@@ -36,12 +34,11 @@ export function getDeliveryStatusLabel(status: DeliveryOrderStatus): string {
export function getPrimaryActionText(status: DeliveryOrderStatus): string {
if (status == 'pending_assignment' || status == 'pending_accept') return '接单'
- if (status == 'accepted' || status == 'waiting_departure') return '我已出发'
+ if (status == 'accepted' || status == 'waiting_departure') return '前往服务'
if (status == 'departed' || status == 'on_the_way') return '我已到达'
if (status == 'arrived') return '签到'
- if (status == 'checked_in') return '开始服务'
if (status == 'in_service' || status == 'serving') return '填写记录'
- if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') return '完成服务'
+ if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') return '等待验收'
if (status == 'completed') return '查看记录'
if (status == 'abnormal' || status == 'exception_pending') return '查看异常'
return '查看详情'
@@ -49,12 +46,11 @@ export function getPrimaryActionText(status: DeliveryOrderStatus): string {
export function getNextStepText(status: DeliveryOrderStatus): string {
if (status == 'pending_assignment' || status == 'pending_accept') return '确认是否接单'
- if (status == 'accepted' || status == 'waiting_departure') return '前往服务对象地址'
+ if (status == 'accepted' || status == 'waiting_departure') return '前往服务对象地址并发起导航'
if (status == 'departed' || status == 'on_the_way') return '到达服务地点'
if (status == 'arrived') return '完成签到确认'
- if (status == 'checked_in') return '开始本次上门服务'
- if (status == 'in_service' || status == 'serving') return '补充服务记录'
- if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') return '确认并完成订单'
+ if (status == 'in_service' || status == 'serving') return '补充服务记录并完成服务'
+ if (status == 'pending_confirm' || status == 'pending_acceptance' || status == 'pending_submit') return '等待用户确认验收'
if (status == 'completed') return '查看历史记录'
if (status == 'abnormal' || status == 'exception_pending') return '跟进异常处置'
return '查看订单详情'