import { useEffect, useState } from 'react' import { Card, Descriptions, Drawer, Image, Space, Spin, Table, Tag, Typography } from 'antd' import { toast } from 'sonner' import { merchantOrderDetail } from '@/apis/mallMerchant' import type { OrderDetailInfo } from '@/types/mall' import { formatUnixTime, getStatusColor, getStatusLabel } from '@/utils/mall' interface OrderDetailDrawerProps { open: boolean merchantId: string orderId?: number refreshToken?: number onClose: () => void } const normalizeSpecText = (value: unknown) => { if (typeof value === 'string') { return value.trim() } if (value === undefined || value === null) { return '' } return String(value).trim() } const parseSkuSpecs = (value?: string) => { if (!value?.trim()) { return [] } try { const parsed = JSON.parse(value) as unknown if (!Array.isArray(parsed)) { return [] } return parsed .map(item => { const record = item as Record const attributeName = normalizeSpecText(record.attributeName) || normalizeSpecText(record.name) const valueName = normalizeSpecText(record.valueName) || normalizeSpecText(record.value) if (!attributeName && !valueName) { return null } return { attributeName, valueName } }) .filter((item): item is { attributeName: string; valueName: string } => Boolean(item)) } catch { return [] } } export default function OrderDetailDrawer({ open, merchantId, orderId, refreshToken, onClose, }: OrderDetailDrawerProps) { const [loading, setLoading] = useState(false) const [data, setData] = useState(null) useEffect(() => { if (!open || !orderId) { return } let cancelled = false const run = async () => { try { setLoading(true) const result = await merchantOrderDetail({ merchantId, orderId, }) if (!cancelled) { setData(result.data) } } catch (error) { const message = error instanceof Error ? error.message : '加载订单详情失败' toast.error(message) } finally { if (!cancelled) { setLoading(false) } } } void run() return () => { cancelled = true } }, [merchantId, open, orderId, refreshToken]) const drawerTitle = data?.orderNo ? `订单详情 ${data.orderNo}` : '订单详情' return ( {!data ? null : ( {data.orderNo} {data.tradeNo} {data.merchantName || '-'} {getStatusLabel(data.orderStatus)} {getStatusLabel(data.afterSaleStatus)} {data.goodsAmount} {data.payAmount} {data.freightAmount} {data.discountAmount} {data.currency || '-'} {formatUnixTime(data.payTime)} {formatUnixTime(data.shipTime)} {formatUnixTime(data.finishTime)} {formatUnixTime(data.cancelTime)} {formatUnixTime(data.autoConfirmAt)} {data.payChannel || '-'} {formatUnixTime(data.createdAt)} {formatUnixTime(data.updatedAt)} {data.receiverName} {data.receiverMobile} {data.receiverCountryCode || '-'} {data.receiverProvince || '-'} {data.receiverCity || '-'} {data.receiverDistrict || '-'} {[data.receiverProvince, data.receiverCity, data.receiverDistrict, data.receiverAddress] .filter(Boolean) .join(' ')} {data.receiverZipCode || '-'} {data.deliveryCompany || '-'} {data.deliveryNo || '-'} {data.sellerRemark || '-'} {data.buyerRemark || '-'} String(record.id)} pagination={false} scroll={{ x: 960 }} dataSource={data.items} columns={[ { title: '商品', dataIndex: 'spuName', width: 280, render: (_, record) => (
{record.coverUrl ? ( ) : (
无图
)} {record.spuName} {record.skuName || '-'}
), }, { title: '规格', dataIndex: 'skuSpecJson', width: 220, render: (_, record) => { const specs = parseSkuSpecs(record.skuSpecJson) if (!specs.length) { return record.skuName || '-' } return ( {specs.map(spec => ( {spec.attributeName ? `${spec.attributeName}: ` : ''} {spec.valueName} ))} ) }, }, { title: '数量', dataIndex: 'buyNum', width: 90, }, { title: '单价', dataIndex: 'salePrice', width: 100, }, { title: '原价', dataIndex: 'originPrice', width: 100, }, { title: '金额', dataIndex: 'itemAmount', width: 120, }, { title: '优惠', dataIndex: 'discountAmount', width: 100, }, { title: '退款', dataIndex: 'refundAmount', width: 100, }, { title: '售后状态', dataIndex: 'afterSaleStatus', width: 120, render: value => {getStatusLabel(value)}, }, ]} /> )} ) }