2026-10-09-protocol-audit-list-mode-demo.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1" />
  6. <title>协议单审核 · 列表模式(已定稿预览)</title>
  7. <link rel="preconnect" href="https://fonts.googleapis.com" />
  8. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  9. <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
  10. <style>
  11. :root {
  12. --primary: #10b981;
  13. --primary-dark: #059669;
  14. --primary-bg: #ecfdf5;
  15. --bg-page: #f8fafc;
  16. --panel: #ffffff;
  17. --text: #0f172a;
  18. --text-secondary: #334155;
  19. --text-muted: #64748b;
  20. --border: #e2e8f0;
  21. --radius: 10px;
  22. --radius-sm: 8px;
  23. --warning: #d97706;
  24. --warning-bg: #fffbeb;
  25. --danger: #dc2626;
  26. --danger-bg: #fef2f2;
  27. }
  28. * { box-sizing: border-box; }
  29. body {
  30. margin: 0;
  31. font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  32. font-size: 14px;
  33. line-height: 1.5;
  34. color: var(--text);
  35. background: var(--bg-page);
  36. }
  37. .page { max-width: 100%; margin: 0 auto; padding: 24px 20px 80px; }
  38. .hero { max-width: 1100px; margin-bottom: 18px; }
  39. .hero h1 {
  40. margin: 0 0 8px;
  41. font-size: 22px;
  42. font-weight: 700;
  43. letter-spacing: -0.02em;
  44. }
  45. .hero h1::before {
  46. content: "";
  47. display: inline-block;
  48. width: 4px;
  49. height: 1.1em;
  50. margin-right: 10px;
  51. vertical-align: -0.15em;
  52. border-radius: 2px;
  53. background: var(--primary);
  54. }
  55. .hero p { margin: 0 0 8px; color: var(--text-muted); max-width: 920px; }
  56. .context-bar { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  57. .pill {
  58. display: inline-flex; align-items: center;
  59. padding: 4px 10px; border-radius: 999px; font-size: 12px;
  60. background: var(--primary-bg); color: var(--primary-dark); border: 1px solid #a7f3d0;
  61. }
  62. .pill.muted { background: #f1f5f9; color: var(--text-muted); border-color: var(--border); }
  63. .decisions {
  64. max-width: 1100px;
  65. margin: 0 0 18px;
  66. padding: 14px 16px;
  67. background: #fff;
  68. border: 1px solid var(--border);
  69. border-radius: var(--radius);
  70. font-size: 13px;
  71. }
  72. .decisions h3 { margin: 0 0 8px; font-size: 14px; }
  73. .decisions ul { margin: 0; padding-left: 18px; color: var(--text-secondary); }
  74. .decisions li { margin: 4px 0; }
  75. .panel {
  76. background: var(--panel);
  77. border: 1px solid var(--border);
  78. border-radius: var(--radius);
  79. box-shadow: 0 1px 3px rgba(15, 23, 42, 0.05);
  80. overflow: hidden;
  81. }
  82. .panel-top { padding: 14px 18px 10px; border-bottom: 1px solid var(--border); }
  83. .panel-top h2 { margin: 0; font-size: 15px; font-weight: 600; }
  84. .panel-top h2::before {
  85. content: "";
  86. display: inline-block;
  87. width: 3px;
  88. height: 1em;
  89. margin-right: 8px;
  90. vertical-align: -0.1em;
  91. border-radius: 2px;
  92. background: var(--primary);
  93. }
  94. .panel-top .sub { margin: 6px 0 0; font-size: 13px; color: var(--text-muted); }
  95. .filter-block { padding: 12px 18px 8px; border-bottom: 1px solid var(--border); }
  96. .filter-grid {
  97. display: grid;
  98. grid-template-columns: repeat(4, 1fr);
  99. gap: 10px;
  100. }
  101. .field label {
  102. display: block;
  103. font-size: 11px;
  104. color: var(--text-muted);
  105. margin-bottom: 4px;
  106. }
  107. .field .ctrl {
  108. height: 36px;
  109. border: 1px solid var(--border);
  110. border-radius: var(--radius-sm);
  111. background: #fff;
  112. padding: 0 12px;
  113. display: flex;
  114. align-items: center;
  115. color: var(--text-muted);
  116. font-size: 13px;
  117. }
  118. .filter-actions {
  119. display: flex;
  120. justify-content: flex-end;
  121. gap: 8px;
  122. margin-top: 10px;
  123. }
  124. .toolbar {
  125. display: flex;
  126. flex-wrap: wrap;
  127. align-items: center;
  128. gap: 8px;
  129. padding: 10px 18px;
  130. border-bottom: 1px solid var(--border);
  131. }
  132. .sel { font-size: 13px; color: var(--text-secondary); margin-right: 4px; }
  133. .sel b { color: var(--primary-dark); }
  134. .btn {
  135. display: inline-flex;
  136. align-items: center;
  137. gap: 6px;
  138. height: 32px;
  139. padding: 0 12px;
  140. border-radius: var(--radius-sm);
  141. font-size: 13px;
  142. font-weight: 500;
  143. border: 1px solid var(--border);
  144. background: #fff;
  145. color: var(--text-secondary);
  146. }
  147. .btn.main {
  148. background: var(--primary);
  149. border-color: var(--primary);
  150. color: #fff;
  151. }
  152. .table-scroll { overflow-x: auto; }
  153. table.list {
  154. width: 100%;
  155. border-collapse: collapse;
  156. font-size: 12px;
  157. min-width: 1540px;
  158. }
  159. table.list th,
  160. table.list td {
  161. padding: 8px 10px;
  162. border-bottom: 1px solid var(--border);
  163. text-align: center;
  164. vertical-align: middle;
  165. white-space: nowrap;
  166. }
  167. table.list th {
  168. font-size: 12px;
  169. font-weight: 600;
  170. color: var(--text-muted);
  171. background: #f8fafc;
  172. position: sticky;
  173. top: 0;
  174. z-index: 1;
  175. }
  176. table.list td.left { text-align: left; }
  177. table.list tr.batch-a td { background: #fff; }
  178. table.list tr.batch-b td { background: #fafbfc; }
  179. table.list tr:hover td { background: #f1f5f9 !important; }
  180. table.list tr.group-a { box-shadow: inset 3px 0 0 #10b981; }
  181. table.list tr.group-b { box-shadow: inset 3px 0 0 #94a3b8; }
  182. table.list tr.alert-row td { color: var(--danger); }
  183. .mono {
  184. font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  185. font-size: 12px;
  186. }
  187. .muted { color: var(--text-muted); }
  188. .name-strong { font-weight: 600; }
  189. .tag {
  190. display: inline-flex;
  191. align-items: center;
  192. padding: 1px 8px;
  193. border-radius: 4px;
  194. font-size: 12px;
  195. font-weight: 500;
  196. line-height: 1.6;
  197. border: 1px solid transparent;
  198. }
  199. .tag.success { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
  200. .tag.warning { background: #fffbeb; color: #b45309; border-color: #fde68a; }
  201. .tag.info { background: #f1f5f9; color: #64748b; border-color: var(--border); }
  202. .op-row { display: inline-flex; flex-wrap: wrap; gap: 4px; justify-content: center; }
  203. .op {
  204. display: inline-flex;
  205. align-items: center;
  206. height: 26px;
  207. padding: 0 8px;
  208. border: 1px solid var(--border);
  209. border-radius: 6px;
  210. background: #fff;
  211. font-size: 12px;
  212. color: var(--text-secondary);
  213. }
  214. .pager {
  215. display: flex;
  216. justify-content: flex-end;
  217. align-items: center;
  218. gap: 8px;
  219. padding: 12px 18px;
  220. border-top: 1px solid var(--border);
  221. font-size: 13px;
  222. color: var(--text-muted);
  223. }
  224. .callout {
  225. margin-top: 16px;
  226. max-width: 1100px;
  227. padding: 12px 14px;
  228. border-radius: var(--radius-sm);
  229. background: var(--primary-bg);
  230. border: 1px solid #a7f3d0;
  231. font-size: 13px;
  232. color: var(--primary-dark);
  233. }
  234. .note {
  235. margin-top: 10px;
  236. font-size: 12px;
  237. color: var(--text-muted);
  238. max-width: 1100px;
  239. }
  240. .pay-multi {
  241. display: flex;
  242. flex-direction: column;
  243. gap: 2px;
  244. line-height: 1.35;
  245. }
  246. </style>
  247. </head>
  248. <body>
  249. <div class="page">
  250. <div class="hero">
  251. <h1>协议单审核 · 列表模式(已定稿预览)</h1>
  252. <p>
  253. 将 <code>/#/market/cell-storage/protocol-audit?tab=cell_storage</code> 从卡片改为
  254. <strong>纯扁平行列表</strong>(一行一样本,协议字段重复),列对齐客户 Excel。
  255. </p>
  256. <div class="context-bar">
  257. <span class="pill">纯扁平行 · 已确认</span>
  258. <span class="pill">年月日 → 采集日期 · 已确认</span>
  259. <span class="pill">是否同存 = 样本数 ≥ 2</span>
  260. <span class="pill muted">P02 · Fresh Mint</span>
  261. </div>
  262. </div>
  263. <div class="decisions">
  264. <h3>已确认决策</h3>
  265. <ul>
  266. <li><strong>行粒度:</strong>一行一样本;同一协议多样本占多行,协议字段重复显示(不做 rowspan)。</li>
  267. <li><strong>采集日期:</strong>原 Excel「年 / 月 / 日」合并为一列,取值自 <code>sampleCollectTime</code> 的日期部分(如 2025-09-15)。</li>
  268. <li><strong>采集时间:</strong>保留完整时间列(含时分)。</li>
  269. <li><strong>是否同存:</strong>计算列,不落库;同协议样本数 ≥ 2 →「是」,否则「否」(同协议各行相同)。</li>
  270. <li><strong>操作列:</strong>保留审核 / 查看 / 编辑 / 删除(Excel 无,系统需要);批量操作按协议 ID 去重。</li>
  271. </ul>
  272. </div>
  273. <div class="panel">
  274. <div class="panel-top">
  275. <h2>列表预览 · 纯扁平行</h2>
  276. <p class="sub">
  277. 郭梦朵协议 ZKZX34000849 含 2 个样本 → 占两行,是否同存均为「是」;左侧绿条仅作示意分组(实现可不加)。
  278. </p>
  279. </div>
  280. <div class="filter-block">
  281. <div class="filter-grid">
  282. <div class="field"><label>关键词</label><div class="ctrl">姓名/身份证号/电话/入库编码</div></div>
  283. <div class="field"><label>协议状态</label><div class="ctrl">全部</div></div>
  284. <div class="field"><label>创建时间</label><div class="ctrl">开始日期 至 结束日期</div></div>
  285. <div class="field"><label>样本采集时间</label><div class="ctrl">开始日期 至 结束日期</div></div>
  286. </div>
  287. <div class="filter-actions">
  288. <span class="btn">重置</span>
  289. <span class="btn main">搜索</span>
  290. </div>
  291. </div>
  292. <div class="toolbar">
  293. <span class="sel">已选 <b>0</b> 项</span>
  294. <span class="btn main">新增</span>
  295. <span class="btn">退费登记</span>
  296. <span class="btn">审核</span>
  297. <span class="btn">协议生效</span>
  298. <span class="btn">协议过期</span>
  299. <span class="btn">协议作废</span>
  300. <span class="btn">协议转科研</span>
  301. <span class="btn">批量删除</span>
  302. </div>
  303. <div class="table-scroll">
  304. <table class="list">
  305. <thead>
  306. <tr>
  307. <th>选</th>
  308. <th>序号</th>
  309. <th>采集日期</th>
  310. <th>实验室流转编号</th>
  311. <th>母亲姓名</th>
  312. <th>协议编号</th>
  313. <th>样本类型</th>
  314. <th>是否同存</th>
  315. <th>采集地区</th>
  316. <th>采集医院</th>
  317. <th>采集时间</th>
  318. <th>缴费方式</th>
  319. <th>首期缴费时间</th>
  320. <th>市场专员</th>
  321. <th>地区经理</th>
  322. <th>运输方式</th>
  323. <th>操作</th>
  324. </tr>
  325. </thead>
  326. <tbody>
  327. <tr class="batch-a group-a">
  328. <td><input type="checkbox" /></td>
  329. <td>1</td>
  330. <td>2025-09-15</td>
  331. <td class="mono left">B0000004</td>
  332. <td class="name-strong">郭梦朵</td>
  333. <td class="mono left">ZKZX34000849</td>
  334. <td class="left">围产期造血干细胞</td>
  335. <td><span class="tag warning">是</span></td>
  336. <td>亳州区域</td>
  337. <td class="left">涡阳县人民医院</td>
  338. <td class="left">2025-09-15 08:51</td>
  339. <td><span class="tag success">趸交</span></td>
  340. <td class="left">2025-09-15 09:20</td>
  341. <td>马丽</td>
  342. <td>张颖</td>
  343. <td>专线车</td>
  344. <td>
  345. <div class="op-row">
  346. <span class="op">审核</span>
  347. <span class="op">查看</span>
  348. <span class="op">编辑</span>
  349. <span class="op">删除</span>
  350. </div>
  351. </td>
  352. </tr>
  353. <tr class="batch-a group-a">
  354. <td><input type="checkbox" /></td>
  355. <td>2</td>
  356. <td>2025-09-15</td>
  357. <td class="mono left">B0000004A</td>
  358. <td class="name-strong">郭梦朵</td>
  359. <td class="mono left">ZKZX34000849</td>
  360. <td class="left">围产期间充质干细胞</td>
  361. <td><span class="tag warning">是</span></td>
  362. <td>亳州区域</td>
  363. <td class="left">涡阳县人民医院</td>
  364. <td class="left">2025-09-15 08:51</td>
  365. <td><span class="tag warning">分期</span></td>
  366. <td class="left">
  367. <div class="pay-multi">
  368. <span>2025-09-15 09:20</span>
  369. <span>2025-10-15 10:05</span>
  370. </div>
  371. </td>
  372. <td>马丽</td>
  373. <td>张颖</td>
  374. <td>专线车</td>
  375. <td>
  376. <div class="op-row">
  377. <span class="op">审核</span>
  378. <span class="op">查看</span>
  379. <span class="op">编辑</span>
  380. <span class="op">删除</span>
  381. </div>
  382. </td>
  383. </tr>
  384. <tr class="batch-b group-b alert-row">
  385. <td><input type="checkbox" /></td>
  386. <td>3</td>
  387. <td>2025-10-10</td>
  388. <td class="mono left muted">—</td>
  389. <td class="name-strong">邵悦</td>
  390. <td class="mono left">ZKZX34000861</td>
  391. <td class="left">围产期造血干细胞</td>
  392. <td><span class="tag info">否</span></td>
  393. <td>亳州区域</td>
  394. <td class="left">涡阳县中医院</td>
  395. <td class="left">2025-10-10 14:22</td>
  396. <td><span class="tag warning">分期</span></td>
  397. <td class="left">
  398. <div class="pay-multi">
  399. <span>2025-10-10 15:01</span>
  400. <span>2025-11-10 09:18</span>
  401. </div>
  402. </td>
  403. <td>张慧茹</td>
  404. <td>张颖</td>
  405. <td>专线车</td>
  406. <td>
  407. <div class="op-row">
  408. <span class="op">审核</span>
  409. <span class="op">查看</span>
  410. <span class="op">编辑</span>
  411. <span class="op">删除</span>
  412. </div>
  413. </td>
  414. </tr>
  415. <tr class="batch-a group-b">
  416. <td><input type="checkbox" /></td>
  417. <td>4</td>
  418. <td>2025-10-12</td>
  419. <td class="mono left">B0000007</td>
  420. <td class="name-strong">尹璐</td>
  421. <td class="mono left">ZKZX34000870</td>
  422. <td class="left">围产期造血干细胞</td>
  423. <td><span class="tag info">否</span></td>
  424. <td>亳州区域</td>
  425. <td class="left">利辛县人民医院</td>
  426. <td class="left">2025-10-12 11:05</td>
  427. <td><span class="tag success">趸交</span></td>
  428. <td class="left">2025-10-12 11:40</td>
  429. <td>马丽</td>
  430. <td>张颖</td>
  431. <td>专线车</td>
  432. <td>
  433. <div class="op-row">
  434. <span class="op">审核</span>
  435. <span class="op">查看</span>
  436. <span class="op">编辑</span>
  437. <span class="op">删除</span>
  438. </div>
  439. </td>
  440. </tr>
  441. </tbody>
  442. </table>
  443. </div>
  444. <div class="pager">共 3 条协议 / 4 行样本 · 接口分页仍按协议 · 前端 flatten · &lt; 1 &gt;</div>
  445. </div>
  446. <div class="callout">
  447. <strong>实现要点:</strong>接口仍返回协议列表(含 <code>sampleItems[]</code>);前端 flatten 成样本行;无样本时仍保留 1 行(流转编号等为空)。
  448. 勾选多样本行时,批量审核/删除按 <code>protocolId</code> 去重。
  449. </div>
  450. <p class="note">
  451. 文件:<code>docs/demos/2026-10-09-protocol-audit-list-mode-demo.html</code>
  452. · 若本预览 OK,回复「设计没问题」或指出要改的点,我再写正式设计文档并进入实现计划。
  453. </p>
  454. </div>
  455. </body>
  456. </html>