import { useEffect, useMemo, useState, type CSSProperties, type ReactNode } from 'react' import { useParams, useSearchParams } from 'react-router-dom' import { Alert, Button, Descriptions, Form, Image, Input, Result, Space, Spin, Steps, Tag, Typography, message, } from 'antd' import { CheckCircleOutlined, ClockCircleOutlined, QrcodeOutlined, SendOutlined, } from '@ant-design/icons' import { deliveryApi } from '../api' import type { DeliveryBindResult, DeliveryOrderInfo, DeliverySubmitResult } from '../types' import { formatDateTime } from '../utils/time' const statusMap: Record = { paid: { color: 'orange', text: '待发货' }, delivering: { color: 'blue', text: '履约中' }, delivered: { color: 'green', text: '已交付' }, ship_failed: { color: 'red', text: '发货失败' }, cancelled: { color: 'default', text: '已取消' }, } export default function Delivery() { const { channel = 'dlc', orderNo = '' } = useParams() const [searchParams] = useSearchParams() const [form] = Form.useForm<{ game_account: string }>() const [order, setOrder] = useState(null) const [bindResult, setBindResult] = useState(null) const [submitResult, setSubmitResult] = useState(null) const [loading, setLoading] = useState(true) const [binding, setBinding] = useState(false) const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') const decodedOrderNo = useMemo(() => decodeURIComponent(orderNo), [orderNo]) const deliveryAuth = useMemo(() => { const exp = searchParams.get('exp') || '' const sign = searchParams.get('sign') || '' return exp && sign ? { exp, sign } : undefined }, [searchParams]) const gameAccount = Form.useWatch('game_account', form) useEffect(() => { if (!decodedOrderNo) { setError('发货链接缺少订单号') setLoading(false) return } setLoading(true) deliveryApi.getOrder(decodedOrderNo, deliveryAuth) .then((data) => { setOrder(data) setError('') }) .catch((e) => setError(e instanceof Error ? e.message : '订单加载失败')) .finally(() => setLoading(false)) }, [decodedOrderNo, deliveryAuth]) const bindAccount = async () => { const values = await form.validateFields() setBinding(true) setSubmitResult(null) try { const result = await deliveryApi.bind(decodedOrderNo, values.game_account, deliveryAuth) setBindResult(result) message.success('绑定二维码已生成') } catch (e) { message.error(e instanceof Error ? e.message : '生成二维码失败') } finally { setBinding(false) } } const submitDelivery = async () => { const values = await form.validateFields() if (!bindResult?.bind_uuid) { message.warning('请先生成绑定二维码') return } setSubmitting(true) try { const result = await deliveryApi.submit(decodedOrderNo, values.game_account, bindResult.bind_uuid, deliveryAuth) setSubmitResult(result) setOrder((prev) => prev ? { ...prev, status: result.status, can_ship: false } : prev) message.success(result.message || '已提交') } catch (e) { message.error(e instanceof Error ? e.message : '提交失败') } finally { setSubmitting(false) } } if (loading) { return ( ) } if (error || !order) { return ( ) } const status = statusMap[order.status] || { color: 'default', text: order.status } const step = submitResult ? 2 : bindResult ? 1 : 0 return (
游戏道具发货 {channel.toUpperCase()} · {decodedOrderNo}
{order.product?.name || '待发货商品'} {order.product?.sku || '-'}
{status.text}
{order.buyer_name || '-'} {order.amount} 积分 {formatDateTime(order.created_at)} {order.product?.game || channel.toUpperCase()} {order.ship_fail_reason && ( )} {!order.can_ship && ( )}
{order.can_ship && (
}, { title: '扫码绑定', icon: }, { title: '提交发货', icon: }, ]} />
{bindResult && (
绑定二维码 绑定凭证 {bindResult.bind_uuid} 打开绑定链接
)} {submitResult && ( )}
)}
) } function CenteredShell({ children }: { children: ReactNode }) { return (
{children}
) } const panelStyle: CSSProperties = { background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: 24, }