Files
zkxw/docs/superpowers/specs/2026-07-13-sample-card-protocol-attachment-design.md
T
2026-07-14 13:55:11 +08:00

220 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 样本卡片 · 协议级附件迷你缩略图展示
> 日期: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<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`**
```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` 底部,展开按钮之上):
```html
<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)` 以保证缩略图行宽度):
```html
<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(本期独立迷你组件)