Przeglądaj źródła

docs: add storage-cert QC info design and HTML demo.

Capture the confirmed sample-level JSON payload approach, field maps from the Excel sheets, and UI entry points for QC fill and market view.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 tygodni temu
rodzic
commit
55ed0d8929

+ 855 - 0
docs/demos/2026-10-09-storage-cert-qc-info-demo.html

@@ -0,0 +1,855 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1" />
+  <title>储存证质检信息 · 填写 / 查看 Demo</title>
+  <link rel="preconnect" href="https://fonts.googleapis.com" />
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
+  <style>
+    :root {
+      --primary: #10b981;
+      --primary-dark: #059669;
+      --primary-bg: #ecfdf5;
+      --bg-page: #f8fafc;
+      --panel: #ffffff;
+      --text: #0f172a;
+      --text-secondary: #334155;
+      --text-muted: #64748b;
+      --border: #e2e8f0;
+      --border-mint: #d9e8e2;
+      --radius: 10px;
+      --radius-sm: 8px;
+      --shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
+      --warning: #d97706;
+      --warning-bg: #fffbeb;
+      --danger: #dc2626;
+      --info: #64748b;
+    }
+    * { box-sizing: border-box; }
+    body {
+      margin: 0;
+      font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+      font-size: 14px;
+      line-height: 1.5;
+      color: var(--text);
+      background: var(--bg-page);
+    }
+    .page {
+      max-width: 1180px;
+      margin: 0 auto;
+      padding: 24px 20px 80px;
+    }
+    .hero { margin-bottom: 20px; max-width: 920px; }
+    .hero h1 {
+      margin: 0 0 8px;
+      font-size: 22px;
+      font-weight: 700;
+      letter-spacing: -0.02em;
+    }
+    .hero h1::before {
+      content: "";
+      display: inline-block;
+      width: 4px;
+      height: 1.1em;
+      margin-right: 10px;
+      vertical-align: -0.15em;
+      border-radius: 2px;
+      background: var(--primary);
+    }
+    .hero p { margin: 0 0 8px; color: var(--text-muted); }
+    .context-bar { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
+    .pill {
+      display: inline-flex; align-items: center; gap: 6px;
+      padding: 4px 10px; border-radius: 999px; font-size: 12px;
+      background: var(--primary-bg); color: var(--primary-dark);
+      border: 1px solid #a7f3d0;
+    }
+    .pill.muted {
+      background: #f1f5f9; color: var(--text-muted); border-color: var(--border);
+    }
+
+    /* Scene tabs */
+    .scene-tabs {
+      display: flex; flex-wrap: wrap; gap: 8px;
+      margin-bottom: 16px; padding: 4px;
+      background: #fff; border: 1px solid var(--border); border-radius: var(--radius);
+    }
+    .scene-tab {
+      border: 1px solid transparent; background: transparent;
+      padding: 8px 14px; border-radius: 8px; cursor: pointer;
+      font-size: 13px; font-weight: 500; color: var(--text-secondary);
+    }
+    .scene-tab:hover { background: #f8fafc; }
+    .scene-tab.active {
+      background: var(--primary-bg); color: var(--primary-dark);
+      border-color: #a7f3d0;
+    }
+    .scene { display: none; }
+    .scene.active { display: block; }
+
+    .section {
+      background: var(--panel);
+      border: 1px solid var(--border);
+      border-radius: var(--radius);
+      padding: 18px 20px;
+      margin-bottom: 16px;
+      box-shadow: var(--shadow);
+    }
+    .section-head {
+      display: flex; align-items: flex-start; justify-content: space-between;
+      gap: 12px; margin-bottom: 14px;
+    }
+    .section-head h2 {
+      margin: 0; font-size: 16px; font-weight: 600;
+    }
+    .section-head .hint { margin: 4px 0 0; font-size: 12px; color: var(--text-muted); }
+
+    .note {
+      padding: 10px 12px; border-radius: 8px;
+      background: var(--warning-bg); border: 1px solid #fde68a;
+      color: #92400e; font-size: 13px; margin-bottom: 14px;
+    }
+    .note.ok {
+      background: var(--primary-bg); border-color: #a7f3d0; color: var(--primary-dark);
+    }
+
+    /* QC card mock */
+    .protocol-bar {
+      display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center;
+      padding: 10px 12px; background: #f8fafc; border: 1px solid var(--border);
+      border-radius: 8px; margin-bottom: 12px; font-size: 13px;
+    }
+    .protocol-bar strong { color: var(--text); }
+    .sample-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
+    .sample-pill {
+      padding: 6px 12px; border-radius: 999px; font-size: 12px;
+      border: 1px solid var(--border); background: #fff; color: var(--text-secondary);
+      cursor: pointer;
+    }
+    .sample-pill.active {
+      border-color: var(--primary); background: var(--primary-bg); color: var(--primary-dark);
+      font-weight: 600;
+    }
+    .wo-card {
+      border: 1px solid var(--border-mint);
+      border-radius: 10px;
+      padding: 14px 16px;
+      background: #fff;
+    }
+    .wo-card-top {
+      display: flex; flex-wrap: wrap; justify-content: space-between;
+      gap: 10px; margin-bottom: 10px;
+    }
+    .wo-meta { font-size: 13px; color: var(--text-secondary); }
+    .wo-meta .code { font-weight: 600; color: var(--text); }
+    .tag {
+      display: inline-flex; align-items: center;
+      padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 500;
+    }
+    .tag.success { background: var(--primary-bg); color: var(--primary-dark); }
+    .tag.warning { background: var(--warning-bg); color: var(--warning); }
+    .tag.info { background: #f1f5f9; color: var(--info); }
+    .wo-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
+    .btn {
+      display: inline-flex; align-items: center; gap: 6px;
+      height: 32px; padding: 0 12px; border-radius: 8px;
+      border: 1px solid var(--border); background: #fff;
+      font-size: 13px; color: var(--text-secondary); cursor: pointer;
+    }
+    .btn:hover { border-color: #94a3b8; }
+    .btn.primary {
+      background: var(--primary); border-color: var(--primary);
+      color: #fff; font-weight: 500;
+    }
+    .btn.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); }
+    .btn.accent {
+      background: var(--primary-bg); border-color: #a7f3d0;
+      color: var(--primary-dark); font-weight: 500;
+    }
+    .btn.ghost { background: transparent; }
+    .btn.sm { height: 28px; padding: 0 10px; font-size: 12px; }
+    .btn-icon {
+      width: 14px; height: 14px; display: inline-block;
+      border: 1.5px solid currentColor; border-radius: 3px; opacity: 0.85;
+    }
+
+    /* Sample info table mock */
+    .toolbar {
+      display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
+      margin-bottom: 12px;
+    }
+    .toolbar .selected { font-size: 13px; color: var(--text-muted); margin-right: 4px; }
+    .table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 8px; }
+    table.list {
+      width: 100%; border-collapse: collapse; min-width: 900px;
+    }
+    table.list th, table.list td {
+      padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--border);
+      font-size: 13px; white-space: nowrap;
+    }
+    table.list th {
+      background: #f8fafc; color: var(--text-muted); font-weight: 600; font-size: 12px;
+    }
+    table.list tr:nth-child(even) td { background: #fafafa; }
+    table.list tr:hover td { background: #f1f5f9; }
+    table.list tr.highlight td { background: #ecfdf5; }
+    .ops { display: flex; gap: 6px; flex-wrap: wrap; }
+
+    /* Dialog */
+    .dialog-mask {
+      position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
+      display: none; align-items: flex-start; justify-content: center;
+      padding: 40px 16px; z-index: 50; overflow-y: auto;
+      backdrop-filter: none;
+    }
+    .dialog-mask.open { display: flex; }
+    .dialog {
+      width: min(920px, 100%);
+      background: #fff; border-radius: 12px;
+      box-shadow: 0 20px 40px rgba(15, 23, 42, 0.18);
+      display: flex; flex-direction: column;
+      max-height: calc(100vh - 80px);
+    }
+    .dialog.wide { width: min(1020px, 100%); }
+    .dialog-header {
+      display: flex; align-items: center; justify-content: space-between;
+      padding: 16px 20px; border-bottom: 1px solid var(--border);
+      flex-shrink: 0;
+    }
+    .dialog-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
+    .dialog-header .sub {
+      margin: 4px 0 0; font-size: 12px; color: var(--text-muted); font-weight: 400;
+    }
+    .dialog-close {
+      width: 32px; height: 32px; border: none; background: #f1f5f9;
+      border-radius: 8px; cursor: pointer; font-size: 18px; color: var(--text-muted);
+    }
+    .dialog-body {
+      padding: 16px 20px; overflow-y: auto; flex: 1;
+    }
+    .dialog-footer {
+      display: flex; justify-content: flex-end; gap: 8px;
+      padding: 12px 20px; border-top: 1px solid var(--border);
+      flex-shrink: 0;
+    }
+
+    .ctx-row {
+      display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
+      margin-bottom: 16px; padding: 12px; background: #f8fafc;
+      border: 1px solid var(--border); border-radius: 8px;
+    }
+    @media (max-width: 800px) {
+      .ctx-row { grid-template-columns: 1fr 1fr; }
+    }
+    .ctx-item label {
+      display: block; font-size: 11px; color: var(--text-muted); margin-bottom: 2px;
+    }
+    .ctx-item .val { font-size: 13px; font-weight: 500; color: var(--text); }
+
+    .field-group {
+      border: 1px solid var(--border);
+      border-radius: 10px;
+      margin-bottom: 14px;
+      overflow: hidden;
+    }
+    .field-group-title {
+      padding: 10px 14px;
+      background: var(--primary-bg);
+      color: var(--primary-dark);
+      font-size: 13px; font-weight: 600;
+      border-bottom: 1px solid #a7f3d0;
+    }
+    .field-group-title.alt { background: #f1f5f9; color: var(--text-secondary); border-bottom-color: var(--border); }
+    .field-group-title.meta { background: #fffbeb; color: #92400e; border-bottom-color: #fde68a; }
+    .form-grid {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 12px 14px;
+      padding: 14px;
+    }
+    @media (max-width: 900px) {
+      .form-grid { grid-template-columns: repeat(2, 1fr); }
+    }
+    @media (max-width: 560px) {
+      .form-grid { grid-template-columns: 1fr; }
+    }
+    .form-item { display: flex; flex-direction: column; gap: 4px; }
+    .form-item.span-2 { grid-column: span 2; }
+    .form-item.span-3 { grid-column: 1 / -1; }
+    .form-item label {
+      font-size: 11px; color: var(--text-muted); font-weight: 500;
+    }
+    .form-item input, .form-item select, .form-item textarea {
+      height: 36px; padding: 0 10px;
+      border: 1px solid var(--border); border-radius: 8px;
+      font-size: 13px; color: var(--text); background: #fff;
+      font-family: inherit;
+    }
+    .form-item textarea {
+      height: auto; min-height: 72px; padding: 8px 10px; resize: vertical;
+    }
+    .form-item input:disabled, .form-item textarea:disabled, .form-item select:disabled {
+      background: #f8fafc; color: var(--text-secondary);
+    }
+    .form-item.readonly .val-box {
+      min-height: 36px; padding: 8px 10px;
+      border: 1px solid var(--border); border-radius: 8px;
+      background: #f8fafc; font-size: 13px; color: var(--text-secondary);
+      white-space: pre-wrap;
+    }
+
+    .type-switch {
+      display: inline-flex; gap: 6px; margin-bottom: 12px;
+    }
+    .field-count {
+      font-size: 12px; color: var(--text-muted);
+    }
+
+    .flow {
+      display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
+      font-size: 13px; color: var(--text-secondary);
+    }
+    .flow .step {
+      padding: 6px 10px; background: #fff; border: 1px solid var(--border);
+      border-radius: 8px;
+    }
+    .flow .arrow { color: var(--text-muted); }
+
+    .legend {
+      display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
+    }
+    @media (max-width: 700px) {
+      .legend { grid-template-columns: 1fr; }
+    }
+    .legend-card {
+      border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
+      background: #fff;
+    }
+    .legend-card h4 { margin: 0 0 8px; font-size: 14px; }
+    .legend-card ul {
+      margin: 0; padding-left: 18px; color: var(--text-secondary); font-size: 12px;
+    }
+    .legend-card li { margin-bottom: 4px; }
+  </style>
+</head>
+<body>
+  <div class="page">
+    <div class="hero">
+      <h1>储存证质检信息 · Demo</h1>
+      <p>
+        质检在「细胞存储质检」工单卡片上<strong>填写</strong>储存证质检信息(字段对齐 Excel 两个 sheet);
+        市场在「样本信息管理 · 细胞存储」上<strong>查看</strong>同一份数据,用于誊写到储存证。
+      </p>
+      <div class="context-bar">
+        <span class="pill">Excel:围产期造血干细胞</span>
+        <span class="pill">Excel:脐带间充质干细胞</span>
+        <span class="pill muted">质检工单 / 我的质检任务</span>
+        <span class="pill muted">样本信息管理</span>
+      </div>
+    </div>
+
+    <div class="section">
+      <div class="section-head">
+        <div>
+          <h2>业务链路(示意)</h2>
+          <p class="hint">当前 Demo 不改业务逻辑,只对齐交互与字段分组</p>
+        </div>
+      </div>
+      <div class="flow">
+        <span class="step">质检完成检测</span>
+        <span class="arrow">→</span>
+        <span class="step" style="border-color:#a7f3d0;background:var(--primary-bg);color:var(--primary-dark);font-weight:600;">填写储存证质检信息</span>
+        <span class="arrow">→</span>
+        <span class="step">市场「样本信息」查看</span>
+        <span class="arrow">→</span>
+        <span class="step">誊写 / 办理储存证</span>
+      </div>
+    </div>
+
+    <div class="scene-tabs" role="tablist">
+      <button type="button" class="scene-tab active" data-scene="overview">① 字段对照</button>
+      <button type="button" class="scene-tab" data-scene="qc-card">② 质检卡片入口</button>
+      <button type="button" class="scene-tab" data-scene="fill-hsc">③ 填写 · 围产期造血</button>
+      <button type="button" class="scene-tab" data-scene="fill-msc">④ 填写 · 脐带间充质</button>
+      <button type="button" class="scene-tab" data-scene="sample-info">⑤ 样本信息查看入口</button>
+    </div>
+
+    <!-- ① overview -->
+    <div class="scene active" id="scene-overview">
+      <div class="note ok">
+        两个 sheet 共用「身份键 + 母亲血检」骨架,检测报告区字段按样本类型分叉;右侧收尾字段也略有差异。
+      </div>
+      <div class="legend">
+        <div class="legend-card">
+          <h4>围产期造血干细胞(约 34 列)</h4>
+          <ul>
+            <li><strong>身份</strong>:序号、实验室流转编码、协议编码、母亲姓名</li>
+            <li><strong>采集运输</strong>:采集时间、运输温度</li>
+            <li><strong>造血干细胞检测报告</strong>:接收/处理/冻存时间、重量、冻存体积、TNC、CD34+、CD34+CD45dim、活性、细菌、HBsAg/HCV/TP/HIV、CMV-IgM</li>
+            <li><strong>母亲血液检测报告</strong>:HBsAg、HBV DNA、抗HCV、HCV RNA、抗TP、CMV IgM、抗HIV、HIV-1RNA</li>
+            <li><strong>收尾</strong>:HLA 分型、储存日期、储存年限、检测结果评论、报告发出日期</li>
+          </ul>
+        </div>
+        <div class="legend-card">
+          <h4>脐带间充质干细胞(约 34 列)</h4>
+          <ul>
+            <li><strong>额外</strong>:是否邮寄</li>
+            <li><strong>身份</strong>:序号、实验室流转编码、协议编码、母亲姓名</li>
+            <li><strong>采集运输</strong>:脐带采集时间、运输温度</li>
+            <li><strong>脐带间充质检测报告</strong>:接收/处理/冻存时间、储存总量(管)、可提供细胞类型/数量/存活率、内毒素/细菌/真菌/支原体、形态、免疫表型</li>
+            <li><strong>母亲血液检测报告</strong>:与造血 sheet 同一组 8 项</li>
+            <li><strong>收尾</strong>:检测结果评判、报告发出日期、储存日期(P2)、冷冻方式、储存温度、储存年限</li>
+          </ul>
+        </div>
+      </div>
+      <div class="section" style="margin-top:16px;">
+        <div class="section-head">
+          <div>
+            <h2>Demo 建议排除出「质检填写」的列</h2>
+            <p class="hint">这些更像列表管理列 / 市场侧信息,可在澄清时确认</p>
+          </div>
+        </div>
+        <p style="margin:0;font-size:13px;color:var(--text-secondary);">
+          建议默认<strong>只读预填、不让质检手填</strong>:序号、协议编码、母亲姓名(从样本/协议带出)。
+          「是否邮寄」偏市场物流,是否纳入质检表待确认。
+          「实验室流转编码」是否等于系统样本编码 / 入库编码,待确认。
+        </p>
+      </div>
+    </div>
+
+    <!-- ② qc card -->
+    <div class="scene" id="scene-qc-card">
+      <div class="section">
+        <div class="section-head">
+          <div>
+            <h2>质检工单 / 我的质检任务 · 细胞存储质检</h2>
+            <p class="hint">在工单卡片操作区新增「填写储存证质检信息」(示意高亮)</p>
+          </div>
+        </div>
+        <div class="note">入口放在<strong>工单卡片</strong>(非任务子卡「填写结果」),因为储存证信息是样本级整表,不是单项检测任务结果。</div>
+
+        <div class="protocol-bar">
+          <span>协议:<strong>ZKZX34000852</strong></span>
+          <span>客户:高居巧</span>
+          <span>来源:细胞存储</span>
+        </div>
+        <div class="sample-pills">
+          <span class="sample-pill active">B0000002 · 围产期造血干细胞</span>
+          <span class="sample-pill">B0000002-MSC · 脐带间充质(示意)</span>
+        </div>
+        <div class="wo-card">
+          <div class="wo-card-top">
+            <div class="wo-meta">
+              <div class="code">入库质检工单 WO-2026-1008-012</div>
+              <div style="margin-top:4px;">
+                <span class="tag success">已完成</span>
+                <span class="tag info" style="margin-left:6px;">结论:通过</span>
+              </div>
+            </div>
+            <div class="field-count">样本类型 → 决定弹窗字段模板</div>
+          </div>
+          <div class="wo-actions">
+            <button type="button" class="btn">查看</button>
+            <button type="button" class="btn">特殊样本</button>
+            <button type="button" class="btn accent" onclick="openFill('hsc')">
+              <span class="btn-icon"></span> 填写储存证质检信息
+            </button>
+            <button type="button" class="btn ghost">更多</button>
+          </div>
+        </div>
+
+        <div style="height:14px;"></div>
+        <div class="wo-card" style="opacity:0.92;">
+          <div class="wo-card-top">
+            <div class="wo-meta">
+              <div class="code">入库质检工单 WO-2026-1009-003</div>
+              <div style="margin-top:4px;">
+                <span class="tag warning">进行中</span>
+              </div>
+            </div>
+          </div>
+          <div class="wo-actions">
+            <button type="button" class="btn primary">配置检测</button>
+            <button type="button" class="btn accent" onclick="openFill('msc')">
+              <span class="btn-icon"></span> 填写储存证质检信息
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- ③ fill HSC summary (opens dialog) -->
+    <div class="scene" id="scene-fill-hsc">
+      <div class="section">
+        <div class="section-head">
+          <div>
+            <h2>填写弹窗 · 围产期造血干细胞</h2>
+            <p class="hint">分组 = Excel 合并表头;三列栅格;长文本(HLA / 评论)跨列</p>
+          </div>
+          <button type="button" class="btn primary" onclick="openFill('hsc')">打开填写弹窗</button>
+        </div>
+        <p style="margin:0;font-size:13px;color:var(--text-muted);">
+          字段按 Excel sheet「围产期造血干细胞」展开:造血检测报告 15 项 + 母亲血检 8 项 + HLA/储存/评论/报告日。
+        </p>
+      </div>
+    </div>
+
+    <!-- ④ fill MSC -->
+    <div class="scene" id="scene-fill-msc">
+      <div class="section">
+        <div class="section-head">
+          <div>
+            <h2>填写弹窗 · 脐带间充质干细胞</h2>
+            <p class="hint">与造血模板共用母亲血检区块;检测报告与收尾字段不同</p>
+          </div>
+          <button type="button" class="btn primary" onclick="openFill('msc')">打开填写弹窗</button>
+        </div>
+        <p style="margin:0;font-size:13px;color:var(--text-muted);">
+          含「是否邮寄」、储存总量(管)、可提供细胞*、内毒素/真菌/支原体、冷冻方式、储存温度等。
+        </p>
+      </div>
+    </div>
+
+    <!-- ⑤ sample info -->
+    <div class="scene" id="scene-sample-info">
+      <div class="section">
+        <div class="section-head">
+          <div>
+            <h2>样本信息管理 · 细胞存储 Tab</h2>
+            <p class="hint">行操作增加「查看储存证的质检信息」;无数据时按钮可禁用或提示未填写</p>
+          </div>
+        </div>
+        <div class="toolbar">
+          <span class="selected">已选 0 项</span>
+          <button type="button" class="btn primary">登记入库</button>
+          <button type="button" class="btn">登记储存证</button>
+          <button type="button" class="btn">登记保险</button>
+        </div>
+        <div class="table-wrap">
+          <table class="list">
+            <thead>
+              <tr>
+                <th>样本编码</th>
+                <th>协议编码</th>
+                <th>母亲姓名</th>
+                <th>样本类型</th>
+                <th>质检</th>
+                <th>储存证</th>
+                <th>操作</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr class="highlight">
+                <td>B0000002</td>
+                <td>ZKZX34000852</td>
+                <td>高居巧</td>
+                <td>围产期造血干细胞</td>
+                <td><span class="tag success">已完成</span></td>
+                <td><span class="tag warning">未办理</span></td>
+                <td>
+                  <div class="ops">
+                    <button type="button" class="btn sm">查看</button>
+                    <button type="button" class="btn sm accent" onclick="openView('hsc')">查看储存证的质检信息</button>
+                  </div>
+                </td>
+              </tr>
+              <tr>
+                <td>BC0000003</td>
+                <td>ZKQD34000831</td>
+                <td>牛晨晨</td>
+                <td>脐带间充质干细胞</td>
+                <td><span class="tag success">已完成</span></td>
+                <td><span class="tag success">已办理</span></td>
+                <td>
+                  <div class="ops">
+                    <button type="button" class="btn sm">查看</button>
+                    <button type="button" class="btn sm accent" onclick="openView('msc')">查看储存证的质检信息</button>
+                  </div>
+                </td>
+              </tr>
+              <tr>
+                <td>B0000099</td>
+                <td>ZKZX34000999</td>
+                <td>示例未填</td>
+                <td>围产期造血干细胞</td>
+                <td><span class="tag warning">进行中</span></td>
+                <td><span class="tag info">—</span></td>
+                <td>
+                  <div class="ops">
+                    <button type="button" class="btn sm">查看</button>
+                    <button type="button" class="btn sm" disabled style="opacity:0.45;cursor:not-allowed;">查看储存证的质检信息</button>
+                  </div>
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Fill dialog -->
+  <div class="dialog-mask" id="fillMask" onclick="maskClose(event,'fillMask')">
+    <div class="dialog wide" role="dialog" aria-modal="true">
+      <div class="dialog-header">
+        <div>
+          <h3 id="fillTitle">填写储存证质检信息</h3>
+          <div class="sub" id="fillSub">按样本类型切换字段模板</div>
+        </div>
+        <button type="button" class="dialog-close" onclick="closeMask('fillMask')" aria-label="关闭">×</button>
+      </div>
+      <div class="dialog-body">
+        <div class="ctx-row">
+          <div class="ctx-item"><label>实验室流转编码</label><div class="val" id="ctxLab">B0000002</div></div>
+          <div class="ctx-item"><label>协议编码</label><div class="val" id="ctxProto">ZKZX34000852</div></div>
+          <div class="ctx-item"><label>母亲姓名</label><div class="val" id="ctxMother">高居巧</div></div>
+          <div class="ctx-item"><label>样本类型</label><div class="val" id="ctxType">围产期造血干细胞</div></div>
+        </div>
+
+        <div id="formHsc">
+          <div class="field-group">
+            <div class="field-group-title alt">采集与运输</div>
+            <div class="form-grid">
+              <div class="form-item"><label>造血干细胞采集时间</label><input value="2025年9月7日15时36分" /></div>
+              <div class="form-item"><label>运输温度 /℃</label><input value="17" /></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title">造血干细胞检测报告</div>
+            <div class="form-grid">
+              <div class="form-item"><label>造血干细胞接收时间</label><input value="2025.9.8 9:00" /></div>
+              <div class="form-item"><label>处理时间</label><input value="2025.9.8 9:30" /></div>
+              <div class="form-item"><label>冻存时间</label><input value="2025.9.8 11:57" /></div>
+              <div class="form-item"><label>造血干细胞重量/g</label><input value="56" /></div>
+              <div class="form-item"><label>造血干细胞冻存体积/mL</label><input value="21.8" /></div>
+              <div class="form-item"><label>有核细胞数 TNC / *10E8</label><input value="2.28" /></div>
+              <div class="form-item"><label>CD34+ 细胞计数 / *10E6</label><input value="16.3" /></div>
+              <div class="form-item"><label>CD34+ CD45dim / *10E5</label><input value="1.27" /></div>
+              <div class="form-item"><label>细胞活性/%</label><input value="90.44" /></div>
+              <div class="form-item"><label>细菌检测</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>乙型肝炎病毒表面抗原(HBsAg)</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>丙型肝炎病毒抗体(抗HCV)</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>梅毒螺旋体特异性抗体(抗TP)</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>人类免疫缺陷病毒抗体(抗HIV-1/2)</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>巨细胞病毒 IgM 抗体 (CMV-IgM)</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title">母亲血液检测报告</div>
+            <div class="form-grid">
+              <div class="form-item"><label>乙型肝炎病毒表面抗原(HBsAg)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>乙型肝炎病毒核酸(HBV DNA)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>丙型肝炎病毒抗体(抗HCV)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>丙型肝炎病毒核酸(HCV RNA)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>梅毒螺旋体特异性抗体(抗TP)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>巨细胞病毒 IgM 抗体 (CMV IgM)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>人类免疫缺陷病毒抗体(抗HIV-1/2)</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>人类免疫缺陷病毒核酸(1型) (HIV-1RNA)</label><select><option>阴</option><option>/</option></select></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title meta">储存证收尾信息</div>
+            <div class="form-grid">
+              <div class="form-item span-3"><label>HLA 分型</label><textarea>A 02:06/24:02
+B 07:02(M)/57:01(T)
+C 06:02/07:02
+DRB1 13:01/15:01
+DQB1 06:02/06:03</textarea></div>
+              <div class="form-item"><label>储存日期</label><input placeholder="YYYY-MM-DD" /></div>
+              <div class="form-item"><label>储存年限</label><input value="20" /></div>
+              <div class="form-item"><label>检测结果评论</label><input value="合格" /></div>
+              <div class="form-item"><label>报告发出日期</label><input placeholder="YYYY-MM-DD" /></div>
+            </div>
+          </div>
+        </div>
+
+        <div id="formMsc" style="display:none;">
+          <div class="field-group">
+            <div class="field-group-title alt">采集与运输 / 邮寄</div>
+            <div class="form-grid">
+              <div class="form-item"><label>是否邮寄</label><select><option>是</option><option selected>否</option></select></div>
+              <div class="form-item"><label>脐带采集时间</label><input value="2025.11.25 15:49" /></div>
+              <div class="form-item"><label>运输温度 /℃</label><input value="" placeholder="可填「常温」或数值" /></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title">脐带间充质干细胞检测报告</div>
+            <div class="form-grid">
+              <div class="form-item"><label>脐带接收时间</label><input value="2025.11.26 10:00" /></div>
+              <div class="form-item"><label>处理时间</label><input value="2025.11.26 16:00" /></div>
+              <div class="form-item"><label>冻存时间</label><input value="2025.12.21" /></div>
+              <div class="form-item"><label>储存总量(管)</label><input value="17" placeholder="Excel 中有 =0+17 公式" /></div>
+              <div class="form-item"><label>可提供细胞类型</label><input value="间充质干细胞" /></div>
+              <div class="form-item"><label>可提供细胞数量 / *10E7</label><input value="8.5" /></div>
+              <div class="form-item"><label>可提供细胞存活率 %</label><input value="97.1" /></div>
+              <div class="form-item"><label>内毒素检测</label><select><option>阴</option><option>阳</option><option>/</option></select></div>
+              <div class="form-item"><label>细菌检测</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>真菌检测</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>支原体检测</label><select><option>阴</option><option>/</option></select></div>
+              <div class="form-item"><label>可提供细胞形态</label><input value="短梭/贴壁型" /></div>
+              <div class="form-item span-2"><label>可提供细胞免疫表型特征</label><input value="符合间充质干细胞表型特性" /></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title">母亲血液检测报告</div>
+            <div class="form-grid">
+              <div class="form-item"><label>乙型肝炎病毒表面抗原(HBsAg)</label><select><option>阴</option></select></div>
+              <div class="form-item"><label>乙型肝炎病毒核酸(HBV DNA)</label><select><option>/</option><option>阴</option></select></div>
+              <div class="form-item"><label>丙型肝炎病毒抗体(抗HCV)</label><select><option>阴</option></select></div>
+              <div class="form-item"><label>丙型肝炎病毒核酸(HCV RNA)</label><select><option>/</option><option>阴</option></select></div>
+              <div class="form-item"><label>梅毒螺旋体特异性抗体(抗TP)</label><select><option>阴</option></select></div>
+              <div class="form-item"><label>巨细胞病毒 IgM 抗体 (CMV IgM)</label><select><option>阴</option></select></div>
+              <div class="form-item"><label>人类免疫缺陷病毒抗体(抗HIV-1/2)</label><select><option>阴</option></select></div>
+              <div class="form-item"><label>人类免疫缺陷病毒核酸(1型) (HIV-1RNA)</label><select><option>阴</option></select></div>
+            </div>
+          </div>
+          <div class="field-group">
+            <div class="field-group-title meta">储存证收尾信息</div>
+            <div class="form-grid">
+              <div class="form-item"><label>检测结果评判</label><input value="合格" /></div>
+              <div class="form-item"><label>报告发出日期</label><input value="2026-03-14" /></div>
+              <div class="form-item"><label>储存日期 (P2冻存日期)</label><input value="2025.12.22" /></div>
+              <div class="form-item"><label>冷冻方式</label><input value="程序降温盒降温" /></div>
+              <div class="form-item"><label>储存温度</label><input value="-196℃" /></div>
+              <div class="form-item"><label>储存年限</label><input value="20" /></div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="dialog-footer">
+        <button type="button" class="btn" onclick="closeMask('fillMask')">取消</button>
+        <button type="button" class="btn primary" onclick="alert('Demo:保存成功(未接后端)'); closeMask('fillMask');">确定</button>
+      </div>
+    </div>
+  </div>
+
+  <!-- View dialog -->
+  <div class="dialog-mask" id="viewMask" onclick="maskClose(event,'viewMask')">
+    <div class="dialog wide" role="dialog" aria-modal="true">
+      <div class="dialog-header">
+        <div>
+          <h3>查看储存证的质检信息</h3>
+          <div class="sub" id="viewSub">只读 · 字段与质检填写一致</div>
+        </div>
+        <button type="button" class="dialog-close" onclick="closeMask('viewMask')" aria-label="关闭">×</button>
+      </div>
+      <div class="dialog-body" id="viewBody">
+        <!-- filled by JS -->
+      </div>
+      <div class="dialog-footer">
+        <button type="button" class="btn primary" onclick="closeMask('viewMask')">关闭</button>
+      </div>
+    </div>
+  </div>
+
+  <script>
+    // Scene tabs
+    document.querySelectorAll('.scene-tab').forEach(function (tab) {
+      tab.addEventListener('click', function () {
+        document.querySelectorAll('.scene-tab').forEach(function (t) { t.classList.remove('active'); });
+        document.querySelectorAll('.scene').forEach(function (s) { s.classList.remove('active'); });
+        tab.classList.add('active');
+        document.getElementById('scene-' + tab.dataset.scene).classList.add('active');
+      });
+    });
+
+    function closeMask(id) {
+      document.getElementById(id).classList.remove('open');
+    }
+    function maskClose(e, id) {
+      if (e.target.id === id) closeMask(id);
+    }
+
+    function openFill(type) {
+      var isHsc = type === 'hsc';
+      document.getElementById('formHsc').style.display = isHsc ? '' : 'none';
+      document.getElementById('formMsc').style.display = isHsc ? 'none' : '';
+      document.getElementById('fillTitle').textContent = '填写储存证质检信息';
+      document.getElementById('fillSub').textContent = isHsc
+        ? '模板:围产期造血干细胞(对齐 Excel sheet)'
+        : '模板:脐带间充质干细胞(对齐 Excel sheet)';
+      document.getElementById('ctxLab').textContent = isHsc ? 'B0000002' : 'BC0000003';
+      document.getElementById('ctxProto').textContent = isHsc ? 'ZKZX34000852' : 'ZKQD34000831';
+      document.getElementById('ctxMother').textContent = isHsc ? '高居巧' : '牛晨晨';
+      document.getElementById('ctxType').textContent = isHsc ? '围产期造血干细胞' : '脐带间充质干细胞';
+      document.getElementById('fillMask').classList.add('open');
+    }
+
+    function ro(label, val, span) {
+      var cls = 'form-item readonly' + (span ? ' span-' + span : '');
+      return '<div class="' + cls + '"><label>' + label + '</label><div class="val-box">' + (val || '—') + '</div></div>';
+    }
+
+    function openView(type) {
+      var isHsc = type === 'hsc';
+      document.getElementById('viewSub').textContent = isHsc
+        ? '样本 B0000002 · 围产期造血干细胞 · 只读'
+        : '样本 BC0000003 · 脐带间充质干细胞 · 只读';
+      var html = '';
+      html += '<div class="ctx-row">' +
+        '<div class="ctx-item"><label>实验室流转编码</label><div class="val">' + (isHsc ? 'B0000002' : 'BC0000003') + '</div></div>' +
+        '<div class="ctx-item"><label>协议编码</label><div class="val">' + (isHsc ? 'ZKZX34000852' : 'ZKQD34000831') + '</div></div>' +
+        '<div class="ctx-item"><label>母亲姓名</label><div class="val">' + (isHsc ? '高居巧' : '牛晨晨') + '</div></div>' +
+        '<div class="ctx-item"><label>样本类型</label><div class="val">' + (isHsc ? '围产期造血干细胞' : '脐带间充质干细胞') + '</div></div>' +
+        '</div>';
+
+      if (isHsc) {
+        html += '<div class="field-group"><div class="field-group-title alt">采集与运输</div><div class="form-grid">' +
+          ro('造血干细胞采集时间', '2025年9月7日15时36分') +
+          ro('运输温度 /℃', '17') +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title">造血干细胞检测报告</div><div class="form-grid">' +
+          ro('造血干细胞接收时间', '2025.9.8 9:00') +
+          ro('处理时间', '2025.9.8 9:30') +
+          ro('冻存时间', '2025.9.8 11:57') +
+          ro('造血干细胞重量/g', '56') +
+          ro('造血干细胞冻存体积/mL', '21.8') +
+          ro('有核细胞数 TNC / *10E8', '2.28') +
+          ro('CD34+ 细胞计数 / *10E6', '16.3') +
+          ro('CD34+ CD45dim / *10E5', '1.27') +
+          ro('细胞活性/%', '90.44') +
+          ro('细菌检测', '阴') +
+          ro('乙型肝炎病毒表面抗原(HBsAg)', '阴') +
+          ro('丙型肝炎病毒抗体(抗HCV)', '阴') +
+          ro('梅毒螺旋体特异性抗体(抗TP)', '阴') +
+          ro('人类免疫缺陷病毒抗体(抗HIV-1/2)', '阴') +
+          ro('巨细胞病毒 IgM 抗体 (CMV-IgM)', '阴') +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title">母亲血液检测报告</div><div class="form-grid">' +
+          ro('HBsAg', '阴') + ro('HBV DNA', '阴') + ro('抗HCV', '阴') + ro('HCV RNA', '阴') +
+          ro('抗TP', '阴') + ro('CMV IgM', '阴') + ro('抗HIV-1/2', '阴') + ro('HIV-1RNA', '阴') +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title meta">储存证收尾信息</div><div class="form-grid">' +
+          ro('HLA 分型', 'A 02:06/24:02\\nB 07:02(M)/57:01(T)\\nC 06:02/07:02\\nDRB1 13:01/15:01\\nDQB1 06:02/06:03', 3) +
+          ro('储存日期', '—') + ro('储存年限', '20') + ro('检测结果评论', '合格') + ro('报告发出日期', '—') +
+          '</div></div>';
+      } else {
+        html += '<div class="field-group"><div class="field-group-title alt">采集与运输 / 邮寄</div><div class="form-grid">' +
+          ro('是否邮寄', '—') + ro('脐带采集时间', '2025.11.25 15:49') + ro('运输温度 /℃', '—') +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title">脐带间充质干细胞检测报告</div><div class="form-grid">' +
+          ro('脐带接收时间', '2025.11.26 10:00') + ro('处理时间', '2025.11.26 16:00') + ro('冻存时间', '2025.12.21') +
+          ro('储存总量(管)', '17') + ro('可提供细胞类型', '间充质干细胞') +
+          ro('可提供细胞数量 / *10E7', '8.5') + ro('可提供细胞存活率 %', '97.1') +
+          ro('内毒素检测', '阴') + ro('细菌检测', '阴') + ro('真菌检测', '阴') + ro('支原体检测', '阴') +
+          ro('可提供细胞形态', '短梭/贴壁型') +
+          ro('可提供细胞免疫表型特征', '符合间充质干细胞表型特性', 2) +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title">母亲血液检测报告</div><div class="form-grid">' +
+          ro('HBsAg', '阴') + ro('HBV DNA', '/') + ro('抗HCV', '阴') + ro('HCV RNA', '/') +
+          ro('抗TP', '阴') + ro('CMV IgM', '阴') + ro('抗HIV-1/2', '阴') + ro('HIV-1RNA', '阴') +
+          '</div></div>';
+        html += '<div class="field-group"><div class="field-group-title meta">储存证收尾信息</div><div class="form-grid">' +
+          ro('检测结果评判', '合格') + ro('报告发出日期', '2026-03-14') +
+          ro('储存日期 (P2冻存日期)', '2025.12.22') + ro('冷冻方式', '程序降温盒降温') +
+          ro('储存温度', '-196℃') + ro('储存年限', '20') +
+          '</div></div>';
+      }
+      document.getElementById('viewBody').innerHTML = html;
+      document.getElementById('viewMask').classList.add('open');
+    }
+  </script>
+</body>
+</html>

+ 284 - 0
docs/superpowers/specs/2026-10-09-storage-cert-qc-info-design.md

@@ -0,0 +1,284 @@
+# 储存证质检信息 — 实现设计(方案 A)
+
+> 日期:2026-10-09  
+> 状态:已确认(2026-10-09)  
+> 关联:质检工单 / 我的质检任务「细胞存储质检」;样本信息管理「细胞存储」;Excel《干细胞储存证数据》  
+> Demo:`docs/demos/2026-10-09-storage-cert-qc-info-demo.html`
+
+---
+
+## 1. 背景
+
+市场办理储存证时,质检数据目前来自 Excel 手工表(两个 sheet):
+
+| Sheet | 样本类型 |
+|-------|----------|
+| 围产期造血干细胞 | 围产期造血干细胞 |
+| 脐带间充质干细胞 | 脐带间充质干细胞 |
+
+流程为:质检部门填表 → 市场部门誊写到储存证。系统内已有入库质检工单与样本信息管理,但**没有**与储存证誊写对齐的结构化质检信息落库与查看入口。
+
+现有 `biz_protocol_storage_cert` 为**协议级**办理状态(办理时间、附件等),不承载上述检测明细。
+
+---
+
+## 2. 目标与非目标
+
+### 2.1 目标
+
+- 质检在「质检工单」「我的质检任务」的「细胞存储质检」**工单卡片**上,可「填写储存证质检信息」(按整份样本一张表)。
+- 字段分组与 Excel 对应 sheet 业务列一致;按样本类型切换两套模板。
+- 市场在「样本信息管理 · 细胞存储」行操作可「查看储存证的质检信息」(只读);未填时提示「质检尚未填写」。
+- 数据按**样本级**持久化,可反复编辑覆盖;允许部分填写即保存。
+
+### 2.2 非目标
+
+- 不自动生成 / 打印储存证 PDF。
+- 不改「登记储存证」办理流,不扩展 `biz_protocol_storage_cert` 明细列。
+- 不从接收单 / 质检任务结果自动回填检测数值(本期全部手填)。
+- 不做 Excel 批量导入。
+- 不为其它样本类型(如胎盘亚全能)做模板;其它类型不显示填写/查看按钮。
+- 不做必填校验、不做乐观锁。
+
+---
+
+## 3. 已确认产品决策
+
+| 决策点 | 结论 |
+|--------|------|
+| 填写入口 | 工单卡片(与查看 / 配置检测同级),非整条子任务「填写结果」 |
+| 粒度 | 整份样本一张表 |
+| 查看可用性 | 始终可点;未填弹窗提示「质检尚未填写」 |
+| 样本类型范围 | 仅围产期造血干细胞、脐带间充质干细胞 |
+| 再次编辑 | 可反复打开保存,覆盖上次 |
+| 数值来源 | 全部手填;系统只带出身份信息 |
+| 校验 | 允许部分填写即保存(草稿式) |
+| 存储方案 | 方案 A:样本级新表 + JSON `payload` |
+
+---
+
+## 4. 数据模型
+
+### 4.1 表 `biz_sample_storage_cert_qc`
+
+| 字段 | 类型(示意) | 说明 |
+|------|--------------|------|
+| `id` | VARCHAR(64) | UUID PK |
+| `sample_code` | VARCHAR(64) | 样本编码;跨表关联键之一 |
+| `collection_protocol_id` | VARCHAR(64) | 冗余协议 id,便于权限/展示;**非**唯一键 |
+| `qc_work_order_id` | VARCHAR(64) NULL | 可选,最近一次从哪张工单填写 |
+| `sample_type_key` | VARCHAR(32) | 模板键:`perinatal_hsc` / `uc_msc` |
+| `payload` | JSON / TEXT | 业务字段对象(见 §5) |
+| `filled_flag` | CHAR(1) | `0`/`1`;任意一次成功保存后为 `1` |
+| 公共字段 | | `tenant_no` … `del_flag`(对齐 database-conventions) |
+
+**唯一性**:`tenant_no + sample_code` 在 `del_flag=0` 下唯一;写入走 upsert。
+
+**关联键**:读写与列表关联一律用 `tenant_no + sample_code`(对齐细胞存储样本跨表约定),不用 `collection_protocol_id` 作唯一匹配。
+
+### 4.2 模板键映射
+
+与现网名称常量对齐(`SAMPLE_TYPE_PERINATAL_HSC` / `SAMPLE_TYPE_UC_MSC`):
+
+| 样本类型名称 | `sample_type_key` |
+|--------------|-------------------|
+| 围产期造血干细胞 | `perinatal_hsc` |
+| 脐带间充质干细胞 | `uc_msc` |
+
+服务端根据样本当前类型解析 `sample_type_key`,**不信任**客户端随意改模板。非以上类型 → 拒绝写入。
+
+### 4.3 身份信息(不进 payload)
+
+弹窗顶部只读展示(从协议 / 样本主数据带出):
+
+- 实验室流转编码:本期约定展示 **`sample_code`**(与系统样本编码一致)
+- 协议编码
+- 母亲姓名
+- 样本类型名称
+
+排除 Excel「序号」列(列表管理用,不入库)。
+
+---
+
+## 5. Payload 字段(对齐 Excel)
+
+键名用稳定英文 snake_case;界面 label 用中文(与 Excel 表头一致)。值为字符串或数字均可,空字段可省略或存 `null`/`""`。阴阳性常见值:`阴` / `阳` / `/`。
+
+### 5.1 `perinatal_hsc`(围产期造血干细胞)
+
+**采集与运输**
+
+| key | 中文 |
+|-----|------|
+| `collection_time` | 造血干细胞采集时间 |
+| `transport_temp_c` | 运输温度 /℃ |
+
+**造血干细胞检测报告**
+
+| key | 中文 |
+|-----|------|
+| `receive_time` | 造血干细胞接收时间 |
+| `process_time` | 处理时间 |
+| `cryo_time` | 冻存时间 |
+| `weight_g` | 造血干细胞重量/g |
+| `cryo_volume_ml` | 造血干细胞冻存体积/mL |
+| `tnc_1e8` | 有核细胞数 TNC / *10E8 |
+| `cd34_count_1e6` | CD34+ 细胞计数 / *10E6 |
+| `cd34_cd45dim_1e5` | CD34+ CD45dim 细胞计数 / *10E5 |
+| `viability_pct` | 细胞活性/% |
+| `bacteria_test` | 细菌检测 |
+| `hbsag` | 乙型肝炎病毒表面抗原(HBsAg) |
+| `anti_hcv` | 丙型肝炎病毒抗体(抗HCV) |
+| `anti_tp` | 梅毒螺旋体特异性抗体(抗TP) |
+| `anti_hiv` | 人类免疫缺陷病毒抗体(抗HIV-1/2) |
+| `cmv_igm` | 巨细胞病毒 IgM 抗体 (CMV-IgM) |
+
+**母亲血液检测报告**
+
+| key | 中文 |
+|-----|------|
+| `mother_hbsag` | 乙型肝炎病毒表面抗原(HBsAg) |
+| `mother_hbv_dna` | 乙型肝炎病毒核酸(HBV DNA) |
+| `mother_anti_hcv` | 丙型肝炎病毒抗体(抗HCV) |
+| `mother_hcv_rna` | 丙型肝炎病毒核酸(HCV RNA) |
+| `mother_anti_tp` | 梅毒螺旋体特异性抗体(抗TP) |
+| `mother_cmv_igm` | 巨细胞病毒 IgM 抗体 (CMV IgM) |
+| `mother_anti_hiv` | 人类免疫缺陷病毒抗体(抗HIV-1/2) |
+| `mother_hiv1_rna` | 人类免疫缺陷病毒核酸(1型) (HIV-1RNA) |
+
+**储存证收尾**
+
+| key | 中文 |
+|-----|------|
+| `hla_typing` | HLA 分型 |
+| `storage_date` | 储存日期 |
+| `storage_years` | 储存年限 |
+| `result_comment` | 检测结果评论 |
+| `report_issue_date` | 报告发出日期 |
+
+### 5.2 `uc_msc`(脐带间充质干细胞)
+
+**采集与运输 / 邮寄**
+
+| key | 中文 |
+|-----|------|
+| `mail_flag` | 是否邮寄 |
+| `collection_time` | 脐带采集时间 |
+| `transport_temp_c` | 运输温度 /℃ |
+
+**脐带间充质干细胞检测报告**
+
+| key | 中文 |
+|-----|------|
+| `receive_time` | 脐带接收时间 |
+| `process_time` | 处理时间 |
+| `cryo_time` | 冻存时间 |
+| `storage_tube_count` | 储存总量(管) |
+| `available_cell_type` | 可提供细胞类型 |
+| `available_cell_count_1e7` | 可提供细胞数量 / *10E7 |
+| `available_viability_pct` | 可提供细胞存活率 % |
+| `endotoxin_test` | 内毒素检测 |
+| `bacteria_test` | 细菌检测 |
+| `fungus_test` | 真菌检测 |
+| `mycoplasma_test` | 支原体检测 |
+| `available_cell_morphology` | 可提供细胞形态 |
+| `available_immunophenotype` | 可提供细胞免疫表型特征 |
+
+**母亲血液检测报告**:与 §5.1 母亲血检 8 个 key 相同。
+
+**储存证收尾**
+
+| key | 中文 |
+|-----|------|
+| `result_judgment` | 检测结果评判 |
+| `report_issue_date` | 报告发出日期 |
+| `storage_date_p2` | 储存日期 (P2冻存日期) |
+| `freeze_method` | 冷冻方式 |
+| `storage_temp` | 储存温度 |
+| `storage_years` | 储存年限 |
+
+---
+
+## 6. API
+
+建议前缀:`/web/admin/sample-storage-cert-qc`。
+
+| 方法 | 路径 | 说明 |
+|------|------|------|
+| GET | `/{sample_code}` | 读详情:身份只读字段 + `sampleTypeKey` + `filledFlag` + `payload` |
+| PUT | `/{sample_code}` | Upsert;body:`payload`(object),可选 `qcWorkOrderId` |
+
+### 6.1 行为
+
+- GET 无行:HTTP 200,`filledFlag=false`,`payload={}`(或 null),**不** 404。
+- PUT:校验样本存在、租户、数据权限、类型为两种之一;写 `sample_type_key`;`filled_flag=1`;覆盖 `payload`。
+- 列表接口本期不扩展整份 payload;样本信息列表点「查看」再 GET。
+
+### 6.2 权限
+
+- PUT:具备质检工单或我的质检任务相关菜单权限(与工单卡片操作对齐;不额外强制「必须是任务执行人」,除非现有卡片按钮已有更严规则则跟随)。
+- GET:样本信息管理菜单可访问;质检填写弹窗预填亦用同一 GET。
+
+---
+
+## 7. 前端
+
+### 7.1 质检入口
+
+- 页面:`qc-work-order`、`my-qc-task`,Tab「细胞存储质检」(`inbound_cell_storage`)。
+- 组件:`QcWorkOrderSingleCard` 操作区增加「填写储存证质检信息」。
+- 显示条件:当前样本类型名为围产期造血干细胞或脐带间充质干细胞。
+- 弹窗:可编辑;GET 预填 → PUT 保存;manage / mine 均显示(规则相同)。
+
+### 7.2 样本信息入口
+
+- 页面:`/#/market/cell-storage/sample-info`,「细胞存储」Tab。
+- 行操作增加「查看储存证的质检信息」;显示条件同两种类型。
+- 始终可点;`filledFlag=false` 时弹窗空态文案「质检尚未填写」(可保留身份区);已填则只读同模板字段。
+- 市场侧不可编辑。
+
+### 7.3 UI 约定
+
+- `label-position="top"`;分组对齐 Demo / Excel 合并表头;栅格最多约 3 列,窄屏降列。
+- 一套字段元数据配置驱动填写(可编)与查看(只读)。
+- 对齐 Fresh Mint / `dialog-form-layout`;遮罩无 backdrop blur。
+- Demo:`docs/demos/2026-10-09-storage-cert-qc-info-demo.html`。
+
+### 7.4 样本类型变更(边界)
+
+极少见。打开表单按**当前**样本类型选模板;保存时按当前模板**整包覆盖** `payload`(实现简单,可接受)。
+
+---
+
+## 8. 错误处理摘要
+
+| 场景 | 行为 |
+|------|------|
+| 不支持类型 PUT | 400;前端不展示按钮 |
+| 样本不存在 / 无权限 | 统一 403/404 |
+| GET 无记录 | 200 + 未填形态 |
+| payload 非 object | 400 |
+| 并发保存 | 后写覆盖 |
+
+---
+
+## 9. 测试要点
+
+- 后端:upsert 覆盖、租户隔离、`filled_flag`、类型门禁、GET 空记录。
+- 前端:两类型按钮显隐;质检保存后市场只读可见;未填空态;manage/mine 均有入口。
+
+---
+
+## 10. 实现落点(指引,非任务清单)
+
+| 层 | 预期触点 |
+|----|----------|
+| SQL | `docs/sql/migrations/2026-10-09-sample-storage-cert-qc.sql` |
+| Model / CRUD / API | `backend/app/models`、`crud`、`api`(新模块或挂入 qc / sample 相关路由并注册) |
+| FE API | `web-admin/src/api/` |
+| 字段元数据 | `web-admin/src/utils/` 或业务组件内配置 |
+| 填写/查看弹窗 | 共用业务组件 |
+| 工单卡片 | `QcWorkOrderSingleCard.vue` |
+| 样本信息 | `UnifiedSampleInfoPage.vue`(细胞存储操作列) |
+
+详细步骤在实现计划中展开(writing-plans)。