forked from gxwebsoft/mp-10550
refactor(order): 重构订单状态处理逻辑并优化送水订单功能
- 将订单状态相关工具函数提取到独立的 utils 文件中 - 统一订单状态文本和颜色显示逻辑 - 移除重复的状态判断函数 - 优化送水订单列表的数据过滤逻辑 - 添加订单编辑模式支持 - 实现订单修改和取消功能 - 修复订单状态判断中的数值转换问题 - 优化送水订单的时间选择组件 - 添加订单数据加载和验证逻辑 - 重构订单详情页的条件渲染逻辑
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import Taro, { useDidShow } from '@tarojs/taro';
|
||||
import {
|
||||
Button,
|
||||
@@ -16,9 +16,8 @@ import {
|
||||
import { View, Text, Image } from '@tarojs/components';
|
||||
import { pageGltUserTicket } from '@/api/glt/gltUserTicket';
|
||||
import type { GltUserTicket } from '@/api/glt/gltUserTicket/model';
|
||||
import { pageGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder';
|
||||
import { pageGltTicketOrder, removeGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder';
|
||||
import type { GltTicketOrder } from '@/api/glt/gltTicketOrder/model';
|
||||
import { getShopUserAddress } from '@/api/shop/shopUserAddress';
|
||||
import { BaseUrl } from '@/config/app';
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@@ -47,8 +46,6 @@ const UserTicketList = () => {
|
||||
const [qrTicket, setQrTicket] = useState<GltUserTicket | null>(null);
|
||||
const [qrImageUrl, setQrImageUrl] = useState('');
|
||||
|
||||
const addressCacheRef = useRef<Record<number, { lng: number; lat: number; fullAddress?: string } | null>>({});
|
||||
|
||||
const getUserId = () => {
|
||||
const raw = Taro.getStorageSync('UserId');
|
||||
const id = Number(raw);
|
||||
@@ -188,17 +185,16 @@ const UserTicketList = () => {
|
||||
});
|
||||
|
||||
const resList = res?.list || [];
|
||||
const nextList = isRefresh ? resList : [...orderList, ...resList];
|
||||
const safeList = resList.filter((o) => Number((o as any)?.deleted) !== 1);
|
||||
const nextList = isRefresh ? safeList : [...orderList, ...safeList];
|
||||
setOrderList(nextList);
|
||||
const count = typeof res?.count === 'number' ? res.count : nextList.length;
|
||||
setOrderTotal(count);
|
||||
setOrderHasMore(nextList.length < count);
|
||||
const serverCount = typeof res?.count === 'number' ? res.count : undefined;
|
||||
const total = typeof serverCount === 'number' ? serverCount : nextList.length;
|
||||
setOrderTotal(total);
|
||||
setOrderHasMore(typeof serverCount === 'number' ? nextList.length < serverCount : resList.length >= PAGE_SIZE);
|
||||
|
||||
if (resList.length > 0) {
|
||||
setOrderPage(currentPage + 1);
|
||||
} else {
|
||||
setOrderHasMore(false);
|
||||
}
|
||||
if (resList.length > 0) setOrderPage(currentPage + 1);
|
||||
else setOrderHasMore(false);
|
||||
} catch (error) {
|
||||
console.error('获取送水订单失败:', error);
|
||||
Taro.showToast({ title: '获取送水订单失败', icon: 'error' });
|
||||
@@ -265,78 +261,60 @@ const UserTicketList = () => {
|
||||
return d.isValid() ? d.format('YYYY年MM月DD日') : v;
|
||||
};
|
||||
|
||||
const parseLatLng = (latRaw?: unknown, lngRaw?: unknown) => {
|
||||
const lat = typeof latRaw === 'number' ? latRaw : parseFloat(String(latRaw ?? ''));
|
||||
const lng = typeof lngRaw === 'number' ? lngRaw : parseFloat(String(lngRaw ?? ''));
|
||||
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
|
||||
if (Math.abs(lat) > 90 || Math.abs(lng) > 180) return null;
|
||||
return { lat, lng };
|
||||
const isTicketOrderPendingDelivery = (order: GltTicketOrder) => {
|
||||
if (!order?.id) return false;
|
||||
if (Number(order.status) === 1) return false;
|
||||
if (Number((order as any)?.deleted) === 1) return false;
|
||||
if (order.receiveConfirmTime || order.sendEndTime || order.sendStartTime) return false;
|
||||
|
||||
const ds = order.deliveryStatus;
|
||||
if (typeof ds === 'number') return ds === 10;
|
||||
return !!order.riderId;
|
||||
};
|
||||
|
||||
const handleNavigateToAddress = async (order: GltTicketOrder) => {
|
||||
try {
|
||||
// Prefer coordinates from backend if present (non-typed fields), otherwise fetch by addressId.
|
||||
const anyOrder = order as any;
|
||||
const direct =
|
||||
parseLatLng(anyOrder?.addressLat ?? anyOrder?.lat, anyOrder?.addressLng ?? anyOrder?.lng) ||
|
||||
parseLatLng(anyOrder?.receiverLat, anyOrder?.receiverLng);
|
||||
|
||||
let coords = direct;
|
||||
let fullAddress: string | undefined = order.address || undefined;
|
||||
|
||||
if (!coords && order.addressId) {
|
||||
const cached = addressCacheRef.current[order.addressId];
|
||||
if (cached) {
|
||||
coords = { lat: cached.lat, lng: cached.lng };
|
||||
fullAddress = fullAddress || cached.fullAddress;
|
||||
} else if (cached === null) {
|
||||
coords = null;
|
||||
} else {
|
||||
const addr = await getShopUserAddress(order.addressId);
|
||||
const parsed = parseLatLng(addr?.lat, addr?.lng);
|
||||
if (parsed) {
|
||||
coords = parsed;
|
||||
fullAddress = fullAddress || addr?.fullAddress || addr?.address || undefined;
|
||||
addressCacheRef.current[order.addressId] = { ...parsed, fullAddress };
|
||||
} else {
|
||||
addressCacheRef.current[order.addressId] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!coords) {
|
||||
if (fullAddress) {
|
||||
await Taro.setClipboardData({ data: fullAddress });
|
||||
Taro.showToast({ title: '未配置定位,地址已复制', icon: 'none' });
|
||||
} else {
|
||||
Taro.showToast({ title: '暂无可导航的地址', icon: 'none' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Taro.openLocation({
|
||||
latitude: coords.lat,
|
||||
longitude: coords.lng,
|
||||
name: '收货地址',
|
||||
address: fullAddress || ''
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('一键导航失败:', e);
|
||||
Taro.showToast({ title: '导航失败,请重试', icon: 'none' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleOneClickCall = async (order: GltTicketOrder) => {
|
||||
const phone = (order.riderPhone || order.storePhone || '').trim();
|
||||
if (!phone) {
|
||||
Taro.showToast({ title: '暂无可呼叫的电话', icon: 'none' });
|
||||
const handleOrderModify = async (order: GltTicketOrder) => {
|
||||
if (!order?.id) {
|
||||
Taro.showToast({ title: '订单信息不完整', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (!isTicketOrderPendingDelivery(order)) {
|
||||
Taro.showToast({ title: '仅“待配送”订单可修改', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
Taro.navigateTo({ url: `/user/ticket/use?orderId=${order.id}` });
|
||||
};
|
||||
|
||||
const handleOrderCancel = async (order: GltTicketOrder) => {
|
||||
if (!order?.id) {
|
||||
Taro.showToast({ title: '订单信息不完整', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (!isTicketOrderPendingDelivery(order)) {
|
||||
Taro.showToast({ title: '仅“待配送”订单可取消', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const modal = await Taro.showModal({
|
||||
title: '取消订单',
|
||||
content: '确定要取消该订单吗?取消后无法恢复。',
|
||||
confirmText: '确认取消'
|
||||
});
|
||||
if (!modal.confirm) return;
|
||||
|
||||
try {
|
||||
await Taro.makePhoneCall({ phoneNumber: phone });
|
||||
Taro.showLoading({ title: '取消中...' });
|
||||
try {
|
||||
await updateGltTicketOrder({ id: order.id, deleted: 1 });
|
||||
} catch (e) {
|
||||
await removeGltTicketOrder(order.id);
|
||||
}
|
||||
Taro.showToast({ title: '订单已取消', icon: 'success' });
|
||||
await reloadOrders(true);
|
||||
} catch (e) {
|
||||
console.error('一键呼叫失败:', e);
|
||||
Taro.showToast({ title: '呼叫失败,请手动拨打', icon: 'none' });
|
||||
console.error('取消送水订单失败:', e);
|
||||
Taro.showToast({ title: '取消失败,请重试', icon: 'none' });
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -576,30 +554,29 @@ const UserTicketList = () => {
|
||||
<View className="mt-1">
|
||||
<Text className="text-xs text-gray-500">下单时间:{formatDateTime(item.createTime)}</Text>
|
||||
</View>
|
||||
{(!!item.addressId || !!item.address || !!item.riderPhone || !!item.storePhone) ? (
|
||||
{item.id ? (
|
||||
<View className="mt-3 flex justify-end gap-2">
|
||||
{(!!item.addressId || !!item.address) ? (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void handleNavigateToAddress(item);
|
||||
}}
|
||||
>
|
||||
一键导航
|
||||
</Button>
|
||||
) : null}
|
||||
{(!!item.riderPhone || !!item.storePhone) ? (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void handleOneClickCall(item);
|
||||
}}
|
||||
>
|
||||
一键呼叫
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
size="small"
|
||||
disabled={!isTicketOrderPendingDelivery(item)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void handleOrderModify(item);
|
||||
}}
|
||||
>
|
||||
订单修改
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="danger"
|
||||
disabled={!isTicketOrderPendingDelivery(item)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void handleOrderCancel(item);
|
||||
}}
|
||||
>
|
||||
订单取消
|
||||
</Button>
|
||||
</View>
|
||||
) : null}
|
||||
{/*{item.storeName ? (*/}
|
||||
|
||||
Reference in New Issue
Block a user