1
This commit is contained in:
@@ -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: '数量',
|
||||
|
||||
Reference in New Issue
Block a user