2026-07-13-sample-card-protocol-attachment-design.md 7.6 KB

样本卡片 · 协议级附件迷你缩略图展示

日期: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 之后),补充:

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<Record<string, unknown>> 附件列表
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

export function getSourceProtocolAttachment(row: Record<string, unknown>): {
  label: string
  files: Array<Record<string, unknown>>
}

按 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 底部,展开按钮之上):

<ProtocolAttachmentMiniReadonly
  :label="attachmentMeta(row).label"
  :files="attachmentMeta(row).files"
  @preview="emit('attachment-preview', $event)"
  @download="emit('attachment-download', $event)"
/>

展开卡片(「基础信息」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) 以保证缩略图行宽度):

<div class="sample-reception-card__meta-item sample-reception-card__meta-item--attachment">
  <dt>{{ attachmentMeta(row).label }}</dt>
  <dd>
    <ProtocolAttachmentMiniReadonly
      :label="''"
      :files="attachmentMeta(row).files"
      ...
    />
  </dd>
</div>

label 放在 dt,组件内 label 传空避免重复。

待接收 / 已接收 同一展示逻辑。

5.5 类型(可选)

web-admin/src/api/pre-transit-sample.ts 可为 PreTransitProtocolRow 补充附件字段注释;样本接收列表仍为 Record<string, unknown>,不强制改类型。


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(本期独立迷你组件)