feat(consignment): 客服手动指定现货匹配的界面入口
后端批次已提供判定/差异/强制留痕与双向读取接口,这里补界面,让客服真的能用上。 - 新增共享弹窗 ConsignmentManualMatchModal:订单侧(匹配弹窗「手动指定现货匹配」)与 库存侧(寄售仓库行内「手动匹配订单」)共用同一套交互与落地逻辑,避免两套 UI 漂移 - 交互:搜索 → 选中(精确/非精确/规格冲突三档标签 + 字段级差异说明)→ 选交付方式 (内部现货履约 / 指派供货打手)→ 非精确需勾选确认、冲突需勾选「确认规格不一致」并必填原因 → 落地;并在落地前提示"现货立刻被占用、点错需撤销寄售履约" - 判定规则收敛成纯函数 consignment-manual-match.ts(需不需要确认/填原因、能否提交、 关键词过滤、标签文案)并加 4 例单测 - 服务与类型补齐:fetchWorkOrderConsignmentMatchOptions / fetchInventoryMatchableOrders, fulfill/assign 入参增加 allowSpecMismatch 与 matchNote - 凭证核销失败的提示沿用既有规则(核销失败不能报成功) 验证:前端 typecheck/lint/format/44 例测试/build 全过;两个新接口未登录访问均 401 (路由已挂载、权限位生效)。
This commit is contained in:
@@ -36,6 +36,7 @@ import {
|
||||
updateAdminConsignmentInventory,
|
||||
} from '@/services/admin/consignment'
|
||||
import { fetchAdminWorkerUsers } from '@/services/admin/worker-platform'
|
||||
import ConsignmentManualMatchModal from '@/components/admin/ConsignmentManualMatchModal'
|
||||
import type { ConsignmentInventory, GiftEvidenceReviewStatus } from '@/types/consignment'
|
||||
import { fetchAdminSession } from '@/services/admin/auth'
|
||||
import { resolveAdminPermission } from '@/utils/admin-auth'
|
||||
@@ -143,6 +144,7 @@ export default function ConsignmentInventoryPanel() {
|
||||
const [editingItem, setEditingItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [viewingItem, setViewingItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [returnItem, setReturnItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [manualMatchItem, setManualMatchItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [returnReason, setReturnReason] = useState<string>('')
|
||||
|
||||
const [editForm] = Form.useForm()
|
||||
@@ -552,6 +554,9 @@ export default function ConsignmentInventoryPanel() {
|
||||
</Button>
|
||||
{record.status === 'in_stock' && canManageConsignment ? (
|
||||
<>
|
||||
<Button size="small" type="link" onClick={() => setManualMatchItem(record)}>
|
||||
手动匹配订单
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
@@ -939,6 +944,20 @@ export default function ConsignmentInventoryPanel() {
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
<ConsignmentManualMatchModal
|
||||
open={Boolean(manualMatchItem)}
|
||||
onClose={() => setManualMatchItem(null)}
|
||||
inventory={
|
||||
manualMatchItem
|
||||
? {
|
||||
inventoryId: manualMatchItem.id,
|
||||
label: manualMatchItem.itemName,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onMatched={() => void inventoriesQuery.refetch()}
|
||||
/>
|
||||
|
||||
<Form.Item name="remark" label="备注">
|
||||
<Input.TextArea rows={2} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -82,6 +82,7 @@ import {
|
||||
} from '@/services/admin/consignment'
|
||||
import type { ConsignmentFulfillmentInfo, WorkOrderInventoryMatchCheck } from '@/types/consignment'
|
||||
import type { UploadedFile, WorkOrder, WorkOrderShare } from '@/types/worker-platform'
|
||||
import ConsignmentManualMatchModal from '@/components/admin/ConsignmentManualMatchModal'
|
||||
import { AdminRangePicker } from '@/components/admin/AdminDatePicker'
|
||||
import dayjs, { ADMIN_DATE_FORMAT } from '@/lib/dayjs'
|
||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
@@ -313,6 +314,7 @@ export default function WorkOrdersPanel() {
|
||||
}>()
|
||||
|
||||
// 寄售库存匹配拦截弹窗状态
|
||||
const [manualMatchOrder, setManualMatchOrder] = useState<WorkOrder | null>(null)
|
||||
const [consignmentInterceptModal, setConsignmentInterceptModal] = useState<{
|
||||
open: boolean
|
||||
order: WorkOrder | null
|
||||
@@ -2970,6 +2972,23 @@ export default function WorkOrdersPanel() {
|
||||
})
|
||||
}}
|
||||
footer={[
|
||||
<Button
|
||||
key="manual-match"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
const currentOrder = consignmentInterceptModal.order
|
||||
if (!currentOrder) return
|
||||
setConsignmentInterceptModal({
|
||||
open: false,
|
||||
order: null,
|
||||
matchData: null,
|
||||
submitting: false,
|
||||
})
|
||||
setManualMatchOrder(currentOrder)
|
||||
}}
|
||||
>
|
||||
手动指定现货匹配
|
||||
</Button>,
|
||||
<Button
|
||||
key="ignore-and-publish"
|
||||
onClick={() => {
|
||||
@@ -3189,6 +3208,22 @@ export default function WorkOrdersPanel() {
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<ConsignmentManualMatchModal
|
||||
open={Boolean(manualMatchOrder)}
|
||||
onClose={() => setManualMatchOrder(null)}
|
||||
workOrder={
|
||||
manualMatchOrder
|
||||
? {
|
||||
workOrderId: manualMatchOrder.workOrderId,
|
||||
label: `#${manualMatchOrder.workOrderId} ${manualMatchOrder.productName || ''}`,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onMatched={() => {
|
||||
void ordersQuery.refetch()
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 撤单 / 撤销寄售履约弹窗 */}
|
||||
<Modal
|
||||
title={`撤单 / 撤销寄售履约 · ${getDisplayOrderNo(revokeConsignmentModal.order)}`}
|
||||
|
||||
Reference in New Issue
Block a user