feat(ticket): 完善水票配送订单功能

- 优化导入路径,修复 PageParam 类型引用
- 新增 DeliverConfirmMode 类型定义,支持拍照完成和等待客户确认两种模式
- 实现配送确认的双模式功能,支持直接完成和等待确认流程
- 重构订单状态判断逻辑,完善配送流程状态管理
- 新增用户端确认收货功能,支持手动确认收货操作
- 优化订单列表展示,增加票号、取货点、门店电话等详细信息
- 添加地址复制和联系门店功能按钮
- 实现补传照片完成订单功能
- 更新订单流程状态显示,提供更准确的状态标识
- 添加配送确认模式切换的单选框界面
- 优化下单成功后的页面跳转逻辑
- 新增水票配送订单后端接口设计文档
This commit is contained in:
2026-02-06 20:33:56 +08:00
parent 661e7574ef
commit 25177d724e
5 changed files with 268 additions and 28 deletions

View File

@@ -16,7 +16,7 @@ import {
import { View, Text, Image } from '@tarojs/components';
import { pageGltUserTicket } from '@/api/glt/gltUserTicket';
import type { GltUserTicket } from '@/api/glt/gltUserTicket/model';
import { pageGltTicketOrder } from '@/api/glt/gltTicketOrder';
import { pageGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder';
import type { GltTicketOrder } from '@/api/glt/gltTicketOrder/model';
import { BaseUrl } from '@/config/app';
import dayjs from "dayjs";
@@ -37,7 +37,10 @@ const UserTicketList = () => {
const [orderPage, setOrderPage] = useState(1);
const [orderTotal, setOrderTotal] = useState(0);
const [activeTab, setActiveTab] = useState<'ticket' | 'order'>('ticket');
const [activeTab, setActiveTab] = useState<'ticket' | 'order'>(() => {
const tab = Taro.getCurrentInstance().router?.params?.tab
return tab === 'order' ? 'order' : 'ticket'
});
const [qrVisible, setQrVisible] = useState(false);
const [qrTicket, setQrTicket] = useState<GltUserTicket | null>(null);
@@ -253,13 +256,56 @@ const UserTicketList = () => {
return d.isValid() ? d.format('YYYY年MM月DD日 HH:mm:ss') : v;
};
const getOrderStatusText = (status?: number) => {
// Backend field meaning may vary; page asks for "是否送达".
if (status === 1) return { text: '已送达', type: 'success' as const };
if (status === 0) return { text: '未送达', type: 'warning' as const };
return { text: status == null ? '-' : String(status), type: 'primary' as const };
const getTicketOrderStatusMeta = (order: GltTicketOrder) => {
if (order.status === 1) return { text: '已冻结', type: 'warning' as const };
const ds = order.deliveryStatus
if (ds === 40 || order.receiveConfirmTime) return { text: '已完成', type: 'success' as const };
if (ds === 30 || order.sendEndTime) return { text: '待确认收货', type: 'primary' as const };
if (ds === 20 || order.sendStartTime) return { text: '配送中', type: 'primary' as const };
if (ds === 10 || order.riderId) return { text: '待配送', type: 'warning' as const };
return { text: '待派单', type: 'primary' as const };
};
const canUserConfirmReceive = (order: GltTicketOrder) => {
if (!order?.id) return false
if (order.status === 1) return false
if (order.deliveryStatus === 40) return false
if (order.receiveConfirmTime) return false
// 必须是“已送达”后才能确认收货
return !!order.sendEndTime || order.deliveryStatus === 30
}
const handleUserConfirmReceive = async (order: GltTicketOrder) => {
if (!order?.id) return
if (!canUserConfirmReceive(order)) return
const modal = await Taro.showModal({
title: '确认收货',
content: '请确认已收到本次送水,确认后将无法撤销。',
confirmText: '确认收货'
})
if (!modal.confirm) return
try {
Taro.showLoading({ title: '提交中...' })
const now = dayjs().format('YYYY-MM-DD HH:mm:ss')
await updateGltTicketOrder({
id: order.id,
deliveryStatus: 40,
receiveConfirmTime: now,
receiveConfirmType: 10
})
Taro.showToast({ title: '已确认收货', icon: 'success' })
await reloadOrders(true)
} catch (e) {
console.error('确认收货失败:', e)
Taro.showToast({ title: '确认失败,请重试', icon: 'none' })
} finally {
Taro.hideLoading()
}
}
useDidShow(() => {
if (activeTab === 'ticket') {
reloadTickets(true).then();
@@ -436,10 +482,52 @@ const UserTicketList = () => {
</View>
</View>
{(() => {
const meta = getOrderStatusText(item.status);
const meta = getTicketOrderStatusMeta(item);
return <Tag type={meta.type}>{meta.text}</Tag>;
})()}
</View>
<View className="mt-2 text-xs text-gray-500">
<Text>{item.id ?? '-'}</Text>
</View>
<View className="mt-1 text-xs text-gray-500">
<Text>{item.address || '-'}</Text>
</View>
{item.storeName ? (
<View className="mt-1 text-xs text-gray-500">
<Text>{item.storeName}</Text>
</View>
) : null}
{item.sendStartTime ? (
<View className="mt-1 text-xs text-gray-500">
<Text>{formatDateTime(item.sendStartTime)}</Text>
</View>
) : null}
{item.sendEndTime ? (
<View className="mt-1 text-xs text-gray-500">
<Text>{formatDateTime(item.sendEndTime)}</Text>
</View>
) : null}
{item.receiveConfirmTime ? (
<View className="mt-1 text-xs text-gray-500">
<Text>{formatDateTime(item.receiveConfirmTime)}</Text>
</View>
) : null}
{item.sendEndImg ? (
<View className="mt-3">
<Image src={item.sendEndImg} mode="aspectFill" style={{ width: '100%', height: '160px', borderRadius: '8px' }} />
</View>
) : null}
{canUserConfirmReceive(item) ? (
<View className="mt-3 flex justify-end">
<Button
type="primary"
size="small"
onClick={() => handleUserConfirmReceive(item)}
>
</Button>
</View>
) : null}
</View>
))}
</View>