web_exam.html 136 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=1280">
  6. <title>实验室安全教育与考试 — 管理后台</title>
  7. <style>
  8. *{box-sizing:border-box;margin:0;padding:0;}
  9. :root{
  10. --blue:#1857d4;--blue-deep:#0f3ea8;--blue-mid:#2e6fe0;--blue-light:#eef3fd;--blue-xlight:#f5f8ff;
  11. --green:#10a37f;--green-dark:#0d8f6f;--green-light:#edfaf6;
  12. --orange:#e67e22;--orange-light:#fef6ec;
  13. --red:#e53e3e;--red-light:#fff5f5;
  14. --gray:#8896a7;--gray-light:#f4f6fa;--gray-border:#dde3ed;
  15. --text-primary:#1a2233;--text-secondary:#4a5568;--text-hint:#8896a7;
  16. --bg:#f0f4fb;--white:#ffffff;
  17. --radius:8px;--shadow:0 2px 12px rgba(24,87,212,.08);--shadow-md:0 4px 20px rgba(24,87,212,.12);
  18. --sidebar-w:260px;--header-h:64px;
  19. }
  20. body{font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--bg);color:var(--text-primary);font-size:15px;display:flex;flex-direction:column;height:100vh;overflow:hidden;}
  21. /* ===== HEADER ===== */
  22. .admin-header{height:var(--header-h);background:linear-gradient(135deg,#1857d4 0%,#2e6fe0 100%);display:flex;align-items:center;padding:0 28px;gap:20px;flex-shrink:0;z-index:200;box-shadow:0 2px 8px rgba(24,87,212,.2);}
  23. .header-logo{display:flex;align-items:center;gap:12px;color:#fff;font-size:17px;font-weight:700;letter-spacing:.3px;min-width:240px;}
  24. .header-logo-icon{width:36px;height:36px;background:rgba(255,255,255,.2);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:18px;}
  25. .header-tabs{display:flex;gap:4px;flex:1;}
  26. .header-tab{color:rgba(255,255,255,.75);font-size:14px;padding:7px 18px;border-radius:6px;cursor:pointer;transition:all .18s;font-weight:500;white-space:nowrap;}
  27. .header-tab:hover{color:#fff;background:rgba(255,255,255,.12);}
  28. .header-tab.active{color:#fff;background:rgba(255,255,255,.2);font-weight:600;}
  29. .header-right{display:flex;align-items:center;gap:14px;margin-left:auto;}
  30. .header-avatar{width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.2);border:2px solid rgba(255,255,255,.35);display:flex;align-items:center;justify-content:center;font-size:15px;cursor:pointer;}
  31. .header-username{color:rgba(255,255,255,.9);font-size:14px;}
  32. /* ===== LAYOUT ===== */
  33. .admin-body{display:flex;flex:1;overflow:hidden;}
  34. .sidebar{width:var(--sidebar-w);background:var(--white);border-right:1px solid var(--gray-border);overflow-y:auto;flex-shrink:0;padding:16px 0;}
  35. .sidebar::-webkit-scrollbar{width:4px;}
  36. .sidebar::-webkit-scrollbar-thumb{background:rgba(24,87,212,.15);border-radius:2px;}
  37. .main-content{flex:1;overflow-y:auto;padding:32px 40px;}
  38. .main-content::-webkit-scrollbar{width:7px;}
  39. .main-content::-webkit-scrollbar-thumb{background:rgba(24,87,212,.15);border-radius:4px;}
  40. /* ===== SIDEBAR ===== */
  41. .nav-group{margin-bottom:6px;}
  42. .nav-group-label{font-size:11px;color:var(--text-hint);font-weight:600;letter-spacing:.8px;padding:10px 20px 4px;text-transform:uppercase;}
  43. .nav-item{display:flex;align-items:center;gap:10px;padding:10px 20px;cursor:pointer;color:var(--text-secondary);font-size:14px;font-weight:500;transition:all .15s;border-left:3px solid transparent;margin:1px 0;}
  44. .nav-item:hover{background:var(--blue-xlight);color:var(--blue);}
  45. .nav-item.active{background:var(--blue-light);color:var(--blue);border-left-color:var(--blue);font-weight:600;}
  46. .nav-item .nav-icon{font-size:16px;width:20px;text-align:center;}
  47. .nav-sub{padding-left:20px;}
  48. .nav-sub .nav-item{font-size:13px;padding:8px 16px;}
  49. /* ===== PAGE SYSTEM ===== */
  50. .page{display:none;}.page.active{display:block;}
  51. /* ===== BREADCRUMB ===== */
  52. .breadcrumb{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text-hint);margin-bottom:24px;}
  53. .breadcrumb a{color:var(--blue);cursor:pointer;text-decoration:none;}
  54. .breadcrumb a:hover{text-decoration:underline;}
  55. .breadcrumb-sep{color:var(--gray-border);}
  56. /* ===== PAGE HEADER ===== */
  57. .page-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;}
  58. .page-title{font-size:22px;font-weight:700;color:var(--text-primary);}
  59. .page-subtitle{font-size:14px;color:var(--text-hint);margin-top:4px;}
  60. /* ===== CARDS ===== */
  61. .card{background:var(--white);border:1px solid var(--gray-border);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow);}
  62. .card-title{font-size:16px;font-weight:700;color:var(--text-primary);display:flex;align-items:center;gap:8px;margin-bottom:18px;}
  63. .card-title::before{content:'';width:3px;height:16px;background:linear-gradient(180deg,var(--blue-mid),var(--blue-deep));border-radius:2px;display:inline-block;}
  64. /* ===== STAT BOXES ===== */
  65. .stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-bottom:24px;}
  66. .stat-box{background:var(--white);border:1px solid var(--gray-border);border-radius:var(--radius);padding:20px 24px;box-shadow:var(--shadow);}
  67. .stat-box-icon{font-size:32px;margin-bottom:10px;}
  68. .stat-box-val{font-size:32px;font-weight:700;color:var(--text-primary);line-height:1;}
  69. .stat-box-label{font-size:13px;color:var(--text-hint);margin-top:8px;}
  70. .stat-box.blue{border-top:3px solid var(--blue);}
  71. .stat-box.green{border-top:3px solid var(--green);}
  72. .stat-box.orange{border-top:3px solid var(--orange);}
  73. .stat-box.red{border-top:3px solid var(--red);}
  74. /* ===== TAGS ===== */
  75. .tag{display:inline-flex;align-items:center;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:500;}
  76. .tag-blue{background:var(--blue-light);color:var(--blue);}
  77. .tag-green{background:var(--green-light);color:var(--green-dark);}
  78. .tag-orange{background:var(--orange-light);color:var(--orange);}
  79. .tag-red{background:var(--red-light);color:var(--red);}
  80. .tag-gray{background:var(--gray-light);color:var(--gray);}
  81. /* ===== BTNS ===== */
  82. .btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;border:none;outline:none;transition:all .18s;padding:0 16px;height:36px;}
  83. .btn:active{opacity:.85;}
  84. .btn-primary{background:linear-gradient(135deg,var(--blue-mid) 0%,var(--blue-deep) 100%);color:#fff;box-shadow:0 2px 8px rgba(24,87,212,.25);}
  85. .btn-green{background:linear-gradient(135deg,#12b48a 0%,var(--green-dark) 100%);color:#fff;}
  86. .btn-outline{background:var(--white);color:var(--blue);border:1px solid rgba(24,87,212,.3);}
  87. .btn-outline-gray{background:var(--white);color:var(--text-secondary);border:1px solid var(--gray-border);}
  88. .btn-sm{height:30px;font-size:12px;padding:0 12px;border-radius:5px;}
  89. .btn-danger{background:var(--white);color:var(--red);border:1px solid rgba(229,62,62,.3);}
  90. .btn-icon{width:30px;height:30px;padding:0;border-radius:5px;}
  91. /* ===== TABLE ===== */
  92. .table-wrap{overflow-x:auto;border-radius:var(--radius);border:1px solid var(--gray-border);}
  93. table{width:100%;border-collapse:collapse;font-size:14px;}
  94. thead tr{background:var(--gray-light);}
  95. th{text-align:left;padding:12px 16px;color:var(--text-hint);font-weight:600;font-size:13px;border-bottom:1px solid var(--gray-border);white-space:nowrap;}
  96. td{padding:14px 16px;border-bottom:1px solid var(--gray-border);color:var(--text-primary);vertical-align:middle;}
  97. tbody tr:last-child td{border-bottom:none;}
  98. tbody tr:hover{background:var(--blue-xlight);}
  99. /* ===== FORM ===== */
  100. .form-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;}
  101. .form-grid.cols-1{grid-template-columns:1fr;}
  102. .form-grid.cols-3{grid-template-columns:1fr 1fr 1fr;}
  103. .form-item{display:flex;flex-direction:column;gap:7px;}
  104. .form-item.col-2{grid-column:span 2;}
  105. .form-label{font-size:14px;color:var(--text-secondary);font-weight:500;}
  106. .form-label.required::after{content:'*';color:var(--red);margin-left:3px;}
  107. .form-input{width:100%;border:1px solid var(--gray-border);border-radius:6px;padding:9px 14px;font-size:14px;color:var(--text-primary);background:var(--white);outline:none;transition:border-color .2s;}
  108. .form-input:focus{border-color:var(--blue);}
  109. textarea.form-input{min-height:110px;resize:vertical;line-height:1.6;}
  110. .form-select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238c8c8c' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 10px center;}
  111. .form-hint{font-size:12px;color:var(--text-hint);}
  112. /* ===== TOGGLE ===== */
  113. .toggle{position:relative;width:44px;height:24px;cursor:pointer;}
  114. .toggle input{opacity:0;width:0;height:0;}
  115. .toggle-slider{position:absolute;inset:0;background:#ccc;border-radius:24px;transition:.3s;}
  116. .toggle-slider::before{content:'';position:absolute;width:18px;height:18px;left:3px;top:3px;background:white;border-radius:50%;transition:.3s;}
  117. .toggle input:checked+.toggle-slider{background:var(--blue);}
  118. .toggle input:checked+.toggle-slider::before{transform:translateX(20px);}
  119. /* ===== SEARCH BAR ===== */
  120. .search-bar{display:flex;gap:10px;align-items:center;margin-bottom:20px;flex-wrap:wrap;}
  121. .search-input-wrap{position:relative;flex:1;min-width:240px;}
  122. .search-input-wrap .search-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-hint);font-size:14px;}
  123. .search-input{width:100%;border:1px solid var(--gray-border);border-radius:6px;padding:8px 14px 8px 36px;font-size:14px;outline:none;background:var(--white);}
  124. .search-input:focus{border-color:var(--blue);}
  125. /* ===== PAGINATION ===== */
  126. .pagination{display:flex;align-items:center;gap:4px;margin-top:20px;justify-content:flex-end;}
  127. .page-btn{min-width:34px;height:34px;border-radius:5px;border:1px solid var(--gray-border);background:var(--white);cursor:pointer;font-size:13px;display:inline-flex;align-items:center;justify-content:center;color:var(--text-secondary);}
  128. .page-btn:hover{border-color:var(--blue);color:var(--blue);}
  129. .page-btn.active{background:var(--blue);color:#fff;border-color:var(--blue);}
  130. .page-info{font-size:13px;color:var(--text-hint);margin-left:10px;}
  131. /* ===== TABS ===== */
  132. .view-tabs{display:flex;gap:0;border-bottom:1px solid var(--gray-border);margin-bottom:24px;}
  133. .view-tab{padding:10px 20px;cursor:pointer;font-size:14px;color:var(--text-hint);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .15s;font-weight:500;}
  134. .view-tab.active{color:var(--blue);border-bottom-color:var(--blue);font-weight:600;}
  135. /* ===== DETAIL PANEL ===== */
  136. .detail-section{margin-bottom:28px;}
  137. .detail-section-title{font-size:13px;font-weight:600;color:var(--text-hint);text-transform:uppercase;letter-spacing:.6px;margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid var(--gray-border);}
  138. .info-row{display:flex;padding:10px 0;border-bottom:1px solid var(--gray-border);}
  139. .info-row:last-child{border-bottom:none;}
  140. .info-key{width:130px;color:var(--text-hint);font-size:14px;flex-shrink:0;}
  141. .info-val{flex:1;font-size:14px;color:var(--text-primary);font-weight:500;}
  142. /* ===== UPLOAD AREA ===== */
  143. .upload-area{border:2px dashed var(--gray-border);border-radius:8px;padding:28px;text-align:center;cursor:pointer;transition:all .2s;background:var(--gray-light);}
  144. .upload-area:hover{border-color:var(--blue);background:var(--blue-xlight);}
  145. .upload-icon{font-size:36px;margin-bottom:10px;}
  146. .upload-text{font-size:14px;color:var(--text-hint);}
  147. /* ===== RICH TEXT PLACEHOLDER ===== */
  148. .rich-editor{border:1px solid var(--gray-border);border-radius:6px;overflow:hidden;}
  149. .rich-toolbar{background:var(--gray-light);padding:8px 14px;display:flex;gap:6px;border-bottom:1px solid var(--gray-border);flex-wrap:wrap;}
  150. .rich-tool-btn{background:var(--white);border:1px solid var(--gray-border);border-radius:4px;padding:3px 9px;font-size:13px;cursor:pointer;color:var(--text-secondary);}
  151. .rich-tool-btn:hover{background:var(--blue-light);color:var(--blue);border-color:rgba(24,87,212,.3);}
  152. .rich-body{min-height:130px;padding:14px;font-size:14px;color:var(--text-primary);outline:none;line-height:1.7;}
  153. /* ===== QR CODE ===== */
  154. .qr-box{width:80px;height:80px;background:var(--white);border:2px solid var(--gray-border);border-radius:8px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:24px;cursor:pointer;transition:all .2s;}
  155. .qr-box:hover{border-color:var(--blue);box-shadow:0 2px 8px rgba(24,87,212,.15);}
  156. /* ===== ATTENDANCE ===== */
  157. .attend-row{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--gray-border);}
  158. .attend-row:last-child{border-bottom:none;}
  159. .attend-avatar{width:38px;height:38px;border-radius:50%;background:var(--blue-light);display:flex;align-items:center;justify-content:center;font-size:17px;flex-shrink:0;}
  160. .attend-info{flex:1;}
  161. .attend-status-yes{color:var(--green);font-size:12px;font-weight:600;}
  162. .attend-status-no{color:var(--red);font-size:12px;font-weight:600;}
  163. .attend-status-late{color:var(--orange);font-size:12px;font-weight:600;}
  164. /* ===== TRAINING EVENT CARD (PC version) ===== */
  165. .training-event-card{background:var(--white);border:1px solid var(--gray-border);border-radius:var(--radius);overflow:hidden;cursor:pointer;transition:all .2s;box-shadow:var(--shadow);}
  166. .training-event-card:hover{box-shadow:var(--shadow-md);border-color:rgba(24,87,212,.2);transform:translateY(-2px);}
  167. .training-event-cover{width:100%;height:160px;background:linear-gradient(135deg,#1857d4,#2e6fe0);display:flex;align-items:center;justify-content:center;font-size:42px;position:relative;overflow:hidden;}
  168. .training-event-body{padding:16px;}
  169. .training-event-title{font-size:15px;font-weight:600;color:var(--text-primary);margin-bottom:10px;line-height:1.4;}
  170. .training-event-meta{font-size:13px;color:var(--text-hint);display:flex;flex-direction:column;gap:5px;}
  171. .training-events-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;}
  172. /* ===== MODAL ===== */
  173. .modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:500;align-items:center;justify-content:center;padding:20px;}
  174. .modal-overlay.active{display:flex;}
  175. .modal-box{background:var(--white);border-radius:12px;width:100%;max-width:600px;max-height:90vh;overflow-y:auto;animation:modalIn .22s ease;box-shadow:0 8px 40px rgba(24,87,212,.18);}
  176. .modal-box.modal-lg{max-width:900px;}
  177. .modal-header{padding:22px 28px 18px;border-bottom:1px solid var(--gray-border);display:flex;align-items:center;justify-content:space-between;}
  178. .modal-title{font-size:17px;font-weight:700;color:var(--text-primary);}
  179. .modal-close{cursor:pointer;font-size:20px;color:var(--text-hint);background:none;border:none;line-height:1;padding:4px;}
  180. .modal-close:hover{color:var(--text-primary);}
  181. .modal-body{padding:24px 28px;}
  182. .modal-footer{padding:18px 28px;border-top:1px solid var(--gray-border);display:flex;align-items:center;justify-content:flex-end;gap:10px;}
  183. @keyframes modalIn{from{transform:scale(.93) translateY(10px);opacity:0;}to{transform:scale(1) translateY(0);opacity:1;}}
  184. /* ===== MISC ===== */
  185. .mt8{margin-top:8px;}.mt12{margin-top:12px;}.mt16{margin-top:16px;}.mt20{margin-top:20px;}
  186. .mb8{margin-bottom:8px;}.mb12{margin-bottom:12px;}.mb16{margin-bottom:16px;}
  187. .flex{display:flex;}.flex-col{flex-direction:column;}.items-center{align-items:center;}
  188. .justify-between{justify-content:space-between;}.gap8{gap:8px;}.gap12{gap:12px;}.gap16{gap:16px;}
  189. .text-sm{font-size:13px;}.text-xs{font-size:12px;}.text-bold{font-weight:600;}.text-gray{color:var(--gray);}
  190. .text-blue{color:var(--blue);}.text-green{color:var(--green);}.text-orange{color:var(--orange);}.text-red{color:var(--red);}
  191. .grid-2{display:grid;grid-template-columns:1fr 1fr;gap:20px;}
  192. .divider{height:1px;background:var(--gray-border);margin:20px 0;}
  193. .empty-state{text-align:center;padding:60px 20px;color:var(--text-hint);}
  194. .empty-icon{font-size:52px;margin-bottom:14px;}
  195. .dot{width:8px;height:8px;border-radius:50%;display:inline-block;}
  196. .dot-green{background:var(--green);}.dot-orange{background:var(--orange);}.dot-red{background:var(--red);}.dot-gray{background:#b0bec5;}
  197. .progress-wrap{background:#e8edf6;border-radius:4px;height:6px;overflow:hidden;margin-top:4px;}
  198. .progress-bar{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--blue-mid),var(--blue));transition:width .4s;}
  199. /* ===== PUBLISH TOGGLE (table-safe) ===== */
  200. .pub-slider{position:absolute;inset:0;border-radius:24px;transition:.3s;cursor:pointer;}
  201. .pub-slider::before{content:'';position:absolute;width:18px;height:18px;left:3px;top:3px;background:white;border-radius:50%;transition:.3s;box-shadow:0 1px 3px rgba(0,0,0,.2);}
  202. .pub-on{background:var(--blue);}
  203. .pub-off{background:#ccc;}
  204. .pub-on::before{transform:translateX(20px);}
  205. </style>
  206. </head>
  207. <body>
  208. <!-- ===== HEADER ===== -->
  209. <div class="admin-header">
  210. <div class="header-logo">
  211. <div class="header-logo-icon">🔬</div>
  212. <span>实验室安全教育与考试</span>
  213. </div>
  214. <div class="header-right">
  215. <div style="color:rgba(255,255,255,.7);font-size:12px;">2026-07-31</div>
  216. <div class="header-username">管理员</div>
  217. <div class="header-avatar">👤</div>
  218. </div>
  219. </div>
  220. <!-- ===== BODY ===== -->
  221. <div class="admin-body">
  222. <!-- ===== SIDEBAR ===== -->
  223. <div class="sidebar" id="sidebar">
  224. <div class="nav-group">
  225. <div class="nav-group-label">培训管理</div>
  226. <div class="nav-item active" onclick="showPage('page-training-plan')"><span class="nav-icon">📋</span>培训计划</div>
  227. <div class="nav-item" onclick="showPage('page-training-record')"><span class="nav-icon">📁</span>培训记录</div>
  228. </div>
  229. <div class="nav-group">
  230. <div class="nav-group-label">培训活动</div>
  231. <div class="nav-item" onclick="showPage('page-training-activity-list')"><span class="nav-icon">📅</span>培训活动</div>
  232. </div>
  233. <div class="nav-group">
  234. <div class="nav-group-label">考试管理</div>
  235. <div class="nav-item" onclick="showPage('page-exam-template')"><span class="nav-icon">📋</span>考试模版</div>
  236. <div class="nav-item"><span class="nav-icon">📝</span>考试计划</div>
  237. <div class="nav-item"><span class="nav-icon">📊</span>考试记录</div>
  238. </div>
  239. <div class="nav-group">
  240. <div class="nav-group-label">用户管理</div>
  241. <div class="nav-item"><span class="nav-icon">👥</span>用户列表</div>
  242. </div>
  243. <div class="nav-group">
  244. <div class="nav-group-label">实验室管理</div>
  245. <div class="nav-item"><span class="nav-icon">🔬</span>实验室列表</div>
  246. </div>
  247. </div>
  248. <!-- ===== MAIN CONTENT ===== -->
  249. <div class="main-content">
  250. <!-- ==================== PAGE: 培训计划 ==================== -->
  251. <div class="page active" id="page-training-plan">
  252. <div class="breadcrumb"><span>培训管理</span><span class="breadcrumb-sep">/</span><span>培训计划</span></div>
  253. <div class="page-header">
  254. <div><div class="page-title">培训计划</div><div class="page-subtitle">管理所有培训活动的计划与发布状态</div></div>
  255. <button class="btn btn-primary" onclick="showPage('page-training-new')">➕ 新建培训</button>
  256. </div>
  257. <div class="search-bar">
  258. <div class="search-input-wrap"><span class="search-icon">🔍</span><input class="search-input" placeholder="搜索培训名称、主讲人..."></div>
  259. <select class="form-input form-select" style="width:130px;"><option>全部状态</option><option>进行中</option><option>即将开始</option><option>已结束</option></select>
  260. <select class="form-input form-select" style="width:130px;"><option>全部分类</option><option>知识讲座</option><option>应急演练</option></select>
  261. <input class="form-input" type="text" placeholder="组织单位" style="width:140px;">
  262. <div class="flex items-center gap8" style="flex-shrink:0;">
  263. <input class="form-input" type="date" style="width:150px;" title="开始日期">
  264. <span style="color:var(--text-hint);font-size:13px;">至</span>
  265. <input class="form-input" type="date" style="width:150px;" title="结束日期">
  266. </div>
  267. <button class="btn btn-outline">筛选</button>
  268. <button class="btn btn-outline-gray">重置</button>
  269. </div>
  270. <div id="plan-teacher-view">
  271. <div class="table-wrap">
  272. <table>
  273. <thead><tr><th>培训名称</th><th>分类</th><th>培训时间</th><th>组织单位</th><th>学时/积分</th><th>活动状态</th><th>签到人数</th><th>签到状态</th><th>发布</th><th>操作</th></tr></thead>
  274. <tbody>
  275. <tr>
  276. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">实验室生物安全操作规范培训</span></td>
  277. <td><span class="tag tag-blue">知识讲座</span></td>
  278. <td style="white-space:nowrap;">2026-08-01<br><span class="text-xs text-gray">10:00–12:00</span></td>
  279. <td>生命科学学院</td>
  280. <td><span class="text-green text-bold">2学时 +10分</span></td>
  281. <td><span class="tag tag-orange">进行中</span></td>
  282. <td><div><span class="text-bold text-blue">42</span> / 60</div><div class="progress-wrap" style="width:80px;margin-top:4px;"><div class="progress-bar" style="width:70%;"></div></div></td>
  283. <td><div class="flex" style="gap:4px;flex-wrap:wrap;"><span class="tag tag-green" style="font-size:10px;">已签到 42</span><span class="tag tag-red" style="font-size:10px;">未到 18</span></div></td>
  284. <td><div style="width:44px;height:24px;position:relative;cursor:pointer;"><input type="checkbox" checked style="opacity:0;width:0;height:0;position:absolute;"><div class="pub-slider pub-on"></div></div></td>
  285. <td><div class="flex gap8" style="flex-wrap:wrap;"><button class="btn btn-sm btn-outline" onclick="showModal('modal-qr')">📷 签到码</button><button class="btn btn-sm btn-outline" onclick="showModal('modal-progress')">📊 进度</button><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-new')">✏️ 编辑</button><button class="btn btn-sm btn-danger" onclick="showModal('modal-confirm-delete')">🗑 删除</button></div></td>
  286. </tr>
  287. <tr>
  288. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">化学品泄漏应急演练2026</span></td>
  289. <td><span class="tag tag-orange">应急演练</span></td>
  290. <td style="white-space:nowrap;">2026-08-05<br><span class="text-xs text-gray">14:00–17:00</span></td>
  291. <td>化学化工学院</td>
  292. <td><span class="text-green text-bold">3学时 +15分</span></td>
  293. <td><span class="tag tag-blue">即将开始</span></td>
  294. <td><div><span class="text-bold text-orange">0</span> / 50</div><div class="progress-wrap" style="width:80px;margin-top:4px;"><div class="progress-bar" style="width:0%;"></div></div></td>
  295. <td><span class="tag tag-gray" style="font-size:10px;">未开始</span></td>
  296. <td><div style="width:44px;height:24px;position:relative;cursor:pointer;"><input type="checkbox" style="opacity:0;width:0;height:0;position:absolute;"><div class="pub-slider pub-off"></div></div></td>
  297. <td><div class="flex gap8" style="flex-wrap:wrap;"><button class="btn btn-sm btn-outline" onclick="showModal('modal-qr')">📷 签到码</button><button class="btn btn-sm btn-outline" onclick="showModal('modal-progress')">📊 进度</button><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-new')">✏️ 编辑</button><button class="btn btn-sm btn-danger" onclick="showModal('modal-confirm-delete')">🗑 删除</button></div></td>
  298. </tr>
  299. <tr>
  300. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">辐射安全基础知识讲座</span></td>
  301. <td><span class="tag tag-blue">知识讲座</span></td>
  302. <td style="white-space:nowrap;">2026-07-20<br><span class="text-xs text-gray">14:00–16:00</span></td>
  303. <td>物理学院</td>
  304. <td><span class="text-green text-bold">2学时 +10分</span></td>
  305. <td><span class="tag tag-gray">已结束</span></td>
  306. <td><div><span class="text-bold text-green">55</span> / 60</div><div class="progress-wrap" style="width:80px;margin-top:4px;"><div class="progress-bar" style="width:92%;background:linear-gradient(90deg,var(--green),var(--green-dark));"></div></div></td>
  307. <td><div class="flex" style="gap:4px;flex-wrap:wrap;"><span class="tag tag-green" style="font-size:10px;">已签到 55</span><span class="tag tag-red" style="font-size:10px;">未到 5</span></div></td>
  308. <td><div style="width:44px;height:24px;position:relative;cursor:pointer;"><input type="checkbox" checked style="opacity:0;width:0;height:0;position:absolute;"><div class="pub-slider pub-on"></div></div></td>
  309. <td><div class="flex gap8" style="flex-wrap:wrap;"><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-record')">查看记录</button><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-new')">✏️ 编辑</button><button class="btn btn-sm btn-danger" onclick="showModal('modal-confirm-delete')">🗑 删除</button></div></td>
  310. </tr>
  311. <tr>
  312. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">全校实验室安全通识培训2026</span></td>
  313. <td><span class="tag tag-blue">知识讲座</span></td>
  314. <td style="white-space:nowrap;">2026-09-01<br><span class="text-xs text-gray">09:00–11:00</span></td>
  315. <td>测试大学</td>
  316. <td><span class="text-green text-bold">2学时 +10分</span></td>
  317. <td><span class="tag tag-blue">即将开始</span></td>
  318. <td><div><span class="text-bold text-gray">0</span> / —</div><div class="progress-wrap" style="width:80px;margin-top:4px;"><div class="progress-bar" style="width:0%;"></div></div></td>
  319. <td><span class="tag tag-gray" style="font-size:10px;">未开始</span></td>
  320. <td><div style="width:44px;height:24px;position:relative;cursor:pointer;"><input type="checkbox" style="opacity:0;width:0;height:0;position:absolute;"><div class="pub-slider pub-off"></div></div></td>
  321. <td><div class="flex gap8" style="flex-wrap:wrap;"><button class="btn btn-sm btn-outline" onclick="showModal('modal-qr')">📷 签到码</button><button class="btn btn-sm btn-outline" onclick="showModal('modal-progress')">📊 进度</button><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-new')">✏️ 编辑</button><button class="btn btn-sm btn-danger" onclick="showModal('modal-confirm-delete')">🗑 删除</button></div></td>
  322. </tr>
  323. </tbody>
  324. </table>
  325. </div>
  326. </div>
  327. <div class="pagination">
  328. <button class="page-btn">«</button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">3</button><button class="page-btn">»</button>
  329. <span class="page-info">共 18 条</span>
  330. </div>
  331. </div>
  332. <!-- ==================== PAGE: 新建培训 ==================== -->
  333. <div class="page" id="page-training-new">
  334. <div class="breadcrumb"><span>培训管理</span><span class="breadcrumb-sep">/</span><span>新建培训</span></div>
  335. <div class="page-header">
  336. <div><div class="page-title">新建培训</div><div class="page-subtitle">填写培训活动信息</div></div>
  337. <div class="flex gap8">
  338. <button class="btn btn-outline-gray" onclick="showPage('page-training-plan')">取消</button>
  339. <button class="btn btn-primary">保存</button>
  340. </div>
  341. </div>
  342. <div class="card">
  343. <div class="card-title">基本信息</div>
  344. <div class="form-grid">
  345. <div class="form-item col-2">
  346. <label class="form-label required">活动名称</label>
  347. <input class="form-input" placeholder="请输入培训活动名称">
  348. </div>
  349. <div class="form-item">
  350. <label class="form-label required">活动分类</label>
  351. <select class="form-input form-select"><option value="">请选择</option><option>知识讲座</option><option>应急演练</option></select>
  352. </div>
  353. <div class="form-item">
  354. <label class="form-label required">组织单位</label>
  355. <input class="form-input" placeholder="请输入组织单位">
  356. </div>
  357. <div class="form-item">
  358. <label class="form-label required">培训开始时间</label>
  359. <input class="form-input" type="datetime-local" step="1">
  360. </div>
  361. <div class="form-item">
  362. <label class="form-label required">培训结束时间</label>
  363. <input class="form-input" type="datetime-local" step="1">
  364. </div>
  365. <div class="form-item">
  366. <label class="form-label required">主讲人</label>
  367. <input class="form-input" placeholder="请输入主讲人姓名">
  368. </div>
  369. <div class="form-item">
  370. <label class="form-label required">培训地点</label>
  371. <input class="form-input" placeholder="请输入培训地点">
  372. </div>
  373. <div class="form-item">
  374. <label class="form-label required">可得学时</label>
  375. <input class="form-input" type="number" placeholder="例:2" min="0.5" step="0.5">
  376. </div>
  377. <div class="form-item">
  378. <label class="form-label required">可得积分</label>
  379. <input class="form-input" type="number" placeholder="例:10" min="0" step="1">
  380. </div>
  381. <div class="form-item col-2">
  382. <label class="form-label required">培训范围</label>
  383. <div class="flex gap8" style="margin-bottom:10px;">
  384. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:7px 14px;border:1.5px solid var(--gray-border);border-radius:6px;font-size:13px;transition:all .15s;" id="scope-label-all" onclick="setScopeMode('all')">
  385. <input type="radio" name="training-scope" value="all" checked style="accent-color:var(--blue);"> 全校
  386. </label>
  387. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:7px 14px;border:1.5px solid var(--gray-border);border-radius:6px;font-size:13px;transition:all .15s;" id="scope-label-dept" onclick="setScopeMode('dept')">
  388. <input type="radio" name="training-scope" value="dept" style="accent-color:var(--blue);"> 指定范围
  389. </label>
  390. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;padding:7px 14px;border:1.5px solid var(--gray-border);border-radius:6px;font-size:13px;transition:all .15s;" id="scope-label-person" onclick="setScopeMode('person')">
  391. <input type="radio" name="training-scope" value="person" style="accent-color:var(--blue);"> 指定人员
  392. </label>
  393. </div>
  394. <!-- 指定范围:院系/专业 -->
  395. <div id="scope-dept-section" style="display:none;padding:14px;background:var(--blue-xlight);border-radius:8px;border:1px solid rgba(24,87,212,.12);">
  396. <div style="font-size:12px;color:var(--text-hint);margin-bottom:10px;">选择参与的院系 / 单位(可多选)</div>
  397. <div style="display:flex;flex-wrap:wrap;gap:8px;">
  398. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">生命科学学院</label>
  399. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">化学化工学院</label>
  400. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">物理学院</label>
  401. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">材料科学学院</label>
  402. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">医学院</label>
  403. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">环境科学学院</label>
  404. <label style="display:flex;align-items:center;gap:5px;font-size:13px;cursor:pointer;padding:4px 10px;border:1px solid var(--gray-border);border-radius:20px;background:var(--white);"><input type="checkbox" style="accent-color:var(--blue);">安全管理处</label>
  405. </div>
  406. </div>
  407. <!-- 指定人员 -->
  408. <div id="scope-person-section" style="display:none;padding:14px;background:var(--blue-xlight);border-radius:8px;border:1px solid rgba(24,87,212,.12);">
  409. <div style="font-size:12px;color:var(--text-hint);margin-bottom:10px;">输入学号或姓名搜索添加,或批量导入</div>
  410. <div class="flex gap8" style="margin-bottom:10px;">
  411. <div class="search-input-wrap" style="flex:1;">
  412. <span class="search-icon">🔍</span>
  413. <input class="search-input" placeholder="搜索学号 / 姓名添加人员...">
  414. </div>
  415. <button class="btn btn-outline" style="white-space:nowrap;">📥 批量导入</button>
  416. </div>
  417. <div style="min-height:60px;border:1px dashed var(--gray-border);border-radius:6px;padding:10px;background:var(--white);">
  418. <div style="display:flex;flex-wrap:wrap;gap:6px;">
  419. <span style="display:inline-flex;align-items:center;gap:5px;background:var(--blue-light);color:var(--blue);border-radius:20px;padding:3px 10px;font-size:12px;">张明远 <span style="cursor:pointer;opacity:.6;" onclick="this.parentElement.remove()">✕</span></span>
  420. <span style="display:inline-flex;align-items:center;gap:5px;background:var(--blue-light);color:var(--blue);border-radius:20px;padding:3px 10px;font-size:12px;">李晓燕 <span style="cursor:pointer;opacity:.6;" onclick="this.parentElement.remove()">✕</span></span>
  421. <span style="font-size:12px;color:var(--text-hint);align-self:center;">已添加 2 人</span>
  422. </div>
  423. </div>
  424. </div>
  425. </div>
  426. </div>
  427. <div class="divider"></div>
  428. <div class="card-title">封面图片</div>
  429. <div class="upload-area" style="max-width:400px;">
  430. <div class="upload-icon">🖼️</div>
  431. <div class="upload-text">点击或拖拽上传封面图<br><span style="font-size:11px;">推荐尺寸 16:9,支持 JPG / PNG,最大 5MB</span></div>
  432. </div>
  433. <div class="divider"></div>
  434. <div class="card-title">培训详情</div>
  435. <div class="rich-editor">
  436. <div class="rich-toolbar">
  437. <button class="rich-tool-btn">B</button><button class="rich-tool-btn"><i>I</i></button><button class="rich-tool-btn">U</button>
  438. <button class="rich-tool-btn">H1</button><button class="rich-tool-btn">H2</button>
  439. <button class="rich-tool-btn">≡</button><button class="rich-tool-btn">•</button>
  440. <button class="rich-tool-btn">🔗</button><button class="rich-tool-btn">🖼</button><button class="rich-tool-btn">📊</button>
  441. </div>
  442. <div class="rich-body" contenteditable="true">请输入培训详情内容,可包含培训目标、培训内容大纲、注意事项等...</div>
  443. </div>
  444. <div class="divider"></div>
  445. <div class="card-title">附件上传</div>
  446. <div class="upload-area">
  447. <div class="upload-icon">📎</div>
  448. <div class="upload-text">点击上传培训相关文件(PPT、PDF、Word等)<br><span style="font-size:11px;">支持 PDF / PPT / PPTX / DOC / DOCX,单文件最大 50MB</span></div>
  449. </div>
  450. <div class="divider"></div>
  451. <div style="display:flex;flex-direction:column;gap:16px;">
  452. <div class="form-item">
  453. <label class="form-label">发布状态</label>
  454. <div class="flex items-center gap8">
  455. <label class="toggle"><input type="checkbox" id="publish-toggle"><span class="toggle-slider"></span></label>
  456. <span style="font-size:13px;color:var(--text-secondary);">开启后培训学员可见</span>
  457. </div>
  458. </div>
  459. <div class="form-item">
  460. <label class="form-label">颁发证书</label>
  461. <div class="flex items-center gap8" style="margin-bottom:10px;">
  462. <label class="toggle"><input type="checkbox" id="cert-toggle" onchange="toggleCertTemplate(this)"><span class="toggle-slider"></span></label>
  463. <span style="font-size:13px;color:var(--text-secondary);">开启后完成培训的学员可获得培训证书</span>
  464. </div>
  465. <div id="cert-template-section" style="display:none;margin-top:8px;padding:14px;background:var(--blue-xlight);border-radius:8px;border:1px solid rgba(24,87,212,.12);">
  466. <label class="form-label" style="margin-bottom:8px;">证书模板</label>
  467. <select class="form-input form-select" style="max-width:360px;">
  468. <option value="">请选择证书模板</option>
  469. <option>实验室安全培训证书(通用)</option>
  470. <option>生物安全专项培训证书</option>
  471. <option>化学品安全培训证书</option>
  472. <option>应急演练参与证书</option>
  473. </select>
  474. <div class="form-hint" style="margin-top:6px;">证书将自动填入学员姓名、培训名称、培训日期及学时信息</div>
  475. </div>
  476. </div>
  477. </div>
  478. </div>
  479. </div>
  480. <!-- ==================== PAGE: 培训记录 ==================== -->
  481. <div class="page" id="page-training-record">
  482. <div class="breadcrumb"><span>培训管理</span><span class="breadcrumb-sep">/</span><span>培训记录</span></div>
  483. <div class="page-header">
  484. <div><div class="page-title">培训记录</div><div class="page-subtitle">已结束培训的参与记录与证明</div></div>
  485. <button class="btn btn-outline">📤 导出记录</button>
  486. </div>
  487. <div class="search-bar">
  488. <div class="search-input-wrap"><span class="search-icon">🔍</span><input class="search-input" placeholder="搜索培训名称、学员姓名..."></div>
  489. <select class="form-input form-select" style="width:130px;"><option>全部分类</option><option>知识讲座</option><option>应急演练</option></select>
  490. <input class="form-input" type="month" style="width:160px;" value="2026-07">
  491. <button class="btn btn-outline">筛选</button>
  492. </div>
  493. <div class="card mb16">
  494. <div class="card-title">已结束培训列表</div>
  495. <div class="table-wrap">
  496. <table>
  497. <thead><tr><th>培训名称</th><th>分类</th><th>培训时间</th><th>主讲人</th><th>应到人数</th><th>实到人数</th><th>签到率</th><th>操作</th></tr></thead>
  498. <tbody>
  499. <tr>
  500. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showModal('modal-record-detail')">辐射安全基础知识讲座</span></td>
  501. <td><span class="tag tag-blue">知识讲座</span></td>
  502. <td style="white-space:nowrap;">2026-07-20<br><span class="text-xs text-gray">14:00–16:00</span></td>
  503. <td>陈教授</td>
  504. <td>60</td>
  505. <td><span class="text-bold text-green">55</span></td>
  506. <td>
  507. <div>91.7%</div>
  508. <div class="progress-wrap" style="width:80px;"><div class="progress-bar" style="width:92%;background:linear-gradient(90deg,var(--green),var(--green-dark));"></div></div>
  509. </td>
  510. <td><div class="flex gap8"><button class="btn btn-sm btn-outline" onclick="showModal('modal-record-detail')">查看详情</button><button class="btn btn-sm btn-outline-gray">导出</button></div></td>
  511. </tr>
  512. <tr>
  513. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showModal('modal-record-detail')">生物安全法规与合规操作</span></td>
  514. <td><span class="tag tag-blue">知识讲座</span></td>
  515. <td style="white-space:nowrap;">2026-07-10<br><span class="text-xs text-gray">09:00–11:00</span></td>
  516. <td>刘研究员</td>
  517. <td>45</td>
  518. <td><span class="text-bold text-green">43</span></td>
  519. <td>
  520. <div>95.6%</div>
  521. <div class="progress-wrap" style="width:80px;"><div class="progress-bar" style="width:96%;background:linear-gradient(90deg,var(--green),var(--green-dark));"></div></div>
  522. </td>
  523. <td><div class="flex gap8"><button class="btn btn-sm btn-outline" onclick="showModal('modal-record-detail')">查看详情</button><button class="btn btn-sm btn-outline-gray">导出</button></div></td>
  524. </tr>
  525. <tr>
  526. <td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showModal('modal-record-detail')">消防疏散应急演练(A区)</span></td>
  527. <td><span class="tag tag-orange">应急演练</span></td>
  528. <td style="white-space:nowrap;">2026-06-25<br><span class="text-xs text-gray">15:00–17:30</span></td>
  529. <td>安全部</td>
  530. <td>120</td>
  531. <td><span class="text-bold text-blue">98</span></td>
  532. <td>
  533. <div>81.7%</div>
  534. <div class="progress-wrap" style="width:80px;"><div class="progress-bar" style="width:82%;"></div></div>
  535. </td>
  536. <td><div class="flex gap8"><button class="btn btn-sm btn-outline" onclick="showModal('modal-record-detail')">查看详情</button><button class="btn btn-sm btn-outline-gray">导出</button></div></td>
  537. </tr>
  538. </tbody>
  539. </table>
  540. </div>
  541. <div class="pagination">
  542. <button class="page-btn">«</button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">»</button>
  543. <span class="page-info">共 9 条</span>
  544. </div>
  545. </div>
  546. </div>
  547. <!-- PAGE: 培训活动列表 -->
  548. <div class="page" id="page-training-activity-list">
  549. <div class="breadcrumb"><span>培训活动</span></div>
  550. <div class="page-header">
  551. <div><div class="page-title">培训活动</div><div class="page-subtitle">全部培训活动列表</div></div>
  552. <button class="btn btn-primary" onclick="showPage('page-training-new');switchMenu('training-mgmt')">+ 新建培训</button>
  553. </div>
  554. <div class="search-bar">
  555. <div class="search-input-wrap"><span class="search-icon">&#128269;</span><input class="search-input" placeholder="搜索培训名称、主讲人..."></div>
  556. <select class="form-input form-select" style="width:130px;"><option>全部状态</option><option>进行中</option><option>即将开始</option><option>报名中</option><option>已结束</option></select>
  557. <select class="form-input form-select" style="width:130px;"><option>全部分类</option><option>知识讲座</option><option>应急演练</option></select>
  558. <button class="btn btn-outline">筛选</button>
  559. <div style="margin-left:auto;display:flex;gap:8px;">
  560. <button class="btn btn-outline-gray" onclick="setListView('grid')">卡片视图</button>
  561. <button class="btn btn-outline-gray" onclick="setListView('list')">列表视图</button>
  562. </div>
  563. </div>
  564. <div id="activity-grid-view" class="training-events-grid">
  565. <div class="training-event-card" onclick="showPage('page-training-activity-detail')">
  566. <div class="training-event-cover" style="background:linear-gradient(135deg,#1857d4,#2e6fe0);">&#129514;<div style="position:absolute;top:10px;left:10px;"><span class="tag tag-orange" style="background:rgba(230,126,34,.9);color:#fff;">进行中</span></div><div style="position:absolute;bottom:10px;right:10px;background:rgba(255,255,255,.15);border-radius:6px;padding:4px 8px;font-size:11px;color:rgba(255,255,255,.9);">2学时</div></div>
  567. <div class="training-event-body"><div class="training-event-title">实验室生物安全操作规范培训</div><div class="training-event-meta"><div>2026-08-01 10:00-12:00</div><div>生命科学楼 B206</div><div class="flex items-center gap8 mt8"><span class="tag tag-blue">知识讲座</span><span class="text-xs text-gray">42/60人</span><span class="text-xs text-green">+2学时+10积分</span></div></div></div>
  568. </div>
  569. <div class="training-event-card" onclick="showPage('page-training-activity-detail')">
  570. <div class="training-event-cover" style="background:linear-gradient(135deg,#e67e22,#f39c12);">&#128666;<div style="position:absolute;top:10px;left:10px;"><span class="tag" style="background:rgba(229,62,62,.9);color:#fff;">即将开始</span></div><div style="position:absolute;bottom:10px;right:10px;background:rgba(255,255,255,.15);border-radius:6px;padding:4px 8px;font-size:11px;color:rgba(255,255,255,.9);">3学时</div></div>
  571. <div class="training-event-body"><div class="training-event-title">化学品泄漏应急演练2026</div><div class="training-event-meta"><div>2026-08-05 14:00-17:00</div><div>化学实验楼 A101</div><div class="flex items-center gap8 mt8"><span class="tag tag-orange">应急演练</span><span class="text-xs text-gray">28/50人</span><span class="text-xs text-green">+3学时+15积分</span></div></div></div>
  572. </div>
  573. <div class="training-event-card" onclick="showPage('page-training-activity-detail')">
  574. <div class="training-event-cover" style="background:linear-gradient(135deg,#10a37f,#0d8f6f);">&#9889;<div style="position:absolute;top:10px;left:10px;"><span class="tag tag-green" style="background:rgba(16,163,127,.9);color:#fff;">报名中</span></div><div style="position:absolute;bottom:10px;right:10px;background:rgba(255,255,255,.15);border-radius:6px;padding:4px 8px;font-size:11px;color:rgba(255,255,255,.9);">1.5学时</div></div>
  575. <div class="training-event-body"><div class="training-event-title">实验室用电安全与消防知识讲座</div><div class="training-event-meta"><div>2026-08-10 09:30-11:00</div><div>综合楼 3F 多功能厅</div><div class="flex items-center gap8 mt8"><span class="tag tag-blue">知识讲座</span><span class="text-xs text-gray">15/80人</span><span class="text-xs text-green">+1.5学时+8积分</span></div></div></div>
  576. </div>
  577. <div class="training-event-card" onclick="showPage('page-training-activity-detail')">
  578. <div class="training-event-cover" style="background:linear-gradient(135deg,#6f42c1,#5a35a0);">&#9762;<div style="position:absolute;top:10px;left:10px;"><span class="tag tag-gray" style="background:rgba(136,150,167,.9);color:#fff;">已结束</span></div><div style="position:absolute;bottom:10px;right:10px;background:rgba(255,255,255,.15);border-radius:6px;padding:4px 8px;font-size:11px;color:rgba(255,255,255,.9);">2学时</div></div>
  579. <div class="training-event-body"><div class="training-event-title">辐射安全基础知识讲座</div><div class="training-event-meta"><div>2026-07-20 14:00-16:00</div><div>物理楼 C301</div><div class="flex items-center gap8 mt8"><span class="tag tag-blue">知识讲座</span><span class="text-xs text-green text-bold">55/60人签到</span></div></div></div>
  580. </div>
  581. <div class="training-event-card" onclick="showPage('page-training-activity-detail')">
  582. <div class="training-event-cover" style="background:linear-gradient(135deg,#e53e3e,#c53030);">&#128293;<div style="position:absolute;top:10px;left:10px;"><span class="tag tag-gray" style="background:rgba(136,150,167,.9);color:#fff;">已结束</span></div><div style="position:absolute;bottom:10px;right:10px;background:rgba(255,255,255,.15);border-radius:6px;padding:4px 8px;font-size:11px;color:rgba(255,255,255,.9);">2.5学时</div></div>
  583. <div class="training-event-body"><div class="training-event-title">消防疏散应急演练(A区)</div><div class="training-event-meta"><div>2026-06-25 15:00-17:30</div><div>A区操场及实验楼</div><div class="flex items-center gap8 mt8"><span class="tag tag-orange">应急演练</span><span class="text-xs text-blue text-bold">98/120人签到</span></div></div></div>
  584. </div>
  585. </div>
  586. <div id="activity-list-view" style="display:none;">
  587. <div class="table-wrap"><table>
  588. <thead><tr><th>培训名称</th><th>分类</th><th>培训时间</th><th>主讲人</th><th>组织单位</th><th>地点</th><th>学时/积分</th><th>签到</th><th>状态</th><th>操作</th></tr></thead>
  589. <tbody>
  590. <tr><td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">实验室生物安全操作规范培训</span></td><td><span class="tag tag-blue">知识讲座</span></td><td>2026-08-01</td><td>李教授</td><td>生命科学学院</td><td>生命科学楼 B206</td><td><span class="text-green text-bold">2学时+10分</span></td><td>42/60</td><td><span class="tag tag-orange">进行中</span></td><td><button class="btn btn-sm btn-outline" onclick="showPage('page-training-activity-detail')">详情</button></td></tr>
  591. <tr><td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">化学品泄漏应急演练2026</span></td><td><span class="tag tag-orange">应急演练</span></td><td>2026-08-05</td><td>王老师</td><td>化学化工学院</td><td>化学楼 A101</td><td><span class="text-green text-bold">3学时+15分</span></td><td>28/50</td><td><span class="tag tag-blue">即将开始</span></td><td><button class="btn btn-sm btn-outline" onclick="showPage('page-training-activity-detail')">详情</button></td></tr>
  592. <tr><td><span class="text-blue" style="cursor:pointer;font-weight:500;" onclick="showPage('page-training-activity-detail')">辐射安全基础知识讲座</span></td><td><span class="tag tag-blue">知识讲座</span></td><td>2026-07-20</td><td>陈教授</td><td>物理学院</td><td>物理楼 C301</td><td><span class="text-green text-bold">2学时+10分</span></td><td>55/60</td><td><span class="tag tag-gray">已结束</span></td><td><button class="btn btn-sm btn-outline-gray" onclick="showPage('page-training-record');switchMenu('training-mgmt')">记录</button></td></tr>
  593. </tbody>
  594. </table></div>
  595. </div>
  596. <div class="pagination" style="margin-top:20px;">
  597. <button class="page-btn">«</button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">»</button>
  598. <span class="page-info">共 12 条</span>
  599. </div>
  600. </div>
  601. <!-- PAGE: 培训活动详情 -->
  602. <div class="page" id="page-training-activity-detail">
  603. <div class="breadcrumb"><a onclick="showPage('page-training-activity-list');switchMenu('training-activity')">培训活动</a><span class="breadcrumb-sep">/</span><span>培训详情</span></div>
  604. <div style="display:flex;gap:20px;align-items:flex-start;">
  605. <div style="flex:1;">
  606. <div class="card mb16">
  607. <div style="background:linear-gradient(135deg,#1857d4,#2e6fe0);border-radius:8px;height:200px;display:flex;align-items:center;justify-content:center;font-size:64px;margin-bottom:20px;position:relative;">&#129514;<div style="position:absolute;top:14px;left:14px;"><span class="tag tag-orange" style="background:rgba(230,126,34,.9);color:#fff;">进行中</span></div><div style="position:absolute;bottom:14px;right:14px;background:rgba(255,255,255,.15);border-radius:8px;padding:6px 12px;color:rgba(255,255,255,.95);font-size:13px;">2学时</div></div>
  608. <div style="font-size:22px;font-weight:700;margin-bottom:12px;">实验室生物安全操作规范培训</div>
  609. <div style="display:flex;gap:8px;margin-bottom:16px;"><span class="tag tag-blue">知识讲座</span><span class="tag tag-green">+2学时</span><span class="tag tag-orange">+10积分</span></div>
  610. <div class="divider"></div>
  611. <div class="detail-section">
  612. <div class="detail-section-title">活动信息</div>
  613. <div class="info-row"><div class="info-key">培训时间</div><div class="info-val">2026-08-01 10:00 – 12:00</div></div>
  614. <div class="info-row"><div class="info-key">培训地点</div><div class="info-val">生命科学楼 B206 报告厅</div></div>
  615. <div class="info-row"><div class="info-key">主讲人</div><div class="info-val">李教授(生命科学学院)</div></div>
  616. <div class="info-row"><div class="info-key">组织单位</div><div class="info-val">生命科学学院 安全教育办公室</div></div>
  617. <div class="info-row"><div class="info-key">培训时长</div><div class="info-val">2 学时(120分钟)</div></div>
  618. <div class="info-row"><div class="info-key">可获学时</div><div class="info-val"><span class="text-green text-bold">2 学时</span></div></div>
  619. <div class="info-row"><div class="info-key">可获积分</div><div class="info-val"><span class="text-orange text-bold">10 积分</span></div></div>
  620. </div>
  621. <div class="divider"></div>
  622. <div class="detail-section">
  623. <div class="detail-section-title">培训详情</div>
  624. <div style="font-size:14px;color:var(--text-secondary);line-height:1.8;">
  625. <p style="margin-bottom:10px;">本次培训旨在强化实验室人员生物安全意识,规范日常实验操作,降低生物安全风险。</p>
  626. <p style="margin-bottom:8px;font-weight:600;color:var(--text-primary);">培训内容:</p>
  627. <ul style="padding-left:20px;display:flex;flex-direction:column;gap:6px;"><li>生物安全基本概念与法律法规解读</li><li>实验室生物安全等级介绍</li><li>个人防护装备(PPE)的正确使用</li><li>生物危险废弃物的分类处置</li><li>意外事故应急处理流程</li></ul>
  628. </div>
  629. </div>
  630. </div>
  631. </div>
  632. <div style="width:300px;flex-shrink:0;">
  633. <div class="card mb16">
  634. <div class="card-title">签到二维码</div>
  635. <div style="text-align:center;padding:16px 0;">
  636. <div style="width:140px;height:140px;background:var(--gray-light);border:2px solid var(--gray-border);border-radius:8px;display:flex;flex-direction:column;align-items:center;justify-content:center;margin:0 auto 12px;font-size:48px;cursor:pointer;" onclick="showModal('modal-qr')">&#9640;<div style="font-size:11px;color:var(--text-hint);margin-top:4px;">点击放大</div></div>
  637. <button class="btn btn-primary" style="width:100%;margin-bottom:8px;" onclick="showModal('modal-qr')">显示签到码</button>
  638. <button class="btn btn-outline" style="width:100%;">下载二维码</button>
  639. </div>
  640. </div>
  641. <div class="card mb16">
  642. <div class="card-title">签到统计</div>
  643. <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px;">
  644. <div style="background:var(--blue-xlight);border-radius:6px;padding:10px;text-align:center;"><div style="font-size:22px;font-weight:700;color:var(--blue);">42</div><div class="text-xs text-gray">已签到</div></div>
  645. <div style="background:var(--red-light);border-radius:6px;padding:10px;text-align:center;"><div style="font-size:22px;font-weight:700;color:var(--red);">18</div><div class="text-xs text-gray">未签到</div></div>
  646. <div style="background:var(--green-light);border-radius:6px;padding:10px;text-align:center;"><div style="font-size:22px;font-weight:700;color:var(--green);">60</div><div class="text-xs text-gray">应到</div></div>
  647. <div style="background:var(--orange-light);border-radius:6px;padding:10px;text-align:center;"><div style="font-size:22px;font-weight:700;color:var(--orange);">70%</div><div class="text-xs text-gray">签到率</div></div>
  648. </div>
  649. <div class="progress-wrap" style="height:8px;"><div class="progress-bar" style="width:70%;"></div></div>
  650. </div>
  651. <div class="card">
  652. <div class="card-title">签到人员</div>
  653. <div style="max-height:280px;overflow-y:auto;">
  654. <div class="attend-row"><div class="attend-avatar">&#128100;</div><div class="attend-info"><div style="font-size:13px;font-weight:500;">张明远</div><div class="text-xs text-gray">20231001</div></div><span class="attend-status-yes">10:05</span></div>
  655. <div class="attend-row"><div class="attend-avatar">&#128100;</div><div class="attend-info"><div style="font-size:13px;font-weight:500;">李晓燕</div><div class="text-xs text-gray">20221056</div></div><span class="attend-status-yes">10:02</span></div>
  656. <div class="attend-row"><div class="attend-avatar">&#128100;</div><div class="attend-info"><div style="font-size:13px;font-weight:500;">王浩然</div><div class="text-xs text-gray">20231089</div></div><span class="attend-status-late">迟到10:22</span></div>
  657. <div class="attend-row"><div class="attend-avatar">&#128100;</div><div class="attend-info"><div style="font-size:13px;font-weight:500;">陈思雨</div><div class="text-xs text-gray">20241103</div></div><span class="attend-status-no">未签到</span></div>
  658. </div>
  659. <button class="btn btn-outline" style="width:100%;margin-top:12px;">查看全部 60 人</button>
  660. </div>
  661. </div>
  662. </div>
  663. </div>
  664. <!-- ==================== MODALS ==================== -->
  665. <!-- QR Code Modal -->
  666. <div class="modal-overlay" id="modal-qr">
  667. <div class="modal-box" style="max-width:360px;">
  668. <div class="modal-header">
  669. <div class="modal-title">&#128247; 签到二维码</div>
  670. <button class="modal-close" onclick="hideModal('modal-qr')">&#10005;</button>
  671. </div>
  672. <div class="modal-body" style="text-align:center;">
  673. <div style="font-size:15px;font-weight:600;margin-bottom:4px;">实验室生物安全操作规范培训</div>
  674. <div style="font-size:12px;color:var(--text-hint);margin-bottom:16px;">2026-08-01 10:00–12:00</div>
  675. <div style="width:200px;height:200px;background:var(--gray-light);border:3px solid var(--gray-border);border-radius:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;margin:0 auto 16px;font-size:80px;">&#9640;</div>
  676. <div style="background:var(--blue-xlight);border-radius:8px;padding:10px;font-size:12px;color:var(--text-secondary);margin-bottom:16px;">
  677. 学员扫描此二维码完成签到<br>
  678. <span style="color:var(--orange);font-weight:500;">有效期至 2026-08-01 12:30</span>
  679. </div>
  680. <div class="flex gap8" style="justify-content:center;">
  681. <button class="btn btn-primary">&#11015; 下载二维码</button>
  682. <button class="btn btn-outline" onclick="hideModal('modal-qr')">关闭</button>
  683. </div>
  684. </div>
  685. </div>
  686. </div>
  687. <!-- Progress Modal -->
  688. <div class="modal-overlay" id="modal-progress">
  689. <div class="modal-box modal-lg">
  690. <div class="modal-header">
  691. <div class="modal-title">&#128202; 培训进度详情</div>
  692. <button class="modal-close" onclick="hideModal('modal-progress')">&#10005;</button>
  693. </div>
  694. <div class="modal-body">
  695. <div style="font-size:15px;font-weight:600;margin-bottom:12px;">实验室生物安全操作规范培训</div>
  696. <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px;">
  697. <div style="background:var(--blue-xlight);border-radius:8px;padding:12px;text-align:center;"><div style="font-size:24px;font-weight:700;color:var(--blue);">60</div><div class="text-xs text-gray">计划人数</div></div>
  698. <div style="background:var(--green-light);border-radius:8px;padding:12px;text-align:center;"><div style="font-size:24px;font-weight:700;color:var(--green);">42</div><div class="text-xs text-gray">已签到</div></div>
  699. <div style="background:var(--red-light);border-radius:8px;padding:12px;text-align:center;"><div style="font-size:24px;font-weight:700;color:var(--red);">18</div><div class="text-xs text-gray">未签到</div></div>
  700. <div style="background:var(--orange-light);border-radius:8px;padding:12px;text-align:center;"><div style="font-size:24px;font-weight:700;color:var(--orange);">70%</div><div class="text-xs text-gray">签到率</div></div>
  701. </div>
  702. <div class="progress-wrap" style="height:10px;margin-bottom:20px;"><div class="progress-bar" style="width:70%;"></div></div>
  703. <div class="table-wrap">
  704. <table>
  705. <thead><tr><th>姓名</th><th>学号</th><th>院系</th><th>签到状态</th><th>签到时间</th><th>获得学时</th><th>获得积分</th></tr></thead>
  706. <tbody>
  707. <tr><td>张明远</td><td>20231001</td><td>生命科学学院</td><td><span class="tag tag-green">已签到</span></td><td>10:05</td><td>2</td><td>10</td></tr>
  708. <tr><td>李晓燕</td><td>20221056</td><td>生命科学学院</td><td><span class="tag tag-green">已签到</span></td><td>10:02</td><td>2</td><td>10</td></tr>
  709. <tr><td>王浩然</td><td>20231089</td><td>化学化工学院</td><td><span class="tag tag-orange">迟到</span></td><td>10:22</td><td>2</td><td>8</td></tr>
  710. <tr><td>陈思雨</td><td>20241103</td><td>物理学院</td><td><span class="tag tag-red">未签到</span></td><td>—</td><td>0</td><td>0</td></tr>
  711. <tr><td>刘志远</td><td>20231055</td><td>生命科学学院</td><td><span class="tag tag-green">已签到</span></td><td>09:58</td><td>2</td><td>10</td></tr>
  712. </tbody>
  713. </table>
  714. </div>
  715. </div>
  716. <div class="modal-footer">
  717. <button class="btn btn-outline">&#128196; 导出Excel</button>
  718. <button class="btn btn-outline-gray" onclick="hideModal('modal-progress')">关闭</button>
  719. </div>
  720. </div>
  721. </div>
  722. <!-- Record Detail Modal -->
  723. <div class="modal-overlay" id="modal-record-detail">
  724. <div class="modal-box modal-lg">
  725. <div class="modal-header">
  726. <div class="modal-title">&#128196; 培训记录详情</div>
  727. <button class="modal-close" onclick="hideModal('modal-record-detail')">&#10005;</button>
  728. </div>
  729. <div class="modal-body">
  730. <div style="display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:16px;">
  731. <div style="font-size:16px;font-weight:700;">辐射安全基础知识讲座</div>
  732. <span class="tag tag-gray">已结束</span>
  733. <span class="tag tag-blue">知识讲座</span>
  734. <span class="tag tag-green" style="gap:4px;">&#127941; 已颁发证书</span>
  735. </div>
  736. <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:16px;font-size:13px;">
  737. <div><span style="color:var(--text-hint);">培训时间:</span>2026-07-20 14:00–16:00</div>
  738. <div><span style="color:var(--text-hint);">培训地点:</span>物理楼 C301</div>
  739. <div><span style="color:var(--text-hint);">主讲人:</span>陈教授</div>
  740. <div><span style="color:var(--text-hint);">签到率:</span><span class="text-green text-bold">91.7% (55/60)</span></div>
  741. <div><span style="color:var(--text-hint);">可得学时:</span><span class="text-green text-bold">2 学时</span></div>
  742. <div><span style="color:var(--text-hint);">可得积分:</span><span class="text-orange text-bold">10 积分</span></div>
  743. <div><span style="color:var(--text-hint);">证书模板:</span>实验室安全培训证书(通用)</div>
  744. </div>
  745. <div class="divider"></div>
  746. <div style="margin-bottom:12px;font-size:13px;font-weight:600;">参与人员签到记录</div>
  747. <div class="table-wrap">
  748. <table>
  749. <thead><tr><th>姓名</th><th>学号</th><th>院系</th><th>签到状态</th><th>签到时间</th><th>获得学时</th><th>获得积分</th><th>证书状态</th><th>操作</th></tr></thead>
  750. <tbody>
  751. <tr><td>张明远</td><td>20231001</td><td>生命科学学院</td><td><span class="tag tag-green">已签到</span></td><td>14:02</td><td><span class="text-green text-bold">2</span></td><td><span class="text-orange text-bold">10</span></td><td><span class="tag tag-green" style="font-size:10px;">已颁发</span></td><td><div class="flex gap8"><span class="text-blue text-sm" style="cursor:pointer;" onclick="showModal('modal-cert-preview')">查看</span><span class="text-blue text-sm" style="cursor:pointer;">下载</span></div></td></tr>
  752. <tr><td>李晓燕</td><td>20221056</td><td>生命科学学院</td><td><span class="tag tag-green">已签到</span></td><td>14:05</td><td><span class="text-green text-bold">2</span></td><td><span class="text-orange text-bold">10</span></td><td><span class="tag tag-green" style="font-size:10px;">已颁发</span></td><td><div class="flex gap8"><span class="text-blue text-sm" style="cursor:pointer;" onclick="showModal('modal-cert-preview')">查看</span><span class="text-blue text-sm" style="cursor:pointer;">下载</span></div></td></tr>
  753. <tr><td>王浩然</td><td>20231089</td><td>化学化工学院</td><td><span class="tag tag-orange">迟到</span></td><td>14:18</td><td><span class="text-green text-bold">2</span></td><td><span class="text-orange text-bold">8</span></td><td><span class="tag tag-green" style="font-size:10px;">已颁发</span></td><td><div class="flex gap8"><span class="text-blue text-sm" style="cursor:pointer;" onclick="showModal('modal-cert-preview')">查看</span><span class="text-blue text-sm" style="cursor:pointer;">下载</span></div></td></tr>
  754. <tr><td>陈思雨</td><td>20241103</td><td>物理学院</td><td><span class="tag tag-red">未签到</span></td><td>—</td><td>0</td><td>0</td><td><span class="tag tag-gray" style="font-size:10px;">未颁发</span></td><td>—</td></tr>
  755. </tbody>
  756. </table>
  757. </div>
  758. </div>
  759. <div class="modal-footer">
  760. <button class="btn btn-outline">&#128196; 导出记录</button>
  761. <button class="btn btn-outline-gray" onclick="hideModal('modal-record-detail')">关闭</button>
  762. </div>
  763. </div>
  764. </div>
  765. <!-- Certificate Preview Modal -->
  766. <div class="modal-overlay" id="modal-cert-preview">
  767. <div class="modal-box" style="max-width:580px;">
  768. <div class="modal-header">
  769. <div class="modal-title">&#127941; 培训证书</div>
  770. <button class="modal-close" onclick="hideModal('modal-cert-preview')">&#10005;</button>
  771. </div>
  772. <div class="modal-body">
  773. <!-- Certificate card -->
  774. <div style="background:linear-gradient(135deg,#1857d4 0%,#0f3ea8 100%);border-radius:12px;padding:32px 28px;color:#fff;position:relative;overflow:hidden;margin-bottom:16px;">
  775. <div style="position:absolute;right:-30px;top:-30px;width:160px;height:160px;background:rgba(255,255,255,.06);border-radius:50%;"></div>
  776. <div style="position:absolute;right:20px;bottom:-40px;width:120px;height:120px;background:rgba(255,255,255,.04);border-radius:50%;"></div>
  777. <div style="text-align:center;margin-bottom:20px;">
  778. <div style="font-size:13px;letter-spacing:2px;opacity:.75;margin-bottom:4px;">实验室安全培训证书</div>
  779. <div style="font-size:11px;opacity:.55;letter-spacing:1px;">LABORATORY SAFETY TRAINING CERTIFICATE</div>
  780. </div>
  781. <div style="text-align:center;margin-bottom:20px;">
  782. <div style="font-size:11px;opacity:.7;margin-bottom:6px;">兹证明</div>
  783. <div style="font-size:26px;font-weight:700;letter-spacing:3px;margin-bottom:6px;">张 明 远</div>
  784. <div style="font-size:11px;opacity:.7;">学号:20231001 &nbsp;·&nbsp; 硕士研究生 &nbsp;·&nbsp; 生命科学学院</div>
  785. </div>
  786. <div style="text-align:center;margin-bottom:20px;font-size:13px;opacity:.85;line-height:1.7;">
  787. 已参加并完成 <strong>辐射安全基础知识讲座</strong> 培训<br>
  788. 培训时间:2026-07-20 &nbsp;·&nbsp; 获得学时:<strong>2 学时</strong> &nbsp;·&nbsp; 获得积分:<strong>10 分</strong>
  789. </div>
  790. <div style="display:flex;justify-content:space-between;align-items:flex-end;border-top:1px solid rgba(255,255,255,.2);padding-top:14px;font-size:11px;opacity:.7;">
  791. <div>证书编号:CERT-2026-0720-001</div>
  792. <div>颁发日期:2026-07-20</div>
  793. <div>颁发单位:实验室安全教育中心</div>
  794. </div>
  795. </div>
  796. <div class="flex gap8" style="justify-content:flex-end;">
  797. <button class="btn btn-primary">&#11015; 下载证书</button>
  798. <button class="btn btn-outline-gray" onclick="hideModal('modal-cert-preview')">关闭</button>
  799. </div>
  800. </div>
  801. </div>
  802. </div>
  803. <!-- Delete Confirm Modal -->
  804. <div class="modal-overlay" id="modal-confirm-delete">
  805. <div class="modal-box" style="max-width:380px;">
  806. <div class="modal-header">
  807. <div class="modal-title">确认删除</div>
  808. <button class="modal-close" onclick="hideModal('modal-confirm-delete')">&#10005;</button>
  809. </div>
  810. <div class="modal-body" style="text-align:center;padding:24px;">
  811. <div style="font-size:40px;margin-bottom:12px;">🗑️</div>
  812. <div style="font-size:15px;font-weight:600;margin-bottom:8px;">确认删除此培训?</div>
  813. <div style="font-size:13px;color:var(--text-hint);">删除后数据将无法恢复,相关签到记录和证书也将一并删除。</div>
  814. </div>
  815. <div class="modal-footer" style="justify-content:center;">
  816. <button class="btn btn-outline-gray" onclick="hideModal('modal-confirm-delete')" style="min-width:90px;">取消</button>
  817. <button class="btn" style="min-width:90px;background:var(--red);color:#fff;" onclick="hideModal('modal-confirm-delete')">确认删除</button>
  818. </div>
  819. </div>
  820. </div>
  821. </div><!-- end main-content -->
  822. <div id="page-exam-template" class="page" style="padding:0;">
  823. <!-- Page Header -->
  824. <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;">
  825. <div>
  826. <div class="page-title">考试模版</div>
  827. <div class="page-subtitle">管理各类考试的模版配置,包含基本设置、考前配置、防作弊及证书颁发等。</div>
  828. </div>
  829. <span class="tag tag-blue" style="font-size:12px;padding:4px 12px;">唯一模版</span>
  830. </div>
  831. <!-- Top-level tabs: 开学考试 / 分级考试 / 应知应会 -->
  832. <div class="view-tabs" id="exam-top-tabs">
  833. <div class="view-tab active" onclick="switchExamTab('kaixue')">开学考试</div>
  834. <div class="view-tab" onclick="switchExamTab('fenji')">分级考试</div>
  835. <div class="view-tab" onclick="switchExamTab('yingzhi')">应知应会</div>
  836. </div>
  837. <!-- ══════════════════════════════════════════════════════════
  838. TAB 1: 开学考试
  839. ══════════════════════════════════════════════════════════ -->
  840. <div id="exam-tab-kaixue" class="exam-tab-panel" style="display:flex;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;">
  841. <!-- Left sidebar nav -->
  842. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  843. <div class="nav-item active" id="kaixue-nav-basic" onclick="switchExamSection('kaixue','basic')">基本设置</div>
  844. <div class="nav-item" id="kaixue-nav-preexam" onclick="switchExamSection('kaixue','preexam')">考前配置</div>
  845. <div class="nav-item" id="kaixue-nav-cheat" onclick="switchExamSection('kaixue','cheat')">防作弊</div>
  846. <div class="nav-item" id="kaixue-nav-cert" onclick="switchExamSection('kaixue','cert')">获得证书</div>
  847. </div>
  848. <!-- Right content area -->
  849. <div style="flex:1;padding:28px;overflow-y:auto;max-height:calc(100vh - 220px);">
  850. <!-- 权限层级 + 每学年自动执行 (common header) -->
  851. <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:12px;">
  852. <div style="display:flex;align-items:center;gap:16px;">
  853. <span class="form-label">权限层级:</span>
  854. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  855. <input type="radio" name="kaixue-perm" value="school" checked> 校级(全校/指定范围)
  856. </label>
  857. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  858. <input type="radio" name="kaixue-perm" value="college"> 院级(所属学院)
  859. </label>
  860. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  861. <input type="radio" name="kaixue-perm" value="lab"> 实验室(自己负责)
  862. </label>
  863. </div>
  864. <div style="display:flex;align-items:center;gap:8px;">
  865. <label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
  866. <span class="form-hint">每学年自动按配置日期创建考试</span>
  867. </div>
  868. </div>
  869. <!-- 基本设置 section -->
  870. <div id="kaixue-sec-basic" class="exam-section">
  871. <div class="card-title">基本设置</div>
  872. <div style="display:flex;flex-direction:column;gap:16px;">
  873. <div style="display:flex;align-items:center;gap:16px;">
  874. <span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span>
  875. <input class="form-input" style="width:200px;" value="开学考试" disabled>
  876. <span class="form-hint">系统内置,不可更改</span>
  877. </div>
  878. <div style="display:flex;align-items:center;gap:16px;">
  879. <span class="form-label" style="width:80px;flex-shrink:0;">风险等级</span>
  880. <select class="form-input form-select" style="width:200px;">
  881. <option value="">请选择风险等级</option>
  882. <option>重大风险</option>
  883. <option>高风险</option>
  884. <option>中风险</option>
  885. <option>低风险</option>
  886. </select>
  887. </div>
  888. <div style="display:flex;align-items:center;gap:16px;">
  889. <span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span>
  890. <input class="form-input" style="width:300px;" placeholder="请输入考试名称">
  891. </div>
  892. <div style="display:flex;align-items:center;gap:16px;">
  893. <span class="form-label" style="width:80px;flex-shrink:0;">答卷次数</span>
  894. <div style="display:flex;align-items:center;gap:8px;">
  895. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-answer-cnt',-1)">-</button>
  896. <input id="kaixue-answer-cnt" type="number" class="form-input" style="width:60px;text-align:center;" value="2" min="1">
  897. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-answer-cnt',1)">+</button>
  898. </div>
  899. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;margin-left:8px;">
  900. <input type="radio" name="kaixue-answer-mode" value="limit" checked> 限制次数
  901. </label>
  902. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  903. <input type="radio" name="kaixue-answer-mode" value="unlimited"> 无限制
  904. </label>
  905. </div>
  906. <div style="display:flex;align-items:center;gap:16px;">
  907. <span class="form-label" style="width:80px;flex-shrink:0;">显示共享</span>
  908. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  909. <span class="form-hint">开课共享,全程有学号信息不可匿名</span>
  910. </div>
  911. <div style="display:flex;align-items:center;gap:16px;">
  912. <span class="form-label" style="width:80px;flex-shrink:0;">启用</span>
  913. <label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
  914. </div>
  915. <div style="display:flex;align-items:flex-start;gap:16px;">
  916. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考卷</span>
  917. <div style="display:flex;flex-direction:column;gap:8px;">
  918. <div style="display:flex;align-items:center;gap:8px;">
  919. <button class="btn btn-outline btn-sm">选择试卷</button>
  920. <span class="tag tag-blue" style="display:inline-flex;align-items:center;gap:4px;">实验室安全基础知识<span onclick="this.parentElement.remove()" style="cursor:pointer;margin-left:2px;">×</span></span>
  921. </div>
  922. <span class="form-hint">建议按知识点选择覆盖本校安全要求的试卷</span>
  923. </div>
  924. </div>
  925. <div style="display:flex;align-items:center;gap:16px;">
  926. <span class="form-label" style="width:80px;flex-shrink:0;">考试时间</span>
  927. <input type="date" class="form-input" style="width:150px;">
  928. <span style="color:var(--text-hint);font-size:13px;">至</span>
  929. <input type="date" class="form-input" style="width:150px;">
  930. </div>
  931. <div style="display:flex;align-items:center;gap:16px;">
  932. <span class="form-label" style="width:80px;flex-shrink:0;">考试时长</span>
  933. <div style="display:flex;align-items:center;gap:8px;">
  934. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-duration',-5)">-</button>
  935. <input id="kaixue-duration" type="number" class="form-input" style="width:70px;text-align:center;" value="60" min="5" step="5">
  936. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-duration',5)">+</button>
  937. <span style="font-size:13px;color:var(--text-secondary);">分钟</span>
  938. </div>
  939. <span class="form-hint">限制答卷最大时长</span>
  940. </div>
  941. <!-- 考试范围 -->
  942. <div style="display:flex;align-items:flex-start;gap:16px;">
  943. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考试范围</span>
  944. <div style="display:flex;flex-direction:column;gap:10px;flex:1;">
  945. <div style="display:flex;gap:16px;">
  946. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  947. <input type="radio" name="kaixue-scope" value="all" checked onclick="setKaixueScope('all')"> 全校
  948. </label>
  949. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  950. <input type="radio" name="kaixue-scope" value="specify" onclick="setKaixueScope('specify')"> 考试范围(指定)
  951. </label>
  952. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;">
  953. <input type="radio" name="kaixue-scope" value="student" onclick="setKaixueScope('student')"> 指定学生
  954. </label>
  955. </div>
  956. <div id="kaixue-scope-table" style="display:none;">
  957. <div class="table-wrap" style="margin-bottom:8px;">
  958. <table>
  959. <thead><tr><th>学院</th><th>专业</th><th>角色</th><th>年级</th><th>操作</th></tr></thead>
  960. <tbody id="kaixue-scope-tbody">
  961. <tr><td colspan="5" style="text-align:center;color:var(--text-hint);">暂无数据,请添加</td></tr>
  962. </tbody>
  963. </table>
  964. </div>
  965. <button class="btn btn-outline btn-sm">+ 添加范围</button>
  966. </div>
  967. </div>
  968. </div>
  969. <!-- 用户类别 -->
  970. <div style="display:flex;align-items:flex-start;gap:16px;">
  971. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span>
  972. <div style="display:flex;flex-direction:column;gap:8px;">
  973. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;">
  974. <input type="checkbox" id="kaixue-user-undergrad" checked onchange="updateKaixueHours()"> 新入学本科生(默认勾选)
  975. </label>
  976. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;">
  977. <input type="checkbox" id="kaixue-user-postgrad" checked onchange="updateKaixueHours()"> 新入学研究生
  978. </label>
  979. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;">
  980. <input type="checkbox" id="kaixue-user-staff" checked onchange="updateKaixueHours()"> 新入职教职工
  981. </label>
  982. </div>
  983. </div>
  984. <!-- 学时配置 -->
  985. <div style="display:flex;align-items:flex-start;gap:16px;">
  986. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span>
  987. <div style="flex:1;">
  988. <div class="table-wrap">
  989. <table>
  990. <thead><tr><th>用户类别</th><th>所需学时</th><th>备注</th></tr></thead>
  991. <tbody id="kaixue-hours-tbody">
  992. <tr><td>新入学本科生</td><td><input type="number" class="form-input" style="width:80px;" value="4" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  993. <tr><td>新入学研究生</td><td><input type="number" class="form-input" style="width:80px;" value="4" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  994. <tr><td>新入职教职工</td><td><input type="number" class="form-input" style="width:80px;" value="2" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  995. </tbody>
  996. </table>
  997. </div>
  998. </div>
  999. </div>
  1000. </div><!-- /flex col -->
  1001. <div style="margin-top:24px;display:flex;gap:12px;">
  1002. <button class="btn btn-primary">保存设置</button>
  1003. <button class="btn btn-outline-gray">重置</button>
  1004. </div>
  1005. </div><!-- /kaixue-sec-basic -->
  1006. <!-- 考前配置 section -->
  1007. <div id="kaixue-sec-preexam" class="exam-section" style="display:none;">
  1008. <div class="card-title">考前配置</div>
  1009. <div style="display:flex;flex-direction:column;gap:20px;">
  1010. <!-- 学时要求 -->
  1011. <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
  1012. <span class="form-label" style="width:80px;flex-shrink:0;">学时要求</span>
  1013. <label class="toggle"><input type="checkbox" id="kaixue-credit-toggle" onchange="document.getElementById('kaixue-credit-detail').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1014. <div id="kaixue-credit-detail" style="display:none;align-items:center;gap:8px;">
  1015. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-credit-val',-1)">-</button>
  1016. <input id="kaixue-credit-val" type="number" class="form-input" style="width:60px;text-align:center;" value="2" min="0">
  1017. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('kaixue-credit-val',1)">+</button>
  1018. <span style="font-size:13px;color:var(--text-secondary);">学时</span>
  1019. <span class="form-hint">参加考试前需完成的最低学时数</span>
  1020. </div>
  1021. </div>
  1022. <!-- 考前学习 -->
  1023. <div style="display:flex;align-items:flex-start;gap:12px;">
  1024. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考前学习</span>
  1025. <div style="display:flex;flex-direction:column;gap:8px;">
  1026. <label class="toggle"><input type="checkbox" id="kaixue-prestudy-toggle" onchange="document.getElementById('kaixue-prestudy-detail').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1027. <div id="kaixue-prestudy-detail" style="display:none;align-items:center;gap:8px;">
  1028. <button class="btn btn-outline btn-sm">选择课程</button>
  1029. <span class="tag tag-blue" style="display:inline-flex;align-items:center;gap:4px;">实验室安全基础课程<span onclick="this.parentElement.remove()" style="cursor:pointer;margin-left:2px;">×</span></span>
  1030. </div>
  1031. </div>
  1032. </div>
  1033. <!-- 模拟考试 -->
  1034. <div style="display:flex;align-items:flex-start;gap:12px;">
  1035. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">模拟考试</span>
  1036. <div style="display:flex;flex-direction:column;gap:8px;">
  1037. <label class="toggle"><input type="checkbox" id="kaixue-mock-toggle" onchange="document.getElementById('kaixue-mock-detail').style.display=this.checked?'block':'none'"><span class="toggle-slider"></span></label>
  1038. <div id="kaixue-mock-detail" style="display:none;">
  1039. <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
  1040. <button class="btn btn-outline btn-sm">选择试卷</button>
  1041. <span class="tag tag-gray" id="kaixue-mock-paper">未选择</span>
  1042. </div>
  1043. <div style="display:flex;gap:16px;font-size:12px;color:var(--text-hint);">
  1044. <span>总题数:<strong style="color:var(--text-primary);">50</strong></span>
  1045. <span>总分:<strong style="color:var(--text-primary);">100</strong></span>
  1046. <span>及格分:<strong style="color:var(--green);">60</strong></span>
  1047. </div>
  1048. </div>
  1049. </div>
  1050. </div>
  1051. <!-- 考试承诺书 -->
  1052. <div style="display:flex;align-items:flex-start;gap:12px;">
  1053. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考试承诺书</span>
  1054. <div style="display:flex;flex-direction:column;gap:8px;flex:1;">
  1055. <label class="toggle"><input type="checkbox" id="kaixue-pledge-toggle" onchange="document.getElementById('kaixue-pledge-detail').style.display=this.checked?'block':'none'"><span class="toggle-slider"></span></label>
  1056. <div id="kaixue-pledge-detail" style="display:none;">
  1057. <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
  1058. <span class="form-label">承诺书编辑</span>
  1059. <label class="toggle"><input type="checkbox" onchange="document.getElementById('kaixue-pledge-editor').style.display=this.checked?'block':'none'"><span class="toggle-slider"></span></label>
  1060. </div>
  1061. <div id="kaixue-pledge-editor" style="display:none;">
  1062. <div class="rich-editor">
  1063. <div class="rich-toolbar">
  1064. <button class="rich-tool-btn">B</button><button class="rich-tool-btn">I</button>
  1065. <button class="rich-tool-btn">U</button><button class="rich-tool-btn">≡</button>
  1066. <button class="rich-tool-btn">链接</button><button class="rich-tool-btn">图片</button>
  1067. </div>
  1068. <div class="rich-body" contenteditable="true">
  1069. <p><strong>实验室安全承诺书</strong></p>
  1070. <p>本人郑重承诺:在参加实验室安全考试及日常实验操作过程中,严格遵守实验室各项安全规章制度,认真执行安全操作规程,不擅自使用不熟悉的仪器设备,不私自改装或违规操作实验装置。发现安全隐患及时上报,如实反映实验情况,对违规行为承担相应法律及校规责任。</p>
  1071. </div>
  1072. </div>
  1073. </div>
  1074. </div>
  1075. </div>
  1076. </div>
  1077. </div>
  1078. <div style="margin-top:24px;display:flex;gap:12px;">
  1079. <button class="btn btn-primary">保存设置</button>
  1080. <button class="btn btn-outline-gray">重置</button>
  1081. </div>
  1082. </div><!-- /kaixue-sec-preexam -->
  1083. <!-- 防作弊 section -->
  1084. <div id="kaixue-sec-cheat" class="exam-section" style="display:none;">
  1085. <div class="card-title">防作弊</div>
  1086. <div style="display:flex;flex-direction:column;gap:16px;">
  1087. <div style="display:flex;align-items:center;gap:12px;">
  1088. <span class="form-label" style="width:100px;flex-shrink:0;">切屏限制</span>
  1089. <label class="toggle"><input type="checkbox" id="kaixue-switchscreen" onchange="document.getElementById('kaixue-switchscreen-cnt-wrap').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1090. <div id="kaixue-switchscreen-cnt-wrap" style="display:none;align-items:center;gap:6px;">
  1091. <span style="font-size:13px;color:var(--text-secondary);">切屏</span>
  1092. <input type="number" class="form-input" style="width:60px;text-align:center;" value="3" min="1">
  1093. <span style="font-size:13px;color:var(--text-secondary);">次后提交</span>
  1094. </div>
  1095. </div>
  1096. <div style="display:flex;align-items:center;gap:12px;">
  1097. <span class="form-label" style="width:100px;flex-shrink:0;">随机题目</span>
  1098. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1099. </div>
  1100. <div style="display:flex;align-items:center;gap:12px;">
  1101. <span class="form-label" style="width:100px;flex-shrink:0;">随机选项</span>
  1102. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1103. </div>
  1104. <div style="display:flex;align-items:center;gap:12px;">
  1105. <span class="form-label" style="width:100px;flex-shrink:0;">人脸识别</span>
  1106. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1107. </div>
  1108. </div>
  1109. <div style="margin-top:24px;display:flex;gap:12px;">
  1110. <button class="btn btn-primary">保存设置</button>
  1111. <button class="btn btn-outline-gray">重置</button>
  1112. </div>
  1113. </div><!-- /kaixue-sec-cheat -->
  1114. <!-- 获得证书 section -->
  1115. <div id="kaixue-sec-cert" class="exam-section" style="display:none;">
  1116. <div class="card-title">获得证书</div>
  1117. <div style="display:flex;flex-direction:column;gap:16px;">
  1118. <div style="display:flex;align-items:center;gap:12px;">
  1119. <span class="form-label" style="width:80px;flex-shrink:0;">颁发证书</span>
  1120. <label class="toggle"><input type="checkbox" id="kaixue-cert-toggle" onchange="document.getElementById('kaixue-cert-tmpl').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1121. </div>
  1122. <div id="kaixue-cert-tmpl" style="display:none;align-items:center;gap:12px;">
  1123. <span class="form-label" style="width:80px;flex-shrink:0;">证书模板</span>
  1124. <select class="form-input form-select" style="width:240px;">
  1125. <option value="">请选择证书模板</option>
  1126. <option>实验室安全教育证书(标准版)</option>
  1127. <option>实验室安全教育证书(精英版)</option>
  1128. </select>
  1129. </div>
  1130. </div>
  1131. <div style="margin-top:24px;display:flex;gap:12px;">
  1132. <button class="btn btn-primary">保存设置</button>
  1133. <button class="btn btn-outline-gray">重置</button>
  1134. </div>
  1135. </div><!-- /kaixue-sec-cert -->
  1136. </div><!-- /right content -->
  1137. </div><!-- /exam-tab-kaixue -->
  1138. <!-- ══════════════════════════════════════════════════════════
  1139. TAB 2: 分级考试
  1140. ══════════════════════════════════════════════════════════ -->
  1141. <div id="exam-tab-fenji" class="exam-tab-panel" style="display:none;">
  1142. <!-- Grade sub-tabs -->
  1143. <div class="view-tabs" id="fenji-grade-tabs" style="margin-bottom:0;padding:0 24px;background:var(--white);border:1px solid var(--gray-border);border-bottom:none;border-radius:var(--radius) var(--radius) 0 0;">
  1144. <div class="view-tab active" onclick="switchGradeTab('g1')">Ⅰ级(重大风险)</div>
  1145. <div class="view-tab" onclick="switchGradeTab('g2')">Ⅱ级(高风险)</div>
  1146. <div class="view-tab" onclick="switchGradeTab('g3')">Ⅲ级(中风险)</div>
  1147. <div class="view-tab" onclick="switchGradeTab('g4')">Ⅳ级(低风险)</div>
  1148. </div>
  1149. <!-- Each grade panel shares the same two-column layout. We generate g1-g4. -->
  1150. <div id="fenji-grade-g1" class="fenji-grade-panel" style="display:flex;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:0 0 var(--radius) var(--radius);box-shadow:var(--shadow);overflow:hidden;">
  1151. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  1152. <div class="nav-item active" onclick="switchExamSection('fenji-g1','basic')">基本设置</div>
  1153. <div class="nav-item" onclick="switchExamSection('fenji-g1','preexam')">考前配置</div>
  1154. <div class="nav-item" onclick="switchExamSection('fenji-g1','cheat')">防作弊</div>
  1155. <div class="nav-item" onclick="switchExamSection('fenji-g1','cert')">获得证书</div>
  1156. </div>
  1157. <div style="flex:1;padding:28px;overflow-y:auto;max-height:calc(100vh - 260px);">
  1158. <!-- Common header -->
  1159. <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:12px;">
  1160. <div style="display:flex;align-items:center;gap:16px;">
  1161. <span class="form-label">权限层级:</span>
  1162. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="fenji-g1-perm" value="school" checked> 校级(全校/指定范围)</label>
  1163. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="fenji-g1-perm" value="college"> 院级(所属学院)</label>
  1164. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="fenji-g1-perm" value="lab"> 实验室(自己负责)</label>
  1165. </div>
  1166. <div style="display:flex;align-items:center;gap:8px;">
  1167. <label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
  1168. <span class="form-hint">每学年自动按配置日期创建考试</span>
  1169. </div>
  1170. </div>
  1171. <!-- 基本设置 -->
  1172. <div id="fenji-g1-sec-basic" class="exam-section">
  1173. <div class="card-title">基本设置</div>
  1174. <div style="display:flex;flex-direction:column;gap:16px;">
  1175. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span><input class="form-input" style="width:200px;" value="Ⅰ级(重大风险)" disabled></div>
  1176. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span><input class="form-input" style="width:300px;" placeholder="请输入考试名称"></div>
  1177. <div style="display:flex;align-items:center;gap:16px;">
  1178. <span class="form-label" style="width:80px;flex-shrink:0;">答卷次数</span>
  1179. <div style="display:flex;align-items:center;gap:8px;">
  1180. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-ans',-1)">-</button>
  1181. <input id="fenji-g1-ans" type="number" class="form-input" style="width:60px;text-align:center;" value="2" min="1">
  1182. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-ans',1)">+</button>
  1183. </div>
  1184. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;margin-left:8px;"><input type="radio" name="fenji-g1-ans-mode" value="limit" checked> 限制次数</label>
  1185. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="fenji-g1-ans-mode" value="unlimited"> 无限制</label>
  1186. </div>
  1187. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">启用</span><label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label></div>
  1188. <div style="display:flex;align-items:flex-start;gap:16px;">
  1189. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考卷</span>
  1190. <div style="display:flex;flex-direction:column;gap:8px;">
  1191. <div style="display:flex;align-items:center;gap:8px;"><button class="btn btn-outline btn-sm">选择试卷</button></div>
  1192. <span class="form-hint">建议按知识点选择覆盖本校安全要求的试卷</span>
  1193. </div>
  1194. </div>
  1195. <div style="display:flex;align-items:center;gap:16px;">
  1196. <span class="form-label" style="width:80px;flex-shrink:0;">考试时间</span>
  1197. <input type="date" class="form-input" style="width:150px;"><span style="color:var(--text-hint);font-size:13px;">至</span><input type="date" class="form-input" style="width:150px;">
  1198. </div>
  1199. <div style="display:flex;align-items:center;gap:16px;">
  1200. <span class="form-label" style="width:80px;flex-shrink:0;">考试时长</span>
  1201. <div style="display:flex;align-items:center;gap:8px;">
  1202. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-dur',-5)">-</button>
  1203. <input id="fenji-g1-dur" type="number" class="form-input" style="width:70px;text-align:center;" value="60" min="5" step="5">
  1204. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-dur',5)">+</button>
  1205. <span style="font-size:13px;color:var(--text-secondary);">分钟</span>
  1206. </div>
  1207. <span class="form-hint">限制答卷最大时长</span>
  1208. </div>
  1209. <!-- 用户类别 (分级考试专用) -->
  1210. <div style="display:flex;align-items:flex-start;gap:16px;">
  1211. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span>
  1212. <div style="display:flex;flex-direction:column;gap:8px;">
  1213. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 通过入学考试后绑定实验室的本科生</label>
  1214. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 绑定实验室的研究生</label>
  1215. </div>
  1216. </div>
  1217. <!-- 学时配置 (分级考试) -->
  1218. <div style="display:flex;align-items:flex-start;gap:16px;">
  1219. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span>
  1220. <div style="padding:10px 14px;background:var(--gray-light);border-radius:6px;font-size:13px;color:var(--text-secondary);">按公共配置的分级规则执行</div>
  1221. </div>
  1222. </div>
  1223. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1224. </div>
  1225. <!-- 考前配置 -->
  1226. <div id="fenji-g1-sec-preexam" class="exam-section" style="display:none;">
  1227. <div class="card-title">考前配置</div>
  1228. <div style="display:flex;flex-direction:column;gap:20px;">
  1229. <div style="display:flex;align-items:center;gap:12px;">
  1230. <span class="form-label" style="width:80px;flex-shrink:0;">学时要求</span>
  1231. <label class="toggle"><input type="checkbox" onchange="this.closest('.exam-section').querySelector('.credit-detail').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1232. <div class="credit-detail" style="display:none;align-items:center;gap:8px;">
  1233. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-credit',-1)">-</button>
  1234. <input id="fenji-g1-credit" type="number" class="form-input" style="width:60px;text-align:center;" value="2" min="0">
  1235. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('fenji-g1-credit',1)">+</button>
  1236. <span style="font-size:13px;color:var(--text-secondary);">学时</span>
  1237. </div>
  1238. </div>
  1239. <div style="display:flex;align-items:center;gap:12px;">
  1240. <span class="form-label" style="width:80px;flex-shrink:0;">考前学习</span>
  1241. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1242. </div>
  1243. <div style="display:flex;align-items:center;gap:12px;">
  1244. <span class="form-label" style="width:80px;flex-shrink:0;">模拟考试</span>
  1245. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1246. </div>
  1247. <div style="display:flex;align-items:center;gap:12px;">
  1248. <span class="form-label" style="width:80px;flex-shrink:0;">考试承诺书</span>
  1249. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1250. </div>
  1251. </div>
  1252. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1253. </div>
  1254. <!-- 防作弊 -->
  1255. <div id="fenji-g1-sec-cheat" class="exam-section" style="display:none;">
  1256. <div class="card-title">防作弊</div>
  1257. <div style="display:flex;flex-direction:column;gap:16px;">
  1258. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">切屏限制</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1259. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">随机题目</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1260. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">随机选项</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1261. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">人脸识别</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1262. </div>
  1263. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1264. </div>
  1265. <!-- 获得证书 -->
  1266. <div id="fenji-g1-sec-cert" class="exam-section" style="display:none;">
  1267. <div class="card-title">获得证书</div>
  1268. <div style="display:flex;flex-direction:column;gap:16px;">
  1269. <div style="display:flex;align-items:center;gap:12px;">
  1270. <span class="form-label" style="width:80px;flex-shrink:0;">颁发证书</span>
  1271. <label class="toggle"><input type="checkbox" onchange="this.closest('.exam-section').querySelector('.cert-tmpl-row').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label>
  1272. </div>
  1273. <div class="cert-tmpl-row" style="display:none;align-items:center;gap:12px;">
  1274. <span class="form-label" style="width:80px;flex-shrink:0;">证书模板</span>
  1275. <select class="form-input form-select" style="width:240px;"><option value="">请选择证书模板</option><option>实验室安全教育证书(标准版)</option></select>
  1276. </div>
  1277. </div>
  1278. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1279. </div>
  1280. </div>
  1281. </div><!-- /fenji-grade-g1 -->
  1282. <!-- Grades g2, g3, g4 share identical structure, only IDs and type labels differ -->
  1283. <div id="fenji-grade-g2" class="fenji-grade-panel" style="display:none;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:0 0 var(--radius) var(--radius);box-shadow:var(--shadow);overflow:hidden;flex-direction:row;">
  1284. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  1285. <div class="nav-item active" onclick="switchExamSection('fenji-g2','basic')">基本设置</div>
  1286. <div class="nav-item" onclick="switchExamSection('fenji-g2','preexam')">考前配置</div>
  1287. <div class="nav-item" onclick="switchExamSection('fenji-g2','cheat')">防作弊</div>
  1288. <div class="nav-item" onclick="switchExamSection('fenji-g2','cert')">获得证书</div>
  1289. </div>
  1290. <div style="flex:1;padding:24px;">
  1291. <div id="fenji-g2-sec-basic" class="exam-section">
  1292. <div class="card-title">基本设置</div>
  1293. <div style="display:flex;flex-direction:column;gap:16px;">
  1294. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span><input class="form-input" style="width:200px;" value="Ⅱ级(高风险)" disabled></div>
  1295. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span><input class="form-input" style="width:300px;" placeholder="请输入考试名称"></div>
  1296. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span><div style="display:flex;flex-direction:column;gap:8px;"><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 通过入学考试后绑定实验室的本科生</label><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 绑定实验室的研究生</label></div></div>
  1297. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span><div style="padding:10px 14px;background:var(--gray-light);border-radius:6px;font-size:13px;color:var(--text-secondary);">按公共配置的分级规则执行</div></div>
  1298. </div>
  1299. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1300. </div>
  1301. <div id="fenji-g2-sec-preexam" class="exam-section" style="display:none;"><div class="card-title">考前配置</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1302. <div id="fenji-g2-sec-cheat" class="exam-section" style="display:none;"><div class="card-title">防作弊</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1303. <div id="fenji-g2-sec-cert" class="exam-section" style="display:none;"><div class="card-title">获得证书</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1304. </div>
  1305. </div><!-- /fenji-grade-g2 -->
  1306. <div id="fenji-grade-g3" class="fenji-grade-panel" style="display:none;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:0 0 var(--radius) var(--radius);box-shadow:var(--shadow);overflow:hidden;flex-direction:row;">
  1307. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  1308. <div class="nav-item active" onclick="switchExamSection('fenji-g3','basic')">基本设置</div>
  1309. <div class="nav-item" onclick="switchExamSection('fenji-g3','preexam')">考前配置</div>
  1310. <div class="nav-item" onclick="switchExamSection('fenji-g3','cheat')">防作弊</div>
  1311. <div class="nav-item" onclick="switchExamSection('fenji-g3','cert')">获得证书</div>
  1312. </div>
  1313. <div style="flex:1;padding:24px;">
  1314. <div id="fenji-g3-sec-basic" class="exam-section">
  1315. <div class="card-title">基本设置</div>
  1316. <div style="display:flex;flex-direction:column;gap:16px;">
  1317. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span><input class="form-input" style="width:200px;" value="Ⅲ级(中风险)" disabled></div>
  1318. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span><input class="form-input" style="width:300px;" placeholder="请输入考试名称"></div>
  1319. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span><div style="display:flex;flex-direction:column;gap:8px;"><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 通过入学考试后绑定实验室的本科生</label><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 绑定实验室的研究生</label></div></div>
  1320. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span><div style="padding:10px 14px;background:var(--gray-light);border-radius:6px;font-size:13px;color:var(--text-secondary);">按公共配置的分级规则执行</div></div>
  1321. </div>
  1322. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1323. </div>
  1324. <div id="fenji-g3-sec-preexam" class="exam-section" style="display:none;"><div class="card-title">考前配置</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1325. <div id="fenji-g3-sec-cheat" class="exam-section" style="display:none;"><div class="card-title">防作弊</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1326. <div id="fenji-g3-sec-cert" class="exam-section" style="display:none;"><div class="card-title">获得证书</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1327. </div>
  1328. </div><!-- /fenji-grade-g3 -->
  1329. <div id="fenji-grade-g4" class="fenji-grade-panel" style="display:none;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:0 0 var(--radius) var(--radius);box-shadow:var(--shadow);overflow:hidden;flex-direction:row;">
  1330. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  1331. <div class="nav-item active" onclick="switchExamSection('fenji-g4','basic')">基本设置</div>
  1332. <div class="nav-item" onclick="switchExamSection('fenji-g4','preexam')">考前配置</div>
  1333. <div class="nav-item" onclick="switchExamSection('fenji-g4','cheat')">防作弊</div>
  1334. <div class="nav-item" onclick="switchExamSection('fenji-g4','cert')">获得证书</div>
  1335. </div>
  1336. <div style="flex:1;padding:24px;">
  1337. <div id="fenji-g4-sec-basic" class="exam-section">
  1338. <div class="card-title">基本设置</div>
  1339. <div style="display:flex;flex-direction:column;gap:16px;">
  1340. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span><input class="form-input" style="width:200px;" value="Ⅳ级(低风险)" disabled></div>
  1341. <div style="display:flex;align-items:center;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span><input class="form-input" style="width:300px;" placeholder="请输入考试名称"></div>
  1342. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span><div style="display:flex;flex-direction:column;gap:8px;"><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 通过入学考试后绑定实验室的本科生</label><label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 绑定实验室的研究生</label></div></div>
  1343. <div style="display:flex;align-items:flex-start;gap:16px;"><span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span><div style="padding:10px 14px;background:var(--gray-light);border-radius:6px;font-size:13px;color:var(--text-secondary);">按公共配置的分级规则执行</div></div>
  1344. </div>
  1345. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1346. </div>
  1347. <div id="fenji-g4-sec-preexam" class="exam-section" style="display:none;"><div class="card-title">考前配置</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1348. <div id="fenji-g4-sec-cheat" class="exam-section" style="display:none;"><div class="card-title">防作弊</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1349. <div id="fenji-g4-sec-cert" class="exam-section" style="display:none;"><div class="card-title">获得证书</div><div style="color:var(--text-hint);font-size:13px;">(配置同Ⅰ级)</div><div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button></div></div>
  1350. </div>
  1351. </div><!-- /fenji-grade-g4 -->
  1352. </div><!-- /exam-tab-fenji -->
  1353. <!-- ══════════════════════════════════════════════════════════
  1354. TAB 3: 应知应会
  1355. ══════════════════════════════════════════════════════════ -->
  1356. <div id="exam-tab-yingzhi" class="exam-tab-panel" style="display:none;gap:0;background:var(--white);border:1px solid var(--gray-border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;flex-direction:row;">
  1357. <div style="width:180px;flex-shrink:0;border-right:1px solid var(--gray-border);background:var(--gray-light);padding:14px 0;">
  1358. <div class="nav-item active" id="yingzhi-nav-basic" onclick="switchExamSection('yingzhi','basic')">基本设置</div>
  1359. <div class="nav-item" id="yingzhi-nav-preexam" onclick="switchExamSection('yingzhi','preexam')">考前配置</div>
  1360. <div class="nav-item" id="yingzhi-nav-cheat" onclick="switchExamSection('yingzhi','cheat')">防作弊</div>
  1361. <div class="nav-item" id="yingzhi-nav-cert" onclick="switchExamSection('yingzhi','cert')">获得证书</div>
  1362. </div>
  1363. <div style="flex:1;padding:28px;overflow-y:auto;max-height:calc(100vh - 220px);">
  1364. <!-- Common header -->
  1365. <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:12px;">
  1366. <div style="display:flex;align-items:center;gap:16px;">
  1367. <span class="form-label">权限层级:</span>
  1368. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="yingzhi-perm" value="school" checked> 校级(全校/指定范围)</label>
  1369. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="yingzhi-perm" value="college"> 院级(所属学院)</label>
  1370. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="yingzhi-perm" value="lab"> 实验室(自己负责)</label>
  1371. </div>
  1372. <div style="display:flex;align-items:center;gap:8px;">
  1373. <label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
  1374. <span class="form-hint">每学年自动按配置日期创建考试</span>
  1375. </div>
  1376. </div>
  1377. <!-- 基本设置 -->
  1378. <div id="yingzhi-sec-basic" class="exam-section">
  1379. <div class="card-title">基本设置</div>
  1380. <div style="display:flex;flex-direction:column;gap:16px;">
  1381. <div style="display:flex;align-items:center;gap:16px;">
  1382. <span class="form-label" style="width:80px;flex-shrink:0;">考试类型</span>
  1383. <input class="form-input" style="width:200px;" value="应知应会" disabled>
  1384. <span class="form-hint">系统内置,不可更改</span>
  1385. </div>
  1386. <div style="display:flex;align-items:center;gap:16px;">
  1387. <span class="form-label" style="width:80px;flex-shrink:0;">考试名称</span>
  1388. <input class="form-input" style="width:300px;" placeholder="请输入考试名称">
  1389. </div>
  1390. <div style="display:flex;align-items:center;gap:16px;">
  1391. <span class="form-label" style="width:80px;flex-shrink:0;">答卷次数</span>
  1392. <div style="display:flex;align-items:center;gap:8px;">
  1393. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('yingzhi-ans',-1)">-</button>
  1394. <input id="yingzhi-ans" type="number" class="form-input" style="width:60px;text-align:center;" value="2" min="1">
  1395. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('yingzhi-ans',1)">+</button>
  1396. </div>
  1397. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;margin-left:8px;"><input type="radio" name="yingzhi-ans-mode" value="limit" checked> 限制次数</label>
  1398. <label style="display:flex;align-items:center;gap:5px;cursor:pointer;font-size:13px;"><input type="radio" name="yingzhi-ans-mode" value="unlimited"> 无限制</label>
  1399. </div>
  1400. <div style="display:flex;align-items:center;gap:16px;">
  1401. <span class="form-label" style="width:80px;flex-shrink:0;">显示共享</span>
  1402. <label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label>
  1403. <span class="form-hint">开课共享,全程有学号信息不可匿名</span>
  1404. </div>
  1405. <div style="display:flex;align-items:center;gap:16px;">
  1406. <span class="form-label" style="width:80px;flex-shrink:0;">启用</span>
  1407. <label class="toggle"><input type="checkbox" checked><span class="toggle-slider"></span></label>
  1408. </div>
  1409. <div style="display:flex;align-items:flex-start;gap:16px;">
  1410. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">考卷</span>
  1411. <div style="display:flex;flex-direction:column;gap:8px;">
  1412. <div style="display:flex;align-items:center;gap:8px;"><button class="btn btn-outline btn-sm">选择试卷</button></div>
  1413. <span class="form-hint">建议按知识点选择覆盖本校安全要求的试卷</span>
  1414. </div>
  1415. </div>
  1416. <div style="display:flex;align-items:center;gap:16px;">
  1417. <span class="form-label" style="width:80px;flex-shrink:0;">考试时间</span>
  1418. <input type="date" class="form-input" style="width:150px;"><span style="color:var(--text-hint);font-size:13px;">至</span><input type="date" class="form-input" style="width:150px;">
  1419. </div>
  1420. <div style="display:flex;align-items:center;gap:16px;">
  1421. <span class="form-label" style="width:80px;flex-shrink:0;">考试时长</span>
  1422. <div style="display:flex;align-items:center;gap:8px;">
  1423. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('yingzhi-dur',-5)">-</button>
  1424. <input id="yingzhi-dur" type="number" class="form-input" style="width:70px;text-align:center;" value="60" min="5" step="5">
  1425. <button class="btn btn-outline-gray btn-sm" onclick="stepNum('yingzhi-dur',5)">+</button>
  1426. <span style="font-size:13px;color:var(--text-secondary);">分钟</span>
  1427. </div>
  1428. <span class="form-hint">限制答卷最大时长</span>
  1429. </div>
  1430. <!-- 用户类别 (应知应会) -->
  1431. <div style="display:flex;align-items:flex-start;gap:16px;">
  1432. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">用户类别</span>
  1433. <div style="display:flex;flex-direction:column;gap:8px;">
  1434. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 无需进入实验室的新入学研究生</label>
  1435. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 新入职教职工</label>
  1436. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;" title="按实验室分级取年度学时值,不可取消">
  1437. <input type="checkbox" checked disabled style="opacity:.5;"> 在读研究生 <span class="tag tag-gray" style="font-size:11px;margin-left:4px;">按实验室分级取年度学时值</span>
  1438. </label>
  1439. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 存量教职工</label>
  1440. <label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:13px;"><input type="checkbox" checked> 在读本科生</label>
  1441. </div>
  1442. </div>
  1443. <!-- 学时配置 (应知应会) -->
  1444. <div style="display:flex;align-items:flex-start;gap:16px;">
  1445. <span class="form-label" style="width:80px;flex-shrink:0;padding-top:4px;">学时配置</span>
  1446. <div style="flex:1;">
  1447. <div class="table-wrap">
  1448. <table>
  1449. <thead><tr><th>用户类别</th><th>所需学时</th><th>备注</th></tr></thead>
  1450. <tbody>
  1451. <tr><td>无需进入实验室的新入学研究生</td><td><input type="number" class="form-input" style="width:80px;" value="2" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  1452. <tr><td>新入职教职工</td><td><input type="number" class="form-input" style="width:80px;" value="2" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  1453. <tr style="background:var(--gray-light);color:var(--text-hint);">
  1454. <td>在读研究生</td>
  1455. <td><input type="number" class="form-input" style="width:80px;background:var(--gray-light);color:var(--text-hint);" disabled placeholder="按分级取值"></td>
  1456. <td><span style="font-size:12px;">按实验室分级取年度学时值</span></td>
  1457. </tr>
  1458. <tr><td>存量教职工</td><td><input type="number" class="form-input" style="width:80px;" value="1" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  1459. <tr><td>在读本科生</td><td><input type="number" class="form-input" style="width:80px;" value="1" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>
  1460. </tbody>
  1461. </table>
  1462. </div>
  1463. </div>
  1464. </div>
  1465. </div>
  1466. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1467. </div><!-- /yingzhi-sec-basic -->
  1468. <!-- 考前配置 -->
  1469. <div id="yingzhi-sec-preexam" class="exam-section" style="display:none;">
  1470. <div class="card-title">考前配置</div>
  1471. <div style="display:flex;flex-direction:column;gap:20px;">
  1472. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:80px;flex-shrink:0;">学时要求</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1473. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:80px;flex-shrink:0;">考前学习</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1474. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:80px;flex-shrink:0;">模拟考试</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1475. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:80px;flex-shrink:0;">考试承诺书</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1476. </div>
  1477. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1478. </div>
  1479. <!-- 防作弊 -->
  1480. <div id="yingzhi-sec-cheat" class="exam-section" style="display:none;">
  1481. <div class="card-title">防作弊</div>
  1482. <div style="display:flex;flex-direction:column;gap:16px;">
  1483. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">切屏限制</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1484. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">随机题目</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1485. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">随机选项</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1486. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:100px;flex-shrink:0;">人脸识别</span><label class="toggle"><input type="checkbox"><span class="toggle-slider"></span></label></div>
  1487. </div>
  1488. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1489. </div>
  1490. <!-- 获得证书 -->
  1491. <div id="yingzhi-sec-cert" class="exam-section" style="display:none;">
  1492. <div class="card-title">获得证书</div>
  1493. <div style="display:flex;flex-direction:column;gap:16px;">
  1494. <div style="display:flex;align-items:center;gap:12px;"><span class="form-label" style="width:80px;flex-shrink:0;">颁发证书</span><label class="toggle"><input type="checkbox" onchange="document.getElementById('yingzhi-cert-tmpl').style.display=this.checked?'flex':'none'"><span class="toggle-slider"></span></label></div>
  1495. <div id="yingzhi-cert-tmpl" style="display:none;align-items:center;gap:12px;">
  1496. <span class="form-label" style="width:80px;flex-shrink:0;">证书模板</span>
  1497. <select class="form-input form-select" style="width:240px;"><option value="">请选择证书模板</option><option>实验室安全教育证书(标准版)</option></select>
  1498. </div>
  1499. </div>
  1500. <div style="margin-top:24px;display:flex;gap:12px;"><button class="btn btn-primary">保存设置</button><button class="btn btn-outline-gray">重置</button></div>
  1501. </div>
  1502. </div><!-- /right content yingzhi -->
  1503. </div><!-- /exam-tab-yingzhi -->
  1504. </div><!-- /page-exam-template -->
  1505. </div><!-- end admin-body -->
  1506. <script>
  1507. // ===== PAGE ROUTING =====
  1508. function showPage(id) {
  1509. document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
  1510. const target = document.getElementById(id);
  1511. if (target) { target.classList.add('active'); window.scrollTo(0,0); }
  1512. // Highlight matching nav item by onclick attr
  1513. document.querySelectorAll('.nav-item').forEach(n => {
  1514. n.classList.remove('active');
  1515. const oc = n.getAttribute('onclick') || '';
  1516. if (oc.includes("'" + id + "'")) n.classList.add('active');
  1517. // detail pages highlight their parent list item
  1518. if (id === 'page-training-activity-detail' && oc.includes("'page-training-activity-list'")) n.classList.add('active');
  1519. });
  1520. }
  1521. // switchMenu kept for backwards compat with any remaining onclick refs — no-op since sidebar is now unified
  1522. function switchMenu() {}
  1523. // ===== CERT TEMPLATE TOGGLE =====
  1524. function toggleCertTemplate(checkbox) {
  1525. document.getElementById('cert-template-section').style.display = checkbox.checked ? 'block' : 'none';
  1526. }
  1527. // ===== TRAINING SCOPE MODE =====
  1528. function setScopeMode(mode) {
  1529. document.getElementById('scope-dept-section').style.display = mode === 'dept' ? 'block' : 'none';
  1530. document.getElementById('scope-person-section').style.display = mode === 'person' ? 'block' : 'none';
  1531. ['all','dept','person'].forEach(function(m) {
  1532. var lbl = document.getElementById('scope-label-' + m);
  1533. if (lbl) {
  1534. lbl.style.borderColor = m === mode ? 'var(--blue)' : 'var(--gray-border)';
  1535. lbl.style.background = m === mode ? 'var(--blue-xlight)' : '';
  1536. lbl.style.color = m === mode ? 'var(--blue)' : '';
  1537. }
  1538. });
  1539. }
  1540. // ===== PUBLISH TOGGLE CLICK =====
  1541. document.addEventListener('click', function(e) {
  1542. const wrap = e.target.closest('.pub-slider');
  1543. if (!wrap) return;
  1544. const isOn = wrap.classList.contains('pub-on');
  1545. wrap.classList.toggle('pub-on', !isOn);
  1546. wrap.classList.toggle('pub-off', isOn);
  1547. });
  1548. // ===== LIST/GRID VIEW =====
  1549. function setListView(mode) {
  1550. const grid = document.getElementById('activity-grid-view');
  1551. const list = document.getElementById('activity-list-view');
  1552. if (!grid || !list) return;
  1553. if (mode === 'grid') { grid.style.display = 'grid'; list.style.display = 'none'; }
  1554. else { grid.style.display = 'none'; list.style.display = 'block'; }
  1555. }
  1556. // ===== MODALS =====
  1557. function showModal(id) {
  1558. const m = document.getElementById(id);
  1559. if (m) m.classList.add('active');
  1560. }
  1561. function hideModal(id) {
  1562. const m = document.getElementById(id);
  1563. if (m) m.classList.remove('active');
  1564. }
  1565. // Close modal on backdrop click
  1566. document.querySelectorAll('.modal-overlay').forEach(m => {
  1567. m.addEventListener('click', function(e) {
  1568. if (e.target === this) this.classList.remove('active');
  1569. });
  1570. });
  1571. // ===== EXAM TEMPLATE PAGE =====
  1572. function switchExamTab(tab) {
  1573. document.querySelectorAll('.exam-tab-panel').forEach(function(p) { p.style.display = 'none'; });
  1574. var panel = document.getElementById('exam-tab-' + tab);
  1575. if (panel) {
  1576. panel.style.display = tab === 'yingzhi' ? 'flex' : 'flex';
  1577. if (tab === 'fenji') {
  1578. // ensure flex for fenji sub-tab wrapper
  1579. panel.style.display = 'block';
  1580. }
  1581. }
  1582. document.querySelectorAll('#exam-top-tabs .view-tab').forEach(function(t) { t.classList.remove('active'); });
  1583. var tabMap = { kaixue: 0, fenji: 1, yingzhi: 2 };
  1584. var tabs = document.querySelectorAll('#exam-top-tabs .view-tab');
  1585. if (tabs[tabMap[tab]]) tabs[tabMap[tab]].classList.add('active');
  1586. }
  1587. function switchExamSection(prefix, section) {
  1588. // Hide all sections in this prefix's container
  1589. var secs = document.querySelectorAll('#' + prefix + '-sec-basic, #' + prefix + '-sec-preexam, #' + prefix + '-sec-cheat, #' + prefix + '-sec-cert');
  1590. secs.forEach(function(s) { s.style.display = 'none'; });
  1591. var target = document.getElementById(prefix + '-sec-' + section);
  1592. if (target) target.style.display = 'block';
  1593. // Update nav active state - find nav items in same sidebar
  1594. var secContainer = target && target.closest('[style*="overflow:hidden"]');
  1595. if (secContainer) {
  1596. secContainer.querySelectorAll('.nav-item').forEach(function(n) {
  1597. n.classList.remove('active');
  1598. var oc = n.getAttribute('onclick') || '';
  1599. if (oc.includes("'" + section + "'")) n.classList.add('active');
  1600. });
  1601. }
  1602. }
  1603. function switchGradeTab(grade) {
  1604. document.querySelectorAll('.fenji-grade-panel').forEach(function(p) { p.style.display = 'none'; });
  1605. var panel = document.getElementById('fenji-grade-' + grade);
  1606. if (panel) panel.style.display = 'flex';
  1607. document.querySelectorAll('#fenji-grade-tabs .view-tab').forEach(function(t) { t.classList.remove('active'); });
  1608. var gradeMap = { g1: 0, g2: 1, g3: 2, g4: 3 };
  1609. var tabs = document.querySelectorAll('#fenji-grade-tabs .view-tab');
  1610. if (tabs[gradeMap[grade]]) tabs[gradeMap[grade]].classList.add('active');
  1611. }
  1612. function setKaixueScope(mode) {
  1613. document.getElementById('kaixue-scope-table').style.display = (mode === 'specify' || mode === 'student') ? 'block' : 'none';
  1614. document.querySelectorAll('[name="kaixue-scope"]').forEach(function(r) {
  1615. r.checked = (r.value === mode);
  1616. });
  1617. }
  1618. function stepNum(id, delta) {
  1619. var el = document.getElementById(id);
  1620. if (!el) return;
  1621. var val = (parseInt(el.value, 10) || 0) + delta;
  1622. var min = parseInt(el.min, 10);
  1623. if (!isNaN(min) && val < min) val = min;
  1624. el.value = val;
  1625. }
  1626. function updateKaixueHours() {
  1627. // Reflect checked user types in the hours table
  1628. var tbody = document.getElementById('kaixue-hours-tbody');
  1629. if (!tbody) return;
  1630. var types = [
  1631. { id: 'kaixue-user-undergrad', label: '新入学本科生', def: 4 },
  1632. { id: 'kaixue-user-postgrad', label: '新入学研究生', def: 4 },
  1633. { id: 'kaixue-user-staff', label: '新入职教职工', def: 2 }
  1634. ];
  1635. var rows = '';
  1636. types.forEach(function(t) {
  1637. var cb = document.getElementById(t.id);
  1638. if (cb && cb.checked) {
  1639. rows += '<tr><td>' + t.label + '</td><td><input type="number" class="form-input" style="width:80px;" value="' + t.def + '" min="0"></td><td><input class="form-input" placeholder="备注说明"></td></tr>';
  1640. }
  1641. });
  1642. tbody.innerHTML = rows || '<tr><td colspan="3" style="text-align:center;color:var(--text-hint);">请先选择用户类别</td></tr>';
  1643. }
  1644. </script>
  1645. </body>
  1646. </html>