日期:2026-09-01
状态:已实现(待浏览器联调)
范围:【生产任务组合模版】新增/编辑弹窗 UI;【生产工单】/【我的生产任务】按模版派发时任务名称占位符正向替换;「保存为模版」反向还原
派发重点场景:两页中【细胞存储】【科研捐赠】生产工单(有样本接收与实验室入库编码)
不改:单条派发、描述/备注字段、{pro_no}别名
task_name 原样落库,无法按工单动态生成。{lab_storage_code},语义对齐【样本接收】中的实验室入库编码(biz_sample_reception.lab_storage_code)。{lab_storage_code} 替换为该工单对应的实验室入库编码,再创建任务、完成派发。| 项 | 决策 |
|---|---|
| 占位符字面量 | {lab_storage_code}(大小写敏感;不实现 {pro_no}) |
| 数据来源 | 样本接收「实验室入库编码」;经生产工单关联解析 |
| 正向空值 | 无编码时替换为空串({lab_storage_code}样本处理 → 样本处理) |
| 作用字段 | 仅任务名称 taskName / task_name |
| 说明文案位置 | 模版维护页「任务条目」区域上方一行 |
| 正向替换时机 | 后端 create_tasks_from_set(按模版派发)统一替换;两页共用 DispatchTaskDialog |
| 派发适用 | 【细胞存储】【科研捐赠】工单:用工单关联样本接收的入库编码赋值;无编码则置空仍派发 |
| 保存为模版 | 反向还原:任务名中的当前入库编码 → {lab_storage_code} 再写入模版 |
| 入口页 | 派发与保存均在【生产工单】/【我的生产任务】共用组件,行为一致 |
模版条目 task_name(可含 {lab_storage_code})
│
▼ 按模版派发 create_tasks_from_set(正向)
生产工单 ──► 解析 lab_storage_code(reception_id / sample_code)
│
▼ 替换占位符
生产任务 task_name(具体编码)
│
▼ 保存为模版 create_set_from_work_order(反向)
模版条目 task_name(编码还原为 {lab_storage_code})
{lab_storage_code} 后写入模版,便于跨工单复用。| 占位符 | 含义 | 解析 |
|---|---|---|
{lab_storage_code} |
实验室入库编码 | 见 §4 |
{xxx} 原样保留。| 模版任务名称 | 入库编码 | 生成结果 |
|---|---|---|
{lab_storage_code}样本处理 |
LS20260301001 |
LS20260301001样本处理 |
{lab_storage_code}样本处理 |
(空) | 样本处理 |
常规分装 |
任意 | 常规分装 |
{unknown}处理 |
任意 | {unknown}处理(未知键不替换) |
| 任务名称 | 入库编码 | 还原结果 |
|---|---|---|
LS001样本处理 |
LS001 |
{lab_storage_code}样本处理 |
前LS001后LS001 |
LS001 |
前{lab_storage_code}后{lab_storage_code} |
常规分装 |
LS001 |
常规分装 |
LS001样本处理 |
(空) | LS001样本处理(不还原) |
lab_storage_code在按模版派发 / 保存为模版路径中,对当前工单:
work_order.reception_id 非空:查本租户未删除的 BizSampleReception,取 lab_storage_code(trim)。sample_code:复用既有按样本编码取入库编码的映射能力(与 production_task 详情补全 labStorageCode 一致,如 get_lab_storage_code_map_by_sample_codes)。临时工单等无接收记录场景:按无编码处理,不阻断操作。
apply_task_name_placeholders(template: str, *, lab_storage_code: str) -> str
template 中所有 {lab_storage_code} 替换为传入值(已 trim;空则空串)。create_tasks_from_set 写入 create_production_task 的 task_name 之前调用。items[] 覆盖,均走同一替换。revert_task_name_placeholders(task_name: str, *, lab_storage_code: str) -> str
规则:
lab_storage_code 经 trim 后为空 → 不改动 task_name。{lab_storage_code}(普通字符串替换,非正则通配)。{lab_storage_code},且又含同一编码文本,仅替换编码文本。接入:
create_set_from_work_order:落库前对每条 task_name 执行还原(编码解析同 §4.1)。无论 items 由服务端从任务加载还是前端提交,均执行一次。两端算法一致;以后端落库结果为准。前端已还原后再提交时:名称中已无具体编码 → 后端再还原为幂等(无匹配则不变)。
taskName 为正向替换后的值。taskName 为反向还原后的值。POST /production-tasks 不做占位符正向替换。若用户在单条派发名称中手写 {lab_storage_code},将按字面保存。
production-task-set/index.vue间距
.set-items-block 内工具栏与 .inner-table-wrap 之间增加垂直间距(建议工具栏 margin-bottom: 12px,或父级 gap: 12px + flex 列布局)。说明文案
任务名称支持动态参数:
{lab_storage_code}(实验室入库编码)。按模版派发时按工单关联样本接收自动替换;无入库编码则置空。保存为模版时,会将任务名中的入库编码还原为该占位符。
.form-tip 或等价 class。查看弹窗
SaveProductionTaskSetDialog.vuemapWorkOrderTasksToSetItems:接收可选 labStorageCode,映射时对 taskName 做反向还原。DispatchTaskDialog 的 headerLabStorageCode 同源:row.labStorageCode 或 reception.labStorageCode)。DispatchTaskDialog.vuePOST /production-tasks/from-set。lab_storage_code,对每条任务名中的 {lab_storage_code} 赋值后建任务;前端不必先本地替换再提交(提交覆盖 items 时后端仍会正向替换)。| 场景 | 行为 |
|---|---|
| 正向:无入库编码 | 占位符 → 空串,继续创建 |
正向:未知 {xxx} |
原样保留 |
| 正向:替换后任务名为空 | 沿用现有「任务名称必填」语义,避免无名任务 |
| 正向:名称超长 | 与现有 create_production_task / VARCHAR(200) 一致 |
| 反向:无入库编码 | 不还原,保留任务原名 |
| 反向:名称不含该编码 | 不改动 |
| 反向:编码为空串 | 不还原(避免误替换) |
create_tasks_from_set:有接收编码 → 任务名正确;无编码 → 空串;前端 items 仍含占位符 → 同样替换。create_set_from_work_order:任务名含入库编码 → 模版为占位符;无编码 → 原文;前端已还原再提交 → 幂等正确。| # | 项 | 位置 |
|---|---|---|
| 1 | 工具栏与列表间距 + 说明文案 | web-admin/.../production-task-set/index.vue |
| 2 | 正向/反向纯函数 + 单测 | backend/app/(建议可测 utils) |
| 3 | create_tasks_from_set 正向替换 |
backend/app/crud/production_task_set.py |
| 4 | create_set_from_work_order 反向还原 |
同上 |
| 5 | 前端映射还原 + 传入 labStorageCode | production-task-set.ts、SaveProductionTaskSetDialog.vue 及打开处 |
{pro_no} 或其它别名