162 lines
5.5 KiB
TypeScript
162 lines
5.5 KiB
TypeScript
import { App, Alert, Button, Card, Empty, Modal, Space, Tag, Typography } from 'antd'
|
|
import { useQuery } from '@tanstack/react-query'
|
|
import { useEffect, useMemo, useState } from 'react'
|
|
|
|
import PercentTable from '@/components/common/PercentTable'
|
|
import {
|
|
fetchAdminConsignmentMatchCandidates,
|
|
matchAdminConsignmentWorkOrder,
|
|
} from '@/services/admin/consignment'
|
|
import type { ConsignmentMatchCandidate } from '@/types/consignment'
|
|
|
|
function money(fen: number) {
|
|
return `¥${((Number(fen || 0) || 0) / 100).toFixed(2)}`
|
|
}
|
|
|
|
export default function ConsignmentWorkOrderMatchModal({
|
|
open,
|
|
workOrderId,
|
|
onClose,
|
|
onMatched,
|
|
}: {
|
|
open: boolean
|
|
workOrderId: number | null
|
|
onClose: () => void
|
|
onMatched?: () => void
|
|
}) {
|
|
const { message } = App.useApp()
|
|
const [selectedIds, setSelectedIds] = useState<number[]>([])
|
|
const [submitting, setSubmitting] = useState(false)
|
|
|
|
const candidatesQuery = useQuery({
|
|
queryKey: ['admin-consignment-match-candidates', workOrderId],
|
|
queryFn: () => fetchAdminConsignmentMatchCandidates(Number(workOrderId)),
|
|
enabled: open && Boolean(workOrderId),
|
|
})
|
|
const data = candidatesQuery.data?.data
|
|
const options = data?.options || []
|
|
const needed = Math.max(1, Number(data?.totalQuantity || 1) - Number(data?.activeQuantity || 0))
|
|
|
|
useEffect(() => {
|
|
if (!open) {
|
|
setSelectedIds([])
|
|
return
|
|
}
|
|
if (options.length === 0) return
|
|
// 默认按缺口选中价格最低的几条,客服可自行调整。
|
|
setSelectedIds(options.slice(0, needed).map((item) => item.inventoryId))
|
|
}, [open, options, needed])
|
|
|
|
const totalPrice = useMemo(
|
|
() =>
|
|
selectedIds.reduce((sum, id) => {
|
|
const item = options.find((option) => option.inventoryId === id)
|
|
return sum + Number(item?.salePriceFen || 0)
|
|
}, 0),
|
|
[options, selectedIds],
|
|
)
|
|
|
|
return (
|
|
<Modal
|
|
title="寄卖库存匹配"
|
|
open={open}
|
|
onCancel={onClose}
|
|
width={860}
|
|
destroyOnHidden
|
|
footer={
|
|
<Space>
|
|
<Button onClick={onClose}>取消</Button>
|
|
<Button
|
|
type="primary"
|
|
loading={submitting}
|
|
disabled={selectedIds.length === 0}
|
|
onClick={async () => {
|
|
if (!workOrderId) return
|
|
setSubmitting(true)
|
|
try {
|
|
const result = await matchAdminConsignmentWorkOrder({
|
|
workOrderId,
|
|
inventoryIds: selectedIds,
|
|
})
|
|
message.success(
|
|
result.data?.matched
|
|
? `已匹配 ${result.data.matchedCount || selectedIds.length} 条寄卖库存`
|
|
: '未匹配到可用库存',
|
|
)
|
|
onMatched?.()
|
|
onClose()
|
|
} catch (error) {
|
|
message.error(error instanceof Error ? error.message : '匹配失败')
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}}
|
|
>
|
|
确认匹配
|
|
</Button>
|
|
</Space>
|
|
}
|
|
>
|
|
{candidatesQuery.isLoading ? <Card loading /> : null}
|
|
{!candidatesQuery.isLoading && options.length === 0 ? (
|
|
<Empty description="没有精确匹配的在库寄卖库存" />
|
|
) : null}
|
|
{options.length > 0 ? (
|
|
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
|
<Alert
|
|
type="info"
|
|
showIcon
|
|
message={`该工单${data?.sharingEnabled ? `拼单 ${data.totalQuantity} 份,已匹配 ${data.activeQuantity} 份` : '为整单'},本次还需 ${needed} 条库存。`}
|
|
description="自动匹配只做精确规格;手动匹配可选多条,拼单工单每选一条生成一个份额。"
|
|
/>
|
|
<PercentTable<ConsignmentMatchCandidate>
|
|
rowKey="inventoryId"
|
|
size="small"
|
|
pagination={false}
|
|
dataSource={options}
|
|
rowSelection={{
|
|
selectedRowKeys: selectedIds,
|
|
onChange: (keys) => setSelectedIds(keys.map((key) => Number(key))),
|
|
}}
|
|
columns={[
|
|
{
|
|
title: '寄卖库存',
|
|
render: (_, item) => (
|
|
<Space direction="vertical" size={0}>
|
|
<Typography.Text strong>{item.itemName}</Typography.Text>
|
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
{item.inventoryNo} · {item.workerName || `打手#${item.workerId || '-'}`}
|
|
</Typography.Text>
|
|
</Space>
|
|
),
|
|
},
|
|
{
|
|
title: '规格',
|
|
render: (_, item) =>
|
|
`${item.system}/${item.serverZone}/${item.series}/${item.diamondLevel}钻/${item.brillianceCount}璀`,
|
|
},
|
|
{
|
|
title: '库存',
|
|
width: 90,
|
|
render: (_, item) => (
|
|
<Tag color={item.remainingQuantity > 1 ? 'blue' : 'default'}>
|
|
剩 {item.remainingQuantity}/{item.quantity}
|
|
</Tag>
|
|
),
|
|
},
|
|
{
|
|
title: '售价',
|
|
width: 110,
|
|
render: (_, item) => money(item.salePriceFen),
|
|
},
|
|
]}
|
|
/>
|
|
<Typography.Text type="secondary">
|
|
已选 {selectedIds.length} 条,合计成本 {money(totalPrice)}
|
|
</Typography.Text>
|
|
</Space>
|
|
) : null}
|
|
</Modal>
|
|
)
|
|
}
|