6.8 KiB
6.8 KiB
质检工单批量导出任务附件 — 设计文档
日期:2026-07-01
状态:待实现
范围:web-admin【质检工单】【我的质检任务】
已确认:方案 A — 纯前端 JSZip 打包下载
已确认:导出过程需可见进度反馈(任务较慢)
1. 背景与目标
1.1 业务需求
用户在【质检工单】或【我的质检任务】中勾选一条或多条质检工单,将该工单下所有检测批次、所有质检任务的附件打包为 ZIP 下载。
目录结构:
{工单}/
{批次}/
{任务}/
附件文件…
1.2 目标
- 工单卡片支持多选(checkbox)。
- 工具栏提供「导出附件」按钮(
btn-ghost+ Download 图标)。 - 浏览器端聚合附件、按层级建目录、生成 ZIP 并触发下载。
- 导出过程展示进度弹窗(阶段 + 百分比 + 当前文件名),避免用户误以为卡死。
1.3 非目标(一期不做)
- 后端流式 ZIP / 异步任务中心
- 跨分页保留勾选
- 工单级其他附件(诊断附件、样本接收附件等)
- 【我的质检任务】仅导出「本人负责任务」的子集模式(与 manage 一致,导出工单下全部任务附件)
2. 技术方案
2.1 方案选定:纯前端 JSZip
| 步骤 | 说明 |
|---|---|
| 1 | 收集已选 workOrderId[] |
| 2 | POST /web/admin/qc-work-orders/inspection-batches/batch 获取批次及内嵌 tasks[].attachmentFiles |
| 3 | 扁平化附件清单,计算 ZIP 内路径 |
| 4 | 逐个 fetchAttachmentBlob(预签名 URL)写入 JSZip |
| 5 | zip.generateAsync({ type: 'blob' }, onUpdate) 生成压缩包 |
| 6 | FileSaver.saveAs 下载 |
依赖:jszip、file-saver(已存在于 package.json)。
2.2 附件数据来源
- 表字段:
biz_qc_task.third_party_attachments(逗号分隔 URL) - API 字段:
attachmentFiles(含fileUrl、fileName) - 实验室自检与第三方检测共用该字段
2.3 目录命名
| 层级 | 规则 |
|---|---|
| 工单 | {workOrderNo};若重名追加 _样本{sampleCode} 等短标识 |
| 批次 | {batchNo}_{batchName};无 batchNo 时用 批次{orderNum} |
| 任务 | {taskNo}_{itemName} |
| 文件 | 优先 fileName;同目录重名用 (2) 后缀 |
无附件的任务/批次不创建空目录。所选工单均无附件时提示「所选工单暂无附件」。
2.4 安全与权限
- 批次批量接口已受
QC_TASK_READONLY_MENU_CODES与租户隔离约束。 - 两菜单共用实现;导出范围为工单下全部任务附件(非仅当前用户任务)。
3. 进度弹窗 UX(核心补充)
环境检测页的「批量下载附件」仅按钮 loading,无细粒度进度。本功能附件量大、耗时长,必须使用独立进度弹窗。
3.1 组件
新建:web-admin/src/components/business/attachment-zip-progress-dialog/index.vue
el-dialog,append-to-body,modal-class="protocol-dialog-mask"(无背景模糊)close-on-click-modal="false",show-close="false"(进行中不可点遮罩关闭)- 宽度约
480px,单栏居中
3.2 三阶段进度
整体进度条 0–100%,按阶段加权:
| 阶段 | 权重 | 说明 |
|---|---|---|
preparing |
0–5% | 请求批次/任务元数据 |
downloading |
5–85% | 逐个拉取附件 blob 并 zip.file() |
zipping |
85–100% | generateAsync 压缩(使用 JSZip metadata.percent) |
展示文案示例:
- 准备中:正在加载工单附件清单…
- 下载中:正在下载附件(12/48)—
报告.pdf - 压缩中:正在生成压缩包…
副文案:当前工单 ZJGD… / 批次 首批检测(可选,简短一行 text-muted)。
3.3 进度条
el-progress:stroke-width="8",:percentage="overallPercent",:status完成时success- 数字百分比与「已完成 N / 共 M 个文件」并列
3.4 完成与失败
| 结果 | 行为 |
|---|---|
| 全部成功 | 进度 100%,状态 success,1s 后自动关闭弹窗;ElMessage.success('已打包下载 N 个附件') |
| 部分失败 | 仍生成 ZIP(跳过失败项);关闭后 ElMessage.warning('已下载 N 个,M 个失败') |
| 全部失败 | 不下载;ElMessage.error('附件下载失败,请稍后重试') |
| 无附件 | 不打开进度弹窗;ElMessage.warning('所选工单暂无附件') |
3.5 取消
- 底部提供「取消」按钮(
Close图标 + 文案) - 使用
AbortController中断进行中的fetch;设置cancelled标志跳过后续文件 - 取消后关闭弹窗,
ElMessage.info('已取消导出') - 已写入 ZIP 的 blob 丢弃,不触发
saveAs
3.6 并发与性能
- 附件下载串行(与环境检测一致),避免浏览器连接数耗尽;进度更准确
- 可选:限制单次最多 50 个工单 或 500 个文件,超出时
ElMessageBox.confirm二次确认
4. 页面改动
4.1 勾选
在 QcWorkOrderSingleCard 卡片头部左侧增加 el-checkbox:
QcWorkOrderProtocolGroup/QcWorkOrderSampleGroup内每张工单卡片各自可勾选- 状态由
QcWorkOrderTabPanel持有:selectedWorkOrderIds: Set<string> - 翻页或重新搜索时清空选择
4.2 工具栏
QcWorkOrderTabPanel.vue 在卡片列表上方增加(manage / mine 均显示):
已选 N 项 | [导出附件](btn-ghost,N=0 时 disabled)
4.3 逻辑抽离
| 文件 | 职责 |
|---|---|
@/utils/attachment-zip.ts |
从 env-detect-record 抽出 fetchAttachmentBlob、ensureUniqueZipEntryName、sanitizeZipPathSegment |
@/utils/qc-work-order-attachment-export.ts |
构建导出清单、ZIP 路径、runQcWorkOrderAttachmentExport() |
attachment-zip-progress-dialog |
进度 UI |
useQcWorkOrderAttachmentExport.ts |
组合 API + 进度回调 + 弹窗显隐 |
5. 接口
一期不新增后端接口,复用:
POST /web/admin/qc-work-orders/inspection-batches/batchPOST /web/admin/attachment/download-url
6. 测试要点
| # | 场景 | 预期 |
|---|---|---|
| 1 | 勾选 1 个工单,含多批次多任务附件 | ZIP 目录三层正确,文件可打开 |
| 2 | 勾选多个工单 | 根目录下多个工单文件夹 |
| 3 | 工单无附件 | 提示暂无附件,不弹进度窗 |
| 4 | 导出中取消 | 中断下载,无文件落盘 |
| 5 | 单个文件 404 | 其余正常打包,warning 提示失败数 |
| 6 | 【我的质检任务】与【质检工单】 | 行为一致 |
| 7 | 协议组 / 样本组 | 各工单独立勾选、导出 |
7. 后续演进
若生产反馈单次导出经常超过 500 文件或超时,再评估方案 B(后端 StreamingResponse ZIP)并保留相同目录规范与进度语义(改为轮询任务状态)。