This commit is contained in:
魔方熊成祥
2026-05-07 22:54:11 +08:00
parent ba0458abe2
commit bd0163af2d
18 changed files with 885 additions and 125 deletions

View File

@@ -1,9 +1,9 @@
import { useEffect, useState } from 'react'
import { Card, Descriptions, Drawer, Space, Spin, Table, Tag, Typography } from 'antd'
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, prettyJsonText } from '@/utils/mall'
import { formatUnixTime, getStatusColor, getStatusLabel } from '@/utils/mall'
interface OrderDetailDrawerProps {
open: boolean
@@ -13,6 +13,49 @@ interface OrderDetailDrawerProps {
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<string, unknown>
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,
@@ -56,8 +99,10 @@ export default function OrderDetailDrawer({
}
}, [merchantId, open, orderId, refreshToken])
const drawerTitle = data?.orderNo ? `订单详情 ${data.orderNo}` : '订单详情'
return (
<Drawer title={orderId ? `订单详情 #${orderId}` : '订单详情'} width={920} open={open} onClose={onClose}>
<Drawer title={drawerTitle} width={1080} open={open} onClose={onClose}>
<Spin spinning={loading}>
{!data ? null : (
<Space direction="vertical" size={16} style={{ width: '100%' }}>
@@ -65,9 +110,6 @@ export default function OrderDetailDrawer({
<Descriptions column={2} size="small">
<Descriptions.Item label="订单号">{data.orderNo}</Descriptions.Item>
<Descriptions.Item label="交易号">{data.tradeNo}</Descriptions.Item>
<Descriptions.Item label="订单 ID">{data.id}</Descriptions.Item>
<Descriptions.Item label="会员 ID">{data.uid}</Descriptions.Item>
<Descriptions.Item label="商家 ID">{data.merchantId}</Descriptions.Item>
<Descriptions.Item label="商家名称">{data.merchantName || '-'}</Descriptions.Item>
<Descriptions.Item label="订单状态">
<Tag color={getStatusColor(data.orderStatus)}>{getStatusLabel(data.orderStatus)}</Tag>
@@ -90,11 +132,6 @@ export default function OrderDetailDrawer({
<Descriptions.Item label="支付渠道">{data.payChannel || '-'}</Descriptions.Item>
<Descriptions.Item label="创建时间">{formatUnixTime(data.createdAt)}</Descriptions.Item>
<Descriptions.Item label="更新时间">{formatUnixTime(data.updatedAt)}</Descriptions.Item>
<Descriptions.Item label="扩展 JSON" span={2}>
<Typography.Text style={{ whiteSpace: 'pre-wrap' }}>
{prettyJsonText(data.extraInfoJson) || '-'}
</Typography.Text>
</Descriptions.Item>
</Descriptions>
</Card>
@@ -125,29 +162,66 @@ export default function OrderDetailDrawer({
<Card className="page-card" title="商品明细">
<Table
rowKey="id"
rowKey={record => String(record.id)}
pagination={false}
scroll={{ x: 960 }}
dataSource={data.items}
columns={[
{
title: '商品',
dataIndex: 'spuName',
width: 280,
render: (_, record) => (
<Space direction="vertical" size={2}>
<Typography.Text strong>{record.spuName}</Typography.Text>
<Typography.Text type="secondary">{record.skuName}</Typography.Text>
</Space>
<div className="order-detail-product">
{record.coverUrl ? (
<Image
src={record.coverUrl}
alt={record.spuName}
width={52}
height={52}
className="order-detail-product__image"
preview={{ src: record.coverUrl }}
/>
) : (
<div className="order-detail-product__placeholder"></div>
)}
<Space direction="vertical" size={2} className="order-detail-product__content">
<Typography.Text
strong
className="order-detail-product__title"
title={record.spuName}
>
{record.spuName}
</Typography.Text>
<Typography.Text type="secondary" ellipsis={{ tooltip: record.skuName }}>
{record.skuName || '-'}
</Typography.Text>
</Space>
</div>
),
},
{
title: '规格 JSON',
title: '规格',
dataIndex: 'skuSpecJson',
width: 260,
render: value => (
<Typography.Text style={{ whiteSpace: 'pre-wrap' }}>
{prettyJsonText(value) || '-'}
</Typography.Text>
),
width: 220,
render: (_, record) => {
const specs = parseSkuSpecs(record.skuSpecJson)
if (!specs.length) {
return record.skuName || '-'
}
return (
<Space size={[4, 4]} wrap>
{specs.map(spec => (
<Tag key={`${spec.attributeName}-${spec.valueName}`}>
{spec.attributeName ? `${spec.attributeName}: ` : ''}
{spec.valueName}
</Tag>
))}
</Space>
)
},
},
{
title: '数量',