index.html 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  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.0" />
  6. <title>入库放行 · 批量审核弹窗 Demo</title>
  7. <link rel="preconnect" href="https://fonts.googleapis.com" />
  8. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  9. <link
  10. href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
  11. rel="stylesheet"
  12. />
  13. <style>
  14. :root {
  15. --primary: #10b981;
  16. --primary-dark: #059669;
  17. --primary-bg: #ecfdf5;
  18. --text: #1e293b;
  19. --text-muted: #64748b;
  20. --text-label: #334155;
  21. --border: #d9e8e2;
  22. --border-soft: #e2e8f0;
  23. --bg-page: #f1f5f9;
  24. --surface: #ffffff;
  25. --table-head: #f8fafc;
  26. --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  27. --radius: 10px;
  28. --mask: rgba(15, 23, 42, 0.35);
  29. --warn: #d97706;
  30. --warn-bg: #fffbeb;
  31. --danger: #dc2626;
  32. --danger-bg: #fef2f2;
  33. }
  34. * {
  35. box-sizing: border-box;
  36. }
  37. html,
  38. body {
  39. margin: 0;
  40. min-height: 100%;
  41. }
  42. body {
  43. font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
  44. font-size: 14px;
  45. line-height: 1.5;
  46. color: var(--text);
  47. background: var(--bg-page);
  48. }
  49. button {
  50. font-family: inherit;
  51. }
  52. .page {
  53. min-height: 100vh;
  54. display: flex;
  55. flex-direction: column;
  56. }
  57. .banner {
  58. position: sticky;
  59. top: 0;
  60. z-index: 20;
  61. background: var(--surface);
  62. border-bottom: 1px solid var(--border-soft);
  63. padding: 14px 24px 16px;
  64. box-shadow: var(--shadow);
  65. }
  66. .banner h1 {
  67. margin: 0;
  68. font-size: 16px;
  69. font-weight: 700;
  70. }
  71. .banner p {
  72. margin: 6px 0 0;
  73. font-size: 13px;
  74. color: var(--text-muted);
  75. max-width: 960px;
  76. }
  77. .banner p strong {
  78. color: var(--primary-dark);
  79. font-weight: 600;
  80. }
  81. .scheme-row {
  82. display: flex;
  83. flex-wrap: wrap;
  84. align-items: center;
  85. gap: 8px;
  86. margin-top: 12px;
  87. }
  88. .scheme-row > span {
  89. font-size: 12px;
  90. color: var(--text-muted);
  91. margin-right: 4px;
  92. }
  93. .scheme-btn {
  94. height: 32px;
  95. padding: 0 12px;
  96. border-radius: 8px;
  97. border: 1px solid var(--border);
  98. background: var(--surface);
  99. color: var(--text);
  100. font-size: 12px;
  101. font-weight: 600;
  102. cursor: pointer;
  103. }
  104. .scheme-btn:hover {
  105. background: #f8fafc;
  106. }
  107. .scheme-btn.is-active {
  108. background: var(--primary);
  109. border-color: var(--primary);
  110. color: #fff;
  111. }
  112. .scheme-btn.is-rec::after {
  113. content: '推荐';
  114. margin-left: 6px;
  115. font-size: 10px;
  116. font-weight: 700;
  117. padding: 1px 5px;
  118. border-radius: 4px;
  119. background: rgba(255, 255, 255, 0.22);
  120. }
  121. .scheme-btn:not(.is-active).is-rec::after {
  122. background: var(--primary-bg);
  123. color: var(--primary-dark);
  124. }
  125. .note {
  126. margin-top: 10px;
  127. font-size: 12px;
  128. color: var(--text-muted);
  129. padding: 8px 10px;
  130. background: #f8fafc;
  131. border: 1px solid var(--border-soft);
  132. border-radius: 8px;
  133. max-width: 960px;
  134. }
  135. .stage {
  136. flex: 1;
  137. position: relative;
  138. min-height: 0;
  139. }
  140. .backdrop {
  141. position: absolute;
  142. inset: 0;
  143. background: var(--mask);
  144. display: flex;
  145. align-items: flex-start;
  146. justify-content: center;
  147. padding: 16px 16px 24px;
  148. overflow: auto;
  149. }
  150. .dialog {
  151. display: none;
  152. flex-direction: column;
  153. background: var(--surface);
  154. border-radius: var(--radius);
  155. box-shadow: 0 16px 48px rgba(15, 23, 42, 0.18);
  156. max-height: calc(100vh - 140px);
  157. overflow: hidden;
  158. width: min(1280px, 100%);
  159. }
  160. .dialog.is-show {
  161. display: flex;
  162. }
  163. .dialog--unified {
  164. width: min(640px, 100%);
  165. }
  166. .dialog--table {
  167. width: min(980px, 100%);
  168. }
  169. .dialog-head {
  170. flex-shrink: 0;
  171. padding: 16px 22px 14px;
  172. border-bottom: 1px solid var(--border-soft);
  173. }
  174. .dialog-head__row {
  175. display: flex;
  176. align-items: flex-start;
  177. justify-content: space-between;
  178. gap: 12px;
  179. }
  180. .dialog-head h2 {
  181. margin: 0;
  182. font-size: 16px;
  183. font-weight: 700;
  184. }
  185. .dialog-head .sub {
  186. margin: 4px 0 0;
  187. font-size: 12px;
  188. color: var(--text-muted);
  189. }
  190. .icon-btn {
  191. width: 28px;
  192. height: 28px;
  193. border: none;
  194. background: transparent;
  195. color: var(--text-muted);
  196. border-radius: 6px;
  197. cursor: pointer;
  198. display: inline-flex;
  199. align-items: center;
  200. justify-content: center;
  201. }
  202. .icon-btn:hover {
  203. background: #f1f5f9;
  204. color: var(--text);
  205. }
  206. .mode-tabs {
  207. display: inline-flex;
  208. gap: 4px;
  209. margin-top: 12px;
  210. padding: 3px;
  211. background: #f1f5f9;
  212. border-radius: 8px;
  213. }
  214. .mode-tab {
  215. height: 28px;
  216. padding: 0 12px;
  217. border: none;
  218. border-radius: 6px;
  219. background: transparent;
  220. font-size: 12px;
  221. font-weight: 600;
  222. color: var(--text-muted);
  223. cursor: pointer;
  224. }
  225. .mode-tab.is-active {
  226. background: #fff;
  227. color: var(--primary-dark);
  228. box-shadow: var(--shadow);
  229. }
  230. .dialog-body {
  231. flex: 1;
  232. min-height: 0;
  233. overflow: hidden;
  234. }
  235. /* —— Scheme A: 队列逐条 —— */
  236. .queue-layout {
  237. display: grid;
  238. grid-template-columns: 240px minmax(0, 1.55fr) minmax(300px, 0.9fr);
  239. height: 100%;
  240. min-height: 520px;
  241. }
  242. .queue-pane {
  243. border-right: 1px solid var(--border-soft);
  244. background: #fcfdfd;
  245. display: flex;
  246. flex-direction: column;
  247. min-height: 0;
  248. }
  249. .queue-pane__h {
  250. flex-shrink: 0;
  251. padding: 12px 14px;
  252. border-bottom: 1px solid var(--border-soft);
  253. font-size: 12px;
  254. font-weight: 700;
  255. letter-spacing: 0.04em;
  256. color: var(--text-muted);
  257. display: flex;
  258. justify-content: space-between;
  259. align-items: center;
  260. }
  261. .queue-pane__h em {
  262. font-style: normal;
  263. color: var(--primary-dark);
  264. font-weight: 700;
  265. }
  266. .queue-list {
  267. flex: 1;
  268. overflow: auto;
  269. padding: 8px;
  270. }
  271. .queue-item {
  272. width: 100%;
  273. text-align: left;
  274. border: 1px solid transparent;
  275. background: transparent;
  276. border-radius: 8px;
  277. padding: 10px 10px;
  278. cursor: pointer;
  279. margin-bottom: 4px;
  280. }
  281. .queue-item:hover {
  282. background: #f8fafc;
  283. }
  284. .queue-item.is-active {
  285. background: var(--primary-bg);
  286. border-color: #a7f3d0;
  287. }
  288. .queue-item.is-done {
  289. opacity: 0.72;
  290. }
  291. .queue-item__code {
  292. font-size: 13px;
  293. font-weight: 700;
  294. font-variant-numeric: tabular-nums;
  295. }
  296. .queue-item__meta {
  297. margin-top: 2px;
  298. font-size: 11px;
  299. color: var(--text-muted);
  300. display: flex;
  301. justify-content: space-between;
  302. gap: 8px;
  303. }
  304. .status-dot {
  305. display: inline-flex;
  306. align-items: center;
  307. gap: 4px;
  308. font-weight: 600;
  309. }
  310. .status-dot::before {
  311. content: '';
  312. width: 6px;
  313. height: 6px;
  314. border-radius: 50%;
  315. background: #94a3b8;
  316. }
  317. .status-dot--pending::before {
  318. background: var(--warn);
  319. }
  320. .status-dot--ok::before {
  321. background: var(--primary);
  322. }
  323. .status-dot--bad::before {
  324. background: var(--danger);
  325. }
  326. .evidence-pane {
  327. min-width: 0;
  328. overflow: auto;
  329. padding: 14px 16px 18px;
  330. border-right: 1px solid var(--border-soft);
  331. background: #fcfdfd;
  332. }
  333. .form-pane {
  334. min-width: 0;
  335. overflow: auto;
  336. padding: 14px 18px 18px;
  337. background: var(--surface);
  338. display: flex;
  339. flex-direction: column;
  340. }
  341. .hero {
  342. padding: 10px 12px;
  343. background: var(--table-head);
  344. border: 1px solid var(--border-soft);
  345. border-radius: 8px;
  346. margin-bottom: 12px;
  347. }
  348. .hero__grid {
  349. display: grid;
  350. grid-template-columns: 1fr 1fr;
  351. gap: 6px 16px;
  352. }
  353. .meta-row {
  354. display: flex;
  355. gap: 8px;
  356. font-size: 13px;
  357. min-width: 0;
  358. }
  359. .meta-row .k {
  360. color: var(--text-label);
  361. flex-shrink: 0;
  362. }
  363. .meta-row .v {
  364. font-weight: 600;
  365. overflow-wrap: anywhere;
  366. }
  367. .verdict {
  368. display: flex;
  369. flex-wrap: wrap;
  370. gap: 8px;
  371. margin-top: 8px;
  372. }
  373. .chip {
  374. display: inline-flex;
  375. align-items: center;
  376. gap: 4px;
  377. height: 22px;
  378. padding: 0 8px;
  379. border-radius: 6px;
  380. font-size: 11px;
  381. font-weight: 600;
  382. background: #f1f5f9;
  383. color: var(--text-muted);
  384. }
  385. .chip--ok {
  386. background: var(--primary-bg);
  387. color: #047857;
  388. }
  389. .chip--warn {
  390. background: var(--warn-bg);
  391. color: var(--warn);
  392. }
  393. .block {
  394. border: 1px solid var(--border-soft);
  395. border-radius: 8px;
  396. background: #fff;
  397. margin-bottom: 8px;
  398. overflow: hidden;
  399. }
  400. .block > summary {
  401. list-style: none;
  402. cursor: pointer;
  403. display: flex;
  404. align-items: center;
  405. justify-content: space-between;
  406. padding: 9px 12px;
  407. font-size: 13px;
  408. font-weight: 700;
  409. }
  410. .block > summary::-webkit-details-marker {
  411. display: none;
  412. }
  413. .block-body {
  414. padding: 0 12px 12px;
  415. border-top: 1px solid #f1f5f9;
  416. }
  417. .kv-grid {
  418. display: grid;
  419. grid-template-columns: 1fr 1fr;
  420. gap: 8px 16px;
  421. padding-top: 10px;
  422. }
  423. .kv dt {
  424. margin: 0;
  425. font-size: 12px;
  426. color: var(--text-label);
  427. }
  428. .kv dd {
  429. margin: 2px 0 0;
  430. font-size: 13px;
  431. }
  432. .tag {
  433. display: inline-flex;
  434. height: 20px;
  435. padding: 0 7px;
  436. border-radius: 4px;
  437. font-size: 12px;
  438. font-weight: 600;
  439. align-items: center;
  440. }
  441. .tag--success {
  442. background: var(--primary-bg);
  443. color: #047857;
  444. }
  445. .tag--info {
  446. background: #f1f5f9;
  447. color: var(--text-muted);
  448. }
  449. .pane-title {
  450. margin: 0 0 10px;
  451. font-size: 12px;
  452. font-weight: 700;
  453. letter-spacing: 0.04em;
  454. color: var(--text-muted);
  455. }
  456. .progress-bar {
  457. margin-bottom: 12px;
  458. padding: 8px 10px;
  459. background: var(--primary-bg);
  460. border: 1px solid #a7f3d0;
  461. border-radius: 8px;
  462. font-size: 12px;
  463. color: #047857;
  464. font-weight: 600;
  465. }
  466. .field {
  467. margin-bottom: 14px;
  468. }
  469. .field label {
  470. display: block;
  471. font-size: 13px;
  472. font-weight: 500;
  473. margin-bottom: 6px;
  474. }
  475. .radios {
  476. display: flex;
  477. gap: 16px;
  478. height: 36px;
  479. align-items: center;
  480. }
  481. .radios label {
  482. display: inline-flex;
  483. align-items: center;
  484. gap: 6px;
  485. margin: 0;
  486. cursor: pointer;
  487. font-weight: 400;
  488. }
  489. textarea {
  490. width: 100%;
  491. border: 1px solid var(--border);
  492. border-radius: 8px;
  493. padding: 8px 10px;
  494. font: inherit;
  495. font-size: 13px;
  496. resize: vertical;
  497. min-height: 72px;
  498. }
  499. textarea:focus {
  500. outline: none;
  501. border-color: var(--primary);
  502. box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15);
  503. }
  504. .upload-row {
  505. display: flex;
  506. flex-wrap: wrap;
  507. gap: 8px;
  508. }
  509. .upload-card {
  510. width: 140px;
  511. height: 96px;
  512. border: 1px dashed #a7f3d0;
  513. border-radius: 8px;
  514. display: flex;
  515. flex-direction: column;
  516. align-items: center;
  517. justify-content: center;
  518. gap: 6px;
  519. color: #047857;
  520. font-size: 12px;
  521. font-weight: 600;
  522. background: #f8fffb;
  523. cursor: pointer;
  524. }
  525. .apply-box {
  526. margin-top: auto;
  527. padding: 10px 12px;
  528. background: #f8fafc;
  529. border: 1px solid var(--border-soft);
  530. border-radius: 8px;
  531. font-size: 12px;
  532. color: var(--text-label);
  533. }
  534. .apply-box label {
  535. display: flex;
  536. align-items: flex-start;
  537. gap: 8px;
  538. cursor: pointer;
  539. }
  540. .apply-box input {
  541. margin-top: 2px;
  542. accent-color: var(--primary);
  543. }
  544. .apply-box .hint {
  545. margin-top: 4px;
  546. color: var(--text-muted);
  547. font-size: 11px;
  548. padding-left: 22px;
  549. }
  550. .dialog-foot {
  551. flex-shrink: 0;
  552. display: flex;
  553. flex-wrap: wrap;
  554. align-items: center;
  555. justify-content: flex-end;
  556. gap: 8px;
  557. padding: 12px 18px;
  558. border-top: 1px solid var(--border-soft);
  559. background: #fff;
  560. }
  561. .dialog-foot .spacer {
  562. margin-right: auto;
  563. font-size: 12px;
  564. color: var(--text-muted);
  565. }
  566. .btn {
  567. height: 36px;
  568. padding: 0 14px;
  569. border-radius: 8px;
  570. border: 1px solid var(--border);
  571. background: #fff;
  572. font-size: 13px;
  573. font-weight: 600;
  574. cursor: pointer;
  575. display: inline-flex;
  576. align-items: center;
  577. gap: 6px;
  578. }
  579. .btn:hover {
  580. background: #f8fafc;
  581. }
  582. .btn--primary {
  583. background: var(--primary);
  584. border-color: var(--primary);
  585. color: #fff;
  586. }
  587. .btn--primary:hover {
  588. background: var(--primary-dark);
  589. }
  590. .btn--ghost-danger {
  591. color: var(--danger);
  592. border-color: #fecaca;
  593. }
  594. .btn--ghost-danger:hover {
  595. background: var(--danger-bg);
  596. }
  597. /* —— Scheme B: 统一结论(现状增强) —— */
  598. .unified-body {
  599. padding: 18px 22px;
  600. overflow: auto;
  601. }
  602. .target-hint {
  603. margin: 0 0 14px;
  604. padding: 10px 12px;
  605. background: var(--warn-bg);
  606. border: 1px solid #fde68a;
  607. border-radius: 8px;
  608. font-size: 13px;
  609. color: #92400e;
  610. }
  611. .target-hint strong {
  612. font-weight: 700;
  613. }
  614. .mini-table {
  615. width: 100%;
  616. border-collapse: collapse;
  617. font-size: 12px;
  618. margin-bottom: 16px;
  619. }
  620. .mini-table th,
  621. .mini-table td {
  622. padding: 8px 10px;
  623. border-bottom: 1px solid var(--border-soft);
  624. text-align: left;
  625. }
  626. .mini-table th {
  627. background: var(--table-head);
  628. font-weight: 600;
  629. color: var(--text-label);
  630. }
  631. .mini-table tr:last-child td {
  632. border-bottom: none;
  633. }
  634. /* —— Scheme C: 表格 + 行内 —— */
  635. .table-body {
  636. padding: 0;
  637. overflow: auto;
  638. max-height: calc(100vh - 280px);
  639. }
  640. .batch-bar {
  641. display: flex;
  642. flex-wrap: wrap;
  643. align-items: center;
  644. gap: 10px 16px;
  645. padding: 12px 18px;
  646. border-bottom: 1px solid var(--border-soft);
  647. background: #f8fffb;
  648. }
  649. .batch-bar .label {
  650. font-size: 12px;
  651. font-weight: 700;
  652. color: var(--text-muted);
  653. }
  654. .data-table {
  655. width: 100%;
  656. border-collapse: collapse;
  657. font-size: 13px;
  658. }
  659. .data-table th,
  660. .data-table td {
  661. padding: 10px 12px;
  662. border-bottom: 1px solid var(--border-soft);
  663. text-align: left;
  664. vertical-align: middle;
  665. }
  666. .data-table th {
  667. position: sticky;
  668. top: 0;
  669. background: var(--table-head);
  670. font-size: 12px;
  671. font-weight: 600;
  672. color: var(--text-label);
  673. z-index: 1;
  674. }
  675. .data-table .mono {
  676. font-variant-numeric: tabular-nums;
  677. font-weight: 600;
  678. }
  679. .row-actions {
  680. display: flex;
  681. gap: 6px;
  682. }
  683. .link-btn {
  684. border: none;
  685. background: none;
  686. color: var(--primary-dark);
  687. font-size: 12px;
  688. font-weight: 600;
  689. cursor: pointer;
  690. padding: 0;
  691. }
  692. .link-btn:hover {
  693. text-decoration: underline;
  694. }
  695. .expand-panel {
  696. display: none;
  697. background: #fcfdfd;
  698. }
  699. .expand-panel.is-open {
  700. display: table-row;
  701. }
  702. .expand-panel td {
  703. padding: 0 12px 14px 36px;
  704. }
  705. .expand-inner {
  706. display: grid;
  707. grid-template-columns: 1fr 280px;
  708. gap: 12px;
  709. padding: 12px;
  710. border: 1px solid var(--border-soft);
  711. border-radius: 8px;
  712. background: #fff;
  713. }
  714. .toast {
  715. position: fixed;
  716. bottom: 24px;
  717. left: 50%;
  718. transform: translateX(-50%) translateY(20px);
  719. background: #1e293b;
  720. color: #fff;
  721. padding: 10px 16px;
  722. border-radius: 8px;
  723. font-size: 13px;
  724. opacity: 0;
  725. pointer-events: none;
  726. transition:
  727. opacity 0.2s,
  728. transform 0.2s;
  729. z-index: 50;
  730. }
  731. .toast.is-show {
  732. opacity: 1;
  733. transform: translateX(-50%) translateY(0);
  734. }
  735. @media (max-width: 1100px) {
  736. .queue-layout {
  737. grid-template-columns: 200px minmax(0, 1fr);
  738. }
  739. .form-pane {
  740. grid-column: 1 / -1;
  741. border-top: 1px solid var(--border-soft);
  742. max-height: 320px;
  743. }
  744. .expand-inner {
  745. grid-template-columns: 1fr;
  746. }
  747. }
  748. </style>
  749. </head>
  750. <body>
  751. <div class="page">
  752. <header class="banner">
  753. <h1>入库放行审核 · 批量审核弹窗 Demo</h1>
  754. <p>
  755. 列表勾选多条后点「批量审核」。目标:既可<strong>逐条核对证据后审核</strong>,也可<strong>对剩余项套用同一结论快速批量提交</strong>。
  756. 下方可切换三种方案对比(Fresh Mint)。
  757. </p>
  758. <div class="scheme-row">
  759. <span>方案</span>
  760. <button type="button" class="scheme-btn is-rec is-active" data-scheme="queue">
  761. A · 队列逐条 + 套用剩余
  762. </button>
  763. <button type="button" class="scheme-btn" data-scheme="unified">
  764. B · 统一结论(现状)
  765. </button>
  766. <button type="button" class="scheme-btn" data-scheme="table">
  767. C · 表格行内审核
  768. </button>
  769. </div>
  770. <div class="note" id="schemeNote">
  771. <strong>A 推荐:</strong>左侧待审队列 → 中间复用单条审核的证据区 →
  772. 右侧结论表单。默认「提交本条并下一条」;勾选「将结论套用到剩余未审项」即可一键批量。与现有单条审核体验连续。
  773. </div>
  774. </header>
  775. <div class="stage">
  776. <div class="backdrop">
  777. <!-- ========== Scheme A ========== -->
  778. <div class="dialog is-show" id="dlg-queue" role="dialog" aria-label="批量审核-队列">
  779. <div class="dialog-head">
  780. <div class="dialog-head__row">
  781. <div>
  782. <h2>批量审核(5 条)</h2>
  783. <p class="sub">逐条核对接收 / 质检 / 生产证据后给出结论;也可对剩余项统一套用。</p>
  784. </div>
  785. <button type="button" class="icon-btn" aria-label="关闭" title="关闭">✕</button>
  786. </div>
  787. <div class="mode-tabs" role="tablist">
  788. <button type="button" class="mode-tab is-active" data-mode="per">逐条核对</button>
  789. <button type="button" class="mode-tab" data-mode="batch">统一提交</button>
  790. </div>
  791. </div>
  792. <div class="dialog-body" id="queue-per-body">
  793. <div class="queue-layout">
  794. <aside class="queue-pane">
  795. <div class="queue-pane__h">
  796. <span>待审队列</span>
  797. <em id="queueProgress">1 / 5</em>
  798. </div>
  799. <div class="queue-list" id="queueList"></div>
  800. </aside>
  801. <section class="evidence-pane">
  802. <div class="hero">
  803. <div class="hero__grid">
  804. <div class="meta-row">
  805. <span class="k">实验室入库编码</span>
  806. <span class="v" id="evLab">RK-2026-0819-003</span>
  807. </div>
  808. <div class="meta-row">
  809. <span class="k">样本编码</span>
  810. <span class="v" id="evCode">YP-CS-20260819-001</span>
  811. </div>
  812. </div>
  813. <div class="verdict">
  814. <span class="chip chip--ok">样本接收:接收</span>
  815. <span class="chip chip--ok">质检工单:已完成</span>
  816. <span class="chip chip--ok">生产工单:已完成</span>
  817. </div>
  818. </div>
  819. <details class="block" open>
  820. <summary><span>接收信息</span><span>▾</span></summary>
  821. <div class="block-body">
  822. <dl class="kv-grid">
  823. <div class="kv">
  824. <dt>接收人</dt>
  825. <dd>张三</dd>
  826. </div>
  827. <div class="kv">
  828. <dt>接收时间</dt>
  829. <dd>2026-08-18 14:22</dd>
  830. </div>
  831. <div class="kv">
  832. <dt>样本类型</dt>
  833. <dd>脐带血</dd>
  834. </div>
  835. <div class="kv">
  836. <dt>接收结论</dt>
  837. <dd><span class="tag tag--success">接收</span></dd>
  838. </div>
  839. </dl>
  840. </div>
  841. </details>
  842. <details class="block" open>
  843. <summary><span>质检信息</span><span>▾</span></summary>
  844. <div class="block-body">
  845. <dl class="kv-grid">
  846. <div class="kv">
  847. <dt>工单状态</dt>
  848. <dd><span class="tag tag--success">已完成</span></dd>
  849. </div>
  850. <div class="kv">
  851. <dt>门禁结论</dt>
  852. <dd><span class="tag tag--success">通过</span></dd>
  853. </div>
  854. <div class="kv">
  855. <dt>过程质检</dt>
  856. <dd>2 项已完成</dd>
  857. </div>
  858. <div class="kv">
  859. <dt>异常项</dt>
  860. <dd>无</dd>
  861. </div>
  862. </dl>
  863. </div>
  864. </details>
  865. <details class="block" open>
  866. <summary><span>生产信息</span><span>▾</span></summary>
  867. <div class="block-body">
  868. <dl class="kv-grid">
  869. <div class="kv">
  870. <dt>工单状态</dt>
  871. <dd><span class="tag tag--success">已完成</span></dd>
  872. </div>
  873. <div class="kv">
  874. <dt>产出数量</dt>
  875. <dd>2 袋</dd>
  876. </div>
  877. <div class="kv">
  878. <dt>废料</dt>
  879. <dd>0</dd>
  880. </div>
  881. <div class="kv">
  882. <dt>执行人</dt>
  883. <dd>李四</dd>
  884. </div>
  885. </dl>
  886. </div>
  887. </details>
  888. </section>
  889. <section class="form-pane">
  890. <p class="pane-title">本条审核</p>
  891. <div class="progress-bar" id="formProgress">正在审核第 1 条 · 还剩 4 条未审</div>
  892. <div class="field">
  893. <label>审核结论</label>
  894. <div class="radios">
  895. <label><input type="radio" name="q-conc" value="tg" checked /> 通过</label>
  896. <label><input type="radio" name="q-conc" value="btg" /> 不通过</label>
  897. </div>
  898. </div>
  899. <div class="field">
  900. <label>备注</label>
  901. <textarea placeholder="可选" maxlength="1000"></textarea>
  902. </div>
  903. <div class="field">
  904. <label>附件</label>
  905. <div class="upload-row">
  906. <div class="upload-card">
  907. <span style="font-size: 20px">+</span>
  908. <span>上传附件</span>
  909. </div>
  910. </div>
  911. </div>
  912. <div class="apply-box">
  913. <label>
  914. <input type="checkbox" id="applyRest" />
  915. <span
  916. >将当前结论 / 备注 / 附件<strong>套用到剩余未审项</strong>并一次性提交</span
  917. >
  918. </label>
  919. <div class="hint">
  920. 勾选后底栏主按钮变为「批量提交剩余」;适合证据已预览、结论一致的场景。
  921. </div>
  922. </div>
  923. </section>
  924. </div>
  925. </div>
  926. <div class="dialog-body" id="queue-batch-body" style="display: none">
  927. <div class="unified-body">
  928. <p class="target-hint">
  929. 将对已选 <strong>5</strong> 条记录提交<strong>相同</strong>审核结果(不逐条展示证据)。
  930. </p>
  931. <table class="mini-table">
  932. <thead>
  933. <tr>
  934. <th>样本编码</th>
  935. <th>入库编码</th>
  936. <th>样本名称</th>
  937. </tr>
  938. </thead>
  939. <tbody id="unifiedMiniTbody"></tbody>
  940. </table>
  941. <div class="field">
  942. <label>审核结论</label>
  943. <div class="radios">
  944. <label><input type="radio" name="qb-conc" value="tg" checked /> 通过</label>
  945. <label><input type="radio" name="qb-conc" value="btg" /> 不通过</label>
  946. </div>
  947. </div>
  948. <div class="field">
  949. <label>备注</label>
  950. <textarea placeholder="可选,将写入全部已选记录"></textarea>
  951. </div>
  952. <div class="field">
  953. <label>附件</label>
  954. <div class="upload-row">
  955. <div class="upload-card">
  956. <span style="font-size: 20px">+</span>
  957. <span>上传附件</span>
  958. </div>
  959. </div>
  960. </div>
  961. </div>
  962. </div>
  963. <div class="dialog-foot" id="queueFoot">
  964. <span class="spacer" id="queueFootHint">建议:有疑义的样本逐条看证据;同质样本可套用剩余。</span>
  965. <button type="button" class="btn">取消</button>
  966. <button type="button" class="btn btn--ghost-danger" id="btnSkip">跳过本条</button>
  967. <button type="button" class="btn btn--primary" id="btnQueuePrimary">
  968. ✓ 提交本条并下一条
  969. </button>
  970. </div>
  971. </div>
  972. <!-- ========== Scheme B ========== -->
  973. <div class="dialog dialog--unified" id="dlg-unified" role="dialog" aria-label="批量审核-统一">
  974. <div class="dialog-head">
  975. <div class="dialog-head__row">
  976. <div>
  977. <h2>批量审核(5 条)</h2>
  978. <p class="sub">与当前线上一致:一份结论写入全部已选记录。</p>
  979. </div>
  980. <button type="button" class="icon-btn" aria-label="关闭">✕</button>
  981. </div>
  982. </div>
  983. <div class="dialog-body" style="overflow: auto">
  984. <div class="unified-body">
  985. <p class="target-hint">
  986. 将对已选 <strong>5</strong> 条记录提交相同审核结果。
  987. </p>
  988. <table class="mini-table">
  989. <thead>
  990. <tr>
  991. <th>#</th>
  992. <th>样本编码</th>
  993. <th>入库编码</th>
  994. <th>名称</th>
  995. </tr>
  996. </thead>
  997. <tbody id="schemeBTbody"></tbody>
  998. </table>
  999. <div class="field">
  1000. <label>审核结论</label>
  1001. <div class="radios">
  1002. <label><input type="radio" name="b-conc" value="tg" checked /> 通过</label>
  1003. <label><input type="radio" name="b-conc" value="btg" /> 不通过</label>
  1004. </div>
  1005. </div>
  1006. <div class="field">
  1007. <label>备注</label>
  1008. <textarea placeholder="可选"></textarea>
  1009. </div>
  1010. <div class="field">
  1011. <label>附件</label>
  1012. <div class="upload-row">
  1013. <div class="upload-card">
  1014. <span style="font-size: 20px">+</span>
  1015. <span>上传附件</span>
  1016. </div>
  1017. </div>
  1018. </div>
  1019. </div>
  1020. </div>
  1021. <div class="dialog-foot">
  1022. <button type="button" class="btn">取消</button>
  1023. <button type="button" class="btn btn--primary" data-toast="已批量提交 5 条(Demo)">
  1024. ✓ 确定
  1025. </button>
  1026. </div>
  1027. </div>
  1028. <!-- ========== Scheme C ========== -->
  1029. <div class="dialog dialog--table" id="dlg-table" role="dialog" aria-label="批量审核-表格">
  1030. <div class="dialog-head">
  1031. <div class="dialog-head__row">
  1032. <div>
  1033. <h2>批量审核(5 条)</h2>
  1034. <p class="sub">表格内逐行结论;顶栏可对勾选行统一填结论后批量提交。</p>
  1035. </div>
  1036. <button type="button" class="icon-btn" aria-label="关闭">✕</button>
  1037. </div>
  1038. </div>
  1039. <div class="dialog-body table-body">
  1040. <div class="batch-bar">
  1041. <span class="label">批量填入</span>
  1042. <div class="radios" style="height: auto">
  1043. <label><input type="radio" name="c-bulk" value="tg" checked /> 通过</label>
  1044. <label><input type="radio" name="c-bulk" value="btg" /> 不通过</label>
  1045. </div>
  1046. <button type="button" class="btn" id="btnFillRows" style="height: 32px">
  1047. 填入已勾选行
  1048. </button>
  1049. <span style="font-size: 12px; color: var(--text-muted)"
  1050. >也可点「展开」看证据后单独改结论</span
  1051. >
  1052. </div>
  1053. <table class="data-table">
  1054. <thead>
  1055. <tr>
  1056. <th style="width: 40px"><input type="checkbox" id="checkAll" checked /></th>
  1057. <th>样本编码</th>
  1058. <th>入库编码</th>
  1059. <th>质检</th>
  1060. <th>生产</th>
  1061. <th>结论</th>
  1062. <th style="width: 100px">操作</th>
  1063. </tr>
  1064. </thead>
  1065. <tbody id="schemeCTbody"></tbody>
  1066. </table>
  1067. </div>
  1068. <div class="dialog-foot">
  1069. <span class="spacer">未填结论的行提交时将提示补全</span>
  1070. <button type="button" class="btn">取消</button>
  1071. <button type="button" class="btn btn--primary" data-toast="已按行结论提交(Demo)">
  1072. ✓ 提交全部
  1073. </button>
  1074. </div>
  1075. </div>
  1076. </div>
  1077. </div>
  1078. </div>
  1079. <div class="toast" id="toast"></div>
  1080. <script>
  1081. const SAMPLES = [
  1082. {
  1083. id: '1',
  1084. code: 'YP-CS-20260819-001',
  1085. lab: 'RK-2026-0819-003',
  1086. name: '脐带血样本 A',
  1087. qc: '已完成',
  1088. prod: '已完成'
  1089. },
  1090. {
  1091. id: '2',
  1092. code: 'YP-CS-20260819-002',
  1093. lab: 'RK-2026-0819-004',
  1094. name: '脐带血样本 B',
  1095. qc: '已完成',
  1096. prod: '已完成'
  1097. },
  1098. {
  1099. id: '3',
  1100. code: 'YP-CS-20260818-015',
  1101. lab: 'RK-2026-0818-021',
  1102. name: '胎盘组织 C',
  1103. qc: '已完成',
  1104. prod: '已完成'
  1105. },
  1106. {
  1107. id: '4',
  1108. code: 'YP-RD-20260817-008',
  1109. lab: 'RK-2026-0817-011',
  1110. name: '捐赠样本 D',
  1111. qc: '已完成',
  1112. prod: '已完成'
  1113. },
  1114. {
  1115. id: '5',
  1116. code: 'YP-CS-20260816-022',
  1117. lab: 'RK-2026-0816-030',
  1118. name: '脐带血样本 E',
  1119. qc: '已完成',
  1120. prod: '已完成'
  1121. }
  1122. ]
  1123. const notes = {
  1124. queue:
  1125. '<strong>A 推荐:</strong>左侧待审队列 → 中间复用单条审核的证据区 → 右侧结论表单。默认「提交本条并下一条」;勾选「将结论套用到剩余未审项」即可一键批量。与现有单条审核体验连续。',
  1126. unified:
  1127. '<strong>B 现状:</strong>窄弹窗 + 统一结论。效率最高,但看不到各样本证据,误审风险大;适合「已在列表/查看里确认过、结论必然一致」的场景。建议在清单上增加已选摘要表(本 Demo 已补)。',
  1128. table:
  1129. '<strong>C 表格行内:</strong>一屏看到全部已选;顶栏批量填结论,行内可展开证据。适合条数不多(≤10)且需要「大部分相同、个别例外」的微调。证据区复用成本较高。'
  1130. }
  1131. let activeIdx = 0
  1132. const decisions = SAMPLES.map(() => null) // null | 'tg' | 'btg'
  1133. function toast(msg) {
  1134. const el = document.getElementById('toast')
  1135. el.textContent = msg
  1136. el.classList.add('is-show')
  1137. clearTimeout(toast._t)
  1138. toast._t = setTimeout(() => el.classList.remove('is-show'), 2200)
  1139. }
  1140. function doneCount() {
  1141. return decisions.filter(Boolean).length
  1142. }
  1143. function renderQueue() {
  1144. const list = document.getElementById('queueList')
  1145. list.innerHTML = SAMPLES.map((s, i) => {
  1146. const d = decisions[i]
  1147. const st =
  1148. d === 'tg'
  1149. ? 'status-dot--ok">已通过'
  1150. : d === 'btg'
  1151. ? 'status-dot--bad">不通过'
  1152. : 'status-dot--pending">待审'
  1153. return `<button type="button" class="queue-item ${i === activeIdx ? 'is-active' : ''} ${d ? 'is-done' : ''}" data-idx="${i}">
  1154. <div class="queue-item__code">${s.code}</div>
  1155. <div class="queue-item__meta">
  1156. <span>${s.lab}</span>
  1157. <span class="status-dot ${st}</span>
  1158. </div>
  1159. </button>`
  1160. }).join('')
  1161. document.getElementById('queueProgress').textContent = `${activeIdx + 1} / ${SAMPLES.length}`
  1162. const left = SAMPLES.length - doneCount()
  1163. document.getElementById('formProgress').textContent =
  1164. `正在审核第 ${activeIdx + 1} 条 · 还剩 ${Math.max(left - (decisions[activeIdx] ? 0 : 1), 0)} 条未审(已完成 ${doneCount()})`
  1165. const s = SAMPLES[activeIdx]
  1166. document.getElementById('evLab').textContent = s.lab
  1167. document.getElementById('evCode').textContent = s.code
  1168. }
  1169. function fillMiniTables() {
  1170. const rows = SAMPLES.map(
  1171. (s, i) =>
  1172. `<tr><td>${s.code}</td><td>${s.lab}</td><td>${s.name}</td></tr>`
  1173. ).join('')
  1174. document.getElementById('unifiedMiniTbody').innerHTML = rows
  1175. document.getElementById('schemeBTbody').innerHTML = SAMPLES.map(
  1176. (s, i) =>
  1177. `<tr><td>${i + 1}</td><td>${s.code}</td><td>${s.lab}</td><td>${s.name}</td></tr>`
  1178. ).join('')
  1179. }
  1180. function renderSchemeC() {
  1181. const tb = document.getElementById('schemeCTbody')
  1182. tb.innerHTML = SAMPLES.map((s, i) => {
  1183. return `
  1184. <tr data-row="${i}">
  1185. <td><input type="checkbox" class="row-check" checked /></td>
  1186. <td class="mono">${s.code}</td>
  1187. <td>${s.lab}</td>
  1188. <td><span class="tag tag--success">${s.qc}</span></td>
  1189. <td><span class="tag tag--success">${s.prod}</span></td>
  1190. <td>
  1191. <select class="row-conc" style="height:32px;border:1px solid var(--border);border-radius:6px;padding:0 8px;font:inherit;font-size:12px">
  1192. <option value="">未填</option>
  1193. <option value="tg">通过</option>
  1194. <option value="btg">不通过</option>
  1195. </select>
  1196. </td>
  1197. <td class="row-actions">
  1198. <button type="button" class="link-btn expand-btn" data-idx="${i}">展开</button>
  1199. </td>
  1200. </tr>
  1201. <tr class="expand-panel" id="exp-${i}">
  1202. <td colspan="7">
  1203. <div class="expand-inner">
  1204. <div>
  1205. <div class="pane-title">证据摘要 · ${s.code}</div>
  1206. <div class="verdict" style="margin:0 0 8px">
  1207. <span class="chip chip--ok">接收:接收</span>
  1208. <span class="chip chip--ok">质检:${s.qc}</span>
  1209. <span class="chip chip--ok">生产:${s.prod}</span>
  1210. </div>
  1211. <p style="margin:0;font-size:12px;color:var(--text-muted)">此处可嵌入与单条审核相同的折叠证据块(接收 / 质检 / 生产)。Demo 仅示意布局。</p>
  1212. </div>
  1213. <div>
  1214. <div class="field" style="margin:0">
  1215. <label>行备注</label>
  1216. <textarea rows="3" placeholder="仅本条"></textarea>
  1217. </div>
  1218. </div>
  1219. </div>
  1220. </td>
  1221. </tr>`
  1222. }).join('')
  1223. }
  1224. function showScheme(name) {
  1225. document.querySelectorAll('.scheme-btn').forEach((b) => {
  1226. b.classList.toggle('is-active', b.dataset.scheme === name)
  1227. })
  1228. document.getElementById('schemeNote').innerHTML = notes[name]
  1229. document.getElementById('dlg-queue').classList.toggle('is-show', name === 'queue')
  1230. document.getElementById('dlg-unified').classList.toggle('is-show', name === 'unified')
  1231. document.getElementById('dlg-table').classList.toggle('is-show', name === 'table')
  1232. }
  1233. function updatePrimaryBtn() {
  1234. const apply = document.getElementById('applyRest').checked
  1235. const btn = document.getElementById('btnQueuePrimary')
  1236. const left = SAMPLES.length - doneCount()
  1237. if (apply) {
  1238. btn.textContent = `✓ 批量提交剩余(${left || SAMPLES.length})`
  1239. } else {
  1240. btn.textContent =
  1241. activeIdx >= SAMPLES.length - 1 && decisions.every(Boolean)
  1242. ? '✓ 完成并关闭'
  1243. : '✓ 提交本条并下一条'
  1244. }
  1245. }
  1246. document.querySelectorAll('.scheme-btn').forEach((btn) => {
  1247. btn.addEventListener('click', () => showScheme(btn.dataset.scheme))
  1248. })
  1249. document.querySelectorAll('.mode-tab').forEach((tab) => {
  1250. tab.addEventListener('click', () => {
  1251. document.querySelectorAll('.mode-tab').forEach((t) => t.classList.remove('is-active'))
  1252. tab.classList.add('is-active')
  1253. const per = tab.dataset.mode === 'per'
  1254. document.getElementById('queue-per-body').style.display = per ? '' : 'none'
  1255. document.getElementById('queue-batch-body').style.display = per ? 'none' : ''
  1256. const foot = document.getElementById('queueFoot')
  1257. document.getElementById('btnSkip').style.display = per ? '' : 'none'
  1258. document.getElementById('queueFootHint').textContent = per
  1259. ? '建议:有疑义的样本逐条看证据;同质样本可套用剩余。'
  1260. : '统一模式:一份结论写入全部 5 条,不等同于逐条核对。'
  1261. document.getElementById('btnQueuePrimary').textContent = per
  1262. ? '✓ 提交本条并下一条'
  1263. : '✓ 统一提交 5 条'
  1264. if (!per) document.getElementById('applyRest').checked = false
  1265. })
  1266. })
  1267. document.getElementById('queueList').addEventListener('click', (e) => {
  1268. const item = e.target.closest('.queue-item')
  1269. if (!item) return
  1270. activeIdx = Number(item.dataset.idx)
  1271. renderQueue()
  1272. updatePrimaryBtn()
  1273. })
  1274. document.getElementById('applyRest').addEventListener('change', updatePrimaryBtn)
  1275. document.getElementById('btnSkip').addEventListener('click', () => {
  1276. if (activeIdx < SAMPLES.length - 1) {
  1277. activeIdx += 1
  1278. renderQueue()
  1279. updatePrimaryBtn()
  1280. toast('已跳过,进入下一条')
  1281. } else {
  1282. toast('已是最后一条')
  1283. }
  1284. })
  1285. document.getElementById('btnQueuePrimary').addEventListener('click', () => {
  1286. const modePer = document.querySelector('.mode-tab.is-active').dataset.mode === 'per'
  1287. if (!modePer) {
  1288. toast('已统一提交 5 条(Demo)')
  1289. return
  1290. }
  1291. const apply = document.getElementById('applyRest').checked
  1292. const conc = document.querySelector('input[name="q-conc"]:checked').value
  1293. if (apply) {
  1294. for (let i = 0; i < SAMPLES.length; i++) {
  1295. if (!decisions[i]) decisions[i] = conc
  1296. }
  1297. renderQueue()
  1298. updatePrimaryBtn()
  1299. toast(`已将结论套用并提交剩余项(Demo)`)
  1300. return
  1301. }
  1302. decisions[activeIdx] = conc
  1303. if (activeIdx < SAMPLES.length - 1) {
  1304. activeIdx += 1
  1305. toast(`第 ${activeIdx} 条已记录,继续下一条`)
  1306. } else {
  1307. toast('全部处理完毕(Demo)')
  1308. }
  1309. renderQueue()
  1310. updatePrimaryBtn()
  1311. })
  1312. document.getElementById('schemeCTbody').addEventListener('click', (e) => {
  1313. const btn = e.target.closest('.expand-btn')
  1314. if (!btn) return
  1315. const panel = document.getElementById('exp-' + btn.dataset.idx)
  1316. const open = panel.classList.toggle('is-open')
  1317. btn.textContent = open ? '收起' : '展开'
  1318. })
  1319. document.getElementById('btnFillRows').addEventListener('click', () => {
  1320. const v = document.querySelector('input[name="c-bulk"]:checked').value
  1321. document.querySelectorAll('#schemeCTbody tr[data-row]').forEach((tr) => {
  1322. const checked = tr.querySelector('.row-check').checked
  1323. if (checked) tr.querySelector('.row-conc').value = v
  1324. })
  1325. toast('已填入已勾选行(Demo)')
  1326. })
  1327. document.getElementById('checkAll').addEventListener('change', (e) => {
  1328. document.querySelectorAll('.row-check').forEach((c) => {
  1329. c.checked = e.target.checked
  1330. })
  1331. })
  1332. document.querySelectorAll('[data-toast]').forEach((btn) => {
  1333. btn.addEventListener('click', () => toast(btn.dataset.toast))
  1334. })
  1335. fillMiniTables()
  1336. renderQueue()
  1337. renderSchemeC()
  1338. updatePrimaryBtn()
  1339. </script>
  1340. </body>
  1341. </html>