# 样本卡片 · 协议级附件迷你缩略图展示 > 日期:2026-07-13 > 状态:已确认(展示形态 B · 迷你缩略图) > 关联:`2026-07-13-protocol-consent-health-survey-design.md`(孕妇健康调查表字段)、捐赠单传染病检测报告字段 --- ## 1. 目标 在以下三个卡片场景中,按来源展示协议单级附件,支持点击预览/下载;无附件显示「**无**」。 | 页面 | 位置 | |------|------| | 预产及在途样本 | 紧凑小卡片 + 展开详情卡片 | | 样本接收 | 待接收 Tab 卡片 + 已接收 Tab 卡片 | ### 1.1 来源与附件映射 | `sourceType` | 展示标签 | 数据字段 | |--------------|----------|----------| | `cell_storage`(默认) | 孕妇健康调查表 | `healthSurveyAttachmentFiles` | | `research_donation` | 传染病检测报告 | `infectiousDiseaseReportAttachmentFiles` | **说明**:展示的是协议/捐赠单录入的附件,**不是**样本接收登记时的 `receiveAttachmentFiles`。 ### 1.2 已确认交互(B · 迷你缩略图) - 每个附件一张 **迷你卡片**:缩略图区 + 单行文件名 + 下载图标 - 图片:缩略图 `object-fit: cover`;文档/视频:与 `ProtocolAttachmentReadonly` 相同的类型图标 - 点击缩略图:图片新开页预览;非图片仅下载区可点 - 点击下载图标:走现有 `downloadAttachmentFile` - 无附件:标签下方显示「无」(不用「暂无附件」) - 多附件:横向排列,超出宽度换行 --- ## 2. 现状缺口 ### 2.1 后端 | 接口 | 细胞存储 | 科研捐赠 | |------|----------|----------| | 预产及在途列表 | 仅有 `healthSurveyFormUrls` 字符串 | 已有 `infectiousDiseaseReportAttachmentFiles` | | 样本接收待/已接收列表 | 无协议附件字段 | 无协议附件字段 | ### 2.2 前端 - `PreTransitProtocolCardList.vue`、`SampleReceptionCardList.vue` 均未展示协议附件 - `ProtocolAttachmentReadonly.vue` 为标准尺寸(140×96),不适合紧凑卡片 --- ## 3. 方案(列表 API 补齐 + 共享迷你组件) 采用列表接口直接返回 `*AttachmentFiles`(与协议 CRUD 字段命名一致),前端新建迷你缩略图组件统一渲染。不做懒加载详情、不做前端 URL 手拆。 --- ## 4. 后端 ### 4.1 `pre_transit_sample.py` 细胞存储行构建处(`_protocol_to_dict` 之后),补充: ```python d["healthSurveyAttachmentFiles"] = urls_to_attachment_list( r.health_survey_form_urls, db, tenant_no ) ``` 科研捐赠侧 `_donation_to_dict(r, db, tenant_no)` 已含 `infectiousDiseaseReportAttachmentFiles`,无需改动。 ### 4.2 `sample_reception.py` 在 `_rows_to_sample_list_items`、`_donation_rows_to_sample_list_items` 构建 `item` 时,从已 join 的 `protocol` / `donation` 实体补充: - 细胞存储:`healthSurveyAttachmentFiles` ← `protocol.health_survey_form_urls` - 科研捐赠:`infectiousDiseaseReportAttachmentFiles` ← `donation.infectious_disease_report_urls` 使用现有 `urls_to_attachment_list`;协议/捐赠对象已在查询行中,不额外 N+1。 ### 4.3 范围外 - 不修改样本接收登记/编辑 API 写入逻辑 - 不修改 checklist 检查项 - 不修改生产工单、质检工单卡片 --- ## 5. 前端 ### 5.1 新建 `ProtocolAttachmentMiniReadonly.vue` 路径:`web-admin/src/components/business/protocol-attachment-mini-readonly/index.vue` **Props** | 属性 | 类型 | 说明 | |------|------|------| | `label` | `string` | 字段标签,如「孕妇健康调查表」 | | `files` | `Array>` | 附件列表 | | `emptyText` | `string` | 默认 `'无'` | **Events**:`preview`、`download`(与 `ProtocolAttachmentReadonly` 一致,由父级或 composable 处理) **尺寸与样式**(对齐 Fresh Mint,缩小版 `ProtocolAttachmentReadonly`) | 元素 | 尺寸 | |------|------| | 单卡宽度 | 80px | | 缩略图区高度 | 56px | | 圆角 | 6px | | 边框 | `#e2e8f0`,hover `#047857` | | 文件名字号 | 10px,单行 ellipsis | | 下载图标 | 18×18,与标准组件同色交互 | | 列表 gap | 6px,横向 flex-wrap | **空态**:仅显示 `label` + `emptyText`(「无」),不渲染缩略图占位。 **逻辑复用**:`resolveFileUrl`、`isImageFile`、`isVideoFile` 与 `ProtocolAttachmentReadonly.vue` 保持一致(可后续抽 util,本期允许组件内复制以保持最小 diff)。 ### 5.2 共享工具 **`web-admin/src/utils/protocol-source-attachment.ts`** ```ts export function getSourceProtocolAttachment(row: Record): { label: string files: Array> } ``` 按 `sourceType` 返回标签与 files 数组。 **`web-admin/src/composables/useAttachmentActions.ts`**(或复用 `qc-work-order/composables/useQcViewAttachmentHelpers.ts` 模式) - `fullFileUrl(url)` - `handleAttachmentPreview(file)` → `window.open` - `handleAttachmentDownload(file)` → `downloadAttachmentFile` 供预产页、样本接收页、卡片组件父级共用。 ### 5.3 `PreTransitProtocolCardList.vue` **紧凑卡片**(`protocol-card-compact__body` 底部,展开按钮之上): ```html ``` **展开卡片**(「基础信息」`protocol-card__kv-grid` 末尾,占满一行 `protocol-card__kv--stack`): - 同上组件;`files` 较多时可横向换行,不限高度截断 **卡片高度**:紧凑卡 `--compact-card-height` 增加约 **52px**(标签 10px + 缩略图 56px + 间距);`has-special-sample` 变体同步上调。 **事件**:组件 `emit` 上抛 `attachment-preview` / `attachment-download`,由 `pre-transit-sample/index.vue` 接入 composable。 ### 5.4 `SampleReceptionCardList.vue` 在 `sample-reception-card__meta` 末尾增加 `meta-item`(`grid-column: 1 / -1` 或 `minmax(160px, 1fr)` 以保证缩略图行宽度): ```html
{{ attachmentMeta(row).label }}
``` `label` 放在 `dt`,组件内 `label` 传空避免重复。 待接收 / 已接收 **同一展示逻辑**。 ### 5.5 类型(可选) `web-admin/src/api/pre-transit-sample.ts` 可为 `PreTransitProtocolRow` 补充附件字段注释;样本接收列表仍为 `Record`,不强制改类型。 --- ## 6. 测试要点 1. 细胞存储协议有/无孕妇健康调查表:预产紧凑卡、展开卡、样本接收待/已接收均正确 2. 科研捐赠有/无传染病检测报告:同上 3. 多附件横向换行;图片预览、PDF 下载 4. 预产「样本未录入」协议(仅有附件、无样本)仍展示附件 5. 列表分页切换后附件仍对应正确协议 6. 租户隔离:不串附件 --- ## 7. 改动文件清单 | 层 | 文件 | |----|------| | DB | 无(字段已存在) | | Backend | `pre_transit_sample.py`、`sample_reception.py` | | Frontend 新建 | `protocol-attachment-mini-readonly/index.vue`、`utils/protocol-source-attachment.ts`、`composables/useAttachmentActions.ts` | | Frontend 修改 | `PreTransitProtocolCardList.vue`、`SampleReceptionCardList.vue`、`pre-transit-sample/index.vue`、`sample-reception/index.vue`(或 `SampleReceptionCardList` 内直接 composable) | --- ## 8. 不在本期 - 样本接收 checklist 勾选 - 知情同意书在卡片展示 - 生产/质检工单卡片 - 将 `ProtocolAttachmentReadonly` 改为 size prop(本期独立迷你组件)