feat(consignment): 接单模板反推规格字段,打手端大批量选项改下拉
- 新增 consignment-template-spec-service:按「分类 + 子树」解析接单模板商品名, 全部可拆成统一维度且无组合碰撞时生成多字段矩阵,否则整类降级为商品名下拉; 选项只存 ruleKey,取价时实时回查模板,模板停用/删除即无价(missingPricePolicy=block)。 - 匹配引擎:模板驱动配置强制使用 profile specs(不再回退库存旧快照),订单侧按 product_rule_id 反解规格;解析失败按 unknown 处理,不参与自动匹配。 - 管理端:寄卖配置新增「从接单模板生成」,弹窗给出推荐模式(规格字段 / 商品名下拉)、 降级原因与组合→模板价覆盖矩阵。 - 打手端:选项超过 8 个改用带搜索的下拉(地铁逃生 100 个商品),规格网格按字段数 自适应铺满;物品名改用商品名,去掉硬编码「苏尔南」后缀。 - 迁移重编号:合并 main 后寄卖线顺延为 147–151,migrate.ts 保留 legacy→new 幂等映射。 - 同工作区的拼单份额报酬口径、超时重匹配兜底、整单/拼单匹配弹窗调整一并提交。 验证:前端 254/254、后端寄卖相关 41/41、双端 tsc 通过;开发库已实跑 发布→审核入库→自动匹配(模板价 1200 → 匹配报酬 1200)。
This commit is contained in:
@@ -36,7 +36,11 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
})
|
||||
const data = candidatesQuery.data?.data
|
||||
const options = data?.options || []
|
||||
const needed = Math.max(1, Number(data?.totalQuantity || 1) - Number(data?.activeQuantity || 0))
|
||||
const totalQuantity = Math.max(1, Number(data?.totalQuantity || 1))
|
||||
// 整单匹配只需一条库存(由该库存一次占满订单总数量);拼单才按剩余份数选多条。
|
||||
const needed = data?.sharingEnabled
|
||||
? Math.max(1, totalQuantity - Number(data?.activeQuantity || 0))
|
||||
: 1
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
@@ -46,9 +50,15 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
}
|
||||
if (options.length === 0) return
|
||||
// 默认只选精确匹配;非精确库存必须人工勾选并填写原因。
|
||||
const exactOptions = options.filter((item) => (item.matchType || 'exact') === 'exact')
|
||||
// 整单还要保证一条库存能一次满足订单总数量,否则默认选中也会被后端拒绝。
|
||||
const exactOptions = options.filter(
|
||||
(item) =>
|
||||
(item.matchType || 'exact') === 'exact' &&
|
||||
(!data?.sharingEnabled || item.remainingQuantity >= 1) &&
|
||||
(data?.sharingEnabled || item.remainingQuantity >= totalQuantity),
|
||||
)
|
||||
setSelectedIds(exactOptions.slice(0, needed).map((item) => item.inventoryId))
|
||||
}, [open, options, needed])
|
||||
}, [open, options, needed, data?.sharingEnabled, totalQuantity])
|
||||
|
||||
const nonExactSelected = useMemo(
|
||||
() =>
|
||||
@@ -120,8 +130,12 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
message={`该工单${data?.sharingEnabled ? `拼单 ${data.totalQuantity} 份,已匹配 ${data.activeQuantity} 份` : '为整单'},本次还需 ${needed} 条库存。`}
|
||||
description="自动匹配只做精确规格;手动匹配可选多条,拼单工单每选一条生成一个份额。选择非精确库存必须填写原因。"
|
||||
message={
|
||||
data?.sharingEnabled
|
||||
? `该工单拼单 ${totalQuantity} 份,已匹配 ${data.activeQuantity} 份,本次还需 ${needed} 份库存。`
|
||||
: `该工单为整单匹配,请选择 1 条可一次满足 ${totalQuantity} 件的库存。`
|
||||
}
|
||||
description="自动匹配只做精确规格;拼单工单可多选,每条库存按剩余数量生成份额。选择非精确库存必须填写原因。"
|
||||
/>
|
||||
<PercentTable<ConsignmentMatchCandidate>
|
||||
rowKey="inventoryId"
|
||||
@@ -129,6 +143,8 @@ export default function ConsignmentWorkOrderMatchModal({
|
||||
pagination={false}
|
||||
dataSource={options}
|
||||
rowSelection={{
|
||||
// 整单只能选一条;拼单可多选,每条库存按剩余数量生成份额。
|
||||
type: data?.sharingEnabled ? 'checkbox' : 'radio',
|
||||
selectedRowKeys: selectedIds,
|
||||
onChange: (keys) => setSelectedIds(keys.map((key) => Number(key))),
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user