feat(consignment): 寄卖规格与价格动态配置重构
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { App, Alert, Button, Card, Empty, Modal, Space, Tag, Typography } from 'antd'
|
||||
import { App, Alert, Button, Card, Empty, Input, Modal, Space, Tag, Typography } from 'antd'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
const { message } = App.useApp()
|
||||
const [selectedIds, setSelectedIds] = useState<number[]>([])
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [nonExactReason, setNonExactReason] = useState('')
|
||||
|
||||
const candidatesQuery = useQuery({
|
||||
queryKey: ['admin-consignment-match-candidates', workOrderId],
|
||||
@@ -40,13 +41,24 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setSelectedIds([])
|
||||
setNonExactReason('')
|
||||
return
|
||||
}
|
||||
if (options.length === 0) return
|
||||
// 默认按缺口选中价格最低的几条,客服可自行调整。
|
||||
setSelectedIds(options.slice(0, needed).map((item) => item.inventoryId))
|
||||
// 默认只选精确匹配;非精确库存必须人工勾选并填写原因。
|
||||
const exactOptions = options.filter((item) => (item.matchType || 'exact') === 'exact')
|
||||
setSelectedIds(exactOptions.slice(0, needed).map((item) => item.inventoryId))
|
||||
}, [open, options, needed])
|
||||
|
||||
const nonExactSelected = useMemo(
|
||||
() =>
|
||||
selectedIds.some((id) => {
|
||||
const item = options.find((option) => option.inventoryId === id)
|
||||
return Boolean(item?.matchType && item.matchType !== 'exact')
|
||||
}),
|
||||
[options, selectedIds],
|
||||
)
|
||||
|
||||
const totalPrice = useMemo(
|
||||
() =>
|
||||
selectedIds.reduce((sum, id) => {
|
||||
@@ -69,7 +81,7 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
<Button
|
||||
type="primary"
|
||||
loading={submitting}
|
||||
disabled={selectedIds.length === 0}
|
||||
disabled={selectedIds.length === 0 || (nonExactSelected && !nonExactReason.trim())}
|
||||
onClick={async () => {
|
||||
if (!workOrderId) return
|
||||
setSubmitting(true)
|
||||
@@ -77,6 +89,8 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
const result = await matchAdminConsignmentWorkOrder({
|
||||
workOrderId,
|
||||
inventoryIds: selectedIds,
|
||||
allowNonExact: nonExactSelected,
|
||||
reason: nonExactSelected ? nonExactReason.trim() : undefined,
|
||||
})
|
||||
message.success(
|
||||
result.data?.matched
|
||||
@@ -99,7 +113,7 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
>
|
||||
{candidatesQuery.isLoading ? <Card loading /> : null}
|
||||
{!candidatesQuery.isLoading && options.length === 0 ? (
|
||||
<Empty description="没有精确匹配的在库寄卖库存" />
|
||||
<Empty description="没有同配置的在库寄卖库存" />
|
||||
) : null}
|
||||
{options.length > 0 ? (
|
||||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||||
@@ -107,7 +121,7 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
type="info"
|
||||
showIcon
|
||||
message={`该工单${data?.sharingEnabled ? `拼单 ${data.totalQuantity} 份,已匹配 ${data.activeQuantity} 份` : '为整单'},本次还需 ${needed} 条库存。`}
|
||||
description="自动匹配只做精确规格;手动匹配可选多条,拼单工单每选一条生成一个份额。"
|
||||
description="自动匹配只做精确规格;手动匹配可选多条,拼单工单每选一条生成一个份额。选择非精确库存必须填写原因。"
|
||||
/>
|
||||
<PercentTable<ConsignmentMatchCandidate>
|
||||
rowKey="inventoryId"
|
||||
@@ -133,8 +147,30 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
{
|
||||
title: '规格',
|
||||
render: (_, item) =>
|
||||
item.specText ||
|
||||
`${item.system}/${item.serverZone}/${item.series}/${item.diamondLevel}钻/${item.brillianceCount}璀`,
|
||||
},
|
||||
{
|
||||
title: '匹配',
|
||||
width: 90,
|
||||
render: (_, item) => {
|
||||
const type = item.matchType || 'exact'
|
||||
const label =
|
||||
type === 'exact'
|
||||
? '精确'
|
||||
: type === 'partial'
|
||||
? '高配'
|
||||
: type === 'unknown'
|
||||
? '未知'
|
||||
: '冲突'
|
||||
const color = type === 'exact' ? 'green' : type === 'partial' ? 'orange' : 'red'
|
||||
return (
|
||||
<Tag color={color} title={item.matchReasons?.join('、') || ''}>
|
||||
{label}
|
||||
</Tag>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '库存',
|
||||
width: 90,
|
||||
@@ -151,6 +187,21 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{nonExactSelected ? (
|
||||
<Space direction="vertical" size={6} style={{ width: '100%' }}>
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
message="已选择非精确库存:请确认规格差异,并填写人工匹配原因。"
|
||||
/>
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
value={nonExactReason}
|
||||
onChange={(event) => setNonExactReason(event.target.value)}
|
||||
placeholder="例如:订单规格未写全,客服已与买家确认可用该库存"
|
||||
/>
|
||||
</Space>
|
||||
) : null}
|
||||
<Typography.Text type="secondary">
|
||||
已选 {selectedIds.length} 条,合计成本 {money(totalPrice)}
|
||||
</Typography.Text>
|
||||
|
||||
Reference in New Issue
Block a user