Files
platform-prototype/final.js
T
liuyunqin d525cf0eb3 feat:西安应急大屏 v3 重构 + 膳食建议页 AI 升级
- 西安应急大屏:index.html 归档为 index-v2.html,新增 index-v3.html(重排布局、统计卡片/告警柱状/心脑血管预警/体检雷达图)
- 同步更新 server.js 默认入口及 emergency-app、employee-app 各页面返回/跳转引用至 index-v2.html
- 体重管理:diet-advice.html 大幅升级(偏好标签条、慢病提醒、热量/营养/钠嘌呤指标、三餐推荐),新增膳食推荐 AI 算法需求文档
2026-06-29 18:00:04 +08:00

225 lines
25 KiB
JavaScript

const fs=require('fs'),f='other-module/emergency-screen/xian-center/index-v3.html';
let d=fs.readFileSync(f,'utf8');
const ok=(s,desc)=>{if(!s){console.log('FAIL: '+desc);process.exit(1);}console.log('OK: '+desc);};
// ═══ CSS changes (no Chinese text needed) ═══
d=d.replace('grid-template-columns:1fr 1fr;gap:10px;','grid-template-columns:1fr 1fr 1fr;gap:8px;');
d=d.replace('.stat-card{background:rgba(24,144,255,0.06);border:1px solid var(--panel-border);border-radius:8px;\r\n padding:12px;text-align:center;transition:all .2s;cursor:pointer;}',
'.stat-card{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:3px;\r\n padding:10px 6px;min-height:66px;text-align:center;position:relative;overflow:hidden;cursor:pointer;\r\n background:linear-gradient(135deg,rgba(24,144,255,0.10),rgba(10,30,60,0.45));\r\n border:1px solid var(--panel-border);border-radius:8px;transition:all .25s;}\r\n .stat-card::before{content:\"\";position:absolute;left:0;top:20%;bottom:20%;width:3px;\r\n background:var(--primary);border-radius:0 2px 2px 0;box-shadow:0 0 8px rgba(24,144,255,0.55);}\r\n .stat-card::after{content:\"\";position:absolute;inset:0;pointer-events:none;\r\n background:radial-gradient(ellipse at 100% 0%,rgba(24,144,255,0.06),transparent 55%);}');
d=d.replace('.stat-card:hover{border-color:rgba(24,144,255,0.35);box-shadow:0 0 12px rgba(24,144,255,0.1);}',
'.stat-card:hover{border-color:rgba(24,144,255,0.45);box-shadow:0 0 16px rgba(24,144,255,0.15);\r\n transform:translateY(-2px);background:linear-gradient(135deg,rgba(24,144,255,0.16),rgba(10,30,60,0.50));}');
d=d.replace('.stat-card__num{font-size:24px;font-weight:700;color:#fff;line-height:1;text-shadow:0 0 8px rgba(255,255,255,0.2);}',
'.stat-card__num{font-size:20px;font-weight:700;color:#fff;line-height:1;\r\n text-shadow:0 0 10px rgba(24,144,255,0.35);font-variant-numeric:tabular-nums;letter-spacing:0.5px;}');
d=d.replace('.stat-card__label{font-size:11px;color:var(--text-sub);margin-top:6px;}',
'.stat-card__label{font-size:10px;color:var(--text-sub);white-space:nowrap;letter-spacing:0.3px;}');
for(const[old,nw] of[
['.stat-card--danger .stat-card__num{color:var(--danger);text-shadow:0 0 8px rgba(255,77,79,0.3);}',
'.stat-card--danger::before{background:var(--danger);box-shadow:0 0 8px rgba(255,77,79,0.55);}\r\n .stat-card--danger .stat-card__num{color:var(--danger);text-shadow:0 0 10px rgba(255,77,79,0.4);}\r\n .stat-card--danger::after{background:radial-gradient(ellipse at 100% 0%,rgba(255,77,79,0.06),transparent 55%);}\r\n .stat-card--danger:hover{border-color:rgba(255,77,79,0.3);box-shadow:0 0 16px rgba(255,77,79,0.15);}'],
['.stat-card--warning .stat-card__num{color:var(--warning);text-shadow:0 0 8px rgba(250,173,20,0.3);}',
'.stat-card--warning::before{background:var(--warning);box-shadow:0 0 8px rgba(250,173,20,0.55);}\r\n .stat-card--warning .stat-card__num{color:var(--warning);text-shadow:0 0 10px rgba(250,173,20,0.4);}\r\n .stat-card--warning::after{background:radial-gradient(ellipse at 100% 0%,rgba(250,173,20,0.06),transparent 55%);}\r\n .stat-card--warning:hover{border-color:rgba(250,173,20,0.3);box-shadow:0 0 16px rgba(250,173,20,0.15);}'],
['.stat-card--success .stat-card__num{color:var(--success);text-shadow:0 0 8px rgba(82,196,26,0.3);}',
'.stat-card--success::before{background:var(--success);box-shadow:0 0 8px rgba(82,196,26,0.55);}\r\n .stat-card--success .stat-card__num{color:var(--success);text-shadow:0 0 10px rgba(82,196,26,0.4);}\r\n .stat-card--success::after{background:radial-gradient(ellipse at 100% 0%,rgba(82,196,26,0.06),transparent 55%);}\r\n .stat-card--success:hover{border-color:rgba(82,196,26,0.3);box-shadow:0 0 16px rgba(82,196,26,0.15);}']
]) d=d.replace(old,nw);
d=d.replace('/* ========== 左侧:服务详情 ========== */','/* ========== 左侧:应急事件统计卡片 ========== */');
// CSS: alarm-bars + radar
d=d.replace(' /* ========== 左侧:体检数据排行 ========== */',
` /* ========== 左侧:穿戴告警统计柱状图 ========== */
.panel--alarm-bars .panel__body{padding:8px 10px 10px;overflow:hidden;}
.alarm-bars{display:flex;flex-direction:column;gap:7px;}
.alarm-bars__row{display:flex;align-items:center;gap:8px;min-height:22px;}
.alarm-bars__label{width:54px;font-size:10px;color:var(--text-sub);text-align:right;flex-shrink:0;white-space:nowrap;line-height:1.2;}
.alarm-bars__track{flex:1;height:12px;background:rgba(24,144,255,0.06);border-radius:6px;overflow:hidden;position:relative;}
.alarm-bars__bar{height:100%;border-radius:6px;transition:width .6s ease;position:relative;min-width:4px;}
.alarm-bars__bar::after{content:"";position:absolute;right:0;top:0;bottom:0;width:2px;background:rgba(255,255,255,0.25);border-radius:0 6px 6px 0;}
.alarm-bars__count{width:32px;font-size:11px;font-weight:700;color:#fff;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums;}
/* ========== 右侧:体检数据雷达图 ========== */
.panel--checkup-radar .panel__body{padding:6px 10px 8px;overflow:hidden;}
.checkup-radar{display:flex;align-items:center;justify-content:center;}
.checkup-radar svg{width:100%;max-width:280px;height:auto;display:block;}
.radar-dot{animation:radarPulse 2s ease-in-out infinite;filter:drop-shadow(0 0 4px rgba(24,144,255,0.6));}
@keyframes radarPulse{0%,100%{opacity:0.6;r:2.5;}50%{opacity:1;r:3.5;}}
/* ========== 左侧:体检数据排行 ========== */`);
// CSS: AI risk tooltip
d=d.replace('.ai-risk-col{min-width:0;display:flex;flex-direction:column;align-items:center;gap:4px;}',
'.ai-risk-col{min-width:0;display:flex;flex-direction:column;align-items:center;gap:4px;position:relative;}');
d=d.replace('.ai-risk-stack{height:72px;width:32px;display:flex;flex-direction:column;justify-content:flex-end;\r\n border-radius:5px 5px 3px 3px;overflow:hidden;background:rgba(24,144,255,0.06);\r\n box-shadow:inset 0 0 0 1px rgba(24,144,255,0.12);}',
'.ai-risk-stack{height:72px;width:32px;display:flex;flex-direction:column;justify-content:flex-end;\r\n border-radius:5px 5px 3px 3px;overflow:hidden;background:rgba(24,144,255,0.06);\r\n box-shadow:inset 0 0 0 1px rgba(24,144,255,0.12);cursor:pointer;}\r\n .ai-risk-stack:hover{border-color:rgba(24,144,255,0.4);}\r\n .ai-risk-tip{position:absolute;bottom:80px;left:50%;transform:translateX(-50%);z-index:30;\r\n background:rgba(10,22,48,0.96);border:1px solid rgba(24,144,255,0.35);border-radius:8px;\r\n padding:8px 10px;white-space:nowrap;pointer-events:none;\r\n opacity:0;transition:opacity .2s,bottom .2s;\r\n box-shadow:0 4px 18px rgba(0,0,0,0.5),0 0 12px rgba(24,144,255,0.15);\r\n font-size:11px;color:var(--text-sub);line-height:1.7;text-align:left;}\r\n .ai-risk-tip::after{content:\"\";position:absolute;top:100%;left:50%;transform:translateX(-50%);\r\n width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;\r\n border-top:5px solid rgba(10,22,48,0.96);}\r\n .ai-risk-tip b{font-weight:600;}\r\n .ai-risk-col:hover .ai-risk-tip{opacity:1;bottom:84px;}');
console.log('CSS all done');
// ═══ HTML: Read 4th stat-card directly from file ═══
const ljsl=d.indexOf('累计受理次数');
const cardBeg=d.lastIndexOf('<div class="stat-card',ljsl);
const gridEnd=d.indexOf('</div>\r\n </div>',ljsl);
const card4=d.substring(cardBeg,gridEnd);
let card4n=card4.replace('累计受理次数','累计工单');
card4n+='\r\n <div class="stat-card stat-card--warning" onclick="openWorkOrderStat(\'待处理\');event.stopPropagation();"><div class="stat-card__num">156</div><div class="stat-card__label">待处理工单</div></div>\r\n <div class="stat-card" onclick="openWorkOrderStat(\'已完成\');event.stopPropagation();"><div class="stat-card__num">3,232</div><div class="stat-card__label">已完成工单</div></div>';
d=d.replace(card4,card4n);
ok(d.includes('待处理工单'),'6 stat cards');
// ═══ HTML: Replace help events with alarm bars ═══
const helpBeg=d.indexOf(' <!-- 应急求助事件(自动滚动列表) -->');
const healthBeg=d.indexOf(' <!-- 人员现状监测(环形图) -->');
const helpBlock=d.substring(helpBeg,healthBeg);
const alarmBars=` <!-- 穿戴告警统计(柱状图) -->
<div class="panel panel--alarm-bars" style="flex-shrink:0;">
<div class="panel__header">
<div class="panel__header-diamond"></div>
<span class="panel__header-title">穿戴告警统计</span>
<select class="panel__header-select" onchange="switchAlarmBarPeriod(this.value)">
<option value="week" selected>近一周</option>
<option value="month">近一月</option>
<option value="year">近一年</option>
</select>
</div>
<div class="panel__body">
<div class="alarm-bars" id="alarmBarsChart">
<div class="alarm-bars__row"><span class="alarm-bars__label">心率异常</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:100%;background:#FF4D4F;box-shadow:0 0 8px #FF4D4F;"></div></div><span class="alarm-bars__count">126</span></div>
<div class="alarm-bars__row"><span class="alarm-bars__label">体温异常</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:42.9%;background:#FAAD14;box-shadow:0 0 8px #FAAD14;"></div></div><span class="alarm-bars__count">54</span></div>
<div class="alarm-bars__row"><span class="alarm-bars__label">血氧异常</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:30.2%;background:#b37feb;box-shadow:0 0 8px #b37feb;"></div></div><span class="alarm-bars__count">38</span></div>
<div class="alarm-bars__row"><span class="alarm-bars__label">压力异常</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:57.1%;background:#1890FF;box-shadow:0 0 8px #1890FF;"></div></div><span class="alarm-bars__count">72</span></div>
<div class="alarm-bars__row"><span class="alarm-bars__label">一键告警</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:14.3%;background:#ff7875;box-shadow:0 0 8px #ff7875;"></div></div><span class="alarm-bars__count">18</span></div>
<div class="alarm-bars__row"><span class="alarm-bars__label">摔倒</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:7.1%;background:#f5222d;box-shadow:0 0 8px #f5222d;"></div></div><span class="alarm-bars__count">9</span></div>
</div>
</div>
</div>
`;
d=d.replace(helpBlock,alarmBars);
ok(d.includes('alarmBarsChart')&&!d.includes('应急求助事件(自动滚动列表)'),'alarm bars + remove help');
// ═══ HTML: Move AI from right to left ═══
const aiBeg=d.indexOf('a id="aiPanel"');
const aiBlockStart=d.lastIndexOf(' <!-- AI智能预警分析 -->',aiBeg);
const rAside=d.lastIndexOf(' </aside>');
const aiBlock=d.substring(aiBlockStart,rAside);
d=d.split(aiBlock).join('');// Remove from right
// Insert before 人员现状监测 on left
const healthPos=d.indexOf(' <!-- 人员现状监测(环形图) -->');
d=d.substring(0,healthPos)+aiBlock+'\r\n'+d.substring(healthPos);
ok(d.includes('AI智能预警分析'),'AI moved to left');
// ═══ HTML: AI rename + dropdown + tooltips ═══
d=d.replace('AI智能预警分析</span>','心脑血管预警统计<span class="panel__header-link" style="margin-left:4px;" onclick="openCvRecordModal();event.stopPropagation();">(历史记录)</span></span>');
d=d.replace(' <!-- AI智能预警分析 -->',' <!-- 心脑血管预警统计 -->');
// Remove old history link, add dropdown
const histBeg=d.indexOf('pan>历史记录</span>');
const linkLineStart=d.lastIndexOf('<!-- [交互] 点击查看',histBeg);
const linkLineEnd=d.indexOf('\r\n',histBeg);
d=d.substring(0,linkLineStart)+'<select class="panel__header-select" onchange="switchCvPeriod(this.value)"><option value="week" selected>近一周</option><option value="month">近一月</option><option value="year">近一年</option></select>'+d.substring(linkLineEnd);
// Remove title attrs
['心梗:高危1、中危2、低危3','脑梗:高危1、中危1、低危4','脑出血:高危1、中危1、低危2','猝死:高危0、中危1、低危3'].forEach(t=>{
const p=d.indexOf('title="'+t+'"');if(p>0)d=d.substring(0,p)+d.substring(p+t.length+8);
});
// Add tooltips + update legend
[{n:'心梗',t:'<div class="ai-risk-tip"><b style="color:#ff7875;">高危</b> 1 &nbsp; <b style="color:#ffc53d;">中危</b> 2 &nbsp; <b style="color:#40a9ff;">低危</b> 3</div>'},
{n:'脑梗',t:'<div class="ai-risk-tip"><b style="color:#ff7875;">高危</b> 1 &nbsp; <b style="color:#ffc53d;">中危</b> 1 &nbsp; <b style="color:#40a9ff;">低危</b> 4</div>'},
{n:'脑出血',t:'<div class="ai-risk-tip"><b style="color:#ff7875;">高危</b> 1 &nbsp; <b style="color:#ffc53d;">中危</b> 1 &nbsp; <b style="color:#40a9ff;">低危</b> 2</div>'},
{n:'猝死',t:'<div class="ai-risk-tip"><b style="color:#ffc53d;">中危</b> 1 &nbsp; <b style="color:#40a9ff;">低危</b> 3</div>'}].forEach(x=>{
d=d.replace('<div class="ai-risk-col__name">'+x.n+'</div>',x.t+'\r\n <div class="ai-risk-col__name">'+x.n+'</div>');
});
d=d.replace('</i>高危</span>','</i>高危 3</span>');
d=d.replace('</i>中危</span>','</i>中危 5</span>');
d=d.replace('</i>低危</span>','</i>低危 12</span>');
ok(d.includes('心脑血管预警统计'),'AI panel updated');
// ═══ HTML: Right panel → 体检数据 + 咨询数据 ═══
const rpBeg=d.indexOf(' <!-- 右侧面板 -->');
const rpEnd=d.indexOf(' </aside>',rpBeg);
const nrp=` <!-- 右侧面板 -->
<aside class="side">
<!-- 体检数据(雷达图) -->
<div class="panel panel--checkup-radar" style="flex-shrink:0;">
<div class="panel__header">
<div class="panel__header-diamond"></div>
<span class="panel__header-title">体检数据</span>
<select class="panel__header-select" onchange="switchCheckupPeriod(this.value)">
<option value="week" selected>近一周</option>
<option value="month">近一月</option>
<option value="year">近一年</option>
</select>
</div>
<div class="panel__body">
<div class="checkup-radar" id="checkupRadar">
<svg viewBox="0 0 240 240" aria-hidden="true">
<polygon points="120,30 198,75 198,165 120,210 42,165 42,75" fill="none" stroke="rgba(24,144,255,0.10)" stroke-width="0.8"/>
<polygon points="120,60 172,90 172,150 120,180 68,150 68,90" fill="none" stroke="rgba(24,144,255,0.14)" stroke-width="0.8"/>
<polygon points="120,90 146,105 146,135 120,150 94,135 94,105" fill="none" stroke="rgba(24,144,255,0.18)" stroke-width="0.8"/>
<line x1="120" y1="120" x2="120" y2="30" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<line x1="120" y1="120" x2="198" y2="75" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<line x1="120" y1="120" x2="198" y2="165" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<line x1="120" y1="120" x2="120" y2="210" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<line x1="120" y1="120" x2="42" y2="165" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<line x1="120" y1="120" x2="42" y2="75" stroke="rgba(24,144,255,0.10)" stroke-width="0.6"/>
<polygon id="checkupRadarPoly" points="120,44 180,85 165,146 120,164 65,152 72,92" fill="rgba(24,144,255,0.18)" stroke="var(--primary)" stroke-width="1.8" stroke-linejoin="round" style="filter:drop-shadow(0 0 4px rgba(24,144,255,0.4));"/>
<circle cx="120" cy="44" r="2.5" fill="var(--primary)" class="radar-dot"/><circle cx="180" cy="85" r="2.5" fill="var(--primary)" class="radar-dot"/>
<circle cx="165" cy="146" r="2.5" fill="var(--primary)" class="radar-dot"/><circle cx="120" cy="164" r="2.5" fill="var(--primary)" class="radar-dot"/>
<circle cx="65" cy="152" r="2.5" fill="var(--primary)" class="radar-dot"/><circle cx="72" cy="92" r="2.5" fill="var(--primary)" class="radar-dot"/>
<text x="120" y="38" fill="#fff" font-size="9" text-anchor="middle" font-weight="700">420</text>
<text x="190" y="82" fill="#fff" font-size="9" text-anchor="start" font-weight="700">385</text>
<text x="172" y="152" fill="#fff" font-size="9" text-anchor="start" font-weight="700">290</text>
<text x="120" y="178" fill="#fff" font-size="9" text-anchor="middle" font-weight="700">245</text>
<text x="50" y="152" fill="#fff" font-size="9" text-anchor="end" font-weight="700">350</text>
<text x="58" y="88" fill="#fff" font-size="9" text-anchor="end" font-weight="700">310</text>
<text x="120" y="18" fill="var(--text-sub)" font-size="10" text-anchor="middle">兴隆医院</text>
<text x="208" y="73" fill="var(--text-sub)" font-size="10" text-anchor="start">宝石花医院</text>
<text x="208" y="170" fill="var(--text-sub)" font-size="10" text-anchor="start">庆阳医院</text>
<text x="120" y="226" fill="var(--text-sub)" font-size="10" text-anchor="middle">高陵医院</text>
<text x="32" y="170" fill="var(--text-sub)" font-size="10" text-anchor="end">采油一厂</text>
<text x="32" y="73" fill="var(--text-sub)" font-size="10" text-anchor="end">采油二厂</text>
<circle cx="120" cy="120" r="2" fill="rgba(24,144,255,0.5)"/>
</svg>
</div>
</div>
</div>
<!-- 咨询数据 -->
<div class="panel" style="flex-shrink:0;">
<div class="panel__header"><div class="panel__header-diamond"></div><span class="panel__header-title">咨询数据</span></div>
<div class="panel__body">
<div class="stat-grid">
<div class="stat-card"><div class="stat-card__num">48</div><div class="stat-card__label">入驻专家</div></div>
<div class="stat-card stat-card--success"><div class="stat-card__num">12,580</div><div class="stat-card__label">累计咨询次数</div></div>
<div class="stat-card stat-card--warning"><div class="stat-card__num">6,342</div><div class="stat-card__label">累计咨询人数</div></div>
</div>
</div>
</div>`;
d=d.replace(d.substring(rpBeg,rpEnd),nrp);
ok(d.includes('checkupRadar')&&d.includes('咨询数据'),'Right panel');
// ═══ JS: openWorkOrderStat ═══
d=d.replace('function closeHelpRecordModal(){',
'function openWorkOrderStat(status){var range=getHelpRecordDateRangeByPeriod(currentEmergencyStatPeriod);document.getElementById("helpRecordOverlay").style.display="flex";document.getElementById("filterHelpType").value="";document.getElementById("filterHelpStatus").value=status||"";document.getElementById("filterHelpDateFrom").value=range.from;document.getElementById("filterHelpDateTo").value=range.to;document.getElementById("filterHelpName").value="";document.getElementById("filterHelpEmpId").value="";helpRecordPageSizeVal=parseInt(document.getElementById("helpRecordPageSize").value)||10;applyHelpRecordFilter();}\r\nfunction closeHelpRecordModal(){');
// ═══ JS: All modules + init ═══
const jsModules=`/* ========== 穿戴告警统计 ========== */
var currentAlarmBarPeriod="week";var ALARM_BAR_BASE=[{label:"心率异常",value:126,color:"#FF4D4F"},{label:"体温异常",value:54,color:"#FAAD14"},{label:"血氧异常",value:38,color:"#b37feb"},{label:"压力异常",value:72,color:"#1890FF"},{label:"一键告警",value:18,color:"#ff7875"},{label:"摔倒",value:9,color:"#f5222d"}];var ALARM_BAR_PERIOD_FACTOR={week:1,month:4.2,year:12.5};function switchAlarmBarPeriod(p){currentAlarmBarPeriod=p||"week";renderAlarmBars();}
function renderAlarmBars(){var f=ALARM_BAR_PERIOD_FACTOR[currentAlarmBarPeriod]||1;var d=ALARM_BAR_BASE.map(function(d){return{label:d.label,value:Math.round(d.value*f),color:d.color};});var m=Math.max.apply(null,d.map(function(d){return d.value;}));var c=document.getElementById("alarmBarsChart");if(!c)return;var h="";d.forEach(function(d){var p=m>0?(d.value/m*100):0;h+='<div class="alarm-bars__row"><span class="alarm-bars__label">'+d.label+'</span><div class="alarm-bars__track"><div class="alarm-bars__bar" style="width:'+p+'%;background:'+d.color+';box-shadow:0 0 8px '+d.color+';"></div></div><span class="alarm-bars__count">'+d.value+'</span></div>';});c.innerHTML=h;}
/* ========== 心脑血管预警统计 ========== */
var currentCvPeriod="week";var CV_PERIOD_FACTOR={week:1,month:3.8,year:11.5};var CV_BASE_DATA={todayWarn:20,highRisk:3,yesterdayChange:"+12%",diseases:[{name:"心梗",high:1,mid:2,low:3},{name:"脑梗",high:1,mid:1,low:4},{name:"脑出血",high:1,mid:1,low:2},{name:"猝死",high:0,mid:1,low:3}],legend:{high:3,mid:5,low:12}};function switchCvPeriod(p){currentCvPeriod=p||"week";updateCvStats();}
function cvPeriodLabel(){var m={week:"本周",month:"本月",year:"本年"};return m[currentCvPeriod]||"本周";}
function updateCvStats(){var f=CV_PERIOD_FACTOR[currentCvPeriod]||1,p=document.getElementById("aiPanel");if(!p)return;var b=p.querySelector(".panel__body");if(!b)return;var w=Math.round(CV_BASE_DATA.todayWarn*f),hi=Math.round(CV_BASE_DATA.highRisk*f),ds=CV_BASE_DATA.diseases,l=CV_BASE_DATA.legend,lH=Math.round(l.high*f),lM=Math.round(l.mid*f),lL=Math.round(l.low*f),mx=Math.max.apply(null,ds.map(function(d){return(d.high+d.mid+d.low)*f;})),h=mx>0?function(v){return Math.max(4,Math.round(v*f/mx*72));}:function(){return 0;};var html='<div class="ai-analysis"><div class="ai-analysis__summary"><div class="ai-analysis__metric"><div class="ai-analysis__metric-num">'+w+'</div><div class="ai-analysis__metric-label">'+cvPeriodLabel()+'预警</div></div><div class="ai-analysis__metric ai-analysis__metric--danger"><div class="ai-analysis__metric-num">'+hi+'</div><div class="ai-analysis__metric-label">高危人数</div></div><div class="ai-analysis__metric ai-analysis__metric--warning"><div class="ai-analysis__metric-num">'+CV_BASE_DATA.yesterdayChange+'</div><div class="ai-analysis__metric-label">较昨日</div></div></div><div class="ai-analysis__section-head"><b>预警分布</b><span>按疾病类型/风险等级</span></div><div class="ai-risk-chart">';ds.forEach(function(d){var hh=h(d.high),hm=h(d.mid),hl=h(d.low),vh=Math.round(d.high*f),vm=Math.round(d.mid*f),vl=Math.round(d.low*f),c=vh+vm+vl,tp=[];if(d.high>0)tp.push('<b style="color:#ff7875;">高危</b> '+vh);if(d.mid>0)tp.push('<b style="color:#ffc53d;">中危</b> '+vm);if(d.low>0)tp.push('<b style="color:#40a9ff;">低危</b> '+vl);html+='<div class="ai-risk-col"><div class="ai-risk-stack">'+(d.high>0?'<span class="ai-risk-stack__seg ai-risk-stack__seg--high" style="height:'+hh+'px;"></span>':'')+(d.mid>0?'<span class="ai-risk-stack__seg ai-risk-stack__seg--mid" style="height:'+hm+'px;"></span>':'')+(d.low>0?'<span class="ai-risk-stack__seg ai-risk-stack__seg--low" style="height:'+hl+'px;"></span>':'')+'</div><div class="ai-risk-tip">'+tp.join(' &nbsp; ')+'</div><div class="ai-risk-col__name">'+d.name+'</div><div class="ai-risk-col__count">'+c+'</div></div>';});html+='</div><div class="ai-analysis__legend"><span><i style="background:var(--danger);color:var(--danger);"></i>高危 '+lH+'</span><span><i style="background:var(--warning);color:var(--warning);"></i>中危 '+lM+'</span><span><i style="background:var(--primary);color:var(--primary);"></i>低危 '+lL+'</span></div></div>';b.innerHTML=html;}
/* ========== 体检数据 ========== */
var currentCheckupPeriod="week";var CHECKUP_BASE=[{name:"兴隆医院",value:420,angle:-90},{name:"宝石花医院",value:385,angle:-30},{name:"庆阳医院",value:290,angle:30},{name:"高陵医院",value:245,angle:90},{name:"采油一厂",value:350,angle:150},{name:"采油二厂",value:310,angle:210}];var CHECKUP_PERIOD_FACTOR={week:1,month:3.6,year:10.8};function switchCheckupPeriod(p){currentCheckupPeriod=p||"week";updateCheckupRadar();}
function updateCheckupRadar(){var poly=document.getElementById("checkupRadarPoly");if(!poly)return;var f=CHECKUP_PERIOD_FACTOR[currentCheckupPeriod]||1,cx=120,cy=120,mR=90,mV=500*f;var pts=CHECKUP_BASE.map(function(d){var r=Math.min(mR,d.value*f/mV*mR);return(cx+r*Math.cos(d.angle*Math.PI/180)).toFixed(0)+","+(cy+r*Math.sin(d.angle*Math.PI/180)).toFixed(0);});poly.setAttribute("points",pts.join(" "));}
/* [初始化] */
renderAlarmBars();`;
d=d.replace('/* [初始化] 页面加载时默认渲染心率区间环形图 */\r\nupdateWearableDistribution();',jsModules);
ok(d.includes('renderAlarmBars')&&d.includes('updateCvStats'),'JS modules');
// ═══ FINAL ═══
const cnt=s=>(d.split(s).length-1);
console.log('\n=== FINAL ===');
console.log('aside:',cnt('</aside>'),'right:',cnt('<!-- 右侧面板 -->'),'left:',cnt('<!-- 左侧面板 -->'));
console.log('6cards:',cnt('待处理工单'),'alarm:',cnt('alarmBarsChart'),'help:',cnt('应急求助事件(自动滚动列表)'));
console.log('CV:',cnt('心脑血管预警统计'),'radar:',cnt('checkupRadar'),'consult:',cnt('咨询数据'));
console.log('health:',cnt('人员现状监测(环形图)'),'renderAlarmBars:',cnt('renderAlarmBars'),'updateCvStats:',cnt('updateCvStats'));
fs.writeFileSync(f,d,'utf8');
console.log('DONE. Size:',d.length);