diff --git a/apps/backend/src/repositories/worker-platform/work-order-query-repo.ts b/apps/backend/src/repositories/worker-platform/work-order-query-repo.ts
index 1b7eade4..ed973ee1 100644
--- a/apps/backend/src/repositories/worker-platform/work-order-query-repo.ts
+++ b/apps/backend/src/repositories/worker-platform/work-order-query-repo.ts
@@ -846,13 +846,13 @@ export function buildWorkOrderWhere({
params.push(normalizedKeyword)
const paramIndex = `$${params.length}`
filters.push(
- `(wo.platform_order_id = ${paramIndex} OR wo.platform_order_id LIKE ${paramIndex} || '%')`,
+ `(wo.platform_order_id = ${paramIndex} OR wo.platform_order_id LIKE ${paramIndex} || '%' OR wo.work_order_no ILIKE '%' || ${paramIndex} || '%')`,
)
} else if (isWorkOrderNoLookupKeyword(normalizedKeyword)) {
params.push(normalizedKeyword)
const paramIndex = `$${params.length}`
filters.push(
- `(wo.work_order_no = ${paramIndex} OR wo.work_order_no ILIKE ${paramIndex} || '%' OR LOWER(wo.work_order_no) = LOWER(${paramIndex}))`,
+ `(wo.work_order_no = ${paramIndex} OR wo.work_order_no ILIKE ${paramIndex} || '%' OR wo.work_order_no ILIKE '%' || ${paramIndex} || '%' OR LOWER(wo.work_order_no) = LOWER(${paramIndex}))`,
)
} else {
params.push(`%${normalizedKeyword}%`)
diff --git a/apps/backend/src/services/worker-platform/consignment-fulfillment-service.ts b/apps/backend/src/services/worker-platform/consignment-fulfillment-service.ts
index 5fabe350..e9ac02a2 100644
--- a/apps/backend/src/services/worker-platform/consignment-fulfillment-service.ts
+++ b/apps/backend/src/services/worker-platform/consignment-fulfillment-service.ts
@@ -250,7 +250,11 @@ export async function listInventoryMatchableOrders(
}
const { items } = await listWorkOrders({
- statuses: [WORK_ORDER_STATUS.UNASSIGNED, WORK_ORDER_STATUS.PENDING_MATERIAL],
+ statuses: [
+ WORK_ORDER_STATUS.UNASSIGNED,
+ WORK_ORDER_STATUS.PENDING_MATERIAL,
+ WORK_ORDER_STATUS.OPEN,
+ ],
keyword: String(input.keyword || '').trim(),
pageSize: Math.min(Math.max(Number(input.pageSize || 50), 1), 100),
sort: 'id_desc',
@@ -358,11 +362,13 @@ export async function fulfillWorkOrderWithConsignment(input: {
throw createHttpError('工单不存在', { statusCode: 404, errorCode: 'work_order_not_found' })
}
- // 仅未分配或待补资料工单支持选用库存履约
+ // 未分配、待补资料或大厅待接单工单支持选用库存履约
if (
- ![WORK_ORDER_STATUS.UNASSIGNED, WORK_ORDER_STATUS.PENDING_MATERIAL].includes(
- workOrder.status as any,
- )
+ ![
+ WORK_ORDER_STATUS.UNASSIGNED,
+ WORK_ORDER_STATUS.PENDING_MATERIAL,
+ WORK_ORDER_STATUS.OPEN,
+ ].includes(workOrder.status as any)
) {
throw createHttpError('当前工单状态不允许直接使用内部库存履约', {
statusCode: 409,
diff --git a/apps/frontend/src/components/admin/ConsignmentManualMatchModal.tsx b/apps/frontend/src/components/admin/ConsignmentManualMatchModal.tsx
index 5b3fac3c..6a9b615e 100644
--- a/apps/frontend/src/components/admin/ConsignmentManualMatchModal.tsx
+++ b/apps/frontend/src/components/admin/ConsignmentManualMatchModal.tsx
@@ -141,6 +141,10 @@ export default function ConsignmentManualMatchModal({
item.inventory.system,
item.inventory.serverZone,
item.inventory.series,
+ item.inventory.workerName,
+ item.inventory.buyerName,
+ item.inventory.sellerGameId,
+ item.inventory.id,
]),
[options, keyword],
)
@@ -148,10 +152,12 @@ export default function ConsignmentManualMatchModal({
() =>
filterManualMatchOptions(orders, keyword, (item) => [
item.workOrderNo,
+ item.workOrderId,
item.productName,
item.requiredSpec.system,
item.requiredSpec.serverZone,
item.requiredSpec.series,
+ item.status,
]),
[orders, keyword],
)
@@ -179,8 +185,8 @@ export default function ConsignmentManualMatchModal({
workOrderId: selected.workOrderId,
inventoryId: selected.inventoryId,
workerId: selected.workerId,
- allowPartial: selected.matchType !== 'exact',
- allowSpecMismatch: selected.matchType === 'conflict',
+ allowPartial: selected.matchType !== 'exact' || confirmedPartial,
+ allowSpecMismatch: selected.matchType === 'conflict' || confirmedMismatch,
matchNote: matchNote.trim(),
})
@@ -259,8 +265,8 @@ export default function ConsignmentManualMatchModal({
}
value={keyword}
@@ -296,6 +302,9 @@ export default function ConsignmentManualMatchModal({
>
+
+ {option.inventory.sourceSubmissionNo || option.inventory.inventoryNo}
+
{option.inventory.itemName}
{meta.label}
@@ -369,9 +378,16 @@ export default function ConsignmentManualMatchModal({
- #{order.workOrderId}
- {order.productName}
+ {order.workOrderNo || `#${order.workOrderId}`}
+ {order.productName}
{meta.label}
+ {order.status === 'open' ? (
+ 大厅待抢
+ ) : order.status === 'unassigned' ? (
+ 待指派
+ ) : order.status === 'pending_material' ? (
+ 待补资料
+ ) : null}
要求:{order.requiredSpec.system || '未指定'}/
@@ -398,7 +414,7 @@ export default function ConsignmentManualMatchModal({
onClick={() => {
setSelected({
workOrderId: order.workOrderId,
- workOrderLabel: `#${order.workOrderId} ${order.productName}`,
+ workOrderLabel: `${order.workOrderNo || `#${order.workOrderId}`} · ${order.productName}`,
inventoryId: inventory?.inventoryId || 0,
inventoryLabel,
matchType: order.matchType,
diff --git a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx
index 65eb8eec..23c6d42c 100644
--- a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx
+++ b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx
@@ -1644,7 +1644,7 @@ export default function WorkOrdersPanel() {
onClick={() => setManualMatchOrder(row)}
title="从全部在库现货里人工指定一件(支持规格冲突强制匹配,需填原因)"
>
- 手动匹配现货
+ 手动匹配寄售
) : null}
{row.capabilities?.canCancel && (canReturnToHall || canCancelOrder) ? (
diff --git a/apps/frontend/src/utils/consignment-manual-match.ts b/apps/frontend/src/utils/consignment-manual-match.ts
index 9b1975b8..3ed89086 100644
--- a/apps/frontend/src/utils/consignment-manual-match.ts
+++ b/apps/frontend/src/utils/consignment-manual-match.ts
@@ -74,9 +74,9 @@ export function filterManualMatchOptions(
/**
* 哪些订单状态可以手动指定现货。
*
- * 与后端 `fulfillWorkOrderWithConsignment` 的前置校验保持一致(只允许 unassigned /
- * pending_material):入口先按同一口径收敛,避免"按钮点了才报错"。
+ * 与后端 `fulfillWorkOrderWithConsignment` 的前置校验保持一致(允许 unassigned /
+ * pending_material / open):入口先按同一口径收敛,避免"按钮点了才报错"。
*/
export function isManuallyMatchableOrderStatus(status: string): boolean {
- return status === 'unassigned' || status === 'pending_material'
+ return status === 'unassigned' || status === 'pending_material' || status === 'open'
}
diff --git a/apps/frontend/tests/consignment-manual-match.test.ts b/apps/frontend/tests/consignment-manual-match.test.ts
index da667db4..b40c327c 100644
--- a/apps/frontend/tests/consignment-manual-match.test.ts
+++ b/apps/frontend/tests/consignment-manual-match.test.ts
@@ -60,11 +60,12 @@ test('关键词过滤:现货能按寄卖单号/物品名搜到,大小写与
assert.equal(filterManualMatchOptions(list, '不存在', pick).length, 0)
})
-test('只有待指派/待补资料的订单能手动指定现货(与后端前置校验同口径)', async () => {
+test('待指派/待补资料/大厅待抢的订单均能手动指定现货(与后端前置校验同口径)', async () => {
const { isManuallyMatchableOrderStatus } =
await import('../src/utils/consignment-manual-match.ts')
assert.equal(isManuallyMatchableOrderStatus('unassigned'), true)
assert.equal(isManuallyMatchableOrderStatus('pending_material'), true)
+ assert.equal(isManuallyMatchableOrderStatus('open'), true)
for (const status of ['in_progress', 'pending_acceptance', 'accepted', 'cancelled', 'disputed']) {
assert.equal(isManuallyMatchableOrderStatus(status), false, `${status} 不该给手动匹配入口`)
}