feat(order-logistics): 改造订单详情物流查看功能
- 后端 ShopOrderServiceImpl.getByIdRel 填充发货单 shopOrderDelivery 信息 - 前端新增 ShopOrderDelivery 类型,ShopOrder 接口添加 shopOrderDelivery 字段 - 物流页支持仅传 orderId 即可加载订单及发货单,无需必须传 expressNo 和 expressCompany - 物流轨迹查询条件调整,失败时展示发货单信息,不阻塞页面 - 新增发货信息卡展示发货人、联系方式、地址、物流单号、发货时间及发货方式 - 新增收货信息卡,展示订单收货人名称、电话、地址 - 订单列表“待付款”Tab修复状态显示为“待付款”,兼容线下付款订单逻辑 - 组件OrderCard中去除isOffline判断,修正订单状态文案一致性 - 物流详情页复制功能优化,支持复制正确的物流单号 - 优化快递状态提示、时间格式化显示及页面空状态处理 - 电话联系客服按钮事件名称修正和调用逻辑优化
This commit is contained in:
@@ -138,6 +138,25 @@
|
||||
- 引入:从 `@tarojs/components` 新增导入 `Button`
|
||||
- 注意:微信客服 Button 在开发者工具中无效,需真机预览测试
|
||||
|
||||
## 订单详情页查看物流改造(显示 shopOrderDelivery)
|
||||
- 需求:订单详情页点击「查看物流」跳转 `/pages/order/logistics`,原页面强制要求 URL 带 expressNo + expressCompany,但详情页只传了 orderId,导致 toast「缺少物流参数」+ 空状态
|
||||
- 后端修复(`/Users/gxwebsoft/JAVA/guilixu-java`,1 个文件):
|
||||
- `ShopOrderServiceImpl.java` — `getByIdRel(orderId)` 原本只调 `baseMapper.selectListRel(param)`,没有像 `pageRel` 那样填充 `shopOrderDelivery`。新增私有方法 `fillShopOrderDelivery(order)`,在快递配送(deliveryType==0/null)且已发货(deliveryStatus>10)时调 `shopOrderDeliveryService.getByOrderId(orderId)` 查发货单,并关联 `shopExpressService.getById(expressId)` 取 expressName 填充
|
||||
- 编译验证:Maven compile 成功
|
||||
- 前端类型补充(`src/api/shop/shopOrder/model/index.ts`):
|
||||
- 新增 `ShopOrderDelivery` 接口(与后端 ShopOrderDelivery 实体对应:deliveryId/orderId/deliveryMethod/expressId/expressName/sendName/sendPhone/sendAddress/expressNo/createTime 等)
|
||||
- `ShopOrder` 接口新增 `shopOrderDelivery?: ShopOrderDelivery | null` 字段
|
||||
- 前端物流页改造(`src/pages/order/logistics.tsx`):
|
||||
- 不再强制要求 URL 带 expressNo/expressCompany;只要求 orderId
|
||||
- 调 `getShopOrder(orderId)` 加载订单(含后端填充的 shopOrderDelivery)
|
||||
- 运单号优先级:URL expressNo → delivery.expressNo → order.expressNo
|
||||
- 物流轨迹:仅在有 expressNo + expressCompany 代码时才查询 queryLogistics,失败不阻塞发货单展示
|
||||
- 新增「发货信息」卡:发货人/联系方式/发货地址/物流单号(可复制)/发货时间/发货方式(10手动/20无需物流/30电子面单)
|
||||
- 新增「收货信息」卡:从订单读 realName/mobile/address
|
||||
- 保留原物流轨迹时间线(仅在查询到时展示)和温馨提示
|
||||
- 空状态:订单接口也失败时才显示
|
||||
- 后端需重启服务才生效
|
||||
|
||||
## 修复 VIP 价格首页不显示 + 后端不认 price 字段
|
||||
- **问题 1(首页)**:`src/pages/index/index.tsx`(tabBar 首页)没接入 `useVipStatus`,热销推荐价格一直显示 `product.price` 而非 `dealerPrice`。商品详情/商城列表/购物车/分类都修了,唯独漏了 tabBar 首页
|
||||
- **问题 2(后端 DTO 缺字段)**:前端 `OrderGoodsItem` 已传 `price` 字段,但后端 `OrderCreateRequest.OrderGoodsItem`(嵌套静态类)**根本没有 `price` 字段**,Jackson 反序列化直接丢弃。后端 `OrderBusinessService.validateAndCalculateTotal()` 和 `saveOrderGoods()` 都用 `goods.getPrice()` 兜底 —— 这就是「前端传的对、后端处理就错」的根本原因
|
||||
@@ -157,3 +176,14 @@
|
||||
- `goods.salePrice` - 市场价(划掉的)
|
||||
- `goods.dealerPrice` - VIP 专享价
|
||||
- 前端传 `dealerPrice` 到 `OrderGoodsItem.price`,后端通过该字段下单
|
||||
|
||||
## 修复订单列表"待付款"Tab 下状态显示"待发货"
|
||||
- 文件:`src/components/common/OrderCard/index.tsx`
|
||||
- 问题:用户切到"待付款"Tab,下方订单卡片状态文字显示"待发货",与 Tab 不一致
|
||||
- 根因:线下付款(payType=9)订单的 `payStatus=false`(数据库 pay_status=0),后端 `statusFilter=0` 的 SQL 条件 `pay_status=0 AND order_status=0` 把它归入"待付款"Tab 是合理的(待商家确认收款)。但 OrderCard 的 `getCardStatus` 因 `isOffline=true` → `!isOffline=false` → 跳过"待付款"分支,进入 `if (deliveryStatus === 10)` → 显示"待发货"
|
||||
- 关键业务规则:
|
||||
- 货到付款(8):后端 Controller `saveLegacy()` 下单时已 `setPayStatus(true)`,不会进入 statusFilter=0
|
||||
- 线下付款(9):保持 `payStatus=false`,会进入 statusFilter=0
|
||||
- 微信支付(1):未付款 `payStatus=false`,正常进入 statusFilter=0
|
||||
- 修复:`getCardStatus` 第 30 行去掉 `&& !isOffline` 条件,改为 `if (!payStatus && !isCod) return { text: '待付款' }`。保留 `!isCod`(货到付款的特殊兜底,虽然正常流程不会走到这里);清理掉不再使用的 `isOffline` 变量
|
||||
- 决策依据:用户认可"线下付款订单出现在待付款 Tab"是合理的,因此只改前端文案判断,不动后端 SQL
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
import type { PageParam } from '@/api/index';
|
||||
import type { ShopOrderGoods } from '@/api/shop/shopOrderGoods/model';
|
||||
|
||||
/**
|
||||
* 发货单(与后端 ShopOrderDelivery 对应)
|
||||
*/
|
||||
export interface ShopOrderDelivery {
|
||||
// 发货单ID
|
||||
deliveryId?: number;
|
||||
// 订单ID
|
||||
orderId?: number;
|
||||
// 发货方式(10手动录入 20无需物流 30电子面单)
|
||||
deliveryMethod?: number;
|
||||
// 物流公司ID
|
||||
expressId?: number;
|
||||
// 物流公司名称(关联查询,非数据库字段)
|
||||
expressName?: string;
|
||||
// 发货人
|
||||
sendName?: string;
|
||||
// 发货人联系方式
|
||||
sendPhone?: string;
|
||||
// 发货地址
|
||||
sendAddress?: string;
|
||||
// 物流单号
|
||||
expressNo?: string;
|
||||
// 电子面单模板内容
|
||||
eorderHtml?: string;
|
||||
// 排序号
|
||||
sortNumber?: number;
|
||||
// 是否删除, 0否, 1是
|
||||
deleted?: number;
|
||||
// 租户id
|
||||
tenantId?: number;
|
||||
// 创建时间
|
||||
createTime?: string;
|
||||
// 修改时间
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
@@ -163,6 +199,8 @@ export interface ShopOrder {
|
||||
hasTakeGift?: string;
|
||||
// 订单商品项
|
||||
orderGoods?: ShopOrderGoods[];
|
||||
// 发货单信息(详情接口关联查询返回,仅快递配送且已发货时有值)
|
||||
shopOrderDelivery?: ShopOrderDelivery | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,6 @@ interface OrderCardProps {
|
||||
const getCardStatus = (order: ShopOrder): { text: string; color: string } => {
|
||||
const { payStatus, deliveryStatus, orderStatus, payType } = order
|
||||
const isCod = payType === 8 // 货到付款
|
||||
const isOffline = payType === 9 // 线下付款
|
||||
|
||||
if (orderStatus === 2) return { text: '已取消', color: '#999' }
|
||||
if (orderStatus === 3) return { text: '取消中', color: '#ff7d00' }
|
||||
@@ -26,8 +25,9 @@ const getCardStatus = (order: ShopOrder): { text: string; color: string } => {
|
||||
// 终态:orderStatus=1 必须最先判断,盖过 deliveryStatus/payStatus
|
||||
if (orderStatus === 1) return { text: '已完成', color: '#0e932e' }
|
||||
|
||||
// 货到付款(8)/线下付款(9):无需在线支付,下单即进入发货流程
|
||||
if (!payStatus && !isCod && !isOffline) return { text: '待付款', color: '#ee0a24' }
|
||||
// 货到付款(8):下单时后端已 setPayStatus(true),不会走到这里
|
||||
// 线下付款(9):保持 payStatus=false(待商家确认收款),应显示"待付款"与 Tab 一致
|
||||
if (!payStatus && !isCod) return { text: '待付款', color: '#ee0a24' }
|
||||
if (deliveryStatus === 10) return { text: '待发货', color: '#4b9cf5' }
|
||||
if (deliveryStatus === 20) return { text: '待收货', color: '#4b9cf5' }
|
||||
if (deliveryStatus === 30) return { text: '已收货', color: '#0e932e' }
|
||||
|
||||
@@ -2,8 +2,11 @@ import React, { useState, useEffect } from 'react'
|
||||
import { View, Text, ScrollView } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { Button } from '@nutui/nutui-react-taro'
|
||||
import { queryLogistics, formatLogisticsStatus, EXPRESS_COMPANIES } from '@/api/shop/shopLogistics'
|
||||
import { queryLogistics, formatLogisticsStatus } from '@/api/shop/shopLogistics'
|
||||
import type { LogisticsInfo, LogisticsTrack } from '@/api/shop/shopLogistics'
|
||||
import { getShopOrder } from '@/api/shop/shopOrder'
|
||||
import type { ShopOrder, ShopOrderDelivery } from '@/api/shop/shopOrder/model'
|
||||
import { OrderStatus } from '@/types/order'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import EmptyState from '@/components/common/EmptyState'
|
||||
|
||||
@@ -11,9 +14,18 @@ definePageConfig({
|
||||
navigationBarTitleText: '物流详情',
|
||||
})
|
||||
|
||||
// 发货方式映射
|
||||
const DELIVERY_METHOD_MAP: Record<number, string> = {
|
||||
10: '手动录入',
|
||||
20: '无需物流',
|
||||
30: '电子面单',
|
||||
}
|
||||
|
||||
const LogisticsPage: React.FC = () => {
|
||||
const { orderId, expressNo, expressCompany } = Taro.getCurrentInstance().router?.params || {}
|
||||
const { orderId, expressNo: urlExpressNo, expressCompany: urlExpressCompany } = Taro.getCurrentInstance().router?.params || {}
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [order, setOrder] = useState<ShopOrder | null>(null)
|
||||
const [delivery, setDelivery] = useState<ShopOrderDelivery | null>(null)
|
||||
const [logisticsInfo, setLogisticsInfo] = useState<LogisticsInfo | null>(null)
|
||||
const [trackList, setTrackList] = useState<LogisticsTrack[]>([])
|
||||
|
||||
@@ -22,27 +34,42 @@ const LogisticsPage: React.FC = () => {
|
||||
}, [])
|
||||
|
||||
const loadLogistics = async () => {
|
||||
if (!orderId) {
|
||||
Taro.showToast({ title: '缺少订单参数', icon: 'none' })
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
try {
|
||||
// 必须传入参数
|
||||
if (!expressNo || !expressCompany) {
|
||||
Taro.showToast({ title: '缺少物流参数', icon: 'none' })
|
||||
setLoading(false)
|
||||
return
|
||||
// 1) 拉取订单详情(含 shopOrderDelivery,由后端 getByIdRel 填充)
|
||||
let orderData: ShopOrder | null = null
|
||||
try {
|
||||
orderData = await getShopOrder(Number(orderId))
|
||||
} catch (err) {
|
||||
console.error('加载订单信息失败', err)
|
||||
}
|
||||
if (orderData) {
|
||||
setOrder(orderData)
|
||||
setDelivery(orderData.shopOrderDelivery ?? null)
|
||||
}
|
||||
|
||||
const res = await queryLogistics({
|
||||
orderId,
|
||||
expressNo,
|
||||
expressCompany,
|
||||
})
|
||||
if (res?.data) {
|
||||
setLogisticsInfo(res.data.logisticsInfo)
|
||||
setTrackList(res.data.trackList)
|
||||
// 2) 决定运单号 / 物流公司:优先 URL 参数,其次订单的 shopOrderDelivery
|
||||
const expressNo = urlExpressNo || orderData?.shopOrderDelivery?.expressNo || orderData?.expressNo || ''
|
||||
// expressCompany 通常是快递公司代码(SF/YTO 等);shopOrderDelivery.expressName 是中文名
|
||||
const expressCompany = urlExpressCompany || ''
|
||||
|
||||
// 3) 如果有运单号 + 物流公司代码,尝试查询物流轨迹(失败不阻塞发货单展示)
|
||||
if (expressNo && expressCompany) {
|
||||
try {
|
||||
const res = await queryLogistics({ orderId, expressNo, expressCompany })
|
||||
if (res?.data) {
|
||||
setLogisticsInfo(res.data.logisticsInfo || null)
|
||||
setTrackList(res.data.trackList || [])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('查询物流轨迹失败', err)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载物流信息失败', err)
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
@@ -50,49 +77,35 @@ const LogisticsPage: React.FC = () => {
|
||||
|
||||
// 复制单号
|
||||
const copyExpressNo = () => {
|
||||
if (logisticsInfo?.expressNo) {
|
||||
Taro.setClipboardData({
|
||||
data: logisticsInfo.expressNo,
|
||||
success: () => {
|
||||
Taro.showToast({ title: '单号已复制', icon: 'success' })
|
||||
},
|
||||
})
|
||||
}
|
||||
const no = logisticsInfo?.expressNo || delivery?.expressNo || order?.expressNo || ''
|
||||
if (!no) return
|
||||
Taro.setClipboardData({
|
||||
data: no,
|
||||
success: () => Taro.showToast({ title: '单号已复制', icon: 'success' }),
|
||||
})
|
||||
}
|
||||
|
||||
// 联系快递员(模拟)
|
||||
const contactRider = () => {
|
||||
// 联系客服
|
||||
const contactService = () => {
|
||||
Taro.makePhoneCall({
|
||||
phoneNumber: '400-811-1111',
|
||||
fail: () => {
|
||||
Taro.showToast({ title: '拨打失败', icon: 'none' })
|
||||
},
|
||||
fail: () => Taro.showToast({ title: '拨打失败', icon: 'none' }),
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string) => {
|
||||
if (!time) return ''
|
||||
const date = new Date(time)
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (time: string) => {
|
||||
if (!time) return ''
|
||||
const date = new Date(time)
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||||
const formatTime = (time?: string) => {
|
||||
if (!time) return '-'
|
||||
return time.replace('T', ' ').slice(0, 19)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading fullscreen />
|
||||
}
|
||||
|
||||
if (!logisticsInfo) {
|
||||
// 既无物流轨迹,又无发货单,又无订单 → 空状态
|
||||
const hasAnything = logisticsInfo || trackList?.length || delivery || order
|
||||
if (!hasAnything) {
|
||||
return (
|
||||
<View className='min-h-screen bg-gray-50 flex items-center justify-center'>
|
||||
<EmptyState text='暂无物流信息' />
|
||||
@@ -100,7 +113,30 @@ const LogisticsPage: React.FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const statusInfo = formatLogisticsStatus(logisticsInfo.status)
|
||||
const statusInfo = logisticsInfo ? formatLogisticsStatus(logisticsInfo.status) : null
|
||||
|
||||
// 发货状态文案:优先用 logisticsInfo.status,否则按订单 deliveryStatus 推断
|
||||
const getDeliveryStatusText = (): string => {
|
||||
if (logisticsInfo?.status) return logisticsInfo.status
|
||||
const ds = order?.deliveryStatus
|
||||
if (ds === 30) return '部分发货'
|
||||
if (ds === 20) return '已发货'
|
||||
if (ds === 10) return '未发货'
|
||||
if (order?.orderStatus === OrderStatus.Completed) return '已签收'
|
||||
return '已发货'
|
||||
}
|
||||
|
||||
// 物流公司名:优先 logisticsInfo,其次 delivery.expressName
|
||||
const expressCompanyName = logisticsInfo?.expressCompanyName || delivery?.expressName || '未知物流'
|
||||
// 运单号:优先 logisticsInfo,其次 delivery,最后 order
|
||||
const expressNo = logisticsInfo?.expressNo || delivery?.expressNo || order?.expressNo || ''
|
||||
// 发货人信息
|
||||
const sendName = delivery?.sendName || order?.expressMerchantName || '-'
|
||||
const sendPhone = delivery?.sendPhone || '-'
|
||||
const sendAddress = delivery?.sendAddress || '-'
|
||||
const deliveryMethodText = delivery?.deliveryMethod != null
|
||||
? DELIVERY_METHOD_MAP[delivery.deliveryMethod] || '-'
|
||||
: (expressNo ? '手动录入' : '无需物流')
|
||||
|
||||
return (
|
||||
<View className='min-h-screen bg-gray-50 pb-4 flex flex-col'>
|
||||
@@ -108,109 +144,147 @@ const LogisticsPage: React.FC = () => {
|
||||
{/* 快递信息卡片 */}
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<View className='flex items-center gap-3'>
|
||||
{/* 快递logo */}
|
||||
<View className='w-12 h-12 rounded-lg bg-gray-100 flex items-center justify-center'>
|
||||
<Text className='text-xl'>📦</Text>
|
||||
</View>
|
||||
<View className='flex-1'>
|
||||
<View className='flex items-center gap-2'>
|
||||
<Text className='text-sm font-medium text-gray-800'>{logisticsInfo.expressCompanyName}</Text>
|
||||
<Text className='text-sm font-medium text-gray-800'>{expressCompanyName}</Text>
|
||||
<Text className='text-xs text-gray-400'>•</Text>
|
||||
<Text className='text-sm text-gray-500'>{statusInfo.text}</Text>
|
||||
<Text className='text-sm text-gray-500'>{getDeliveryStatusText()}</Text>
|
||||
</View>
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
运单号: {logisticsInfo.expressNo}
|
||||
运单号: {expressNo || '暂无'}
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
className='px-3 py-1 rounded-full bg-gray-50'
|
||||
onClick={copyExpressNo}
|
||||
>
|
||||
<Text className='text-xs text-gray-500'>复制</Text>
|
||||
</View>
|
||||
{expressNo ? (
|
||||
<View
|
||||
className='px-3 py-1 rounded-full bg-gray-50'
|
||||
onClick={copyExpressNo}
|
||||
>
|
||||
<Text className='text-xs text-gray-500'>复制</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{/* 预计送达 */}
|
||||
{logisticsInfo.estimatedTime && (
|
||||
<View className='mt-3 pt-3 border-t border-gray-100'>
|
||||
<Text className='text-xs text-gray-400'>
|
||||
预计送达: {formatDate(logisticsInfo.estimatedTime)} 24:00 前
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 当前状态 */}
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<View className='flex items-start gap-3'>
|
||||
<View className={`w-10 h-10 rounded-full flex items-center justify-center ${statusInfo.icon === '🚚' ? 'bg-green-50' : 'bg-gray-50'}`}>
|
||||
<Text>{statusInfo.icon}</Text>
|
||||
</View>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-sm font-medium text-gray-800'>{logisticsInfo.status}</Text>
|
||||
{logisticsInfo.currentLocation && (
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
当前位于: {logisticsInfo.currentLocation}
|
||||
</Text>
|
||||
)}
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
更新时间: {formatTime(logisticsInfo.updateTime)}
|
||||
</Text>
|
||||
</View>
|
||||
{/* 发货方式 */}
|
||||
<View className='mt-3 pt-3 border-t border-gray-100 flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货方式</Text>
|
||||
<Text className='text-xs text-gray-600'>{deliveryMethodText}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 收货人信息 */}
|
||||
{logisticsInfo.receiverInfo && (
|
||||
{/* 发货单信息(shopOrderDelivery) */}
|
||||
{delivery ? (
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>发货信息</Text>
|
||||
<View className='space-y-2'>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货人</Text>
|
||||
<Text className='text-xs text-gray-600'>{sendName}</Text>
|
||||
</View>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>联系方式</Text>
|
||||
<Text className='text-xs text-gray-600'>{sendPhone}</Text>
|
||||
</View>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货地址</Text>
|
||||
<Text className='text-xs text-gray-600 text-right flex-1 ml-4'>{sendAddress}</Text>
|
||||
</View>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>物流单号</Text>
|
||||
<View className='flex items-center gap-1'>
|
||||
<Text className='text-xs text-gray-600'>{delivery.expressNo || '-'}</Text>
|
||||
{delivery.expressNo ? (
|
||||
<Text className='text-xs text-blue-500' onClick={copyExpressNo}>复制</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
{delivery.createTime ? (
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货时间</Text>
|
||||
<Text className='text-xs text-gray-600'>{formatTime(delivery.createTime)}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{/* 收货人信息(从订单读取) */}
|
||||
{order ? (
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-2 block'>收货信息</Text>
|
||||
<View className='flex items-center gap-2'>
|
||||
<Text className='text-sm text-gray-600'>{logisticsInfo.receiverInfo.name}</Text>
|
||||
<Text className='text-sm text-gray-500'>{logisticsInfo.receiverInfo.phone}</Text>
|
||||
<Text className='text-sm text-gray-600'>{order.realName || '-'}</Text>
|
||||
<Text className='text-sm text-gray-500'>{order.mobile || order.phone || '-'}</Text>
|
||||
</View>
|
||||
<Text className='text-xs text-gray-400 mt-1'>{logisticsInfo.receiverInfo.address}</Text>
|
||||
<Text className='text-xs text-gray-400 mt-1'>{order.address || '-'}</Text>
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
{/* 物流轨迹 */}
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>物流详情</Text>
|
||||
<View className='relative'>
|
||||
{/* 竖线 */}
|
||||
<View className='absolute left-4 top-0 bottom-0 w-px bg-gray-200' />
|
||||
{/* 物流轨迹(仅在查询到时展示) */}
|
||||
{trackList && trackList.length > 0 ? (
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>物流详情</Text>
|
||||
<View className='relative'>
|
||||
{/* 竖线 */}
|
||||
<View className='absolute left-4 top-0 bottom-0 w-px bg-gray-200' />
|
||||
|
||||
{trackList.map((item, idx) => (
|
||||
<View key={idx} className={`relative flex gap-3 pb-4 ${idx === trackList.length - 1 ? 'pb-0' : ''}`}>
|
||||
{/* 圆点 */}
|
||||
<View
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center z-10 ${
|
||||
item.isCompleted ? 'bg-green-500' : 'bg-white border-2 border-green-500'
|
||||
}`}
|
||||
>
|
||||
{item.isCompleted && idx !== 0 ? (
|
||||
<Text className='text-white text-xs'>✓</Text>
|
||||
) : !item.isCompleted ? (
|
||||
<View className='w-2 h-2 rounded-full bg-green-500' />
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{/* 内容 */}
|
||||
<View className='flex-1 pt-1'>
|
||||
<View className='flex justify-between items-start'>
|
||||
<Text className={`text-sm ${item.isCompleted ? 'text-gray-600' : 'text-gray-800 font-medium'}`}>
|
||||
{item.status}
|
||||
</Text>
|
||||
<Text className='text-xs text-gray-400'>{formatTime(item.time)}</Text>
|
||||
{trackList.map((item, idx) => (
|
||||
<View key={idx} className={`relative flex gap-3 pb-4 ${idx === trackList.length - 1 ? 'pb-0' : ''}`}>
|
||||
{/* 圆点 */}
|
||||
<View
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center z-10 ${
|
||||
item.isCompleted ? 'bg-green-500' : 'bg-white border-2 border-green-500'
|
||||
}`}
|
||||
>
|
||||
{item.isCompleted && idx !== 0 ? (
|
||||
<Text className='text-white text-xs'>✓</Text>
|
||||
) : !item.isCompleted ? (
|
||||
<View className='w-2 h-2 rounded-full bg-green-500' />
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{/* 内容 */}
|
||||
<View className='flex-1 pt-1'>
|
||||
<View className='flex justify-between items-start'>
|
||||
<Text className={`text-sm ${item.isCompleted ? 'text-gray-600' : 'text-gray-800 font-medium'}`}>
|
||||
{item.status}
|
||||
</Text>
|
||||
<Text className='text-xs text-gray-400'>{formatTime(item.time)}</Text>
|
||||
</View>
|
||||
<Text className={`text-xs mt-1 ${item.isCompleted ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||
{item.description}
|
||||
</Text>
|
||||
<Text className='text-xs text-gray-400 mt-1'>{item.location}</Text>
|
||||
</View>
|
||||
<Text className={`text-xs mt-1 ${item.isCompleted ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||
{item.description}
|
||||
</Text>
|
||||
<Text className='text-xs text-gray-400 mt-1'>{item.location}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{/* 当前状态(仅在查询到 logisticsInfo 时展示) */}
|
||||
{logisticsInfo ? (
|
||||
<View className='bg-white mx-3 mt-3 p-4 rounded-xl'>
|
||||
<View className='flex items-start gap-3'>
|
||||
<View className={`w-10 h-10 rounded-full flex items-center justify-center ${statusInfo?.icon === '🚚' ? 'bg-green-50' : 'bg-gray-50'}`}>
|
||||
<Text>{statusInfo?.icon}</Text>
|
||||
</View>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-sm font-medium text-gray-800'>{logisticsInfo.status}</Text>
|
||||
{logisticsInfo.currentLocation ? (
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
当前位于: {logisticsInfo.currentLocation}
|
||||
</Text>
|
||||
) : null}
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
更新时间: {formatTime(logisticsInfo.updateTime)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{/* 温馨提示 */}
|
||||
<View className='bg-orange-50 mx-3 mt-3 p-4 rounded-xl'>
|
||||
@@ -229,7 +303,7 @@ const LogisticsPage: React.FC = () => {
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1 rounded-full border-gray-300 text-gray-600'
|
||||
onClick={contactRider}
|
||||
onClick={contactService}
|
||||
>
|
||||
联系客服
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user