feat(payment): 新增线下付款方式及相关订单状态支持
- 支付页面新增线下付款选项,支持微信转账付款方式 - 订单流程新增线下付款待确认(offline_pending)阶段 - 订单详情与订单卡片显示线下付款状态及提示信息 - 支付接口和模型增加线下付款支付方式(payType=9)支持 - 订单列表页区分线下付款待确认与待收款状态 - 地址编辑页修复智能识别按钮无法点击问题,调整布局避免被Textarea遮挡
This commit is contained in:
@@ -24,6 +24,7 @@ const PAY_TYPE_MAP: Record<number, string> = {
|
||||
6: '免费',
|
||||
7: '积分支付',
|
||||
8: '货到付款',
|
||||
9: '线下付款',
|
||||
}
|
||||
|
||||
// 发票状态映射
|
||||
@@ -44,6 +45,7 @@ const DELIVERY_STATUS_MAP: Record<number, string> = {
|
||||
const getOrderDisplayStatus = (order: ShopOrder): { title: string; bgColor: string; subtitle: string } => {
|
||||
const { payStatus, deliveryStatus, orderStatus, payType } = order
|
||||
const isCod = payType === 8 // 货到付款
|
||||
const isOffline = payType === 9 // 线下付款
|
||||
|
||||
// 退款/取消相关状态
|
||||
if (orderStatus === OrderStatus.Cancelled) {
|
||||
@@ -63,9 +65,12 @@ const getOrderDisplayStatus = (order: ShopOrder): { title: string; bgColor: stri
|
||||
}
|
||||
|
||||
// 正常订单流程
|
||||
if (!payStatus && !isCod) {
|
||||
if (!payStatus && !isCod && !isOffline) {
|
||||
return { bgColor: '#ff7d00', title: '待付款', subtitle: '请尽快完成支付' }
|
||||
}
|
||||
if (isOffline && !payStatus) {
|
||||
return { bgColor: '#ff7d00', title: '线下付款·待确认', subtitle: '请通过微信转账完成付款,商家确认后发货' }
|
||||
}
|
||||
if (isCod && !payStatus) {
|
||||
return { bgColor: '#4b9cf5', title: '货到付款·待发货', subtitle: '送达时支付' }
|
||||
}
|
||||
@@ -87,17 +92,20 @@ const getOrderDisplayStatus = (order: ShopOrder): { title: string; bgColor: stri
|
||||
}
|
||||
|
||||
/** 根据订单状态判断当前操作阶段 */
|
||||
type OrderPhase = 'unpaid' | 'unshipped' | 'shipped' | 'completed' | 'cancelled' | 'refund'
|
||||
type OrderPhase = 'unpaid' | 'offline_pending' | 'unshipped' | 'shipped' | 'completed' | 'cancelled' | 'refund'
|
||||
|
||||
const getOrderPhase = (order: ShopOrder): OrderPhase => {
|
||||
const { payStatus, deliveryStatus, orderStatus, payType } = order
|
||||
const isCod = payType === 8 // 货到付款
|
||||
const isOffline = payType === 9 // 线下付款
|
||||
|
||||
// 退款/取消
|
||||
if ([OrderStatus.Cancelled, OrderStatus.Cancelling, OrderStatus.RefundSuccess, OrderStatus.RefundApply, OrderStatus.ClientRefundApply, OrderStatus.RefundRejected].includes(orderStatus as OrderStatus)) {
|
||||
return orderStatus === OrderStatus.Cancelled || orderStatus === OrderStatus.RefundSuccess ? 'cancelled' : 'refund'
|
||||
}
|
||||
|
||||
// 线下付款且未确认收款:等待商家确认,不显示"立即支付"按钮
|
||||
if (isOffline && !payStatus) return 'offline_pending'
|
||||
// 货到付款订单:payStatus=true 但还未实际付款,直接进入 unshipped 阶段(不需要"立即支付"按钮)
|
||||
if (!payStatus && !isCod) return 'unpaid'
|
||||
if (deliveryStatus === 10) return 'unshipped'
|
||||
@@ -445,6 +453,28 @@ const OrderDetailPage: React.FC = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 线下付款·待确认: 取消 + 联系客服 */}
|
||||
{phase === 'offline_pending' && (
|
||||
<>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ddd', color: '#666' }}
|
||||
onClick={handleCancelOrder}
|
||||
>
|
||||
取消订单
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ backgroundColor: '#4b9cf5' }}
|
||||
onClick={() => Taro.makePhoneCall({ phoneNumber: '400-000-0000' })}
|
||||
>
|
||||
联系客服
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 待发货: 退款 + 联系客服 */}
|
||||
{phase === 'unshipped' && (
|
||||
<>
|
||||
|
||||
@@ -125,7 +125,13 @@ const CheckoutPage: React.FC = () => {
|
||||
const [rushBuyProducts, setRushBuyProducts] = useState<ShopGoods[]>([])
|
||||
|
||||
// 支付方式相关状态
|
||||
const [payType, setPayType] = useState<number>(8) // 8: 货到付款
|
||||
const [payType, setPayType] = useState<number>(8) // 8: 货到付款(默认),9: 线下付款
|
||||
|
||||
// 支付方式选项
|
||||
const PAYMENT_OPTIONS = [
|
||||
{ id: 8, name: '货到付款', desc: '送达时支付', icon: '货', bgColor: '#f0fdf4', iconBg: '#dbeafe', iconColor: '#2563eb', borderColor: '#22c55e' },
|
||||
{ id: 9, name: '线下付款', desc: '微信转账,商家确认后发货', icon: '线', bgColor: '#fffbeb', iconBg: '#fef3c7', iconColor: '#d97706', borderColor: '#f59e0b' },
|
||||
]
|
||||
|
||||
// 从本地存储获取立即购买的数据
|
||||
const buyNowItems = useMemo(() => {
|
||||
@@ -263,7 +269,8 @@ const CheckoutPage: React.FC = () => {
|
||||
specInfo: item.skuSpec || item.sku?.sku || item.product?.specName,
|
||||
}))
|
||||
|
||||
// 货到付款:后端会自动设置 payStatus=true,无需前端传递 payStatus/payTime
|
||||
// 货到付款(8):后端会自动设置 payStatus=true,无需前端传递 payStatus/payTime
|
||||
// 线下付款(9):后端会设置 payStatus=false,等待商家确认收款后才设为 true
|
||||
const orderParams: OrderCreateRequest = {
|
||||
goodsItems,
|
||||
addressId: defaultAddress.id,
|
||||
@@ -441,25 +448,45 @@ const CheckoutPage: React.FC = () => {
|
||||
<View className='bg-white rounded-lg mt-3 p-3'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>支付方式</Text>
|
||||
|
||||
{/* 货到付款 */}
|
||||
<View
|
||||
className='flex items-center py-3 px-2 rounded-lg'
|
||||
style={{ backgroundColor: '#f0fdf4' }}
|
||||
>
|
||||
<View className='w-8 h-8 rounded-full bg-blue-50 flex items-center justify-center mr-3'>
|
||||
<Text className='text-sm'>货</Text>
|
||||
{PAYMENT_OPTIONS.map((option) => {
|
||||
const selected = payType === option.id
|
||||
return (
|
||||
<View
|
||||
key={option.id}
|
||||
className='flex items-center py-3 px-2 rounded-lg mb-2'
|
||||
style={{ backgroundColor: selected ? option.bgColor : '#f9fafb' }}
|
||||
onClick={() => setPayType(option.id)}
|
||||
>
|
||||
<View
|
||||
className='w-8 h-8 rounded-full flex items-center justify-center mr-3'
|
||||
style={{ backgroundColor: option.iconBg }}
|
||||
>
|
||||
<Text className='text-sm' style={{ color: option.iconColor }}>{option.icon}</Text>
|
||||
</View>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-sm text-gray-800 block'>{option.name}</Text>
|
||||
<Text className='text-xs text-gray-400 block'>{option.desc}</Text>
|
||||
</View>
|
||||
<View
|
||||
className='w-5 h-5 rounded-full border-2 flex items-center justify-center'
|
||||
style={{ borderColor: selected ? option.borderColor : '#d1d5db' }}
|
||||
>
|
||||
{selected && (
|
||||
<View className='w-2 h-2 rounded-full' style={{ backgroundColor: option.borderColor }} />
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* 线下付款选中时显示提示 */}
|
||||
{payType === 9 && (
|
||||
<View className='mt-2 p-2 rounded-lg' style={{ backgroundColor: '#fef3c7' }}>
|
||||
<Text className='text-xs text-orange-600'>
|
||||
请下单后通过微信转账付款,商家确认收款后发货。
|
||||
</Text>
|
||||
</View>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-sm text-gray-800 block'>货到付款</Text>
|
||||
<Text className='text-xs text-gray-400 block'>送达时支付</Text>
|
||||
</View>
|
||||
<View
|
||||
className='w-5 h-5 rounded-full border-2 flex items-center justify-center'
|
||||
style={{ borderColor: '#22c55e' }}
|
||||
>
|
||||
<View className='w-2 h-2 rounded-full' style={{ backgroundColor: '#22c55e' }} />
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 凑单推荐 */}
|
||||
|
||||
@@ -92,7 +92,11 @@ function parseSendEndImg(raw: string): string[] {
|
||||
function getStatusText(order: ShopOrder): string {
|
||||
if (order.orderStatus === 1) return '已完成'
|
||||
if (order.orderStatus === 2) return '已关闭'
|
||||
if (order.payStatus === false || order.payStatus === null) return '待收款'
|
||||
if (order.payStatus === false || order.payStatus === null) {
|
||||
// 线下付款待确认收款
|
||||
if (order.payType === 9) return '待确认收款'
|
||||
return '待收款'
|
||||
}
|
||||
if (order.payStatus && order.deliveryStatus === 10) return '待发货'
|
||||
if (order.payStatus && order.deliveryStatus === 20) return '待收货'
|
||||
return '进行中'
|
||||
@@ -413,7 +417,7 @@ export default function StoreOrdersPage() {
|
||||
{/* 金额 + 支付方式 */}
|
||||
<View className='flex justify-between items-center mb-3'>
|
||||
<Text className='text-xs text-gray-400'>
|
||||
{order.payType === 0 ? '货到付款' : order.payType === 4 ? '现金支付' : '货到付款'}
|
||||
{order.payType === 8 ? '货到付款' : order.payType === 9 ? '线下付款' : order.payType === 4 ? '现金支付' : order.payType === 1 ? '微信支付' : '其他'}
|
||||
</Text>
|
||||
<Text className='text-sm text-gray-700'>
|
||||
实付:<Text className='text-red-500 font-medium'>¥{order.payPrice || order.totalPrice}</Text>
|
||||
|
||||
@@ -465,7 +465,7 @@ const AddressEditPage: React.FC = () => {
|
||||
{/* 地址智能识别区 */}
|
||||
<View className="bg-white px-3 pt-3 pb-2">
|
||||
<View
|
||||
className="rounded-lg p-2 relative"
|
||||
className="rounded-lg p-2"
|
||||
style={{border: '1px dashed #0e932e'}}
|
||||
>
|
||||
<Textarea
|
||||
@@ -475,19 +475,20 @@ const AddressEditPage: React.FC = () => {
|
||||
placeholder="粘贴文本,点击「识别」自动识别收货人、地址、电话"
|
||||
maxlength={200}
|
||||
/>
|
||||
<View
|
||||
className="absolute right-2 bottom-2"
|
||||
style={{
|
||||
backgroundColor: '#0e932e',
|
||||
borderRadius: '12px',
|
||||
paddingLeft: '12px',
|
||||
paddingRight: '12px',
|
||||
paddingTop: '4px',
|
||||
paddingBottom: '4px'
|
||||
}}
|
||||
onClick={recognizeAddress}
|
||||
>
|
||||
<Text className="text-white text-xs">识别</Text>
|
||||
<View className="flex justify-end mt-2">
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: '#0e932e',
|
||||
borderRadius: '12px',
|
||||
paddingLeft: '12px',
|
||||
paddingRight: '12px',
|
||||
paddingTop: '4px',
|
||||
paddingBottom: '4px'
|
||||
}}
|
||||
onClick={recognizeAddress}
|
||||
>
|
||||
<Text className="text-white text-xs">识别</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user