日期:2026-07-07
状态:已批准(2026-07-07)
范围:web-admin【质检工单】【我的质检任务】入库类 Tab(细胞存储、科研捐赠);后端按样本编码查工单小接口
关联:2026-06-25-qc-work-order-protocol-group-design.md(历史协议组方案,现网未接入列表)、2026-06-16-qc-work-order-tab-split-design.md
细胞存储、科研捐赠入库质检列表中,样本卡标题栏展示 「同协议样本」 胶囊(实验室编号 + 样本类型),数据来自后端 protocolSiblingSamples enrich。
现状:
<span>,不可点击;scrollToInboundSampleCard(anchorId) 工具函数,但未绑定到 UI;scrollToInboundSampleCard)。QcWorkOrderProtocolGroup 协议组外层卡片。total、不触发服务端翻页)。| 决策点 | 结论 |
|---|---|
| 我的质检任务 · 插入卡内容 | 选项 A:仍插入完整工单卡;任务区可为空;操作区仅「查看」等只读能力(与现网 showWorkOrderViewOnly 一致) |
| 插入位置 | 紧贴点击来源那张样本卡下方,不按全局排序重排 |
| 点击当前样本芯片 | 仅高亮,不重复插入、不请求接口 |
| 插入卡生命周期 | Tab 切换、搜索/重置、翻页、loadData() 重载时清空所有插入卡 |
| 去重 | 同一 sampleCode 已在主列表或已插入 → 只滚动到已有卡,不重复插入 |
| 无质检工单 | Toast:该样本暂无质检工单(不插入空壳卡) |
| 状态 | 表现 |
|---|---|
当前样本(is-current) |
保持现有高亮样式;cursor: default;点击仅触发高亮动画 |
| 可跳转样本 | cursor: pointer;hover 边框/底色略加深;role="button" + 键盘可聚焦 |
| 加载中(拉取兄弟样本) | 来源卡下方显示细条 loading(v-loading 或 skeleton),禁止重复点击 |
用户点击芯片(非当前)
→ 解析 targetSampleCode(来自 link.sampleCode)
→ 若 document.getElementById(anchorId) 存在
→ scrollToInboundSampleCard(anchorId)
→ 否则
→ 若 insertedCards 或主列表已含该 sampleCode → 滚动到已有 anchor
→ 否则 POST/GET 按样本拉工单
→ 有工单:在来源卡下插入 SampleGroup,再滚动高亮
→ 无工单:ElMessage.info
插入卡在标题栏副标题区增加轻量 muted 文案:「同协议关联」(或同等语义 Tag),与主列表卡区分;其余结构与普通 QcWorkOrderSampleGroup 一致。
pageMode=mine → showWorkOrderViewOnly=true,showWorkOrderOps=false。presetTasksByWorkOrderId:仅填入当前用户在该工单下的任务;无任务则为 {} 或空数组 → 任务区自然为空。QcInboundProtocolSiblingLink 增加 sampleCode(拉数主键;anchorId 仍由 buildInboundSampleCardAnchorId(sampleCode) 生成):
export type QcInboundProtocolSiblingLink = {
sampleCode: string
anchorId: string
labStorageCode: string
sampleTypeName: string
}
buildProtocolSiblingLinksFromSamples 从 QcProtocolSiblingSample.sampleCode 写入。
GET /api/web/admin/qc-work-orders/by-sample
| 参数 | 必填 | 说明 |
|---|---|---|
sampleCode |
是 | 样本编码,精确匹配 |
receptionSource |
是 | cell_storage | research_donation,与 Tab 一致 |
响应:{ list: QcWorkOrder[] }(0~N 条,同一样本可有多条工单:接收 + 生产过程等)
查询规则:
tenant_no = 当前用户租户del_flag = 0work_order_type = inboundsample_code 精确匹配reception_source 匹配_enrich_work_order_list_items、_enrich_work_order_qc_targets、_enrich_unfinished_badge_counts 等,含 protocolSiblingSamples不用现有 keyword 的原因:列表关键词为 LIKE 模糊匹配,不可靠。
插入卡渲染前,对返回的 workOrder.id 列表调用现有「我的任务」查询(可批量 by workOrderIds,或逐工单 fetchMyQcTaskList 带 workOrderId 过滤——以实现时现有 API 能力为准),仅合并指派给当前用户的任务到 presetTasksByWorkOrderId。
若无批量接口,首期可接受:插入后对每个 workOrderId 并行请求 mine tasks(通常 1~2 条工单,可接受)。
pageMode=manage 时,插入卡工单 ID 走现有 fetchQcInspectionBatchesBatch 写入 presetBatchesByWorkOrderId。
useInboundSiblingNavigation建议新建 composables/useInboundSiblingNavigation.ts,由 useQcWorkOrderTabPage 持有并下传 QcWorkOrderCardList。
type InsertedInboundSiblingCard = {
sampleCode: string
anchorId: string
insertedAfterAnchorId: string
group: QcWorkOrderSampleGroupModel
protocolSiblingLinks: QcInboundProtocolSiblingLink[]
showProtocolPaymentAtGroup: boolean
loading?: boolean
isInserted: true
}
// 状态
insertedCards: Ref<InsertedInboundSiblingCard[]>
navigating: Ref<boolean>
// 方法
async function navigateToSibling(payload: {
targetSampleCode: string
sourceAnchorId: string
receptionSource: string
protocolSiblingLinks: QcInboundProtocolSiblingLink[]
}): Promise<void>
function clearInsertedCards(): void
navigateToSibling 逻辑:
targetAnchorId。insertedCards/主列表 groupKey 已含 → scrollToInboundSampleCard。fetchQcWorkOrdersBySample → groupInboundQcWorkOrders → 构建 protocolSiblingLinks(复用 buildProtocolSiblingLinksFromSamples)→ push 到 insertedCards(insertedAfterAnchorId = sourceAnchorId)→ nextTick 后滚动。清空时机:loadData 开头、handleTabChange、handleReset、翻页事件。
QcWorkOrderCardList)入库 grouped 分支:
<template v-for="item in inboundSampleListItems" :key="item.group.groupKey">
<QcWorkOrderSampleGroup ... @sibling-navigate="onSiblingNavigate" />
<QcWorkOrderSampleGroup
v-for="ins in insertedCardsAfter(item.cardAnchorId)"
:key="'ins-' + ins.sampleCode"
:is-inserted-sibling="true"
...
/>
</template>
「所有」Tab 中 kind === 'sample-group' 的入库项同样支持插入(resolveProtocolSiblingLinksForGroup 已有 links)。
QcWorkOrderSampleGroup:
<button type="button"> 或带 @click 的可聚焦元素;@click → 仅 scrollToInboundSampleCard(cardAnchorId);emit('sibling-navigate', { targetSampleCode, protocolSiblingLinks });isInsertedSibling?: boolean 控制「同协议关联」标识。| 路径 | 改动 |
|---|---|
web-admin/src/utils/qc-work-order-inbound-sample-cards.ts |
link 增加 sampleCode |
web-admin/src/views/.../QcWorkOrderSampleGroup.vue |
可点击芯片 + emit |
web-admin/src/views/.../QcWorkOrderCardList.vue |
插入卡渲染、事件桥接 |
web-admin/src/views/.../composables/useInboundSiblingNavigation.ts |
新建 |
web-admin/src/views/.../composables/useQcWorkOrderTabPage.ts |
集成 composable、清空、下传 props |
web-admin/src/api/qc-work-order.ts |
fetchQcWorkOrdersBySample |
backend/app/crud/qc_work_order.py |
list_work_orders_by_sample |
backend/app/api/qc.py(或 qc_work_order 路由模块) |
注册 GET 路由 |
| 场景 | 处理 |
|---|---|
| 协议有样本、尚无质检工单 | Toast,不插入 |
| 接口 403 / 无数据权限 | Toast:无权查看该样本质检信息 |
| 网络失败 | Toast + 不插入;navigating 复位 |
| 连续快速点击不同芯片 | 防抖或队列;以最后一次为准 |
| 插入后用户翻页 | 插入卡清空(已确认) |
细胞存储 paymentSummary |
插入卡 showProtocolPaymentAtGroup 按 buildInboundSampleListItems 同规则(协议内 payment host) |
| 特殊样本 | 插入卡继承 enrich 的 isSpecial / specialRemark,与主列表一致 |
2026-06-25-qc-work-order-protocol-group-design.md 规划协议组 + 样本 Tab;现网为每样本独立卡 + 标题栏同协议芯片。本需求在现架构上补跨卡导航,不推翻当前列表形态。若未来接入协议组,导航可内聚为组内 Tab 切换,插入卡逻辑可废弃。
by-sample 接口 + 前端 API 封装sampleCode 写入 link 模型useInboundSiblingNavigation + 仅滚动(列表内互跳)| 项 | 确认人 | 日期 |
|---|---|---|
| 我的任务插入卡策略(选项 A) | 产品/用户 | 2026-07-07 |
| 全文设计 | 待审核 | — |