feat(payment): 新增线下付款方式及相关订单状态支持

- 支付页面新增线下付款选项,支持微信转账付款方式
- 订单流程新增线下付款待确认(offline_pending)阶段
- 订单详情与订单卡片显示线下付款状态及提示信息
- 支付接口和模型增加线下付款支付方式(payType=9)支持
- 订单列表页区分线下付款待确认与待收款状态
- 地址编辑页修复智能识别按钮无法点击问题,调整布局避免被Textarea遮挡
This commit is contained in:
2026-07-14 22:15:31 +08:00
parent 44b3cb10be
commit dfbcbcf615
7 changed files with 116 additions and 40 deletions

View File

@@ -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' && (
<>

View File

@@ -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>
{/* 凑单推荐 */}

View File

@@ -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>

View File

@@ -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>