# 质检工单 Tab 拆分与分型展示设计 > 日期:2026-06-16 > 状态:已确认(2026-06-16 组件边界已确认) > 范围:`web-admin` 质检工单 + 我的质检任务;后端列表 API 补充分型过滤 > 关联:`2026-06-12-qc-work-order-fee-summary-design.md`、`outbound-qc-pilot-view-sections.ts`、`2026-06-10-basic-experiment-design.md` --- ## 1. 背景与目标 ### 1.1 问题 【质检工单】【我的质检任务】原有三 Tab(入库 / 出库 / 临时),同一 Tab 内混用字段模板,导致: - 出库卡片出现入库字段(如「来源类型」`receptionSource`) - 基础实验与中试/临床共用卡片 meta,关键业务字段缺失或错位 - 入库采集与捐赠在组头、查看弹窗混用「协议」语义 ### 1.2 目标 将业务线拆为 **6 个 Tab**,每个 Tab **仅展示、仅查询** 对应子类型的工单与任务;列表卡片、查看弹窗、筛选区与 Tab 一一对应。 ### 1.3 非目标 - 不新增工单创建入口(捐赠入库仍由样本接收自动触发) - 不改动质检任务执行、配置检测、物料使用逻辑 - 不在本期做「全部工单」汇总 Tab - 不改动生产工单模块 Tab 结构 --- ## 2. Tab 定义 ### 2.1 Tab 配置表(权威) | Tab Key | 展示标签 | `workOrderType` | `receptionSource` | `bizType` | 列表布局 | |---------|----------|-----------------|-------------------|-----------|----------| | `inbound_cell_storage` | 采集入库 | `inbound` | `cell_storage` | — | 样本分组 `inbound-grouped` | | `inbound_research_donation` | 捐赠入库 | `inbound` | `research_donation` | — | 样本分组 `inbound-grouped` | | `outbound_pilot_service` | 中试 | `outbound` | — | `pilot_service` | 单列 `standalone` | | `outbound_basic_experiment` | 基础实验 | `outbound` | — | `basic_experiment` | 单列 `standalone` | | `outbound_clinical_application` | 临床应用 | `outbound` | — | `clinical_application` | 单列 `standalone` | | `temporary` | 临时 | `temporary` | — | — | 单列 `standalone` | **Tab 栏视觉分组(可选 CSS):** 入库 2 | 出库 3 | 临时 1,标签使用短文案。 **默认 Tab:** `inbound_cell_storage`(替换原 `inbound`)。 **路由记忆:** `?tab=`,刷新/分享链接保持当前 Tab。 ### 2.2 数据隔离规则(强约束) 每个 Tab 的列表 API 请求 **必须同时携带** 上表中的过滤参数,禁止仅传 `workOrderType` 后在客户端再筛: ``` 采集入库 → workOrderType=inbound AND receptionSource=cell_storage 捐赠入库 → workOrderType=inbound AND receptionSource=research_donation 中试 → workOrderType=outbound AND bizType=pilot_service 基础实验 → workOrderType=outbound AND bizType=basic_experiment 临床应用 → workOrderType=outbound AND bizType=clinical_application 临时 → workOrderType=temporary ``` **验收:** 在任一 Tab 下,列表中不得出现其它子类型的工单;切换 Tab 后分页重置为第 1 页。 --- ## 3. 筛选区(按 Tab) 移除原「业务类型」「来源类型」下拉(已由 Tab 固定)。 | Tab | 关键词 placeholder | 时间筛选标签 | 时间字段 | 额外筛选 | |-----|-------------------|-------------|----------|----------| | 采集入库 | 工单号/样本编码/入库编码 | 创建时间 | `createTime` | 触发来源(样本接收 / 生产过程) | | 捐赠入库 | 工单号/样本编码/入库编码 | 创建时间 | `createTime` | 触发来源 | | 中试 | 工单号/申请单号/申请单名称/患者 | 输注时间 | `infusionTime` | — | | 基础实验 | 工单号/申请单号/课题名称/客户 | 交付时间 | `infusionTime`(schedule 提货时间) | — | | 临床应用 | 工单号/申请单号/申请单名称/患者 | 输注时间 | `infusionTime` | — | | 临时 | 工单号 | 创建时间 | `createTime` | — | 默认时间范围:近 30 天(出库类按输注/交付时间,入库/临时按创建时间)。 --- ## 4. 组件边界(已确认) | 模块 | 策略 | 说明 | |------|------|------| | `QcWorkOrderTaskSubList` | **共用** | 任务执行、填结果、物料与 Tab 无关 | | 配置检测 / 调整检测弹窗 | **共用** | 三种工单类型逻辑相同 | | `QcWorkOrderCardList` + `QcWorkOrderSampleGroup` + `QcWorkOrderSingleCard` | **共用** | 各 Tab 通过 props 传入**卡片 meta 字段模板**(组头文案、meta 列、费用条开关) | | 查看详情弹窗 | **不共用** | 六 Tab 各一份**独立查看界面**;信息区块差异大,禁止再用 Host + variant 分支 | | Tab 页内容(筛选 + 工具栏 + 列表) | **拷贝再改** | 从现有入库/出库/临时 Tab **剪出/拷贝**为 6 个子页面;`index.vue` 仅 Tab 壳 | **我的质检任务:** 与质检工单 **共用同一套 6 个 Tab 子组件**(`tabs/*`),**已确认**。差异仅: - prop `pageMode='mine'` - 卡片操作:仅「查看」,无配置检测 / 新增 / 删除等 - 任务区 `taskListMode='mine'` - 数据:`GET /qc-tasks/mine` + 与 Tab 一致的 `receptionSource` / `bizType` 过滤 **维护成本:** 6 个 Tab 文件 + 2 个壳页面(`qc-work-order/index.vue`、`my-qc-task/index.vue`),**不是** 12 份拷贝。 --- ## 5. 列表卡片展示矩阵 共用 `QcWorkOrderCardList` / `QcWorkOrderSampleGroup` / `QcWorkOrderSingleCard`;各 Tab 子页面传入不同的 **cardMetaConfig**(字段 label、取值、Tag、费用条),禁止在卡片内用 `activeTypeTab === 'outbound'` 大包大揽。 ### 5.1 入库 · 采集(`inbound_cell_storage`) **样本组头(`QcWorkOrderSampleGroup`)** - 样本编码、样本类型、**采集协议号**、**协议状态**、接收时间、经手人 **组内费用条(组级,已有逻辑扩展)** - 组内存在 `cell_storage` 工单且 `paymentSummary` 时展示 `QcWorkOrderProtocolPaymentPanel` **卡片 meta** - 质检对象、触发来源 Tag、关联生产工单(仅 `triggerSource=production`)、实验室入库编码、检测方式、创建时间、备注 **卡片费用条** - `paymentSummary`(`QcWorkOrderProtocolPaymentPanel` compact) **工具栏** - 「新增入库质检工单」(仅本 Tab;`receptionSource` 固定 `cell_storage`) ### 5.2 入库 · 捐赠(`inbound_research_donation`) **样本组头** - 样本编码、样本类型、**捐赠单号**(`protocolNo` 字段存捐赠编号)、**捐赠状态**、接收时间、经手人 **卡片 meta** - 同采集入库卡片结构;**不展示**协议状态、费用相关字段 **工具栏** - 无新增按钮 ### 5.3 出库 · 中试(`outbound_pilot_service`) **卡片 meta** - 申请单号、申请单名称、渠道名称、**患者姓名**、第 N 次 / 共 M 次、输注时间、检测方式、创建时间、备注 **禁止展示** - `receptionSource`、业务类型 Tag(Tab 已表达)、基础实验专属字段 **费用条** - 不展示 ### 5.4 出库 · 基础实验(`outbound_basic_experiment`) **卡片 meta** - 申请单号、**课题名称**、**客户名称**、**交付时间**、售卖金额(可选,来自 `feeSummary.saleAmount`)、检测方式、创建时间、备注 **费用条** - `QcWorkOrderFeeSummaryBar`(`feeSummary`) ### 5.5 出库 · 临床应用(`outbound_clinical_application`) **卡片 meta** - 申请单号、申请单名称、渠道名称、患者姓名、第 N 次 / 共 M 次、输注时间、检测方式、创建时间、备注 **费用条** - `QcWorkOrderFeeSummaryBar`(与 `2026-06-13-production-work-order-fee-summary-design.md` D1=B 一致) ### 5.6 临时(`temporary`) 保持现状:产品编码/名称/类型、检测方式、创建时间;工具栏「新增临时工单」。 --- ## 6. 查看弹窗(六份独立界面) **不共用** `QcWorkOrderViewDialogHost`、`OutboundQcViewDialog` 及其中 `variant` 分支。自现有弹窗**拷贝**后按 Tab 删减字段;检测任务区块(实验室/第三方/参数/附件/物料)可抽 **`useQcWorkOrderTaskViewSections.ts`** 供六弹窗 import,避免任务展示逻辑拷六份。 ### 6.1 Tab → 弹窗组件(一一对应) | Tab Key | 新建组件 | 拷贝来源 | 主要差异 | |---------|----------|----------|----------| | `inbound_cell_storage` | `InboundCellStorageViewDialog.vue` | 现 `QcWorkOrderViewDialogHost` + `useQcWorkOrderViewDialog` | 协议状态、协议缴费、运输、样本接收 | | `inbound_research_donation` | `InboundResearchDonationViewDialog.vue` | 上者拷贝 | **无**协议缴费;捐赠状态;组头/基本信息用捐赠文案 | | `outbound_pilot_service` | `OutboundPilotViewDialog.vue` | 现 `OutboundQcViewDialog` | 仅中试:申请单、患者、输注方案/计划、产品、检测任务;**无**缴费区块 | | `outbound_basic_experiment` | `OutboundBasicExperimentViewDialog.vue` | 出库弹窗拷贝 | 课题/客户/交付计划/产品/缴费;**无**患者、输注方案 | | `outbound_clinical_application` | `OutboundClinicalViewDialog.vue` | 出库弹窗拷贝 | 同中试结构 + 临床应用缴费信息(如有) | | `temporary` | `TemporaryQcViewDialog.vue` | 入库弹窗拷贝 | 仅产品信息 + 检测任务;**无**接收/运输/协议 | ### 6.2 挂载方式 - 各 **Tab 子页面** 内直接 `import` 对应弹窗,`:visible` + `@view` 打开;**不再**使用统一 `openView(row)` 按类型分流。 - 旧文件 `QcWorkOrderViewDialogHost.vue`、`OutboundQcViewDialog.vue` 在六弹窗落地并切换引用后**删除**(或首版标记 `@deprecated` 再删)。 ### 6.3 可共用的底层(非 UI 壳) | 共用项 | 说明 | |--------|------| | `SystemViewDialog` | 布局壳 | | `ProtocolAttachmentReadonly` | 附件只读 | | `QcWorkOrderProtocolPaymentPanel` | 仅采集入库弹窗使用 | | `fetchQcWorkOrderDetail` / `fetchOutboundQcDetail` | API | | `useQcWorkOrderTaskViewSections`(新建) | 检测任务 sections + 附件 slot + 物料 slot | --- ## 7. Tab 子页面结构(拷贝再改) ``` web-admin/src/views/quality-inspection/qc-work-order/ index.vue # 6 Tab 壳 + ?tab= 路由 tabs/ InboundCellStorageTab.vue # 从原 index 入库区块剪出 InboundResearchDonationTab.vue # 拷贝 InboundCellStorageTab 再改 OutboundPilotTab.vue # 从原出库区块剪出 OutboundBasicExperimentTab.vue # 拷贝 OutboundPilotTab 再改 OutboundClinicalTab.vue # 拷贝 OutboundPilotTab 再改 TemporaryTab.vue # 从原临时区块剪出 view-dialogs/ InboundCellStorageViewDialog.vue InboundResearchDonationViewDialog.vue OutboundPilotViewDialog.vue OutboundBasicExperimentViewDialog.vue OutboundClinicalViewDialog.vue TemporaryQcViewDialog.vue ``` `my-qc-task/index.vue` 同样 6 Tab 壳,**import 上述 `tabs/*` 组件**,传 `page-mode="mine"`。 --- ## 8. 我的质检任务 与质检工单 **共用** `tabs/*` 子组件与 `cardMetaConfig`;差异: - `pageMode='mine'`:卡片仅「查看」 - 任务列表 `taskListMode='mine'` - 数据流:先按 Tab 参数请求 `GET /qc-tasks/mine`,再聚合为卡片 **后端扩展 `list_mine_tasks`:** 增加 `receptionSource`、`bizType` 过滤,与工单列表对齐,避免客户端从全量任务中筛类型。 **`qc-mine-task-groups.ts`:** - `QcWorkOrderTypeTab` 替换为 `QcWorkOrderTabKey` - `resolveWorkOrderTabKey(task)` 由 `workOrderType` + `receptionSource` / `bizType` 解析 - `filterMineEntriesByWorkOrderSearch` 按 `tabKey` 固定过滤,移除 `bizType`/`receptionSource` 筛选项 --- ## 9. 后端 API 变更 ### 7.1 `GET /qc-work-orders`(列表) 新增 Query 参数: | 参数 | 类型 | 说明 | |------|------|------| | `receptionSource` | string | 入库分型:`cell_storage` / `research_donation` | `list_work_orders()` 在 `work_order_type=inbound` 且 `reception_source` 有值时: ```python q0 = q0.filter(BizQcWorkOrder.reception_source == rs) ``` 出库 Tab 继续传 `bizType`(已有)。 ### 7.2 `GET /qc-tasks/mine` 新增 Query 参数:`receptionSource`、`bizType`,在 join `BizQcWorkOrder` 后过滤。 ### 7.3 列表 enrich 确认基础实验列表项含 `projectTitle`(课题名称)、`customerName`;缺则于 `_enrich_outbound_list_fields` 补充 join。 --- ## 10. 前端模块结构(摘要) ``` web-admin/src/utils/qc-work-order-tabs.ts # Tab 常量 + toListQueryParams web-admin/src/utils/qc-work-order-card-meta.ts # 六 Tab 的 cardMetaConfig 导出(供 CardList props) web-admin/src/views/.../qc-work-order/tabs/ # 6 Tab 子页面(拷贝再改) web-admin/src/views/.../qc-work-order/view-dialogs/ # 6 独立查看弹窗 web-admin/src/views/.../qc-work-order/composables/useQcWorkOrderTaskViewSections.ts # 检测任务区块共用 ``` **共用不改业务:** `QcWorkOrderTaskSubList`、配置检测弹窗(仍在 `index.vue` 或 composable)。 **删除/替换:** `QcWorkOrderViewDialogHost.vue`、`OutboundQcViewDialog.vue`、`useQcWorkOrderViewDialog.ts`(逻辑拆入六弹窗 + task composable)。 --- ## 11. 验收清单 ### 11.1 数据隔离 - [ ] 采集入库 Tab 仅 `receptionSource=cell_storage` 的 inbound 工单 - [ ] 捐赠入库 Tab 仅 `research_donation` 的 inbound 工单 - [ ] 中试 / 基础实验 / 临床 Tab 各自仅对应 `bizType` 的 outbound 工单 - [ ] 临时 Tab 仅 `workOrderType=temporary` - [ ] 我的质检任务六 Tab 与上相同(任务所属工单类型一致) ### 11.2 展示 - [ ] 出库卡片无 `receptionSource` 字段 - [ ] 基础实验卡片无患者/输注疗程字段,有课题/客户/交付时间 - [ ] 捐赠组头为捐赠单号/捐赠状态,非协议文案 - [ ] 采集入库查看弹窗有协议缴费;捐赠无 - [ ] 六 Tab 各打开对应**独立**查看弹窗,无交叉引用 Host/variant - [ ] 基础实验查看弹窗无患者/输注方案;中试/临床无课题专属区块 ### 11.3 操作 - [ ] 仅采集入库 Tab 有「新增入库质检工单」 - [ ] 仅临时 Tab 有「新增临时工单」 - [ ] 配置检测 / 调整检测 / 编辑备注行为与改版前一致 ### 11.4 回归 - [ ] Tab 切换重置分页与筛选(保留 Tab 专属默认时间范围) - [ ] URL `?tab=` 可深链到指定 Tab - [ ] 费用摘要条规则与 `2026-06-12-qc-work-order-fee-summary-design.md` 一致 --- ## 12. 决策记录 | 项 | 决策 | |----|------| | Tab 数量 | 6(入库 2 + 出库 3 + 临时 1) | | Tab 标签 | 短标签:采集入库 / 捐赠入库 / 中试 / 基础实验 / 临床应用 / 临时 | | 实现策略 | **Tab 子页面拷贝再改**;`index.vue` 仅 Tab 壳 | | 数据过滤 | **服务端** 强制分型,禁止仅靠前端过滤 | | TaskSubList | **共用** | | 配置检测弹窗 | **共用** | | CardList + SampleGroup | **共用**,props 传 `cardMetaConfig` | | 查看弹窗 | **六份独立界面**,不共用 Host / OutboundQcViewDialog | | 检测任务展示 | composable 共用,避免六份弹窗重复任务 UI 逻辑 | | 我的质检任务 | **已确认** 共用 6 个 `tabs/*`,`pageMode='mine'` |