- 新增地址类型定义,增强前端地址数据结构 - 新增地址编辑页面,支持地址智能识别和定位选点功能 - 地址编辑支持省市区选择及默认地址设置 - 新增地址列表页面,支持地址展示、删除、编辑和选择功能 - 实现售后申请页面,支持选择售后类型和退款原因 - 售后申请支持商品选择、退款金额计算和凭证上传 - 新增售后详情页面,支持售后状态展示及申请取消 - 优化页面加载和用户交互体验,增加错误提示和权限处理
251 lines
9.1 KiB
TypeScript
251 lines
9.1 KiB
TypeScript
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 type { LogisticsInfo, LogisticsTrack } from '@/api/shop/shopLogistics'
|
||
import Loading from '@/components/common/Loading'
|
||
import EmptyState from '@/components/common/EmptyState'
|
||
|
||
definePageConfig({
|
||
navigationBarTitleText: '物流详情',
|
||
})
|
||
|
||
const LogisticsPage: React.FC = () => {
|
||
const { orderId, expressNo, expressCompany } = Taro.getCurrentInstance().router?.params || {}
|
||
const [loading, setLoading] = useState(true)
|
||
const [logisticsInfo, setLogisticsInfo] = useState<LogisticsInfo | null>(null)
|
||
const [trackList, setTrackList] = useState<LogisticsTrack[]>([])
|
||
|
||
useEffect(() => {
|
||
loadLogistics()
|
||
}, [])
|
||
|
||
const loadLogistics = async () => {
|
||
setLoading(true)
|
||
try {
|
||
// 必须传入参数
|
||
if (!expressNo || !expressCompany) {
|
||
Taro.showToast({ title: '缺少物流参数', icon: 'none' })
|
||
setLoading(false)
|
||
return
|
||
}
|
||
|
||
const res = await queryLogistics({
|
||
orderId,
|
||
expressNo,
|
||
expressCompany,
|
||
})
|
||
if (res?.data) {
|
||
setLogisticsInfo(res.data.logisticsInfo)
|
||
setTrackList(res.data.trackList)
|
||
}
|
||
} catch (err) {
|
||
console.error('加载物流信息失败', err)
|
||
Taro.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
// 复制单号
|
||
const copyExpressNo = () => {
|
||
if (logisticsInfo?.expressNo) {
|
||
Taro.setClipboardData({
|
||
data: logisticsInfo.expressNo,
|
||
success: () => {
|
||
Taro.showToast({ title: '单号已复制', icon: 'success' })
|
||
},
|
||
})
|
||
}
|
||
}
|
||
|
||
// 联系快递员(模拟)
|
||
const contactRider = () => {
|
||
Taro.makePhoneCall({
|
||
phoneNumber: '400-811-1111',
|
||
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')}`
|
||
}
|
||
|
||
if (loading) {
|
||
return <Loading fullscreen />
|
||
}
|
||
|
||
if (!logisticsInfo) {
|
||
return (
|
||
<View className='min-h-screen bg-gray-50 flex items-center justify-center'>
|
||
<EmptyState text='暂无物流信息' />
|
||
</View>
|
||
)
|
||
}
|
||
|
||
const statusInfo = formatLogisticsStatus(logisticsInfo.status)
|
||
|
||
return (
|
||
<View className='min-h-screen bg-gray-50 pb-4 flex flex-col'>
|
||
<ScrollView scrollY className='flex-1'>
|
||
{/* 快递信息卡片 */}
|
||
<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-xs text-gray-400'>•</Text>
|
||
<Text className='text-sm text-gray-500'>{statusInfo.text}</Text>
|
||
</View>
|
||
<Text className='text-xs text-gray-400 mt-1'>
|
||
运单号: {logisticsInfo.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>
|
||
</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>
|
||
</View>
|
||
|
||
{/* 收货人信息 */}
|
||
{logisticsInfo.receiverInfo && (
|
||
<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>
|
||
</View>
|
||
<Text className='text-xs text-gray-400 mt-1'>{logisticsInfo.receiverInfo.address}</Text>
|
||
</View>
|
||
)}
|
||
|
||
{/* 物流轨迹 */}
|
||
<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>
|
||
</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 className='bg-orange-50 mx-3 mt-3 p-4 rounded-xl'>
|
||
<Text className='text-sm text-orange-600 font-medium'>温馨提示</Text>
|
||
<Text className='text-xs text-orange-500 mt-1 leading-5'>
|
||
1. 快递在运输过程中可能存在延迟,请耐心等待{'\n'}
|
||
2. 如有疑问可联系快递公司客服{'\n'}
|
||
3. 签收前请检查包裹是否完好
|
||
</Text>
|
||
</View>
|
||
</ScrollView>
|
||
|
||
{/* 底部操作 */}
|
||
<View className='bg-white border-t border-gray-100 p-3' style={{ paddingBottom: '20px' }}>
|
||
<View className='flex gap-3'>
|
||
<Button
|
||
size='small'
|
||
className='flex-1 rounded-full border-gray-300 text-gray-600'
|
||
onClick={contactRider}
|
||
>
|
||
联系客服
|
||
</Button>
|
||
<Button
|
||
size='small'
|
||
className='flex-1 rounded-full'
|
||
style={{ backgroundColor: '#0e932e' }}
|
||
onClick={() => Taro.showToast({ title: '功能开发中', icon: 'none' })}
|
||
>
|
||
催单
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default LogisticsPage
|