feat: 体检模块侧边栏消歧与统计页重构;一线医疗床位页筛选交互升级

- 健康体检:侧边栏「员工体检报告」按类型消歧为常规/胃肠镜/冠脉CTA体检报告
- 「结果动态综合统计」更名「项目结果综合统计」,新增图表/列表双 Tab、级联筛选、表格分页与报告详情弹窗
- 新增「体检影像管理」分组(用户体检信息/影像文件分类/影像文件详细/文件处理日志)
- 员工体检大表新增高级查询;单位体检项目统计等页面同步侧边栏文案
- 一线医疗:楼栋/床位/站点相关页面单位部门筛选改为级联组件+站点搜索下拉,新增地图选点,调整表格列
This commit is contained in:
liuyunqin
2026-09-20 18:01:48 +08:00
parent 80b0c78bdd
commit 2fc599fabe
53 changed files with 1533 additions and 567 deletions
+1 -1
View File
@@ -255,7 +255,7 @@ const SITE_MAP = [
{ file:'post-unit-count.html', label:'单位体检人数统计' },
{ file:'post-unit-cost-stat.html', label:'单位体检费用统计' },
{ file:'post-unit-item-stat.html', label:'单位体检项目统计' },
{ file:'post-result-comprehensive-stat.html', label:'结果动态综合统计' },
{ file:'post-result-comprehensive-stat.html', label:'项目结果综合统计' },
{ file:'post-hospital-count.html', label:'医院体检人数统计' },
{ file:'post-doctor-count.html', label:'医生检查人数统计' },
{ file:'post-item-stat.html', label:'医院体检项目统计' },
+192 -79
View File
@@ -45,6 +45,32 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-input{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
.filter-input:focus{border-color:var(--primary);}
/* 单位部门级联筛选 + 站点搜索下拉 */
.org-wrap{position:relative;display:inline-block;}
.org-trigger{height:36px;min-width:130px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;display:flex;align-items:center;user-select:none;white-space:nowrap;}
.org-trigger.open,.org-trigger:hover{border-color:var(--primary);}
.org-panel{display:none;position:absolute;top:36px;left:0;z-index:310;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);width:380px;}
.org-panel.open{display:flex;}
.org-col{flex:1;overflow-y:auto;max-height:200px;border-right:1px solid var(--border);}
.org-col:last-child{border-right:none;}
.org-col__title{padding:6px 12px;font-size:var(--font-sm);color:var(--text-placeholder);background:#FAFAFA;border-bottom:1px solid var(--border);font-weight:500;}
.org-item{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;}
.org-item:hover{background:#F0F8FF;color:var(--primary);}
.org-item.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.station-search{position:relative;}
.station-search__panel{display:none;position:absolute;top:34px;left:0;right:0;z-index:320;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);max-height:240px;overflow-y:auto;}
.station-search__panel.open{display:block;}
.station-search__item{padding:8px 12px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:8px;}
.station-search__item:hover{background:#F0F8FF;}
.station-search__item .ss-name{color:var(--text-primary);font-weight:500;}
.station-search__item .ss-org{color:var(--text-placeholder);font-size:var(--font-xs);}
.station-search__empty{padding:16px 12px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
.map-picker{width:100%;height:200px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#e6f7ff;display:flex;align-items:center;justify-content:center;color:var(--primary);font-size:var(--font-sm);gap:6px;cursor:pointer;position:relative;overflow:hidden;transition:border-color .2s;}
.map-picker:hover{border-color:var(--primary);}
.map-picker__bg{position:absolute;inset:0;opacity:.15;background:repeating-linear-gradient(0deg,transparent,transparent 19px,#91d5ff 19px,#91d5ff 20px),repeating-linear-gradient(90deg,transparent,transparent 19px,#91d5ff 19px,#91d5ff 20px);}
.map-picker__pin{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%);z-index:2;display:none;}
.map-picker__label{position:relative;z-index:2;display:flex;align-items:center;gap:6px;}
.map-picker__coord{position:absolute;bottom:8px;left:50%;transform:translateX(-50%);background:rgba(255,255,255,0.92);padding:4px 14px;border-radius:12px;font-size:var(--font-xs);box-shadow:0 1px 4px rgba(0,0,0,0.1);z-index:3;display:none;white-space:nowrap;color:var(--text-primary);max-width:calc(100% - 16px);overflow:hidden;text-overflow:ellipsis;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
@@ -155,16 +181,20 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏:单位→部门→站点三级级联 -->
<div class="filter-bar">
<div class="filter-item">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()">
<option value="">全部单位</option>
</select>
</div>
<div class="filter-item">
<label>所属部门</label>
<select class="filter-select" id="filterDepart" onchange="onFilterDepart()" style="min-width:130px;">
<option value="">全部部门</option>
</select>
<label>所属单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
<div class="org-col" id="unitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" onclick="selectUnit(this,'')">全部单位/部门</div>
</div>
<div class="org-col" id="deptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" onclick="selectDept(this,'')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>所属站点</label>
@@ -200,7 +230,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<thead>
<tr>
<th style="width:40px;"><input type="checkbox" id="ckAll" onchange="toggleAll(this)"></th>
<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:120px;">所属站点</th><th style="min-width:140px;">楼栋名称</th>
<th style="width:56px;">序号</th><th style="min-width:140px;">楼栋名称</th><th style="min-width:120px;">所属站点</th><th style="min-width:160px;">所属单位部门</th>
<th style="min-width:220px;">详细地址</th><th>宿舍数量</th><th>负责人</th><th style="min-width:110px;">负责人电话</th><th style="width:110px;">操作</th>
</tr>
</thead>
@@ -215,28 +245,25 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="modal-mask" id="editMask">
<div class="modal--wide">
<div class="modal__title" id="editTitle">新增楼栋信息</div>
<div class="form-row">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属单位</span>
<select class="form-control" id="eUnit" onchange="onEditUnit()"><option value="">请选择单位</option></select>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属部门</span>
<select class="form-control" id="eDepart"><option value="">请先选择单位</option></select>
</div>
</div>
<div class="form-row">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属站点</span>
<select class="form-control" id="eStation"><option value="">请先选择部门</option></select>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 楼栋名称</span>
<input class="form-control" id="eName" placeholder="如:樊学倒班宿舍1号楼">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属站点</span>
<div class="station-search" id="stationSearch">
<input class="form-control" id="eStation" placeholder="请输入站点名称搜索" autocomplete="off" oninput="onStationInput()" onfocus="onStationFocus()">
<div class="station-search__panel" id="stationPanel"></div>
</div>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 楼栋名称</span>
<input class="form-control" id="eName" placeholder="如:樊学倒班宿舍1号楼">
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 详细地址</span>
<div class="map-picker" id="mapPicker" onclick="pickLocation(event)">
<div class="map-picker__bg"></div>
<svg class="map-picker__pin" id="mapPin" width="28" height="36" viewBox="0 0 28 36" fill="none"><path d="M14 0C6.27 0 0 6.27 0 14c0 10.5 14 22 14 22s14-11.5 14-22C28 6.27 21.73 0 14 0z" fill="#1890FF"/><circle cx="14" cy="14" r="6" fill="#fff"/></svg>
<span class="map-picker__label" id="mapLabel"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 1C4.24 1 2 3.24 2 6c0 3.75 5 7 5 7s5-3.25 5-7c0-2.76-2.24-5-5-5z" stroke="currentColor" stroke-width="1.2"/><circle cx="7" cy="6" r="1.5" stroke="currentColor" stroke-width="1"/></svg>在地图上点击选择坐标位置</span>
<div class="map-picker__coord" id="mapCoord"></div>
</div>
<input class="form-control" id="eAddr" placeholder="楼栋详细地址">
</div>
<div class="form-row">
@@ -262,7 +289,10 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="modal__title">批量导入数据</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 选择站点(导入的楼栋将挂在该站点下)</span>
<select class="form-control" id="iStation"><option value="">请选择站点</option><option>樊学站点基地</option><option>麻子山站点</option><option>桐园站点基地</option><option>西峰基地</option><option>姬圪台站点</option><option>靖边气田站点</option></select>
<div class="station-search" id="importStationSearch">
<input class="form-control" id="iStation" placeholder="请输入站点名称搜索" autocomplete="off" oninput="onImportStationInput()" onfocus="onImportStationFocus()">
<div class="station-search__panel" id="importStationPanel"></div>
</div>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 上传文件</span>
@@ -359,49 +389,73 @@ var bldDorms={
{name:'503室',beds:4,manager:'田广润',mobile:'13991923747',alloc:0,dev:'—'}
]
};
function fillOptions(sel,opts,keepVal){
var v=keepVal||'';
sel.innerHTML='<option value="">'+(sel.dataset.tip||'全部')+'</option>'+opts.map(function(o){return '<option'+(o===v?' selected':'')+'>'+o+'</option>';}).join('');
var selUnit='',selDept='',stationList=[],stationMeta={};
function fillOptions(sel,opts){
sel.innerHTML='<option value="">'+(sel.dataset.tip||'全部')+'</option>'+opts.map(function(o){return '<option>'+o+'</option>';}).join('');
}
function buildStationMeta(){
stationList=[];stationMeta={};
Object.keys(unitDeparts).forEach(function(u){
unitDeparts[u].forEach(function(d){
(depStations[d]||[]).forEach(function(st){
stationList.push({name:st,unit:u,depart:d});
stationMeta[st]={unit:u,depart:d};
});
});
});
}
function initFilters(){
var su=document.getElementById('filterUnit');su.dataset.tip='全部单位';
su.innerHTML='<option value="">全部单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
document.getElementById('filterDepart').dataset.tip='全部部门';
document.getElementById('filterStation').dataset.tip='全部站点';
document.getElementById('eUnit').innerHTML='<option value="">请选择单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
var unitHtml=Object.keys(unitDeparts).map(function(u){return '<div class="org-item" onclick="selectUnit(this,\''+u+'\')">'+u+'</div>';}).join('');
document.getElementById('unitCol').insertAdjacentHTML('beforeend',unitHtml);
var allStations=Object.keys(depStations).reduce(function(a,k){return a.concat(depStations[k]);},[]);
var fs=document.getElementById('filterStation');fs.dataset.tip='全部站点';
fillOptions(fs,allStations);
buildStationMeta();
}
/* [交互] 单位变更时级联刷新部门与站点 */
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var depSel=document.getElementById('filterDepart'),staSel=document.getElementById('filterStation');
var deps=u?unitDeparts[u]:Object.keys(unitDeparts).reduce(function(a,u2){return a.concat(unitDeparts[u2]);},[]);
depSel.dataset.tip='全部部门';fillOptions(depSel,deps);
var stas=deps.reduce(function(a,d){return a.concat(depStations[d]||[]);},[]);
staSel.dataset.tip='全部站点';fillOptions(staSel,stas);
/* [交互] 单位部门级联筛选 */
function toggleOrg(){
document.getElementById('orgPanel').classList.toggle('open');
document.getElementById('orgTrigger').classList.toggle('open');
}
function selectUnit(elem,unitName){
document.querySelectorAll('#unitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selUnit=unitName;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
(unitDeparts[unitName]||[]).forEach(function(d){html+='<div class="org-item" onclick="selectDept(this,\''+d+'\')">'+d+'</div>';});
document.getElementById('deptCol').innerHTML=html;
selDept='';
renderTable();
}
function onFilterDepart(){
var d=document.getElementById('filterDepart').value;
var staSel=document.getElementById('filterStation');
staSel.dataset.tip='全部站点';
var stas=d?(depStations[d]||[]):Object.keys(depStations).reduce(function(a,k){return a.concat(depStations[k]);},[]);
fillOptions(staSel,stas);
function selectDept(elem,deptName){
document.querySelectorAll('#deptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selDept=deptName;
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
renderTable();
}
function resetOrg(){
selUnit='';selDept='';
document.getElementById('orgTrigger').textContent='全部单位/部门';
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
document.querySelectorAll('#unitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
}
function renderTable(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepart').value,s=document.getElementById('filterStation').value,k=document.getElementById('filterName').value.trim();
var list=buildings.filter(function(b){return (!u||b.unit===u)&&(!d||b.depart===d)&&(!s||b.station===s)&&(!k||b.name.indexOf(k)>=0);});
var s=document.getElementById('filterStation').value,k=document.getElementById('filterName').value.trim();
var list=buildings.filter(function(b){return (!selUnit||b.unit===selUnit)&&(!selDept||b.depart===selDept)&&(!s||b.station===s)&&(!k||b.name.indexOf(k)>=0);});
document.getElementById('tbody').innerHTML=list.map(function(b){
var i=buildings.indexOf(b);
return '<tr><td><input type="checkbox" class="rowCk" value="'+i+'" onchange="syncAllCk()"></td>'
+'<td>'+(i+1)+'</td><td>'+b.unit+'</td><td>'+b.depart+'</td><td>'+b.station+'</td><td style="font-weight:500;">'+b.name+'</td>'
+'<td>'+(i+1)+'</td><td style="font-weight:500;">'+b.name+'</td><td>'+b.station+'</td><td>'+b.unit+' / '+b.depart+'</td>'
+'<td>'+b.addr+'</td><td><span class="link-num" onclick="openDorm('+i+')">'+b.dorms+'</span></td><td>'+b.manager+'</td><td>'+b.mobile+'</td>'
+'<td><button class="btn btn-link" onclick="openEdit('+i+')">编辑</button><button class="btn btn-link--danger" onclick="askDelete('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="11">没有符合条件的楼栋</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的楼栋</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 栋';
document.getElementById('ckAll').checked=false;
}
function resetFilter(){['filterUnit','filterDepart','filterStation'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterName').value='';onFilterUnit();}
function resetFilter(){resetOrg();document.getElementById('filterStation').value='';document.getElementById('filterName').value='';renderTable();}
/* [交互] 复选框与批量删除 */
function toggleAll(ck){document.querySelectorAll('.rowCk').forEach(function(c){c.checked=ck.checked;});}
function syncAllCk(){
@@ -416,51 +470,101 @@ function batchDelete(){
document.getElementById('confirmMask').classList.add('open');
}
/* [交互] 新增/编辑 */
var editIdx=-1;
function onEditUnit(){
var u=document.getElementById('eUnit').value;
var depSel=document.getElementById('eDepart'),staSel=document.getElementById('eStation');
depSel.innerHTML='<option value="">请选择部门</option>'+(u?unitDeparts[u]:[]).map(function(d){return '<option>'+d+'</option>';}).join('');
staSel.innerHTML='<option value="">请先选择部门</option>';
var editIdx=-1,selStation='';
function onStationInput(){selStation='';renderStationPanel();}
function onStationFocus(){renderStationPanel();}
function renderStationPanel(){
var q=document.getElementById('eStation').value.trim();
var list=stationList.filter(function(st){return !q||st.name.indexOf(q)>=0;});
document.getElementById('stationPanel').innerHTML=list.map(function(st){
return '<div class="station-search__item" onclick="pickStation(\''+st.name+'\')"><span class="ss-name">'+st.name+'</span><span class="ss-org">'+st.unit+' / '+st.depart+'</span></div>';
}).join('')||'<div class="station-search__empty">未查询到站点</div>';
document.getElementById('stationPanel').classList.add('open');
}
function onEditDepart(){
var d=document.getElementById('eDepart').value;
document.getElementById('eStation').innerHTML='<option value="">请选择站点</option>'+(d?depStations[d]:[]).map(function(s){return '<option>'+s+'</option>';}).join('');
function pickStation(name){
selStation=name;
document.getElementById('eStation').value=name;
document.getElementById('stationPanel').classList.remove('open');
}
/* [交互] 地图选点:点击地图生成坐标并回填详细地址 */
function pickLocation(e){
var picker=document.getElementById('mapPicker');
var rect=picker.getBoundingClientRect();
var x=e.clientX-rect.left;
var y=e.clientY-rect.top;
var pin=document.getElementById('mapPin');
pin.style.display='block';
pin.style.left=x+'px';
pin.style.top=y+'px';
document.getElementById('mapLabel').style.display='none';
var addresses=['樊学作业区基地营地东侧','麻子山作业区站点北门','桐园作业区生活区西区','西峰基地食堂南侧','靖边气田综合管理营地北侧'];
var idx=Math.floor(Math.random()*addresses.length);
var coord=document.getElementById('mapCoord');
coord.innerHTML='<svg width="12" height="12" viewBox="0 0 14 14" fill="none" style="vertical-align:-1px;margin-right:4px;"><path d="M7 1C4.24 1 2 3.24 2 6c0 3.75 5 7 5 7s5-3.25 5-7c0-2.76-2.24-5-5-5z" stroke="#1890FF" stroke-width="1.2"/><circle cx="7" cy="6" r="1.5" stroke="#1890FF" stroke-width="1"/></svg>'+addresses[idx];
coord.style.display='block';
document.getElementById('eAddr').value=addresses[idx];
}
function resetMapPicker(){
document.getElementById('mapPin').style.display='none';
document.getElementById('mapLabel').style.display='flex';
document.getElementById('mapCoord').style.display='none';
document.getElementById('mapCoord').innerHTML='';
}
function openEdit(i){
editIdx=i;
resetMapPicker();
document.getElementById('editTitle').textContent=i<0?'新增楼栋信息':'编辑楼栋信息';
onEditUnit();
if(i>=0){
var b=buildings[i];
document.getElementById('eUnit').value=b.unit;onEditUnit();
document.getElementById('eDepart').innerHTML='<option>请选择部门</option>'+unitDeparts[b.unit].map(function(d){return '<option'+(d===b.depart?' selected':'')+'>'+d+'</option>';}).join('');
onEditDepart();
document.getElementById('eStation').innerHTML='<option>请选择站点</option>'+depStations[b.depart].map(function(s){return '<option'+(s===b.station?' selected':'')+'>'+s+'</option>';}).join('');
selStation=b.station;
document.getElementById('eStation').value=b.station;
document.getElementById('eName').value=b.name;document.getElementById('eAddr').value=b.addr;
document.getElementById('eManager').value=b.manager;document.getElementById('eMobile').value=b.mobile;
}else{
['eName','eAddr','eManager','eMobile'].forEach(function(id){document.getElementById(id).value='';});
document.getElementById('eUnit').value='';onEditUnit();
selStation='';
['eStation','eName','eAddr','eManager','eMobile'].forEach(function(id){document.getElementById(id).value='';});
}
document.getElementById('stationPanel').classList.remove('open');
document.getElementById('editMask').classList.add('open');
}
function closeEdit(){document.getElementById('editMask').classList.remove('open');}
function saveEdit(){
var u=document.getElementById('eUnit').value,d=document.getElementById('eDepart').value,s=document.getElementById('eStation').value;
var s=selStation||document.getElementById('eStation').value.trim();
var n=document.getElementById('eName').value.trim(),a=document.getElementById('eAddr').value.trim();
if(!u||!d||!s||!n||!a){showToast('请完整填写带 * 的必填项');return;}
var meta=stationMeta[s];
if(!s||!meta||!n||!a){showToast('请完整填写带 * 的必填项');return;}
var dup=buildings.some(function(b,i){return i!==editIdx&&b.station===s&&b.name===n;});
if(dup){showToast('该站点下已存在同名楼栋');return;}
var obj={unit:u,depart:d,station:s,name:n,addr:a,dorms:editIdx>=0?buildings[editIdx].dorms:0,manager:document.getElementById('eManager').value.trim()||'—',mobile:document.getElementById('eMobile').value.trim()||'—'};
var obj={unit:meta.unit,depart:meta.depart,station:s,name:n,addr:a,dorms:editIdx>=0?buildings[editIdx].dorms:0,manager:document.getElementById('eManager').value.trim()||'—',mobile:document.getElementById('eMobile').value.trim()||'—'};
if(editIdx<0)buildings.push(obj);else buildings[editIdx]=obj;
closeEdit();renderTable();showToast(editIdx<0?'楼栋已新增':'楼栋已更新');
}
/* [交互] 导入 */
function openImport(){document.getElementById('importMask').classList.add('open');}
var selImportStation='';
function onImportStationInput(){selImportStation='';renderImportStationPanel();}
function onImportStationFocus(){renderImportStationPanel();}
function renderImportStationPanel(){
var q=document.getElementById('iStation').value.trim();
var list=stationList.filter(function(st){return !q||st.name.indexOf(q)>=0;});
document.getElementById('importStationPanel').innerHTML=list.map(function(st){
return '<div class="station-search__item" onclick="pickImportStation(\''+st.name+'\')"><span class="ss-name">'+st.name+'</span><span class="ss-org">'+st.unit+' / '+st.depart+'</span></div>';
}).join('')||'<div class="station-search__empty">未查询到站点</div>';
document.getElementById('importStationPanel').classList.add('open');
}
function pickImportStation(name){
selImportStation=name;
document.getElementById('iStation').value=name;
document.getElementById('importStationPanel').classList.remove('open');
}
function openImport(){
selImportStation='';
document.getElementById('iStation').value='';
document.getElementById('importStationPanel').classList.remove('open');
document.getElementById('importMask').classList.add('open');
}
function closeImport(){document.getElementById('importMask').classList.remove('open');}
function doImport(){
if(!document.getElementById('iStation').value){showToast('请先选择导入目标站点');return;}
if(!selImportStation){showToast('请先选择导入目标站点');return;}
closeImport();showToast('导入任务已提交,请到「查看导入任务」查看结果');
}
/* [交互] 导入任务(异步任务演示) */
@@ -515,7 +619,16 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
initFilters();onFilterUnit();
/* [交互] 点击面板外部关闭下拉 */
document.addEventListener('click',function(e){
var ow=document.getElementById('orgWrap');
if(ow&&!ow.contains(e.target)){document.getElementById('orgPanel').classList.remove('open');document.getElementById('orgTrigger').classList.remove('open');}
var ss=document.getElementById('stationSearch');
if(ss&&!ss.contains(e.target)){document.getElementById('stationPanel').classList.remove('open');}
var is=document.getElementById('importStationSearch');
if(is&&!is.contains(e.target)){document.getElementById('importStationPanel').classList.remove('open');}
});
initFilters();renderTable();
</script>
</body>
</html>
@@ -45,6 +45,17 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-input{height:36px;min-width:120px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
.filter-input:focus{border-color:var(--primary);}
.org-wrap{position:relative;display:inline-block;}
.org-trigger{height:36px;min-width:130px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;display:flex;align-items:center;user-select:none;white-space:nowrap;}
.org-trigger.open,.org-trigger:hover{border-color:var(--primary);}
.org-panel{display:none;position:absolute;top:36px;left:0;z-index:310;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);width:380px;}
.org-panel.open{display:flex;}
.org-col{flex:1;overflow-y:auto;max-height:200px;border-right:1px solid var(--border);}
.org-col:last-child{border-right:none;}
.org-col__title{padding:6px 12px;font-size:var(--font-sm);color:var(--text-placeholder);background:#FAFAFA;border-bottom:1px solid var(--border);font-weight:500;}
.org-item{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;}
.org-item:hover{background:#F0F8FF;color:var(--primary);}
.org-item.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
@@ -152,16 +163,28 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()"><option value="">全部单位</option></select>
</div>
<div class="filter-item">
<label>所属部门</label>
<select class="filter-select" id="filterDepart" onchange="renderTable()" style="min-width:130px;"><option value="">全部部门</option></select>
<label>所属单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
<div class="org-col" id="unitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" onclick="selectUnit(this,'')">全部单位/部门</div>
<div class="org-item" onclick="selectUnit(this,'采油一厂')">采油一厂</div>
<div class="org-item" onclick="selectUnit(this,'采油二厂')">采油二厂</div>
<div class="org-item" onclick="selectUnit(this,'第四采油厂')">第四采油厂</div>
<div class="org-item" onclick="selectUnit(this,'采气一厂')">采气一厂</div>
</div>
<div class="org-col" id="deptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" onclick="selectDept(this,'')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>设备编号</label>
<input class="filter-input" id="filterCode" placeholder="如:HC-" oninput="renderTable()">
<input class="filter-input" id="filterCode" placeholder="请输入" oninput="renderTable()">
</div>
<div class="filter-item">
<label>分配状态</label>
@@ -189,7 +212,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<table>
<thead>
<tr>
<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:100px;">设备编号</th><th>分配状态</th><th>分配类型</th>
<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:100px;">设备编号</th><th style="min-width:130px;">MAC地址</th><th>分配状态</th><th>分配类型</th>
<th style="min-width:230px;">分配宿舍/床位</th><th style="min-width:100px;">分配日期</th><th style="width:150px;">操作</th>
</tr>
</thead>
@@ -289,43 +312,65 @@ var dormTree={
};
/* 与「设备列表」呼叫设备一致;alloc:1宿舍/2床位 */
var devices=[
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-01',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-1',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-02',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-2',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 102-1',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-04',alloc:1,type:1,space:'樊学倒班宿舍1号楼 · 101室',time:'2026-03-05'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HC-MZ-01',alloc:1,type:1,space:'麻子山倒班宿舍楼 · 101室',time:'2026-03-19'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-01',alloc:1,type:2,space:'桐园倒班宿舍1号楼 · 301-1',time:'2026-04-10'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-02',alloc:1,type:1,space:'桐园倒班宿舍1号楼 · 201室',time:'2026-04-10'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-01',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 301-1',time:'2026-05-12'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-02',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 302-1',time:'2026-05-12'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-03',alloc:1,type:1,space:'西峰基地综合宿舍楼 · 402室',time:'2026-05-12'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-01',alloc:1,type:2,space:'靖边气田倒班宿舍楼 · 401-1',time:'2026-06-24'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-02',alloc:0,type:0,space:'',time:''}
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-01',mac:'00:1A:2B:3C:4D:01',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-1',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-02',mac:'00:1A:2B:3C:4D:02',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-2',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',mac:'00:1A:2B:3C:4D:03',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 102-1',time:'2026-03-05'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-04',mac:'00:1A:2B:3C:4D:04',alloc:1,type:1,space:'樊学倒班宿舍1号楼 · 101室',time:'2026-03-05'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HC-MZ-01',mac:'00:1A:2B:3C:4D:05',alloc:1,type:1,space:'麻子山倒班宿舍楼 · 101室',time:'2026-03-19'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-01',mac:'00:1A:2B:3C:4D:06',alloc:1,type:2,space:'桐园倒班宿舍1号楼 · 301-1',time:'2026-04-10'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-02',mac:'00:1A:2B:3C:4D:07',alloc:1,type:1,space:'桐园倒班宿舍1号楼 · 201室',time:'2026-04-10'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-01',mac:'00:1A:2B:3C:4D:08',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 301-1',time:'2026-05-12'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-02',mac:'00:1A:2B:3C:4D:09',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 302-1',time:'2026-05-12'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-03',mac:'00:1A:2B:3C:4D:0A',alloc:1,type:1,space:'西峰基地综合宿舍楼 · 402室',time:'2026-05-12'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-01',mac:'00:1A:2B:3C:4D:0B',alloc:1,type:2,space:'靖边气田倒班宿舍楼 · 401-1',time:'2026-06-24'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-02',mac:'00:1A:2B:3C:4D:0C',alloc:0,type:0,space:'',time:''}
];
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var depSel=document.getElementById('filterDepart');
var deps=u?unitDeparts[u]:Object.keys(unitDeparts).reduce(function(a,u2){return a.concat(unitDeparts[u2]);},[]);
var uniq=[];deps.forEach(function(x){if(uniq.indexOf(x)<0)uniq.push(x);});
depSel.innerHTML='<option value="">全部部门</option>'+uniq.map(function(d){return '<option>'+d+'</option>';}).join('');
renderTable();
var selUnit='',selDept='';
function toggleOrg(){
document.getElementById('orgPanel').classList.toggle('open');
document.getElementById('orgTrigger').classList.toggle('open');
}
function selectUnit(elem,unitName){
document.querySelectorAll('#unitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selUnit=unitName;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
(unitDeparts[unitName]||[]).forEach(function(d){
html+='<div class="org-item" onclick="selectDept(this,\''+d+'\')">'+d+'</div>';
});
document.getElementById('deptCol').innerHTML=html;
selDept='';
}
function selectDept(elem,deptName){
document.querySelectorAll('#deptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selDept=deptName;
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
}
function resetOrg(){
selUnit='';selDept='';
document.getElementById('orgTrigger').textContent='全部单位/部门';
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
document.querySelectorAll('#unitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
}
function renderTable(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepart').value,k=document.getElementById('filterCode').value.trim().toUpperCase(),s=document.getElementById('filterStatus').value;
var u=selUnit,d=selDept,k=document.getElementById('filterCode').value.trim().toUpperCase(),s=document.getElementById('filterStatus').value;
var list=devices.filter(function(r){return (!u||r.unit===u)&&(!d||r.depart===d)&&(!k||r.code.toUpperCase().indexOf(k)>=0)&&(s===''||String(r.alloc)===s);});
document.getElementById('tbody').innerHTML=list.map(function(r){
var i=devices.indexOf(r);
var typeTxt=r.alloc?('<span class="badge '+(r.type===1?'badge--info':'badge--success')+'">'+(r.type===1?'宿舍':'床位')+'</span>'):'<span class="badge badge--muted">-</span>';
var typeTxt=r.alloc?(r.type===1?'宿舍':'床位'):'-';
var ops=r.alloc
?'<button class="btn btn-link" onclick="openAlloc('+i+')">重新分配</button><button class="btn btn-link--danger" onclick="askUnbind('+i+')">解除分配</button>'
:'<button class="btn btn-link" onclick="openAlloc('+i+')">设备分配</button>';
return '<tr><td>'+(i+1)+'</td><td>'+r.unit+'</td><td>'+r.depart+'</td><td style="font-family:monospace;font-weight:500;">'+r.code+'</td>'
return '<tr><td>'+(i+1)+'</td><td>'+r.unit+'</td><td>'+r.depart+'</td><td style="font-family:monospace;font-weight:500;">'+r.code+'</td><td style="font-family:monospace;">'+(r.mac||'—')+'</td>'
+'<td>'+(r.alloc?'<span class="badge badge--success">已分配</span>':'<span class="badge badge--warning">未分配</span>')+'</td>'
+'<td>'+typeTxt+'</td><td>'+(r.space||'-')+'</td><td>'+(r.time||'-')+'</td><td>'+ops+'</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="9">没有符合条件的设备</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的设备</td></tr>';
document.getElementById('totalTip').textContent='共 '+devices.length+' 台 · 已分配 '+devices.filter(function(r){return r.alloc;}).length+' 台';
}
function resetFilter(){['filterUnit','filterDepart','filterStatus'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterCode').value='';onFilterUnit();}
function resetFilter(){resetOrg();document.getElementById('filterStatus').value='';document.getElementById('filterCode').value='';renderTable();}
/* [交互] 设备分配/重新分配 */
var allocIdx=-1;
function openAlloc(i){
@@ -400,8 +445,8 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
document.getElementById('filterUnit').innerHTML='<option value="">全部单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
onFilterUnit();
resetOrg();
renderTable();
</script>
</body>
</html>
+107 -58
View File
@@ -45,6 +45,17 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-input{height:36px;min-width:120px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
.filter-input:focus{border-color:var(--primary);}
.org-wrap{position:relative;display:inline-block;}
.org-trigger{height:36px;min-width:130px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;display:flex;align-items:center;user-select:none;white-space:nowrap;}
.org-trigger.open,.org-trigger:hover{border-color:var(--primary);}
.org-panel{display:none;position:absolute;top:36px;left:0;z-index:310;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);width:380px;}
.org-panel.open{display:flex;}
.org-col{flex:1;overflow-y:auto;max-height:200px;border-right:1px solid var(--border);}
.org-col:last-child{border-right:none;}
.org-col__title{padding:6px 12px;font-size:var(--font-sm);color:var(--text-placeholder);background:#FAFAFA;border-bottom:1px solid var(--border);font-weight:500;}
.org-item{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;}
.org-item:hover{background:#F0F8FF;color:var(--primary);}
.org-item.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
@@ -155,21 +166,33 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()"><option value="">全部单位</option></select>
</div>
<div class="filter-item">
<label>所属部门</label>
<select class="filter-select" id="filterDepart" onchange="renderAll()" style="min-width:130px;"><option value="">全部部门</option></select>
<label>所属单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
<div class="org-col" id="unitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" onclick="selectUnit(this,'')">全部单位/部门</div>
<div class="org-item" onclick="selectUnit(this,'采油一厂')">采油一厂</div>
<div class="org-item" onclick="selectUnit(this,'采油二厂')">采油二厂</div>
<div class="org-item" onclick="selectUnit(this,'第四采油厂')">第四采油厂</div>
<div class="org-item" onclick="selectUnit(this,'采气一厂')">采气一厂</div>
</div>
<div class="org-col" id="deptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" onclick="selectDept(this,'')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>设备编号</label>
<input class="filter-input" id="filterCode" placeholder="如:HC-FX" oninput="renderAll()">
<input class="filter-input" id="filterCode" placeholder="请输入" oninput="renderAll()">
</div>
<div class="filter-item">
<label>设备状态</label>
<select class="filter-select" id="filterStatus" onchange="renderAll()" style="min-width:100px;">
<option value="">全部</option><option value="0">正常</option><option value="1">故障</option><option value="2">停用</option>
<option value="">全部</option><option value="0">在线</option><option value="1">离线</option>
</select>
</div>
<button class="btn btn-primary" onclick="renderAll();showToast('查询完成')">
@@ -217,53 +240,57 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<select class="form-control" id="eUnit" onchange="onEditUnit()"><option value="">请选择单位</option></select>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属部门</span>
<span class="form-label">所属部门</span>
<select class="form-control" id="eDepart"><option value="">请选择部门</option></select>
</div>
</div>
<div class="form-row">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 设备编号</span>
<input class="form-control" id="eCode" placeholder="全局唯一,如:HC-FX-05">
<input class="form-control" id="eCode" placeholder="请输入">
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> <span id="eNameLabel">设备名称</span></span>
<input class="form-control" id="eName" placeholder="如:床头呼叫按钮">
<input class="form-control" id="eName" placeholder="请输入">
</div>
</div>
<div class="form-row">
<div class="form-item">
<span class="form-label">设备型号</span>
<input class="form-control" id="eModel" placeholder="如:KQ-P1">
<input class="form-control" id="eModel" placeholder="请输入">
</div>
<div class="form-item">
<span class="form-label">入库日期</span>
<input class="form-control" id="eStore" type="date">
</div>
</div>
<div class="form-item">
<span class="form-label">MAC地址</span>
<input class="form-control" id="eMac" placeholder="请输入">
</div>
<div class="form-row">
<div class="form-item">
<span class="form-label">设备状态</span>
<select class="form-control" id="eStatus"><option value="0">正常</option><option value="1">故障</option><option value="2">停用</option></select>
<select class="form-control" id="eStatus"><option value="0">在线</option><option value="1">离线</option></select>
</div>
<div class="form-item">
<span class="form-label">管理人</span>
<input class="form-control" id="eManager" placeholder="设备管理人姓名">
<input class="form-control" id="eManager" placeholder="请输入">
</div>
</div>
<div class="form-row">
<div class="form-item">
<span class="form-label">管理人电话</span>
<input class="form-control" id="eMphone" placeholder="11位手机号">
<input class="form-control" id="eMphone" placeholder="请输入">
</div>
<div class="form-item">
<span class="form-label">设备厂家</span>
<input class="form-control" id="eMfr" placeholder="生产厂家名称">
<input class="form-control" id="eMfr" placeholder="请输入">
</div>
</div>
<div class="form-item">
<span class="form-label">厂家电话</span>
<input class="form-control" id="eMfrTel" placeholder="厂家售后服务电话">
<input class="form-control" id="eMfrTel" placeholder="请输入">
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeEdit()">取消</button>
@@ -282,7 +309,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<select class="form-control" id="iUnit"><option value="">请选择单位</option></select>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 所属部门</span>
<span class="form-label">所属部门</span>
<select class="form-control" id="iDepart"><option value="">请选择部门</option></select>
</div>
</div>
@@ -329,52 +356,52 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<script>
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
var statusBadge={'0':'badge--success','1':'badge--danger','2':'badge--muted'};
var statusText={'0':'正常','1':'故障','2':'停用'};
/* 呼叫设备:分配关系与「分配管理」「床位信息」勾稽(HC-FX-03 故障见告警记录演示) */
var statusBadge={'0':'badge--success','1':'badge--muted'};
var statusText={'0':'在线','1':'离线'};
/* 呼叫设备:分配关系与「分配管理」「床位信息」勾稽(HC-FX-03 离线见告警记录演示) */
var callDevices=[
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-01',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-02',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-03-02',status:'1',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-04',name:'房间一键呼叫面板',model:'KQ-P2',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HC-MZ-01',name:'房间一键呼叫面板',model:'KQ-P2',store:'2026-03-18',status:'0',manager:'安伟',mphone:'13991345012',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-01',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-02',name:'房间一键呼叫面板',model:'KQ-P2',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-01',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-02',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-03',name:'房间一键呼叫面板',model:'KQ-P2',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-01',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-06-23',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-02',name:'床头呼叫按钮',model:'KQ-P1',store:'2026-09-01',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'CQ中控医疗',mfrTel:'029-86521700'}
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-01',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:01',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-02',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:02',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:03',store:'2026-03-02',status:'1',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-04',name:'房间一键呼叫面板',model:'KQ-P2',mac:'00:1A:2B:3C:4D:04',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HC-MZ-01',name:'房间一键呼叫面板',model:'KQ-P2',mac:'00:1A:2B:3C:4D:05',store:'2026-03-18',status:'0',manager:'安伟',mphone:'13991345012',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-01',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:06',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-02',name:'房间一键呼叫面板',model:'KQ-P2',mac:'00:1A:2B:3C:4D:07',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-01',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:08',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-02',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:09',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-03',name:'房间一键呼叫面板',model:'KQ-P2',mac:'00:1A:2B:3C:4D:0A',store:'2026-05-11',status:'0',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-01',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:0B',store:'2026-06-23',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'CQ中控医疗',mfrTel:'029-86521700'},
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-02',name:'床头呼叫按钮',model:'KQ-P1',mac:'00:1A:2B:3C:4D:0C',store:'2026-09-01',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'CQ中控医疗',mfrTel:'029-86521700'}
];
/* 终端设备:值班室固定部署,不参与宿舍/床位分配 */
var termDevices=[
{unit:'采油一厂',depart:'樊学作业区',code:'HD-FX-01',name:'值班室接收终端',model:'KQ-T10',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HD-MZ-01',name:'值班室接收终端',model:'KQ-T10',store:'2026-03-18',status:'0',manager:'安伟',mphone:'13991345012',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油二厂',depart:'桐园作业区',code:'HD-TY-01',name:'值班室接收终端',model:'KQ-T10',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HD-XF-01',name:'值班室接收终端',model:'KQ-T10',store:'2026-05-11',status:'1',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采气一厂',depart:'靖边气田',code:'HD-JB-01',name:'值班室接收终端',model:'KQ-T10',store:'2026-06-23',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'华旭智能',mfrTel:'029-87334210'}
{unit:'采油一厂',depart:'樊学作业区',code:'HD-FX-01',name:'值班室接收终端',model:'KQ-T10',mac:'00:1A:2B:3C:4D:11',store:'2026-03-02',status:'0',manager:'王守正',mphone:'13991203456',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油一厂',depart:'麻子山作业区',code:'HD-MZ-01',name:'值班室接收终端',model:'KQ-T10',mac:'00:1A:2B:3C:4D:12',store:'2026-03-18',status:'0',manager:'安伟',mphone:'13991345012',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采油二厂',depart:'桐园作业区',code:'HD-TY-01',name:'值班室接收终端',model:'KQ-T10',mac:'00:1A:2B:3C:4D:13',store:'2026-04-09',status:'0',manager:'陈国栋',mphone:'13991567208',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'第四采油厂',depart:'西峰作业区',code:'HD-XF-01',name:'值班室接收终端',model:'KQ-T10',mac:'00:1A:2B:3C:4D:14',store:'2026-05-11',status:'1',manager:'马文秀',mphone:'13991784320',mfr:'华旭智能',mfrTel:'029-87334210'},
{unit:'采气一厂',depart:'靖边气田',code:'HD-JB-01',name:'值班室接收终端',model:'KQ-T10',mac:'00:1A:2B:3C:4D:15',store:'2026-06-23',status:'0',manager:'周铁祥',mphone:'13991923745',mfr:'华旭智能',mfrTel:'029-87334210'}
];
var curTab='call';
function tabName(){return curTab==='call'?'呼叫设备':'终端设备';}
function headRow(){
var name=curTab==='call'?'设备名称':'终端名称';
return '<tr><th style="width:40px;"><input type="checkbox" id="ckAll" onchange="toggleAll(this)"></th>'
+'<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:100px;">设备编号</th><th style="min-width:120px;">'+name+'</th><th>设备型号</th><th>入库日期</th><th>设备状态</th><th>管理人</th><th style="min-width:105px;">管理人电话</th><th style="min-width:110px;">设备厂家</th><th style="min-width:110px;">厂家电话</th><th style="width:110px;">操作</th></tr>';
+'<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:100px;">设备编号</th><th style="min-width:120px;">'+name+'</th><th>设备型号</th><th style="min-width:130px;">MAC地址</th><th>入库日期</th><th>设备状态</th><th>管理人</th><th style="min-width:105px;">管理人电话</th><th style="min-width:110px;">设备厂家</th><th style="min-width:110px;">厂家电话</th><th style="width:110px;">操作</th></tr>';
}
function renderAll(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepart').value,k=document.getElementById('filterCode').value.trim().toUpperCase(),s=document.getElementById('filterStatus').value;
var u=selUnit,d=selDept,k=document.getElementById('filterCode').value.trim().toUpperCase(),s=document.getElementById('filterStatus').value;
var src=curTab==='call'?callDevices:termDevices;
var list=src.filter(function(r){return (!u||r.unit===u)&&(!d||r.depart===d)&&(!k||r.code.toUpperCase().indexOf(k)>=0)&&(s===''||r.status===s);});
document.getElementById('thead').innerHTML=headRow();
document.getElementById('tbody').innerHTML=list.map(function(r){
var i=src.indexOf(r);
return '<tr><td><input type="checkbox" class="rowCk" value="'+i+'" onchange="syncAllCk()"></td>'
+'<td>'+(i+1)+'</td><td>'+r.unit+'</td><td>'+r.depart+'</td><td style="font-family:monospace;font-weight:500;">'+r.code+'</td><td>'+r.name+'</td><td>'+r.model+'</td><td>'+r.store+'</td>'
+'<td>'+(i+1)+'</td><td>'+r.unit+'</td><td>'+r.depart+'</td><td style="font-family:monospace;font-weight:500;">'+r.code+'</td><td>'+r.name+'</td><td>'+r.model+'</td><td style="font-family:monospace;">'+(r.mac||'—')+'</td><td>'+r.store+'</td>'
+'<td><span class="badge '+statusBadge[r.status]+'">'+statusText[r.status]+'</span></td>'
+'<td>'+r.manager+'</td><td>'+r.mphone+'</td><td>'+r.mfr+'</td><td>'+r.mfrTel+'</td>'
+'<td><button class="btn btn-link" onclick="openEdit('+i+')">编辑</button><button class="btn btn-link--danger" onclick="askDelete('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="14">没有符合条件的设备</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 台(正常 '+list.filter(function(r){return r.status==='0';}).length+' / 故障 '+list.filter(function(r){return r.status==='1';}).length+' / 停用 '+list.filter(function(r){return r.status==='2';}).length+')';
}).join('')||'<tr><td class="td-empty" colspan="15">没有符合条件的设备</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 台(在线 '+list.filter(function(r){return r.status==='0';}).length+' / 离线 '+list.filter(function(r){return r.status==='1';}).length+')';
}
function switchTab(t){
curTab=t;
@@ -382,15 +409,37 @@ function switchTab(t){
document.getElementById('tabTerm').classList.toggle('active',t==='term');
renderAll();
}
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var depSel=document.getElementById('filterDepart');
var deps=u?unitDeparts[u]:Object.keys(unitDeparts).reduce(function(a,u2){return a.concat(unitDeparts[u2]);},[]);
var uniq=[];deps.forEach(function(x){if(uniq.indexOf(x)<0)uniq.push(x);});
depSel.innerHTML='<option value="">全部部门</option>'+uniq.map(function(d){return '<option>'+d+'</option>';}).join('');
renderAll();
var selUnit='',selDept='';
function toggleOrg(){
document.getElementById('orgPanel').classList.toggle('open');
document.getElementById('orgTrigger').classList.toggle('open');
}
function resetFilter(){['filterUnit','filterDepart','filterStatus'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterCode').value='';onFilterUnit();}
function selectUnit(elem,unitName){
document.querySelectorAll('#unitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selUnit=unitName;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
(unitDeparts[unitName]||[]).forEach(function(d){
html+='<div class="org-item" onclick="selectDept(this,\''+d+'\')">'+d+'</div>';
});
document.getElementById('deptCol').innerHTML=html;
selDept='';
}
function selectDept(elem,deptName){
document.querySelectorAll('#deptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selDept=deptName;
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
}
function resetOrg(){
selUnit='';selDept='';
document.getElementById('orgTrigger').textContent='全部单位/部门';
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
document.querySelectorAll('#unitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
}
function resetFilter(){resetOrg();document.getElementById('filterStatus').value='';document.getElementById('filterCode').value='';renderAll();}
/* [交互] 复选框与批量删除 */
function toggleAll(ck){document.querySelectorAll('.rowCk').forEach(function(c){c.checked=ck.checked;});}
function syncAllCk(){
@@ -420,13 +469,13 @@ function openEdit(i){
document.getElementById('eUnit').value=r.unit;onEditUnit();
document.getElementById('eDepart').innerHTML='<option value="">请选择部门</option>'+unitDeparts[r.unit].map(function(d){return '<option'+(d===r.depart?' selected':'')+'>'+d+'</option>';}).join('');
document.getElementById('eCode').value=r.code;document.getElementById('eName').value=r.name;
document.getElementById('eModel').value=r.model;document.getElementById('eStore').value=r.store;
document.getElementById('eModel').value=r.model;document.getElementById('eMac').value=r.mac;document.getElementById('eStore').value=r.store;
document.getElementById('eStatus').value=r.status;document.getElementById('eManager').value=r.manager;
document.getElementById('eMphone').value=r.mphone;document.getElementById('eMfr').value=r.mfr;
document.getElementById('eMfrTel').value=r.mfrTel;
}else{
document.getElementById('eUnit').value='';onEditUnit();
['eCode','eName','eModel','eStore','eManager','eMphone','eMfr','eMfrTel'].forEach(function(id){document.getElementById(id).value='';});
['eCode','eName','eModel','eMac','eStore','eManager','eMphone','eMfr','eMfrTel'].forEach(function(id){document.getElementById(id).value='';});
document.getElementById('eStatus').value='0';
}
document.getElementById('editMask').classList.add('open');
@@ -434,11 +483,11 @@ function openEdit(i){
function closeEdit(){document.getElementById('editMask').classList.remove('open');}
function saveEdit(){
var u=document.getElementById('eUnit').value,d=document.getElementById('eDepart').value,c=document.getElementById('eCode').value.trim(),n=document.getElementById('eName').value.trim();
if(!u||!d||!c||!n){showToast('请完整填写带 * 的必填项');return;}
if(!u||!c||!n){showToast('请完整填写带 * 的必填项');return;}
var src=curTab==='call'?callDevices:termDevices;
var dup=callDevices.concat(termDevices).some(function(r){return r!==src[editIdx]&&r.code===c;});
if(dup){showToast('设备编号已存在,须全局唯一');return;}
var obj={unit:u,depart:d,code:c,name:n,model:document.getElementById('eModel').value.trim()||'—',store:document.getElementById('eStore').value||'—',status:document.getElementById('eStatus').value,manager:document.getElementById('eManager').value.trim()||'—',mphone:document.getElementById('eMphone').value.trim()||'—',mfr:document.getElementById('eMfr').value.trim()||'—',mfrTel:document.getElementById('eMfrTel').value.trim()||'—'};
var obj={unit:u,depart:d,code:c,name:n,model:document.getElementById('eModel').value.trim()||'—',mac:document.getElementById('eMac').value.trim(),store:document.getElementById('eStore').value||'—',status:document.getElementById('eStatus').value,manager:document.getElementById('eManager').value.trim()||'—',mphone:document.getElementById('eMphone').value.trim()||'—',mfr:document.getElementById('eMfr').value.trim()||'—',mfrTel:document.getElementById('eMfrTel').value.trim()||'—'};
if(editIdx<0)src.push(obj);else src[editIdx]=obj;
closeEdit();renderAll();showToast(editIdx<0?tabName()+'已新增':tabName()+'已更新');
}
@@ -455,7 +504,7 @@ function openImport(){
}
function closeImport(){document.getElementById('importMask').classList.remove('open');}
function doImport(){
if(!document.getElementById('iUnit').value||!document.getElementById('iDepart').value){showToast('请选择单位与部门');return;}
if(!document.getElementById('iUnit').value){showToast('请选择单位');return;}
closeImport();showToast('导入任务已提交,请到「查看导入任务」查看结果');
}
/* [交互] 导入任务(异步任务演示) */
@@ -495,8 +544,8 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
document.getElementById('filterUnit').innerHTML='<option value="">全部单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
onFilterUnit();
resetOrg();
renderAll();
</script>
</body>
</html>
+97 -8
View File
@@ -45,6 +45,18 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-input{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
.filter-input:focus{border-color:var(--primary);}
/* 单位部门级联筛选 */
.org-wrap{position:relative;display:inline-block;}
.org-trigger{height:36px;min-width:130px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;display:flex;align-items:center;user-select:none;white-space:nowrap;}
.org-trigger.open,.org-trigger:hover{border-color:var(--primary);}
.org-panel{display:none;position:absolute;top:36px;left:0;z-index:310;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);width:380px;}
.org-panel.open{display:flex;}
.org-col{flex:1;overflow-y:auto;max-height:200px;border-right:1px solid var(--border);}
.org-col:last-child{border-right:none;}
.org-col__title{padding:6px 12px;font-size:var(--font-sm);color:var(--text-placeholder);background:#FAFAFA;border-bottom:1px solid var(--border);font-weight:500;}
.org-item{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;}
.org-item:hover{background:#F0F8FF;color:var(--primary);}
.org-item.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
@@ -152,6 +164,28 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>所属单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
<div class="org-col" id="unitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" onclick="selectUnit(this,'')">全部单位/部门</div>
</div>
<div class="org-col" id="deptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" onclick="selectDept(this,'')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>所属站点</label>
<select class="filter-select" id="filterStation" onchange="renderTable()" style="min-width:140px;">
<option value="">全部站点</option>
</select>
</div>
<div class="filter-item">
<label>所属楼栋</label>
<select class="filter-select" id="filterBuilding" onchange="renderTable()" style="min-width:180px;">
@@ -192,8 +226,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<thead>
<tr>
<th style="width:40px;"><input type="checkbox" id="ckAll" onchange="toggleAll(this)"></th>
<th style="width:56px;">序号</th><th style="min-width:110px;">宿舍名称</th><th style="min-width:180px;">所属楼栋</th><th>床位数量</th><th>负责人</th>
<th style="min-width:110px;">负责人电话</th><th>是否分配设备</th><th style="min-width:110px;">设备编号</th><th style="width:110px;">操作</th>
<th style="width:56px;">序号</th><th style="min-width:110px;">宿舍名称</th><th style="min-width:180px;">所属楼栋</th><th style="min-width:120px;">所属站点</th><th style="min-width:160px;">所属单位部门</th>
<th>床位数量</th><th>是否分配设备</th><th style="min-width:110px;">设备编号</th><th>负责人</th><th style="min-width:110px;">负责人电话</th><th style="width:110px;">操作</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
@@ -302,6 +336,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<script>
/* 楼栋清单与「楼栋信息」页勾稽 */
var buildings=['樊学倒班宿舍1号楼','麻子山倒班宿舍楼','桐园倒班宿舍1号楼','西峰基地综合宿舍楼','靖边气田倒班宿舍楼'];
/* 单位→部门→站点三级映射与楼栋归属,与「楼栋信息」页口径一致 */
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
var depStations={'樊学作业区':['樊学站点基地'],'麻子山作业区':['麻子山站点'],'桐园作业区':['桐园站点基地'],'姬圪台作业区':['姬圪台站点'],'西峰作业区':['西峰基地'],'靖边气田':['靖边气田站点']};
var buildingMeta={
'樊学倒班宿舍1号楼':{station:'樊学站点基地',unit:'采油一厂',depart:'樊学作业区'},
'麻子山倒班宿舍楼':{station:'麻子山站点',unit:'采油一厂',depart:'麻子山作业区'},
'桐园倒班宿舍1号楼':{station:'桐园站点基地',unit:'采油二厂',depart:'桐园作业区'},
'西峰基地综合宿舍楼':{station:'西峰基地',unit:'第四采油厂',depart:'西峰作业区'},
'靖边气田倒班宿舍楼':{station:'靖边气田站点',unit:'采气一厂',depart:'靖边气田'}
};
var selUnit='',selDept='';
/* alloc:绑定到宿舍本身的设备(房间级呼叫);床位级绑定见「分配管理」 */
var dorms=[
{name:'101室',building:'樊学倒班宿舍1号楼',beds:4,manager:'石维国',mobile:'13991203456',alloc:1,device:'HC-FX-04'},
@@ -341,26 +386,65 @@ var bedAssigns={
'靖边气田倒班宿舍楼|401-1':'CQ036441','靖边气田倒班宿舍楼|401-2':'CQ029905'
};
function initFilters(){
var unitHtml=Object.keys(unitDeparts).map(function(u){return '<div class="org-item" onclick="selectUnit(this,\''+u+'\')">'+u+'</div>';}).join('');
document.getElementById('unitCol').insertAdjacentHTML('beforeend',unitHtml);
var opts='<option value="">全部楼栋</option>'+buildings.map(function(b){return '<option>'+b+'</option>';}).join('');
document.getElementById('filterBuilding').innerHTML=opts;
document.getElementById('eBuilding').innerHTML='<option value="">请选择楼栋</option>'+buildings.map(function(b){return '<option>'+b+'</option>';}).join('');
document.getElementById('iBuilding').innerHTML='<option value="">请选择楼栋</option>'+buildings.map(function(b){return '<option>'+b+'</option>';}).join('');
var allStations=Object.keys(depStations).reduce(function(a,k){return a.concat(depStations[k]);},[]);
document.getElementById('filterStation').innerHTML='<option value="">全部站点</option>'+allStations.map(function(s){return '<option>'+s+'</option>';}).join('');
}
/* [交互] 单位部门级联筛选 */
function toggleOrg(){
document.getElementById('orgPanel').classList.toggle('open');
document.getElementById('orgTrigger').classList.toggle('open');
}
function selectUnit(elem,unitName){
document.querySelectorAll('#unitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selUnit=unitName;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
(unitDeparts[unitName]||[]).forEach(function(d){html+='<div class="org-item" onclick="selectDept(this,\''+d+'\')">'+d+'</div>';});
document.getElementById('deptCol').innerHTML=html;
selDept='';
renderTable();
}
function selectDept(elem,deptName){
document.querySelectorAll('#deptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selDept=deptName;
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
renderTable();
}
function resetOrg(){
selUnit='';selDept='';
document.getElementById('orgTrigger').textContent='全部单位/部门';
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
document.querySelectorAll('#unitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
}
function renderTable(){
var b=document.getElementById('filterBuilding').value,n=document.getElementById('filterName').value.trim(),a=document.getElementById('filterAlloc').value;
var list=dorms.filter(function(r){return (!b||r.building===b)&&(!n||r.name.indexOf(n)>=0)&&(a===''||String(r.alloc)===a);});
var s=document.getElementById('filterStation').value,b=document.getElementById('filterBuilding').value,n=document.getElementById('filterName').value.trim(),a=document.getElementById('filterAlloc').value;
var list=dorms.filter(function(r){
var meta=buildingMeta[r.building]||{};
return (!selUnit||meta.unit===selUnit)&&(!selDept||meta.depart===selDept)&&(!s||meta.station===s)&&(!b||r.building===b)&&(!n||r.name.indexOf(n)>=0)&&(a===''||String(r.alloc)===a);
});
document.getElementById('tbody').innerHTML=list.map(function(r){
var i=dorms.indexOf(r);
var meta=buildingMeta[r.building]||{};
return '<tr><td><input type="checkbox" class="rowCk" value="'+i+'" onchange="syncAllCk()"></td>'
+'<td>'+(i+1)+'</td><td style="font-weight:500;">'+r.name+'</td><td>'+r.building+'</td><td><span class="link-num" onclick="openBed('+i+')">'+r.beds+'</span></td><td>'+r.manager+'</td><td>'+r.mobile+'</td>'
+'<td>'+(i+1)+'</td><td style="font-weight:500;">'+r.name+'</td><td>'+r.building+'</td><td>'+(meta.station||'')+'</td><td>'+(meta.unit?meta.unit+' / '+meta.depart:'')+'</td>'
+'<td><span class="link-num" onclick="openBed('+i+')">'+r.beds+'</span></td>'
+'<td>'+(r.alloc?'<span class="badge badge--success">已分配</span>':'<span class="badge badge--muted">未分配</span>')+'</td>'
+'<td style="font-family:monospace;">'+(r.device||'-')+'</td>'
+'<td style="font-family:monospace;">'+(r.device||'-')+'</td><td>'+r.manager+'</td><td>'+r.mobile+'</td>'
+'<td><button class="btn btn-link" onclick="openEdit('+i+')">编辑</button><button class="btn btn-link--danger" onclick="askDelete('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的宿舍</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="12">没有符合条件的宿舍</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 间 · 房间级设备 '+dorms.filter(function(r){return r.alloc;}).length+' 台';
document.getElementById('ckAll').checked=false;
}
function resetFilter(){['filterBuilding','filterAlloc'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterName').value='';renderTable();}
function resetFilter(){resetOrg();['filterStation','filterBuilding','filterAlloc'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterName').value='';renderTable();}
/* [交互] 复选框与批量删除 */
function toggleAll(ck){document.querySelectorAll('.rowCk').forEach(function(c){c.checked=ck.checked;});}
function syncAllCk(){
@@ -459,6 +543,11 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
/* [交互] 点击面板外部关闭下拉 */
document.addEventListener('click',function(e){
var ow=document.getElementById('orgWrap');
if(ow&&!ow.contains(e.target)){document.getElementById('orgPanel').classList.remove('open');document.getElementById('orgTrigger').classList.remove('open');}
});
initFilters();renderTable();
</script>
</body>
+80 -86
View File
@@ -45,6 +45,17 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-input{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
.filter-input:focus{border-color:var(--primary);}
.org-wrap{position:relative;display:inline-block;}
.org-trigger{height:36px;min-width:130px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;display:flex;align-items:center;user-select:none;white-space:nowrap;}
.org-trigger.open,.org-trigger:hover{border-color:var(--primary);}
.org-panel{display:none;position:absolute;top:36px;left:0;z-index:310;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);width:380px;}
.org-panel.open{display:flex;}
.org-col{flex:1;overflow-y:auto;max-height:200px;border-right:1px solid var(--border);}
.org-col:last-child{border-right:none;}
.org-col__title{padding:6px 12px;font-size:var(--font-sm);color:var(--text-placeholder);background:#FAFAFA;border-bottom:1px solid var(--border);font-weight:500;}
.org-item{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:4px;}
.org-item:hover{background:#F0F8FF;color:var(--primary);}
.org-item.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
@@ -151,16 +162,24 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏:单位→部门级联 + 站点名称 -->
<div class="filter-bar">
<div class="filter-item">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()">
<option value="">全部单位</option>
</select>
</div>
<div class="filter-item">
<label>所属部门</label>
<select class="filter-select" id="filterDepart" onchange="renderTable()" style="min-width:130px;">
<option value="">全部部门</option>
</select>
<label>所属单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
<div class="org-col" id="unitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" onclick="selectUnit(this,'')">全部单位/部门</div>
<div class="org-item" onclick="selectUnit(this,'采油一厂')">采油一厂</div>
<div class="org-item" onclick="selectUnit(this,'采油二厂')">采油二厂</div>
<div class="org-item" onclick="selectUnit(this,'第四采油厂')">第四采油厂</div>
<div class="org-item" onclick="selectUnit(this,'采气一厂')">采气一厂</div>
</div>
<div class="org-col" id="deptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" onclick="selectDept(this,'')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>站点名称</label>
@@ -186,7 +205,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<table>
<thead>
<tr>
<th style="width:56px;">序号</th><th>所属单位</th><th>所属部门</th><th style="min-width:140px;">站点名称</th>
<th style="width:56px;">序号</th><th style="min-width:140px;">站点名称</th><th>所属单位</th><th>所属部门</th>
<th style="min-width:220px;">详细地址</th><th>负责人</th><th style="min-width:110px;">联系电话</th>
<th style="min-width:100px;">设立日期</th><th style="min-width:220px;">简介</th><th style="width:110px;">操作</th>
</tr>
@@ -198,7 +217,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
</main>
<!-- [交互] 新增/编辑站点:地点支持地图选点/手动输入双模式 -->
<!-- [交互] 新增/编辑站点:地点采用地图选点 + 详细地址 -->
<div class="modal-mask" id="editMask">
<div class="modal--wide">
<div class="modal__title" id="editTitle">新增站点</div>
@@ -218,41 +237,14 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 地点</span>
<div class="radio-line">
<label><input type="radio" name="geoType" value="0" checked onchange="switchGeoType()"> 地图选点</label>
<label><input type="radio" name="geoType" value="1" onchange="switchGeoType()"> 手动输入</label>
<div class="map-pick" id="mapPick" onclick="demoPickPoint()">
<svg width="26" height="26" viewBox="0 0 26 26" fill="none"><path d="M13 3c4 0 7 3 7 6.8 0 5-7 13.2-7 13.2S6 14.8 6 9.8C6 6 9 3 13 3z" stroke="#1890FF" stroke-width="1.6" fill="none"/><circle cx="13" cy="9.8" r="2.4" stroke="#1890FF" stroke-width="1.6"/></svg>
<span id="mapPickText">点击在地图上拾取站点坐标</span>
</div>
</div>
<!-- 地图选点模式 -->
<div id="geoMapMode">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 地图选点</span>
<div class="map-pick" id="mapPick" onclick="demoPickPoint()">
<svg width="26" height="26" viewBox="0 0 26 26" fill="none"><path d="M13 3c4 0 7 3 7 6.8 0 5-7 13.2-7 13.2S6 14.8 6 9.8C6 6 9 3 13 3z" stroke="#1890FF" stroke-width="1.6" fill="none"/><circle cx="13" cy="9.8" r="2.4" stroke="#1890FF" stroke-width="1.6"/></svg>
<span id="mapPickText">点击在地图上拾取站点坐标</span>
</div>
<div class="form-item" style="margin-bottom:0;">
<span class="form-label">选址地址</span>
<input class="form-control" id="eMapAddr" readonly placeholder="选点后自动回填地址">
</div>
</div>
</div>
<!-- 手动输入模式 -->
<div id="geoInputMode" style="display:none;">
<div class="form-row">
<div class="form-item">
<span class="form-label"><span class="req">*</span> 经度</span>
<input class="form-control" id="eLng" placeholder="如:107.3412">
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 纬度</span>
<input class="form-control" id="eLat" placeholder="如:36.1147">
</div>
</div>
<div class="form-item">
<span class="form-label"><span class="req">*</span> 详细地址</span>
<input class="form-control" id="eAddr2" placeholder="站点详细地址">
</div>
<div class="form-item">
<span class="form-label">详细地址</span>
<input class="form-control" id="eAddr" placeholder="请补充详细地址,如:站点基地东侧">
</div>
<div class="form-row">
<div class="form-item">
@@ -305,37 +297,52 @@ var stations=[
{unit:'第四采油厂',depart:'西峰作业区',name:'西峰基地',addr:'甘肃省西峰市西峰基地食堂南侧',manager:'马文秀',mobile:'13991784320',setUp:'2017-09-01',desc:'第四采油厂机关基地,医疗点与救护车驻点合一。'},
{unit:'采气一厂',depart:'靖边气田',name:'靖边气田站点',addr:'陕西省靖边县靖边气田综合管理营地北侧',manager:'周铁祥',mobile:'13991923745',setUp:'2020-11-11',desc:'靖边气田处理厂站点,负责气田倒班人员床头呼叫值守。'}
];
function fillOptions(sel,opts,keepVal){
var v=keepVal||'';
sel.innerHTML='<option value="">'+(sel.dataset.tip||'全部')+'</option>'+opts.map(function(o){return '<option'+(o===v?' selected':'')+'>'+o+'</option>';}).join('');
}
function initFilters(){
var su=document.getElementById('filterUnit');su.dataset.tip='全部单位';
su.innerHTML='<option value="">全部单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
document.getElementById('filterDepart').dataset.tip='全部部门';
document.getElementById('eUnit').innerHTML='<option value="">请选择单位</option>'+Object.keys(unitDeparts).map(function(u){return '<option>'+u+'</option>';}).join('');
}
/* [交互] 筛选单位变更时级联刷新部门 */
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var depSel=document.getElementById('filterDepart');
var deps=u?unitDeparts[u]:Object.keys(unitDeparts).reduce(function(a,u2){return a.concat(unitDeparts[u2]);},[]);
depSel.dataset.tip='全部部门';fillOptions(depSel,deps);
renderTable();
var selUnit='',selDept='';
function toggleOrg(){
document.getElementById('orgPanel').classList.toggle('open');
document.getElementById('orgTrigger').classList.toggle('open');
}
function selectUnit(elem,unitName){
document.querySelectorAll('#unitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selUnit=unitName;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
(unitDeparts[unitName]||[]).forEach(function(d){
html+='<div class="org-item" onclick="selectDept(this,\''+d+'\')">'+d+'</div>';
});
document.getElementById('deptCol').innerHTML=html;
selDept='';
}
function selectDept(elem,deptName){
document.querySelectorAll('#deptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
selDept=deptName;
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
}
function resetOrg(){
selUnit='';selDept='';
document.getElementById('orgTrigger').textContent='全部单位/部门';
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
document.querySelectorAll('#unitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'\')">全部部门</div>';
}
function renderTable(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepart').value,k=document.getElementById('filterName').value.trim();
var u=selUnit,d=selDept,k=document.getElementById('filterName').value.trim();
var list=stations.filter(function(s){return (!u||s.unit===u)&&(!d||s.depart===d)&&(!k||s.name.indexOf(k)>=0);});
document.getElementById('tbody').innerHTML=list.map(function(s){
var i=stations.indexOf(s);
return '<tr><td>'+(i+1)+'</td><td>'+s.unit+'</td><td>'+s.depart+'</td><td style="font-weight:500;">'+s.name+'</td>'
return '<tr><td>'+(i+1)+'</td><td style="font-weight:500;">'+s.name+'</td><td>'+s.unit+'</td><td>'+s.depart+'</td>'
+'<td>'+s.addr+'</td><td>'+s.manager+'</td><td>'+s.mobile+'</td><td>'+s.setUp+'</td>'
+'<td style="white-space:normal;min-width:220px;">'+s.desc+'</td>'
+'<td><button class="btn btn-link" onclick="openEdit('+i+')">编辑</button><button class="btn btn-link--danger" onclick="askDelete('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的站点</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 个站点';
}
function resetFilter(){['filterUnit','filterDepart'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterName').value='';onFilterUnit();}
function resetFilter(){resetOrg();document.getElementById('filterName').value='';renderTable();}
/* [交互] 新增/编辑 */
var editIdx=-1;
function onEditUnit(){
@@ -343,19 +350,15 @@ function onEditUnit(){
var depSel=document.getElementById('eDepart');
depSel.innerHTML='<option value="">请选择部门</option>'+(u?unitDeparts[u]:[]).map(function(d){return '<option>'+d+'</option>';}).join('');
}
function geoTypeVal(){var r=document.querySelector('input[name="geoType"]:checked');return r?r.value:'0';}
function switchGeoType(){
var map=geoTypeVal()==='0';
document.getElementById('geoMapMode').style.display=map?'':'none';
document.getElementById('geoInputMode').style.display=map?'none':'';
}
/* [交互] 地图选点演示:点击拾取坐标并回填地址 */
/* [交互] 地图选点演示:点击拾取坐标并回填详细地址 */
var pickedPoint=null;
function demoPickPoint(){
pickedPoint={lng:'107.3412',lat:'36.1147'};
document.getElementById('mapPickText').innerHTML='<span class="picked">已选坐标:107.3412, 36.1147(点击可重选)</span>';
var addr=document.getElementById('eName').value.trim();
document.getElementById('eMapAddr').value='已拾取:'+(addr||'站点位置')+'(经度107.3412 纬度36.1147)';
if(!document.getElementById('eAddr').value){
var nm=document.getElementById('eName').value.trim();
document.getElementById('eAddr').value='陕西省延安市吴起县'+(nm||'站点')+'基地';
}
showToast('地图选点完成');
}
function openEdit(i){
@@ -363,21 +366,19 @@ function openEdit(i){
document.getElementById('editTitle').textContent=i<0?'新增站点':'编辑站点';
onEditUnit();
pickedPoint=null;document.getElementById('mapPickText').textContent='点击在地图上拾取站点坐标';
document.getElementById('eMapAddr').value='';
document.getElementById('eAddr').value='';
if(i>=0){
var s=stations[i];
document.getElementById('eUnit').value=s.unit;onEditUnit();
document.getElementById('eDepart').innerHTML='<option>请选择部门</option>'+unitDeparts[s.unit].map(function(d){return '<option'+(d===s.depart?' selected':'')+'>'+d+'</option>';}).join('');
document.getElementById('eName').value=s.name;
document.querySelector('input[name="geoType"][value="0"]').checked=true;switchGeoType();
document.getElementById('eMapAddr').value='已拾取:'+s.addr;
document.getElementById('eAddr').value=s.addr;
document.getElementById('mapPickText').innerHTML='<span class="picked">已选坐标:107.3412, 36.1147(点击可重选)</span>';
document.getElementById('eManager').value=s.manager;document.getElementById('eMobile').value=s.mobile;
document.getElementById('eSetUp').value=s.setUp;document.getElementById('eDesc').value=s.desc;
}else{
['eName','eManager','eMobile','eSetUp','eDesc','eLng','eLat','eAddr2'].forEach(function(id){document.getElementById(id).value='';});
['eName','eManager','eMobile','eSetUp','eDesc','eAddr'].forEach(function(id){document.getElementById(id).value='';});
document.getElementById('eUnit').value='';onEditUnit();
document.querySelector('input[name="geoType"][value="0"]').checked=true;switchGeoType();
}
document.getElementById('editMask').classList.add('open');
}
@@ -385,15 +386,8 @@ function closeEdit(){document.getElementById('editMask').classList.remove('open'
function saveEdit(){
var u=document.getElementById('eUnit').value,d=document.getElementById('eDepart').value,n=document.getElementById('eName').value.trim();
if(!u||!d||!n){showToast('请完整填写带 * 的必填项');return;}
var addr;
if(geoTypeVal()==='0'){
if(!document.getElementById('eMapAddr').value){showToast('请先在地图上拾取站点坐标');return;}
addr=document.getElementById('eMapAddr').value;
}else{
var lng=document.getElementById('eLng').value.trim(),lat=document.getElementById('eLat').value.trim(),a2=document.getElementById('eAddr2').value.trim();
if(!lng||!lat||!a2){showToast('手动输入模式下经度/纬度/详细地址为必填');return;}
addr=a2;
}
var addr=document.getElementById('eAddr').value.trim();
if(!addr){showToast('请先在地图上拾取站点坐标并补充详细地址');return;}
var dup=stations.some(function(s,i){return i!==editIdx&&s.unit===u&&s.name===n;});
if(dup){showToast('该单位下已存在同名站点');return;}
var obj={unit:u,depart:d,name:n,addr:addr,
@@ -419,7 +413,7 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
initFilters();onFilterUnit();
initFilters();resetOrg();renderTable();
</script>
</body>
</html>
@@ -96,6 +96,15 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
.form-control:focus{border-color:var(--primary);}
.form-control:disabled{background:#F5F5F5;color:var(--text-secondary);cursor:not-allowed;}
select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;-webkit-appearance:none;padding-right:28px;cursor:pointer;}
.search-select{position:relative;}
.search-select__list{position:absolute;top:100%;left:0;right:0;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);max-height:260px;overflow-y:auto;z-index:30;display:none;margin-top:2px;}
.search-select__list.open{display:block;}
.search-select__item{padding:9px 12px;font-size:var(--font-sm);cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:10px;border-bottom:1px solid #F5F5F5;}
.search-select__item:last-child{border-bottom:none;}
.search-select__item:hover,.search-select__item.active{background:var(--primary-light);}
.search-select__item .plate{font-weight:600;color:var(--text-primary);}
.search-select__item .point{color:var(--text-secondary);font-size:var(--font-xs);}
.search-select__empty{padding:12px;color:var(--text-placeholder);font-size:var(--font-sm);text-align:center;}
.radio-group{display:flex;gap:20px;height:36px;align-items:center;flex-wrap:wrap;}
.radio{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;}
.radio input{accent-color:var(--primary);cursor:pointer;}
@@ -246,9 +255,12 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="form-section">救护车归属</div>
<div class="form-grid">
<div class="form-item"><span class="form-label"><span class="req">*</span> 所属医疗点</span>
<select class="form-control" id="fPoint" onchange="onFormPoint()"><option value="">请选择医疗点</option><option>樊学作业区医疗点</option><option>安塞采油队长住点医疗室</option><option>姬塬作业区医疗点</option><option>姬圪台作业区医疗点</option><option>桐园作业区医疗点</option><option>西峰基地医疗点</option><option>靖边气田医疗点</option><option>乌审旗采气区医疗点</option></select></div>
<input class="form-control" id="fPoint" disabled placeholder="默认医疗点"></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 救护车</span>
<select class="form-control" id="fAmb" onchange="onFormAmb()"><option value="">请先选择医疗点</option></select></div>
<div class="search-select">
<input class="form-control" id="fAmb" placeholder="请输入车牌号搜索" autocomplete="off" oninput="onAmbSearch()" onfocus="onAmbSearch()">
<div class="search-select__list" id="fAmbList"></div>
</div></div>
<div class="form-item"><span class="form-label">救护类型</span>
<input class="form-control" id="fAmbulanceType" disabled placeholder="选择救护车后自动带出"></div>
</div>
@@ -263,11 +275,10 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<label class="radio"><input type="radio" name="fDispatch" value="平诊转运">平诊转运</label>
<label class="radio"><input type="radio" name="fDispatch" value="日常巡诊">日常巡诊</label>
<label class="radio"><input type="radio" name="fDispatch" value="应急戒备">应急戒备</label>
<label class="radio"><input type="radio" name="fDispatch" value="床头呼叫">床头呼叫</label>
<label class="radio"><input type="radio" name="fDispatch" value="其他">其他</label>
</div></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 出勤司机</span>
<select class="form-control" id="fDriver" onchange="onFormDriver()"><option value="">请选择司机</option></select></div>
<input class="form-control" id="fDriver" disabled placeholder="选择救护车后自动带出"></div>
<div class="form-item"><span class="form-label">司机电话</span>
<input class="form-control" id="fTel" disabled placeholder="选择司机后自动带出"></div>
<div class="form-item form-item--full"><span class="form-label"><span class="req">*</span> 随车医护</span>
@@ -366,14 +377,6 @@ var ambulances=[
{plate:'CQ·0917E',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',driver:'苏文斌',tel:'15598486703'},
{plate:'CQ·2201F',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',driver:'曹雪芹',tel:'18642802940'}
];
/* 救护车司机列表:从救护车台账驾驶员去重提取 */
var drivers=[];
(function(){
var seen={};
ambulances.forEach(function(a){
if(!seen[a.driver]){seen[a.driver]=true;drivers.push({name:a.driver,tel:a.tel});}
});
})();
/* 医护人员池:与「基础配置·医护人员管理」一致 */
var medStaff=[
{id:1,name:'韩东学',job:'主治医师',post:'医生',point:'樊学作业区医疗点'},
@@ -406,7 +409,6 @@ var attendances=[
];
var nextId=13,editingId=null,pendingDelId=null;
var dispatchBadge={'急诊转运':'badge--danger','平诊转运':'badge--info','日常巡诊':'badge--success','应急戒备':'badge--warning','床头呼叫':'badge--info','其他':'badge--muted'};
var typeBadge={'急救型':'badge--danger','转运型':'badge--info'};
/* ===== 筛选与渲染 ===== */
var pageSize=10,currentPage=1;
@@ -448,15 +450,15 @@ function renderTable(){
html+='<tr>'
+'<td>'+(start+i+1)+'</td>'
+'<td>'+r.date+'</td>'
+'<td style="text-align:left;">'+r.point+'</td>'
+'<td>'+r.point+'</td>'
+'<td style="font-weight:600;">'+r.plate+'</td>'
+'<td><span class="badge '+typeBadge[r.type]+'">'+r.type+'</span></td>'
+'<td>'+r.type+'</td>'
+'<td><span class="badge '+dispatchBadge[r.dispatch]+'">'+r.dispatch+'</span></td>'
+'<td>'+r.driver+'</td>'
+'<td>'+r.tel+'</td>'
+'<td style="text-align:left;">'+(r.staff.join('、'))+'</td>'
+'<td style="text-align:left;max-width:180px;overflow:hidden;text-overflow:ellipsis;">'+r.out+'</td>'
+'<td style="text-align:left;max-width:180px;overflow:hidden;text-overflow:ellipsis;">'+r.to+'</td>'
+'<td>'+(r.staff.join('、'))+'</td>'
+'<td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;">'+r.out+'</td>'
+'<td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;">'+r.to+'</td>'
+'<td class="td-actions">'
+'<button class="btn btn-link" onclick="openDetail('+r.id+')">详情</button>'
+'<button class="btn btn-link" onclick="openForm('+r.id+')">编辑</button>'
@@ -482,36 +484,27 @@ function turnPage(d){
}
/* ===== 新增/编辑抽屉 ===== */
function fillAmb(point,keep){
var sel=document.getElementById('fAmb');
sel.innerHTML='<option value="">请选择救护车</option>';
ambulances.filter(function(a){return !point||a.point===point;}).forEach(function(a){
var o=document.createElement('option');o.textContent=a.plate;
if(keep&&a.plate===keep)o.selected=true;
sel.appendChild(o);
});
var DEFAULT_POINT='樊学作业区医疗点';
function onAmbSearch(){
var kw=document.getElementById('fAmb').value.trim().toUpperCase();
var list=ambulances.filter(function(a){return !kw||a.plate.toUpperCase().indexOf(kw)!==-1;});
var box=document.getElementById('fAmbList');
box.innerHTML=list.length?list.map(function(a){
return '<div class="search-select__item" onmousedown="selectAmb(\''+a.plate+'\')">'
+'<span class="plate">'+a.plate+'</span>'
+'<span class="point">'+a.point+' · '+a.type+'</span></div>';
}).join(''):'<div class="search-select__empty">无匹配救护车</div>';
box.classList.add('open');
}
function fillDriverSel(keep){
var sel=document.getElementById('fDriver');
sel.innerHTML='<option value="">请选择司机</option>';
drivers.forEach(function(d){
var o=document.createElement('option');o.textContent=d.name;
if(keep&&d.name===keep)o.selected=true;
sel.appendChild(o);
});
}
function onFormPoint(){fillAmb(document.getElementById('fPoint').value);clearAmbType();}
function onFormAmb(){
var plate=document.getElementById('fAmb').value;
function selectAmb(plate){
var a=ambulances.find(function(x){return x.plate===plate;});
document.getElementById('fAmb').value=plate;
document.getElementById('fAmbulanceType').value=a?a.type:'';
document.getElementById('fDriver').value=a?a.driver:'';
document.getElementById('fTel').value=a?a.tel:'';
document.getElementById('fAmbList').classList.remove('open');
}
function clearAmbType(){document.getElementById('fAmbulanceType').value='';}
function onFormDriver(){
var name=document.getElementById('fDriver').value;
var d=drivers.find(function(x){return x.name===name;});
document.getElementById('fTel').value=d?d.tel:'';
}
function closeAmbList(){document.getElementById('fAmbList').classList.remove('open');}
var selectedStaffIds=[];
function setRadio(name,val){
document.querySelectorAll('input[name="'+name+'"]').forEach(function(r){r.checked=(r.value===val);});
@@ -535,23 +528,24 @@ function openForm(id){
editingId=id||null;
document.getElementById('formTitle').textContent=id?'编辑出勤记录':'新增出勤';
var r=id?attendances.find(function(x){return x.id===id;}):null;
document.getElementById('fPoint').value=r?r.point:'';
fillAmb(r?r.point:'',r?r.plate:'');
if(!r)document.getElementById('fAmb').innerHTML='<option value="">请先选择医疗点</option>';
fillDriverSel(r?r.driver:'');
document.getElementById('fDate').value=r?r.date:'';
document.getElementById('fPoint').value=r?r.point:DEFAULT_POINT;
document.getElementById('fAmb').value=r?r.plate:'';
document.getElementById('fAmbulanceType').value=r?r.type:'';
document.getElementById('fDriver').value=r?r.driver:'';
document.getElementById('fTel').value=r?r.tel:'';
document.getElementById('fDate').value=r?r.date:todayStr();
setRadio('fDispatch',r?r.dispatch:'急诊转运');
selectedStaffIds=(r?r.staff.slice():[]).map(function(n){var m=medStaff.find(function(x){return x.name===n;});return m?m.id:null;}).filter(function(id){return id!=null;});
renderStaffChips();
document.getElementById('fOut').value=r?r.out:'';
document.getElementById('fTo').value=r?r.to:'';
if(r){onFormAmb();onFormDriver();}else{clearAmbType();document.getElementById('fTel').value='';}
closeAmbList();
document.getElementById('formMask').classList.add('open');
document.getElementById('formDrawer').classList.add('open');
}
function saveForm(){
var point=document.getElementById('fPoint').value;
var plate=document.getElementById('fAmb').value;
var plate=document.getElementById('fAmb').value.trim();
var date=document.getElementById('fDate').value;
var dispatch=getRadio('fDispatch');
var driver=document.getElementById('fDriver').value;
@@ -565,12 +559,11 @@ function saveForm(){
if(!out){showToast('请填写出勤地点');return;}
if(!to){showToast('请填写到达地点');return;}
var a=ambulances.find(function(x){return x.plate===plate;});
var d=drivers.find(function(x){return x.name===driver;});
var staffNames=selectedStaffIds.map(function(id){return medStaff.find(function(m){return m.id===id;}).name;});
var obj={
id:editingId||nextId++,
date:date,point:point,plate:plate,type:a.type,dispatch:dispatch,
driver:driver,tel:d.tel,staff:staffNames,out:out,to:to
driver:driver,tel:a.tel,staff:staffNames,out:out,to:to
};
if(editingId){
attendances.splice(attendances.findIndex(function(x){return x.id===editingId;}),1,obj);
@@ -588,7 +581,7 @@ function openDetail(id){
function d(k,v,full){return '<div class="detail-item'+(full?' detail-item--full':'')+'"><div class="dk">'+k+'</div><div class="dv">'+(v===''||v==null?'—':v)+'</div></div>';}
document.getElementById('detailBody').innerHTML=
'<div class="form-section" style="margin-top:0;">救护车信息</div>'
+'<div class="detail-grid">'+d('所属医疗点',r.point)+d('救护车',r.plate)+d('救护类型','<span class="badge '+typeBadge[r.type]+'">'+r.type+'</span>')+'</div>'
+'<div class="detail-grid">'+d('所属医疗点',r.point)+d('救护车',r.plate)+d('救护类型',r.type)+'</div>'
+'<div class="form-section">出勤信息</div>'
+'<div class="detail-grid">'
+d('出勤日期',r.date)
@@ -606,7 +599,8 @@ function openDetail(id){
/* ===== 随车医护选择弹窗 ===== */
function renderStaffList(){
var kw=document.getElementById('staffKeyword').value.trim();
var list=medStaff.filter(function(m){return !kw||m.name.indexOf(kw)!==-1;});
var point=document.getElementById('fPoint').value;
var list=medStaff.filter(function(m){return (!point||m.point===point)&&(!kw||m.name.indexOf(kw)!==-1);});
var body=document.getElementById('staffListBody');
if(!list.length){body.innerHTML='<tr><td colspan="5" class="td-empty">暂无匹配人员</td></tr>';return;}
body.innerHTML=list.map(function(m){
@@ -652,6 +646,13 @@ function closeDrawer(k){
document.getElementById(k+'Drawer').classList.remove('open');
}
function closeModal(id){document.getElementById(id).classList.remove('open');}
function todayStr(){
var d=new Date();
return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');
}
document.addEventListener('click',function(e){
if(!e.target.closest('.search-select'))closeAmbList();
});
function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
@@ -30,7 +30,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.main{margin-left:240px;margin-top:56px;padding:16px 20px;min-height:calc(100vh - 56px);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
@@ -39,28 +39,29 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 所属单位→所属医疗点级联;清空单位回到全局统计(与老系统 allowClear 一致) */
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-bar{background:#fff;padding:12px 20px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-select{height:36px;min-width:140px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
.filter-select:disabled{background-color:#F5F5F5;color:var(--text-placeholder);cursor:not-allowed;}
/* 卡片:基本数据(32%)+出勤次数(66%),布局与老系统 number-container 一致 */
.num-row{display:flex;gap:16px;margin-top:16px;align-items:stretch;}
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 20px;}
.stat-card__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin-bottom:12px;font-size:var(--font-base);}
.stat-card__grid{display:flex;justify-content:space-around;background:#F8F8FA;border-radius:10px;padding:14px 8px;}
.num-row{display:flex;gap:16px;margin-top:12px;align-items:stretch;}
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:12px 16px;}
.stat-card__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin-bottom:8px;}
.stat-card__grid{display:flex;justify-content:space-around;background:#F8F8FA;border-radius:10px;padding:10px 8px;}
.stat-cell{text-align:center;}
.stat-cell__val{font-size:22px;font-weight:600;color:var(--text-primary);}
.stat-cell__val{font-size:20px;font-weight:600;color:var(--text-primary);}
.stat-cell__val small{font-size:var(--font-xs);font-weight:400;color:var(--text-placeholder);margin-left:2px;}
.stat-cell__name{font-size:var(--font-sm);color:var(--text-secondary);margin-top:4px;}
.stat-cell__name{font-size:var(--font-sm);color:var(--text-secondary);margin-top:2px;}
.num-card--l{width:32%;}.num-card--r{width:68%;}
.chart-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-top:16px;}
.chart-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 22px;}
.chart-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:12px;}
.chart-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:14px 18px;}
.chart-card--full{grid-column:1/-1;}
.chart-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);display:flex;align-items:center;justify-content:space-between;}
.chart-card__title .lg{display:flex;gap:12px;font-size:var(--font-xs);font-weight:400;color:var(--text-secondary);}
.chart-card__title .lg i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:4px;vertical-align:-1px;}
.chart-card svg{width:100%;height:auto;display:block;margin-top:10px;}
.chart-card--full svg{max-height:230px;}
.wk-switch{display:inline-flex;height:26px;line-height:26px;text-align:center;font-weight:400;cursor:pointer;font-size:var(--font-sm);}
.wk-switch span{width:50px;border:1px solid var(--primary);}
.wk-switch span:first-child{border-radius:var(--radius-sm) 0 0 var(--radius-sm);}
@@ -155,13 +156,13 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="chart-grid">
<div class="chart-card">
<div class="chart-card__title">救护车类型占比<span style="font-weight:400;color:var(--text-placeholder);font-size:var(--font-xs);">(辆)</span></div>
<svg id="pieType" viewBox="0 0 560 240"></svg>
<svg id="pieType" viewBox="0 0 560 220"></svg>
</div>
<div class="chart-card">
<div class="chart-card__title">出勤次数占比
<span class="wk-switch" id="wkDisp"><span class="on" onclick="setWeek('wkDisp',true)">本周</span><span class="off" onclick="setWeek('wkDisp',false)">累计</span></span>
</div>
<svg id="pieDisp" viewBox="0 0 560 260"></svg>
<svg id="pieDisp" viewBox="0 0 560 230"></svg>
</div>
<div class="chart-card">
<div class="chart-card__title">医护人员出勤次数排名TOP5
@@ -182,7 +183,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="chart-card__title">本周出勤次数变化趋势
<span class="lg" id="trendLg"></span>
</div>
<svg id="chartTrend" viewBox="0 0 1120 280"></svg>
<svg id="chartTrend" viewBox="0 0 1120 200"></svg>
</div>
</div>
</main>
@@ -191,38 +192,38 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<script>
/* 数据源与「救护车配置」页勾稽:6 辆车=4 急救型+2 转运型,辐射人数取车辆覆盖人数;
出勤类口径:急救转运/平诊转运/日常巡诊/应急戒备/其他出勤(dispatch_type 字典) */
var TYPES=['jz','pz','rc','yj','qt'];
var TYPE_NAME={jz:'急救转运',pz:'平诊转运',rc:'日常巡诊',yj:'应急戒备',qt:'其他出勤'};
var DISC=['#5087ec','#68bbc4','#58a55c','#f2bd42','#ee752f'];
出勤类口径:急救转运/平诊转运/日常巡诊/应急戒备/床头呼叫/其他出勤(dispatch_type 字典) */
var TYPES=['jz','pz','rc','yj','ct','qt'];
var TYPE_NAME={jz:'急救转运',pz:'平诊转运',rc:'日常巡诊',yj:'应急戒备',ct:'床头呼叫',qt:'其他出勤'};
var DISC=['#5087ec','#68bbc4','#58a55c','#f2bd42','#9966cc','#ee752f'];
var pts=[
{unit:'采油一厂',name:'樊学作业区医疗点',total:1,jj:1,zy:0,rad:860,
w:{jz:3,pz:2,rc:5,yj:1,qt:1},t:{jz:42,pz:28,rc:76,yj:15,qt:9},
staff:[{name:'李桂芳',w:{jz:2,pz:1,rc:3,yj:1,qt:1},t:{jz:24,pz:16,rc:41,yj:8,qt:5}},
{name:'石维国',w:{jz:1,pz:1,rc:2,yj:0,qt:0},t:{jz:18,pz:12,rc:35,yj:7,qt:4}}]},
w:{jz:3,pz:2,rc:5,yj:1,qt:1,ct:0},t:{jz:42,pz:28,rc:76,yj:15,qt:9,ct:0},
staff:[{name:'李桂芳',w:{jz:2,pz:1,rc:3,yj:1,qt:1,ct:0},t:{jz:24,pz:16,rc:41,yj:8,qt:5,ct:0}},
{name:'石维国',w:{jz:1,pz:1,rc:2,yj:0,qt:0,ct:0},t:{jz:18,pz:12,rc:35,yj:7,qt:4,ct:0}}]},
{unit:'采油一厂',name:'安塞采油队长住点医疗室',total:1,jj:0,zy:1,rad:540,
w:{jz:0,pz:3,rc:2,yj:0,qt:1},t:{jz:9,pz:41,rc:33,yj:6,qt:10},
staff:[{name:'周娟',w:{jz:0,pz:3,rc:2,yj:0,qt:1},t:{jz:9,pz:41,rc:33,yj:6,qt:10}}]},
w:{jz:0,pz:3,rc:2,yj:0,qt:1,ct:0},t:{jz:9,pz:41,rc:33,yj:6,qt:10,ct:0},
staff:[{name:'周娟',w:{jz:0,pz:3,rc:2,yj:0,qt:1,ct:0},t:{jz:9,pz:41,rc:33,yj:6,qt:10,ct:0}}]},
{unit:'采油一厂',name:'姬塬作业区医疗点',total:0,jj:0,zy:0,rad:0,
w:{jz:0,pz:0,rc:0,yj:0,qt:0},t:{jz:0,pz:0,rc:0,yj:0,qt:0},staff:[]},
w:{jz:0,pz:0,rc:0,yj:0,qt:0,ct:0},t:{jz:0,pz:0,rc:0,yj:0,qt:0,ct:0},staff:[]},
{unit:'采油二厂',name:'姬圪台作业区医疗点',total:1,jj:1,zy:0,rad:1240,
w:{jz:2,pz:1,rc:4,yj:2,qt:0},t:{jz:35,pz:22,rc:61,yj:18,qt:7},
staff:[{name:'曲志明',w:{jz:1,pz:1,rc:2,yj:1,qt:0},t:{jz:19,pz:13,rc:34,yj:10,qt:4}},
{name:'张秀兰',w:{jz:1,pz:0,rc:2,yj:1,qt:0},t:{jz:16,pz:9,rc:27,yj:8,qt:3}}]},
w:{jz:2,pz:1,rc:4,yj:2,qt:0,ct:0},t:{jz:35,pz:22,rc:61,yj:18,qt:7,ct:0},
staff:[{name:'曲志明',w:{jz:1,pz:1,rc:2,yj:1,qt:0,ct:0},t:{jz:19,pz:13,rc:34,yj:10,qt:4,ct:0}},
{name:'张秀兰',w:{jz:1,pz:0,rc:2,yj:1,qt:0,ct:0},t:{jz:16,pz:9,rc:27,yj:8,qt:3,ct:0}}]},
{unit:'采油二厂',name:'桐园作业区医疗点',total:0,jj:0,zy:0,rad:0,
w:{jz:0,pz:0,rc:0,yj:0,qt:0},t:{jz:0,pz:0,rc:0,yj:0,qt:0},staff:[]},
w:{jz:0,pz:0,rc:0,yj:0,qt:0,ct:0},t:{jz:0,pz:0,rc:0,yj:0,qt:0,ct:0},staff:[]},
{unit:'第四采油厂',name:'西峰基地医疗点',total:1,jj:0,zy:1,rad:2100,
w:{jz:1,pz:6,rc:7,yj:3,qt:2},t:{jz:18,pz:88,rc:97,yj:26,qt:14},
staff:[{name:'高建军',w:{jz:1,pz:3,rc:3,yj:2,qt:1},t:{jz:10,pz:45,rc:49,yj:14,qt:8}},
{name:'闫婷',w:{jz:0,pz:3,rc:4,yj:1,qt:1},t:{jz:8,pz:43,rc:48,yj:12,qt:6}}]},
w:{jz:1,pz:6,rc:7,yj:3,qt:2,ct:0},t:{jz:18,pz:88,rc:97,yj:26,qt:14,ct:0},
staff:[{name:'高建军',w:{jz:1,pz:3,rc:3,yj:2,qt:1,ct:0},t:{jz:10,pz:45,rc:49,yj:14,qt:8,ct:0}},
{name:'闫婷',w:{jz:0,pz:3,rc:4,yj:1,qt:1,ct:0},t:{jz:8,pz:43,rc:48,yj:12,qt:6,ct:0}}]},
{unit:'采气一厂',name:'靖边气田医疗点',total:1,jj:1,zy:0,rad:760,
w:{jz:2,pz:1,rc:3,yj:1,qt:1},t:{jz:27,pz:19,rc:48,yj:12,qt:6},
staff:[{name:'刘玉梅',w:{jz:2,pz:1,rc:2,yj:1,qt:0},t:{jz:16,pz:11,rc:29,yj:7,qt:3}},
{name:'呼建国',w:{jz:0,pz:0,rc:1,yj:0,qt:1},t:{jz:11,pz:8,rc:19,yj:5,qt:3}}]},
w:{jz:2,pz:1,rc:3,yj:1,qt:1,ct:0},t:{jz:27,pz:19,rc:48,yj:12,qt:6,ct:0},
staff:[{name:'刘玉梅',w:{jz:2,pz:1,rc:2,yj:1,qt:0,ct:0},t:{jz:16,pz:11,rc:29,yj:7,qt:3,ct:0}},
{name:'呼建国',w:{jz:0,pz:0,rc:1,yj:0,qt:1,ct:0},t:{jz:11,pz:8,rc:19,yj:5,qt:3,ct:0}}]},
{unit:'采气一厂',name:'乌审旗采气区医疗点',total:1,jj:1,zy:0,rad:680,
w:{jz:1,pz:1,rc:3,yj:2,qt:0},t:{jz:22,pz:14,rc:40,yj:16,qt:5},
staff:[{name:'徐立成',w:{jz:1,pz:0,rc:2,yj:1,qt:0},t:{jz:13,pz:8,rc:24,yj:10,qt:3}},
{name:'马丽华',w:{jz:0,pz:1,rc:1,yj:1,qt:0},t:{jz:9,pz:6,rc:16,yj:6,qt:2}}]}
w:{jz:1,pz:1,rc:3,yj:2,qt:0,ct:0},t:{jz:22,pz:14,rc:40,yj:16,qt:5,ct:0},
staff:[{name:'徐立成',w:{jz:1,pz:0,rc:2,yj:1,qt:0,ct:0},t:{jz:13,pz:8,rc:24,yj:10,qt:3,ct:0}},
{name:'马丽华',w:{jz:0,pz:1,rc:1,yj:1,qt:0,ct:0},t:{jz:9,pz:6,rc:16,yj:6,qt:2,ct:0}}]}
];
var weekState={wkDisp:true,wkTop:true};
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;');}
@@ -307,7 +308,7 @@ function drawTop5(id,data,color,unit){
svg.innerHTML=html;
svg.setAttribute('viewBox','0 0 '+W+' '+h);
}
/* ===== 本周逐日堆积柱状(5系列 stack,照搬老系统 barCharts) ===== */
/* ===== 本周逐日堆积柱状(6系列 stack,照搬老系统 barCharts) ===== */
var DAYS=['周一','周二','周三','周四','周五','周六','周日'];
var WT=[2,1,2,1,3,2,1];
function splitWeek(v){
@@ -323,7 +324,7 @@ function renderTrend(){
return {name:TYPE_NAME[k],color:DISC[i],data:days};
});
document.getElementById('trendLg').innerHTML=series.map(function(s){return '<span><i style="background:'+s.color+';"></i>'+s.name+'</span>';}).join('');
var svg=document.getElementById('chartTrend'),W=1120,H=280,left=44,bottom=34,top=12,right=16;
var svg=document.getElementById('chartTrend'),W=1120,H=200,left=44,bottom=34,top=12,right=16;
var max=0;DAYS.forEach(function(d,i){var s=0;series.forEach(function(se){s+=se.data[i];});if(s>max)max=s;});
max=max||1;
var step=(W-left-right)/7,bw=Math.min(56,step*0.5),html='';
@@ -375,7 +376,7 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
/* 初始化:单位下拉 + 出勤类型下拉(dispatch_type 字典类) */
/* 初始化:单位下拉 + 出勤类型下拉(dispatch_type 字典类) */
(function(){
var us=[];pts.forEach(function(p){if(us.indexOf(p.unit)<0)us.push(p.unit);});
document.getElementById('selUnit').innerHTML='<option value="">全部单位</option>'+us.map(function(u){return '<option>'+esc(u)+'</option>';}).join('');
@@ -39,6 +39,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.action-bar{display:flex;justify-content:flex-start;gap:8px;margin-top:16px;}
.date-switch{display:inline-flex;height:32px;line-height:32px;border:1px solid var(--primary);border-radius:var(--radius-sm);overflow:hidden;font-size:var(--font-sm);}
.date-switch span{padding:0 16px;cursor:pointer;color:var(--primary);background:#fff;border-right:1px solid var(--primary);white-space:nowrap;}
.date-switch span:last-child{border-right:none;}
@@ -62,7 +63,7 @@ thead tr.grp th{background:#F0F5FF;color:var(--text-primary);border-right:1px so
tbody td{padding:12px 14px;border-bottom:1px solid #F0F0F0;text-align:center;white-space:nowrap;font-size:var(--font-sm);}
tbody tr:hover td{background:#F8FBFF;}
.td-empty{text-align:center;padding:48px 0;color:var(--text-placeholder);}
.num-cur{color:var(--primary);font-weight:600;}
.num-cur{color:var(--text-primary);font-weight:600;}
.num-total{color:var(--text-secondary);}
.badge{display:inline-block;padding:1px 8px;border-radius:10px;font-size:var(--font-xs);line-height:18px;}
.badge--danger{background:#FFF1F0;color:#FF4D4F;border:1px solid #FFA39E;}
@@ -130,7 +131,7 @@ tbody tr:hover td{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- 时间切换 + 导出操作(照搬老系统 DateButtonSelector + 导出按钮) -->
<!-- 时间切换(照搬老系统 DateButtonSelector) -->
<div class="toolbar">
<div class="date-switch" id="dateSwitch">
<span class="on" data-type="week" onclick="switchDateType('week')">本周</span>
@@ -138,10 +139,6 @@ tbody tr:hover td{background:#F8FBFF;}
<span data-type="year" onclick="switchDateType('year')">本年</span>
<span data-type="picker" onclick="switchDateType('picker')">自定义</span>
</div>
<div style="display:flex;gap:8px;">
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出</button>
<button class="btn btn-default" onclick="openExportLog()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>查看导出记录</button>
</div>
</div>
<!-- 筛选栏:单位→医疗点级联 + 救护类型 + 时间 -->
@@ -178,7 +175,11 @@ tbody tr:hover td{background:#F8FBFF;}
</div>
</div>
<!-- 分组报表:基础信息 + 类出勤次数(本周/累计) -->
<!-- 分组报表:基础信息 + 类出勤次数(本周/累计) -->
<div class="action-bar">
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default" onclick="openExportLog()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>查看导出记录</button>
</div>
<div class="table-card">
<div class="table-card__content">
<table>
@@ -193,6 +194,7 @@ tbody tr:hover td{background:#F8FBFF;}
<th colspan="2">平诊转运</th>
<th colspan="2">日常巡诊</th>
<th colspan="2">应急戒备</th>
<th colspan="2">床头呼叫</th>
<th colspan="2">其他</th>
</tr>
<tr>
@@ -206,6 +208,8 @@ tbody tr:hover td{background:#F8FBFF;}
<th>累计出勤次数</th>
<th><span class="period-text">本周</span>出勤次数</th>
<th>累计出勤次数</th>
<th><span class="period-text">本周</span>出勤次数</th>
<th>累计出勤次数</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
@@ -233,23 +237,23 @@ tbody tr:hover td{background:#F8FBFF;}
<div class="toast" id="toast"></div>
<script>
/* 数据源:6 辆救护车(与「救护车配置」页车牌/车型/救护类型勾稽,与「救护车统计-图表」类出勤口径同源);
出勤类:急诊转运/平诊转运/日常巡诊/应急戒备/其他,各分 本周/累计 出勤次数 */
/* 数据源:6 辆救护车(与「救护车配置」页车牌/车型/救护类型勾稽,与「救护车统计-图表」类出勤口径同源);
出勤类:急诊转运/平诊转运/日常巡诊/应急戒备/床头呼叫/其他,各分 本周/累计 出勤次数 */
var cars=[
{unit:'采油一厂',point:'樊学作业区医疗点',type:'急救型',model:'江铃轻型监护型救护车',plate:'CQ·0712A',
w:{jz:3,pz:2,rc:5,yj:1,qt:1},t:{jz:42,pz:28,rc:76,yj:15,qt:9}},
w:{jz:3,pz:2,rc:5,yj:1,qt:1,ct:0},t:{jz:42,pz:28,rc:76,yj:15,qt:9,ct:0}},
{unit:'采油二厂',point:'姬圪台作业区医疗点',type:'急救型',model:'福田图雅诺监护型救护车',plate:'CQ·1366B',
w:{jz:2,pz:1,rc:4,yj:2,qt:0},t:{jz:35,pz:22,rc:61,yj:18,qt:7}},
w:{jz:2,pz:1,rc:4,yj:2,qt:0,ct:0},t:{jz:35,pz:22,rc:61,yj:18,qt:7,ct:0}},
{unit:'第四采油厂',point:'西峰基地医疗点',type:'转运型',model:'福特全顺转运型救护车',plate:'CQ·0208C',
w:{jz:1,pz:6,rc:7,yj:3,qt:2},t:{jz:18,pz:88,rc:97,yj:26,qt:14}},
w:{jz:1,pz:6,rc:7,yj:3,qt:2,ct:0},t:{jz:18,pz:88,rc:97,yj:26,qt:14,ct:0}},
{unit:'采气一厂',point:'靖边气田医疗点',type:'急救型',model:'东风轻型救护车',plate:'CQ·1533D',
w:{jz:2,pz:1,rc:3,yj:1,qt:1},t:{jz:27,pz:19,rc:48,yj:12,qt:6}},
w:{jz:2,pz:1,rc:3,yj:1,qt:1,ct:0},t:{jz:27,pz:19,rc:48,yj:12,qt:6,ct:0}},
{unit:'采油一厂',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',plate:'CQ·0917E',
w:{jz:0,pz:3,rc:2,yj:0,qt:1},t:{jz:9,pz:41,rc:33,yj:6,qt:10}},
w:{jz:0,pz:3,rc:2,yj:0,qt:1,ct:0},t:{jz:9,pz:41,rc:33,yj:6,qt:10,ct:0}},
{unit:'采气一厂',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',plate:'CQ·2201F',
w:{jz:1,pz:1,rc:3,yj:2,qt:0},t:{jz:22,pz:14,rc:40,yj:16,qt:5}}
w:{jz:1,pz:1,rc:3,yj:2,qt:0,ct:0},t:{jz:22,pz:14,rc:40,yj:16,qt:5,ct:0}}
];
var TYPES=['jz','pz','rc','yj','qt'];
var TYPES=['jz','pz','rc','yj','ct','qt'];
var dateType='week';
var PERIOD_TEXT={week:'本周',month:'本月',year:'本年',picker:'时段内'};
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;');}
@@ -38,9 +38,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 工具栏:时间切换 + 导出操作 */
/* 工具栏:时间切换 */
.toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.action-bar{display:flex;justify-content:flex-end;margin-top:16px;}
.action-bar{display:flex;justify-content:flex-start;gap:8px;margin-top:16px;}
.date-switch{display:inline-flex;height:32px;line-height:32px;border:1px solid var(--primary);border-radius:var(--radius-sm);overflow:hidden;font-size:var(--font-sm);}
.date-switch span{padding:0 16px;cursor:pointer;color:var(--primary);background:#fff;border-right:1px solid var(--primary);white-space:nowrap;}
.date-switch span:last-child{border-right:none;}
@@ -135,7 +135,7 @@ th.sortable:hover{color:var(--primary);}
<!-- 主内容 -->
<main class="main">
<!-- 时间切换 + 导出操作(照搬老系统 DateButtonSelector + 导出按钮) -->
<!-- 时间切换(照搬老系统 DateButtonSelector) -->
<div class="toolbar">
<div class="date-switch" id="dateSwitch">
<span class="on" data-type="week" onclick="switchDateType('week')">本周</span>
@@ -143,10 +143,6 @@ th.sortable:hover{color:var(--primary);}
<span data-type="year" onclick="switchDateType('year')">本年</span>
<span data-type="picker" onclick="switchDateType('picker')">自定义</span>
</div>
<div style="display:flex;gap:8px;">
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出</button>
<button class="btn btn-default" onclick="openExportLog()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>查看导出记录</button>
</div>
</div>
<!-- 筛选栏:所属单位 + 时间 -->
@@ -176,6 +172,7 @@ th.sortable:hover{color:var(--primary);}
<!-- 分组大表:就诊人次 / 慢病管理 / 培训宣教 / 体检报告解读 多级表头 -->
<div class="action-bar">
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default" onclick="openExportLog()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6.5" cy="6.5" r="4.5" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>查看导出记录</button>
</div>
<div class="table-card">
<div class="table-card__content">
@@ -135,11 +135,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<option value="">全部</option>
</select>
</div>
<button class="btn btn-primary" onclick="openImport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>批量导入数据</button>
<button class="btn btn-primary" onclick="openImport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导入模板数据</button>
<button class="btn btn-default" onclick="openImportTasks()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导入任务</button>
<button class="btn btn-default" onclick="showToast('救助物品模板下载功能演示')">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v7M4.5 6.5L7 9l2.5-2.5M2 12h10" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>救助物品模板下载
</button>
</div>
<!-- 小药棒:6 项指标通栏 -->
@@ -181,6 +178,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="modal__title"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 14V3M6.5 6.5L10 3l3.5 3.5M3 16h14" stroke="#1890FF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg> 导入救助物品数据</div>
<div style="margin-top:14px;">
<div class="drop-zone" onclick="showToast('演示环境请选择本地文件(仅支持.xlsx)')">点击选择或拖拽文件到此处上传<br>救助物品模板.xlsx</div>
<div style="font-size:var(--font-xs);color:var(--text-placeholder);margin-top:6px;">
<a href="javascript:void(0)" style="color:var(--primary);text-decoration:none;" onclick="showToast('模板下载中…')">下载导入模板</a> · 请按模板列整理后上传
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeImport()">取消</button>
+3 -3
View File
@@ -105,7 +105,7 @@
│ │ ├── 单位体检费用统计 → post-unit-cost-stat.html
│ │ │ └── Tab:二级单位 / 三级单位
│ │ ├── 单位体检项目统计 → post-unit-item-stat.html
│ │ ├── 结果动态综合统计 → post-result-comprehensive-stat.html
│ │ ├── 项目结果综合统计 → post-result-comprehensive-stat.html
│ │ ├── 医院体检人数统计 → post-hospital-count.html
│ │ ├── 医生检查人数统计 → post-doctor-count.html
│ │ ├── 医院体检项目统计 → post-item-stat.html
@@ -232,7 +232,7 @@
| 52 | 单位体检人数统计 | post-unit-count.html | 页面 |
| 53 | 单位体检费用统计 | post-unit-cost-stat.html | 页面 |
| 54 | 单位体检项目统计 | post-unit-item-stat.html | 页面 |
| 55 | 结果动态综合统计 | post-result-comprehensive-stat.html | 页面 |
| 55 | 项目结果综合统计 | post-result-comprehensive-stat.html | 页面 |
| 56 | 医院体检人数统计 | post-hospital-count.html | 页面 |
| 57 | 医生检查人数统计 | post-doctor-count.html | 页面 |
| 58 | 医院体检项目统计 | post-item-stat.html | 页面 |
@@ -1388,7 +1388,7 @@
---
#### 55. 结果动态综合统计(post-result-comprehensive-stat.html
#### 55. 项目结果综合统计(post-result-comprehensive-stat.html
![页面截图](screenshots/post-result-comprehensive-stat.jpg)
@@ -158,9 +158,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item active">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -151,9 +151,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -125,9 +125,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -129,9 +129,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item active">医生实时检查情况</a>
@@ -151,9 +151,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -130,9 +130,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -125,9 +125,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -125,9 +125,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -129,9 +129,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item active">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -148,9 +148,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item active">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -148,9 +148,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item active">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -134,9 +134,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item active">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -114,9 +114,9 @@
</div>
<div class="sidebar-subgroup__body">
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
<a href="mid-participation.html" class="sidebar-item">已检员工</a>
<a href="mid-appointment-track2.html" class="sidebar-item">体检预约跟踪</a>
<a href="mid-unit-appointment.html" class="sidebar-item">单位预约人数统计</a>
<a href="mid-hospital-realtime.html" class="sidebar-item">医院实时预约人数</a>
<a href="mid-doctor-realtime.html" class="sidebar-item">医生实时检查情况</a>
@@ -143,7 +143,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -158,7 +158,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -187,7 +187,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -197,7 +197,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -126,7 +126,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -142,7 +142,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -171,7 +171,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -181,7 +181,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -153,7 +153,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -168,7 +168,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -197,7 +197,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -207,7 +207,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -117,7 +117,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -132,7 +132,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -161,7 +161,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -171,7 +171,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -184,7 +184,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -199,7 +199,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -228,7 +228,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -238,7 +238,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item active">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item active">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
+4 -4
View File
@@ -143,7 +143,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -158,7 +158,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -187,7 +187,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -197,7 +197,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item active">冠脉CTA体检统计</a>
</div>
</div>
@@ -151,7 +151,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -166,7 +166,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -195,7 +195,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -205,7 +205,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -126,7 +126,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -142,7 +142,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item active">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -171,7 +171,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -181,7 +181,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -105,6 +105,20 @@
.year-picker__dropdown div{padding:8px 14px;font-size:var(--font-sm);cursor:pointer;}
.year-picker__dropdown div:hover{background:var(--primary-light);}
.year-picker__dropdown div.selected{color:var(--primary);font-weight:600;}
/* 高级查询 */
.adv-panel{background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:none;}
.adv-panel.open{display:block;}
.adv-panel__head{font-size:var(--font-base);font-weight:600;margin-bottom:12px;}
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:10px;}
.adv-field{min-width:210px;}
.adv-op{min-width:90px;}
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;max-width:440px;}
.adv-value{flex:1;min-width:0;}
.adv-range-sep{color:var(--text-secondary);flex-shrink:0;}
.adv-del{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;color:var(--text-secondary);cursor:pointer;flex-shrink:0;font-size:var(--font-sm);line-height:1;}
.adv-del:hover{color:var(--danger);border-color:var(--danger);}
.adv-actions{display:flex;gap:12px;margin-top:2px;}
.adv-arrow{font-size:10px;}
</style>
</head>
<body>
@@ -142,7 +156,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item active">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -158,7 +172,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -187,7 +201,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -197,7 +211,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -259,6 +273,18 @@
</div>
<button class="btn btn-primary" onclick="applyFilter()">查询</button>
<button class="btn btn-default" onclick="resetFilter()">重置</button>
<button class="btn btn-default" id="advToggle" onclick="toggleAdvanced()">高级查询 <span class="adv-arrow"></span></button>
</div>
<!-- 高级查询 -->
<div class="adv-panel" id="advPanel">
<div class="adv-panel__head">高级查询</div>
<div id="advConditions"></div>
<div class="adv-actions">
<button class="btn btn-default" onclick="addAdvCondition()">+ 添加条件</button>
<button class="btn btn-primary" onclick="applyFilter()">查询</button>
<button class="btn btn-default" onclick="clearAdvanced()">清空</button>
</div>
</div>
<!-- 操作栏 -->
@@ -532,21 +558,130 @@ function hasAbnormal(r){
for(var k in r.r){if(r.r[k][1])return true;}
return false;
}
/* 按筛选条件取当前展示行:异常员工 + 姓名/员工编号/身份证号模糊匹配 */
/* 按筛选条件取当前展示行:异常员工 + 姓名/员工编号/身份证号模糊匹配 + 高级查询条件 */
function getDisplayRows(){
var v=document.getElementById('abnormalFilter').value;
var name=document.getElementById('fName').value.trim();
var empNo=document.getElementById('fEmpNo').value.trim();
var idCard=document.getElementById('fIdCard').value.trim();
var conds=readAdvancedConditions();
return MASTER_ROWS.filter(function(r){
if(v==='abnormal'&&!hasAbnormal(r))return false;
if(v==='normal'&&hasAbnormal(r))return false;
if(name&&r.name.indexOf(name)<0)return false;
if(empNo&&r.empNo.indexOf(empNo)<0)return false;
if(idCard&&r.idCard.indexOf(idCard)<0)return false;
for(var i=0;i<conds.length;i++){if(!matchCondition(r,conds[i]))return false;}
return true;
});
}
/* ============ 高级查询 ============ */
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
var ADV_FIELD_OPTS='';
/* 字段下拉选项:基本信息 + 全部体检项目(按一级分类分组,二级分类·项目名 去重) */
function buildAdvFieldOpts(){
var o='<optgroup label="基本信息">';
BASE_COLS.forEach(function(c){if(c.key!=='idx')o+='<option value="'+c.key+'">'+c.label+'</option>';});
o+='</optgroup>';
ITEM_TREE.forEach(function(g){
o+='<optgroup label="'+g.lvl1+'">';
g.lvl2.forEach(function(s){
s.items.forEach(function(it){o+='<option value="'+s.name+'|'+it+'">'+s.name+'·'+it+'</option>';});
});
o+='</optgroup>';
});
return o;
}
function advConditionRowHTML(){
return '<div class="adv-cond-row">'
+'<select class="filter-select adv-field">'+ADV_FIELD_OPTS+'</select>'
+'<select class="filter-select adv-op" onchange="onAdvOpChange(this)">'+ADV_OPS.map(function(o){return '<option value="'+o+'">'+o+'</option>';}).join('')+'</select>'
+'<span class="adv-value-wrap"><input class="filter-input adv-value" type="text" placeholder="请输入值"></span>'
+'<button class="adv-del" type="button" title="删除该条件" onclick="removeAdvCondition(this)"></button>'
+'</div>';
}
/* 运算符切换为"范围"时,值区域变成最小/最大两个输入框 */
function onAdvOpChange(sel){
var wrap=sel.parentElement.querySelector('.adv-value-wrap');
var isRange=sel.value==='范围';
var alreadyRange=!!wrap.querySelector('.adv-range-from');
if(isRange&&!alreadyRange){
wrap.innerHTML='<input class="filter-input adv-value adv-range-from" type="text" placeholder="最小值">'
+'<span class="adv-range-sep">~</span>'
+'<input class="filter-input adv-value adv-range-to" type="text" placeholder="最大值">';
}else if(!isRange&&alreadyRange){
wrap.innerHTML='<input class="filter-input adv-value" type="text" placeholder="请输入值">';
}
}
function addAdvCondition(){
document.getElementById('advConditions').insertAdjacentHTML('beforeend',advConditionRowHTML());
}
function removeAdvCondition(el){el.parentElement.remove();}
function toggleAdvanced(){
var p=document.getElementById('advPanel');
var open=p.classList.toggle('open');
var arr=document.getElementById('advToggle').querySelector('.adv-arrow');
if(arr)arr.textContent=open?'▴':'▾';
if(open&&!p.querySelector('.adv-cond-row'))addAdvCondition();
}
function clearAdvanced(){
document.getElementById('advConditions').innerHTML='';
renderBigTable();
showToast('已清空高级查询条件');
}
/* 读取面板中已填值的条件(忽略值为空的行;范围条件任一边有值即计入) */
function readAdvancedConditions(){
var conds=[];
document.querySelectorAll('#advConditions .adv-cond-row').forEach(function(row){
var field=row.querySelector('.adv-field').value;
var op=row.querySelector('.adv-op').value;
if(op==='范围'){
var from=row.querySelector('.adv-range-from').value.trim();
var to=row.querySelector('.adv-range-to').value.trim();
if(from!==''||to!=='')conds.push({field:field,op:op,from:from,to:to});
}else{
var value=row.querySelector('.adv-value').value.trim();
if(value!=='')conds.push({field:field,op:op,value:value});
}
});
return conds;
}
/* 取某字段在某员工上的值:基本信息直接用 key,项目结果取"结果"字符串 */
function getFieldRaw(row,field){
if(field.indexOf('|')>=0){
var i=field.indexOf('|');
return resultVal(row,{cat:field.slice(0,i),name:field.slice(i+1)});
}
return row[field];
}
/* 单条条件匹配:包含/=/!= 按字符串,>、>=、<<=、范围 按数值 */
function matchCondition(row,cond){
var raw=getFieldRaw(row,cond.field);
var str=String(raw==null?'':raw);
if(cond.op==='包含')return str.indexOf(cond.value)>=0;
if(cond.op==='=')return str===cond.value;
if(cond.op==='!=')return str!==cond.value;
var n=numericOf(str);
if(n===null)return false;
if(cond.op==='范围'){
if(cond.from!==''){
var lo=parseFloat(cond.from);
if(!isNaN(lo)&&n<lo)return false;
}
if(cond.to!==''){
var hi=parseFloat(cond.to);
if(!isNaN(hi)&&n>hi)return false;
}
return true;
}
var c=parseFloat(cond.value);
if(isNaN(c))return false;
if(cond.op==='>')return n>c;
if(cond.op==='>=')return n>=c;
if(cond.op==='<')return n<c;
if(cond.op==='<=')return n<=c;
return true;
}
function renderBigTable(){
document.getElementById('bigTable').innerHTML=buildHead()+buildBody();
document.getElementById('pageTotal').textContent='共 '+getDisplayRows().length+' 条';
@@ -580,6 +715,7 @@ function resetFilter(){
showToast('已重置筛选条件');
}
renderBigTable();
ADV_FIELD_OPTS=buildAdvFieldOpts();
/* 体检年份选择控件 */
function pickYear(el){
var dp=el.parentElement,input=dp.parentElement.querySelector('input');
@@ -143,7 +143,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -158,7 +158,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -187,7 +187,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item active">胃肠镜体检统计</a>
</div>
</div>
@@ -197,7 +197,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
+18 -49
View File
@@ -130,15 +130,6 @@
.bar-row__val{width:210px;flex-shrink:0;font-size:var(--font-sm);color:var(--text-secondary);font-variant-numeric:tabular-nums;}
.bar-row__val b{color:var(--text-primary);font-weight:600;}
.bar-row__delta{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;}
/* 专项筛查卡片项 */
.screen-item{display:flex;align-items:center;padding:13px 0;border-bottom:1px solid #F0F0F0;}
.screen-item:first-child{padding-top:0;}
.screen-item:last-child{border-bottom:none;padding-bottom:0;}
.screen-item__name{width:180px;flex-shrink:0;font-size:var(--font-sm);color:var(--text-primary);}
.screen-item__val{font-size:var(--font-base);font-weight:600;color:var(--danger);margin-right:6px;font-variant-numeric:tabular-nums;}
.screen-item__unit{font-size:var(--font-sm);color:var(--text-secondary);}
.screen-item__rate{margin-left:auto;font-size:var(--font-sm);color:var(--text-placeholder);font-variant-numeric:tabular-nums;text-align:right;}
.screen-item__rate .sub{font-size:var(--font-xs);margin-top:3px;}
/* 趋势图例 */
.trend-legend{display:flex;gap:18px;font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:10px;}
.trend-legend i{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:5px;vertical-align:-1px;}
@@ -179,7 +170,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item active">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -194,7 +185,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -223,7 +214,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -233,10 +224,22 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
@@ -323,18 +326,9 @@ function unitData(idx,year){
var recheckDone=Math.round(recheckAdvised*(0.60+pseudo(s+13.1)*0.30)); // 已完成复查人数
var completeCnt=Math.round(examined*(0.90+pseudo(s+14.7)*0.09)); // 数据完整人数
var avgAge=34+Math.round(pseudo(s+15.2)*16); // 平均年龄 34~50
var cancerScreened=Math.round(examined*(0.55+pseudo(s+16.1)*0.30));// 防癌筛查已筛人数
var cancerPos=Math.round(cancerScreened*(0.05+pseudo(s+16.7)*0.09)); // Septin9 阳性
var ca724Pos=Math.round(cancerScreened*(0.04+pseudo(s+17.3)*0.08)); // CA724 阳性
var ctaScreened=Math.round(examined*(0.20+pseudo(s+18.1)*0.25)); // 冠脉CTA已检人数
var ctaAbn=Math.round(ctaScreened*(0.08+pseudo(s+18.9)*0.14)); // 冠脉CTA异常人数
var gastroScreened=Math.round(examined*(0.18+pseudo(s+20.5)*0.22)); // 胃肠镜已检人数
var gastroAbn=Math.round(gastroScreened*(0.10+pseudo(s+21.1)*0.20));// 胃肠镜异常人数
return {
name:UNITS[idx], total:total, examined:examined, abnormal:abnormal, keyAbn:keyAbn, healthy:healthy,
recheckAdvised:recheckAdvised, recheckDone:recheckDone, completeCnt:completeCnt, avgAge:avgAge,
cancerScreened:cancerScreened, cancerPos:cancerPos, ca724Pos:ca724Pos,
ctaScreened:ctaScreened, ctaAbn:ctaAbn, gastroScreened:gastroScreened, gastroAbn:gastroAbn,
hospital:HOSPITALS[idx%HOSPITALS.length]
};
}
@@ -350,9 +344,7 @@ function computeStats(idx,year){
recheckAdvised:d.recheckAdvised, recheckDone:d.recheckDone,
recheckRate:d.recheckAdvised?d.recheckDone/d.recheckAdvised*100:0,
completeCnt:d.completeCnt, completeRate:d.examined?d.completeCnt/d.examined*100:0,
avgAge:d.avgAge, male:male, female:female,
cancerScreened:d.cancerScreened, cancerPos:d.cancerPos, ca724Pos:d.ca724Pos,
ctaScreened:d.ctaScreened, ctaAbn:d.ctaAbn, gastroScreened:d.gastroScreened, gastroAbn:d.gastroAbn
avgAge:d.avgAge, male:male, female:female
};
}
/* 年龄×性别分布 */
@@ -498,7 +490,6 @@ function renderReport(){
var idx=state.unitIdx, year=state.year;
var prevYear=String(parseInt(year,10)-1);
var d=unitData(idx,year);
var pd=unitData(idx,prevYear);
var cur=computeStats(idx,year);
var prev=computeStats(idx,prevYear);
var rank=indicatorRank(idx,year);
@@ -568,17 +559,6 @@ function renderReport(){
});
});
/* 专项筛查(阳性率按实际受检人数计算) */
var screenHtml=''
+'<div class="screen-item"><div class="screen-item__name">防癌筛查 · Septin9</div><span class="screen-item__val">'+fmt(d.cancerPos)+'</span><span class="screen-item__unit">人阳性</span>'
+'<div class="screen-item__rate"><div>阳性率 '+pct(d.cancerPos,d.cancerScreened)+' · 已筛 '+fmt(d.cancerScreened)+' 人</div><div class="sub">较上年 '+trendSpan(d.cancerPos-pd.cancerPos,false,'down','人')+'</div></div></div>'
+'<div class="screen-item"><div class="screen-item__name">防癌筛查 · CA724</div><span class="screen-item__val">'+fmt(d.ca724Pos)+'</span><span class="screen-item__unit">人阳性</span>'
+'<div class="screen-item__rate"><div>阳性率 '+pct(d.ca724Pos,d.cancerScreened)+' · 已筛 '+fmt(d.cancerScreened)+' 人</div><div class="sub">较上年 '+trendSpan(d.ca724Pos-pd.ca724Pos,false,'down','人')+'</div></div></div>'
+'<div class="screen-item"><div class="screen-item__name">冠脉CTA</div><span class="screen-item__val">'+fmt(d.ctaAbn)+'</span><span class="screen-item__unit">人异常</span>'
+'<div class="screen-item__rate"><div>异常率 '+pct(d.ctaAbn,d.ctaScreened)+' · 已检 '+fmt(d.ctaScreened)+' 人</div><div class="sub">较上年 '+trendSpan(d.ctaAbn-pd.ctaAbn,false,'down','人')+'</div></div></div>'
+'<div class="screen-item"><div class="screen-item__name">胃肠镜</div><span class="screen-item__val">'+fmt(d.gastroAbn)+'</span><span class="screen-item__unit">人异常</span>'
+'<div class="screen-item__rate"><div>异常率 '+pct(d.gastroAbn,d.gastroScreened)+' · 已检 '+fmt(d.gastroScreened)+' 人</div><div class="sub">较上年 '+trendSpan(d.gastroAbn-pd.gastroAbn,false,'down','人')+'</div></div></div>';
/* 近三年趋势 */
var trendLegend='<div class="trend-legend"><span><i style="background:#1890FF"></i>受检率</span><span><i style="background:#FAAD14"></i>检出率</span></div>';
@@ -620,11 +600,7 @@ function renderReport(){
+'<div class="report-card__header"><div class="report-card__bar"></div><div class="report-card__title">疾病(病种)分布</div><div class="report-card__sub">按病种字典归类 · 附较上年</div></div>'
+'<div class="report-card__body"><table class="dist-table">'
+'<thead><tr><th style="width:24px;"></th><th>病种</th><th>人数</th><th>占比</th><th>较上年</th></tr></thead>'
+'<tbody>'+distRows+'</tbody></table></div></div>'
+'<div class="report-card">'
+'<div class="report-card__header"><div class="report-card__bar"></div><div class="report-card__title">专项筛查结果</div><div class="report-card__sub">阳性率按实际受检人数计算</div></div>'
+'<div class="report-card__body">'+screenHtml+'</div></div>';
+'<tbody>'+distRows+'</tbody></table></div></div>';
}
/* 导出群体报告(CSVExcel 兼容) */
@@ -673,13 +649,6 @@ function exportReport(){
dist.forEach(function(g){g.items.forEach(function(x){var pv=distPrevMap[x.name]||0;rows.push([g.cat+' · '+x.name,x.cnt,pct(x.cnt,d.examined),pv,x.cnt-pv]);});});
rows.push([]);
rows.push(['专项筛查结果','阳性/异常','率','已筛/已检','较上年']);
rows.push(['防癌筛查 Septin9',d.cancerPos,pct(d.cancerPos,d.cancerScreened),'已筛 '+d.cancerScreened,d.cancerPos-pd.cancerPos]);
rows.push(['防癌筛查 CA724',d.ca724Pos,pct(d.ca724Pos,d.cancerScreened),'已筛 '+d.cancerScreened,d.ca724Pos-pd.ca724Pos]);
rows.push(['冠脉CTA',d.ctaAbn,pct(d.ctaAbn,d.ctaScreened),'已检 '+d.ctaScreened,d.ctaAbn-pd.ctaAbn]);
rows.push(['胃肠镜',d.gastroAbn,pct(d.gastroAbn,d.gastroScreened),'已检 '+d.gastroScreened,d.gastroAbn-pd.gastroAbn]);
rows.push([]);
rows.push(['近三年趋势','受检率(%)','检出率(%)']);
years.forEach(function(y){var s=computeStats(idx,y);rows.push([y+'年',s.attendRate.toFixed(1),s.detectRate.toFixed(1)]);});
@@ -126,7 +126,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -142,7 +142,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item active">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -171,7 +171,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -181,7 +181,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -153,7 +153,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item active">大病人员信息</a>
@@ -168,7 +168,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -197,7 +197,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -207,7 +207,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -145,6 +145,7 @@
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
@@ -157,6 +158,8 @@
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -142,6 +142,7 @@
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
@@ -154,6 +155,8 @@
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -143,6 +143,7 @@
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
@@ -155,6 +156,8 @@
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -171,6 +171,7 @@
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
<a href="post-indicator-notify.html" class="sidebar-item">指标异常通知</a>
@@ -183,6 +184,8 @@
<div class="sidebar-subgroup__body">
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -125,7 +125,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -140,7 +140,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -169,7 +169,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -179,7 +179,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
+4 -4
View File
@@ -117,7 +117,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -132,7 +132,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item active">医院体检项目统计</a>
@@ -161,7 +161,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -171,7 +171,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -150,7 +150,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -165,7 +165,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -194,7 +194,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -204,7 +204,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -152,7 +152,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item active">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item active">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -168,7 +168,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -197,7 +197,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -207,7 +207,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -203,7 +203,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -218,7 +218,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -247,7 +247,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item active">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item active">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -257,7 +257,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -136,7 +136,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -151,7 +151,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -180,7 +180,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -190,7 +190,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康体检 - 结果动态综合统计 | 健康CQ原型</title>
<title>健康体检 - 项目结果综合统计 | 健康CQ原型</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;overflow:hidden;}
@@ -84,6 +84,92 @@
.col rect{transition:opacity .2s;}
.col:hover rect{opacity:0.85;}
.empty-hint{text-align:center;color:var(--text-placeholder);padding:40px 0;font-size:var(--font-sm);}
/* Tabs */
.tabs{display:flex;border-bottom:1px solid var(--border);margin-bottom:16px;flex-shrink:0;}
.tab{padding:10px 22px;font-size:var(--font-base);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;user-select:none;}
.tab:hover{color:var(--primary);}
.tab.active{color:var(--primary);font-weight:600;border-bottom-color:var(--primary);}
/* Tab 面板 */
.tab-panel{flex:1;min-height:0;display:flex;flex-direction:column;}
/* 列表工具条 */
.list-toolbar{margin-bottom:16px;flex-shrink:0;display:flex;gap:12px;}
.btn{height:32px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.btn-xs{height:26px;padding:0 12px;font-size:var(--font-xs);}
/* 级联下拉 */
.cascade-select{position:relative;}
.cascade-select__trigger{height:32px;min-width:200px;border:1px solid var(--border);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 10px;cursor:pointer;font-size:var(--font-sm);background:#fff;}
.cascade-select__trigger:hover{border-color:var(--primary);}
.cascade-select__trigger .cascade-select__value{color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:210px;flex:1;}
.cascade-select__trigger .cascade-select__value.placeholder{color:var(--text-placeholder);}
.cascade-select__trigger svg{flex-shrink:0;}
.cascade-select__dropdown{display:none;position:absolute;top:38px;left:0;width:460px;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);z-index:60;}
.cascade-select__dropdown.open{display:block;}
.cs-body{display:flex;height:220px;}
.cs-left{width:150px;flex-shrink:0;border-right:1px solid var(--border);overflow-y:auto;padding:6px 0;}
.cs-right{flex:1;overflow-y:auto;padding:6px 0;}
.cs-cat{padding:9px 14px;font-size:var(--font-sm);cursor:pointer;color:var(--text-primary);white-space:nowrap;}
.cs-cat:hover{background:#F5F7FA;}
.cs-cat.active{background:var(--primary-light);color:var(--primary);font-weight:600;}
.cs-item{display:flex;align-items:center;gap:8px;padding:7px 14px;font-size:var(--font-sm);cursor:pointer;color:var(--text-primary);}
.cs-item:hover{background:#F5F7FA;}
.cs-item input{accent-color:var(--primary);cursor:pointer;flex-shrink:0;}
.cs-footer{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--border);}
.cs-footer .cs-count{color:var(--text-secondary);font-size:var(--font-sm);flex:1;}
/* 表格 */
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden;}
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);flex-shrink:0;}
.table-card__title{font-size:var(--font-base);font-weight:600;}
.table-card__content{flex:1;overflow:auto;min-height:0;}
.table-card__content table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:10px 14px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);white-space:nowrap;position:sticky;top:0;z-index:2;}
tbody td{padding:10px 14px;font-size:var(--font-sm);color:var(--text-primary);border:1px solid var(--border);white-space:nowrap;}
tbody tr:hover td{background:#F8FBFF;}
.res-abn{color:var(--danger);font-weight:600;}
.res-ok{color:var(--success);}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}
.link:hover{color:#40a9ff;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
/* 分页 */
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:12px 16px;gap:8px;border-top:1px solid var(--border);flex-shrink:0;}
.pagination .page-info{font-size:var(--font-sm);color:var(--text-secondary);margin-right:auto;}
.page-btn{min-width:30px;height:30px;padding:0 6px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;color:var(--text-primary);}
.page-btn:hover:not(:disabled){color:var(--primary);border-color:var(--primary);}
.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:var(--text-placeholder);cursor:not-allowed;background:#F5F7FA;}
.page-ellipsis{color:var(--text-secondary);font-size:var(--font-sm);padding:0 2px;}
/* 轻提示 */
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-16px);background:#fff;box-shadow:var(--shadow-md);border-radius:var(--radius-sm);padding:10px 20px;font-size:var(--font-sm);color:var(--text-primary);opacity:0;pointer-events:none;transition:all .25s;z-index:1000;display:flex;align-items:center;gap:8px;}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
.toast svg{flex-shrink:0;}
/* 详情弹窗(体检项目结果,报告版式) */
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;align-items:center;justify-content:center;}
.modal-mask.open{display:flex;}
.modal{width:640px;max-width:92vw;max-height:85vh;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);display:flex;flex-direction:column;overflow:hidden;}
.modal--wide{width:860px;}
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);flex-shrink:0;}
.modal__title{font-size:var(--font-lg);font-weight:600;}
.modal__close{width:32px;height:32px;border-radius:50%;border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-secondary);font-size:18px;}
.modal__close:hover{background:#F5F5F5;color:var(--text-primary);}
.modal__body{flex:1;overflow-y:auto;padding:20px 24px;}
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:12px 24px;border-top:1px solid var(--border);flex-shrink:0;}
/* 报告版式 */
.cta-rpt{border:1px solid var(--border);border-radius:var(--radius-md);overflow:hidden;}
.cta-rpt__band{background:#001529;color:#fff;padding:14px 20px;font-size:var(--font-base);font-weight:600;display:flex;align-items:center;justify-content:space-between;}
.cta-rpt__meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px 20px;padding:14px 20px;border-bottom:1px dashed var(--border);font-size:var(--font-sm);}
.cta-rpt__meta b{color:var(--text-secondary);font-weight:400;}
.cta-rpt__sec{padding:14px 20px;border-bottom:1px solid var(--border);}
.cta-rpt__sec:last-child{border-bottom:none;}
.cta-rpt__sec-t{font-size:var(--font-sm);font-weight:600;color:var(--primary);margin-bottom:8px;}
.cta-rpt__sec table{width:100%;border-collapse:collapse;}
.cta-rpt__sec thead th{background:#FAFAFA;padding:8px 12px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);white-space:nowrap;position:static;}
.cta-rpt__sec tbody td{padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);border:1px solid var(--border);white-space:nowrap;}
.report-note{border-radius:var(--radius-sm);padding:12px 16px;font-size:var(--font-sm);line-height:1.9;}
.report-note--conclusion{border:1px solid #FFD591;background:#FFF7E6;color:#873800;}
.report-note--advice{border:1px solid #B7EB8F;background:#F6FFED;color:#389E0D;}
.result-abnormal{color:var(--danger);font-weight:600;}
</style>
</head>
<body>
@@ -121,7 +207,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -136,7 +222,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item active">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item active">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -165,7 +251,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -175,47 +261,223 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->
<main class="main">
<!-- 页头 -->
<div class="page-header">
<div class="page-title">结果动态综合统计</div>
<div class="page-title">项目结果综合统计</div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-row">
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" style="min-width:96px;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
<!-- Tabs -->
<div class="tabs">
<div class="tab active" onclick="switchTab('chart', this)">图表</div>
<div class="tab" onclick="switchTab('list', this)">列表</div>
</div>
<!-- 图表 Tab -->
<div class="tab-panel" id="tab-chart">
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-row">
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" style="min-width:96px;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div class="filter-item">
<label>单位:</label>
<select class="filter-select filter-select--item">
<option>请选择</option><option selected>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
</select>
</div>
</div>
<div class="filter-item">
<label>单位:</label>
<select class="filter-select filter-select--item">
<option>请选择</option><option selected>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
</select>
</div>
<!-- 2×2 图表 -->
<div class="chart-grid" id="chart-grid"></div>
</div>
<!-- 列表 Tab -->
<div class="tab-panel" id="tab-list" style="display:none;">
<!-- 列表筛选栏 -->
<div class="filter-bar">
<div class="filter-row">
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" style="min-width:96px;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div class="filter-item">
<label>体检项目:</label>
<div class="cascade-select">
<div class="cascade-select__trigger" onclick="toggleCascade(event)">
<span class="cascade-select__value placeholder" id="cascade-value">全部项目</span>
<svg width="10" height="6" viewBox="0 0 10 6"><path d="M0 0l5 6 5-6z" fill="#999"/></svg>
</div>
<div class="cascade-select__dropdown" id="cascade-dropdown" onclick="event.stopPropagation();">
<div class="cs-body">
<div class="cs-left" id="cs-category"></div>
<div class="cs-right" id="cs-items"></div>
</div>
<div class="cs-footer">
<span class="cs-count" id="cs-count">已选 0 项</span>
<span class="link" onclick="clearCascade()">清空</span>
<button class="btn btn-primary btn-xs" onclick="closeCascade()">确定</button>
</div>
</div>
</div>
</div>
<div class="filter-item">
<label>单位部门:</label>
<select class="filter-select filter-select--item" id="fUnit" onchange="applyListFilter()">
<option value="">全部</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option><option>公共事务中心</option>
</select>
</div>
<div class="filter-item">
<label>姓名:</label>
<input class="filter-input" type="text" id="fName" placeholder="请输入" oninput="applyListFilter()" style="min-width:110px;">
</div>
<div class="filter-item">
<label>员工编号:</label>
<input class="filter-input" type="text" id="fEmpNo" placeholder="请输入" oninput="applyListFilter()" style="min-width:130px;">
</div>
<div class="filter-item">
<label>性别:</label>
<select class="filter-select filter-select--item" id="fGender" onchange="applyListFilter()">
<option value="">全部</option><option></option><option></option>
</select>
</div>
<div class="filter-item">
<label>年龄:</label>
<select class="filter-select filter-select--item" id="fAge" onchange="applyListFilter()">
<option value="">全部</option><option>20-29岁</option><option>30-39岁</option><option>40-49岁</option><option>50-59岁</option><option>60岁及以上</option>
</select>
</div>
<div class="filter-item">
<label>岗位:</label>
<select class="filter-select filter-select--item" id="fPos" onchange="applyListFilter()">
<option value="">全部</option><option>采油工</option><option>集输工</option><option>技术员</option><option>管理岗</option><option>后勤岗</option><option>机关岗</option>
</select>
</div>
<div class="filter-item">
<label>是否异常:</label>
<select class="filter-select filter-select--item" id="fAbn" onchange="applyListFilter()">
<option value="">全部</option><option value="yes"></option><option value="no"></option>
</select>
</div>
</div>
</div>
<!-- 列表操作栏 -->
<div class="list-toolbar">
<button class="btn btn-primary" onclick="exportList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导出列表数据
</button>
</div>
<!-- 列表表格 -->
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">体检结果列表 <span class="tag tag-blue" id="list-total" style="margin-left:8px;"></span></div>
</div>
<div class="table-card__content">
<table>
<thead>
<tr>
<th style="width:56px;">序号</th>
<th>体检项目</th>
<th>是否异常</th>
<th>姓名</th>
<th>员工编号</th>
<th>性别</th>
<th>年龄</th>
<th>单位</th>
<th>部门</th>
<th style="width:90px;">详细信息</th>
</tr>
</thead>
<tbody id="list-tbody"></tbody>
</table>
</div>
<div class="pagination">
<span class="page-info" id="list-page-info"></span>
<button class="page-btn" id="list-prev" onclick="gotoPage(listPage-1)"></button>
<span id="list-page-btns"></span>
<button class="page-btn" id="list-next" onclick="gotoPage(listPage+1)"></button>
</div>
</div>
</div>
<!-- 2×2 图表 -->
<div class="chart-grid" id="chart-grid"></div>
</main>
<!-- 轻提示 -->
<div class="toast" id="toast">
<svg width="15" height="15" viewBox="0 0 15 15" fill="none"><circle cx="7.5" cy="7.5" r="6.5" fill="#52C41A"/><path d="M4.6 7.7l2 2 3.8-4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span id="toastText"></span>
</div>
<!-- 详细信息弹窗:体检项目结果 -->
<div class="modal-mask" id="detail-mask" onclick="if(event.target===this)closeDetail()">
<div class="modal modal--wide">
<div class="modal__header">
<div class="modal__title" id="detail-title">体检项目结果</div>
<button class="modal__close" onclick="closeDetail()"></button>
</div>
<div class="modal__body">
<div class="cta-rpt">
<div class="cta-rpt__band" id="detail-band">体检项目</div>
<div class="cta-rpt__meta" id="detail-meta"></div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">检查结果</div>
<table>
<thead><tr><th style="width:200px;">项目名称</th><th>结果</th><th>单位</th><th>参考值</th></tr></thead>
<tbody id="detail-body"></tbody>
</table>
</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">体检结论</div>
<div class="report-note" id="detail-conclusion"></div>
</div>
</div>
</div>
<div class="modal__footer">
<button class="btn btn-primary" onclick="closeDetail()">关闭</button>
</div>
</div>
</div>
<script>
/* 可统计的体检项目;base 为整体异常率基准,维度会在此基础上浮动 */
@@ -444,6 +706,288 @@ document.querySelectorAll('.year-picker>input').forEach(function(inp){
document.addEventListener('click',function(){
document.querySelectorAll('.year-picker__dropdown.open').forEach(function(d){d.classList.remove('open');});
});
/* ==================== 列表 Tab ==================== */
/* 部门 -> 单位 映射与数值年龄 */
var UNIT_BY_DEPT={
'采油作业一区':'第一采油厂','采油作业二区':'第一采油厂',
'集输作业一区':'第二采油厂','集输作业二区':'第二采油厂',
'机关':'第三采油厂','后勤保障':'公共事务中心'
};
var AGE_NUM={'20-29岁':[20,29],'30-39岁':[30,39],'40-49岁':[40,49],'50-59岁':[50,59],'60岁及以上':[60,75]};
function empAge(emp){var r=AGE_NUM[emp.age]||[30,39];return r[0]+(hashNum(emp.id+'age')%(r[1]-r[0]+1));}
/* 列表行:以体检项目为单位,一人多条 */
var LIST_ROWS=[];
(function buildListRows(){
EMP_POOL.forEach(function(emp,empIdx){
ITEMS.forEach(function(it){
LIST_ROWS.push({
idx:LIST_ROWS.length+1,
empKey:empIdx,
name:emp.name,empNo:emp.id,gender:emp.gender,age:empAge(emp),ageRange:emp.age,
unit:UNIT_BY_DEPT[emp.dept],dept:emp.dept,position:emp.position,
itemCode:it.code,itemName:it.name,abnormal:isAbnormal(emp,it)
});
});
});
})();
/* 体检项目级联结构(分类 -> 项目) */
var ITEM_CATEGORIES=[
{name:'检验类',items:[
{name:'血常规',code:'XM1001'},{name:'尿常规',code:'XM1002'},{name:'肝功能',code:'XM1003'},
{name:'肾功能',code:'XM1004'},{name:'血脂',code:'XM1005'},{name:'空腹血糖',code:'XM1006'},
{name:'肿瘤标志物',code:'XM1010'}
]},
{name:'影像类',items:[
{name:'心电图',code:'XM1007'},{name:'腹部彩超',code:'XM1008'},{name:'胸部正位片',code:'XM1009'},
{name:'骨密度',code:'XM1011'},{name:'甲状腺彩超',code:'XM1012'}
]}
];
var selectedCodes=[]; /* 多选的项目编码 */
var csActiveCat=0; /* 当前分类 */
function itemName(code){for(var i=0;i<ITEMS.length;i++){if(ITEMS[i].code===code)return ITEMS[i].name;}return code;}
function renderCascade(){
var left=ITEM_CATEGORIES.map(function(cat,ci){
return '<div class="cs-cat'+(ci===csActiveCat?' active':'')+'" onclick="selectCat('+ci+')">'+esc(cat.name)+'</div>';
}).join('');
document.getElementById('cs-category').innerHTML=left;
var right=ITEM_CATEGORIES[csActiveCat].items.map(function(it){
var checked=selectedCodes.indexOf(it.code)>=0?' checked':'';
return '<label class="cs-item"><input type="checkbox"'+checked+' onchange="toggleItem(\''+it.code+'\')"><span>'+esc(it.name)+'</span></label>';
}).join('');
document.getElementById('cs-items').innerHTML=right;
updateCascadeValue();
}
function selectCat(ci){csActiveCat=ci;renderCascade();}
function toggleItem(code){
var i=selectedCodes.indexOf(code);
if(i>=0)selectedCodes.splice(i,1);else selectedCodes.push(code);
updateCascadeValue();
applyListFilter();
}
function clearCascade(){selectedCodes=[];renderCascade();applyListFilter();}
function closeCascade(){document.getElementById('cascade-dropdown').classList.remove('open');}
function updateCascadeValue(){
var val=document.getElementById('cascade-value');
var cnt=selectedCodes.length;
document.getElementById('cs-count').textContent='已选 '+cnt+' 项';
if(cnt===0){val.textContent='全部项目';val.classList.add('placeholder');}
else{val.classList.remove('placeholder');val.textContent=selectedCodes.map(itemName).join('、');}
}
function toggleCascade(e){
if(e)e.stopPropagation();
document.getElementById('cascade-dropdown').classList.toggle('open');
}
document.addEventListener('click',function(){closeCascade();});
/* 按筛选条件取列表行(以体检项目为单位) */
function getListRows(){
var unit=document.getElementById('fUnit').value;
var name=document.getElementById('fName').value.trim();
var empNo=document.getElementById('fEmpNo').value.trim();
var gender=document.getElementById('fGender').value;
var age=document.getElementById('fAge').value;
var pos=document.getElementById('fPos').value;
var abn=document.getElementById('fAbn').value;
return LIST_ROWS.filter(function(r){
if(unit&&r.unit!==unit)return false;
if(name&&r.name.indexOf(name)<0)return false;
if(empNo&&r.empNo.indexOf(empNo)<0)return false;
if(gender&&r.gender!==gender)return false;
if(age&&r.ageRange!==age)return false;
if(pos&&r.position!==pos)return false;
if(selectedCodes.length&&selectedCodes.indexOf(r.itemCode)<0)return false;
if(abn==='yes'&&!r.abnormal)return false;
if(abn==='no'&&r.abnormal)return false;
return true;
});
}
/* 分页状态 */
var listPage=1,listPageSize=20;
/* 页码窗口:首页/末页 + 当前页前后各2页 */
function pageList(current,total){
if(total<=7){var arr=[];for(var i=1;i<=total;i++)arr.push(i);return arr;}
var set={};
[1,2,total-1,total].forEach(function(p){if(p>=1&&p<=total)set[p]=1;});
for(var p=current-2;p<=current+2;p++){if(p>=1&&p<=total)set[p]=1;}
var arr=Object.keys(set).map(Number).sort(function(a,b){return a-b;});
var out=[],prev=0;
arr.forEach(function(p){if(prev&&p-prev>1)out.push('...');out.push(p);prev=p;});
return out;
}
function renderPagination(total,totalPages){
document.getElementById('list-page-info').textContent='共 '+total+' 条,每页 '+listPageSize+' 条';
var html='';
pageList(listPage,totalPages).forEach(function(p){
if(p==='...'){html+='<span class="page-ellipsis"></span>';}
else{html+='<button class="page-btn'+(p===listPage?' active':'')+'" onclick="gotoPage('+p+')">'+p+'</button>';}
});
document.getElementById('list-page-btns').innerHTML=html;
document.getElementById('list-prev').disabled=listPage<=1;
document.getElementById('list-next').disabled=listPage>=totalPages;
}
function gotoPage(p){
var totalPages=Math.max(1,Math.ceil(getListRows().length/listPageSize));
if(p<1)p=1;
if(p>totalPages)p=totalPages;
listPage=p;
renderListTable();
}
/* 筛选变化时回到第一页 */
function applyListFilter(){listPage=1;renderListTable();}
function renderListTable(){
var all=getListRows();
var total=all.length;
var totalPages=Math.max(1,Math.ceil(total/listPageSize));
if(listPage>totalPages)listPage=totalPages;
var start=(listPage-1)*listPageSize;
var rows=all.slice(start,start+listPageSize);
var html=rows.map(function(r,ri){
return '<tr>'+
'<td>'+(start+ri+1)+'</td>'+
'<td>'+esc(r.itemName)+'</td>'+
'<td>'+(r.abnormal?'<span class="res-abn"></span>':'<span class="res-ok"></span>')+'</td>'+
'<td>'+esc(r.name)+'</td>'+
'<td>'+esc(r.empNo)+'</td>'+
'<td>'+esc(r.gender)+'</td>'+
'<td>'+r.age+'</td>'+
'<td>'+esc(r.unit)+'</td>'+
'<td>'+esc(r.dept)+'</td>'+
'<td><span class="link" onclick="openDetail('+r.idx+')">查看</span></td>'+
'</tr>';
}).join('');
document.getElementById('list-tbody').innerHTML=html||'<tr><td colspan="10" style="text-align:center;color:var(--text-placeholder);padding:40px 0;">暂无数据</td></tr>';
document.getElementById('list-total').textContent='共 '+total+' 条';
renderPagination(total,totalPages);
}
/* 导出 CSV */
function exportList(){
var rows=getListRows();
if(!rows.length){showToast('暂无数据可导出');return;}
var head=['序号','姓名','员工编号','性别','年龄','单位','部门','体检项目','是否异常'];
var lines=[head.join(',')];
rows.forEach(function(r,ri){
lines.push([ri+1,r.name,r.empNo,r.gender,r.age,r.unit,r.dept,r.itemName,r.abnormal?'是':'否'].join(','));
});
var csv='\ufeff'+lines.join('\r\n');
var blob=new Blob([csv],{type:'text/csv;charset=utf-8;'});
var a=document.createElement('a');
a.href=URL.createObjectURL(blob);
a.download='项目结果综合统计列表.csv';
document.body.appendChild(a);a.click();document.body.removeChild(a);
URL.revokeObjectURL(a.href);
showToast('导出成功,共 '+rows.length+' 条');
}
/* 各体检项目的检查明细(结果/单位/参考值,ok 为正常值,bad 为异常值) */
var ITEM_DETAIL={
'XM1001':{subs:[
{name:'白细胞计数',unit:'×10⁹/L',ref:'3.5~9.5',ok:'6.2',bad:'10.8 ↑'},
{name:'红细胞计数',unit:'×10¹²/L',ref:'4.3~5.8',ok:'4.8',bad:'3.9 ↓'},
{name:'血红蛋白',unit:'g/L',ref:'130~175',ok:'145',bad:'112 ↓'},
{name:'血小板计数',unit:'×10⁹/L',ref:'125~350',ok:'220',bad:'96 ↓'}
]},
'XM1002':{subs:[
{name:'尿糖',unit:'-',ref:'阴性',ok:'阴性',bad:'阳性 +'},
{name:'尿蛋白',unit:'-',ref:'阴性',ok:'阴性',bad:'±'},
{name:'尿潜血',unit:'-',ref:'阴性',ok:'阴性',bad:'阳性 +'},
{name:'尿白细胞',unit:'-',ref:'阴性',ok:'阴性',bad:'阳性 +'}
]},
'XM1003':{subs:[
{name:'丙氨酸氨基转移酶(ALT)',unit:'U/L',ref:'9~50',ok:'25',bad:'68 ↑'},
{name:'天门冬氨酸氨基转移酶(AST)',unit:'U/L',ref:'15~40',ok:'22',bad:'52 ↑'},
{name:'总胆红素',unit:'μmol/L',ref:'3.4~20.5',ok:'12.5',bad:'24.8 ↑'},
{name:'白蛋白',unit:'g/L',ref:'40~55',ok:'45.0',bad:'38.6 ↓'}
]},
'XM1004':{subs:[
{name:'肌酐',unit:'μmol/L',ref:'57~97',ok:'78',bad:'108 ↑'},
{name:'尿素',unit:'mmol/L',ref:'3.1~8.8',ok:'5.6',bad:'10.2 ↑'},
{name:'尿酸',unit:'μmol/L',ref:'208~428',ok:'320',bad:'465 ↑'}
]},
'XM1005':{subs:[
{name:'总胆固醇',unit:'mmol/L',ref:'<5.2',ok:'4.6',bad:'5.9 '},
{name:'甘油三酯',unit:'mmol/L',ref:'<1.7',ok:'1.4',bad:'2.2 '},
{name:'低密度脂蛋白胆固醇',unit:'mmol/L',ref:'<3.4',ok:'2.8',bad:'3.9 '},
{name:'高密度脂蛋白胆固醇',unit:'mmol/L',ref:'>1.04',ok:'1.25',bad:'0.92 ↓'}
]},
'XM1006':{subs:[
{name:'空腹血糖',unit:'mmol/L',ref:'3.9~6.1',ok:'5.3',bad:'7.2 ↑'},
{name:'糖化血红蛋白',unit:'%',ref:'4.0~6.0',ok:'5.4',bad:'6.8 ↑'}
]},
'XM1007':{subs:[
{name:'心率',unit:'次/分',ref:'60~100',ok:'72',bad:'58 ↓'},
{name:'心律',unit:'-',ref:'节律整齐',ok:'齐',bad:'偶发早搏'},
{name:'ST-T改变',unit:'-',ref:'无异常',ok:'未见异常',bad:'T波低平'}
]},
'XM1008':{subs:[
{name:'肝脏',unit:'-',ref:'未见异常',ok:'未见异常',bad:'脂肪肝'},
{name:'胆囊',unit:'-',ref:'未见异常',ok:'未见异常',bad:'胆囊息肉'},
{name:'胰腺',unit:'-',ref:'未见异常',ok:'未见异常',bad:'未见异常'},
{name:'脾脏',unit:'-',ref:'未见异常',ok:'未见异常',bad:'未见异常'}
]},
'XM1009':{subs:[
{name:'双肺纹理',unit:'-',ref:'未见异常',ok:'未见异常',bad:'双肺纹理增多'},
{name:'心脏形态',unit:'-',ref:'未见异常',ok:'未见异常',bad:'未见异常'},
{name:'胸廓',unit:'-',ref:'未见异常',ok:'未见异常',bad:'未见异常'}
]},
'XM1010':{subs:[
{name:'癌胚抗原(CEA)',unit:'ng/mL',ref:'<5.0',ok:'1.8',bad:'8.6 '},
{name:'甲胎蛋白(AFP)',unit:'ng/mL',ref:'<7.0',ok:'2.5',bad:'12.3 '},
{name:'糖类抗原19-9(CA19-9)',unit:'U/mL',ref:'<37',ok:'15.2',bad:'46.8 '}
]},
'XM1011':{subs:[
{name:'骨密度 T值',unit:'SD',ref:'-1~1',ok:'0.2',bad:'-2.6 ↓'},
{name:'骨密度 Z值',unit:'SD',ref:'-2~2',ok:'0.1',bad:'-1.2'}
]},
'XM1012':{subs:[
{name:'甲状腺大小形态',unit:'-',ref:'未见异常',ok:'未见异常',bad:'未见异常'},
{name:'甲状腺回声',unit:'-',ref:'未见异常',ok:'未见异常',bad:'回声欠均'},
{name:'结节',unit:'-',ref:'无',ok:'无',bad:'右叶结节 0.5×0.4cm'}
]}
};
/* 详情弹窗:展示该体检项目的检查结果 */
function openDetail(idx){
var r=LIST_ROWS[idx-1];
if(!r)return;
var detail=ITEM_DETAIL[r.itemCode]||{subs:[]};
document.getElementById('detail-title').textContent=r.name+' · '+r.itemName+'结果';
document.getElementById('detail-band').textContent=r.itemName;
document.getElementById('detail-meta').innerHTML=
'<span><b>姓名:</b>'+esc(r.name)+'</span><span><b>性别:</b>'+esc(r.gender)+'</span><span><b>年龄:</b>'+r.age+'岁</span>'+
'<span><b>员工编号:</b>'+esc(r.empNo)+'</span><span><b>单位:</b>'+esc(r.unit)+'</span><span><b>部门:</b>'+esc(r.dept)+'</span>';
var badIdx=(r.abnormal&&detail.subs.length)?(hashNum(r.empNo+r.itemCode)%detail.subs.length):-1;
document.getElementById('detail-body').innerHTML=detail.subs.map(function(s,si){
var val=(si===badIdx)?s.bad:s.ok;
var abn=(si===badIdx);
return '<tr><td>'+esc(s.name)+'</td><td class="'+(abn?'result-abnormal':'')+'">'+esc(val)+'</td><td>'+esc(s.unit)+'</td><td>'+esc(s.ref)+'</td></tr>';
}).join('');
var concl=document.getElementById('detail-conclusion');
if(r.abnormal){
concl.className='report-note report-note--conclusion';
concl.textContent='该员工「'+r.itemName+'」存在异常指标,建议进一步复查或专科随访。';
}else{
concl.className='report-note report-note--advice';
concl.textContent='该员工「'+r.itemName+'」各项指标均在参考范围内,未见明显异常。';
}
document.getElementById('detail-mask').classList.add('open');
}
function closeDetail(){
document.getElementById('detail-mask').classList.remove('open');
}
/* Tab 切换 */
function switchTab(tab,el){
document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active');});
el.classList.add('active');
document.getElementById('tab-chart').style.display=tab==='chart'?'flex':'none';
document.getElementById('tab-list').style.display=tab==='list'?'flex':'none';
}
/* 轻提示 */
var toastTimer=null;
function showToast(msg){
document.getElementById('toastText').textContent=msg;
var t=document.getElementById('toast');
t.classList.add('show');clearTimeout(toastTimer);
toastTimer=setTimeout(function(){t.classList.remove('show');},2400);
}
renderCascade();
renderListTable();
/* 初始化:生成四个面板并各自默认项目 + 维度渲染 */
(function init(){
buildPanels();
@@ -126,7 +126,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -142,7 +142,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -171,7 +171,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -181,7 +181,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -145,7 +145,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -161,7 +161,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item active">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -190,7 +190,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -200,7 +200,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -156,7 +156,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -172,7 +172,7 @@
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -201,7 +201,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -211,7 +211,7 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
@@ -137,7 +137,7 @@
常规体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-employee.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-employee.html" class="sidebar-item">常规体检报告</a>
<a href="post-group-report.html" class="sidebar-item">单位群体报告</a>
<a href="post-employee-master.html" class="sidebar-item">员工体检大表</a>
<a href="post-illness-personnel.html" class="sidebar-item">大病人员信息</a>
@@ -152,7 +152,7 @@
<a href="post-unit-count.html" class="sidebar-item">单位体检人数统计</a>
<a href="post-unit-cost-stat.html" class="sidebar-item">单位体检费用统计</a>
<a href="post-unit-item-stat.html" class="sidebar-item active">单位体检项目统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">结果动态综合统计</a>
<a href="post-result-comprehensive-stat.html" class="sidebar-item">项目结果综合统计</a>
<a href="post-hospital-count.html" class="sidebar-item">医院体检人数统计</a>
<a href="post-doctor-count.html" class="sidebar-item">医生检查人数统计</a>
<a href="post-item-stat.html" class="sidebar-item">医院体检项目统计</a>
@@ -181,7 +181,7 @@
胃肠镜体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-report-gastro.html" class="sidebar-item">员工体检报告</a>
<a href="post-report-gastro.html" class="sidebar-item">胃肠镜体检报告</a>
<a href="post-gastro-stat.html" class="sidebar-item">胃肠镜体检统计</a>
</div>
</div>
@@ -191,10 +191,22 @@
冠脉CTA体检
</div>
<div class="sidebar-subgroup__body">
<a href="post-cta-report.html" class="sidebar-item">员工体检报告</a>
<a href="post-cta-report.html" class="sidebar-item">冠脉CTA体检报告</a>
<a href="post-cta-stat.html" class="sidebar-item">冠脉CTA体检统计</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 12 12"><path d="M4 2l4 4-4 4" fill="none" stroke-width="1.5" stroke-linecap="round"/></svg>
体检影像管理
</div>
<div class="sidebar-subgroup__body">
<a href="post-imc-user.html" class="sidebar-item">用户体检信息</a>
<a href="post-imc-class.html" class="sidebar-item">用户影像文件分类</a>
<a href="post-imc-info.html" class="sidebar-item">用户影像文件详细</a>
<a href="post-imc-log.html" class="sidebar-item">文件处理日志</a>
</div>
</div>
</aside>
<!-- 主内容 -->