|
@@ -258,6 +258,54 @@ html, body { width:100%; height:100%; overflow:auto; background:var(--bg-deep);
|
|
|
.warn-detail { font-size:25px; color:var(--text-dim); display:flex; align-items:center; gap:10px; }
|
|
.warn-detail { font-size:25px; color:var(--text-dim); display:flex; align-items:center; gap:10px; }
|
|
|
.warn-metric-val { color:#fb923c; font-weight:600; }
|
|
.warn-metric-val { color:#fb923c; font-weight:600; }
|
|
|
|
|
|
|
|
|
|
+/* ========== CHEMICAL INVENTORY TAGS ========== */
|
|
|
|
|
+.chem-tag-row { display:flex; gap:12px; margin-bottom:12px; }
|
|
|
|
|
+.chem-tag {
|
|
|
|
|
+ flex:1; padding:14px 18px; border-radius:8px; text-align:center; cursor:pointer;
|
|
|
|
|
+ font-size:26px; font-weight:600; letter-spacing:2px; border:1px solid rgba(30,144,255,0.3);
|
|
|
|
|
+ background:var(--bg-card); color:var(--text-dim); transition:all 0.25s;
|
|
|
|
|
+}
|
|
|
|
|
+.chem-tag.active {
|
|
|
|
|
+ border-color:var(--cyan); color:var(--cyan);
|
|
|
|
|
+ background:rgba(0,216,255,0.1); text-shadow:0 0 10px rgba(0,216,255,0.5);
|
|
|
|
|
+ box-shadow:0 0 12px rgba(0,216,255,0.15);
|
|
|
|
|
+}
|
|
|
|
|
+.chem-tag:hover:not(.active) { border-color:rgba(30,144,255,0.6); color:var(--text); }
|
|
|
|
|
+
|
|
|
|
|
+/* chemical inventory summary cards */
|
|
|
|
|
+.chem-stat-card {
|
|
|
|
|
+ border-radius:10px; padding:16px 20px;
|
|
|
|
|
+ background:rgba(4,14,38,0.7); border:1px solid rgba(30,144,255,0.15);
|
|
|
|
|
+ transition:border-color 0.25s;
|
|
|
|
|
+}
|
|
|
|
|
+.chem-stat-card:hover { border-color:rgba(30,144,255,0.4); }
|
|
|
|
|
+.chem-stat-card.total { border-color:rgba(30,144,255,0.3); background:rgba(30,144,255,0.06); text-align:center; }
|
|
|
|
|
+.chem-stat-card.ctrl { border-color:rgba(239,68,68,0.25); background:rgba(239,68,68,0.05); }
|
|
|
|
|
+.chem-stat-card.free { border-color:rgba(0,216,255,0.2); background:rgba(0,216,255,0.04); }
|
|
|
|
|
+.chem-stat-card.cat { border-color:rgba(255,215,64,0.22); background:rgba(255,215,64,0.04); }
|
|
|
|
|
+.csc-label { font-size:24px; color:var(--text-dim); letter-spacing:2px; margin-bottom:4px; }
|
|
|
|
|
+.csc-value { font-size:52px; font-weight:700; color:#ef4444; line-height:1.1; }
|
|
|
|
|
+.csc-value.total { color:var(--white); }
|
|
|
|
|
+.chem-stat-card.total .csc-value { color:var(--white); font-size:60px; }
|
|
|
|
|
+.csc-unit { font-size:24px; font-weight:400; color:var(--text-dim); margin-left:6px; }
|
|
|
|
|
+.csc-sub { font-size:22px; color:rgba(239,68,68,0.55); margin-top:8px; }
|
|
|
|
|
+
|
|
|
|
|
+/* warn-item type: emergency vs chemical */
|
|
|
|
|
+.warn-item.type-emergency {
|
|
|
|
|
+ background:rgba(245,158,11,0.06); border:1px solid rgba(245,158,11,0.25);
|
|
|
|
|
+}
|
|
|
|
|
+.warn-item.type-chemical {
|
|
|
|
|
+ background:rgba(0,216,255,0.04); border:1px solid rgba(0,216,255,0.18);
|
|
|
|
|
+}
|
|
|
|
|
+.warn-type-badge {
|
|
|
|
|
+ display:inline-flex; align-items:center; gap:5px;
|
|
|
|
|
+ padding:4px 12px; border-radius:6px; font-size:22px; font-weight:600; margin-right:10px;
|
|
|
|
|
+}
|
|
|
|
|
+.warn-type-badge.emergency { background:rgba(245,158,11,0.15); color:#fbbf24; }
|
|
|
|
|
+.warn-type-badge.chemical { background:rgba(0,216,255,0.12); color:var(--cyan); }
|
|
|
|
|
+.warn-lab-info { font-size:27px; font-weight:600; color:#fcd34d; flex:1; min-width:0; }
|
|
|
|
|
+.warn-lab-info.chemical { color:#67e8f9; }
|
|
|
|
|
+
|
|
|
/* ========== DEVICE STATS ========== */
|
|
/* ========== DEVICE STATS ========== */
|
|
|
.device-online-row { display:flex; gap:18px; margin-bottom:18px; }
|
|
.device-online-row { display:flex; gap:18px; margin-bottom:18px; }
|
|
|
.device-stat-chip {
|
|
.device-stat-chip {
|
|
@@ -478,6 +526,88 @@ html, body { width:100%; height:100%; overflow:auto; background:var(--bg-deep);
|
|
|
.btn-alert-confirm:hover { background:rgba(239,68,68,0.3); }
|
|
.btn-alert-confirm:hover { background:rgba(239,68,68,0.3); }
|
|
|
.btn-alert-ignore { padding:22px 70px; border-radius:10px; cursor:pointer; border:1px solid var(--border); background:transparent; color:var(--text-dim); font-size:32px; transition:all 0.2s; }
|
|
.btn-alert-ignore { padding:22px 70px; border-radius:10px; cursor:pointer; border:1px solid var(--border); background:transparent; color:var(--text-dim); font-size:32px; transition:all 0.2s; }
|
|
|
|
|
|
|
|
|
|
+/* ========== EVACUATION MODAL ========== */
|
|
|
|
|
+#evac-modal {
|
|
|
|
|
+ position:fixed; inset:0; z-index:10000;
|
|
|
|
|
+ display:none; align-items:center; justify-content:center;
|
|
|
|
|
+ background:rgba(0,2,18,0.94); backdrop-filter:blur(8px);
|
|
|
|
|
+}
|
|
|
|
|
+#evac-modal.show { display:flex; animation:alertFadeIn 0.4s ease; }
|
|
|
|
|
+.evac-inner {
|
|
|
|
|
+ position:relative; width:2820px; border-radius:20px; overflow:hidden;
|
|
|
|
|
+ background:linear-gradient(135deg,rgba(2,8,28,0.99),rgba(1,5,20,0.99));
|
|
|
|
|
+ border:2px solid rgba(30,144,255,0.45);
|
|
|
|
|
+ box-shadow:0 0 200px rgba(30,144,255,0.22), inset 0 0 120px rgba(0,50,140,0.05);
|
|
|
|
|
+ display:flex; flex-direction:column; max-height:96vh;
|
|
|
|
|
+}
|
|
|
|
|
+.evac-hdr {
|
|
|
|
|
+ padding:36px 55px; display:flex; align-items:center; gap:28px; flex-shrink:0;
|
|
|
|
|
+ border-bottom:1px solid rgba(30,144,255,0.2);
|
|
|
|
|
+ background:linear-gradient(90deg,rgba(0,55,160,0.12),transparent);
|
|
|
|
|
+}
|
|
|
|
|
+.evac-hdr-icon { font-size:68px; }
|
|
|
|
|
+.evac-hdr-title { font-size:50px; font-weight:700; color:var(--white); letter-spacing:6px; }
|
|
|
|
|
+.evac-hdr-close {
|
|
|
|
|
+ margin-left:auto; width:68px; height:68px; border-radius:10px;
|
|
|
|
|
+ border:1px solid rgba(30,144,255,0.35); background:rgba(30,144,255,0.08);
|
|
|
|
|
+ color:var(--cyan); font-size:34px; cursor:pointer;
|
|
|
|
|
+ display:flex; align-items:center; justify-content:center; transition:background 0.2s;
|
|
|
|
|
+}
|
|
|
|
|
+.evac-hdr-close:hover { background:rgba(30,144,255,0.22); }
|
|
|
|
|
+.evac-body { display:flex; flex:1; min-height:0; overflow:hidden; }
|
|
|
|
|
+.evac-map-col { flex:1; min-width:0; padding:26px 28px; display:flex; flex-direction:column; gap:14px; }
|
|
|
|
|
+.evac-legend { display:flex; align-items:center; gap:12px; justify-content:flex-end; }
|
|
|
|
|
+.evac-legend-rect { width:36px; height:14px; background:#1e90ff; border-radius:3px; }
|
|
|
|
|
+.evac-legend-text { font-size:26px; color:var(--text-dim); }
|
|
|
|
|
+.evac-map-wrap {
|
|
|
|
|
+ flex:1; min-height:0; position:relative;
|
|
|
|
|
+ border-radius:10px; background:rgba(1,8,28,0.65);
|
|
|
|
|
+ border:1px solid rgba(30,144,255,0.15);
|
|
|
|
|
+}
|
|
|
|
|
+.evac-map-wrap svg { width:100%; height:100%; display:block; }
|
|
|
|
|
+.evac-popup {
|
|
|
|
|
+ position:absolute; left:3%; top:53%;
|
|
|
|
|
+ width:390px; border-radius:10px; padding:18px 24px 20px;
|
|
|
|
|
+ background:rgba(45,4,6,0.97); border:2px solid rgba(239,68,68,0.6);
|
|
|
|
|
+ box-shadow:0 0 50px rgba(239,68,68,0.28); z-index:5; pointer-events:none;
|
|
|
|
|
+}
|
|
|
|
|
+.evac-popup-hdr { display:flex; align-items:center; gap:14px; margin-bottom:12px; }
|
|
|
|
|
+.evac-popup-alarm { font-size:30px; }
|
|
|
|
|
+.evac-popup-time { font-size:22px; color:rgba(255,255,255,0.65); flex:1; }
|
|
|
|
|
+.evac-popup-x { color:rgba(255,255,255,0.45); font-size:22px; }
|
|
|
|
|
+.evac-popup-badge { background:#ef4444; color:#fff; font-size:20px; font-weight:700; border-radius:5px; padding:4px 14px; display:inline-block; margin-bottom:12px; letter-spacing:1px; }
|
|
|
|
|
+.evac-popup-msg { font-size:28px; font-weight:600; color:#fff; letter-spacing:1px; }
|
|
|
|
|
+.evac-right-col { flex:0 0 540px; border-left:1px solid rgba(30,144,255,0.15); display:flex; flex-direction:column; }
|
|
|
|
|
+.evac-right-hdr { padding:22px 28px; font-size:26px; color:var(--text-dim); letter-spacing:3px; border-bottom:1px solid rgba(30,144,255,0.12); background:rgba(30,144,255,0.04); flex-shrink:0; }
|
|
|
|
|
+.evac-cam-slot { flex:1; border-bottom:1px solid rgba(30,144,255,0.1); display:flex; align-items:center; justify-content:center; background:rgba(12,15,26,0.6); color:rgba(255,255,255,0.2); font-size:26px; letter-spacing:3px; min-height:180px; }
|
|
|
|
|
+.evac-floor-lbl { padding:14px 28px; font-size:24px; color:var(--text-dim); letter-spacing:3px; background:rgba(30,144,255,0.06); border-top:1px solid rgba(30,144,255,0.12); border-bottom:1px solid rgba(30,144,255,0.12); flex-shrink:0; }
|
|
|
|
|
+.evac-footer { padding:26px 50px; display:flex; gap:28px; align-items:stretch; border-top:1px solid rgba(30,144,255,0.15); flex-shrink:0; }
|
|
|
|
|
+.evac-metrics { flex:0 0 490px; display:flex; flex-direction:column; gap:12px; justify-content:center; }
|
|
|
|
|
+.evac-metric-row { background:rgba(239,68,68,0.06); border:1px solid rgba(239,68,68,0.2); border-radius:8px; padding:14px 20px; display:flex; justify-content:space-between; align-items:center; }
|
|
|
|
|
+.evac-metric-col .evac-ml { font-size:22px; color:var(--text-dim); margin-bottom:4px; }
|
|
|
|
|
+.evac-metric-col .evac-mv { font-size:28px; font-weight:600; color:#ef4444; }
|
|
|
|
|
+.evac-metric-rt { font-size:26px; color:rgba(255,255,255,0.75); }
|
|
|
|
|
+.evac-broadcast { flex:1; border:1px solid rgba(30,144,255,0.22); border-radius:12px; padding:18px 24px; display:flex; flex-direction:column; gap:14px; background:rgba(30,144,255,0.04); }
|
|
|
|
|
+.evac-bc-hdr { display:flex; align-items:center; gap:14px; }
|
|
|
|
|
+.evac-bc-icon { font-size:28px; }
|
|
|
|
|
+.evac-bc-title { font-size:30px; font-weight:600; color:var(--white); flex:1; }
|
|
|
|
|
+.evac-bc-device { font-size:24px; color:var(--text-dim); }
|
|
|
|
|
+.evac-speaker-row { display:flex; gap:14px; }
|
|
|
|
|
+.evac-speaker-btn { flex:1; padding:14px 8px; border-radius:8px; font-size:23px; border:1px solid rgba(30,144,255,0.35); background:rgba(30,144,255,0.1); color:var(--cyan); cursor:pointer; transition:background 0.2s; white-space:nowrap; }
|
|
|
|
|
+.evac-speaker-btn:hover, .evac-speaker-btn.on { background:rgba(30,144,255,0.25); border-color:var(--cyan); }
|
|
|
|
|
+.evac-input-row { display:flex; gap:14px; }
|
|
|
|
|
+.evac-text-input { flex:1; padding:13px 18px; border-radius:8px; font-size:26px; border:1px solid rgba(30,144,255,0.25); background:rgba(0,8,36,0.7); color:var(--white); outline:none; }
|
|
|
|
|
+.evac-text-input::placeholder { color:rgba(255,255,255,0.2); }
|
|
|
|
|
+.evac-send-btn { padding:13px 40px; border-radius:8px; font-size:26px; font-weight:600; border:1px solid rgba(30,144,255,0.4); background:rgba(30,144,255,0.14); color:var(--cyan); cursor:pointer; transition:background 0.2s; }
|
|
|
|
|
+.evac-send-btn:hover { background:rgba(30,144,255,0.3); }
|
|
|
|
|
+.evac-actions { flex:0 0 380px; display:flex; flex-direction:column; gap:14px; justify-content:center; }
|
|
|
|
|
+.evac-btn-later { padding:22px; border-radius:10px; font-size:30px; font-weight:600; border:1px solid rgba(255,255,255,0.2); background:rgba(255,255,255,0.05); color:rgba(255,255,255,0.55); cursor:pointer; }
|
|
|
|
|
+.evac-btn-later:hover { background:rgba(255,255,255,0.1); }
|
|
|
|
|
+.evac-btn-exec { padding:22px; border-radius:10px; font-size:30px; font-weight:700; border:2px solid #ef4444; background:rgba(239,68,68,0.16); color:#ef4444; cursor:pointer; }
|
|
|
|
|
+.evac-btn-exec:hover { background:rgba(239,68,68,0.32); }
|
|
|
|
|
+.btn-alert-evac { padding:22px 70px; border-radius:10px; cursor:pointer; border:2px solid #f59e0b; background:rgba(245,158,11,0.15); color:#f59e0b; font-size:32px; font-weight:600; transition:all 0.2s; }
|
|
|
|
|
+.btn-alert-evac:hover { background:rgba(245,158,11,0.3); }
|
|
|
|
|
+
|
|
|
/* ========== SCROLLBAR ========== */
|
|
/* ========== SCROLLBAR ========== */
|
|
|
::-webkit-scrollbar { width:8px; height:8px; }
|
|
::-webkit-scrollbar { width:8px; height:8px; }
|
|
|
::-webkit-scrollbar-track { background:transparent; }
|
|
::-webkit-scrollbar-track { background:transparent; }
|
|
@@ -540,11 +670,179 @@ html, body { width:100%; height:100%; overflow:auto; background:var(--bg-deep);
|
|
|
</div>
|
|
</div>
|
|
|
<div class="alert-modal-footer">
|
|
<div class="alert-modal-footer">
|
|
|
<button class="btn-alert-ignore" onclick="closeAlert()">稍后处理</button>
|
|
<button class="btn-alert-ignore" onclick="closeAlert()">稍后处理</button>
|
|
|
|
|
+ <button class="btn-alert-evac" onclick="openEvac()">🚪 应急疏散</button>
|
|
|
<button class="btn-alert-confirm" onclick="closeAlert()">确认处理</button>
|
|
<button class="btn-alert-confirm" onclick="closeAlert()">确认处理</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+<!-- ===== EVACUATION MODAL ===== -->
|
|
|
|
|
+<div id="evac-modal">
|
|
|
|
|
+ <div class="evac-inner">
|
|
|
|
|
+ <!-- Header -->
|
|
|
|
|
+ <div class="evac-hdr">
|
|
|
|
|
+ <span class="evac-hdr-icon">🚪</span>
|
|
|
|
|
+ <span class="evac-hdr-title">应急疏散</span>
|
|
|
|
|
+ <button class="evac-hdr-close" onclick="closeEvac()">✕</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- Body -->
|
|
|
|
|
+ <div class="evac-body">
|
|
|
|
|
+ <!-- Left: floor plan -->
|
|
|
|
|
+ <div class="evac-map-col">
|
|
|
|
|
+ <div class="evac-legend">
|
|
|
|
|
+ <div class="evac-legend-rect"></div>
|
|
|
|
|
+ <span class="evac-legend-text">应急疏散路线图</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-map-wrap">
|
|
|
|
|
+ <svg viewBox="0 0 1060 590" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
+ <defs>
|
|
|
|
|
+ <marker id="ev-arrow" markerWidth="9" markerHeight="7" refX="8" refY="3.5" orient="auto">
|
|
|
|
|
+ <polygon points="0 0, 9 3.5, 0 7" fill="#1e90ff"/>
|
|
|
|
|
+ </marker>
|
|
|
|
|
+ </defs>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Top row background -->
|
|
|
|
|
+ <rect x="45" y="40" width="758" height="185" fill="rgba(0,15,50,0.3)" stroke="rgba(30,144,255,0.28)" stroke-width="1.5"/>
|
|
|
|
|
+ <!-- Emergency exit zone top-right -->
|
|
|
|
|
+ <rect x="808" y="40" width="205" height="185" fill="rgba(30,144,255,0.04)" stroke="rgba(30,144,255,0.22)" stroke-width="1" stroke-dasharray="9,5" rx="2"/>
|
|
|
|
|
+ <text x="910" y="118" text-anchor="middle" fill="rgba(30,144,255,0.6)" font-size="22">紧急</text>
|
|
|
|
|
+ <text x="910" y="146" text-anchor="middle" fill="rgba(30,144,255,0.6)" font-size="22">出口</text>
|
|
|
|
|
+ <line x1="870" y1="132" x2="918" y2="132" stroke="#1e90ff" stroke-width="3" marker-end="url(#ev-arrow)" opacity="0.75"/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- A101 -->
|
|
|
|
|
+ <rect x="45" y="40" width="190" height="185" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="45" y1="40" x2="235" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="235" y1="40" x2="45" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="140" y="140" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A101</text>
|
|
|
|
|
+ <!-- A102 -->
|
|
|
|
|
+ <rect x="235" y="40" width="190" height="185" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="235" y1="40" x2="425" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="425" y1="40" x2="235" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="330" y="140" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A102</text>
|
|
|
|
|
+ <!-- A103 -->
|
|
|
|
|
+ <rect x="425" y="40" width="190" height="185" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="425" y1="40" x2="615" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="615" y1="40" x2="425" y2="225" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="520" y="140" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A103</text>
|
|
|
|
|
+ <!-- A104 (blue — near top-right exit) -->
|
|
|
|
|
+ <rect x="615" y="40" width="193" height="185" fill="rgba(30,144,255,0.1)" stroke="#1e90ff" stroke-width="3"/>
|
|
|
|
|
+ <line x1="615" y1="40" x2="808" y2="225" stroke="rgba(30,144,255,0.18)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="808" y1="40" x2="615" y2="225" stroke="rgba(30,144,255,0.18)" stroke-width="1"/>
|
|
|
|
|
+ <text x="711" y="140" text-anchor="middle" fill="#7ab8e8" font-size="22">A104</text>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Corridor -->
|
|
|
|
|
+ <rect x="45" y="225" width="968" height="85" fill="rgba(0,25,70,0.18)" stroke="rgba(30,144,255,0.2)" stroke-width="1"/>
|
|
|
|
|
+ <text x="350" y="277" text-anchor="middle" fill="rgba(168,204,232,0.38)" font-size="24" letter-spacing="6">走道</text>
|
|
|
|
|
+ <!-- Assembly points -->
|
|
|
|
|
+ <circle cx="645" cy="268" r="22" fill="none" stroke="#1e90ff" stroke-width="2.5" opacity="0.75"/>
|
|
|
|
|
+ <circle cx="755" cy="268" r="22" fill="none" stroke="#1e90ff" stroke-width="2.5" opacity="0.75"/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Bottom section background -->
|
|
|
|
|
+ <rect x="45" y="310" width="968" height="242" fill="rgba(0,15,50,0.3)" stroke="rgba(30,144,255,0.28)" stroke-width="1.5"/>
|
|
|
|
|
+ <!-- Emergency exit zone bottom-left label -->
|
|
|
|
|
+ <text x="22" y="445" text-anchor="middle" fill="rgba(30,144,255,0.58)" font-size="19" transform="rotate(-90,22,445)">紧急出口</text>
|
|
|
|
|
+ <line x1="47" y1="432" x2="16" y2="432" stroke="#1e90ff" stroke-width="3" marker-end="url(#ev-arrow)" opacity="0.7"/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- A105 (red — alert room) -->
|
|
|
|
|
+ <rect x="45" y="310" width="185" height="242" fill="rgba(239,68,68,0.1)" stroke="#ef4444" stroke-width="3"/>
|
|
|
|
|
+ <line x1="45" y1="310" x2="230" y2="552" stroke="rgba(239,68,68,0.22)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="230" y1="310" x2="45" y2="552" stroke="rgba(239,68,68,0.22)" stroke-width="1.5"/>
|
|
|
|
|
+ <text x="137" y="440" text-anchor="middle" fill="#ef4444" font-size="22" font-weight="600">A105</text>
|
|
|
|
|
+ <!-- A106 -->
|
|
|
|
|
+ <rect x="230" y="310" width="185" height="242" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="230" y1="310" x2="415" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="415" y1="310" x2="230" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="322" y="440" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A106</text>
|
|
|
|
|
+ <!-- A107 -->
|
|
|
|
|
+ <rect x="415" y="310" width="185" height="242" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="415" y1="310" x2="600" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="600" y1="310" x2="415" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="507" y="440" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A107</text>
|
|
|
|
|
+ <!-- A108 -->
|
|
|
|
|
+ <rect x="600" y="310" width="185" height="242" fill="rgba(0,15,50,0.25)" stroke="rgba(30,144,255,0.42)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="600" y1="310" x2="785" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <line x1="785" y1="310" x2="600" y2="552" stroke="rgba(30,144,255,0.12)" stroke-width="1"/>
|
|
|
|
|
+ <text x="692" y="440" text-anchor="middle" fill="rgba(168,204,232,0.65)" font-size="22">A108</text>
|
|
|
|
|
+ <!-- A109 (blue — evacuation destination) -->
|
|
|
|
|
+ <rect x="785" y="310" width="228" height="242" fill="rgba(30,144,255,0.1)" stroke="#1e90ff" stroke-width="3"/>
|
|
|
|
|
+ <line x1="785" y1="310" x2="1013" y2="552" stroke="rgba(30,144,255,0.18)" stroke-width="1.5"/>
|
|
|
|
|
+ <line x1="1013" y1="310" x2="785" y2="552" stroke="rgba(30,144,255,0.18)" stroke-width="1.5"/>
|
|
|
|
|
+ <text x="899" y="440" text-anchor="middle" fill="#7ab8e8" font-size="22" font-weight="600">A109</text>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Evacuation route: bottom-left through corridor -->
|
|
|
|
|
+ <line x1="230" y1="432" x2="50" y2="432" stroke="#1e90ff" stroke-width="3" stroke-dasharray="14,7" marker-end="url(#ev-arrow)"/>
|
|
|
|
|
+ <!-- Evacuation route: top-right toward exit -->
|
|
|
|
|
+ <line x1="808" y1="132" x2="862" y2="132" stroke="#1e90ff" stroke-width="3" stroke-dasharray="14,7" marker-end="url(#ev-arrow)"/>
|
|
|
|
|
+ <!-- Connector: corridor down to A104 corridor side -->
|
|
|
|
|
+ <line x1="711" y1="225" x2="711" y2="310" stroke="#1e90ff" stroke-width="3" stroke-dasharray="14,7"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Floating alert popup over A105 -->
|
|
|
|
|
+ <div class="evac-popup">
|
|
|
|
|
+ <div class="evac-popup-hdr">
|
|
|
|
|
+ <span class="evac-popup-alarm">🚨</span>
|
|
|
|
|
+ <span class="evac-popup-time">2026-03-23 14:32:18</span>
|
|
|
|
|
+ <span class="evac-popup-x">✕</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-popup-badge">触发风险</div>
|
|
|
|
|
+ <div class="evac-popup-msg">发生风险:TVOC浓度超标</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- Right: camera feeds -->
|
|
|
|
|
+ <div class="evac-right-col">
|
|
|
|
|
+ <div class="evac-right-hdr">房间号-实验室名称</div>
|
|
|
|
|
+ <div class="evac-cam-slot">实时视频监控</div>
|
|
|
|
|
+ <div class="evac-cam-slot">实时视频监控</div>
|
|
|
|
|
+ <div class="evac-floor-lbl">楼道 2层</div>
|
|
|
|
|
+ <div class="evac-cam-slot">实时视频监控</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- Footer -->
|
|
|
|
|
+ <div class="evac-footer">
|
|
|
|
|
+ <!-- Alert metrics -->
|
|
|
|
|
+ <div class="evac-metrics">
|
|
|
|
|
+ <div class="evac-metric-row">
|
|
|
|
|
+ <div class="evac-metric-col">
|
|
|
|
|
+ <div class="evac-ml">告警指标</div>
|
|
|
|
|
+ <div class="evac-mv">TVOC 浓度超标</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-metric-rt">2.85 / 0.6 mg/m³</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-metric-row">
|
|
|
|
|
+ <div class="evac-metric-col">
|
|
|
|
|
+ <div class="evac-ml">当前值 / 安全阈值</div>
|
|
|
|
|
+ <div class="evac-mv">TVOC 浓度超标</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-metric-rt">2.85 / 0.6 mg/m³</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- Voice broadcast -->
|
|
|
|
|
+ <div class="evac-broadcast">
|
|
|
|
|
+ <div class="evac-bc-hdr">
|
|
|
|
|
+ <span class="evac-bc-icon">📢</span>
|
|
|
|
|
+ <span class="evac-bc-title">语音广播</span>
|
|
|
|
|
+ <span class="evac-bc-device">选择播放设备</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-speaker-row">
|
|
|
|
|
+ <button class="evac-speaker-btn on">NKL1FB1122 喇叭</button>
|
|
|
|
|
+ <button class="evac-speaker-btn on">NKL1FB1122 喇叭</button>
|
|
|
|
|
+ <button class="evac-speaker-btn on">NKL1FB1122 喇叭</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="evac-input-row">
|
|
|
|
|
+ <input type="text" class="evac-text-input" placeholder="请输入喊话内容"/>
|
|
|
|
|
+ <button class="evac-send-btn">发送</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- Action buttons -->
|
|
|
|
|
+ <div class="evac-actions">
|
|
|
|
|
+ <button class="evac-btn-later" onclick="closeEvac()">稍后处理</button>
|
|
|
|
|
+ <button class="evac-btn-exec">执行疏散</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
<!-- ===== MAIN WRAPPER ===== -->
|
|
<!-- ===== MAIN WRAPPER ===== -->
|
|
|
<div id="scale-root">
|
|
<div id="scale-root">
|
|
|
<div class="bg-hex"></div>
|
|
<div class="bg-hex"></div>
|
|
@@ -799,6 +1097,83 @@ html, body { width:100%; height:100%; overflow:auto; background:var(--bg-deep);
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <!-- 危化品库存统计 -->
|
|
|
|
|
+ <div class="panel" style="flex:0 0 auto;min-height:460px">
|
|
|
|
|
+ <div class="border-beam"></div>
|
|
|
|
|
+ <svg class="pc tl" viewBox="0 0 14 14"><path d="M0 14 V0 H14" fill="none" stroke="rgba(30,144,255,0.85)" stroke-width="1.5"/></svg>
|
|
|
|
|
+ <svg class="pc tr" viewBox="0 0 14 14"><path d="M0 14 V0 H14" fill="none" stroke="rgba(30,144,255,0.85)" stroke-width="1.5"/></svg>
|
|
|
|
|
+ <svg class="pc bl" viewBox="0 0 14 14"><path d="M0 14 V0 H14" fill="none" stroke="rgba(30,144,255,0.85)" stroke-width="1.5"/></svg>
|
|
|
|
|
+ <svg class="pc br" viewBox="0 0 14 14"><path d="M0 14 V0 H14" fill="none" stroke="rgba(30,144,255,0.85)" stroke-width="1.5"/></svg>
|
|
|
|
|
+ <div class="panel-header">
|
|
|
|
|
+ <div class="panel-header-icon">⚗️</div>
|
|
|
|
|
+ <span class="panel-title">化学品库存动态统计</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="padding:12px 20px 15px;display:flex;gap:15px;height:calc(100% - 82px)">
|
|
|
|
|
+ <!-- 左侧: 饼图 -->
|
|
|
|
|
+ <div style="flex:1;min-width:0;display:flex;flex-direction:column">
|
|
|
|
|
+ <div id="chart-chem-inventory" style="flex:1;min-height:0"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 右侧: 存量统计 -->
|
|
|
|
|
+ <div style="flex:0 0 380px;display:flex;flex-direction:column;gap:14px;justify-content:center">
|
|
|
|
|
+ <!-- 总量 -->
|
|
|
|
|
+ <div class="chem-stat-card total">
|
|
|
|
|
+ <div class="csc-label">存量化学品总量</div>
|
|
|
|
|
+ <div class="csc-value">1,229<span class="csc-unit">L</span></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 管控 -->
|
|
|
|
|
+ <div class="chem-stat-card ctrl">
|
|
|
|
|
+ <div style="display:flex;justify-content:space-between;align-items:flex-start">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div class="csc-label">管控类化学品</div>
|
|
|
|
|
+ <div class="csc-value">269<span class="csc-unit">L</span></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="csc-pct-ring" style="--pct:21.9;--clr:#ef4444">
|
|
|
|
|
+ <svg viewBox="0 0 60 60" width="80" height="80">
|
|
|
|
|
+ <circle cx="30" cy="30" r="24" fill="none" stroke="rgba(239,68,68,0.12)" stroke-width="6"/>
|
|
|
|
|
+ <circle cx="30" cy="30" r="24" fill="none" stroke="#ef4444" stroke-width="6"
|
|
|
|
|
+ stroke-dasharray="27.5 123" stroke-linecap="round" transform="rotate(-90 30 30)"
|
|
|
|
|
+ style="filter:drop-shadow(0 0 4px rgba(239,68,68,0.7))"/>
|
|
|
|
|
+ <text x="30" y="35" text-anchor="middle" fill="#ef4444" font-size="11" font-weight="700" font-family="Arial">21.9%</text>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="csc-sub">占总量 21.9% · 共 5 类目</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 非管控 -->
|
|
|
|
|
+ <div class="chem-stat-card free">
|
|
|
|
|
+ <div style="display:flex;justify-content:space-between;align-items:flex-start">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div class="csc-label">非管控类化学品</div>
|
|
|
|
|
+ <div class="csc-value" style="color:#00d8ff">960<span class="csc-unit">L</span></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <svg viewBox="0 0 60 60" width="80" height="80">
|
|
|
|
|
+ <circle cx="30" cy="30" r="24" fill="none" stroke="rgba(0,216,255,0.12)" stroke-width="6"/>
|
|
|
|
|
+ <circle cx="30" cy="30" r="24" fill="none" stroke="#00d8ff" stroke-width="6"
|
|
|
|
|
+ stroke-dasharray="101 50.6" stroke-linecap="round" transform="rotate(-90 30 30)"
|
|
|
|
|
+ style="filter:drop-shadow(0 0 4px rgba(0,216,255,0.7))"/>
|
|
|
|
|
+ <text x="30" y="35" text-anchor="middle" fill="#00d8ff" font-size="11" font-weight="700" font-family="Arial">78.1%</text>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="csc-sub" style="color:rgba(0,216,255,0.55)">占总量 78.1% · 共 5 类目</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 类目总数 -->
|
|
|
|
|
+ <div class="chem-stat-card cat">
|
|
|
|
|
+ <div class="csc-label">存量化学品总类目数</div>
|
|
|
|
|
+ <div style="display:flex;align-items:baseline;gap:12px;margin-top:8px">
|
|
|
|
|
+ <div class="csc-value" style="color:#ffd740">10<span class="csc-unit">类</span></div>
|
|
|
|
|
+ <div style="display:flex;gap:6px;align-items:center">
|
|
|
|
|
+ <span style="font-size:22px;color:rgba(239,68,68,0.8)">管控 5类</span>
|
|
|
|
|
+ <span style="font-size:20px;color:rgba(168,204,232,0.3)">|</span>
|
|
|
|
|
+ <span style="font-size:22px;color:rgba(0,216,255,0.8)">非管控 5类</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
</div><!-- /2区 -->
|
|
</div><!-- /2区 -->
|
|
|
|
|
|
|
|
<!-- ======== 3区: 实时监控 ======== -->
|
|
<!-- ======== 3区: 实时监控 ======== -->
|
|
@@ -868,10 +1243,20 @@ html, body { width:100%; height:100%; overflow:auto; background:var(--bg-deep);
|
|
|
<div class="panel-header">
|
|
<div class="panel-header">
|
|
|
<div class="panel-header-icon">⚠️</div>
|
|
<div class="panel-header-icon">⚠️</div>
|
|
|
<span class="panel-title">实验室实时风险预警</span>
|
|
<span class="panel-title">实验室实时风险预警</span>
|
|
|
- <div style="margin-left:auto;display:flex;align-items:center;gap:10px;flex-shrink:0">
|
|
|
|
|
- <span style="font-size:25px;color:var(--text-dim)">本月</span>
|
|
|
|
|
- <span style="font-size:50px;font-weight:700;color:#f59e0b">42</span>
|
|
|
|
|
- <span style="font-size:25px;color:var(--text-dim)">次</span>
|
|
|
|
|
|
|
+ <div style="margin-left:auto;display:flex;align-items:center;gap:18px;flex-shrink:0">
|
|
|
|
|
+ <div style="display:flex;align-items:center;gap:6px">
|
|
|
|
|
+ <span style="font-size:22px">🚨</span>
|
|
|
|
|
+ <span style="font-size:24px;color:var(--text-dim)">应急</span>
|
|
|
|
|
+ <span style="font-size:44px;font-weight:700;color:#f59e0b">28</span>
|
|
|
|
|
+ <span style="font-size:22px;color:var(--text-dim)">次</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="width:1px;height:40px;background:rgba(30,144,255,0.3)"></div>
|
|
|
|
|
+ <div style="display:flex;align-items:center;gap:6px">
|
|
|
|
|
+ <span style="font-size:22px">☣️</span>
|
|
|
|
|
+ <span style="font-size:24px;color:var(--text-dim)">违规带离</span>
|
|
|
|
|
+ <span style="font-size:44px;font-weight:700;color:var(--cyan)">14</span>
|
|
|
|
|
+ <span style="font-size:22px;color:var(--text-dim)">次</span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div style="flex:1;min-height:0;padding:15px 20px;display:flex;flex-direction:column">
|
|
<div style="flex:1;min-height:0;padding:15px 20px;display:flex;flex-direction:column">
|
|
@@ -960,6 +1345,44 @@ setTimeout(() => animFlip('flip-total', 0, 1284, 2000), 600);
|
|
|
setTimeout(() => animFlip('flip-current', 0, 47, 1500), 900);
|
|
setTimeout(() => animFlip('flip-current', 0, 47, 1500), 900);
|
|
|
|
|
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
|
|
+// CHEMICAL INVENTORY (2区 化学品库存动态统计)
|
|
|
|
|
+// ====================================================
|
|
|
|
|
+const CHEM_PIE_DATA = [
|
|
|
|
|
+ {name:'管控化学品', value:269, itemStyle:{color:'#ef4444'}},
|
|
|
|
|
+ {name:'非管控化学品', value:960, itemStyle:{color:'#1e90ff'}},
|
|
|
|
|
+];
|
|
|
|
|
+let chemChart = null;
|
|
|
|
|
+function renderChemChart() {
|
|
|
|
|
+ if (!chemChart) {
|
|
|
|
|
+ const el = document.getElementById('chart-chem-inventory');
|
|
|
|
|
+ if (!el) return;
|
|
|
|
|
+ chemChart = echarts.init(el);
|
|
|
|
|
+ }
|
|
|
|
|
+ chemChart.setOption({
|
|
|
|
|
+ backgroundColor:'transparent',
|
|
|
|
|
+ tooltip:{ trigger:'item', formatter:'{b}<br/>数量:{c} L<br/>占比:{d}%' },
|
|
|
|
|
+ series:[{
|
|
|
|
|
+ type:'pie', radius:['32%','62%'],
|
|
|
|
|
+ center:['50%','52%'],
|
|
|
|
|
+ avoidLabelOverlap:true,
|
|
|
|
|
+ label:{
|
|
|
|
|
+ show:true, position:'outside',
|
|
|
|
|
+ formatter:params => `{name|${params.name}}\n{val|${params.value}L ${params.percent}%}`,
|
|
|
|
|
+ rich:{
|
|
|
|
|
+ name:{ fontSize:24, color:'#a8cce8', lineHeight:30 },
|
|
|
|
|
+ val:{ fontSize:22, color:'#ffd740', lineHeight:26 },
|
|
|
|
|
+ },
|
|
|
|
|
+ distanceToLabelLine:10,
|
|
|
|
|
+ },
|
|
|
|
|
+ labelLine:{ show:true, length:25, length2:35, lineStyle:{color:'rgba(168,204,232,0.5)'} },
|
|
|
|
|
+ emphasis:{ label:{fontSize:26, fontWeight:'bold'}, scale:true, scaleSize:8 },
|
|
|
|
|
+ data: CHEM_PIE_DATA,
|
|
|
|
|
+ }],
|
|
|
|
|
+ }, true);
|
|
|
|
|
+}
|
|
|
|
|
+renderChemChart();
|
|
|
|
|
+
|
|
|
|
|
+// ====================================================
|
|
|
// SENSOR LIST (4区上)
|
|
// SENSOR LIST (4区上)
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
const LABS = [
|
|
const LABS = [
|
|
@@ -998,29 +1421,44 @@ function buildSensorList() {
|
|
|
buildSensorList();
|
|
buildSensorList();
|
|
|
|
|
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
-// WARNING LIST (4区下)
|
|
|
|
|
|
|
+// WARNING LIST (4区下) — 应急预警 + 危化品违规带离
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
const WARNS = [
|
|
const WARNS = [
|
|
|
- {lab:'化学分析实验室', room:'A301', unit:'化学研究所', metric:'TVOC浓度超标', val:'2.85 mg/m³', time:'2026-03-05 14:32:18'},
|
|
|
|
|
- {lab:'有机合成实验室', room:'B302', unit:'化学研究所', metric:'CO₂浓度偏高', val:'725 ppm', time:'2026-03-05 13:58:44'},
|
|
|
|
|
- {lab:'高压实验室', room:'D101', unit:'工程研究所', metric:'温度异常', val:'38.5 °C', time:'2026-03-05 12:15:07'},
|
|
|
|
|
- {lab:'生物安全实验室', room:'B201', unit:'生物研究所', metric:'湿度超标', val:'88% RH', time:'2026-03-05 11:40:22'},
|
|
|
|
|
- {lab:'材料测试实验室', room:'C401', unit:'材料研究所', metric:'TVOC超标', val:'1.2 mg/m³', time:'2026-03-05 10:08:55'},
|
|
|
|
|
- {lab:'光学检测实验室', room:'C203', unit:'物理研究所', metric:'O₂浓度偏低', val:'19.2 %', time:'2026-03-05 09:22:33'},
|
|
|
|
|
- {lab:'精密仪器实验室', room:'A205', unit:'物理研究所', metric:'温度超标', val:'28.5 °C', time:'2026-03-05 08:45:11'},
|
|
|
|
|
- {lab:'低温实验室', room:'D205', unit:'物理研究所', metric:'气压异常', val:'85 kPa', time:'2026-03-04 23:12:40'},
|
|
|
|
|
|
|
+ {type:'emergency', lab:'化学分析实验室', room:'A301', unit:'化学研究所', metric:'TVOC浓度超标', val:'2.85 mg/m³', time:'2026-03-23 14:32:18'},
|
|
|
|
|
+ {type:'chemical', lab:'有机合成实验室', room:'B302', unit:'化学研究所', person:'李研究员', action:'盐酸未正常使用违规带离', time:'2026-03-23 13:58:44'},
|
|
|
|
|
+ {type:'emergency', lab:'有机合成实验室', room:'B302', unit:'化学研究所', metric:'CO₂浓度偏高', val:'725 ppm', time:'2026-03-23 12:15:07'},
|
|
|
|
|
+ {type:'chemical', lab:'生物安全实验室', room:'B201', unit:'生物研究所', person:'王实验员', action:'甲醇溶液违规带出实验室', time:'2026-03-22 17:40:22'},
|
|
|
|
|
+ {type:'emergency', lab:'高压实验室', room:'D101', unit:'工程研究所', metric:'温度异常', val:'38.5 °C', time:'2026-03-22 12:15:07'},
|
|
|
|
|
+ {type:'emergency', lab:'生物安全实验室', room:'B201', unit:'生物研究所', metric:'湿度超标', val:'88% RH', time:'2026-03-21 11:40:22'},
|
|
|
|
|
+ {type:'chemical', lab:'材料测试实验室', room:'C401', unit:'材料研究所', person:'张工程师', action:'硫酸未经审批带离', time:'2026-03-21 10:08:55'},
|
|
|
|
|
+ {type:'emergency', lab:'光学检测实验室', room:'C203', unit:'物理研究所', metric:'O₂浓度偏低', val:'19.2 %', time:'2026-03-20 09:22:33'},
|
|
|
|
|
+ {type:'chemical', lab:'精密仪器实验室', room:'A205', unit:'物理研究所', person:'赵研究员', action:'丙酮溶液违规带离实验楼', time:'2026-03-19 16:45:11'},
|
|
|
|
|
+ {type:'emergency', lab:'低温实验室', room:'D205', unit:'物理研究所', metric:'气压异常', val:'85 kPa', time:'2026-03-18 23:12:40'},
|
|
|
];
|
|
];
|
|
|
function buildWarnList() {
|
|
function buildWarnList() {
|
|
|
const el = document.getElementById('warn-list'); if (!el) return;
|
|
const el = document.getElementById('warn-list'); if (!el) return;
|
|
|
const all = [...WARNS, ...WARNS];
|
|
const all = [...WARNS, ...WARNS];
|
|
|
- el.innerHTML = all.map(w => `
|
|
|
|
|
- <div class="warn-item">
|
|
|
|
|
- <div class="warn-item-head">
|
|
|
|
|
- <span class="warn-lab">🚨 ${w.lab}(${w.room})- ${w.unit}</span>
|
|
|
|
|
|
|
+ el.innerHTML = all.map(w => {
|
|
|
|
|
+ const isEm = w.type === 'emergency';
|
|
|
|
|
+ const labCls = isEm ? 'warn-lab-info' : 'warn-lab-info chemical';
|
|
|
|
|
+ const badge = isEm
|
|
|
|
|
+ ? '<span class="warn-type-badge emergency">🚨 应急预警</span>'
|
|
|
|
|
+ : '<span class="warn-type-badge chemical">☣️ 违规带离</span>';
|
|
|
|
|
+ const detail = isEm
|
|
|
|
|
+ ? `异常传感器:<span class="warn-metric-val">${w.metric} ${w.val}</span>`
|
|
|
|
|
+ : `<span style="color:#67e8f9">${w.person}</span> — <span style="color:#fbbf24">${w.action}</span>`;
|
|
|
|
|
+ return `
|
|
|
|
|
+ <div class="warn-item type-${w.type}">
|
|
|
|
|
+ <div class="warn-item-head" style="flex-wrap:wrap;gap:8px 15px">
|
|
|
|
|
+ <div style="display:flex;align-items:center;flex:1;min-width:0;gap:8px">
|
|
|
|
|
+ ${badge}
|
|
|
|
|
+ <span class="${labCls}">${w.lab}(${w.room})- ${w.unit}</span>
|
|
|
|
|
+ </div>
|
|
|
<span class="warn-time">${w.time}</span>
|
|
<span class="warn-time">${w.time}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="warn-detail">异常指标:<span class="warn-metric-val">${w.metric} ${w.val}</span></div>
|
|
|
|
|
- </div>`).join('');
|
|
|
|
|
|
|
+ <div class="warn-detail">${detail}</div>
|
|
|
|
|
+ </div>`;
|
|
|
|
|
+ }).join('');
|
|
|
}
|
|
}
|
|
|
buildWarnList();
|
|
buildWarnList();
|
|
|
|
|
|
|
@@ -1028,6 +1466,8 @@ buildWarnList();
|
|
|
// ALERT MODAL + CAMERA
|
|
// ALERT MODAL + CAMERA
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
function closeAlert() { document.getElementById('alert-modal').classList.remove('show'); }
|
|
function closeAlert() { document.getElementById('alert-modal').classList.remove('show'); }
|
|
|
|
|
+function openEvac() { document.getElementById('evac-modal').classList.add('show'); }
|
|
|
|
|
+function closeEvac() { document.getElementById('evac-modal').classList.remove('show'); }
|
|
|
|
|
|
|
|
let alertCamRAF = null;
|
|
let alertCamRAF = null;
|
|
|
function startAlertCam() {
|
|
function startAlertCam() {
|
|
@@ -1436,7 +1876,7 @@ function drawFakeCam(canvas, idx) {
|
|
|
// RESIZE CHARTS
|
|
// RESIZE CHARTS
|
|
|
// ====================================================
|
|
// ====================================================
|
|
|
window.addEventListener('resize', () => {
|
|
window.addEventListener('resize', () => {
|
|
|
- ['chart-donut','chart-stack','chart-line','chart-gauge','chart-ring','chart-pie-status','chart-pie-usage'].forEach(id => {
|
|
|
|
|
|
|
+ ['chart-donut','chart-stack','chart-line','chart-gauge','chart-ring','chart-pie-status','chart-pie-usage','chart-chem-inventory'].forEach(id => {
|
|
|
const el = document.getElementById(id);
|
|
const el = document.getElementById(id);
|
|
|
if (el) { const c = echarts.getInstanceByDom(el); if (c) c.resize(); }
|
|
if (el) { const c = echarts.getInstanceByDom(el); if (c) c.resize(); }
|
|
|
});
|
|
});
|