11 KiB
11 KiB
生产任务组合模版 — 任务名称动态参数与条目区间距
日期:2026-09-01
状态:已实现(待浏览器联调)
范围:【生产任务组合模版】新增/编辑弹窗 UI;【生产工单】/【我的生产任务】按模版派发时任务名称占位符正向替换;「保存为模版」反向还原
派发重点场景:两页中【细胞存储】【科研捐赠】生产工单(有样本接收与实验室入库编码)
不改:单条派发、描述/备注字段、{pro_no}别名
1. 背景与目标
1.1 问题
- 模版新增/编辑弹窗中,「添加条目」按钮与条目列表间距过紧。
- 同套模版在不同生产工单上复用时,任务名称需要带上该工单对应的实验室入库编码;当前
task_name原样落库,无法按工单动态生成。 - 「保存为模版」若直接写入已替换的具体编码,模版无法跨工单复用。
1.2 目标
- 「任务条目」区:按钮与列表增加间距;区域上方增加一行可用动态参数说明。
- 约定唯一占位符
{lab_storage_code},语义对齐【样本接收】中的实验室入库编码(biz_sample_reception.lab_storage_code)。 - 【生产工单】与【我的生产任务】中,对【细胞存储】【科研捐赠】等工单按模版派发时:将模版任务名中的
{lab_storage_code}替换为该工单对应的实验室入库编码,再创建任务、完成派发。 - 「保存为模版」时,对任务名称做反向还原后再写入模版。
1.3 已确认产品决策
| 项 | 决策 |
|---|---|
| 占位符字面量 | {lab_storage_code}(大小写敏感;不实现 {pro_no}) |
| 数据来源 | 样本接收「实验室入库编码」;经生产工单关联解析 |
| 正向空值 | 无编码时替换为空串({lab_storage_code}样本处理 → 样本处理) |
| 作用字段 | 仅任务名称 taskName / task_name |
| 说明文案位置 | 模版维护页「任务条目」区域上方一行 |
| 正向替换时机 | 后端 create_tasks_from_set(按模版派发)统一替换;两页共用 DispatchTaskDialog |
| 派发适用 | 【细胞存储】【科研捐赠】工单:用工单关联样本接收的入库编码赋值;无编码则置空仍派发 |
| 保存为模版 | 反向还原:任务名中的当前入库编码 → {lab_storage_code} 再写入模版 |
| 入口页 | 派发与保存均在【生产工单】/【我的生产任务】共用组件,行为一致 |
2. 概念与数据流
模版条目 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}后写入模版,便于跨工单复用。
3. 占位符规范
3.1 白名单
| 占位符 | 含义 | 解析 |
|---|---|---|
{lab_storage_code} |
实验室入库编码 | 见 §4 |
- 仅替换白名单键;未知
{xxx}原样保留。 - 同一名称内可多次出现,全部替换。
- 不支持嵌套、条件表达式或过滤器。
3.2 正向示例
| 模版任务名称 | 入库编码 | 生成结果 |
|---|---|---|
{lab_storage_code}样本处理 |
LS20260301001 |
LS20260301001样本处理 |
{lab_storage_code}样本处理 |
(空) | 样本处理 |
常规分装 |
任意 | 常规分装 |
{unknown}处理 |
任意 | {unknown}处理(未知键不替换) |
3.3 反向示例
| 任务名称 | 入库编码 | 还原结果 |
|---|---|---|
LS001样本处理 |
LS001 |
{lab_storage_code}样本处理 |
前LS001后LS001 |
LS001 |
前{lab_storage_code}后{lab_storage_code} |
常规分装 |
LS001 |
常规分装 |
LS001样本处理 |
(空) | LS001样本处理(不还原) |
4. 后端解析与替换
4.1 解析 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)。 - 仍为空 → 视为无编码(正向替换空串;反向不还原)。
临时工单等无接收记录场景:按无编码处理,不阻断操作。
4.2 正向替换函数
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[]覆盖,均走同一替换。
4.3 反向还原函数
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由服务端从任务加载还是前端提交,均执行一次。 - 前端 打开「保存为模版」时:用当前工单入库编码对预览条目做同样还原,弹窗内显示占位符并可再编辑。
两端算法一致;以后端落库结果为准。前端已还原后再提交时:名称中已无具体编码 → 后端再还原为幂等(无匹配则不变)。
4.4 API / 表结构
- 不改表结构、不改请求/响应字段契约。
- 按模版派发响应中的新建任务
taskName为正向替换后的值。 - 保存为模版详情中的条目
taskName为反向还原后的值。
4.5 单条派发
POST /production-tasks 不做占位符正向替换。若用户在单条派发名称中手写 {lab_storage_code},将按字面保存。
5. 前端
5.1 生产任务组合模版页 production-task-set/index.vue
间距
.set-items-block内工具栏与.inner-table-wrap之间增加垂直间距(建议工具栏margin-bottom: 12px,或父级gap: 12px+ flex 列布局)。
说明文案
-
放在「任务条目」内容区顶部、「添加条目」工具栏之上。
-
文案建议:
任务名称支持动态参数:
{lab_storage_code}(实验室入库编码)。按模版派发时按工单关联样本接收自动替换;无入库编码则置空。保存为模版时,会将任务名中的入库编码还原为该占位符。 -
样式:次要说明(12px / muted);可用
.form-tip或等价 class。
查看弹窗
- 展示模版原文(可含占位符);不做示例替换预览。
5.2 保存为模版 SaveProductionTaskSetDialog.vue
- 【生产工单】与【我的生产任务】共用,同一套反向还原,无分端特殊分支。
- 扩展
mapWorkOrderTasksToSetItems:接收可选labStorageCode,映射时对taskName做反向还原。 - 打开弹窗的调用方传入当前工单实验室入库编码(与
DispatchTaskDialog的headerLabStorageCode同源:row.labStorageCode或reception.labStorageCode)。 - 保存弹窗本次不强制加参数说明文案(维护页已有说明);间距可与维护页对齐,非必须。
5.3 派发弹窗 DispatchTaskDialog.vue
- 【生产工单】与【我的生产任务】共用;【细胞存储】【科研捐赠】Tab 下选择「任务组合模版」派发时,走
POST /production-tasks/from-set。 - 替换在后端完成:解析该工单
lab_storage_code,对每条任务名中的{lab_storage_code}赋值后建任务;前端不必先本地替换再提交(提交覆盖items时后端仍会正向替换)。 - 派发成功后关闭弹窗并刷新任务列表,列表中任务名应为已代入入库编码的最终名称。
- 本次不强制在派发弹窗加占位符说明与间距(维护页已有说明)。
6. 错误处理与边界
| 场景 | 行为 |
|---|---|
| 正向:无入库编码 | 占位符 → 空串,继续创建 |
正向:未知 {xxx} |
原样保留 |
| 正向:替换后任务名为空 | 沿用现有「任务名称必填」语义,避免无名任务 |
| 正向:名称超长 | 与现有 create_production_task / VARCHAR(200) 一致 |
| 反向:无入库编码 | 不还原,保留任务原名 |
| 反向:名称不含该编码 | 不改动 |
| 反向:编码为空串 | 不还原(避免误替换) |
7. 测试要点
- 纯函数正向:有编码 / 空编码 / 无占位符 / 多次出现 / 未知键。
- 纯函数反向:有编码命中 / 多次命中 / 无编码不还原 / 名称不含编码。
create_tasks_from_set:有接收编码 → 任务名正确;无编码 → 空串;前端 items 仍含占位符 → 同样替换。create_set_from_work_order:任务名含入库编码 → 模版为占位符;无编码 → 原文;前端已还原再提交 → 幂等正确。- 前端:维护页说明与间距;保存弹窗(两入口)打开后有编码时任务名显示占位符。
8. 实现范围清单
| # | 项 | 位置 |
|---|---|---|
| 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 及打开处 |
9. 非目标
{pro_no}或其它别名- 描述、备注字段的占位符
- 单条派发路径正向替换
- 派发弹窗内「替换后预览」专用 UI
- 按部分匹配/模糊匹配还原(仅精确子串等于完整入库编码)