Compare commits
7
Commits
4767c24d3d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cacb3e01d5 | ||
|
|
ccc24d787c | ||
|
|
70cbe278c5 | ||
|
|
143803a73f | ||
|
|
5e4eed60b9 | ||
|
|
2fc599fabe | ||
|
|
80b0c78bdd |
+5
-4
@@ -238,10 +238,10 @@ const SITE_MAP = [
|
||||
{ file:'pre-cta-plan-mgmt.html', label:'冠脉CTA单位体检计划管理' },
|
||||
{ file:'pre-special-cta.html', label:'冠脉CTA体检人员' },
|
||||
{ file:'pre-special-hospital.html', label:'指定员工体检医院' },
|
||||
{ file:'mid-appointment-track.html', label:'体检预约' },
|
||||
// { file:'mid-appointment-track.html', label:'体检预约' },
|
||||
{ file:'mid-appointment-track2.html', label:'体检预约跟踪' },
|
||||
{ file:'mid-selected-items.html', label:'已选项目' },
|
||||
{ file:'mid-participation.html', label:'已检员工' },
|
||||
// { file:'mid-selected-items.html', label:'已选项目' },
|
||||
// { file:'mid-participation.html', label:'已检员工' },
|
||||
{ file:'mid-unit-appointment.html', label:'单位预约人数统计' },
|
||||
{ file:'mid-hospital-realtime.html', label:'医院实时预约人数' },
|
||||
{ file:'mid-doctor-realtime.html', label:'医生实时检查情况' },
|
||||
@@ -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:'医院体检项目统计' },
|
||||
@@ -386,6 +386,7 @@ const SITE_MAP = [
|
||||
{ file:'din-place-goal.html', label:'场所目标考核' },
|
||||
{ file:'prod-recipe-plan.html', label:'食谱排期' },
|
||||
{ file:'prod-clean-order.html', label:'净菜下单' },
|
||||
{ file:'prod-clean-order-history.html', label:'净菜下单生成历史' },
|
||||
{ file:'prod-proc-combo.html', label:'配菜组合' },
|
||||
{ file:'prod-proc-cook.html', label:'烹饪加工' },
|
||||
{ file:'prod-proc-serve.html', label:'出餐管理' },
|
||||
|
||||
@@ -46,14 +46,26 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.filter-input{height:36px;min-width:110px;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);}
|
||||
.filter-date{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);color:var(--text-primary);outline:none;}
|
||||
/* 单位部门级联筛选 */
|
||||
.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);}
|
||||
.btn-link{background:none;color:var(--primary);padding:0 4px;height:auto;}.btn-link:hover{color:#40a9ff;}
|
||||
.tab-bar{display:flex;gap:4px;margin-top:16px;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);padding:6px 16px 0;}
|
||||
.tab-item{padding:10px 18px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;}
|
||||
.tab-bar{display:flex;gap:4px;margin:0 0 16px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:6px 16px;}
|
||||
.tab-item{padding:10px 18px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;}
|
||||
.tab-item.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.table-card{background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;margin-top:16px;}
|
||||
.table-card__head{display:flex;align-items:center;justify-content:flex-start;padding:12px 16px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-sm);font-weight:600;}
|
||||
.table-card__content{overflow-x:auto;-webkit-overflow-scrolling:touch;}
|
||||
@@ -133,35 +145,65 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<!-- 主内容 -->
|
||||
<main class="main">
|
||||
|
||||
<!-- [交互] TAB:设备分配记录 / 床位分配记录 -->
|
||||
<div class="tab-bar">
|
||||
<div class="tab-item active" id="tabDev" onclick="switchTab('dev')">设备分配记录</div>
|
||||
<div class="tab-item" id="tabEmp" onclick="switchTab('emp')">床位分配记录</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 筛选栏:字段组随 TAB 切换显隐 -->
|
||||
<div class="filter-bar">
|
||||
<!-- 设备分配记录筛选组 -->
|
||||
<div id="fgrpDev" style="display:contents;">
|
||||
<div class="filter-item">
|
||||
<label>设备所属单位</label>
|
||||
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()" style="min-width:120px;"><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>
|
||||
<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="filterDevCode" placeholder="如:HC-" oninput="renderAll()">
|
||||
<input class="filter-input" id="filterDevCode" placeholder="请输入" oninput="renderAll()">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>分配形式</label>
|
||||
<label>分配类型</label>
|
||||
<select class="filter-select" id="filterType" onchange="renderAll()" style="min-width:100px;">
|
||||
<option value="">全部</option><option value="1">宿舍</option><option value="2">床位</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>宿舍名称</label>
|
||||
<input class="filter-input" id="filterHostel" placeholder="如:101室" oninput="renderAll()">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 床位分配记录筛选组 -->
|
||||
<div id="fgrpEmp" style="display:none;">
|
||||
<div class="filter-item">
|
||||
<label>所属单位部门</label>
|
||||
<div class="org-wrap" id="empOrgWrap">
|
||||
<div class="org-trigger" id="empOrgTrigger" onclick="toggleEmpOrg()">全部单位/部门</div>
|
||||
<div class="org-panel" id="empOrgPanel">
|
||||
<div class="org-col" id="empUnitCol">
|
||||
<div class="org-col__title">单位</div>
|
||||
<div class="org-item active" onclick="selectEmpUnit(this,'')">全部单位/部门</div>
|
||||
</div>
|
||||
<div class="org-col" id="empDeptCol">
|
||||
<div class="org-col__title">部门</div>
|
||||
<div class="org-item active" onclick="selectEmpDept(this,'')">全部部门</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>所属站点</label>
|
||||
<select class="filter-select" id="filterEStation" onchange="renderAll()" style="min-width:140px;"><option value="">全部站点</option></select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>所属楼栋</label>
|
||||
<select class="filter-select" id="filterEBuilding" onchange="onFilterEBuilding()" style="min-width:180px;"><option value="">全部楼栋</option></select>
|
||||
@@ -170,6 +212,10 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<label>所属宿舍</label>
|
||||
<select class="filter-select" id="filterEDorm" onchange="renderAll()" style="min-width:110px;"><option value="">全部宿舍</option></select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>床位编号</label>
|
||||
<input class="filter-input" id="filterEBed" placeholder="请输入床位编号" oninput="renderAll()">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>员工姓名</label>
|
||||
<input class="filter-input" id="filterEName" placeholder="请输入员工姓名" oninput="renderAll()">
|
||||
@@ -179,11 +225,7 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<input class="filter-input" id="filterENo" placeholder="请输入员工编号" oninput="renderAll()">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 公共筛选:床位名称 + 分配日期 -->
|
||||
<div class="filter-item">
|
||||
<label>床位名称</label>
|
||||
<input class="filter-input" id="filterBed" placeholder="如:101-1" oninput="renderAll()" style="min-width:100px;">
|
||||
</div>
|
||||
<!-- 公共筛选:分配日期 -->
|
||||
<div class="filter-item">
|
||||
<label>分配日期</label>
|
||||
<input class="filter-date" type="date" id="filterFrom" value="2026-01-01" onchange="renderAll()">
|
||||
@@ -197,12 +239,6 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- [交互] TAB:设备分配记录 / 床位分配记录 -->
|
||||
<div class="tab-bar">
|
||||
<div class="tab-item active" id="tabDev" onclick="switchTab('dev')">设备分配记录</div>
|
||||
<div class="tab-item" id="tabEmp" onclick="switchTab('emp')">床位分配记录</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<div class="table-card__head">
|
||||
<div style="display:flex;gap:8px;">
|
||||
@@ -233,21 +269,31 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
|
||||
<script>
|
||||
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
|
||||
var depStations={'樊学作业区':['樊学站点基地'],'麻子山作业区':['麻子山站点'],'桐园作业区':['桐园站点基地'],'姬圪台作业区':['姬圪台站点'],'西峰作业区':['西峰基地'],'靖边气田':['靖边气田站点']};
|
||||
function stationOf(r){return (depStations[r.depart]||[])[0]||'';}
|
||||
/* 楼栋→站点/单位/部门映射,与「床位信息」页口径一致 */
|
||||
var buildingMeta={
|
||||
'樊学倒班宿舍1号楼':{station:'樊学站点基地',unit:'采油一厂',depart:'樊学作业区'},
|
||||
'麻子山倒班宿舍楼':{station:'麻子山站点',unit:'采油一厂',depart:'麻子山作业区'},
|
||||
'桐园倒班宿舍1号楼':{station:'桐园站点基地',unit:'采油二厂',depart:'桐园作业区'},
|
||||
'西峰基地综合宿舍楼':{station:'西峰基地',unit:'第四采油厂',depart:'西峰作业区'},
|
||||
'靖边气田倒班宿舍楼':{station:'靖边气田站点',unit:'采气一厂',depart:'靖边气田'}
|
||||
};
|
||||
var curTab='dev';
|
||||
/* 设备分配记录:含换绑历史(解除日期非空即历史) */
|
||||
var devRecords=[
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-01',type:2,space:'樊学倒班宿舍1号楼 · 101-1',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-02',type:2,space:'樊学倒班宿舍1号楼 · 101-2',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',type:2,space:'樊学倒班宿舍1号楼 · 102-1',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-03',type:2,space:'樊学倒班宿舍1号楼 · 201-1',bind:'2026-04-02',unbind:'2026-04-08'},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'HC-FX-04',type:1,space:'樊学倒班宿舍1号楼 · 101室',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'麻子山作业区',code:'HC-MZ-01',type:1,space:'麻子山倒班宿舍楼 · 101室',bind:'2026-03-19',unbind:''},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-01',type:2,space:'桐园倒班宿舍1号楼 · 301-1',bind:'2026-04-10',unbind:''},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'HC-TY-02',type:1,space:'桐园倒班宿舍1号楼 · 201室',bind:'2026-04-10',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-01',type:2,space:'西峰基地综合宿舍楼 · 301-1',bind:'2026-05-12',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-02',type:2,space:'西峰基地综合宿舍楼 · 302-1',bind:'2026-05-12',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'HC-XF-03',type:1,space:'西峰基地综合宿舍楼 · 402室',bind:'2026-05-12',unbind:''},
|
||||
{unit:'采气一厂',depart:'靖边气田',code:'HC-JB-01',type:2,space:'靖边气田倒班宿舍楼 · 401-1',bind:'2026-06-24',unbind:''}
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10001',type:2,space:'樊学倒班宿舍1号楼 · 101-1',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10002',type:2,space:'樊学倒班宿舍1号楼 · 101-2',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10003',type:2,space:'樊学倒班宿舍1号楼 · 102-1',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10003',type:2,space:'樊学倒班宿舍1号楼 · 201-1',bind:'2026-04-02',unbind:'2026-04-08'},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10004',type:1,space:'樊学倒班宿舍1号楼 · 101室',bind:'2026-03-05',unbind:''},
|
||||
{unit:'采油一厂',depart:'麻子山作业区',code:'10005',type:1,space:'麻子山倒班宿舍楼 · 101室',bind:'2026-03-19',unbind:''},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'10006',type:2,space:'桐园倒班宿舍1号楼 · 301-1',bind:'2026-04-10',unbind:''},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'10007',type:1,space:'桐园倒班宿舍1号楼 · 201室',bind:'2026-04-10',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10008',type:2,space:'西峰基地综合宿舍楼 · 301-1',bind:'2026-05-12',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10009',type:2,space:'西峰基地综合宿舍楼 · 302-1',bind:'2026-05-12',unbind:''},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10010',type:1,space:'西峰基地综合宿舍楼 · 402室',bind:'2026-05-12',unbind:''},
|
||||
{unit:'采气一厂',depart:'靖边气田',code:'10011',type:2,space:'靖边气田倒班宿舍楼 · 401-1',bind:'2026-06-24',unbind:''}
|
||||
];
|
||||
/* 床位分配记录:当前生效+历史换宿/退住 */
|
||||
var empRecords=[
|
||||
@@ -268,17 +314,71 @@ var empRecords=[
|
||||
{bed:'201-2',dorm:'201室',building:'桐园倒班宿舍1号楼',name:'赵万福',no:'CQ033096',org:'采油二厂',depart:'桐园作业区',bind:'2026-03-01',unbind:'2026-04-09'}
|
||||
];
|
||||
/* 列结构与老系统一致:无「当前状态」列 */
|
||||
function headDev(){return '<tr><th style="width:56px;">序号</th><th>设备所属单位</th><th>设备所属部门</th><th style="min-width:100px;">设备编号</th><th>分配形式</th><th style="min-width:230px;">宿舍/床位名称</th><th>分配日期</th><th>解除日期</th></tr>';}
|
||||
function headEmp(){return '<tr><th style="width:56px;">序号</th><th style="min-width:90px;">床位编号</th><th>所属宿舍</th><th style="min-width:180px;">所属楼栋</th><th>分配员工</th><th>员工编号</th><th style="min-width:120px;">员工单位</th><th style="min-width:150px;">员工部门</th><th>分配日期</th><th>解除日期</th></tr>';}
|
||||
function headDev(){return '<tr><th style="width:56px;">序号</th><th style="min-width:100px;">设备编号</th><th style="min-width:160px;">所属单位部门</th><th>分配类型</th><th style="min-width:230px;">宿舍/床位信息</th><th style="min-width:120px;">所属站点</th><th>分配日期</th><th>解绑日期</th></tr>';}
|
||||
function headEmp(){return '<tr><th style="width:56px;">序号</th><th style="min-width:90px;">床位编号</th><th>所属宿舍</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:120px;">员工单位</th><th style="min-width:150px;">员工部门</th><th>分配日期</th><th>解除日期</th></tr>';}
|
||||
function inRange(d){var f=document.getElementById('filterFrom').value,t=document.getElementById('filterTo').value;return (!f||d>=f)&&(!t||d<=t);}
|
||||
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('');
|
||||
/* [交互] 单位部门级联筛选 */
|
||||
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='';
|
||||
renderAll();
|
||||
}
|
||||
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||'全部部门');
|
||||
renderAll();
|
||||
}
|
||||
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>';
|
||||
}
|
||||
/* [交互] 床位分配记录:所属单位部门级联筛选 */
|
||||
var selEmpUnit='',selEmpDept='';
|
||||
function toggleEmpOrg(){
|
||||
document.getElementById('empOrgPanel').classList.toggle('open');
|
||||
document.getElementById('empOrgTrigger').classList.toggle('open');
|
||||
}
|
||||
function selectEmpUnit(elem,unitName){
|
||||
document.querySelectorAll('#empUnitCol .org-item').forEach(function(i){i.classList.remove('active');});
|
||||
elem.classList.add('active');
|
||||
selEmpUnit=unitName;
|
||||
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectEmpDept(this,\'\')">全部部门</div>';
|
||||
(unitDeparts[unitName]||[]).forEach(function(d){html+='<div class="org-item" onclick="selectEmpDept(this,\''+d+'\')">'+d+'</div>';});
|
||||
document.getElementById('empDeptCol').innerHTML=html;
|
||||
selEmpDept='';
|
||||
renderAll();
|
||||
}
|
||||
function selectEmpDept(elem,deptName){
|
||||
document.querySelectorAll('#empDeptCol .org-item').forEach(function(i){i.classList.remove('active');});
|
||||
elem.classList.add('active');
|
||||
selEmpDept=deptName;
|
||||
document.getElementById('empOrgTrigger').textContent=(selEmpUnit||'全部单位')+' / '+(selEmpDept||'全部部门');
|
||||
renderAll();
|
||||
}
|
||||
function resetEmpOrg(){
|
||||
selEmpUnit='';selEmpDept='';
|
||||
document.getElementById('empOrgTrigger').textContent='全部单位/部门';
|
||||
document.getElementById('empOrgPanel').classList.remove('open');
|
||||
document.getElementById('empOrgTrigger').classList.remove('open');
|
||||
document.querySelectorAll('#empUnitCol .org-item').forEach(function(i,idx){i.classList.toggle('active',idx===0);});
|
||||
document.getElementById('empDeptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectEmpDept(this,\'\')">全部部门</div>';
|
||||
}
|
||||
var buildings=[];empRecords.forEach(function(r){if(buildings.indexOf(r.building)<0)buildings.push(r.building);});
|
||||
function onFilterEBuilding(){
|
||||
var b=document.getElementById('filterEBuilding').value;
|
||||
@@ -289,31 +389,34 @@ function onFilterEBuilding(){
|
||||
function renderAll(){
|
||||
var rows='';
|
||||
if(curTab==='dev'){
|
||||
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepart').value,kc=document.getElementById('filterDevCode').value.trim().toUpperCase();
|
||||
var tp=document.getElementById('filterType').value,hs=document.getElementById('filterHostel').value.trim(),bd=document.getElementById('filterBed').value.trim();
|
||||
var kc=document.getElementById('filterDevCode').value.trim().toUpperCase();
|
||||
var tp=document.getElementById('filterType').value;
|
||||
var list=devRecords.filter(function(r){
|
||||
return (!u||r.unit===u)&&(!d||r.depart===d)&&(!kc||r.code.toUpperCase().indexOf(kc)>=0)
|
||||
&&(!tp||String(r.type)===tp)&&(!hs||r.space.indexOf(hs)>=0)&&(!bd||r.space.indexOf(bd)>=0)&&inRange(r.bind);
|
||||
return (!selUnit||r.unit===selUnit)&&(!selDept||r.depart===selDept)&&(!kc||r.code.toUpperCase().indexOf(kc)>=0)
|
||||
&&(!tp||String(r.type)===tp)&&inRange(r.bind);
|
||||
});
|
||||
rows=list.map(function(r,i){
|
||||
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 style="font-family:monospace;font-weight:500;">'+r.code+'</td><td>'+r.unit+' / '+r.depart+'</td>'
|
||||
+'<td><span class="badge '+(r.type===1?'badge--info':'badge--success')+'">'+(r.type===1?'宿舍':'床位')+'</span></td>'
|
||||
+'<td>'+r.space+'</td><td>'+r.bind+'</td><td>'+(r.unbind||'-')+'</td></tr>';
|
||||
+'<td>'+r.space+'</td><td>'+stationOf(r)+'</td><td>'+r.bind+'</td><td>'+(r.unbind||'-')+'</td></tr>';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="8">没有符合条件的分配记录</td></tr>';
|
||||
document.getElementById('thead').innerHTML=headDev();
|
||||
document.getElementById('totalTip').textContent='共 '+list.length+' 条 · 未解除 '+list.filter(function(r){return !r.unbind;}).length+' 条';
|
||||
}else{
|
||||
var b2=document.getElementById('filterEBuilding').value,d2=document.getElementById('filterEDorm').value;
|
||||
var en=document.getElementById('filterEName').value.trim(),eo=document.getElementById('filterENo').value.trim().toUpperCase();
|
||||
var bd2=document.getElementById('filterBed').value.trim();
|
||||
var b2=document.getElementById('filterEBuilding').value,d2=document.getElementById('filterEDorm').value,s2=document.getElementById('filterEStation').value;
|
||||
var en=document.getElementById('filterEName').value.trim(),eo=document.getElementById('filterENo').value.trim().toUpperCase(),bd=document.getElementById('filterEBed').value.trim();
|
||||
var list2=empRecords.filter(function(r){
|
||||
return (!b2||r.building===b2)&&(!d2||r.dorm===d2)&&(!en||r.name.indexOf(en)>=0)
|
||||
&&(!eo||r.no.toUpperCase().indexOf(eo)>=0)&&(!bd2||r.bed.indexOf(bd2)>=0)&&inRange(r.bind);
|
||||
var m=buildingMeta[r.building]||{};
|
||||
return (!selEmpUnit||m.unit===selEmpUnit)&&(!selEmpDept||m.depart===selEmpDept)&&(!s2||m.station===s2)
|
||||
&&(!b2||r.building===b2)&&(!d2||r.dorm===d2)&&(!bd||r.bed.indexOf(bd)>=0)&&(!en||r.name.indexOf(en)>=0)
|
||||
&&(!eo||r.no.toUpperCase().indexOf(eo)>=0)&&inRange(r.bind);
|
||||
});
|
||||
rows=list2.map(function(r,i){
|
||||
var m=buildingMeta[r.building]||{};
|
||||
return '<tr><td>'+(i+1)+'</td><td style="font-family:monospace;font-weight:500;">'+r.bed+'</td><td>'+r.dorm+'</td><td>'+r.building+'</td>'
|
||||
+'<td>'+(m.station||'')+'</td><td>'+(m.unit?m.unit+' / '+m.depart:'')+'</td>'
|
||||
+'<td>'+r.name+'</td><td>'+r.no+'</td><td>'+r.org+'</td><td>'+r.depart+'</td><td>'+r.bind+'</td><td>'+(r.unbind||'-')+'</td></tr>';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的分配记录</td></tr>';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="12">没有符合条件的分配记录</td></tr>';
|
||||
document.getElementById('thead').innerHTML=headEmp();
|
||||
document.getElementById('totalTip').textContent='共 '+list2.length+' 条 · 未解除 '+list2.filter(function(r){return !r.unbind;}).length+' 条';
|
||||
}
|
||||
@@ -329,8 +432,9 @@ function switchTab(t){
|
||||
renderAll();
|
||||
}
|
||||
function resetFilter(){
|
||||
['filterUnit','filterDepart','filterType','filterEBuilding','filterEDorm'].forEach(function(id){document.getElementById(id).value='';});
|
||||
['filterDevCode','filterHostel','filterEName','filterENo','filterBed'].forEach(function(id){document.getElementById(id).value='';});
|
||||
resetOrg();resetEmpOrg();
|
||||
['filterType','filterEStation','filterEBuilding','filterEDorm'].forEach(function(id){document.getElementById(id).value='';});
|
||||
['filterDevCode','filterEBed','filterEName','filterENo'].forEach(function(id){document.getElementById(id).value='';});
|
||||
document.getElementById('filterFrom').value='2026-01-01';document.getElementById('filterTo').value='2026-09-07';
|
||||
renderAll();
|
||||
}
|
||||
@@ -350,9 +454,19 @@ 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('');
|
||||
/* [交互] 点击面板外部关闭下拉 */
|
||||
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 ow2=document.getElementById('empOrgWrap');
|
||||
if(ow2&&!ow2.contains(e.target)){document.getElementById('empOrgPanel').classList.remove('open');document.getElementById('empOrgTrigger').classList.remove('open');}
|
||||
});
|
||||
document.getElementById('unitCol').insertAdjacentHTML('beforeend',Object.keys(unitDeparts).map(function(u){return '<div class="org-item" onclick="selectUnit(this,\''+u+'\')">'+u+'</div>';}).join(''));
|
||||
document.getElementById('empUnitCol').insertAdjacentHTML('beforeend',Object.keys(unitDeparts).map(function(u){return '<div class="org-item" onclick="selectEmpUnit(this,\''+u+'\')">'+u+'</div>';}).join(''));
|
||||
document.getElementById('filterEBuilding').innerHTML='<option value="">全部楼栋</option>'+buildings.map(function(b){return '<option>'+b+'</option>';}).join('');
|
||||
onFilterUnit();onFilterEBuilding();
|
||||
var allStations=Object.keys(depStations).reduce(function(a,k){return a.concat(depStations[k]);},[]);
|
||||
document.getElementById('filterEStation').innerHTML='<option value="">全部站点</option>'+allStations.map(function(s){return '<option>'+s+'</option>';}).join('');
|
||||
onFilterEBuilding();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -39,12 +39,24 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.sidebar-group.open .sidebar-group__items{display:block;}
|
||||
.sidebar-item--sub{padding-left:44px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.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-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-bar{background:#fff;padding:12px 16px;display:flex;align-items:center;gap:12px;flex-wrap:nowrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
|
||||
.filter-item{display:flex;align-items:center;gap:6px;flex-shrink:0;}
|
||||
.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-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);}
|
||||
@@ -91,6 +103,15 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
|
||||
.form-control{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);color:var(--text-primary);outline:none;width:100%;}
|
||||
.form-control:focus{border-color:var(--primary);}
|
||||
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__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;}
|
||||
.search-select__panel.open{display:block;}
|
||||
.search-select__item{padding:8px 12px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
||||
.search-select__item:hover{background:#F0F8FF;}
|
||||
.search-select__item .ss-name{color:var(--text-primary);font-weight:500;}
|
||||
.search-select__item .ss-org{color:var(--text-placeholder);font-size:var(--font-xs);}
|
||||
.search-select__empty{padding:16px 12px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
.upload-box{border:1px dashed var(--border);border-radius:var(--radius-md);padding:28px 16px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);cursor:pointer;transition:all .2s;background:#FAFAFA;}
|
||||
.upload-box:hover{border-color:var(--primary);color:var(--primary);background:#F0F8FF;}
|
||||
.upload-box svg{display:block;margin:0 auto 8px;}
|
||||
@@ -160,34 +181,52 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
<!-- [交互] 筛选栏:楼栋→宿舍级联 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item">
|
||||
<label>床位编号</label>
|
||||
<input class="filter-input" id="filterBed" placeholder="请输入床位编号" oninput="renderTable()" style="min-width:110px;">
|
||||
<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:120px;">
|
||||
<option value="">全部站点</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>所属楼栋</label>
|
||||
<select class="filter-select" id="filterBuilding" onchange="onFilterBuilding()" style="min-width:180px;">
|
||||
<select class="filter-select" id="filterBuilding" onchange="onFilterBuilding()" style="min-width:160px;">
|
||||
<option value="">全部楼栋</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>所属宿舍</label>
|
||||
<select class="filter-select" id="filterDorm" onchange="renderTable()" style="min-width:110px;">
|
||||
<select class="filter-select" id="filterDorm" onchange="renderTable()" style="min-width:90px;">
|
||||
<option value="">全部宿舍</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>是否分配员工</label>
|
||||
<select class="filter-select" id="filterAlloc" onchange="renderTable()" style="min-width:100px;">
|
||||
<select class="filter-select" id="filterAlloc" onchange="renderTable()" style="min-width:90px;">
|
||||
<option value="">全部</option><option value="1">是</option><option value="0">否</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>员工姓名</label>
|
||||
<input class="filter-input" id="filterEmpName" placeholder="请输入员工姓名" oninput="renderTable()" style="min-width:110px;">
|
||||
<input class="filter-input" id="filterEmpName" placeholder="请输入员工姓名" oninput="renderTable()" style="min-width:100px;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>员工编号</label>
|
||||
<input class="filter-input" id="filterEmpNo" placeholder="请输入员工编号" oninput="renderTable()" style="min-width:110px;">
|
||||
<input class="filter-input" id="filterEmpNo" placeholder="请输入员工编号" oninput="renderTable()" style="min-width:100px;">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="renderTable();showToast('查询完成')">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.3"/><path d="M9.5 9.5L13 13" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/></svg>查询
|
||||
@@ -204,8 +243,6 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
</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="openTaskRecord('import')"><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-primary" onclick="openAllocImport()"><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="openTaskRecord('alloc')"><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-primary" onclick="showToast('床位列表已导出(演示)')"><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="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
|
||||
<button class="btn btn-danger" onclick="batchDelete()">批量删除</button>
|
||||
</div>
|
||||
@@ -215,8 +252,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:100px;">床位编号</th><th style="min-width:180px;">所属楼栋</th><th style="min-width:90px;">所属宿舍</th>
|
||||
<th>是否分配员工</th><th>员工姓名</th><th>员工编号</th><th style="min-width:110px;">员工联系方式</th><th style="min-width:100px;">床头设备</th><th style="width:170px;">操作</th>
|
||||
<th style="width:56px;">序号</th><th style="min-width:100px;">床位编号</th><th style="min-width:90px;">所属宿舍</th><th style="min-width:180px;">所属楼栋</th><th style="min-width:120px;">所属站点</th><th style="min-width:160px;">所属单位部门</th>
|
||||
<th>是否分配员工</th><th>员工姓名</th><th>员工编号</th><th style="min-width:110px;">员工联系方式</th><th style="min-width:110px;">床头设备编号</th><th style="width:170px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbody"></tbody>
|
||||
@@ -233,11 +270,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
<div class="form-row">
|
||||
<div class="form-item">
|
||||
<span class="form-label"><span class="req">*</span> 所属楼栋</span>
|
||||
<select class="form-control" id="eBuilding" onchange="onEditBuilding()"><option value="">请选择楼栋</option></select>
|
||||
<div class="search-select" id="eBuildingSearch">
|
||||
<input class="form-control" id="eBuilding" placeholder="请输入楼栋名称搜索" autocomplete="off" oninput="onBuildingInput()" onfocus="onBuildingFocus()">
|
||||
<div class="search-select__panel" id="buildingPanel"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label"><span class="req">*</span> 所属宿舍</span>
|
||||
<select class="form-control" id="eDorm"><option value="">请先选择楼栋</option></select>
|
||||
<div class="search-select" id="eDormSearch">
|
||||
<input class="form-control" id="eDorm" placeholder="请输入宿舍名称搜索" autocomplete="off" oninput="onDormInput()" onfocus="onDormFocus()">
|
||||
<div class="search-select__panel" id="dormPanel"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
@@ -257,11 +300,17 @@ 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="iBuilding" onchange="onImportBuilding()"><option value="">请选择楼栋</option></select>
|
||||
<div class="search-select" id="iBuildingSearch">
|
||||
<input class="form-control" id="iBuilding" placeholder="请输入楼栋名称搜索" autocomplete="off" oninput="onImportBuildingInput()" onfocus="onImportBuildingFocus()">
|
||||
<div class="search-select__panel" id="iBuildingPanel"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label">所属宿舍(留空则按模板内宿舍列挂接)</span>
|
||||
<select class="form-control" id="iDorm"><option value="">全部宿舍</option></select>
|
||||
<div class="search-select" id="iDormSearch">
|
||||
<input class="form-control" id="iDorm" placeholder="请输入宿舍名称搜索" autocomplete="off" oninput="onImportDormInput()" onfocus="onImportDormFocus()">
|
||||
<div class="search-select__panel" id="iDormPanel"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label"><span class="req">*</span> 上传文件</span>
|
||||
@@ -280,28 +329,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 分配导入:按员工编号+床位编号批量绑定 -->
|
||||
<div class="modal-mask" id="allocImportMask">
|
||||
<div class="modal">
|
||||
<div class="modal__title">批量导入分配数据</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label"><span class="req">*</span> 上传文件</span>
|
||||
<div class="upload-box" onclick="showToast('已选择文件:床位分配-模板.xlsx')">
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none"><path d="M13 17V6m0 0L8.5 10.5M13 6l4.5 4.5" stroke="#999" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M4 18v2.5A1.5 1.5 0 0 0 5.5 22h15a1.5 1.5 0 0 0 1.5-1.5V18" stroke="#999" stroke-width="1.6" stroke-linecap="round"/></svg>
|
||||
点击上传 Excel 文件(.xlsx)
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size:var(--font-xs);color:var(--text-placeholder);margin-bottom:4px;">
|
||||
<a href="javascript:void(0)" style="color:var(--primary);text-decoration:none;" onclick="showToast('模板下载中…')">下载导入模板</a> · 模板列:员工编号 / 床位编号;导入成功即建立床位-员工分配关系
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn-default" onclick="closeAllocImport()">取消</button>
|
||||
<button class="btn btn-primary" onclick="doAllocImport()">开始导入</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 导入/分配导入:异步任务列表 -->
|
||||
<!-- [交互] 导入任务:异步任务列表 -->
|
||||
<div class="modal-mask" id="taskMask">
|
||||
<div class="modal--wide">
|
||||
<div class="modal__title" id="taskTitle">床位导入任务</div>
|
||||
@@ -382,7 +410,17 @@ var assigns={
|
||||
'靖边气田倒班宿舍楼|401-1':'CQ036441','靖边气田倒班宿舍楼|401-2':'CQ029905'
|
||||
};
|
||||
/* 床位→床头设备(key同上) */
|
||||
var bedDevices={'樊学倒班宿舍1号楼|101-1':'HC-FX-01','樊学倒班宿舍1号楼|101-2':'HC-FX-02','樊学倒班宿舍1号楼|102-1':'HC-FX-03','桐园倒班宿舍1号楼|301-1':'HC-TY-01','西峰基地综合宿舍楼|301-1':'HC-XF-01','西峰基地综合宿舍楼|302-1':'HC-XF-02','靖边气田倒班宿舍楼|401-1':'HC-JB-01'};
|
||||
var bedDevices={'樊学倒班宿舍1号楼|101-1':'10001','樊学倒班宿舍1号楼|101-2':'10002','樊学倒班宿舍1号楼|102-1':'10003','桐园倒班宿舍1号楼|301-1':'10006','西峰基地综合宿舍楼|301-1':'10008','西峰基地综合宿舍楼|302-1':'10009','靖边气田倒班宿舍楼|401-1':'10011'};
|
||||
/* 单位→部门→站点三级映射与楼栋归属,与「楼栋信息」「宿舍信息」页口径一致 */
|
||||
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
|
||||
var depStations={'樊学作业区':['樊学站点基地'],'麻子山作业区':['麻子山站点'],'桐园作业区':['桐园站点基地'],'姬圪台作业区':['姬圪台站点'],'西峰作业区':['西峰基地'],'靖边气田':['靖边气田站点']};
|
||||
var buildingMeta={
|
||||
'樊学倒班宿舍1号楼':{station:'樊学站点基地',unit:'采油一厂',depart:'樊学作业区'},
|
||||
'麻子山倒班宿舍楼':{station:'麻子山站点',unit:'采油一厂',depart:'麻子山作业区'},
|
||||
'桐园倒班宿舍1号楼':{station:'桐园站点基地',unit:'采油二厂',depart:'桐园作业区'},
|
||||
'西峰基地综合宿舍楼':{station:'西峰基地',unit:'第四采油厂',depart:'西峰作业区'},
|
||||
'靖边气田倒班宿舍楼':{station:'靖边气田站点',unit:'采气一厂',depart:'靖边气田'}
|
||||
};
|
||||
var beds=[];
|
||||
dormMeta.forEach(function(d){
|
||||
for(var nn=1;nn<=d.beds;nn++){
|
||||
@@ -393,9 +431,11 @@ dormMeta.forEach(function(d){
|
||||
var pageNo=1,pageSize=10;
|
||||
function uniqBuildings(){var bs=[];dormMeta.forEach(function(d){if(bs.indexOf(d.building)<0)bs.push(d.building);});return bs;}
|
||||
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);
|
||||
document.getElementById('filterBuilding').innerHTML='<option value="">全部楼栋</option>'+uniqBuildings().map(function(b){return '<option>'+b+'</option>';}).join('');
|
||||
document.getElementById('eBuilding').innerHTML='<option value="">请选择楼栋</option>'+uniqBuildings().map(function(b){return '<option>'+b+'</option>';}).join('');
|
||||
document.getElementById('iBuilding').innerHTML='<option value="">请选择楼栋</option>'+uniqBuildings().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 dormOptionsHtml(b,cur){
|
||||
var ds=[];dormMeta.forEach(function(d){if((!b||d.building===b)&&ds.indexOf(d.name)<0)ds.push(d.name);});
|
||||
@@ -407,12 +447,45 @@ function onFilterBuilding(){
|
||||
renderTable();
|
||||
}
|
||||
function empOf(no){for(var i=0;i<empPool.length;i++){if(empPool[i].no===no)return empPool[i];}return null;}
|
||||
/* [交互] 单位部门级联筛选 */
|
||||
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='';
|
||||
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 filtered(){
|
||||
var b=document.getElementById('filterBuilding').value,d=document.getElementById('filterDorm').value,kc=document.getElementById('filterBed').value.trim();
|
||||
var b=document.getElementById('filterBuilding').value,d=document.getElementById('filterDorm').value,s=document.getElementById('filterStation').value;
|
||||
var a=document.getElementById('filterAlloc').value,en=document.getElementById('filterEmpName').value.trim(),eo=document.getElementById('filterEmpNo').value.trim().toUpperCase();
|
||||
return beds.filter(function(r){
|
||||
var key=r.building+'|'+r.code,e=assigns[key]?empOf(assigns[key]):null;
|
||||
return (!b||r.building===b)&&(!d||r.dorm===d)&&(!kc||r.code.indexOf(kc)>=0)
|
||||
var meta=buildingMeta[r.building]||{};
|
||||
return (!selUnit||meta.unit===selUnit)&&(!selDept||meta.depart===selDept)&&(!s||meta.station===s)
|
||||
&&(!b||r.building===b)&&(!d||r.dorm===d)
|
||||
&&(a===''||String(assigns[key]?1:0)===a)
|
||||
&&(!en||(e&&e.name.indexOf(en)>=0))&&(!eo||(assigns[key]&&assigns[key].indexOf(eo)>=0));
|
||||
});
|
||||
@@ -424,14 +497,15 @@ function renderTable(){
|
||||
var slice=list.slice((pageNo-1)*pageSize,pageNo*pageSize);
|
||||
document.getElementById('tbody').innerHTML=slice.map(function(r){
|
||||
var i=beds.indexOf(r),key=r.building+'|'+r.code,no=assigns[key],e=no?empOf(no):null;
|
||||
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-family:monospace;font-weight:500;">'+r.code+'</td><td>'+r.building+'</td><td>'+r.dorm+'</td>'
|
||||
+'<td>'+(i+1)+'</td><td style="font-family:monospace;font-weight:500;">'+r.code+'</td><td>'+r.dorm+'</td><td>'+r.building+'</td><td>'+(meta.station||'')+'</td><td>'+(meta.unit?meta.unit+' / '+meta.depart:'')+'</td>'
|
||||
+(e?'<td><span class="badge badge--success">是</span></td><td>'+e.name+'</td><td>'+e.no+'</td><td>'+e.phone+'</td>'
|
||||
:'<td><span class="badge badge--muted">否</span></td><td>-</td><td>-</td><td>-</td>')
|
||||
+'<td style="font-family:monospace;">'+(bedDevices[key]||'-')+'</td>'
|
||||
+'<td>'+(e?'<button class="btn btn-link--danger" onclick="askUnassign(\''+key+'\')">解除分配</button>':'<button class="btn btn-link" onclick="openAlloc(\''+key+'\',\''+r.code+'\')">分配员工</button>')
|
||||
+'<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="13">没有符合条件的床位</td></tr>';
|
||||
document.getElementById('totalTip').textContent='共 '+list.length+' 床 · 已分配 '+Object.keys(assigns).length+' · 第 '+pageNo+'/'+pages+' 页';
|
||||
document.getElementById('ckAll').checked=false;
|
||||
}
|
||||
@@ -439,7 +513,7 @@ function gotoPage(d){
|
||||
var pages=Math.max(1,Math.ceil(filtered().length/pageSize));
|
||||
pageNo=Math.min(pages,Math.max(1,pageNo+d));renderTable();
|
||||
}
|
||||
function resetFilter(){['filterBuilding','filterDorm','filterAlloc'].forEach(function(id){document.getElementById(id).value='';});['filterBed','filterEmpName','filterEmpNo'].forEach(function(id){document.getElementById(id).value='';});onFilterBuilding();}
|
||||
function resetFilter(){resetOrg();['filterStation','filterBuilding','filterDorm','filterAlloc'].forEach(function(id){document.getElementById(id).value='';});['filterEmpName','filterEmpNo'].forEach(function(id){document.getElementById(id).value='';});onFilterBuilding();}
|
||||
/* [交互] 复选框与批量删除 */
|
||||
function toggleAll(ck){document.querySelectorAll('.rowCk').forEach(function(c){c.checked=ck.checked;});}
|
||||
function syncAllCk(){
|
||||
@@ -457,29 +531,63 @@ function batchDelete(){
|
||||
batchAskDelete();
|
||||
}
|
||||
/* [交互] 新增/编辑床位 */
|
||||
var editIdx=-1;
|
||||
function onEditBuilding(){
|
||||
var b=document.getElementById('eBuilding').value;
|
||||
document.getElementById('eDorm').innerHTML='<option value="">请选择宿舍</option>'+dormOptionsHtml(b);
|
||||
var editIdx=-1,selBuilding='',selDorm='';
|
||||
function onBuildingInput(){selBuilding='';selDorm='';document.getElementById('eDorm').value='';renderBuildingPanel();}
|
||||
function onBuildingFocus(){renderBuildingPanel();}
|
||||
function renderBuildingPanel(){
|
||||
var q=document.getElementById('eBuilding').value.trim();
|
||||
var list=uniqBuildings().filter(function(b){return !q||b.indexOf(q)>=0;});
|
||||
document.getElementById('buildingPanel').innerHTML=list.map(function(b){
|
||||
var meta=buildingMeta[b]||{};
|
||||
return '<div class="search-select__item" onclick="pickBuilding(\''+b+'\')"><span class="ss-name">'+b+'</span><span class="ss-org">'+(meta.station||'')+'</span></div>';
|
||||
}).join('')||'<div class="search-select__empty">未查询到楼栋</div>';
|
||||
document.getElementById('buildingPanel').classList.add('open');
|
||||
}
|
||||
function pickBuilding(name){
|
||||
selBuilding=name;
|
||||
document.getElementById('eBuilding').value=name;
|
||||
selDorm='';document.getElementById('eDorm').value='';
|
||||
document.getElementById('buildingPanel').classList.remove('open');
|
||||
}
|
||||
function onDormInput(){selDorm='';renderDormPanel();}
|
||||
function onDormFocus(){renderDormPanel();}
|
||||
function renderDormPanel(){
|
||||
var q=document.getElementById('eDorm').value.trim();
|
||||
var list=(selBuilding?dormMeta.filter(function(d){return d.building===selBuilding;}):[]).filter(function(d){return !q||d.name.indexOf(q)>=0;});
|
||||
document.getElementById('dormPanel').innerHTML=list.map(function(d){
|
||||
return '<div class="search-select__item" onclick="pickDorm(\''+d.name+'\')"><span class="ss-name">'+d.name+'</span></div>';
|
||||
}).join('')||'<div class="search-select__empty">'+(selBuilding?'未查询到宿舍':'请先选择楼栋')+'</div>';
|
||||
document.getElementById('dormPanel').classList.add('open');
|
||||
}
|
||||
function pickDorm(name){
|
||||
selDorm=name;
|
||||
document.getElementById('eDorm').value=name;
|
||||
document.getElementById('dormPanel').classList.remove('open');
|
||||
}
|
||||
function openEdit(i){
|
||||
editIdx=i;
|
||||
document.getElementById('editTitle').textContent=i<0?'新增床位信息':'编辑床位信息';
|
||||
if(i>=0){
|
||||
var r=beds[i];
|
||||
document.getElementById('eBuilding').value=r.building;onEditBuilding();
|
||||
document.getElementById('eDorm').innerHTML='<option value="">请选择宿舍</option>'+dormOptionsHtml(r.building,r.dorm);
|
||||
selBuilding=r.building;selDorm=r.dorm;
|
||||
document.getElementById('eBuilding').value=r.building;
|
||||
document.getElementById('eDorm').value=r.dorm;
|
||||
document.getElementById('eCode').value=r.code;
|
||||
}else{
|
||||
document.getElementById('eBuilding').value='';onEditBuilding();
|
||||
selBuilding='';selDorm='';
|
||||
document.getElementById('eBuilding').value='';
|
||||
document.getElementById('eDorm').value='';
|
||||
document.getElementById('eCode').value='';
|
||||
}
|
||||
document.getElementById('buildingPanel').classList.remove('open');
|
||||
document.getElementById('dormPanel').classList.remove('open');
|
||||
document.getElementById('editMask').classList.add('open');
|
||||
}
|
||||
function closeEdit(){document.getElementById('editMask').classList.remove('open');}
|
||||
function saveEdit(){
|
||||
var b=document.getElementById('eBuilding').value,d=document.getElementById('eDorm').value,c=document.getElementById('eCode').value.trim();
|
||||
if(!b||!d||!c){showToast('请完整填写带 * 的必填项');return;}
|
||||
var b=selBuilding||document.getElementById('eBuilding').value.trim(),d=selDorm||document.getElementById('eDorm').value.trim(),c=document.getElementById('eCode').value.trim();
|
||||
var dormOk=dormMeta.some(function(x){return x.building===b&&x.name===d;});
|
||||
if(!b||!buildingMeta[b]||!d||!dormOk||!c){showToast('请完整填写带 * 的必填项');return;}
|
||||
var dup=beds.some(function(r,i){return i!==editIdx&&r.building===b&&r.dorm===d&&r.code===c;});
|
||||
if(dup){showToast('该宿舍下已存在同名床位编号');return;}
|
||||
if(editIdx<0){beds.push({code:c,building:b,dorm:d});showToast('床位已新增');}
|
||||
@@ -496,27 +604,62 @@ function saveEdit(){
|
||||
closeEdit();pageNo=1;renderTable();
|
||||
}
|
||||
/* [交互] 导入 */
|
||||
function openImport(){document.getElementById('importMask').classList.add('open');}
|
||||
function closeImport(){document.getElementById('importMask').classList.remove('open');}
|
||||
function onImportBuilding(){
|
||||
var b=document.getElementById('iBuilding').value;
|
||||
document.getElementById('iDorm').innerHTML='<option value="">全部宿舍</option>'+dormOptionsHtml(b);
|
||||
var selImportBuilding='',selImportDorm='';
|
||||
function onImportBuildingInput(){selImportBuilding='';selImportDorm='';document.getElementById('iDorm').value='';renderImportBuildingPanel();}
|
||||
function onImportBuildingFocus(){renderImportBuildingPanel();}
|
||||
function renderImportBuildingPanel(){
|
||||
var q=document.getElementById('iBuilding').value.trim();
|
||||
var list=uniqBuildings().filter(function(b){return !q||b.indexOf(q)>=0;});
|
||||
document.getElementById('iBuildingPanel').innerHTML=list.map(function(b){
|
||||
var meta=buildingMeta[b]||{};
|
||||
return '<div class="search-select__item" onclick="pickImportBuilding(\''+b+'\')"><span class="ss-name">'+b+'</span><span class="ss-org">'+(meta.station||'')+'</span></div>';
|
||||
}).join('')||'<div class="search-select__empty">未查询到楼栋</div>';
|
||||
document.getElementById('iBuildingPanel').classList.add('open');
|
||||
}
|
||||
function pickImportBuilding(name){
|
||||
selImportBuilding=name;
|
||||
document.getElementById('iBuilding').value=name;
|
||||
selImportDorm='';document.getElementById('iDorm').value='';
|
||||
document.getElementById('iBuildingPanel').classList.remove('open');
|
||||
}
|
||||
function onImportDormInput(){selImportDorm='';renderImportDormPanel();}
|
||||
function onImportDormFocus(){renderImportDormPanel();}
|
||||
function renderImportDormPanel(){
|
||||
var q=document.getElementById('iDorm').value.trim();
|
||||
var list=selImportBuilding?dormMeta.filter(function(d){return d.building===selImportBuilding;}):[];
|
||||
list=list.filter(function(d){return !q||d.name.indexOf(q)>=0;});
|
||||
var html=(selImportBuilding&&!q)?'<div class="search-select__item" onclick="pickImportDorm(\'\')"><span class="ss-name">全部宿舍</span></div>':'';
|
||||
html+=list.map(function(d){
|
||||
return '<div class="search-select__item" onclick="pickImportDorm(\''+d.name+'\')"><span class="ss-name">'+d.name+'</span></div>';
|
||||
}).join('');
|
||||
if(!html)html='<div class="search-select__empty">'+(selImportBuilding?'未查询到宿舍':'请先选择楼栋')+'</div>';
|
||||
document.getElementById('iDormPanel').innerHTML=html;
|
||||
document.getElementById('iDormPanel').classList.add('open');
|
||||
}
|
||||
function pickImportDorm(name){
|
||||
selImportDorm=name;
|
||||
document.getElementById('iDorm').value=name;
|
||||
document.getElementById('iDormPanel').classList.remove('open');
|
||||
}
|
||||
function openImport(){
|
||||
selImportBuilding='';selImportDorm='';
|
||||
document.getElementById('iBuilding').value='';
|
||||
document.getElementById('iDorm').value='';
|
||||
document.getElementById('iBuildingPanel').classList.remove('open');
|
||||
document.getElementById('iDormPanel').classList.remove('open');
|
||||
document.getElementById('importMask').classList.add('open');
|
||||
}
|
||||
function closeImport(){document.getElementById('importMask').classList.remove('open');}
|
||||
function doImport(){
|
||||
if(!document.getElementById('iBuilding').value){showToast('请先选择所属楼栋');return;}
|
||||
if(!selImportBuilding){showToast('请先选择所属楼栋');return;}
|
||||
closeImport();showToast('导入任务已提交,请到「查看导入任务」查看结果');
|
||||
}
|
||||
/* [交互] 分配导入 */
|
||||
function openAllocImport(){document.getElementById('allocImportMask').classList.add('open');}
|
||||
function closeAllocImport(){document.getElementById('allocImportMask').classList.remove('open');}
|
||||
function doAllocImport(){closeAllocImport();showToast('分配导入任务已提交,请到「查看分配导入任务」查看结果');}
|
||||
/* [交互] 导入/分配导入任务(异步任务演示) */
|
||||
/* [交互] 导入任务(异步任务演示) */
|
||||
var taskRecords={
|
||||
import:[['床位清单-模板.xlsx','2026-09-05 17:15:08','成功:新增 8 床'],['床位清单-8月.xlsx','2026-08-20 09:55:17','成功:新增 12 床']],
|
||||
alloc:[['床位分配-模板.xlsx','2026-09-06 11:20:34','成功:绑定 12 条、失败 0 条'],['床位分配-8月.xlsx','2026-08-22 15:03:49','成功:绑定 20 条、失败 2 条']]
|
||||
import:[['床位清单-模板.xlsx','2026-09-05 17:15:08','成功:新增 8 床'],['床位清单-8月.xlsx','2026-08-20 09:55:17','成功:新增 12 床']]
|
||||
};
|
||||
function openTaskRecord(type){
|
||||
var titleMap={import:'床位导入任务',alloc:'分配导入任务'};
|
||||
var titleMap={import:'床位导入任务'};
|
||||
document.getElementById('taskTitle').textContent=titleMap[type];
|
||||
var head='<thead><tr><th>文件名称</th><th>时间</th><th>状态</th><th>操作</th></tr></thead>';
|
||||
var rows=taskRecords[type].map(function(r){
|
||||
@@ -601,6 +744,19 @@ 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');}
|
||||
var bs=document.getElementById('eBuildingSearch');
|
||||
if(bs&&!bs.contains(e.target)){document.getElementById('buildingPanel').classList.remove('open');}
|
||||
var ds=document.getElementById('eDormSearch');
|
||||
if(ds&&!ds.contains(e.target)){document.getElementById('dormPanel').classList.remove('open');}
|
||||
var ibs=document.getElementById('iBuildingSearch');
|
||||
if(ibs&&!ibs.contains(e.target)){document.getElementById('iBuildingPanel').classList.remove('open');}
|
||||
var ids=document.getElementById('iDormSearch');
|
||||
if(ids&&!ids.contains(e.target)){document.getElementById('iDormPanel').classList.remove('open');}
|
||||
});
|
||||
initFilters();onFilterBuilding();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -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>
|
||||
@@ -335,73 +365,97 @@ var buildings=[
|
||||
/* [交互] 宿舍数量下钻演示数据:按楼栋名挂宿舍清单,列与宿舍信息页一致 */
|
||||
var bldDorms={
|
||||
'樊学倒班宿舍1号楼':[
|
||||
{name:'301室',beds:6,manager:'石维国',mobile:'13991203457',alloc:1,dev:'BC-2026-0031'},
|
||||
{name:'302室',beds:4,manager:'曲志明',mobile:'13991203458',alloc:1,dev:'BC-2026-0032'},
|
||||
{name:'301室',beds:6,manager:'石维国',mobile:'13991203457',alloc:1,dev:'30001'},
|
||||
{name:'302室',beds:4,manager:'曲志明',mobile:'13991203458',alloc:1,dev:'30002'},
|
||||
{name:'305室',beds:6,manager:'李怀安',mobile:'13991203459',alloc:0,dev:'—'}
|
||||
],
|
||||
'麻子山倒班宿舍楼':[
|
||||
{name:'201室',beds:4,manager:'高建民',mobile:'13991345013',alloc:1,dev:'BC-2026-0015'},
|
||||
{name:'201室',beds:4,manager:'高建民',mobile:'13991345013',alloc:1,dev:'30003'},
|
||||
{name:'204室',beds:4,manager:'杨立强',mobile:'13991345014',alloc:0,dev:'—'}
|
||||
],
|
||||
'桐园倒班宿舍1号楼':[
|
||||
{name:'401室',beds:8,manager:'白吉增',mobile:'13991567209',alloc:1,dev:'BC-2026-0021'},
|
||||
{name:'402室',beds:6,manager:'师振峰',mobile:'13991567210',alloc:1,dev:'BC-2026-0022'},
|
||||
{name:'401室',beds:8,manager:'白吉增',mobile:'13991567209',alloc:1,dev:'30004'},
|
||||
{name:'402室',beds:6,manager:'师振峰',mobile:'13991567210',alloc:1,dev:'30005'},
|
||||
{name:'406室',beds:6,manager:'冯志清',mobile:'13991567211',alloc:0,dev:'—'}
|
||||
],
|
||||
'西峰基地综合宿舍楼':[
|
||||
{name:'101室',beds:4,manager:'康维桢',mobile:'13991784321',alloc:1,dev:'BC-2026-0008'},
|
||||
{name:'102室',beds:4,manager:'邵瑞庭',mobile:'13991784322',alloc:1,dev:'BC-2026-0009'},
|
||||
{name:'101室',beds:4,manager:'康维桢',mobile:'13991784321',alloc:1,dev:'30006'},
|
||||
{name:'102室',beds:4,manager:'邵瑞庭',mobile:'13991784322',alloc:1,dev:'30007'},
|
||||
{name:'201室',beds:6,manager:'卢翰文',mobile:'13991784323',alloc:0,dev:'—'},
|
||||
{name:'202室',beds:6,manager:'崔秉章',mobile:'13991784324',alloc:1,dev:'BC-2026-0010'}
|
||||
{name:'202室',beds:6,manager:'崔秉章',mobile:'13991784324',alloc:1,dev:'30008'}
|
||||
],
|
||||
'靖边气田倒班宿舍楼':[
|
||||
{name:'501室',beds:4,manager:'戴文渊',mobile:'13991923746',alloc:1,dev:'BC-2026-0003'},
|
||||
{name:'501室',beds:4,manager:'戴文渊',mobile:'13991923746',alloc:1,dev:'30009'},
|
||||
{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>
|
||||
|
||||
@@ -39,7 +39,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.sidebar-group.open .sidebar-group__items{display:block;}
|
||||
.sidebar-item--sub{padding-left:44px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.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:16px 24px;display:flex;align-items:center;gap:8px;flex-wrap:nowrap;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:120px;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;}
|
||||
@@ -49,10 +49,10 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.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-link{background:none;color:var(--primary);padding:0 4px;height:auto;}.btn-link:hover{color:#40a9ff;}
|
||||
.tab-bar{display:flex;gap:4px;margin-top:16px;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);padding:6px 16px 0;}
|
||||
.tab-item{padding:10px 18px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;}
|
||||
.tab-bar{display:flex;gap:4px;margin-bottom:16px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:8px 16px;}
|
||||
.tab-item{padding:10px 18px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;}
|
||||
.tab-item.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.table-card{background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__head{display:flex;align-items:center;justify-content:flex-start;padding:12px 16px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-sm);font-weight:600;}
|
||||
.table-card__ops{display:flex;align-items:center;gap:8px;}
|
||||
@@ -95,6 +95,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
|
||||
/* 工单详情:呼叫类型+阶段标签+处置状态 */
|
||||
.ct-head{display:flex;align-items:center;gap:10px;font-size:var(--font-base);font-weight:600;margin-bottom:14px;}
|
||||
.ct-head .st{font-size:var(--font-sm);font-weight:400;color:var(--text-secondary);}
|
||||
.ct-head__title{display:flex;align-items:center;gap:8px;}
|
||||
.ct-head__status{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:var(--font-sm);font-weight:400;color:var(--text-secondary);white-space:nowrap;}
|
||||
.info-card{border-radius:var(--radius-sm);padding:12px 14px;display:grid;grid-template-columns:1fr 1fr;gap:8px 18px;}
|
||||
.info-card--blue{background:#F0F8FF;border:1px solid #D6E9FF;}
|
||||
.info-card--grey{background:#FAFAFA;border:1px solid var(--border);}
|
||||
@@ -161,37 +163,39 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
|
||||
<!-- 主内容 -->
|
||||
<main class="main">
|
||||
|
||||
<!-- [交互] TAB 切换呼入/呼出记录 -->
|
||||
<div class="tab-bar">
|
||||
<div class="tab-item active" id="tabIn" onclick="switchTab('in')">员工呼叫</div>
|
||||
<div class="tab-item" id="tabOut" onclick="switchTab('out')">终端呼叫</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 筛选栏:员工呼入/终端呼出各自独立一组条件,切换 TAB 时整组替换 -->
|
||||
<div class="filter-bar">
|
||||
<div id="fgrpIn" style="display:contents;">
|
||||
<div class="filter-item"><label>员工姓名</label><input class="filter-input" id="inName" placeholder="姓名" oninput="renderAll()"></div>
|
||||
<div class="filter-item"><label>员工编号</label><input class="filter-input" id="inNo" placeholder="如:CQ021578" oninput="renderAll()"></div>
|
||||
<div class="filter-item"><label>主呼设备</label><input class="filter-input" id="inDev" placeholder="如:HC-FX" oninput="renderAll()"></div>
|
||||
<div class="filter-item"><label>呼叫形式</label>
|
||||
<select class="filter-select" id="inType" onchange="renderAll()" style="min-width:110px;"><option value="">全部</option><option>宿舍呼叫</option><option>床位呼叫</option></select>
|
||||
<div class="filter-item"><label>呼叫设备编号</label><input class="filter-input" id="inDev" placeholder="请输入" oninput="renderAll()" style="width:80px;min-width:80px;"></div>
|
||||
<div class="filter-item"><label>员工姓名</label><input class="filter-input" id="inName" placeholder="请输入" oninput="renderAll()" style="width:72px;min-width:72px;"></div>
|
||||
<div class="filter-item"><label>员工编号</label><input class="filter-input" id="inNo" placeholder="请输入" oninput="renderAll()" style="width:92px;min-width:92px;"></div>
|
||||
<div class="filter-item"><label>呼叫类型</label>
|
||||
<select class="filter-select" id="inType" onchange="renderAll()" style="width:86px;min-width:86px;"><option value="">全部</option><option>宿舍呼叫</option><option>床位呼叫</option></select>
|
||||
</div>
|
||||
<div class="filter-item"><label>呼叫状态</label>
|
||||
<select class="filter-select" id="inStatus" onchange="renderAll()" style="min-width:100px;"><option value="">全部</option><option>已接通</option><option>未接通</option></select>
|
||||
<select class="filter-select" id="inStatus" onchange="renderAll()" style="width:76px;min-width:76px;"><option value="">全部</option><option>已接通</option><option>未接通</option></select>
|
||||
</div>
|
||||
<div class="filter-item"><label>救助状态</label>
|
||||
<select class="filter-select" id="inRescue" onchange="renderAll()" style="min-width:110px;"><option value="">全部</option><option>等待处理</option><option>处理中</option><option>处理完成</option></select>
|
||||
<select class="filter-select" id="inRescue" onchange="renderAll()" style="width:86px;min-width:86px;"><option value="">全部</option><option>待处理</option><option>已完成</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fgrpOut" style="display:none;">
|
||||
<div class="filter-item"><label>主呼设备</label><input class="filter-input" id="outFrom" placeholder="如:HD-FX" oninput="renderAll()"></div>
|
||||
<div class="filter-item"><label>被呼设备</label><input class="filter-input" id="outTo" placeholder="如:HC-FX" oninput="renderAll()"></div>
|
||||
<div class="filter-item"><label>呼叫形式</label>
|
||||
<select class="filter-select" id="outType" onchange="renderAll()" style="min-width:110px;"><option value="">全部</option><option>语音呼叫</option><option>视频呼叫</option><option>一键呼叫</option></select>
|
||||
</div>
|
||||
<div class="filter-item"><label>呼叫设备编号</label><input class="filter-input" id="outFrom" placeholder="请输入" oninput="renderAll()" style="width:92px;min-width:92px;"></div>
|
||||
<div class="filter-item"><label>呼叫状态</label>
|
||||
<select class="filter-select" id="outStatus" onchange="renderAll()" style="min-width:110px;"><option value="">全部</option><option>已接听</option><option>未接听</option><option>已挂断</option></select>
|
||||
<select class="filter-select" id="outStatus" onchange="renderAll()" style="width:86px;min-width:86px;"><option value="">全部</option><option>已接听</option><option>未接听</option><option>已挂断</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>呼叫日期</label>
|
||||
<input class="filter-input" type="date" id="filterFrom" value="2026-09-01" onchange="renderAll()" style="min-width:130px;">
|
||||
<input class="filter-input" type="date" id="filterFrom" value="2026-09-01" onchange="renderAll()" style="width:100px;min-width:100px;">
|
||||
<span style="color:var(--text-placeholder);font-size:var(--font-xs);">至</span>
|
||||
<input class="filter-input" type="date" id="filterTo" value="2026-09-07" onchange="renderAll()" style="min-width:130px;">
|
||||
<input class="filter-input" type="date" id="filterTo" value="2026-09-07" onchange="renderAll()" style="width:100px;min-width:100px;">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="renderAll();showToast('查询完成')">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.3"/><path d="M9.5 9.5L13 13" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/></svg>查询
|
||||
@@ -200,12 +204,6 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- [交互] TAB 切换呼入/呼出记录 -->
|
||||
<div class="tab-bar">
|
||||
<div class="tab-item active" id="tabIn" onclick="switchTab('in')">员工呼入</div>
|
||||
<div class="tab-item" id="tabOut" onclick="switchTab('out')">终端呼出</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<div class="table-card__head">
|
||||
<!-- [交互] 导出/查看导出任务仅员工呼入 TAB 提供,终端呼出不支持导出 -->
|
||||
@@ -263,35 +261,37 @@ var empProfiles={
|
||||
'CQ022317':{name:'徐立成',sex:'男',age:49,unit:'采油二厂',dept:'桐园作业区',phone:'15089628819',chronic:'慢性支气管炎'},
|
||||
'CQ029905':{name:'呼建国',sex:'男',age:53,unit:'采气一厂',dept:'靖边气田',phone:'18730413345',chronic:'支气管哮喘'}
|
||||
};
|
||||
/* 员工呼入:主呼设备=床头/面板(HC-*),被呼设备=值班室终端(HD-*);赵万福(182/110)与结果异常 YC-20260905-05 同源事件 */
|
||||
/* 员工呼入:主呼设备=床头/面板,被呼设备=值班室终端;赵万福(182/110)与结果异常 YC-20260905-05 同源事件 */
|
||||
var incoming=[
|
||||
{dev:'HC-FX-01',to:'HD-FX-01',time:'2026-09-07 02:14',resp:'2026-09-07 02:15',end:'2026-09-07 02:41',ct:'床位呼叫',no:'CQ021578',cs:'已接通',rs:'处理完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-1',out:'否',reason:'夜间胸闷,请求值班医生到场',info:'值班医生 2 分钟到场,心电监护无异常,处置后回床休息。'},
|
||||
{dev:'HC-FX-04',to:'HD-FX-01',time:'2026-09-06 23:41',resp:'2026-09-06 23:42',end:'2026-09-06 23:58',ct:'宿舍呼叫',no:'CQ032144',cs:'已接通',rs:'处理完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101室',out:'否',reason:'同事突发头晕',info:'到 101 室处置,测血压 126/78,判定为体位性低血压,补液后缓解。'},
|
||||
{dev:'HC-TY-02',to:'HD-TY-01',time:'2026-09-05 21:07',resp:'2026-09-05 21:08',end:'2026-09-05 21:52',ct:'宿舍呼叫',no:'CQ033096',cs:'已接通',rs:'处理完成',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 201室',out:'是',amb:'桐园医疗点救护车 1 号',drv:'马永林',med:'值班医生白文斌、护士钱晓芸',adate:'2026-09-05',reason:'自测血压 182/110 伴头痛',info:'值班医生到场复测确认,按高血压危象指引处置并通知管理医生,救护车送医疗点降压治疗后平稳,该事件进入结果异常台账(YC-20260905-05)。'},
|
||||
{dev:'HC-JB-01',to:'HD-JB-01',time:'2026-09-05 14:22',resp:'2026-09-05 14:23',end:'2026-09-05 14:50',ct:'床位呼叫',no:'CQ036441',cs:'已接通',rs:'处理完成',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1',out:'否',reason:'发热 38.6℃ 需退热处置',info:'靖边医疗点到宿舍处置,物理降温+口服退热,次日随访已退热。'},
|
||||
{dev:'HC-XF-01',to:'HD-XF-01',time:'2026-09-04 07:52',resp:'—',end:'2026-09-04 07:58',ct:'床位呼叫',no:'CQ010564',cs:'未接通',rs:'处理完成',station:'西峰基地',space:'西峰基地综合宿舍楼 · 301-1',out:'否',reason:'晨起手指麻木请求咨询',info:'首呼未接听,5 分钟后值班室回拨接通,指导活动观察,当日无碍。'},
|
||||
{dev:'HC-MZ-01',to:'HD-MZ-01',time:'2026-09-03 19:36',resp:'2026-09-03 19:37',end:'—',ct:'宿舍呼叫',no:'CQ045130',cs:'已接通',rs:'等待处理',station:'麻子山站点',space:'麻子山倒班宿舍楼 · 101室',out:'',reason:'踝扭伤冰敷后疼痛加重',info:'待夜班值班医生到场评估,已电话指导制动抬高。'},
|
||||
{dev:'HC-XF-03',to:'HD-XF-01',time:'2026-09-02 12:03',resp:'2026-09-02 12:04',end:'—',ct:'宿舍呼叫',no:'CQ027710',cs:'已接通',rs:'处理中',station:'西峰基地',space:'西峰基地综合宿舍楼 · 402室',out:'',reason:'外伤换药到期提醒请求处理',info:'值班室已派单,14:00 医疗点护士到宿舍换药。'},
|
||||
{dev:'HC-FX-02',to:'HD-FX-01',time:'2026-09-01 22:48',resp:'2026-09-01 22:49',end:'2026-09-01 23:05',ct:'床位呼叫',no:'CQ032144',cs:'已接通',rs:'处理完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-2',out:'否',reason:'失眠需助眠指导',info:'健康指导并纳入随访关注,无需药物介入。'},
|
||||
{dev:'HC-TY-01',to:'HD-TY-01',time:'2026-09-07 06:02',resp:'—',end:'—',ct:'床位呼叫',no:'CQ022317',cs:'未接通',rs:'等待处理',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 301-1',out:'',reason:'晨起呼叫后设备侧无声',info:'终端未接听,值班员正现场核实;超过 10 分钟将联动告警考核。'},
|
||||
{dev:'HC-JB-01',to:'HD-JB-01',time:'2026-09-06 05:36',resp:'2026-09-06 05:37',end:'2026-09-06 06:01',ct:'床位呼叫',no:'CQ029905',cs:'已接通',rs:'处理完成',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1',out:'否',reason:'哮喘吸入剂用完需应急配药',info:'值班医生送备用吸入剂并预约复诊。'}
|
||||
{dev:'10001',to:'10013',time:'2026-09-07 02:14',resp:'2026-09-07 02:15',end:'2026-09-07 02:41',ct:'床位呼叫',no:'CQ021578',cs:'已接通',rs:'已完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-1',out:'否',reason:'夜间胸闷,请求值班医生到场',info:'值班医生 2 分钟到场,心电监护无异常,处置后回床休息。'},
|
||||
{dev:'10004',to:'10013',time:'2026-09-06 23:41',resp:'2026-09-06 23:42',end:'2026-09-06 23:58',ct:'宿舍呼叫',no:'CQ032144',cs:'已接通',rs:'已完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101室',out:'否',reason:'同事突发头晕',info:'到 101 室处置,测血压 126/78,判定为体位性低血压,补液后缓解。'},
|
||||
{dev:'10007',to:'10015',time:'2026-09-05 21:07',resp:'2026-09-05 21:08',end:'2026-09-05 21:52',ct:'宿舍呼叫',no:'CQ033096',cs:'已接通',rs:'已完成',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 201室',out:'是',amb:'桐园医疗点救护车 1 号',drv:'马永林',med:'值班医生白文斌、护士钱晓芸',adate:'2026-09-05',reason:'自测血压 182/110 伴头痛',info:'值班医生到场复测确认,按高血压危象指引处置并通知管理医生,救护车送医疗点降压治疗后平稳,该事件进入结果异常台账(YC-20260905-05)。'},
|
||||
{dev:'10011',to:'10017',time:'2026-09-05 14:22',resp:'2026-09-05 14:23',end:'2026-09-05 14:50',ct:'床位呼叫',no:'CQ036441',cs:'已接通',rs:'已完成',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1',out:'否',reason:'发热 38.6℃ 需退热处置',info:'靖边医疗点到宿舍处置,物理降温+口服退热,次日随访已退热。'},
|
||||
{dev:'10008',to:'10016',time:'2026-09-04 07:52',resp:'—',end:'2026-09-04 07:58',ct:'床位呼叫',no:'CQ010564',cs:'未接通',rs:'已完成',station:'西峰基地',space:'西峰基地综合宿舍楼 · 301-1',out:'否',reason:'晨起手指麻木请求咨询',info:'首呼未接听,5 分钟后值班室回拨接通,指导活动观察,当日无碍。'},
|
||||
{dev:'10005',to:'10014',time:'2026-09-03 19:36',resp:'2026-09-03 19:37',end:'—',ct:'宿舍呼叫',no:'CQ045130',cs:'已接通',rs:'待处理',station:'麻子山站点',space:'麻子山倒班宿舍楼 · 101室',out:'',reason:'踝扭伤冰敷后疼痛加重',info:'待夜班值班医生到场评估,已电话指导制动抬高。'},
|
||||
{dev:'10010',to:'10016',time:'2026-09-02 12:03',resp:'2026-09-02 12:04',end:'—',ct:'宿舍呼叫',no:'CQ027710',cs:'已接通',rs:'待处理',station:'西峰基地',space:'西峰基地综合宿舍楼 · 402室',out:'',reason:'外伤换药到期提醒请求处理',info:'值班室已派单,14:00 医疗点护士到宿舍换药。'},
|
||||
{dev:'10002',to:'10013',time:'2026-09-01 22:48',resp:'2026-09-01 22:49',end:'2026-09-01 23:05',ct:'床位呼叫',no:'CQ032144',cs:'已接通',rs:'已完成',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-2',out:'否',reason:'失眠需助眠指导',info:'健康指导并纳入随访关注,无需药物介入。'},
|
||||
{dev:'10006',to:'10015',time:'2026-09-07 06:02',resp:'—',end:'—',ct:'床位呼叫',no:'CQ022317',cs:'未接通',rs:'待处理',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 301-1',out:'',reason:'晨起呼叫后设备侧无声',info:'终端未接听,值班员正现场核实;超过 10 分钟将联动告警考核。'},
|
||||
{dev:'10011',to:'10017',time:'2026-09-06 05:36',resp:'2026-09-06 05:37',end:'2026-09-06 06:01',ct:'床位呼叫',no:'CQ029905',cs:'已接通',rs:'已完成',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1',out:'否',reason:'哮喘吸入剂用完需应急配药',info:'值班医生送备用吸入剂并预约复诊。'}
|
||||
];
|
||||
/* 终端呼出:值班室查房呼叫,无救助状态;被呼床位空置时详情不关联员工 */
|
||||
var outgoing=[
|
||||
{from:'HD-FX-01',to:'HC-FX-01',time:'2026-09-07 07:10',resp:'2026-09-07 07:11',end:'2026-09-07 07:13',ct:'语音呼叫',cs:'已接听',no:'CQ021578',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-1'},
|
||||
{from:'HD-FX-01',to:'HC-FX-04',time:'2026-09-06 21:30',resp:'2026-09-06 21:31',end:'2026-09-06 21:35',ct:'视频呼叫',cs:'已接听',no:'CQ032144',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101室'},
|
||||
{from:'HD-MZ-01',to:'HC-MZ-01',time:'2026-09-06 20:05',resp:'—',end:'—',ct:'语音呼叫',cs:'未接听',no:'CQ045130',station:'麻子山站点',space:'麻子山倒班宿舍楼 · 101室'},
|
||||
{from:'HD-TY-01',to:'HC-TY-01',time:'2026-09-05 19:50',resp:'2026-09-05 19:51',end:'2026-09-05 19:54',ct:'视频呼叫',cs:'已接听',no:'CQ022317',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 301-1'},
|
||||
{from:'HD-TY-01',to:'HC-TY-02',time:'2026-09-05 08:20',resp:'—',end:'2026-09-05 08:21',ct:'一键呼叫',cs:'已挂断',no:'CQ033096',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 201室'},
|
||||
{from:'HD-XF-01',to:'HC-XF-02',time:'2026-09-04 18:40',resp:'—',end:'—',ct:'语音呼叫',cs:'未接听',no:'',station:'西峰基地',space:'西峰基地综合宿舍楼 · 302-1'},
|
||||
{from:'HD-JB-01',to:'HC-JB-01',time:'2026-09-04 07:15',resp:'2026-09-04 07:16',end:'2026-09-04 07:19',ct:'视频呼叫',cs:'已接听',no:'CQ036441',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1'},
|
||||
{from:'HD-JB-01',to:'HC-JB-01',time:'2026-09-01 20:30',resp:'2026-09-01 20:31',end:'2026-09-01 20:36',ct:'语音呼叫',cs:'已接听',no:'CQ029905',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1'}
|
||||
{from:'10013',to:'10001',time:'2026-09-07 07:10',resp:'2026-09-07 07:11',end:'2026-09-07 07:13',ct:'语音呼叫',cs:'已接听',no:'CQ021578',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101-1'},
|
||||
{from:'10013',to:'10004',time:'2026-09-06 21:30',resp:'2026-09-06 21:31',end:'2026-09-06 21:35',ct:'视频呼叫',cs:'已接听',no:'CQ032144',station:'樊学站点基地',space:'樊学倒班宿舍1号楼 · 101室'},
|
||||
{from:'10014',to:'10005',time:'2026-09-06 20:05',resp:'—',end:'—',ct:'语音呼叫',cs:'未接听',no:'CQ045130',station:'麻子山站点',space:'麻子山倒班宿舍楼 · 101室'},
|
||||
{from:'10015',to:'10006',time:'2026-09-05 19:50',resp:'2026-09-05 19:51',end:'2026-09-05 19:54',ct:'视频呼叫',cs:'已接听',no:'CQ022317',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 301-1'},
|
||||
{from:'10015',to:'10007',time:'2026-09-05 08:20',resp:'—',end:'2026-09-05 08:21',ct:'一键呼叫',cs:'已挂断',no:'CQ033096',station:'桐园站点基地',space:'桐园倒班宿舍1号楼 · 201室'},
|
||||
{from:'10016',to:'10009',time:'2026-09-04 18:40',resp:'—',end:'—',ct:'语音呼叫',cs:'未接听',no:'',station:'西峰基地',space:'西峰基地综合宿舍楼 · 302-1'},
|
||||
{from:'10017',to:'10011',time:'2026-09-04 07:15',resp:'2026-09-04 07:16',end:'2026-09-04 07:19',ct:'视频呼叫',cs:'已接听',no:'CQ036441',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1'},
|
||||
{from:'10017',to:'10011',time:'2026-09-01 20:30',resp:'2026-09-01 20:31',end:'2026-09-01 20:36',ct:'语音呼叫',cs:'已接听',no:'CQ029905',station:'靖边气田站点',space:'靖边气田倒班宿舍楼 · 401-1'}
|
||||
];
|
||||
var inBadge={'已接通':'badge--success','未接通':'badge--danger'};
|
||||
var rsBadge={'等待处理':'badge--warning','处理中':'badge--info','处理完成':'badge--success'};
|
||||
var rsBadge={'待处理':'badge--warning','已完成':'badge--success'};
|
||||
var outBadge={'已接听':'badge--success','未接听':'badge--danger','已挂断':'badge--muted'};
|
||||
function headIn(){return '<tr><th style="width:56px;">序号</th><th style="min-width:100px;">主呼设备</th><th style="min-width:140px;">呼叫时间</th><th>呼叫形式</th><th>员工姓名</th><th>员工编号</th><th>呼叫状态</th><th>救助状态</th><th style="min-width:120px;">站点名称</th><th style="min-width:200px;">宿舍/床位信息</th><th style="width:80px;">操作</th></tr>';}
|
||||
function headOut(){return '<tr><th style="width:56px;">序号</th><th style="min-width:100px;">主呼设备</th><th style="min-width:100px;">被呼设备</th><th style="min-width:140px;">呼叫时间</th><th>呼叫形式</th><th>呼叫状态</th><th style="min-width:120px;">站点名称</th><th style="min-width:200px;">宿舍/床位信息</th><th style="width:80px;">操作</th></tr>';}
|
||||
/* 值班室终端名称映射:终端呼叫时主呼设备(呼叫设备编号)对应的终端名称 */
|
||||
var termNames={'10013':'樊学值班室终端','10014':'麻子山值班室终端','10015':'桐园值班室终端','10016':'西峰值班室终端','10017':'靖边值班室终端'};
|
||||
function headIn(){return '<tr><th style="width:56px;">序号</th><th style="min-width:110px;">呼叫设备编号</th><th>员工姓名</th><th>员工编号</th><th style="min-width:140px;">所属单位部门</th><th style="min-width:140px;">呼叫时间</th><th>呼叫状态</th><th style="min-width:110px;">被呼设备编号</th><th>救助状态</th><th style="min-width:120px;">站点名称</th><th style="min-width:140px;">楼栋名称</th><th style="min-width:120px;">宿舍/床位信息</th><th style="width:80px;">操作</th></tr>';}
|
||||
function headOut(){return '<tr><th style="width:56px;">序号</th><th style="min-width:110px;">呼叫设备编号</th><th style="min-width:140px;">终端名称</th><th style="min-width:140px;">呼叫时间</th><th>呼叫状态</th><th style="min-width:110px;">被呼设备编号</th><th style="min-width:120px;">宿舍/床位信息</th><th style="min-width:140px;">楼栋名称</th><th style="min-width:120px;">站点名称</th><th style="width:80px;">操作</th></tr>';}
|
||||
function renderAll(){
|
||||
var f=document.getElementById('filterFrom').value,t=document.getElementById('filterTo').value;
|
||||
if(curTab==='in'){
|
||||
@@ -304,24 +304,24 @@ function renderAll(){
|
||||
});
|
||||
document.getElementById('thead').innerHTML=headIn();
|
||||
document.getElementById('tbody').innerHTML=list.map(function(r){
|
||||
var i=incoming.indexOf(r),p=empProfiles[r.no]||{};
|
||||
return '<tr><td>'+(i+1)+'</td><td style="font-family:monospace;">'+r.dev+'</td><td>'+r.time+'</td><td>'+r.ct+'</td><td>'+(p.name||'—')+'</td><td>'+r.no+'</td>'
|
||||
+'<td><span class="badge '+inBadge[r.cs]+'">'+r.cs+'</span></td><td><span class="badge '+rsBadge[r.rs]+'">'+r.rs+'</span></td>'
|
||||
+'<td>'+r.station+'</td><td>'+r.space+'</td><td><button class="btn btn-link" onclick="openDetailIn('+i+')">详细信息</button></td></tr>';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="11">没有符合条件的呼叫记录</td></tr>';
|
||||
var i=incoming.indexOf(r),p=empProfiles[r.no]||{},seg=r.space.split(' · ');
|
||||
return '<tr><td>'+(i+1)+'</td><td style="font-family:monospace;">'+r.dev+'</td><td>'+(p.name||'—')+'</td><td>'+r.no+'</td><td>'+(p.unit?p.unit+'/'+p.dept:'—')+'</td><td>'+r.time+'</td>'
|
||||
+'<td><span class="badge '+inBadge[r.cs]+'">'+r.cs+'</span></td><td style="font-family:monospace;">'+(r.cs==='未接通'?'-':r.to)+'</td><td><span class="badge '+rsBadge[r.rs]+'">'+r.rs+'</span></td>'
|
||||
+'<td>'+r.station+'</td><td>'+seg[0]+'</td><td>'+seg[1]+'</td><td><button class="btn btn-link" onclick="openDetailIn('+i+')">详细信息</button></td></tr>';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="13">没有符合条件的呼叫记录</td></tr>';
|
||||
document.getElementById('totalTip').textContent='共 '+list.length+' 条记录';
|
||||
}else{
|
||||
var kf=document.getElementById('outFrom').value.trim().toUpperCase();
|
||||
var kt=document.getElementById('outTo').value.trim().toUpperCase();
|
||||
var tc2=document.getElementById('outType').value,sc2=document.getElementById('outStatus').value;
|
||||
var list2=outgoing.filter(function(r){return r.from.toUpperCase().indexOf(kf)>=0&&r.to.toUpperCase().indexOf(kt)>=0&&(!tc2||r.ct===tc2)&&(!sc2||r.cs===sc2)&&r.time.slice(0,10)>=f&&r.time.slice(0,10)<=t;});
|
||||
var sc2=document.getElementById('outStatus').value;
|
||||
var list2=outgoing.filter(function(r){return r.from.toUpperCase().indexOf(kf)>=0&&(!sc2||r.cs===sc2)&&r.time.slice(0,10)>=f&&r.time.slice(0,10)<=t;});
|
||||
document.getElementById('thead').innerHTML=headOut();
|
||||
document.getElementById('tbody').innerHTML=list2.map(function(r){
|
||||
var i=outgoing.indexOf(r);
|
||||
return '<tr><td>'+(i+1)+'</td><td style="font-family:monospace;">'+r.from+'</td><td style="font-family:monospace;">'+r.to+'</td><td>'+r.time+'</td><td>'+r.ct+'</td>'
|
||||
+'<td><span class="badge '+outBadge[r.cs]+'">'+r.cs+'</span></td><td>'+r.station+'</td><td>'+r.space+'</td>'
|
||||
var i=outgoing.indexOf(r),seg=r.space.split(' · ');
|
||||
return '<tr><td>'+(i+1)+'</td><td style="font-family:monospace;">'+r.from+'</td><td>'+(termNames[r.from]||'—')+'</td><td>'+r.time+'</td>'
|
||||
+'<td><span class="badge '+outBadge[r.cs]+'">'+r.cs+'</span></td><td style="font-family:monospace;">'+r.to+'</td>'
|
||||
+'<td>'+seg[1]+'</td><td>'+seg[0]+'</td><td>'+r.station+'</td>'
|
||||
+'<td><button class="btn btn-link" onclick="openDetailOut('+i+')">详细信息</button></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='共 '+list2.length+' 条记录';
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,7 @@ function switchTab(t){
|
||||
renderAll();
|
||||
}
|
||||
function resetFilter(){
|
||||
['inName','inNo','inDev','inType','inStatus','inRescue','outFrom','outTo','outType','outStatus'].forEach(function(id){document.getElementById(id).value='';});
|
||||
['inName','inNo','inDev','inType','inStatus','inRescue','outFrom','outStatus'].forEach(function(id){document.getElementById(id).value='';});
|
||||
document.getElementById('filterFrom').value='2026-09-01';
|
||||
document.getElementById('filterTo').value='2026-09-07';
|
||||
renderAll();
|
||||
@@ -364,18 +364,19 @@ function dormCard(r){
|
||||
function openDetailIn(i){
|
||||
var r=incoming[i];
|
||||
var stage=r.cs==='已接通'?'<span class="badge badge--success">已接听</span>':'<span class="badge badge--danger">未接听</span>';
|
||||
var emg=r.rs==='处理完成'
|
||||
? '<div class="info-card">'
|
||||
+ik('是否出勤',r.out==='是'?'是':'否')
|
||||
+ik('出勤救护车',r.amb||'—')+ik('出勤司机',r.drv||'—')+ik('随车医护',r.med||'—')+ik('出勤日期',r.adate||'—')
|
||||
+ik('救助信息',r.info,true)+'</div>'
|
||||
var emg=r.rs==='已完成'
|
||||
? (r.out==='是'
|
||||
? '<div class="info-card">'
|
||||
+ik('是否出勤','是')+ik('出勤救护车',r.amb||'—')+ik('出勤司机',r.drv||'—')+ik('随车医护',r.med||'—')+ik('出勤日期',r.adate||'—')
|
||||
+ik('救助信息',r.info,true)+'</div>'
|
||||
: '<div class="info-card">'+ik('是否出勤','否')+ik('救助信息',r.info,true)+'</div>')
|
||||
: '<div class="info-card info-card--grey" style="grid-template-columns:1fr;"><div class="info-empty">暂无急救信息</div></div>';
|
||||
document.getElementById('detailBody').innerHTML=
|
||||
'<div class="ct-head">'+r.ct+' '+stage+'<span class="st">'+r.rs+'</span></div>'
|
||||
'<div class="ct-head"><span class="ct-head__title">'+r.ct+' '+stage+'</span><span class="ct-head__status">救助状态:<span class="badge '+rsBadge[r.rs]+'">'+r.rs+'</span></span></div>'
|
||||
+'<div class="detail-grid">'
|
||||
+dk('呼叫时间',r.time)+dk('响应时间',r.resp)
|
||||
+dk('结束时间',r.end)+dk('呼叫类型',r.ct)
|
||||
+dk('主呼设备',r.dev)+dk('被呼设备',r.to)
|
||||
+dk('呼叫时间',r.time)+dk('接听时间',r.cs==='未接通'?'-':r.resp)
|
||||
+dk('结束时间',r.cs==='未接通'?'-':r.end)+dk('呼叫类型',r.ct)
|
||||
+dk('呼叫设备编号',r.dev)+dk('被呼设备编号',r.cs==='未接通'?'-':r.to)
|
||||
+'</div>'
|
||||
+'<div class="form-section">员工信息</div>'+empCard(r.no)
|
||||
+'<div class="form-section">宿舍信息</div>'+dormCard(r)
|
||||
@@ -385,17 +386,14 @@ function openDetailIn(i){
|
||||
/* 呼出工单详情:无救助处置环节,急救信息固定占位 */
|
||||
function openDetailOut(i){
|
||||
var r=outgoing[i];
|
||||
var stage=r.cs==='已接听'?'<span class="badge badge--success">已接听</span>':'<span class="badge badge--danger">未接听</span>';
|
||||
var seg=r.space.split(' · ');
|
||||
document.getElementById('detailBody').innerHTML=
|
||||
'<div class="ct-head">'+r.ct+' '+stage+'<span class="st">'+r.cs+'</span></div>'
|
||||
+'<div class="detail-grid">'
|
||||
+dk('呼叫时间',r.time)+dk('响应时间',r.resp)
|
||||
+dk('结束时间',r.end)+dk('呼叫类型',r.ct)
|
||||
+dk('主呼设备',r.from)+dk('被呼设备',r.to)
|
||||
+'</div>'
|
||||
+'<div class="form-section">员工信息</div>'+empCard(r.no)
|
||||
+'<div class="form-section">宿舍信息</div>'+dormCard(r)
|
||||
+'<div class="form-section">急救信息</div><div class="info-card info-card--grey" style="grid-template-columns:1fr;"><div class="info-empty">暂无急救信息</div></div>';
|
||||
'<div class="detail-grid">'
|
||||
+dk('呼叫设备编号',r.from)+dk('终端名称',termNames[r.from]||'—')
|
||||
+dk('呼叫时间',r.time)+dk('呼叫状态','<span class="badge '+outBadge[r.cs]+'">'+r.cs+'</span>')
|
||||
+dk('被呼设备编号',r.to)+dk('宿舍/床位信息',seg[1])
|
||||
+dk('站点名称',r.station)+dk('楼栋名称',seg[0])
|
||||
+'</div>';
|
||||
document.getElementById('detailMask').classList.add('open');
|
||||
}
|
||||
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
|
||||
|
||||
@@ -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:'10001',mac:'00:1A:2B:3C:4D:01',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-1',time:'2026-03-05'},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10002',mac:'00:1A:2B:3C:4D:02',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 101-2',time:'2026-03-05'},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10003',mac:'00:1A:2B:3C:4D:03',alloc:1,type:2,space:'樊学倒班宿舍1号楼 · 102-1',time:'2026-03-05'},
|
||||
{unit:'采油一厂',depart:'樊学作业区',code:'10004',mac:'00:1A:2B:3C:4D:04',alloc:1,type:1,space:'樊学倒班宿舍1号楼 · 101室',time:'2026-03-05'},
|
||||
{unit:'采油一厂',depart:'麻子山作业区',code:'10005',mac:'00:1A:2B:3C:4D:05',alloc:1,type:1,space:'麻子山倒班宿舍楼 · 101室',time:'2026-03-19'},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'10006',mac:'00:1A:2B:3C:4D:06',alloc:1,type:2,space:'桐园倒班宿舍1号楼 · 301-1',time:'2026-04-10'},
|
||||
{unit:'采油二厂',depart:'桐园作业区',code:'10007',mac:'00:1A:2B:3C:4D:07',alloc:1,type:1,space:'桐园倒班宿舍1号楼 · 201室',time:'2026-04-10'},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10008',mac:'00:1A:2B:3C:4D:08',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 301-1',time:'2026-05-12'},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10009',mac:'00:1A:2B:3C:4D:09',alloc:1,type:2,space:'西峰基地综合宿舍楼 · 302-1',time:'2026-05-12'},
|
||||
{unit:'第四采油厂',depart:'西峰作业区',code:'10010',mac:'00:1A:2B:3C:4D:0A',alloc:1,type:1,space:'西峰基地综合宿舍楼 · 402室',time:'2026-05-12'},
|
||||
{unit:'采气一厂',depart:'靖边气田',code:'10011',mac:'00:1A:2B:3C:4D:0B',alloc:1,type:2,space:'靖边气田倒班宿舍楼 · 401-1',time:'2026-06-24'},
|
||||
{unit:'采气一厂',depart:'靖边气田',code:'10012',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>
|
||||
|
||||
@@ -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':'离线'};
|
||||
/* 呼叫设备:分配关系与「分配管理」「床位信息」勾稽(10003 离线见告警记录演示) */
|
||||
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:'10001',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:'10002',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:'10003',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:'10004',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:'10005',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:'10006',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:'10007',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:'10008',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:'10009',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:'10010',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:'10011',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:'10012',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:'10013',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:'10014',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:'10015',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:'10016',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:'10017',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>
|
||||
|
||||
@@ -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);}
|
||||
@@ -90,6 +102,15 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
|
||||
.form-control{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);color:var(--text-primary);outline:none;width:100%;}
|
||||
.form-control:focus{border-color:var(--primary);}
|
||||
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;}
|
||||
/* [交互] 楼栋搜索下拉(新增/编辑宿舍):下拉项显示楼栋名称 + 所属站点 */
|
||||
.building-search{position:relative;}
|
||||
.building-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;}
|
||||
.building-search__panel.open{display:block;}
|
||||
.building-search__item{padding:8px 12px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:8px;}
|
||||
.building-search__item:hover{background:#F0F8FF;}
|
||||
.building-search__item .ss-name{color:var(--text-primary);font-weight:500;}
|
||||
.building-search__item .ss-org{color:var(--text-placeholder);font-size:var(--font-xs);}
|
||||
.building-search__empty{padding:16px 12px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
.upload-box{border:1px dashed var(--border);border-radius:var(--radius-md);padding:28px 16px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);cursor:pointer;transition:all .2s;background:#FAFAFA;}
|
||||
.upload-box:hover{border-color:var(--primary);color:var(--primary);background:#F0F8FF;}
|
||||
.upload-box svg{display:block;margin:0 auto 8px;}
|
||||
@@ -152,6 +173,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 +235,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>分配类型</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>
|
||||
@@ -209,7 +252,10 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
<div class="modal__title" id="editTitle">新增宿舍信息</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label"><span class="req">*</span> 所属楼栋</span>
|
||||
<select class="form-control" id="eBuilding"><option value="">请选择楼栋</option></select>
|
||||
<div class="building-search" id="buildingSearch">
|
||||
<input class="form-control" id="eBuilding" placeholder="请输入楼栋名称搜索" autocomplete="off" oninput="onBuildingInput()" onfocus="onBuildingFocus()">
|
||||
<div class="building-search__panel" id="buildingPanel"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-item">
|
||||
@@ -218,7 +264,7 @@ 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>
|
||||
<input class="form-control" id="eBeds" type="number" min="1" max="8" placeholder="1-8">
|
||||
<input class="form-control" id="eBeds" type="number" min="1" max="8" placeholder="如:4">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@@ -263,12 +309,12 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 床位数量下钻:该宿舍的床位清单(列与「床位信息」一致) -->
|
||||
<!-- [交互] 床位数量下钻:该宿舍的床位清单(床位→员工、床位→床头设备绑定) -->
|
||||
<div class="modal-mask" id="bedMask">
|
||||
<div class="modal--list">
|
||||
<div class="modal__title" id="bedTitle">宿舍床位清单</div>
|
||||
<table class="record-table">
|
||||
<thead><tr><th>床位编号</th><th>是否分配员工</th><th>员工姓名</th><th>员工编号</th><th>员工所属单位</th><th>员工所属部门</th><th>员工联系方式</th></tr></thead>
|
||||
<thead><tr><th>床位编号</th><th>是否分配设备</th><th>设备编号</th><th>是否分配员工</th><th>员工姓名</th><th>员工编号</th><th>员工所属单位</th><th>员工所属部门</th><th>员工联系方式</th></tr></thead>
|
||||
<tbody id="bedTbody"></tbody>
|
||||
</table>
|
||||
<div class="modal__footer"><button class="btn btn-default" onclick="closeBed()">关闭</button></div>
|
||||
@@ -302,22 +348,35 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
|
||||
<script>
|
||||
/* 楼栋清单与「楼栋信息」页勾稽 */
|
||||
var buildings=['樊学倒班宿舍1号楼','麻子山倒班宿舍楼','桐园倒班宿舍1号楼','西峰基地综合宿舍楼','靖边气田倒班宿舍楼'];
|
||||
/* alloc:绑定到宿舍本身的设备(房间级呼叫);床位级绑定见「分配管理」 */
|
||||
/* 单位→部门→站点三级映射与楼栋归属,与「楼栋信息」页口径一致 */
|
||||
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
|
||||
var depStations={'樊学作业区':['樊学站点基地'],'麻子山作业区':['麻子山站点'],'桐园作业区':['桐园站点基地'],'姬圪台作业区':['姬圪台站点'],'西峰作业区':['西峰基地'],'靖边气田':['靖边气田站点']};
|
||||
var buildingMeta={
|
||||
'樊学倒班宿舍1号楼':{station:'樊学站点基地',unit:'采油一厂',depart:'樊学作业区'},
|
||||
'麻子山倒班宿舍楼':{station:'麻子山站点',unit:'采油一厂',depart:'麻子山作业区'},
|
||||
'桐园倒班宿舍1号楼':{station:'桐园站点基地',unit:'采油二厂',depart:'桐园作业区'},
|
||||
'西峰基地综合宿舍楼':{station:'西峰基地',unit:'第四采油厂',depart:'西峰作业区'},
|
||||
'靖边气田倒班宿舍楼':{station:'靖边气田站点',unit:'采气一厂',depart:'靖边气田'}
|
||||
};
|
||||
/* 楼栋搜索下拉数据:楼栋名称 + 所属站点 */
|
||||
var buildingList=buildings.map(function(b){var m=buildingMeta[b];return {name:b,station:m.station,unit:m.unit,depart:m.depart};});
|
||||
var selUnit='',selDept='';
|
||||
/* 分配设备:宿舍型=房间一键呼叫面板(单台);床位型=床头呼叫按钮(可多床多台,编号逗号分隔) */
|
||||
var dorms=[
|
||||
{name:'101室',building:'樊学倒班宿舍1号楼',beds:4,manager:'石维国',mobile:'13991203456',alloc:1,device:'HC-FX-04'},
|
||||
{name:'102室',building:'樊学倒班宿舍1号楼',beds:4,manager:'李桂芳',mobile:'13991207788',alloc:0,device:''},
|
||||
{name:'201室',building:'樊学倒班宿舍1号楼',beds:2,manager:'—',mobile:'—',alloc:0,device:''},
|
||||
{name:'101室',building:'麻子山倒班宿舍楼',beds:4,manager:'杨慧敏',mobile:'13991345012',alloc:1,device:'HC-MZ-01'},
|
||||
{name:'102室',building:'麻子山倒班宿舍楼',beds:2,manager:'—',mobile:'—',alloc:0,device:''},
|
||||
{name:'201室',building:'桐园倒班宿舍1号楼',beds:2,manager:'赵万福',mobile:'13991567208',alloc:1,device:'HC-TY-02'},
|
||||
{name:'301室',building:'桐园倒班宿舍1号楼',beds:4,manager:'—',mobile:'—',alloc:0,device:''},
|
||||
{name:'302室',building:'桐园倒班宿舍1号楼',beds:2,manager:'—',mobile:'—',alloc:0,device:''},
|
||||
{name:'301室',building:'西峰基地综合宿舍楼',beds:4,manager:'高建军',mobile:'13991784320',alloc:0,device:''},
|
||||
{name:'302室',building:'西峰基地综合宿舍楼',beds:4,manager:'闫婷',mobile:'13991784531',alloc:0,device:''},
|
||||
{name:'401室',building:'西峰基地综合宿舍楼',beds:2,manager:'—',mobile:'—',alloc:0,device:''},
|
||||
{name:'402室',building:'西峰基地综合宿舍楼',beds:4,manager:'徐立成',mobile:'13991784906',alloc:1,device:'HC-XF-03'},
|
||||
{name:'401室',building:'靖边气田倒班宿舍楼',beds:4,manager:'刘玉梅',mobile:'13991923745',alloc:0,device:''},
|
||||
{name:'402室',building:'靖边气田倒班宿舍楼',beds:2,manager:'呼建国',mobile:'13991923870',alloc:0,device:''}
|
||||
{name:'101室',building:'樊学倒班宿舍1号楼',beds:4,manager:'石维国',mobile:'13991203456',alloc:1,allocType:'床位',device:'10001,10002'},
|
||||
{name:'102室',building:'樊学倒班宿舍1号楼',beds:4,manager:'李桂芳',mobile:'13991207788',alloc:0,allocType:'',device:''},
|
||||
{name:'201室',building:'樊学倒班宿舍1号楼',beds:2,manager:'—',mobile:'—',alloc:0,allocType:'',device:''},
|
||||
{name:'101室',building:'麻子山倒班宿舍楼',beds:4,manager:'杨慧敏',mobile:'13991345012',alloc:1,allocType:'宿舍',device:'10005'},
|
||||
{name:'102室',building:'麻子山倒班宿舍楼',beds:2,manager:'—',mobile:'—',alloc:0,allocType:'',device:''},
|
||||
{name:'201室',building:'桐园倒班宿舍1号楼',beds:2,manager:'赵万福',mobile:'13991567208',alloc:1,allocType:'宿舍',device:'10007'},
|
||||
{name:'301室',building:'桐园倒班宿舍1号楼',beds:4,manager:'—',mobile:'—',alloc:0,allocType:'',device:''},
|
||||
{name:'302室',building:'桐园倒班宿舍1号楼',beds:2,manager:'—',mobile:'—',alloc:0,allocType:'',device:''},
|
||||
{name:'301室',building:'西峰基地综合宿舍楼',beds:4,manager:'高建军',mobile:'13991784320',alloc:0,allocType:'',device:''},
|
||||
{name:'302室',building:'西峰基地综合宿舍楼',beds:4,manager:'闫婷',mobile:'13991784531',alloc:0,allocType:'',device:''},
|
||||
{name:'401室',building:'西峰基地综合宿舍楼',beds:2,manager:'—',mobile:'—',alloc:0,allocType:'',device:''},
|
||||
{name:'402室',building:'西峰基地综合宿舍楼',beds:4,manager:'徐立成',mobile:'13991784906',alloc:1,allocType:'宿舍',device:'10010'},
|
||||
{name:'401室',building:'靖边气田倒班宿舍楼',beds:4,manager:'刘玉梅',mobile:'13991923745',alloc:0,allocType:'',device:''},
|
||||
{name:'402室',building:'靖边气田倒班宿舍楼',beds:2,manager:'呼建国',mobile:'13991923870',alloc:0,allocType:'',device:''}
|
||||
];
|
||||
/* [交互] 床位下钻演示数据:员工池与「床位信息」页一致(key=楼栋|床位编号) */
|
||||
var empLite={
|
||||
@@ -340,27 +399,76 @@ var bedAssigns={
|
||||
'西峰基地综合宿舍楼|301-1':'CQ010564','西峰基地综合宿舍楼|301-2':'CQ041822','西峰基地综合宿舍楼|302-1':'CQ044871','西峰基地综合宿舍楼|402-1':'CQ027710',
|
||||
'靖边气田倒班宿舍楼|401-1':'CQ036441','靖边气田倒班宿舍楼|401-2':'CQ029905'
|
||||
};
|
||||
/* 床位→床头设备(key=楼栋|床位编号,与「床位信息」页一致) */
|
||||
var bedDevices={
|
||||
'樊学倒班宿舍1号楼|101-1':'10001','樊学倒班宿舍1号楼|101-2':'10002','樊学倒班宿舍1号楼|102-1':'10003',
|
||||
'桐园倒班宿舍1号楼|301-1':'10006','西峰基地综合宿舍楼|301-1':'10008','西峰基地综合宿舍楼|302-1':'10009',
|
||||
'靖边气田倒班宿舍楼|401-1':'10011'
|
||||
};
|
||||
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 deviceText(r){
|
||||
return r.alloc ? r.device : '-';
|
||||
}
|
||||
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>'+(r.alloc?(r.allocType==='床位'?'<span class="badge badge--info">床位</span>':'<span class="badge badge--success">宿舍</span>'):'-')+'</td>'
|
||||
+'<td style="font-family:monospace;">'+deviceText(r)+'</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>';
|
||||
document.getElementById('totalTip').textContent='共 '+list.length+' 间 · 房间级设备 '+dorms.filter(function(r){return r.alloc;}).length+' 台';
|
||||
}).join('')||'<tr><td class="td-empty" colspan="13">没有符合条件的宿舍</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(){
|
||||
@@ -374,6 +482,23 @@ function batchDelete(){
|
||||
document.getElementById('confirmText').textContent='确认删除选中的 '+ids.length+' 间宿舍?其下床位将一并失效。';
|
||||
document.getElementById('confirmMask').classList.add('open');
|
||||
}
|
||||
/* [交互] 楼栋搜索下拉(新增/编辑宿舍) */
|
||||
var selBuilding='';
|
||||
function onBuildingInput(){selBuilding='';renderBuildingPanel();}
|
||||
function onBuildingFocus(){renderBuildingPanel();}
|
||||
function renderBuildingPanel(){
|
||||
var q=document.getElementById('eBuilding').value.trim();
|
||||
var list=buildingList.filter(function(b){return !q||b.name.indexOf(q)>=0;});
|
||||
document.getElementById('buildingPanel').innerHTML=list.map(function(b){
|
||||
return '<div class="building-search__item" onclick="pickBuilding(\''+b.name+'\')"><span class="ss-name">'+b.name+'</span><span class="ss-org">'+b.station+'</span></div>';
|
||||
}).join('')||'<div class="building-search__empty">未查询到楼栋</div>';
|
||||
document.getElementById('buildingPanel').classList.add('open');
|
||||
}
|
||||
function pickBuilding(name){
|
||||
selBuilding=name;
|
||||
document.getElementById('eBuilding').value=name;
|
||||
document.getElementById('buildingPanel').classList.remove('open');
|
||||
}
|
||||
/* [交互] 新增/编辑 */
|
||||
var editIdx=-1;
|
||||
function openEdit(i){
|
||||
@@ -381,21 +506,24 @@ function openEdit(i){
|
||||
document.getElementById('editTitle').textContent=i<0?'新增宿舍信息':'编辑宿舍信息';
|
||||
if(i>=0){
|
||||
var r=dorms[i];
|
||||
selBuilding=r.building;
|
||||
document.getElementById('eBuilding').value=r.building;document.getElementById('eName').value=r.name;
|
||||
document.getElementById('eBeds').value=r.beds;document.getElementById('eManager').value=r.manager==='—'?'':r.manager;
|
||||
document.getElementById('eMobile').value=r.mobile==='—'?'':r.mobile;
|
||||
}else{
|
||||
selBuilding='';
|
||||
document.getElementById('eBuilding').value='';['eName','eBeds','eManager','eMobile'].forEach(function(id){document.getElementById(id).value='';});
|
||||
}
|
||||
document.getElementById('buildingPanel').classList.remove('open');
|
||||
document.getElementById('editMask').classList.add('open');
|
||||
}
|
||||
function closeEdit(){document.getElementById('editMask').classList.remove('open');}
|
||||
function saveEdit(){
|
||||
var b=document.getElementById('eBuilding').value,n=document.getElementById('eName').value.trim(),beds=parseInt(document.getElementById('eBeds').value,10);
|
||||
if(!b||!n||!beds||beds<1||beds>8){showToast('请完整填写,床位数量须在 1-8 之间');return;}
|
||||
var b=selBuilding||document.getElementById('eBuilding').value.trim(),n=document.getElementById('eName').value.trim(),beds=parseInt(document.getElementById('eBeds').value,10);
|
||||
if(!b||!buildingMeta[b]||!n||!beds||beds<1||beds>8){showToast('请完整填写,床位数量须在 1-8 之间');return;}
|
||||
var dup=dorms.some(function(r,i){return i!==editIdx&&r.building===b&&r.name===n;});
|
||||
if(dup){showToast('该楼栋下已存在同名宿舍');return;}
|
||||
var obj={name:n,building:b,beds:beds,manager:document.getElementById('eManager').value.trim()||'—',mobile:document.getElementById('eMobile').value.trim()||'—',alloc:editIdx>=0?dorms[editIdx].alloc:0,device:editIdx>=0?dorms[editIdx].device:''};
|
||||
var obj={name:n,building:b,beds:beds,manager:document.getElementById('eManager').value.trim()||'—',mobile:document.getElementById('eMobile').value.trim()||'—',alloc:editIdx>=0?dorms[editIdx].alloc:0,allocType:editIdx>=0?dorms[editIdx].allocType:'',device:editIdx>=0?dorms[editIdx].device:''};
|
||||
if(editIdx<0)dorms.push(obj);else dorms[editIdx]=obj;
|
||||
closeEdit();renderTable();showToast(editIdx<0?'宿舍已新增(将生成 '+beds+' 条床位)':'宿舍已更新');
|
||||
}
|
||||
@@ -429,7 +557,10 @@ function openBed(i){
|
||||
var room=r.name.replace('室',''),rows='';
|
||||
for(var nn=1;nn<=r.beds;nn++){
|
||||
var code=room+'-'+nn,no=bedAssigns[r.building+'|'+code],e=no?empLite[no]:null;
|
||||
var dev=bedDevices[r.building+'|'+code];
|
||||
rows+='<tr><td style="font-family:monospace;">'+code+'</td>'
|
||||
+(dev?'<td><span class="badge badge--success">是</span></td><td style="font-family:monospace;">'+dev+'</td>'
|
||||
:'<td><span class="badge badge--muted">否</span></td><td>—</td>')
|
||||
+(e?'<td><span class="badge badge--success">是</span></td><td>'+e.name+'</td><td>'+no+'</td><td>'+e.unit+'</td><td>'+e.depart+'</td><td>'+e.phone+'</td>'
|
||||
:'<td><span class="badge badge--muted">否</span></td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td>')
|
||||
+'</tr>';
|
||||
@@ -459,6 +590,13 @@ 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');}
|
||||
var bs=document.getElementById('buildingSearch');
|
||||
if(bs&&!bs.contains(e.target)){document.getElementById('buildingPanel').classList.remove('open');}
|
||||
});
|
||||
initFilters();renderTable();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -136,7 +136,7 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item">
|
||||
<label>告警设备</label>
|
||||
<input class="filter-input" id="filterDev" placeholder="如:HC-FX-03" oninput="render()">
|
||||
<input class="filter-input" id="filterDev" placeholder="请输入" oninput="render()">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>宿舍名称</label>
|
||||
@@ -194,14 +194,14 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script>
|
||||
/* 告警数据:与呼叫记录/设备列表跨页勾稽——徐立成未接通升级、杨慧敏超时未派单、HC-FX-03 与 HD-XF-01 离线 */
|
||||
/* 告警数据:与呼叫记录/设备列表跨页勾稽——徐立成未接通升级、杨慧敏超时未派单、10003 与 10016 离线 */
|
||||
var alarms=[
|
||||
{time:'2026-09-07 06:14',type:'呼叫超时未接听',reason:'床位呼叫(HC-TY-01)发起后 10 分钟内值班终端未接听,关联 2026-09-07 06:02 徐立成(CQ022317)呼入',dev:'HC-TY-01',dorm:'桐园倒班宿舍1号楼 · 301室'},
|
||||
{time:'2026-09-06 20:16',type:'呼叫超时未接听',reason:'值班终端(HD-MZ-01)呼出语音呼叫 10 分钟内被呼侧未接听,员工巡检途中未携房卡',dev:'HC-MZ-01',dorm:'麻子山倒班宿舍楼 · 101室'},
|
||||
{time:'2026-09-04 18:51',type:'呼叫超时未接听',reason:'值班终端(HD-XF-01)呼出语音呼叫 10 分钟内被呼侧未接听,被呼员工外出就餐',dev:'HC-XF-02',dorm:'西峰基地综合宿舍楼 · 302室'},
|
||||
{time:'2026-09-04 09:20',type:'设备离线告警',reason:'呼叫设备心跳丢失超过 15 分钟,判定线路老化已派单维修(102-1 曲志明床位)',dev:'HC-FX-03',dorm:'樊学倒班宿舍1号楼 · 102室'},
|
||||
{time:'2026-09-03 20:08',type:'救助处置超时',reason:'呼入已接通但 30 分钟内未派单到场处置,关联 2026-09-03 19:36 杨慧敏(CQ045130)踝扭伤呼入',dev:'HC-MZ-01',dorm:'麻子山倒班宿舍楼 · 101室'},
|
||||
{time:'2026-09-02 23:47',type:'设备离线告警',reason:'值班终端离线超过 15 分钟,疑似电源适配器故障,呼入转接至备用座机',dev:'HD-XF-01',dorm:'西峰基地值班室'}
|
||||
{time:'2026-09-07 06:14',type:'呼叫超时未接听',reason:'床位呼叫(10006)发起后 10 分钟内值班终端未接听,关联 2026-09-07 06:02 徐立成(CQ022317)呼入',dev:'10006',dorm:'桐园倒班宿舍1号楼 · 301室'},
|
||||
{time:'2026-09-06 20:16',type:'呼叫超时未接听',reason:'值班终端(10014)呼出语音呼叫 10 分钟内被呼侧未接听,员工巡检途中未携房卡',dev:'10005',dorm:'麻子山倒班宿舍楼 · 101室'},
|
||||
{time:'2026-09-04 18:51',type:'呼叫超时未接听',reason:'值班终端(10016)呼出语音呼叫 10 分钟内被呼侧未接听,被呼员工外出就餐',dev:'10009',dorm:'西峰基地综合宿舍楼 · 302室'},
|
||||
{time:'2026-09-04 09:20',type:'设备离线告警',reason:'呼叫设备心跳丢失超过 15 分钟,判定线路老化已派单维修(102-1 曲志明床位)',dev:'10003',dorm:'樊学倒班宿舍1号楼 · 102室'},
|
||||
{time:'2026-09-03 20:08',type:'救助处置超时',reason:'呼入已接通但 30 分钟内未派单到场处置,关联 2026-09-03 19:36 杨慧敏(CQ045130)踝扭伤呼入',dev:'10005',dorm:'麻子山倒班宿舍楼 · 101室'},
|
||||
{time:'2026-09-02 23:47',type:'设备离线告警',reason:'值班终端离线超过 15 分钟,疑似电源适配器故障,呼入转接至备用座机',dev:'10016',dorm:'西峰基地值班室'}
|
||||
];
|
||||
var typeBadge={'呼叫超时未接听':'badge--danger','设备离线告警':'badge--warning','救助处置超时':'badge--info'};
|
||||
function day(dt){return dt.slice(0,10);}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -294,7 +294,7 @@ tbody td:last-child:has(.more-wrap:hover){z-index:30;}
|
||||
<div class="modal--wide">
|
||||
<div class="modal__header"><span id="devFormTitle">新增设备</span><button class="modal__close" onclick="closeDevForm()">×</button></div>
|
||||
<div class="modal__body">
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>设备编号</div><div class="form-row__control"><input class="form-input" id="dCode" placeholder="如:FX-BP-01"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>设备编号</div><div class="form-row__control"><input class="form-input" id="dCode" placeholder="请输入"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>设备名称</div><div class="form-row__control"><input class="form-input" id="dName" placeholder="如:血压检测仪"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>设备类型</div><div class="form-row__control">
|
||||
<select class="filter-select" id="dCat" style="width:200px;" onchange="onDevCat()">
|
||||
@@ -343,7 +343,7 @@ tbody td:last-child:has(.more-wrap:hover){z-index:30;}
|
||||
<div class="modal">
|
||||
<div class="modal__header"><span id="termFormTitle">新增终端</span><button class="modal__close" onclick="closeTermForm()">×</button></div>
|
||||
<div class="modal__body">
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>终端编号</div><div class="form-row__control"><input class="form-input" id="tCode" placeholder="如:FX-T01"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>终端编号</div><div class="form-row__control"><input class="form-input" id="tCode" placeholder="请输入"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>终端名称</div><div class="form-row__control"><input class="form-input" id="tName" placeholder="如:入口导诊终端"></div></div>
|
||||
<div class="form-row"><div class="form-row__label"><i>*</i>是否有效</div><div class="form-row__control">
|
||||
<div class="radio-group"><label><input type="radio" name="tStatus" value="1" checked>有效</label><label><input type="radio" name="tStatus" value="0">无效</label></div>
|
||||
@@ -476,43 +476,43 @@ var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油
|
||||
/* 健康室数据:与站点/作业区勾稽;devices 内类型 1测量/2心理检测/3治疗设备/4监测设备;设备 status 1有效/0禁用 */
|
||||
var rooms=[
|
||||
{name:'樊学站点健康小屋',place:'樊学站点基地综合楼一层',unit:'采油一厂',depar:'樊学作业区',use:['樊学作业区'],status:'是',legal:'是',
|
||||
devices:[{code:'FX-BP-01',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量,支持数据云端回传。',notice:'测量前静坐 5 分钟,保持安静。'},
|
||||
{code:'FX-BG-02',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:'空腹或餐后 2 小时测量。'},
|
||||
{code:'FX-PSY-03',name:'心理测评系统',cat:2,len:40,num:2,status:1,remark:'心理健康量表在线测评。',notice:'请独立完成测评。'},
|
||||
{code:'FX-ECG-04',name:'十二导联心电',cat:4,len:0,num:0,status:1,remark:'十二导联心电采集与判读。',notice:'测量前去除金属饰品。'}],
|
||||
terminals:[{code:'FX-T01',name:'入口导诊终端',status:1},{code:'FX-T02',name:'报告打印终端',status:1}],
|
||||
devices:[{code:'20001',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量,支持数据云端回传。',notice:'测量前静坐 5 分钟,保持安静。'},
|
||||
{code:'20002',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:'空腹或餐后 2 小时测量。'},
|
||||
{code:'20003',name:'心理测评系统',cat:2,len:40,num:2,status:1,remark:'心理健康量表在线测评。',notice:'请独立完成测评。'},
|
||||
{code:'20004',name:'十二导联心电',cat:4,len:0,num:0,status:1,remark:'十二导联心电采集与判读。',notice:'测量前去除金属饰品。'}],
|
||||
terminals:[{code:'20005',name:'入口导诊终端',status:1},{code:'20006',name:'报告打印终端',status:1}],
|
||||
admins:[{userName:'fxyisheng',realName:'樊学健康师'},{userName:'fxhushi',realName:'樊学护士长'}],
|
||||
selfLayouts:[{date:'2026-09-10',times:[{open:'08:00',close:'12:00'},{open:'14:00',close:'18:00'}],status:'是'},
|
||||
{date:'2026-09-11',times:[{open:'08:00',close:'12:00'}],status:'是'}]},
|
||||
{name:'麻子山站点健康小屋',place:'麻子山站点综合办公楼二层',unit:'采油一厂',depar:'麻子山作业区',use:['麻子山作业区'],status:'是',legal:'是',
|
||||
devices:[{code:'MZ-BP-01',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'MZ-BG-02',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:''},
|
||||
{code:'MZ-BMI-03',name:'体成分分析仪',cat:1,len:20,num:8,status:1,remark:'体脂率、肌肉量等多指标分析。',notice:'光脚站立测量。'}],
|
||||
terminals:[{code:'MZ-T01',name:'自助签到终端',status:1}],
|
||||
devices:[{code:'20007',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'20008',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:''},
|
||||
{code:'20009',name:'体成分分析仪',cat:1,len:20,num:8,status:1,remark:'体脂率、肌肉量等多指标分析。',notice:'光脚站立测量。'}],
|
||||
terminals:[{code:'20010',name:'自助签到终端',status:1}],
|
||||
admins:[{userName:'mzjiankangshi',realName:'麻子山健康师'}],
|
||||
selfLayouts:[{date:'2026-09-10',times:[{open:'09:00',close:'17:00'}],status:'是'}]},
|
||||
{name:'桐园站点健康小屋',place:'桐园站点基地服务中心一层',unit:'采油二厂',depar:'桐园作业区',use:['桐园作业区','姬圪台作业区'],status:'是',legal:'是',
|
||||
devices:[{code:'TY-BP-01',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'TY-BMI-02',name:'体成分分析仪',cat:1,len:20,num:8,status:0,remark:'体脂率、肌肉量等多指标分析。',notice:'设备检修中,暂不可预约。'},
|
||||
{code:'TY-PSY-03',name:'心理测评系统',cat:2,len:40,num:2,status:1,remark:'心理健康量表在线测评。',notice:''},
|
||||
{code:'TY-DEC-04',name:'音乐放松舱',cat:3,len:60,num:2,status:1,remark:'音乐放松与生物反馈训练。',notice:'请穿宽松衣物。'}],
|
||||
terminals:[{code:'TY-T01',name:'入口导诊终端',status:1},{code:'TY-T02',name:'报告打印终端',status:1}],
|
||||
devices:[{code:'20011',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'20012',name:'体成分分析仪',cat:1,len:20,num:8,status:0,remark:'体脂率、肌肉量等多指标分析。',notice:'设备检修中,暂不可预约。'},
|
||||
{code:'20013',name:'心理测评系统',cat:2,len:40,num:2,status:1,remark:'心理健康量表在线测评。',notice:''},
|
||||
{code:'20014',name:'音乐放松舱',cat:3,len:60,num:2,status:1,remark:'音乐放松与生物反馈训练。',notice:'请穿宽松衣物。'}],
|
||||
terminals:[{code:'20015',name:'入口导诊终端',status:1},{code:'20016',name:'报告打印终端',status:1}],
|
||||
admins:[{userName:'tyyisheng',realName:'桐园健康师'},{userName:'tyhushi',realName:'桐园护士长'}],
|
||||
selfLayouts:[{date:'2026-09-10',times:[{open:'08:00',close:'12:00'},{open:'13:30',close:'17:30'}],status:'是'},
|
||||
{date:'2026-09-12',times:[{open:'08:30',close:'11:30'}],status:'否'}]},
|
||||
{name:'西峰基地健康小屋',place:'西峰基地职工服务中心二层',unit:'第四采油厂',depar:'西峰作业区',use:['西峰作业区'],status:'是',legal:'否',
|
||||
devices:[{code:'XF-BP-01',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'XF-BG-02',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:''},
|
||||
{code:'XF-LAS-03',name:'激光治疗仪',cat:3,len:30,num:3,status:1,remark:'低能量激光理疗。',notice:'遵医嘱使用。'},
|
||||
{code:'XF-ECG-04',name:'十二导联心电',cat:4,len:0,num:0,status:1,remark:'十二导联心电采集。',notice:''},
|
||||
{code:'XF-WCH-05',name:'智能手表充电柜',cat:4,len:0,num:0,status:1,remark:'健康手表集中充电与数据同步。',notice:''}],
|
||||
terminals:[{code:'XF-T01',name:'入口导诊终端',status:1},{code:'XF-T02',name:'报告打印终端',status:1}],
|
||||
devices:[{code:'20017',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'20018',name:'血糖分析仪',cat:1,len:20,num:6,status:1,remark:'指尖采血快速检测血糖。',notice:''},
|
||||
{code:'20019',name:'激光治疗仪',cat:3,len:30,num:3,status:1,remark:'低能量激光理疗。',notice:'遵医嘱使用。'},
|
||||
{code:'20020',name:'十二导联心电',cat:4,len:0,num:0,status:1,remark:'十二导联心电采集。',notice:''},
|
||||
{code:'20021',name:'智能手表充电柜',cat:4,len:0,num:0,status:1,remark:'健康手表集中充电与数据同步。',notice:''}],
|
||||
terminals:[{code:'20022',name:'入口导诊终端',status:1},{code:'20023',name:'报告打印终端',status:1}],
|
||||
admins:[{userName:'xfjiankangshi',realName:'西峰健康师'}],
|
||||
selfLayouts:[{date:'2026-09-10',times:[{open:'08:00',close:'18:00'}],status:'是'}]},
|
||||
{name:'靖边气田健康小屋',place:'靖边气田站点营地活动室',unit:'采气一厂',depar:'靖边气田',use:['靖边气田'],status:'否',legal:'是',
|
||||
devices:[{code:'JB-BP-01',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'JB-BMI-02',name:'体成分分析仪',cat:1,len:20,num:8,status:1,remark:'体脂率、肌肉量等多指标分析。',notice:''}],
|
||||
terminals:[{code:'JB-T01',name:'入口导诊终端',status:0}],
|
||||
devices:[{code:'20024',name:'血压检测仪',cat:1,len:30,num:4,status:1,remark:'全自动臂式血压测量。',notice:''},
|
||||
{code:'20025',name:'体成分分析仪',cat:1,len:20,num:8,status:1,remark:'体脂率、肌肉量等多指标分析。',notice:''}],
|
||||
terminals:[{code:'20026',name:'入口导诊终端',status:0}],
|
||||
admins:[{userName:'jbjiankangshi',realName:'靖边健康师'}],
|
||||
selfLayouts:[]}
|
||||
];
|
||||
|
||||
@@ -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,'&').replace(/</g,'<');}
|
||||
@@ -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,'&').replace(/</g,'<');}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -168,6 +168,21 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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>
|
||||
@@ -237,16 +252,20 @@
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" id="fIdCard" placeholder="请输入" style="width:160px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>异常员工:</label>
|
||||
<select class="filter-select" style="min-width:100px;" id="abnormalFilter">
|
||||
<option value="">全部</option>
|
||||
<option value="abnormal">是</option>
|
||||
<option value="normal">否</option>
|
||||
</select>
|
||||
</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -585,21 +604,135 @@ 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('|');
|
||||
var cat=field.slice(0,i),name=field.slice(i+1);
|
||||
var val=row.r[cat+'|'+name]||row.r[name];
|
||||
if(!val)val=[(CAT_DEFAULTS[cat]||['-'])[0]];
|
||||
return val[0];
|
||||
}
|
||||
return row[field];
|
||||
}
|
||||
/* 数值化:取结果中的首个数字 */
|
||||
function numericOf(v){
|
||||
var m=String(v).match(/[-+]?\d+(?:\.\d+)?/);
|
||||
return m?parseFloat(m[0]):null;
|
||||
}
|
||||
/* 单条条件匹配:包含/=/!= 按字符串,>、>=、<、<=、范围 按数值 */
|
||||
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+' 条';
|
||||
@@ -789,7 +922,6 @@ function applyFilter(){
|
||||
showToast('查询完成');
|
||||
}
|
||||
function resetFilter(){
|
||||
document.getElementById('abnormalFilter').value='';
|
||||
document.getElementById('fName').value='';
|
||||
document.getElementById('fEmpNo').value='';
|
||||
document.getElementById('fIdCard').value='';
|
||||
@@ -797,6 +929,7 @@ function resetFilter(){
|
||||
showToast('已重置筛选条件');
|
||||
}
|
||||
renderBigTable();
|
||||
ADV_FIELD_OPTS=buildAdvFieldOpts();
|
||||
/* 体检年份选择控件 */
|
||||
function pickYear(el){
|
||||
var dp=el.parentElement,input=dp.parentElement.querySelector('input');
|
||||
|
||||
@@ -119,6 +119,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -184,6 +203,18 @@
|
||||
</div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -399,6 +430,114 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='#unitTable tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'unit',label:'单位',idx:1},{key:'dept',label:'部门',idx:2},{key:'year',label:'体检年份',idx:3},
|
||||
{key:'signHospital',label:'签约医院',idx:4},{key:'hospital',label:'承检医院',idx:5},{key:'range',label:'体检计划日期范围',idx:6},
|
||||
{key:'total',label:'总人数',idx:7},{key:'should',label:'应检人数',idx:8},{key:'selected',label:'已选项目人数',idx:9},
|
||||
{key:'booked',label:'已预约人数',idx:10},{key:'checked',label:'已检人数',idx:11},{key:'unchecked',label:'未检人数',idx:12},
|
||||
{key:'rate',label:'受检率',idx:13},{key:'detectRate',label:'检出率',idx:14},{key:'keyAbn',label:'重点指标异常人数',idx:15},
|
||||
{key:'ctaAbn',label:'冠脉CTA异常人数',idx:16},{key:'cancerAbn',label:'防癌筛查异常人数',idx:17},
|
||||
{key:'planFee',label:'计划总费用(元)',idx:18},{key:'actualFee',label:'实际总费用(元)',idx:19},{key:'perFee',label:'人均费用(元)',idx:20}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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)
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -120,6 +120,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -157,10 +176,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item active">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.html" class="sidebar-item active">体检预约</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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
@@ -213,12 +232,6 @@
|
||||
<option>请选择单位部门</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>医院选择:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择医院</option><option>延安医院</option><option>泾河医院</option><option>市中心医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>姓名:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入姓名" style="width:130px;min-width:unset;">
|
||||
@@ -231,53 +244,23 @@
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入身份证号" style="width:180px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>性别:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择性别</option><option>男</option><option>女</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>年龄范围:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择年龄范围</option><option>年龄<35</option><option>35≤年龄<40</option><option>40≤年龄<45</option><option>年龄≥45</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>机构分类:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择机构分类</option><option>子公司</option><option>三产公司</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>是否上市员工:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>是</option><option>否</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>防癌项:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择防癌项</option><option>Septin9</option><option>CA724</option><option>两项都有</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择体检状态</option><option>未体检</option><option>体检中</option><option>已体检</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>预约状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择预约状态</option><option>未预约</option><option>已预约</option><option>已取消预约</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -509,6 +492,117 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'name',label:'姓名',idx:1},{key:'gender',label:'性别',idx:2},{key:'age',label:'年龄',idx:3},
|
||||
{key:'ageRange',label:'年龄范围',idx:4},{key:'idCard',label:'身份证号',idx:5},{key:'unit',label:'单位',idx:6},
|
||||
{key:'dept',label:'部门',idx:7},{key:'orgType',label:'机构分类',idx:8},{key:'empNo',label:'员工编号',idx:9},
|
||||
{key:'examCode',label:'体检编码',idx:10},{key:'empType',label:'用工形式',idx:11},{key:'marriage',label:'婚姻状态',idx:12},
|
||||
{key:'examType',label:'体检类型',idx:13},{key:'cancer',label:'防癌项',idx:14},{key:'listed',label:'是否上市员工',idx:15},
|
||||
{key:'hospital',label:'体检医院',idx:16},{key:'feeTotal',label:'体检总费用',idx:17},{key:'fee1',label:'必检费用',idx:18},
|
||||
{key:'fee2',label:'选检费用',idx:19},{key:'fee3',label:'妇科费用',idx:20},{key:'fee4',label:'防癌筛查费用',idx:21},
|
||||
{key:'bookStatus',label:'预约状态',idx:22},{key:'bookInfo',label:'体检预约信息',idx:23},{key:'bookDate',label:'预约日期',idx:24},
|
||||
{key:'examStatus',label:'体检状态',idx:25},{key:'examDate',label:'体检日期',idx:26},{key:'year',label:'体检年份',idx:27},
|
||||
{key:'plan',label:'体检计划',idx:28}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -146,6 +146,25 @@
|
||||
.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;}
|
||||
[id^="advConditions"]{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -183,10 +202,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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 active">体检预约跟踪</a>
|
||||
<a href="mid-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
@@ -249,77 +268,35 @@
|
||||
<option>请选择单位部门</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>医院选择:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择医院</option><option>延安医院</option><option>泾河医院</option><option>市中心医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>姓名:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入姓名" style="width:130px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>员工编号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="width:140px;min-width:unset;">
|
||||
<input class="filter-input" type="text" placeholder="请输入员工编号" style="width:140px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入身份证号" style="width:180px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>性别:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择性别</option><option>男</option><option>女</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>年龄范围:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择年龄范围</option><option>年龄<35</option><option>35≤年龄<40</option><option>40≤年龄<45</option><option>年龄≥45</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>机构分类:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择机构分类</option><option>子公司</option><option>三产公司</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>是否上市员工:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>是</option><option>否</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>防癌项:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择防癌项</option><option>Septin9</option><option>CA724</option><option>两项都有</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择体检状态</option><option>未体检</option><option>体检中</option><option>已体检</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>预约状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择预约状态</option><option>未预约</option><option>已预约</option><option>已取消预约</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>前置机预约状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择前置机预约状态</option><option>未预约</option><option>预约成功</option><option>已取消预约</option><option>预约失败</option><option>预约中</option><option>取消预约失败</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</button>
|
||||
<button class="btn btn-default" id="advToggle-should" onclick="toggleAdvanced('should')">高级查询 <span class="adv-arrow">▾</span></button>
|
||||
</div>
|
||||
|
||||
<!-- 高级查询 -->
|
||||
<div class="adv-panel" id="advPanel-should">
|
||||
<div class="adv-panel__head">高级查询</div>
|
||||
<div id="advConditions-should"></div>
|
||||
<div class="adv-actions">
|
||||
<button class="btn btn-default" onclick="addAdvCondition('should')">+ 添加条件</button>
|
||||
<button class="btn btn-primary" onclick="applyFilter('should')">查询</button>
|
||||
<button class="btn btn-default" onclick="clearAdvanced('should')">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -405,12 +382,6 @@
|
||||
<option>请选择单位部门</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>医院选择:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择医院</option><option>延安医院</option><option>泾河医院</option><option>兴隆园医院</option><option>市中心医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>姓名:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入姓名" style="width:130px;min-width:unset;">
|
||||
@@ -428,6 +399,18 @@
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</button>
|
||||
<button class="btn btn-default" id="advToggle-selected" onclick="toggleAdvanced('selected')">高级查询 <span class="adv-arrow">▾</span></button>
|
||||
</div>
|
||||
|
||||
<!-- 高级查询 -->
|
||||
<div class="adv-panel" id="advPanel-selected">
|
||||
<div class="adv-panel__head">高级查询</div>
|
||||
<div id="advConditions-selected"></div>
|
||||
<div class="adv-actions">
|
||||
<button class="btn btn-default" onclick="addAdvCondition('selected')">+ 添加条件</button>
|
||||
<button class="btn btn-primary" onclick="applyFilter('selected')">查询</button>
|
||||
<button class="btn btn-default" onclick="clearAdvanced('selected')">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -509,12 +492,6 @@
|
||||
<option>请选择单位部门</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>医院选择:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择医院</option><option>延安医院</option><option>泾河医院</option><option>兴隆园医院</option><option>市中心医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>姓名:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入姓名" style="width:130px;min-width:unset;">
|
||||
@@ -527,15 +504,23 @@
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入身份证号" style="width:180px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检编码:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入体检编码" style="width:150px;min-width:unset;">
|
||||
</div>
|
||||
<button class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</button>
|
||||
<button class="btn btn-default" id="advToggle-booked" onclick="toggleAdvanced('booked')">高级查询 <span class="adv-arrow">▾</span></button>
|
||||
</div>
|
||||
|
||||
<!-- 高级查询 -->
|
||||
<div class="adv-panel" id="advPanel-booked">
|
||||
<div class="adv-panel__head">高级查询</div>
|
||||
<div id="advConditions-booked"></div>
|
||||
<div class="adv-actions">
|
||||
<button class="btn btn-default" onclick="addAdvCondition('booked')">+ 添加条件</button>
|
||||
<button class="btn btn-primary" onclick="applyFilter('booked')">查询</button>
|
||||
<button class="btn btn-default" onclick="clearAdvanced('booked')">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -622,54 +607,38 @@
|
||||
<div class="filter-item">
|
||||
<label>单位部门:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检医院:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>延安医院</option><option>市中心医院</option><option>矿区总医院</option>
|
||||
<option>请选择单位部门</option><option>公司机关</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>姓名:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="width:130px;min-width:unset;">
|
||||
<input class="filter-input" type="text" placeholder="请输入姓名" style="width:130px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>员工编号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="width:140px;min-width:unset;">
|
||||
<input class="filter-input" type="text" placeholder="请输入员工编号" style="width:140px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="width:180px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检编码:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="width:150px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>系统体检状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>未体检</option><option>体检中</option><option>已体检</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>医院体检状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>未预约</option><option>已预约</option><option>体检中</option><option>已终审</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检日期:</label>
|
||||
<input class="filter-input" type="date" style="width:150px;min-width:unset;">
|
||||
<span style="color:var(--text-secondary);">至</span>
|
||||
<input class="filter-input" type="date" style="width:150px;min-width:unset;">
|
||||
<input class="filter-input" type="text" placeholder="请输入身份证号" style="width:180px;min-width:unset;">
|
||||
</div>
|
||||
<button class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4" stroke="currentColor" stroke-width="1.4"/><path d="M10 10l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</button>
|
||||
<button class="btn btn-default" id="advToggle-participated" onclick="toggleAdvanced('participated')">高级查询 <span class="adv-arrow">▾</span></button>
|
||||
</div>
|
||||
|
||||
<!-- 高级查询 -->
|
||||
<div class="adv-panel" id="advPanel-participated">
|
||||
<div class="adv-panel__head">高级查询</div>
|
||||
<div id="advConditions-participated"></div>
|
||||
<div class="adv-actions">
|
||||
<button class="btn btn-default" onclick="addAdvCondition('participated')">+ 添加条件</button>
|
||||
<button class="btn btn-primary" onclick="applyFilter('participated')">查询</button>
|
||||
<button class="btn btn-default" onclick="clearAdvanced('participated')">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -1114,6 +1083,148 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_TABS={
|
||||
'should':{
|
||||
selector:'#tab-should .table-card__content tbody tr',
|
||||
fields:[
|
||||
{key:'name',label:'姓名',idx:1},{key:'empNo',label:'员工编号',idx:2},{key:'gender',label:'性别',idx:3},
|
||||
{key:'age',label:'年龄',idx:4},{key:'idCard',label:'身份证号',idx:5},{key:'unit',label:'单位',idx:6},
|
||||
{key:'dept',label:'部门',idx:7},{key:'orgType',label:'机构分类',idx:8},{key:'examCode',label:'体检编码',idx:9},
|
||||
{key:'empType',label:'用工形式',idx:10},{key:'marriage',label:'婚姻状态',idx:11},{key:'cancer',label:'防癌项',idx:12},
|
||||
{key:'listed',label:'是否上市员工',idx:13},{key:'bookStatus',label:'预约状态',idx:14},{key:'frontStatus',label:'前置机预约状态',idx:15},
|
||||
{key:'bookDate',label:'预约日期',idx:16},{key:'examStatus',label:'体检状态',idx:17},{key:'examDate',label:'体检日期',idx:18},
|
||||
{key:'year',label:'体检年份',idx:19},{key:'plan',label:'体检计划',idx:20}
|
||||
]
|
||||
},
|
||||
'selected':{
|
||||
selector:'#tab-selected .table-card__content tbody tr',
|
||||
fields:[
|
||||
{key:'name',label:'姓名',idx:1},{key:'empNo',label:'员工编号',idx:2},{key:'gender',label:'性别',idx:3},
|
||||
{key:'age',label:'年龄',idx:4},{key:'idCard',label:'身份证号',idx:5},{key:'unit',label:'单位',idx:6},
|
||||
{key:'dept',label:'部门',idx:7},{key:'hospital',label:'体检医院',idx:8},{key:'itemCount',label:'体检项目数',idx:9},
|
||||
{key:'feeTotal',label:'体检总费用',idx:10},{key:'fee1',label:'必检费用',idx:11},{key:'fee2',label:'选检费用',idx:12},
|
||||
{key:'fee3',label:'妇科费用',idx:13},{key:'fee4',label:'防癌筛查费用',idx:14},{key:'submitDate',label:'提交项目日期',idx:15},
|
||||
{key:'year',label:'体检年份',idx:16},{key:'plan',label:'体检计划',idx:17}
|
||||
]
|
||||
},
|
||||
'booked':{
|
||||
selector:'#tab-booked .table-card__content tbody tr',
|
||||
fields:[
|
||||
{key:'name',label:'姓名',idx:2},{key:'empNo',label:'员工编号',idx:3},{key:'gender',label:'性别',idx:4},
|
||||
{key:'age',label:'年龄',idx:5},{key:'idCard',label:'身份证号',idx:6},{key:'unit',label:'单位',idx:7},
|
||||
{key:'dept',label:'部门',idx:8},{key:'hospital',label:'体检医院',idx:9},{key:'examCode',label:'体检编码',idx:10},
|
||||
{key:'bookDate',label:'预约日期',idx:11},{key:'year',label:'体检年份',idx:12},{key:'plan',label:'体检计划',idx:13}
|
||||
]
|
||||
},
|
||||
'participated':{
|
||||
selector:'#tab-participated .table-card__content tbody tr',
|
||||
fields:[
|
||||
{key:'name',label:'姓名',idx:1},{key:'empNo',label:'员工编号',idx:2},{key:'gender',label:'性别',idx:3},
|
||||
{key:'age',label:'年龄',idx:4},{key:'idCard',label:'身份证号',idx:5},{key:'unit',label:'单位',idx:6},
|
||||
{key:'dept',label:'部门',idx:7},{key:'hospital',label:'体检医院',idx:8},{key:'examCode',label:'体检编码',idx:9},
|
||||
{key:'sysStatus',label:'系统体检状态',idx:10},{key:'hospStatus',label:'医院体检状态',idx:11},{key:'examDate',label:'体检日期',idx:12},
|
||||
{key:'checkedItems',label:'已检项/全部',idx:13},{key:'year',label:'体检年份',idx:14},{key:'plan',label:'体检计划',idx:15}
|
||||
]
|
||||
}
|
||||
};
|
||||
function fieldOpts(tabId){return ADV_TABS[tabId].fields.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');}
|
||||
function advConditionRowHTML(tabId){
|
||||
return '<div class="adv-cond-row">'
|
||||
+'<select class="filter-select adv-field">'+fieldOpts(tabId)+'</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(tabId){document.getElementById('advConditions-'+tabId).insertAdjacentHTML('beforeend',advConditionRowHTML(tabId));}
|
||||
function removeAdvCondition(el){el.parentElement.remove();}
|
||||
function toggleAdvanced(tabId){
|
||||
var p=document.getElementById('advPanel-'+tabId);
|
||||
var open=p.classList.toggle('open');
|
||||
var arr=document.getElementById('advToggle-'+tabId).querySelector('.adv-arrow');
|
||||
if(arr)arr.textContent=open?'▴':'▾';
|
||||
if(open&&!p.querySelector('.adv-cond-row'))addAdvCondition(tabId);
|
||||
}
|
||||
function readAdvancedConditions(tabId){
|
||||
var conds=[];
|
||||
document.querySelectorAll('#advConditions-'+tabId+' .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;
|
||||
}
|
||||
function fieldByKey(tabId,key){var fields=ADV_TABS[tabId].fields;for(var i=0;i<fields.length;i++){if(fields[i].key===key)return fields[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond,tabId){
|
||||
var f=fieldByKey(tabId,cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(tabId){
|
||||
var conds=readAdvancedConditions(tabId);
|
||||
document.querySelectorAll(ADV_TABS[tabId].selector).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i],tabId)){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(tabId){
|
||||
document.getElementById('advConditions-'+tabId).innerHTML='';
|
||||
document.querySelectorAll(ADV_TABS[tabId].selector).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -150,10 +150,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -87,6 +87,25 @@
|
||||
/* 三级单位tab下才显示的部门列 */
|
||||
.col-dept{display:none;}
|
||||
body.level-3 .col-dept{display:table-cell;}
|
||||
/* 高级查询 */
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -124,10 +143,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
@@ -179,6 +198,18 @@
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -246,6 +277,110 @@ function toggleSubgroup(header){
|
||||
var sub=active.closest('.sidebar-subgroup');
|
||||
if(sub)sub.classList.add('open');
|
||||
})();
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'unit',label:'单位',idx:1},{key:'dept',label:'部门',idx:2},{key:'should',label:'应体检人数',idx:3},
|
||||
{key:'booked',label:'已预约人数',idx:4},{key:'participated',label:'已参检人数',idx:5},{key:'unparticipated',label:'未参检人数',idx:6},
|
||||
{key:'unbooked',label:'未预约人数',idx:7},{key:'year',label:'体检年份',idx:8}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -128,10 +128,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -150,10 +150,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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,10 +129,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -87,6 +87,25 @@
|
||||
/* 三级单位tab下才显示的部门列 */
|
||||
.col-dept{display:none;}
|
||||
body.level-3 .col-dept{display:table-cell;}
|
||||
/* 高级查询 */
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -124,10 +143,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
@@ -179,6 +198,18 @@
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -246,6 +277,110 @@ function toggleSubgroup(header){
|
||||
var sub=active.closest('.sidebar-subgroup');
|
||||
if(sub)sub.classList.add('open');
|
||||
})();
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'unit',label:'单位',idx:1},{key:'dept',label:'部门',idx:2},{key:'should',label:'应体检人数',idx:3},
|
||||
{key:'questionnaire',label:'填写问卷人数',idx:4},{key:'booked',label:'已预约人数',idx:5},{key:'participated',label:'已参检人数',idx:6},
|
||||
{key:'unparticipated',label:'未参检人数',idx:7},{key:'unbooked',label:'未预约人数',idx:8},{key:'year',label:'体检年份',idx:9}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -124,10 +124,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -128,10 +128,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -147,10 +147,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.html" class="sidebar-item active">已检员工</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>
|
||||
|
||||
@@ -147,10 +147,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item active">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -96,6 +96,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -133,10 +152,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
@@ -200,6 +219,18 @@
|
||||
查询
|
||||
</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
@@ -286,6 +317,110 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'unit',label:'单位',idx:1},{key:'dept',label:'部门',idx:2},{key:'should',label:'应体检人数',idx:3},
|
||||
{key:'selected',label:'已选项目人数',idx:4},{key:'booked',label:'已预约人数',idx:5},{key:'participated',label:'已参检人数',idx:6},
|
||||
{key:'unparticipated',label:'未参检人数',idx:7},{key:'unbooked',label:'未预约人数',idx:8},{key:'year',label:'体检年份',idx:9}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -113,10 +113,10 @@
|
||||
常规体检
|
||||
</div>
|
||||
<div class="sidebar-subgroup__body">
|
||||
<a href="mid-appointment-track.html" class="sidebar-item">体检预约</a>
|
||||
<!-- <a href="mid-appointment-track.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-selected-items.html" class="sidebar-item">已选项目</a>
|
||||
<a href="mid-participation.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>
|
||||
|
||||
@@ -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,21 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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 +157,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 +173,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 +202,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 +212,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>
|
||||
@@ -249,16 +264,20 @@
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" id="fIdCard" placeholder="请输入" style="width:160px;min-width:unset;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>异常员工:</label>
|
||||
<select class="filter-select" style="min-width:100px;" id="abnormalFilter">
|
||||
<option value="">全部</option>
|
||||
<option value="abnormal">是</option>
|
||||
<option value="normal">否</option>
|
||||
</select>
|
||||
</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 +551,127 @@ 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+' 条';
|
||||
@@ -572,7 +697,6 @@ function applyFilter(){
|
||||
showToast('查询完成');
|
||||
}
|
||||
function resetFilter(){
|
||||
document.getElementById('abnormalFilter').value='';
|
||||
document.getElementById('fName').value='';
|
||||
document.getElementById('fEmpNo').value='';
|
||||
document.getElementById('fIdCard').value='';
|
||||
@@ -580,6 +704,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>
|
||||
|
||||
@@ -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>';
|
||||
}
|
||||
|
||||
/* 导出群体报告(CSV,Excel 兼容) */
|
||||
@@ -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>
|
||||
|
||||
@@ -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,107 @@
|
||||
.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;}
|
||||
/* 高级查询 */
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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>
|
||||
@@ -121,7 +222,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 +237,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 +266,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 +276,211 @@
|
||||
冠脉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>
|
||||
<button class="btn btn-default" id="advToggle" onclick="toggleAdvanced()">高级查询 <span class="adv-arrow">▾</span></button>
|
||||
</div>
|
||||
</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="applyListFilter()">查询</button>
|
||||
<button class="btn btn-default" onclick="clearAdvanced()">清空</button>
|
||||
</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 +709,365 @@ 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 conds=readAdvancedConditions();
|
||||
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(selectedCodes.length&&selectedCodes.indexOf(r.itemCode)<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_FIELDS=[
|
||||
{key:'name',label:'姓名'},{key:'empNo',label:'员工编号'},{key:'gender',label:'性别'},
|
||||
{key:'age',label:'年龄'},{key:'ageRange',label:'年龄范围'},{key:'unit',label:'单位'},
|
||||
{key:'dept',label:'部门'},{key:'position',label:'岗位'},{key:'itemCode',label:'项目编码'},
|
||||
{key:'itemName',label:'体检项目'},{key:'abnormal',label:'是否异常'}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function getFieldRaw(row,field){
|
||||
if(field==='abnormal')return row.abnormal?'是':'否';
|
||||
return row[field];
|
||||
}
|
||||
function numericOf(v){var m=String(v).match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
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 clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
applyListFilter();
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 分页状态 */
|
||||
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>
|
||||
|
||||
<!-- 主内容 -->
|
||||
|
||||
@@ -154,6 +154,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -290,20 +309,20 @@
|
||||
<option>请选择</option><option>泾河医院</option><option>庆阳医院</option><option>延安医院</option><option>兴隆园医院</option><option>宁夏宝石花医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>性别:</label>
|
||||
<select class="filter-select" style="min-width:90px;">
|
||||
<option>请选择</option><option>男</option><option>女</option><option>通用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>年龄区间:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>年龄<35</option><option>35≤年龄<40</option><option>40≤年龄<45</option><option>年龄≥45</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
@@ -1041,6 +1060,110 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'hospital',label:'医院名称',idx:1},{key:'year',label:'体检年份',idx:2},{key:'tplName',label:'模板名称',idx:3},
|
||||
{key:'gender',label:'性别',idx:4},{key:'ageRange',label:'年龄区间',idx:5},{key:'discountType',label:'折扣类型',idx:6},
|
||||
{key:'discountValue',label:'折扣数值(元)',idx:7},{key:'discountPercent',label:'折扣百分比(%)',idx:8}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -146,6 +146,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -282,32 +301,20 @@
|
||||
<option>请选择</option><option>第一采油厂</option><option>第二采油厂</option><option>第三采油厂</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>体检医院:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>泾河医院</option><option>庆阳医院</option><option>延安医院</option><option>兴隆园医院</option><option>宁夏宝石花医院</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>性别:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>男</option><option>女</option><option>通用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>年龄范围:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>年龄<35</option><option>35≤年龄<40</option><option>40≤年龄<45</option><option>年龄≥45</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>费用审核状态:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option><option>审核通过</option><option>待审核</option><option>已拒绝</option><option>未提交</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
@@ -992,6 +999,111 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'comboName',label:'项目组合名称',idx:2},{key:'hospital',label:'承检医院',idx:3},{key:'unit',label:'单位',idx:4},
|
||||
{key:'year',label:'体检年份',idx:5},{key:'gender',label:'性别',idx:6},{key:'ageRange',label:'年龄范围',idx:7},
|
||||
{key:'declaredFee',label:'本次提交费用(元)',idx:8},{key:'auditStatus',label:'费用审核状态',idx:9},
|
||||
{key:'effectiveFee',label:'必检项目费用(已生效)',idx:10},{key:'genStatus',label:'生成状态',idx:11},{key:'genTime',label:'生成时间',idx:12}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -153,6 +153,25 @@
|
||||
.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;}
|
||||
#advConditions{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px;}
|
||||
.adv-cond-row{display:flex;align-items:center;gap:8px;margin-bottom:0;min-width:0;}
|
||||
.adv-field{min-width:140px;flex:1;}
|
||||
.adv-op{min-width:72px;flex:none;}
|
||||
.adv-value-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0;max-width:none;}
|
||||
.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;}
|
||||
/* 轻提示 */
|
||||
.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;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -300,33 +319,20 @@
|
||||
<label>身份证号:</label>
|
||||
<input class="filter-input" type="text" placeholder="请输入" style="min-width:160px;">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>性别:</label>
|
||||
<select class="filter-select" style="min-width:120px;">
|
||||
<option>请选择性别</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>年龄范围:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option>
|
||||
<option>年龄<35</option>
|
||||
<option>35≤年龄<40</option>
|
||||
<option>40≤年龄<45</option>
|
||||
<option>年龄≥45</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label>防癌类型:</label>
|
||||
<select class="filter-select">
|
||||
<option>请选择</option>
|
||||
<option>Septin9</option>
|
||||
<option>CA724</option>
|
||||
<option>两项都有</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
<button class="btn btn-default">重置</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>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
@@ -827,6 +833,114 @@ 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');});
|
||||
});
|
||||
/* ============ 高级查询 ============ */
|
||||
var ADV_OPS=['包含','=','!=','>','>=','<','<=','范围'];
|
||||
var ADV_ROW_SELECTOR='.table-card__content tbody tr';
|
||||
var ADV_FIELDS=[
|
||||
{key:'name',label:'姓名',idx:1},{key:'empNo',label:'员工编号',idx:2},{key:'gender',label:'性别',idx:3},
|
||||
{key:'age',label:'年龄',idx:4},{key:'ageRange',label:'年龄范围',idx:5},{key:'idCard',label:'身份证号',idx:6},
|
||||
{key:'phone',label:'手机号',idx:7},{key:'unit',label:'单位',idx:8},{key:'dept',label:'部门',idx:9},
|
||||
{key:'year',label:'体检年份',idx:10},{key:'plan',label:'体检计划',idx:11},{key:'planUnit',label:'体检计划单位',idx:12},
|
||||
{key:'cancerType',label:'防癌筛查类型',idx:13},{key:'cancerYear',label:'防癌检查年份',idx:14},
|
||||
{key:'s9Checked',label:'Septin9是否已检',idx:15},{key:'s9Abn',label:'Septin9检查异常',idx:16},{key:'s9Value',label:'Septin9项目值',idx:17},
|
||||
{key:'caChecked',label:'CA724是否已检',idx:18},{key:'caAbn',label:'CA724检查异常',idx:19},{key:'caValue',label:'CA724项目值',idx:20}
|
||||
];
|
||||
var ADV_FIELD_OPTS=ADV_FIELDS.map(function(f){return '<option value="'+f.key+'">'+f.label+'</option>';}).join('');
|
||||
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 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;
|
||||
}
|
||||
function fieldByKey(key){for(var i=0;i<ADV_FIELDS.length;i++){if(ADV_FIELDS[i].key===key)return ADV_FIELDS[i];}return null;}
|
||||
function getFieldRaw(tr,f){var td=tr.cells[f.idx];return td?td.textContent.trim():'';}
|
||||
function numericOf(v){var s=String(v).replace(/,/g,'').replace(/[¥¥]/g,'');var m=s.match(/[-+]?\d+(?:\.\d+)?/);return m?parseFloat(m[0]):null;}
|
||||
function matchCondition(tr,cond){
|
||||
var f=fieldByKey(cond.field);if(!f)return true;
|
||||
var str=getFieldRaw(tr,f);
|
||||
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 applyFilter(){
|
||||
var conds=readAdvancedConditions();
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){
|
||||
var show=true;
|
||||
for(var i=0;i<conds.length;i++){if(!matchCondition(tr,conds[i])){show=false;break;}}
|
||||
tr.style.display=show?'':'none';
|
||||
});
|
||||
showToast('查询完成');
|
||||
}
|
||||
function clearAdvanced(){
|
||||
document.getElementById('advConditions').innerHTML='';
|
||||
document.querySelectorAll(ADV_ROW_SELECTOR).forEach(function(tr){tr.style.display='';});
|
||||
showToast('已清空高级查询条件');
|
||||
}
|
||||
/* 轻提示 */
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<!-- 轻提示 -->
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -451,6 +451,10 @@ const FOODS = [
|
||||
{type:'编辑',title:'调整净菜类型',time:'2026-03-15 10:42',user:'王秀兰',role:'营养师',dept:'营养管理部',summary:'新增「免淘米」净菜规格,出净率 95%。',changes:[{field:'净菜类型',old:'1 项(普洗组)',new:'2 项(普洗组/免淘米)'}]},
|
||||
{type:'价格调整',title:'更新基础定价',time:'2026-06-05 16:30',user:'张明',role:'采购员',dept:'采购部',summary:'根据 6 月市场行情调整粳米基础定价。',changes:[{field:'基础定价',old:'3.2 元/100g',new:'3.5 元/100g'}]},
|
||||
{type:'编辑',title:'修订色泽描述',time:'2026-07-21 11:05',user:'李建国',role:'营养师',dept:'营养管理部',summary:'色泽描述语义化,补充感官评级。',changes:[{field:'色泽描述',old:'乳白色,半透明',new:'乳白色,半透明,颗粒饱满'},{field:'感官评级',old:'—',new:'优'}]}
|
||||
],
|
||||
priceHistory:[
|
||||
{price:'3.5',time:'2026-06-05 16:30',method:'手工录入',prevPrice:'3.2',operator:'张明'},
|
||||
{price:'3.2',time:'2026-03-20 10:00',method:'食材验收价',prevPrice:'3.0',operator:'王芳'}
|
||||
]},
|
||||
{name:'猪里脊肉',code:'0201001',alias:'里脊',firstClass:'畜禽肉类',secondClass:'猪肉',icon:'🥩',edible:'100',waterRate:'71.4',dataSource:0,basePrice:'18.8',vegTypes:[
|
||||
{groupId:'g1',alias:'里脊肉丝',specAlias:'细丝',yieldRate:'92',edibleRate:'100',cutSpec:{shape:'丝',length:10,width:1,height:1,technique:'平刀'}},
|
||||
@@ -474,6 +478,11 @@ const FOODS = [
|
||||
{type:'编辑',title:'修正嘌呤数据',time:'2026-05-18 13:20',user:'王秀兰',role:'营养师',dept:'营养管理部',summary:'对齐《中国食物成分表》标准,修正嘌呤相关指标。',changes:[{field:'总嘌呤',old:'118.5 mg',new:'132.6 mg'},{field:'次黄嘌呤',old:'42.1 mg',new:'51.2 mg'}]},
|
||||
{type:'价格调整',title:'更新基础定价',time:'2026-07-09 17:00',user:'张明',role:'采购员',dept:'采购部',summary:'夏季猪肉行情稳定,微调定价。',changes:[{field:'基础定价',old:'19.2 元/100g',new:'18.8 元/100g'}]},
|
||||
{type:'编辑',title:'调整慎食人群说明',time:'2026-08-01 10:15',user:'李建国',role:'营养师',dept:'营养管理部',summary:'细化肾病人群慎食提示。',changes:[{field:'人群备注',old:'肾病患者慎食',new:'肾病人群需控制蛋白质摄入'}]}
|
||||
],
|
||||
priceHistory:[
|
||||
{price:'18.8',time:'2026-07-09 17:00',method:'手工录入',prevPrice:'19.2',operator:'张明'},
|
||||
{price:'19.2',time:'2026-05-30 15:20',method:'食材验收价',prevPrice:'18.8',operator:'王芳'},
|
||||
{price:'18.8',time:'2026-04-02 09:10',method:'手工录入',prevPrice:'16.5',operator:'张明'}
|
||||
]},
|
||||
{name:'菠菜',code:'0701001',alias:'菠菜',firstClass:'蔬菜类',secondClass:'叶菜类',icon:'🥬',edible:'89',waterRate:'91.2',dataSource:0,basePrice:'4.2',vegTypes:[],
|
||||
nutri:{calorie:24,protein:2.6,fat:0.3,carbohydrate:3.1,dietary_fiber:1.7,
|
||||
@@ -490,6 +499,10 @@ const FOODS = [
|
||||
{type:'编辑',title:'完善叶酸数据',time:'2026-03-02 14:30',user:'李建国',role:'营养师',dept:'营养管理部',summary:'对齐最新检测数据,叶酸含量上调。',changes:[{field:'叶酸',old:'87 μg',new:'110 μg'}]},
|
||||
{type:'价格调整',title:'更新基础定价',time:'2026-05-20 11:00',user:'张明',role:'采购员',dept:'采购部',summary:'夏季本地菠菜集中上市,定价下调。',changes:[{field:'基础定价',old:'5.5 元/100g',new:'4.2 元/100g'}]},
|
||||
{type:'编辑',title:'调整慎食提示',time:'2026-06-12 09:40',user:'王秀兰',role:'营养师',dept:'营养管理部',summary:'明确草酸含量高的人群风险提示。',changes:[{field:'人群备注',old:'—',new:'肾结石人群慎食,草酸含量高'}]}
|
||||
],
|
||||
priceHistory:[
|
||||
{price:'4.2',time:'2026-05-20 11:00',method:'食材验收价',prevPrice:'5.5',operator:'张明'},
|
||||
{price:'5.5',time:'2026-03-15 09:40',method:'手工录入',prevPrice:'5.0',operator:'王芳'}
|
||||
]},
|
||||
{name:'鸡蛋',code:'0401001',alias:'鸡蛋',firstClass:'蛋类',secondClass:'鸡蛋',icon:'🥚',edible:'88',waterRate:'74.1',dataSource:0,basePrice:'6.5',vegTypes:[],
|
||||
nutri:{calorie:144,protein:13.3,fat:8.8,carbohydrate:2.8,dietary_fiber:0,
|
||||
@@ -506,6 +519,10 @@ const FOODS = [
|
||||
{type:'编辑',title:'完善生物素与胆碱',time:'2026-02-25 16:00',user:'李建国',role:'营养师',dept:'营养管理部',summary:'补充生物素、总胆碱等 4 项指标。',changes:[{field:'生物素',old:'—',new:'20 μg'},{field:'总胆碱',old:'—',new:'294 mg'}]},
|
||||
{type:'价格调整',title:'更新基础定价',time:'2026-04-18 10:20',user:'张明',role:'采购员',dept:'采购部',summary:'蛋价季节性波动,微调。',changes:[{field:'基础定价',old:'7.0 元/100g',new:'6.5 元/100g'}]},
|
||||
{type:'编辑',title:'补充碘含量',time:'2026-07-04 14:50',user:'王秀兰',role:'营养师',dept:'营养管理部',summary:'补充碘元素数据,完善矿物质维度。',changes:[{field:'碘',old:'—',new:'27.2 μg'}]}
|
||||
],
|
||||
priceHistory:[
|
||||
{price:'6.5',time:'2026-04-18 10:20',method:'手工录入',prevPrice:'7.0',operator:'张明'},
|
||||
{price:'7.0',time:'2026-02-20 08:50',method:'食材验收价',prevPrice:'6.8',operator:'王芳'}
|
||||
]},
|
||||
{name:'豆腐(北)',code:'0601001',alias:'北豆腐',firstClass:'豆及豆制品',secondClass:'豆制品',icon:'🧈',edible:'100',waterRate:'82.8',dataSource:1,basePrice:'5.0',vegTypes:[
|
||||
{groupId:'g1',alias:'块类组',specAlias:'大方块',yieldRate:'96',edibleRate:'100',cutSpec:{shape:'块',length:30,width:30,height:20,technique:'直刀'}},
|
||||
@@ -526,6 +543,10 @@ const FOODS = [
|
||||
{type:'价格调整',title:'更新基础定价',time:'2026-05-12 09:30',user:'张明',role:'采购员',dept:'采购部',summary:'豆制品批发价上调。',changes:[{field:'基础定价',old:'4.5 元/100g',new:'5.0 元/100g'}]},
|
||||
{type:'编辑',title:'补充痛风人群提示',time:'2026-06-28 11:45',user:'李建国',role:'营养师',dept:'营养管理部',summary:'新增痛风急性期适量提示。',changes:[{field:'人群备注',old:'—',new:'痛风急性期适量,嘌呤含量偏高'}]},
|
||||
{type:'编辑',title:'修订矿物质数据',time:'2026-08-05 14:00',user:'陈晓燕',role:'营养师',dept:'营养管理部',summary:'对齐《中国食物成分表》修正钙、镁数据。',changes:[{field:'钙',old:'105 mg',new:'138 mg'},{field:'镁',old:'52 mg',new:'63 mg'}]}
|
||||
],
|
||||
priceHistory:[
|
||||
{price:'5.0',time:'2026-05-12 09:30',method:'食材验收价',prevPrice:'4.5',operator:'张明'},
|
||||
{price:'4.5',time:'2026-03-01 14:00',method:'手工录入',prevPrice:'4.2',operator:'王芳'}
|
||||
]}
|
||||
];
|
||||
|
||||
@@ -579,7 +600,8 @@ function renderBasicInfo(f){
|
||||
html += `<div class="profile-field"><div class="profile-field__label">可食部分</div><div class="profile-field__value">${f.edible}%</div></div>`;
|
||||
html += `<div class="profile-field"><div class="profile-field__label">水分</div><div class="profile-field__value">${f.waterRate}%</div></div>`;
|
||||
html += `<div class="profile-field"><div class="profile-field__label">数据来源</div><div class="profile-field__value">${sourceLabel}</div></div>`;
|
||||
const priceHint = f.basePrice ? `<span style="font-size:var(--font-xs);color:var(--text-placeholder);margin-left:8px;font-weight:400;">(2026-06-05 手工录入)</span>` : '';
|
||||
const ph0 = (f.priceHistory && f.priceHistory.length) ? f.priceHistory[0] : null;
|
||||
const priceHint = (f.basePrice && ph0) ? `<span style="font-size:var(--font-xs);color:var(--primary);margin-left:8px;font-weight:400;cursor:pointer;" onclick="openPriceHistoryDialog()">(${ph0.time.slice(0,10)} ${ph0.method})</span>` : '';
|
||||
html += `<div class="profile-field"><div class="profile-field__label">基础定价</div><div class="profile-field__value">${f.basePrice ? f.basePrice + ' 元/100g' : '—'}${priceHint}</div></div>`;
|
||||
let vegTypesValue;
|
||||
if(f.vegTypes && f.vegTypes.length > 0){
|
||||
@@ -1033,6 +1055,34 @@ function confirmDeleteFood(){
|
||||
closeDeleteFoodDialog();
|
||||
alert('已删除该食材');
|
||||
}
|
||||
|
||||
/* ========== 价格变更历史弹窗 ========== */
|
||||
// 打开价格变更历史弹窗,回显当前选中食材的基础定价变更记录
|
||||
function openPriceHistoryDialog(){
|
||||
var f = FOODS[currentFood];
|
||||
var list = f.priceHistory || [];
|
||||
var body = document.getElementById('priceHistoryBody');
|
||||
if(list.length===0){
|
||||
body.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--text-placeholder);padding:40px;">暂无价格变更记录</td></tr>';
|
||||
} else {
|
||||
var rows = '';
|
||||
list.forEach(function(h){
|
||||
rows += '<tr>';
|
||||
rows += '<td>' + (h.price ? h.price + ' 元/100g' : '—') + '</td>';
|
||||
rows += '<td>' + (h.time || '—') + '</td>';
|
||||
rows += '<td>' + (h.method || '—') + '</td>';
|
||||
rows += '<td>' + (h.prevPrice ? h.prevPrice + ' 元/100g' : '—') + '</td>';
|
||||
rows += '<td>' + (h.operator || '—') + '</td>';
|
||||
rows += '</tr>';
|
||||
});
|
||||
body.innerHTML = rows;
|
||||
}
|
||||
document.getElementById('priceHistoryDialog').classList.add('open');
|
||||
}
|
||||
// 关闭价格变更历史弹窗
|
||||
function closePriceHistoryDialog(){
|
||||
document.getElementById('priceHistoryDialog').classList.remove('open');
|
||||
}
|
||||
</script>
|
||||
<!-- 食材分类管理弹窗 -->
|
||||
<div class="dialog-mask" id="categoryDialog">
|
||||
@@ -1103,5 +1153,23 @@ function confirmDeleteFood(){
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 价格变更历史弹窗 -->
|
||||
<div class="dialog-mask" id="priceHistoryDialog">
|
||||
<div class="dialog" style="width:640px;">
|
||||
<div class="dialog__header">
|
||||
<div class="dialog__title">价格变更历史</div>
|
||||
<button class="dialog__close" onclick="closePriceHistoryDialog()">✕</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<table class="cat-table">
|
||||
<thead><tr><th>价格</th><th>更新时间</th><th>更新方式</th><th>变更前价格</th><th>操作人</th></tr></thead>
|
||||
<tbody id="priceHistoryBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="dialog__footer">
|
||||
<button class="btn btn-default" onclick="closePriceHistoryDialog()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -95,6 +95,35 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
.modal-close:hover{background:#f5f5f5;color:var(--text-primary);}
|
||||
.modal-body{padding:24px;overflow-y:auto;flex:1;}
|
||||
|
||||
/* 主 Tab 栏 */
|
||||
.main-tabs{display:flex;gap:0;background:#fff;border-radius:var(--radius-md);padding:0 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.main-tab{padding:14px 20px;font-size:var(--font-base);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;white-space:nowrap;transition:all .2s;user-select:none;}
|
||||
.main-tab:hover{color:var(--primary);}
|
||||
.main-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.main-tab-panel{display:none;}
|
||||
.main-tab-panel.active{display:block;}
|
||||
|
||||
/* 取餐明细 - 视频回放与丢餐提示 */
|
||||
.video-info{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
|
||||
.scale-info{display:flex;gap:24px;padding:12px 16px;background:#F8FAFC;border-radius:var(--radius-md);margin-bottom:16px;font-size:var(--font-sm);}
|
||||
.scale-info__item{color:var(--text-primary);}
|
||||
.scale-info__item strong{color:var(--primary);font-weight:600;}
|
||||
.raw-data-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.raw-data-table thead th{background:#FAFAFA;padding:10px 14px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
.raw-data-table tbody td{padding:12px 14px;color:var(--text-primary);border-bottom:1px solid #f0f0f0;white-space:nowrap;}
|
||||
.raw-data-table tbody tr:last-child td{border-bottom:none;}
|
||||
.raw-data-table tbody tr:hover{background:#F8FBFF;}
|
||||
.video-player{background:#000;border-radius:var(--radius-md);overflow:hidden;}
|
||||
.video-player__screen{position:relative;height:320px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2e 0%,#16213e 50%,#0f3460 100%);}
|
||||
.video-player__placeholder{color:rgba(255,255,255,0.6);font-size:var(--font-sm);letter-spacing:2px;}
|
||||
.video-player__bigplay{position:absolute;width:64px;height:64px;border-radius:50%;background:rgba(255,255,255,0.2);color:#fff;display:flex;align-items:center;justify-content:center;font-size:26px;cursor:pointer;transition:all .2s;}
|
||||
.video-player__bigplay:hover{background:rgba(255,255,255,0.35);}
|
||||
.video-player__bar{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#111;}
|
||||
.video-player__btn{width:32px;height:32px;border:none;border-radius:50%;background:rgba(255,255,255,0.15);color:#fff;cursor:pointer;font-size:14px;}
|
||||
.video-player__time{color:rgba(255,255,255,0.7);font-size:var(--font-xs);white-space:nowrap;}
|
||||
.video-player__progress{flex:1;height:4px;background:rgba(255,255,255,0.2);border-radius:2px;position:relative;}
|
||||
.video-player__progress-fill{width:40%;height:100%;background:var(--primary);border-radius:2px;}
|
||||
|
||||
/* 十维档案 Tab */
|
||||
.profile-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:20px;overflow-x:auto;}
|
||||
.profile-tab{padding:10px 16px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;white-space:nowrap;transition:all .2s;}
|
||||
@@ -300,6 +329,15 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主 Tab 栏 -->
|
||||
<div class="main-tabs">
|
||||
<div class="main-tab active" data-tab="record" onclick="switchMainTab(this,'record')">用户取餐记录</div>
|
||||
<div class="main-tab" data-tab="detail" onclick="switchMainTab(this,'detail')">取餐明细</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab1: 用户取餐记录 -->
|
||||
<div class="main-tab-panel active" data-panel="record">
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>员工姓名</label><input class="filter-input" type="text" placeholder="请输入姓名" style="min-width:120px;"></div>
|
||||
@@ -328,7 +366,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="table-card__title">取餐管理</div>
|
||||
</div>
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<table id="recordTable">
|
||||
<thead>
|
||||
<!-- 交互注释:结算重量=终端结算时电子秤计重;实际重量=结算后复秤电子秤上传的实际重量;重量偏差=实际重量-结算重量,偏差超过±10克(含)显示橙色预警、超过±15克显示红色异常,未复秤显示"—" -->
|
||||
<tr><th>就餐记录号</th><th>姓名</th><th>员工编号</th><th>单位</th><th>部门</th><th>餐次</th><th>取餐方式</th><th>取餐类型</th><th>人员类别</th><th>取餐开始时间</th><th>取餐结束时间</th><th>取餐总数量</th><th>结算重量(克)</th><th>实际重量(克)</th><th>重量偏差(克)</th><th>总金额</th><th>营养评分</th><th>操作</th></tr>
|
||||
@@ -356,6 +394,70 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /Tab1: 用户取餐记录 -->
|
||||
|
||||
<!-- Tab2: 取餐明细 -->
|
||||
<div class="main-tab-panel" data-panel="detail">
|
||||
|
||||
<!-- 取餐明细筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>记录状态</label><select class="filter-select"><option>全部</option><option>正常</option><option>无主记录</option><option>手动修正</option></select></div>
|
||||
<div class="filter-item"><label>用户</label><input class="filter-input" type="text" placeholder="工号 / 姓名" style="min-width:140px;"></div>
|
||||
<div class="filter-item"><label>餐次</label><select class="filter-select"><option>全部</option><option>早餐</option><option>中餐</option><option>晚餐</option><option>加餐</option></select></div>
|
||||
<div class="filter-item"><label>餐品名称</label><input class="filter-input" type="text" placeholder="请输入餐品名称" style="min-width:140px;"></div>
|
||||
<div class="filter-item"><label>取餐方式</label><select class="filter-select"><option>全部</option><option>餐线取餐</option><option>档口取餐</option></select></div>
|
||||
<div class="filter-item"><label>取餐设备</label><select class="filter-select"><option>全部</option><option>一号餐线·2号秤</option><option>一号餐线·3号秤</option><option>一号餐线·5号秤</option><option>二号档口·1号秤</option><option>三号档口·2号秤</option></select></div>
|
||||
<div class="filter-item"><label>就餐记录号</label><input class="filter-input" type="text" placeholder="请输入就餐记录号" style="min-width:140px;"></div>
|
||||
<div class="filter-item"><label>日期范围</label><input class="filter-input" type="date" style="min-width:130px;"> <span style="margin:0 4px;color:#999;">至</span> <input class="filter-input" type="date" style="min-width:130px;"></div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
<button class="btn btn-default">重置</button>
|
||||
</div>
|
||||
|
||||
<!-- 取餐明细操作栏 -->
|
||||
<div class="action-bar">
|
||||
<button class="btn btn-primary"><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>
|
||||
<button class="btn btn-default"><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>
|
||||
</div>
|
||||
|
||||
<!-- 取餐明细数据表格 -->
|
||||
<div class="table-card">
|
||||
<div class="table-card__header">
|
||||
<div class="table-card__title">取餐明细</div>
|
||||
</div>
|
||||
<div class="table-card__content">
|
||||
<table id="detailTable">
|
||||
<thead>
|
||||
<!-- 交互注释:记录状态标识取餐记录与取餐人的对应关系——正常=已关联到取餐人;无主记录=未关联到取餐人(丢餐排查重点);手动修正=经人工核对后补全关联 -->
|
||||
<tr><th>序号</th><th>取用记录ID</th><th>餐次</th><th>餐品名称</th><th>记录状态</th><th>取餐重量(克)</th><th>金额</th><th>用户(工号-姓名)</th><th>人员类别</th><th>取餐时间</th><th>取餐设备</th><th>取餐方式</th><th>取餐类型</th><th>就餐记录号</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>1</td><td>P-20260428-0001</td><td>午餐</td><td>清炒生菜</td><td><span class="tag tag-green">正常</span></td><td>133.0</td><td>¥6.00</td><td>EMP001-张伟</td><td><span class="tag tag-orange">会员</span></td><td>2026-04-28 11:02:15</td><td>一号餐线·3号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-001</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>2</td><td>P-20260428-0002</td><td>午餐</td><td>西红柿鸡蛋</td><td><span class="tag tag-green">正常</span></td><td>114.0</td><td>¥8.00</td><td>EMP001-张伟</td><td><span class="tag tag-orange">会员</span></td><td>2026-04-28 11:03:02</td><td>一号餐线·3号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-001</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>3</td><td>P-20260428-0003</td><td>午餐</td><td>辣子鸡丁</td><td><span class="tag tag-green">正常</span></td><td>79.0</td><td>¥8.00</td><td>EMP001-张伟</td><td><span class="tag tag-orange">会员</span></td><td>2026-04-28 11:04:30</td><td>一号餐线·3号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-001</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>4</td><td>P-20260428-0011</td><td>午餐</td><td>红烧肉</td><td><span class="tag tag-green">正常</span></td><td>85.0</td><td>¥16.00</td><td>EMP003-王磊</td><td><span class="tag tag-gray">非会员</span></td><td>2026-04-28 11:16:40</td><td>一号餐线·5号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-003</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>5</td><td>P-20260428-0021</td><td>午餐</td><td>宫保鸡丁</td><td><span class="tag tag-red">无主记录</span></td><td>110.0</td><td>¥9.50</td><td>—</td><td>—</td><td>2026-04-28 11:25:08</td><td>一号餐线·5号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>—</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>6</td><td>P-20260428-0022</td><td>午餐</td><td>米饭</td><td><span class="tag tag-red">无主记录</span></td><td>200.0</td><td>¥12.00</td><td>—</td><td>—</td><td>2026-04-28 11:25:33</td><td>一号餐线·5号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>—</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>7</td><td>P-20260428-0031</td><td>晚餐</td><td>清蒸鲈鱼</td><td><span class="tag tag-red">无主记录</span></td><td>120.0</td><td>¥8.00</td><td>—</td><td>—</td><td>2026-04-28 18:12:45</td><td>二号档口·1号秤</td><td><span class="tag tag-green">档口取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>—</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>8</td><td>P-20260427-0005</td><td>午餐</td><td>糖醋里脊</td><td><span class="tag tag-orange">手动修正</span></td><td>95.0</td><td>¥12.00</td><td>EMP007-林教授</td><td><span class="tag tag-purple">外部人员</span></td><td>2026-04-27 12:12:20</td><td>二号档口·1号秤</td><td><span class="tag tag-green">档口取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-007</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>9</td><td>P-20260427-0018</td><td>午餐</td><td>余量套餐B</td><td><span class="tag tag-orange">手动修正</span></td><td>195.0</td><td>¥5.00</td><td>EMP102-王保安</td><td><span class="tag tag-blue">工作人员</span></td><td>2026-04-27 14:11:05</td><td>一号餐线·2号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td>SP-001-003</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>10</td><td>P-20260428-0004</td><td>早餐</td><td>小米粥</td><td><span class="tag tag-green">正常</span></td><td>250.0</td><td>¥4.00</td><td>EMP005-陈刚</td><td><span class="tag tag-gray">非会员</span></td><td>2026-04-28 07:49:10</td><td>一号餐线·2号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-005</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>11</td><td>P-20260428-0041</td><td>午餐</td><td>鸡胸肉沙拉</td><td><span class="tag tag-green">正常</span></td><td>180.0</td><td>¥10.80</td><td>EMP006-刘芳</td><td><span class="tag tag-orange">会员</span></td><td>2026-04-28 11:46:22</td><td>三号档口·2号秤</td><td><span class="tag tag-green">档口取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td>M-20260428-006</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
<tr><td>12</td><td>P-20260428-0023</td><td>午餐</td><td>余量套餐A</td><td><span class="tag tag-green">正常</span></td><td>220.0</td><td>¥5.00</td><td>EMP101-张运维</td><td><span class="tag tag-blue">员工</span></td><td>2026-04-28 14:05:50</td><td>一号餐线·2号秤</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td>SP-001-001</td><td class="td-actions"><a class="link">原始数据</a> <a class="link">查看视频</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<span>显示 12 条,共 3,562 条</span>
|
||||
<button class="page-btn">‹</button>
|
||||
<button class="page-btn active">1</button>
|
||||
<button class="page-btn">2</button>
|
||||
<button class="page-btn">3</button>
|
||||
<button class="page-btn">›</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /Tab2: 取餐明细 -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 交互脚本 -->
|
||||
@@ -367,20 +469,25 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
|
||||
const Modal = {
|
||||
open(title, bodyHtml) {
|
||||
this.close();
|
||||
this.stack(title, bodyHtml);
|
||||
},
|
||||
stack(title, bodyHtml) {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'modal-overlay';
|
||||
overlay.innerHTML = `<div class="modal">
|
||||
<div class="modal-header"><div class="modal-title">${title}</div><button class="modal-close" onclick="Modal.close()">×</button></div>
|
||||
<div class="modal-header"><div class="modal-title">${title}</div><button class="modal-close" onclick="Modal.closeTop(this.closest('.modal-overlay'))">×</button></div>
|
||||
<div class="modal-body">${bodyHtml}</div>
|
||||
</div>`;
|
||||
overlay.addEventListener('click', e => { if (e.target === overlay) Modal.close(); });
|
||||
overlay.addEventListener('click', e => { if (e.target === overlay) Modal.closeTop(overlay); });
|
||||
document.body.appendChild(overlay);
|
||||
},
|
||||
close() {
|
||||
const o = document.querySelector('.modal-overlay');
|
||||
if (!o) return;
|
||||
o.classList.add('closing');
|
||||
setTimeout(() => o.remove(), 200);
|
||||
document.querySelectorAll('.modal-overlay').forEach(o => { o.classList.add('closing'); setTimeout(() => o.remove(), 200); });
|
||||
},
|
||||
closeTop(overlay) {
|
||||
if (!overlay) return;
|
||||
overlay.classList.add('closing');
|
||||
setTimeout(() => overlay.remove(), 200);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -575,7 +682,7 @@ function showUserProfile(name) {
|
||||
}
|
||||
|
||||
/* 绑定表格中的"查看用户信息"链接 */
|
||||
document.querySelectorAll('tbody tr').forEach(row => {
|
||||
document.querySelectorAll('#recordTable tbody tr').forEach(row => {
|
||||
const link = row.querySelector('td:last-child .link');
|
||||
const nameCell = row.querySelector('td:nth-child(2)');
|
||||
if (link && nameCell) {
|
||||
@@ -662,15 +769,24 @@ function showMealDetail(row) {
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
const tableRows = items.map(i => `<tr>
|
||||
<td>${i.name}</td>
|
||||
<td>¥${i.price.toFixed(2)}</td>
|
||||
<td>${i.weight.toFixed(1)}</td>
|
||||
<td>${i.cal.toFixed(1)}</td>
|
||||
<td>${i.carb.toFixed(1)}</td>
|
||||
<td>${i.fat.toFixed(1)}</td>
|
||||
<td>${i.protein.toFixed(1)}</td>
|
||||
</tr>`).join('');
|
||||
const dateStr = (recordId.match(/\d{8}/) || ['20260428'])[0].replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
||||
const methodDeviceMap = { '餐线取餐':'一号餐线·3号秤', '档口+餐线':'二号档口·1号秤', '点餐取餐':'一号餐线·3号秤', '外卖/外带':'二号档口·1号秤', '余量取餐':'一号餐线·2号秤' };
|
||||
const device = methodDeviceMap[pickupMethod] || '一号餐线·3号秤';
|
||||
|
||||
const tableRows = items.map((i, idx) => {
|
||||
const itemTime = dateStr + ' ' + startTime + ':' + String(idx * 8).padStart(2, '0');
|
||||
return `<tr>
|
||||
<td>${i.name}</td>
|
||||
<td>¥${i.price.toFixed(2)}</td>
|
||||
<td>${i.weight.toFixed(1)}</td>
|
||||
<td>${i.cal.toFixed(1)}</td>
|
||||
<td>${i.carb.toFixed(1)}</td>
|
||||
<td>${i.fat.toFixed(1)}</td>
|
||||
<td>${i.protein.toFixed(1)}</td>
|
||||
<td><a class="link" onclick="showMealItemRawData('${i.name}', ${i.weight}, '${name}', '${itemTime}', '${device}')">查看</a></td>
|
||||
<td><a class="link" onclick="showMealItemVideo('${i.name}', '${itemTime}', '${recordId}')">查看</a></td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
|
||||
const summaryRow = `<tr>
|
||||
<td>合计</td>
|
||||
@@ -680,12 +796,14 @@ function showMealDetail(row) {
|
||||
<td>${totalCarb.toFixed(1)}克</td>
|
||||
<td>${totalFat.toFixed(1)}克</td>
|
||||
<td>${totalProtein.toFixed(1)}克</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>`;
|
||||
|
||||
const tableHtml = `<div class="meal-section">
|
||||
<div class="meal-section__title">餐品列表</div>
|
||||
<table class="meal-table">
|
||||
<thead><tr><th>餐品名称</th><th>金额</th><th>重量(克)</th><th>热量(千卡)</th><th>碳水化合物(克)</th><th>脂肪(克)</th><th>蛋白质(克)</th></tr></thead>
|
||||
<thead><tr><th>餐品名称</th><th>金额</th><th>重量(克)</th><th>热量(千卡)</th><th>碳水化合物(克)</th><th>脂肪(克)</th><th>蛋白质(克)</th><th>原始数据</th><th>查看视频</th></tr></thead>
|
||||
<tbody>${tableRows}${summaryRow}</tbody>
|
||||
</table>
|
||||
</div>`;
|
||||
@@ -693,13 +811,127 @@ function showMealDetail(row) {
|
||||
Modal.open('取餐详情 — ' + name, infoHtml + tableHtml);
|
||||
}
|
||||
|
||||
function showMealItemRawData(dish, weight, user, time, device) {
|
||||
buildRawDataModal({ dish: dish, statusText: '正常', weight: parseFloat(weight), user: user, time: time, device: device });
|
||||
}
|
||||
|
||||
function showMealItemVideo(dish, time, recordId) {
|
||||
buildVideoModal({ id: recordId, dish: dish, statusText: '正常', time: time });
|
||||
}
|
||||
|
||||
/* 绑定"取餐总数量"列点击事件 */
|
||||
document.querySelectorAll('tbody tr').forEach(row => {
|
||||
document.querySelectorAll('#recordTable tbody tr').forEach(row => {
|
||||
const quantityLink = row.querySelector('td:nth-child(12) .link');
|
||||
if (quantityLink) {
|
||||
quantityLink.addEventListener('click', () => showMealDetail(row));
|
||||
}
|
||||
});
|
||||
|
||||
/* ========== 主 Tab 切换 ========== */
|
||||
function switchMainTab(el, key) {
|
||||
document.querySelectorAll('.main-tab').forEach(t => t.classList.remove('active'));
|
||||
el.classList.add('active');
|
||||
document.querySelectorAll('.main-tab-panel').forEach(p => p.classList.remove('active'));
|
||||
document.querySelector(`.main-tab-panel[data-panel="${key}"]`).classList.add('active');
|
||||
}
|
||||
|
||||
/* ========== 取餐明细 - 原始数据弹窗 ========== */
|
||||
function showRawData(row) {
|
||||
const c = row.querySelectorAll('td');
|
||||
Modal.close();
|
||||
buildRawDataModal({
|
||||
dish: c[3].textContent.trim(),
|
||||
statusText: c[4].textContent.trim(),
|
||||
weight: parseFloat(c[5].textContent.trim()),
|
||||
user: c[7].textContent.trim(),
|
||||
time: c[9].textContent.trim(),
|
||||
device: c[10].textContent.trim()
|
||||
});
|
||||
}
|
||||
|
||||
function buildRawDataModal(data) {
|
||||
const deviceNoMap = { '一号餐线·2号秤':'SC-002', '一号餐线·3号秤':'SC-003', '一号餐线·5号秤':'SC-005', '二号档口·1号秤':'DC-001', '三号档口·2号秤':'DC-002' };
|
||||
const deviceNo = deviceNoMap[data.device] || '—';
|
||||
|
||||
const isOrphan = data.statusText === '无主记录';
|
||||
const isManual = data.statusText === '手动修正';
|
||||
const recStatus = (isOrphan || isManual) ? '未识别' : '已识别';
|
||||
const recUser = (isOrphan || isManual) ? '—' : data.user;
|
||||
const statusTag = recStatus === '已识别' ? '<span class="tag tag-green">已识别</span>' : '<span class="tag tag-red">未识别</span>';
|
||||
|
||||
const samples = [
|
||||
{ t: minusSeconds(data.time, 5), w: (data.weight - 38).toFixed(1), d: '+' + (data.weight - 38).toFixed(1), stable: '否' },
|
||||
{ t: minusSeconds(data.time, 2), w: (data.weight - 9).toFixed(1), d: '+29.0', stable: '否' },
|
||||
{ t: data.time, w: data.weight.toFixed(1), d: '+9.0', stable: '是' }
|
||||
];
|
||||
|
||||
const rowsHtml = samples.map(s => `<tr>
|
||||
<td>${s.t}</td>
|
||||
<td>${s.w}</td>
|
||||
<td>${s.d}</td>
|
||||
<td>${statusTag}</td>
|
||||
<td>${recUser}</td>
|
||||
<td>${s.stable === '是' ? '<span class="tag tag-green">是</span>' : '<span class="tag tag-orange">否</span>'}</td>
|
||||
</tr>`).join('');
|
||||
|
||||
const html = `<div class="scale-info">
|
||||
<div class="scale-info__item">设备编号:<strong>${deviceNo}</strong></div>
|
||||
<div class="scale-info__item">设备名称:<strong>${data.device}</strong></div>
|
||||
</div>
|
||||
<table class="raw-data-table">
|
||||
<thead><tr><th>采样时间</th><th>当前重量(g)</th><th>较上次变化重量(g)</th><th>识别状态</th><th>用户</th><th>是否稳定</th></tr></thead>
|
||||
<tbody>${rowsHtml}</tbody>
|
||||
</table>`;
|
||||
|
||||
Modal.stack('秤原始数据 — ' + data.dish, html);
|
||||
}
|
||||
|
||||
function minusSeconds(timeStr, sec) {
|
||||
const parts = timeStr.split(' ');
|
||||
const date = parts[0];
|
||||
const hms = parts[1].split(':').map(Number);
|
||||
const d = new Date(0);
|
||||
d.setHours(hms[0], hms[1], hms[2] - sec);
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
return date + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
|
||||
}
|
||||
|
||||
/* ========== 取餐明细 - 视频回放弹窗 ========== */
|
||||
function showPickupVideo(row) {
|
||||
const c = row.querySelectorAll('td');
|
||||
Modal.close();
|
||||
buildVideoModal({
|
||||
id: c[1].textContent.trim(),
|
||||
dish: c[3].textContent.trim(),
|
||||
statusText: c[4].textContent.trim(),
|
||||
time: c[9].textContent.trim()
|
||||
});
|
||||
}
|
||||
|
||||
function buildVideoModal(data) {
|
||||
const html = `<div class="video-info">取用记录ID:${data.id} · 取餐时间:${data.time} · 记录状态:${data.statusText}</div>
|
||||
<div class="video-player">
|
||||
<div class="video-player__screen">
|
||||
<div class="video-player__placeholder">取餐过程监控画面</div>
|
||||
<div class="video-player__bigplay">▶</div>
|
||||
</div>
|
||||
<div class="video-player__bar">
|
||||
<button class="video-player__btn">▶</button>
|
||||
<span class="video-player__time">00:00 / 00:18</span>
|
||||
<div class="video-player__progress"><div class="video-player__progress-fill"></div></div>
|
||||
</div>
|
||||
</div>`;
|
||||
Modal.stack('取餐视频回放 — ' + data.dish, html);
|
||||
}
|
||||
|
||||
/* 绑定取餐明细表格操作列 */
|
||||
document.querySelectorAll('#detailTable tbody tr').forEach(row => {
|
||||
const links = row.querySelectorAll('.td-actions .link');
|
||||
if (links.length >= 2) {
|
||||
links[0].addEventListener('click', () => showRawData(row));
|
||||
links[1].addEventListener('click', () => showPickupVideo(row));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,18 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-09-21] feat: 净菜订单生成历史双维度全链路对账 + 食物成分价格变更历史弹窗
|
||||
|
||||
- `prod-clean-order-history.html` 净菜订单生成历史「数据明细」弹窗重构为餐品/食材双维度对账视图:
|
||||
- 新增维度切换 Tab(🍽️ 餐品维度 / 🥬 食材维度),弹窗加宽至 1240px 并支持横向滚动
|
||||
- 餐品维度 12 列双层分组表头:生重环节(推荐生重/下单重量/组配生重)、熟重环节(推荐熟重/烹制熟重)、取餐环节(预测人数/实际人数/取餐重量),三组分色,每个实际值下方带差异率徽标
|
||||
- 食材维度 9 列聚合表:同一食材跨餐品合并一行,采供环节(采购数量/实收数量)、加工环节(出净率显式列/净菜产出量)、组配环节(组配实际用量),毛/净口径经出净率换算桥接
|
||||
- 食材行可展开查看被哪些餐品使用、各餐品用量贡献与占比;餐品行展开子表列名同步更名
|
||||
- 新增 ING_YIELD 出净率表与 COOK_YIELD 熟成率常量,各环节数值由确定性伪随机派生;生成中订单下游环节统一「—」占位
|
||||
- 表头文案不含「AI」字样,统一用「推荐生重/推荐熟重/推荐用量」,一级分组表头不带口径后缀
|
||||
- `din-ingredient.html` 食物成分:
|
||||
- 基础定价旁的定价来源提示改为可点击入口,新增「价格变更历史」弹窗(价格/更新时间/更新方式/变更前价格/操作人)
|
||||
- FOODS 数据模型新增 priceHistory 价格变更记录数组
|
||||
|
||||
## [2026-09-14] feat: 餐品定价规格拆分为餐品规格与餐品净菜包规格
|
||||
|
||||
- `din-dish-add.html` 新增/编辑餐品:
|
||||
|
||||
@@ -0,0 +1,749 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
|
||||
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
|
||||
.top-bar__right .btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 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);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:0.65;margin-right:2px;}
|
||||
.sidebar-item.active .mi{opacity:0.9;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
/* 页面标题行 */
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__left{display:flex;align-items:baseline;gap:12px;}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
|
||||
.page-header__desc{font-size:var(--font-sm);color:var(--text-placeholder);}
|
||||
.page-header__right{display:flex;align-items:center;gap:12px;}
|
||||
/* 搜索区 */
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
|
||||
.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-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:180px;}
|
||||
.filter-input:focus{border-color:var(--primary);}
|
||||
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;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;cursor:pointer;min-width:130px;}
|
||||
.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;}
|
||||
.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);text-decoration:none;}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
/* 表格容器 */
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-card__content{overflow-x:auto;overflow-y:hidden;}
|
||||
.table-card__content table{min-width:100%;white-space:nowrap;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:12px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
/* 餐次/人数多行单元格 */
|
||||
.meal-cell{line-height:1.9;}
|
||||
/* 标签 */
|
||||
.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);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.tag-cyan{background:#E6FFFB;color:#08979C;border:1px solid #87E8DE;}
|
||||
.tag-cyan::before{content:'';display:inline-block;width:6px;height:6px;border-radius:50%;background:#13C2C2;margin-right:5px;vertical-align:middle;animation:statusPulse 1.4s ease-in-out infinite;}
|
||||
@keyframes statusPulse{0%,100%{opacity:0.3;transform:scale(0.8);}50%{opacity:1;transform:scale(1.2);}}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
|
||||
/* 分页 */
|
||||
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
|
||||
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);margin-right:8px;}
|
||||
.page-btn{min-width:32px;height:32px;padding:0 8px;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):not(.active){color:var(--primary);border-color:var(--primary);}
|
||||
.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
|
||||
.page-btn:disabled{color:#ccc;cursor:not-allowed;background:#f5f5f5;}
|
||||
/* 弹窗 */
|
||||
.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.show{display:flex;}
|
||||
.modal{background:#fff;border-radius:var(--radius-lg);width:1240px;max-width:94vw;max-height:88vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.15);}
|
||||
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);}
|
||||
.modal__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.modal__close{width:32px;height:32px;border:none;background:none;font-size:18px;cursor:pointer;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;color:var(--text-secondary);}
|
||||
.modal__close:hover{background:#f5f5f5;color:var(--text-primary);}
|
||||
.modal__body{padding:20px 24px;overflow-y:auto;overflow-x:auto;flex:1;}
|
||||
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
|
||||
/* 弹窗概要 */
|
||||
.detail-summary{display:flex;flex-wrap:wrap;gap:20px 40px;padding:14px 16px;background:#F7F9FC;border-radius:var(--radius-sm);margin-bottom:16px;}
|
||||
.detail-summary__item{display:flex;gap:8px;font-size:var(--font-sm);}
|
||||
.detail-summary__label{color:var(--text-placeholder);}
|
||||
.detail-summary__value{color:var(--text-primary);font-weight:500;}
|
||||
/* 弹窗内餐品表 */
|
||||
.dish-table{width:100%;border-collapse:collapse;border:1px solid var(--border);}
|
||||
.dish-table thead th{background:#F7F9FC;padding:8px 12px;font-size:var(--font-xs);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);text-align:left;white-space:nowrap;}
|
||||
.dish-table tbody td{padding:10px 12px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid #f0f0f0;white-space:nowrap;}
|
||||
.dish-table tr.dish-row{cursor:pointer;}
|
||||
.dish-table tr.dish-row:hover{background:#F8FBFF;}
|
||||
.dish-table tr.dish-row.expanded{background:#F0F7FF;}
|
||||
.dish-table tr.dish-row:last-child td{border-bottom:none;}
|
||||
.dish-table tr.ingredient-row{background:#FBFCFE;}
|
||||
.dish-table tr.ingredient-row td{padding:0;border-bottom:1px solid #f0f0f0;}
|
||||
.expand-arrow{display:inline-block;width:20px;text-align:center;transition:transform .2s;font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.expand-arrow.expanded{transform:rotate(90deg);}
|
||||
/* 食材子表 */
|
||||
.ing-table{width:100%;border-collapse:collapse;}
|
||||
.ing-table thead th{background:#FAFBFC;padding:6px 12px 6px 36px;font-size:var(--font-xs);color:var(--text-placeholder);font-weight:500;border-bottom:1px dashed #e5e5e5;text-align:left;white-space:nowrap;}
|
||||
.ing-table tbody td{padding:8px 12px 8px 36px;font-size:var(--font-xs);color:var(--text-secondary);border-bottom:1px dashed #f0f0f0;white-space:nowrap;}
|
||||
.ing-table tbody td:first-child{padding-left:36px;font-weight:500;color:var(--text-primary);}
|
||||
.ing-table tbody tr:last-child td{border-bottom:none;}
|
||||
.ing-type{display:inline-block;padding:1px 7px;border-radius:8px;font-size:10px;line-height:16px;}
|
||||
.ing-type--main{background:var(--primary-light);color:var(--primary);}
|
||||
.ing-type--aux{background:#F6FFED;color:var(--success);}
|
||||
.ing-type--season{background:#FFF7E6;color:var(--warning);}
|
||||
/* 维度切换 Tab */
|
||||
.dim-tabs{display:flex;gap:8px;margin-bottom:14px;}
|
||||
.dim-tab{height:34px;padding:0 22px;border-radius:6px;border:1px solid var(--border);background:#fff;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
|
||||
.dim-tab:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.dim-tab.active{background:var(--primary);border-color:var(--primary);color:#fff;font-weight:500;}
|
||||
/* 环节分组表头(生重/熟重/取餐、采供/加工/组配 分色) */
|
||||
.dish-table thead th.th-group{font-weight:600;border-bottom:none;}
|
||||
.dish-table thead th.th-group--raw,.dish-table thead th.th-group--ing-purchase{background:#F0F7FF;color:#1D6FE8;text-align:center;}
|
||||
.dish-table thead th.th-group--cook,.dish-table thead th.th-group--ing-process{background:#FFF7E6;color:#D48806;text-align:center;}
|
||||
.dish-table thead th.th-group--pickup,.dish-table thead th.th-group--ing-combo{background:#F6FFED;color:#389E0D;text-align:center;}
|
||||
/* 预测值/实际值单元格 */
|
||||
.val{font-weight:600;}
|
||||
.val--base{color:var(--primary);}
|
||||
.val-empty{color:var(--text-placeholder);font-weight:400;}
|
||||
.val-diff{margin-top:3px;line-height:1;}
|
||||
.tag--mini{font-size:10px;padding:1px 6px;border-radius:8px;}
|
||||
/* 空态 */
|
||||
.empty-state{text-align:center;color:var(--text-placeholder);padding:60px 0;font-size:var(--font-sm);}
|
||||
.empty-state .icon{font-size:40px;display:block;margin-bottom:12px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav">
|
||||
<a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html" class="active">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a>
|
||||
</nav>
|
||||
<div class="top-bar__right"><a class="btn-back" href="../../index.html">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">健康生产</div>
|
||||
<a href="prod-qc-daily.html" class="sidebar-item"><span class="mi">🛡️</span>食安监控</a>
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item active"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
<a href="prod-proc-stats.html" class="sidebar-item"><span class="mi">📊</span>生产统计</a>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<!-- 页面标题行 -->
|
||||
<div class="page-header">
|
||||
<div class="page-header__left">
|
||||
<span class="page-header__title">净菜订单生成历史</span>
|
||||
<span class="page-header__desc">自动生成的净菜订单记录,点击「数据明细」按餐品/食材双维度追溯下单、组配、烹制、取餐各环节准确性</span>
|
||||
</div>
|
||||
<div class="page-header__right">
|
||||
<a class="btn btn-default" href="prod-clean-order.html">← 返回净菜订单</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索区 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>订单号</label><input class="filter-input" id="fOrderNo" placeholder="请输入订单号"></div>
|
||||
<div class="filter-item"><label>生成状态</label><select class="filter-select" id="fStatus"><option value="">全部</option><option value="generating">生成中</option><option value="completed">已完成</option></select></div>
|
||||
<div class="filter-item"><label>生成餐次</label><select class="filter-select" id="fMeal"><option value="">全部</option><option value="早餐">早餐</option><option value="午餐">午餐</option><option value="晚餐">晚餐</option></select></div>
|
||||
<div class="filter-item"><label>食谱日期</label><input class="filter-input" type="date" id="fDate"></div>
|
||||
<button class="btn btn-primary" onclick="applyFilter()">查询</button>
|
||||
<button class="btn btn-default" onclick="resetFilter()">重置</button>
|
||||
</div>
|
||||
|
||||
<!-- 生成记录表格(订单维度) -->
|
||||
<div class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">生成记录</div><div class="table-card__title" style="font-weight:400;color:var(--text-secondary);" id="tableSubTitle"></div></div>
|
||||
<div class="table-card__content">
|
||||
<table id="historyTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:56px;">序号</th>
|
||||
<th>订单号</th>
|
||||
<th>食谱日期</th>
|
||||
<th>生成方式</th>
|
||||
<th>生成状态</th>
|
||||
<th>餐次</th>
|
||||
<th>人数</th>
|
||||
<th>菜品种类</th>
|
||||
<th>预定配送时间</th>
|
||||
<th>开始生成时间</th>
|
||||
<th>生成成功时间</th>
|
||||
<th>创建人</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="historyTableBody"></tbody>
|
||||
</table>
|
||||
<div class="empty-state" id="historyEmpty" style="display:none;"><span class="icon">📭</span>暂无符合条件的净菜订单生成记录</div>
|
||||
</div>
|
||||
<div class="pagination" id="pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据明细弹窗 -->
|
||||
<div class="modal-mask" id="modal-detail">
|
||||
<div class="modal">
|
||||
<div class="modal__header">
|
||||
<div class="modal__title">数据明细 — <span id="detailOrderNo"></span></div>
|
||||
<button class="modal__close" onclick="closeDetail()">✕</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<div class="detail-summary" id="detailSummary"></div>
|
||||
<div class="dim-tabs">
|
||||
<button class="dim-tab active" id="tabDish" onclick="switchDim('dish')">🍽️ 餐品维度</button>
|
||||
<button class="dim-tab" id="tabIng" onclick="switchDim('ing')">🥬 食材维度</button>
|
||||
</div>
|
||||
<div id="dimDishWrap">
|
||||
<table class="dish-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:28px;" rowspan="2"></th>
|
||||
<th rowspan="2">餐次</th>
|
||||
<th rowspan="2">餐品名称</th>
|
||||
<th rowspan="2">规格</th>
|
||||
<th colspan="3" class="th-group th-group--raw">生重环节</th>
|
||||
<th colspan="2" class="th-group th-group--cook">熟重环节</th>
|
||||
<th colspan="3" class="th-group th-group--pickup">取餐环节</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>推荐生重(kg)</th><th>下单重量(kg)</th><th>组配生重(kg)</th>
|
||||
<th>推荐熟重(kg)</th><th>烹制熟重(kg)</th>
|
||||
<th>预测人数</th><th>实际人数</th><th>取餐重量(kg)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="detailDishBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="dimIngWrap" style="display:none;">
|
||||
<table class="dish-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:28px;" rowspan="2"></th>
|
||||
<th rowspan="2">食材名称</th>
|
||||
<th rowspan="2">类别</th>
|
||||
<th rowspan="2">推荐用量(kg·净)</th>
|
||||
<th colspan="2" class="th-group th-group--ing-purchase">采供环节</th>
|
||||
<th colspan="2" class="th-group th-group--ing-process">加工环节</th>
|
||||
<th class="th-group th-group--ing-combo">组配环节</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>采购数量(kg·毛)</th><th>实收数量(kg·毛)</th>
|
||||
<th>出净率</th><th>净菜产出量(kg·净)</th>
|
||||
<th>组配实际用量(kg·净)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="detailIngBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn-default" onclick="closeDetail()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ========== 工具函数 ========== */
|
||||
function pad2(n){return String(n).padStart(2,'0');}
|
||||
function dateOffsetStr(offset){
|
||||
var d=new Date();
|
||||
d.setDate(d.getDate()+offset);
|
||||
return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate());
|
||||
}
|
||||
|
||||
/* ========== 餐品食材配方 BOM(每份基准克重) ========== */
|
||||
var recipeBom={
|
||||
'宫保鸡丁':[{name:'鸡胸肉',type:'主材',perServing:120},{name:'花生米',type:'辅材',perServing:30},{name:'黄瓜丁',type:'辅材',perServing:40},{name:'干辣椒',type:'调料',perServing:10},{name:'葱姜蒜',type:'调料',perServing:15}],
|
||||
'清蒸鲈鱼':[{name:'鲈鱼',type:'主材',perServing:200},{name:'姜片',type:'调料',perServing:10},{name:'葱丝',type:'调料',perServing:8}],
|
||||
'红烧肉':[{name:'五花肉',type:'主材',perServing:200},{name:'酱油',type:'调料',perServing:15},{name:'冰糖',type:'调料',perServing:10},{name:'八角',type:'调料',perServing:3}],
|
||||
'麻婆豆腐':[{name:'嫩豆腐',type:'主材',perServing:200},{name:'肉末',type:'辅材',perServing:50},{name:'豆瓣酱',type:'调料',perServing:12},{name:'花椒',type:'调料',perServing:2}],
|
||||
'番茄炒蛋':[{name:'番茄',type:'主材',perServing:150},{name:'鸡蛋',type:'主材',perServing:100},{name:'葱花',type:'调料',perServing:5}],
|
||||
'炒时蔬':[{name:'西兰花',type:'主材',perServing:120},{name:'胡萝卜',type:'辅材',perServing:50},{name:'大蒜',type:'调料',perServing:8}],
|
||||
'小米粥':[{name:'小米',type:'主材',perServing:80},{name:'清水',type:'辅材',perServing:250}],
|
||||
'煮鸡蛋':[{name:'鸡蛋',type:'主材',perServing:60}],
|
||||
'白粥':[{name:'大米',type:'主材',perServing:60},{name:'清水',type:'辅材',perServing:300}],
|
||||
'豆浆':[{name:'黄豆',type:'主材',perServing:50},{name:'清水',type:'辅材',perServing:300}],
|
||||
'馒头':[{name:'面粉',type:'主材',perServing:100},{name:'酵母',type:'调料',perServing:2}],
|
||||
'茶叶蛋':[{name:'鸡蛋',type:'主材',perServing:60},{name:'茶叶',type:'调料',perServing:5}],
|
||||
'鱼香肉丝':[{name:'猪里脊',type:'主材',perServing:120},{name:'木耳',type:'辅材',perServing:30},{name:'胡萝卜',type:'辅材',perServing:30},{name:'青椒',type:'辅材',perServing:30},{name:'豆瓣酱',type:'调料',perServing:10}],
|
||||
'糖醋排骨':[{name:'猪排骨',type:'主材',perServing:180},{name:'白糖',type:'调料',perServing:20},{name:'香醋',type:'调料',perServing:10}],
|
||||
'清炒西兰花':[{name:'西兰花',type:'主材',perServing:150},{name:'大蒜',type:'调料',perServing:8}],
|
||||
'紫菜蛋花汤':[{name:'紫菜',type:'主材',perServing:5},{name:'鸡蛋',type:'辅材',perServing:50},{name:'清水',type:'辅材',perServing:300}],
|
||||
'米饭':[{name:'大米',type:'主材',perServing:100},{name:'清水',type:'辅材',perServing:150}],
|
||||
'糖醋里脊':[{name:'猪里脊',type:'主材',perServing:150},{name:'白糖',type:'调料',perServing:20},{name:'香醋',type:'调料',perServing:10}],
|
||||
'冬瓜排骨汤':[{name:'冬瓜',type:'主材',perServing:120},{name:'猪排骨',type:'辅材',perServing:100},{name:'清水',type:'辅材',perServing:300}],
|
||||
'酸奶':[{name:'酸奶',type:'主材',perServing:200}]
|
||||
};
|
||||
var ING_TYPE_CLS={'主材':'ing-type--main','辅材':'ing-type--aux','调料':'ing-type--season'};
|
||||
/* 食材出净率(毛菜→净菜换算桥梁,食材维度显式露出) */
|
||||
var ING_YIELD={'鸡胸肉':0.88,'花生米':0.98,'黄瓜':0.85,'干辣椒':1,'葱姜蒜':0.9,'鲈鱼':0.82,'姜片':0.9,'葱丝':0.9,'五花肉':0.88,'酱油':1,'冰糖':1,'八角':1,'嫩豆腐':0.95,'肉末':0.9,'豆瓣酱':1,'花椒':1,'番茄':0.92,'鸡蛋':0.98,'葱花':0.9,'西兰花':0.82,'胡萝卜':0.88,'大蒜':0.88,'小米':1,'清水':1,'大米':1,'黄豆':1,'面粉':1,'酵母':1,'猪里脊':0.9,'木耳':1,'青椒':0.88,'猪排骨':0.85,'白糖':1,'香醋':1,'紫菜':1,'冬瓜':0.85,'酸奶':1,'茶叶':1};
|
||||
/* AI 预测熟成率(生重→熟重) */
|
||||
var COOK_YIELD=0.85;
|
||||
function round2(n){return parseFloat(n.toFixed(2));}
|
||||
|
||||
/* ========== 净菜订单生成历史数据(订单维度,一个订单可含多个餐次) ========== */
|
||||
var genOrders=[
|
||||
{orderNo:'CO-1001',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'06:30',startTime:'05:30:00',successTime:'05:31:03',actualRatio:0.98,
|
||||
meals:[{meal:'早餐',dinerCount:80}],
|
||||
details:[
|
||||
{meal:'早餐',foodName:'小米粥',spec:'300g/标准份',dinerCount:80,servings:80,needSeasoning:false,actualRatio:0.97},
|
||||
{meal:'早餐',foodName:'煮鸡蛋',spec:'60g/标准份',dinerCount:80,servings:80,needSeasoning:false,actualRatio:1.02},
|
||||
{meal:'早餐',foodName:'白粥',spec:'300g/标准份',dinerCount:80,servings:80,needSeasoning:false,actualRatio:0.99},
|
||||
{meal:'早餐',foodName:'豆浆',spec:'350g/标准份',dinerCount:80,servings:80,needSeasoning:false,actualRatio:0.98}
|
||||
]},
|
||||
{orderNo:'CO-1002',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'10:00',startTime:'09:30:00',successTime:'09:31:12',actualRatio:0.97,
|
||||
meals:[{meal:'午餐',dinerCount:120}],
|
||||
details:[
|
||||
{meal:'午餐',foodName:'宫保鸡丁',spec:'500g/标准份',dinerCount:120,servings:120,needSeasoning:true,actualRatio:0.96},
|
||||
{meal:'午餐',foodName:'清蒸鲈鱼',spec:'600g/标准份',dinerCount:120,servings:120,needSeasoning:false,actualRatio:1.03},
|
||||
{meal:'午餐',foodName:'红烧肉',spec:'500g/标准份',dinerCount:120,servings:120,needSeasoning:true,actualRatio:1.04},
|
||||
{meal:'午餐',foodName:'番茄炒蛋',spec:'400g/标准份',dinerCount:120,servings:120,needSeasoning:false,actualRatio:0.97}
|
||||
]},
|
||||
{orderNo:'CO-1003',dayOffset:0,genType:'auto',genStatus:'generating',creator:'系统',deliverTime:'16:00',startTime:'14:30:00',successTime:'',actualRatio:0.99,
|
||||
meals:[{meal:'早餐',dinerCount:80},{meal:'晚餐',dinerCount:100}],
|
||||
details:[
|
||||
{meal:'早餐',foodName:'馒头',spec:'100g/标准份',dinerCount:80,servings:160,needSeasoning:false,actualRatio:1.01},
|
||||
{meal:'早餐',foodName:'茶叶蛋',spec:'60g/标准份',dinerCount:80,servings:80,needSeasoning:true,actualRatio:1.03},
|
||||
{meal:'晚餐',foodName:'米饭',spec:'250g/标准份',dinerCount:100,servings:200,needSeasoning:false,actualRatio:0.98},
|
||||
{meal:'晚餐',foodName:'宫保鸡丁',spec:'500g/标准份',dinerCount:100,servings:100,needSeasoning:true,actualRatio:0.96},
|
||||
{meal:'晚餐',foodName:'馒头',spec:'100g/标准份',dinerCount:100,servings:200,needSeasoning:false,actualRatio:1.00}
|
||||
]},
|
||||
{orderNo:'CO-1004',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'16:00',startTime:'13:40:00',successTime:'13:41:20',actualRatio:1.01,
|
||||
meals:[{meal:'午餐',dinerCount:110},{meal:'晚餐',dinerCount:90}],
|
||||
details:[
|
||||
{meal:'午餐',foodName:'糖醋排骨',spec:'350g/标准份',dinerCount:110,servings:110,needSeasoning:true,actualRatio:0.95},
|
||||
{meal:'午餐',foodName:'米饭',spec:'250g/标准份',dinerCount:110,servings:220,needSeasoning:false,actualRatio:0.98},
|
||||
{meal:'午餐',foodName:'鱼香肉丝',spec:'400g/标准份',dinerCount:110,servings:110,needSeasoning:true,actualRatio:0.99},
|
||||
{meal:'晚餐',foodName:'米饭',spec:'250g/标准份',dinerCount:90,servings:180,needSeasoning:false,actualRatio:0.98},
|
||||
{meal:'晚餐',foodName:'红烧肉',spec:'500g/标准份',dinerCount:90,servings:90,needSeasoning:true,actualRatio:1.04},
|
||||
{meal:'晚餐',foodName:'清蒸鲈鱼',spec:'600g/标准份',dinerCount:90,servings:90,needSeasoning:false,actualRatio:0.99}
|
||||
]},
|
||||
{orderNo:'CO-1005',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'16:00',startTime:'14:05:00',successTime:'14:06:11',actualRatio:1.01,
|
||||
meals:[{meal:'晚餐',dinerCount:95}],
|
||||
details:[
|
||||
{meal:'晚餐',foodName:'麻婆豆腐',spec:'400g/标准份',dinerCount:95,servings:95,needSeasoning:true,actualRatio:1.01},
|
||||
{meal:'晚餐',foodName:'炒时蔬',spec:'300g/标准份',dinerCount:95,servings:95,needSeasoning:false,actualRatio:1.05},
|
||||
{meal:'晚餐',foodName:'冬瓜排骨汤',spec:'400g/标准份',dinerCount:95,servings:95,needSeasoning:true,actualRatio:1.03}
|
||||
]},
|
||||
{orderNo:'CO-1006',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'06:30',startTime:'05:20:00',successTime:'05:20:58',actualRatio:0.99,
|
||||
meals:[{meal:'早餐',dinerCount:75}],
|
||||
details:[
|
||||
{meal:'早餐',foodName:'馒头',spec:'100g/标准份',dinerCount:75,servings:150,needSeasoning:false,actualRatio:1.01},
|
||||
{meal:'早餐',foodName:'煮鸡蛋',spec:'60g/标准份',dinerCount:75,servings:75,needSeasoning:false,actualRatio:1.02},
|
||||
{meal:'早餐',foodName:'豆浆',spec:'350g/标准份',dinerCount:75,servings:75,needSeasoning:false,actualRatio:0.98}
|
||||
]},
|
||||
{orderNo:'CO-1007',dayOffset:0,genType:'auto',genStatus:'generating',creator:'系统',deliverTime:'10:00',startTime:'09:40:00',successTime:'',actualRatio:0.98,
|
||||
meals:[{meal:'午餐',dinerCount:130}],
|
||||
details:[
|
||||
{meal:'午餐',foodName:'宫保鸡丁',spec:'500g/标准份',dinerCount:130,servings:130,needSeasoning:true,actualRatio:0.96},
|
||||
{meal:'午餐',foodName:'鱼香肉丝',spec:'400g/标准份',dinerCount:130,servings:130,needSeasoning:true,actualRatio:0.99},
|
||||
{meal:'午餐',foodName:'紫菜蛋花汤',spec:'350g/标准份',dinerCount:130,servings:130,needSeasoning:false,actualRatio:1.00},
|
||||
{meal:'午餐',foodName:'米饭',spec:'250g/标准份',dinerCount:130,servings:260,needSeasoning:false,actualRatio:0.98}
|
||||
]},
|
||||
{orderNo:'CO-1008',dayOffset:0,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'16:00',startTime:'14:20:00',successTime:'14:21:05',actualRatio:0.99,
|
||||
meals:[{meal:'晚餐',dinerCount:85}],
|
||||
details:[
|
||||
{meal:'晚餐',foodName:'糖醋里脊',spec:'350g/标准份',dinerCount:85,servings:85,needSeasoning:true,actualRatio:0.96},
|
||||
{meal:'晚餐',foodName:'清炒西兰花',spec:'300g/标准份',dinerCount:85,servings:85,needSeasoning:false,actualRatio:1.02},
|
||||
{meal:'晚餐',foodName:'酸奶',spec:'200g/标准份',dinerCount:85,servings:85,needSeasoning:false,actualRatio:0.97}
|
||||
]},
|
||||
{orderNo:'CO-1009',dayOffset:-1,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'10:00',startTime:'09:20:00',successTime:'09:21:30',actualRatio:0.99,
|
||||
meals:[{meal:'午餐',dinerCount:90}],
|
||||
details:[
|
||||
{meal:'午餐',foodName:'番茄炒蛋',spec:'400g/标准份',dinerCount:90,servings:90,needSeasoning:false,actualRatio:0.97},
|
||||
{meal:'午餐',foodName:'炒时蔬',spec:'300g/标准份',dinerCount:90,servings:90,needSeasoning:false,actualRatio:1.05}
|
||||
]},
|
||||
{orderNo:'CO-1010',dayOffset:-1,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'16:00',startTime:'14:10:00',successTime:'14:11:45',actualRatio:1.02,
|
||||
meals:[{meal:'晚餐',dinerCount:90}],
|
||||
details:[
|
||||
{meal:'晚餐',foodName:'麻婆豆腐',spec:'400g/标准份',dinerCount:90,servings:90,needSeasoning:true,actualRatio:1.01},
|
||||
{meal:'晚餐',foodName:'红烧肉',spec:'500g/标准份',dinerCount:90,servings:90,needSeasoning:true,actualRatio:0.98}
|
||||
]},
|
||||
{orderNo:'CO-1011',dayOffset:-1,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'06:30',startTime:'05:15:00',successTime:'05:16:02',actualRatio:0.99,
|
||||
meals:[{meal:'早餐',dinerCount:70}],
|
||||
details:[
|
||||
{meal:'早餐',foodName:'白粥',spec:'300g/标准份',dinerCount:70,servings:70,needSeasoning:false,actualRatio:0.99},
|
||||
{meal:'早餐',foodName:'茶叶蛋',spec:'60g/标准份',dinerCount:70,servings:70,needSeasoning:true,actualRatio:1.03},
|
||||
{meal:'早餐',foodName:'豆浆',spec:'350g/标准份',dinerCount:70,servings:70,needSeasoning:false,actualRatio:0.98}
|
||||
]},
|
||||
{orderNo:'CO-1012',dayOffset:-1,genType:'auto',genStatus:'completed',creator:'系统',deliverTime:'10:00',startTime:'09:10:00',successTime:'09:11:10',actualRatio:1.00,
|
||||
meals:[{meal:'午餐',dinerCount:100}],
|
||||
details:[
|
||||
{meal:'午餐',foodName:'宫保鸡丁',spec:'500g/标准份',dinerCount:100,servings:100,needSeasoning:true,actualRatio:0.96},
|
||||
{meal:'午餐',foodName:'清蒸鲈鱼',spec:'600g/标准份',dinerCount:100,servings:100,needSeasoning:false,actualRatio:1.03},
|
||||
{meal:'午餐',foodName:'米饭',spec:'250g/标准份',dinerCount:100,servings:200,needSeasoning:false,actualRatio:0.98}
|
||||
]}
|
||||
];
|
||||
|
||||
/* 基于名称的确定性哈希 */
|
||||
function nameHash(s){
|
||||
var h=0;
|
||||
for(var i=0;i<s.length;i++){h=(h*31+s.charCodeAt(i))%1000;}
|
||||
return h;
|
||||
}
|
||||
/* ========== 派生:为订单每个餐品补齐双维度全链路数据 ========== */
|
||||
/* 餐品维度:AI 预测双值(生重/熟重)+ 下单/组配/烹制/取餐各环节实际值 */
|
||||
function deriveDetail(order){
|
||||
var generating=order.genStatus==='generating';
|
||||
return order.details.map(function(d){
|
||||
var bom=recipeBom[d.foodName]||[];
|
||||
var baseRatio=d.actualRatio!=null?d.actualRatio:(order.actualRatio||1);
|
||||
var perDishGram=bom.reduce(function(s,i){return s+i.perServing;},0);
|
||||
var recRaw=parseFloat((perDishGram*d.servings/1000).toFixed(2));
|
||||
var recCooked=parseFloat((recRaw*COOK_YIELD).toFixed(2));
|
||||
/* 下游环节实际值:生成中订单环节未发生,置 null 由展示层占位 */
|
||||
var orderWeight=null,comboRaw=null,cookCooked=null,actualDiner=null,pickupWeight=null;
|
||||
if(!generating){
|
||||
var seed=nameHash(d.foodName+d.meal);
|
||||
orderWeight=round2(recRaw*(1+(seed%9-4)/100));
|
||||
comboRaw=round2(orderWeight*(1+((seed>>2)%9-4)/100));
|
||||
cookCooked=round2(comboRaw*(COOK_YIELD+((seed>>3)%7-3)/100));
|
||||
actualDiner=Math.round(d.dinerCount*(1+((seed>>4)%11-5)/100));
|
||||
pickupWeight=round2(cookCooked*(0.88+(seed>>5)%7/100));
|
||||
}
|
||||
var ingredients=bom.map(function(ing){
|
||||
var ingRec=parseFloat((ing.perServing*d.servings/1000).toFixed(3));
|
||||
var offset=(nameHash(ing.name)%13-6)/100;
|
||||
var ingRatio=baseRatio*(1+offset);
|
||||
var ingAct=parseFloat((ingRec*ingRatio).toFixed(3));
|
||||
return {name:ing.name,type:ing.type,perServing:ing.perServing,rec:ingRec,act:ingAct};
|
||||
});
|
||||
return {
|
||||
meal:d.meal,foodName:d.foodName,spec:d.spec,dinerCount:d.dinerCount,servings:d.servings,needSeasoning:d.needSeasoning,
|
||||
recRaw:recRaw,recCooked:recCooked,
|
||||
orderWeight:orderWeight,comboRaw:comboRaw,cookCooked:cookCooked,actualDiner:actualDiner,pickupWeight:pickupWeight,
|
||||
ingredients:ingredients
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/* 食材维度:同一食材跨餐品合并一行,链路覆盖 AI 用量→采购/实收(毛)→净菜产出(净)→组配实际(净) */
|
||||
function deriveIngredients(order){
|
||||
var generating=order.genStatus==='generating';
|
||||
var map={};
|
||||
deriveDetail(order).forEach(function(d){
|
||||
d.ingredients.forEach(function(ing){
|
||||
if(!map[ing.name]){
|
||||
map[ing.name]={name:ing.name,type:ing.type,aiUsed:0,seed:nameHash(ing.name),
|
||||
yieldRate:ING_YIELD[ing.name]!=null?ING_YIELD[ing.name]:0.9,usedBy:[]};
|
||||
}
|
||||
map[ing.name].aiUsed=parseFloat((map[ing.name].aiUsed+ing.rec).toFixed(3));
|
||||
map[ing.name].usedBy.push({meal:d.meal,foodName:d.foodName,qty:ing.rec});
|
||||
});
|
||||
});
|
||||
return Object.keys(map).map(function(k){
|
||||
var m=map[k],ai=m.aiUsed;
|
||||
if(!generating){
|
||||
var safety=1+(m.seed%7)/100; /* 采购安全余量 0~6% */
|
||||
m.purchase=parseFloat((ai/m.yieldRate*safety).toFixed(3)); /* 采购(毛)=净用量按出净率反推 */
|
||||
m.received=parseFloat((m.purchase*(1-(m.seed%4)/100)).toFixed(3)); /* 实收(毛)含收货损耗 0~3% */
|
||||
m.netOut=parseFloat((m.received*m.yieldRate).toFixed(3)); /* 净菜产出(净)=实收×出净率 */
|
||||
m.comboUsed=parseFloat((ai*(1+(m.seed%13-6)/100)).toFixed(3)); /* 组配实际(净)±6% */
|
||||
}else{
|
||||
m.purchase=m.received=m.netOut=m.comboUsed=null;
|
||||
}
|
||||
return m;
|
||||
});
|
||||
}
|
||||
|
||||
/* ========== 差异率 ========== */
|
||||
function diffInfo(act,rec){
|
||||
if(!rec)return {cls:'flat',text:'— 持平'};
|
||||
var pct=(act-rec)/rec*100;
|
||||
if(pct>1)return {cls:'tag-red',text:'↑ 超 '+pct.toFixed(1)+'%'};
|
||||
if(pct<-1)return {cls:'tag-green',text:'↓ 省 '+Math.abs(pct).toFixed(1)+'%'};
|
||||
return {cls:'tag-gray',text:'— 持平'};
|
||||
}
|
||||
|
||||
/* ========== 菜品种类(去重) ========== */
|
||||
function distinctDishCount(o){
|
||||
var set={};
|
||||
o.details.forEach(function(d){set[d.foodName]=1;});
|
||||
return Object.keys(set).length;
|
||||
}
|
||||
|
||||
/* ========== 渲染状态 ========== */
|
||||
var currentPage=1;
|
||||
var pageSize=10;
|
||||
var filteredOrders=[];
|
||||
|
||||
function applyFilter(){currentPage=1;renderList();}
|
||||
function resetFilter(){
|
||||
document.getElementById('fOrderNo').value='';
|
||||
document.getElementById('fStatus').value='';
|
||||
document.getElementById('fMeal').value='';
|
||||
document.getElementById('fDate').value='';
|
||||
currentPage=1;renderList();
|
||||
}
|
||||
function buildFilteredOrders(){
|
||||
var fOrderNo=document.getElementById('fOrderNo').value.trim();
|
||||
var fStatus=document.getElementById('fStatus').value;
|
||||
var fMeal=document.getElementById('fMeal').value;
|
||||
var fDate=document.getElementById('fDate').value;
|
||||
return genOrders.filter(function(o){
|
||||
if(fOrderNo&&o.orderNo.indexOf(fOrderNo)<0)return false;
|
||||
if(fStatus&&o.genStatus!==fStatus)return false;
|
||||
if(fMeal&&!o.meals.some(function(m){return m.meal===fMeal;}))return false;
|
||||
if(fDate&&dateOffsetStr(o.dayOffset)!==fDate)return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function renderList(){
|
||||
filteredOrders=buildFilteredOrders();
|
||||
renderPage();
|
||||
}
|
||||
|
||||
function renderPage(){
|
||||
var total=filteredOrders.length;
|
||||
var totalPages=Math.max(1,Math.ceil(total/pageSize));
|
||||
if(currentPage<1)currentPage=1;
|
||||
if(currentPage>totalPages)currentPage=totalPages;
|
||||
var start=(currentPage-1)*pageSize;
|
||||
var items=filteredOrders.slice(start,start+pageSize);
|
||||
|
||||
document.getElementById('tableSubTitle').textContent='共 '+total+' 单';
|
||||
var empty=document.getElementById('historyEmpty');
|
||||
empty.style.display=total?'none':'block';
|
||||
|
||||
var body=document.getElementById('historyTableBody');
|
||||
if(!total){body.innerHTML='';renderPagination(0);return;}
|
||||
|
||||
var html='';
|
||||
items.forEach(function(o,i){
|
||||
var seq=(currentPage-1)*pageSize+i+1;
|
||||
html+=renderOrderRow(o,seq);
|
||||
});
|
||||
body.innerHTML=html;
|
||||
renderPagination(total);
|
||||
}
|
||||
|
||||
function renderOrderRow(o,seq){
|
||||
var genTypeTag='<span class="tag tag-blue">⚡ 自动</span>';
|
||||
var statusTag=o.genStatus==='generating'
|
||||
?'<span class="tag tag-cyan">生成中</span>'
|
||||
:'<span class="tag tag-green">已完成</span>';
|
||||
var mealCells=o.meals.map(function(m){return '<div>'+m.meal+'</div>';}).join('');
|
||||
var countCells=o.meals.map(function(m){return '<div>'+m.dinerCount+'</div>';}).join('');
|
||||
var recipeDate=dateOffsetStr(o.dayOffset);
|
||||
var deliverDate=dateOffsetStr(o.dayOffset+1);
|
||||
var successText=o.genStatus==='generating'?'—':(recipeDate+' '+o.successTime);
|
||||
|
||||
var html='<tr>';
|
||||
html+='<td style="color:var(--text-secondary);">'+seq+'</td>';
|
||||
html+='<td><b>'+o.orderNo+'</b></td>';
|
||||
html+='<td>'+recipeDate+'</td>';
|
||||
html+='<td>'+genTypeTag+'</td>';
|
||||
html+='<td>'+statusTag+'</td>';
|
||||
html+='<td class="meal-cell">'+mealCells+'</td>';
|
||||
html+='<td class="meal-cell">'+countCells+'</td>';
|
||||
html+='<td>'+distinctDishCount(o)+'</td>';
|
||||
html+='<td style="color:var(--text-secondary);">'+deliverDate+' '+o.deliverTime+'</td>';
|
||||
html+='<td style="color:var(--text-secondary);">'+recipeDate+' '+o.startTime+'</td>';
|
||||
html+='<td style="color:var(--text-secondary);">'+successText+'</td>';
|
||||
html+='<td>'+o.creator+'</td>';
|
||||
html+='<td><span class="link" onclick="openDetail(\''+o.orderNo+'\')">数据明细</span></td>';
|
||||
html+='</tr>';
|
||||
return html;
|
||||
}
|
||||
|
||||
/* ========== 分页 ========== */
|
||||
function renderPagination(total){
|
||||
var totalPages=Math.max(1,Math.ceil(total/pageSize));
|
||||
var start=total?((currentPage-1)*pageSize+1):0;
|
||||
var end=Math.min(currentPage*pageSize,total);
|
||||
var html='<span>显示 '+start+'-'+end+' 条,共 '+total+' 条</span>';
|
||||
html+='<button class="page-btn" '+(currentPage===1?'disabled':'')+' onclick="goPage('+(currentPage-1)+')">‹</button>';
|
||||
for(var p=1;p<=totalPages;p++){
|
||||
html+='<button class="page-btn'+(p===currentPage?' active':'')+'" onclick="goPage('+p+')">'+p+'</button>';
|
||||
}
|
||||
html+='<button class="page-btn" '+(currentPage===totalPages?'disabled':'')+' onclick="goPage('+(currentPage+1)+')">›</button>';
|
||||
document.getElementById('pagination').innerHTML=html;
|
||||
}
|
||||
function goPage(p){
|
||||
var totalPages=Math.max(1,Math.ceil(filteredOrders.length/pageSize));
|
||||
if(p<1||p>totalPages)return;
|
||||
currentPage=p;
|
||||
renderPage();
|
||||
}
|
||||
|
||||
/* ========== 数据明细弹窗 ========== */
|
||||
function openDetail(orderNo){
|
||||
var o=genOrders.find(function(x){return x.orderNo===orderNo;});
|
||||
if(!o)return;
|
||||
document.getElementById('detailOrderNo').textContent=orderNo;
|
||||
/* 概要 */
|
||||
var recipeDate=dateOffsetStr(o.dayOffset);
|
||||
var deliverDate=dateOffsetStr(o.dayOffset+1);
|
||||
var statusText=o.genStatus==='generating'?'生成中':'已完成';
|
||||
var summary='';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">食谱日期</span><span class="detail-summary__value">'+recipeDate+'</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">生成方式</span><span class="detail-summary__value">自动</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">生成状态</span><span class="detail-summary__value">'+statusText+'</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">生成餐次</span><span class="detail-summary__value">'+o.meals.map(function(m){return m.meal+' '+m.dinerCount+'人';}).join('、')+'</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">预定配送时间</span><span class="detail-summary__value">'+deliverDate+' '+o.deliverTime+'</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">创建人</span><span class="detail-summary__value">'+o.creator+'</span></div>';
|
||||
summary+='<div class="detail-summary__item"><span class="detail-summary__label">菜品种类</span><span class="detail-summary__value">'+distinctDishCount(o)+'</span></div>';
|
||||
document.getElementById('detailSummary').innerHTML=summary;
|
||||
/* 双维度渲染并重置回餐品维度 Tab */
|
||||
renderDishRows(deriveDetail(o));
|
||||
renderIngRows(deriveIngredients(o));
|
||||
switchDim('dish');
|
||||
document.getElementById('modal-detail').classList.add('show');
|
||||
}
|
||||
function closeDetail(){document.getElementById('modal-detail').classList.remove('show');}
|
||||
|
||||
/* ========== 维度切换 ========== */
|
||||
function switchDim(dim){
|
||||
document.getElementById('tabDish').classList.toggle('active',dim==='dish');
|
||||
document.getElementById('tabIng').classList.toggle('active',dim==='ing');
|
||||
document.getElementById('dimDishWrap').style.display=dim==='dish'?'':'none';
|
||||
document.getElementById('dimIngWrap').style.display=dim==='ing'?'':'none';
|
||||
}
|
||||
|
||||
/* ========== 数值 + 差异徽标单元格(生成中环节未发生时占位 —) ========== */
|
||||
function stageCell(val,rec){
|
||||
if(val==null)return '<td><span class="val-empty">—</span></td>';
|
||||
var diff=diffInfo(val,rec);
|
||||
return '<td><div class="val">'+val.toFixed(2)+'</div><div class="val-diff"><span class="tag tag--mini '+diff.cls+'">'+diff.text+'</span></div></td>';
|
||||
}
|
||||
function dinerCell(actual,plan){
|
||||
if(actual==null)return '<td><span class="val-empty">—</span></td>';
|
||||
var diff=diffInfo(actual,plan);
|
||||
return '<td><div class="val">'+actual+'</div><div class="val-diff"><span class="tag tag--mini '+diff.cls+'">'+diff.text+'</span></div></td>';
|
||||
}
|
||||
function ingStageCell(val,rec){
|
||||
if(val==null||rec==null)return '<td><span class="val-empty">—</span></td>';
|
||||
var diff=diffInfo(val,rec);
|
||||
return '<td><div class="val">'+val.toFixed(3)+'</div><div class="val-diff"><span class="tag tag--mini '+diff.cls+'">'+diff.text+'</span></div></td>';
|
||||
}
|
||||
function plainCell(val){
|
||||
if(val==null)return '<td><span class="val-empty">—</span></td>';
|
||||
return '<td><div class="val">'+val.toFixed(3)+'</div></td>';
|
||||
}
|
||||
|
||||
/* ========== 餐品维度表:生重生、熟重熟分开对账 ========== */
|
||||
function renderDishRows(details){
|
||||
var html='';
|
||||
details.forEach(function(d){
|
||||
html+='<tr class="dish-row" onclick="toggleIng(this)">';
|
||||
html+='<td><span class="expand-arrow">▶</span></td>';
|
||||
html+='<td><span class="tag tag-blue">'+d.meal+'</span></td>';
|
||||
html+='<td style="font-weight:600;">'+d.foodName+'</td>';
|
||||
html+='<td>'+d.spec+'</td>';
|
||||
/* 生重环节 */
|
||||
html+='<td><span class="val val--base">'+d.recRaw.toFixed(2)+'</span></td>';
|
||||
html+=stageCell(d.orderWeight,d.recRaw);
|
||||
html+=stageCell(d.comboRaw,d.recRaw);
|
||||
/* 熟重环节 */
|
||||
html+='<td><span class="val val--base">'+d.recCooked.toFixed(2)+'</span></td>';
|
||||
html+=stageCell(d.cookCooked,d.recCooked);
|
||||
/* 取餐环节 */
|
||||
html+='<td><span class="val">'+d.dinerCount+'</span></td>';
|
||||
html+=dinerCell(d.actualDiner,d.dinerCount);
|
||||
html+=stageCell(d.pickupWeight,d.recCooked);
|
||||
html+='</tr>';
|
||||
html+='<tr class="ingredient-row" style="display:none;">';
|
||||
html+='<td colspan="12">'+buildIngredientsDetail(d)+'</td>';
|
||||
html+='</tr>';
|
||||
});
|
||||
document.getElementById('detailDishBody').innerHTML=html;
|
||||
}
|
||||
|
||||
/* ========== 食材维度表:跨餐品合并一行,毛/净口径沿链路对账 ========== */
|
||||
function renderIngRows(ings){
|
||||
var html='';
|
||||
ings.forEach(function(m){
|
||||
html+='<tr class="dish-row" onclick="toggleIng(this)">';
|
||||
html+='<td><span class="expand-arrow">▶</span></td>';
|
||||
html+='<td style="font-weight:600;">'+m.name+'</td>';
|
||||
var cls=ING_TYPE_CLS[m.type]||'';
|
||||
html+='<td><span class="ing-type '+cls+'">'+m.type+'</span></td>';
|
||||
html+='<td><span class="val val--base">'+m.aiUsed.toFixed(3)+'</span></td>';
|
||||
/* 采供环节:采购(链路起点)+ 实收 vs 采购(收货损耗) */
|
||||
html+=plainCell(m.purchase);
|
||||
html+=ingStageCell(m.received,m.purchase);
|
||||
/* 加工环节:出净率 + 净菜产出 */
|
||||
html+='<td><span class="val">'+(m.received!=null?(m.yieldRate*100).toFixed(0)+'%':'—')+'</span></td>';
|
||||
html+=plainCell(m.netOut);
|
||||
/* 组配环节:实际用量 vs AI 生成用量(核心对账) */
|
||||
html+=ingStageCell(m.comboUsed,m.aiUsed);
|
||||
html+='</tr>';
|
||||
html+='<tr class="ingredient-row" style="display:none;">';
|
||||
html+='<td colspan="9">'+buildUsedByDetail(m)+'</td>';
|
||||
html+='</tr>';
|
||||
});
|
||||
document.getElementById('detailIngBody').innerHTML=html;
|
||||
}
|
||||
document.querySelector('.modal-mask').addEventListener('click',function(e){if(e.target===this)closeDetail();});
|
||||
|
||||
function buildIngredientsDetail(d){
|
||||
var html='<table class="ing-table"><thead><tr><th>食材名称</th><th>类别</th><th>每份用量(g)</th><th>推荐总量(kg)</th><th>组配实际用量(kg)</th><th>差异率</th></tr></thead><tbody>';
|
||||
d.ingredients.forEach(function(ing){
|
||||
var cls=ING_TYPE_CLS[ing.type]||'';
|
||||
var diff=diffInfo(ing.act,ing.rec);
|
||||
html+='<tr>';
|
||||
html+='<td>'+ing.name+'</td>';
|
||||
html+='<td><span class="ing-type '+cls+'">'+ing.type+'</span></td>';
|
||||
html+='<td>'+ing.perServing+'</td>';
|
||||
html+='<td>'+ing.rec.toFixed(3)+'</td>';
|
||||
html+='<td>'+ing.act.toFixed(3)+'</td>';
|
||||
html+='<td><span class="tag '+diff.cls+'">'+diff.text+'</span></td>';
|
||||
html+='</tr>';
|
||||
});
|
||||
html+='</tbody></table>';
|
||||
return html;
|
||||
}
|
||||
/* 食材维度展开:该食材被哪些餐品使用(AI 用量贡献与占比) */
|
||||
function buildUsedByDetail(m){
|
||||
var html='<table class="ing-table"><thead><tr><th>餐次</th><th>使用餐品</th><th>用量贡献(kg)</th><th>占比</th></tr></thead><tbody>';
|
||||
m.usedBy.forEach(function(u){
|
||||
html+='<tr>';
|
||||
html+='<td>'+u.meal+'</td>';
|
||||
html+='<td>'+u.foodName+'</td>';
|
||||
html+='<td>'+u.qty.toFixed(3)+'</td>';
|
||||
html+='<td>'+(u.qty/m.aiUsed*100).toFixed(1)+'%</td>';
|
||||
html+='</tr>';
|
||||
});
|
||||
html+='</tbody></table>';
|
||||
return html;
|
||||
}
|
||||
function toggleIng(tr){
|
||||
var next=tr.nextElementSibling;
|
||||
var arrow=tr.querySelector('.expand-arrow');
|
||||
if(next&&next.classList.contains('ingredient-row')){
|
||||
var isOpen=next.style.display!=='none';
|
||||
next.style.display=isOpen?'none':'table-row';
|
||||
tr.classList.toggle('expanded',!isOpen);
|
||||
arrow.classList.toggle('expanded',!isOpen);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 初始化 ========== */
|
||||
renderList();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -268,7 +268,7 @@ tr.detail-row td.td-actions{background:#FAFBFC;box-shadow:-4px 0 8px rgba(0,0,0,
|
||||
<div class="action-bar">
|
||||
<button class="btn btn-primary" onclick="openModal('modal-order')">➕ 新建订单</button>
|
||||
<button class="btn" style="background:#722ED1;color:#fff;" onclick="openModal('modal-recipe-order')">📋 由食谱快速创建订单</button>
|
||||
<button class="btn" style="background:#13C2C2;color:#fff;" onclick="openModal('modal-gen-history')">📜 净菜订单生成历史</button>
|
||||
<a class="btn" style="background:#13C2C2;color:#fff;text-decoration:none;" href="prod-clean-order-history.html">📜 净菜订单生成历史</a>
|
||||
</div>
|
||||
<div class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">净菜订单</div></div>
|
||||
@@ -398,34 +398,6 @@ tr.detail-row td.td-actions{background:#FAFBFC;box-shadow:-4px 0 8px rgba(0,0,0,
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 弹窗:净菜订单生成历史 -->
|
||||
<div class="modal-mask" id="modal-gen-history">
|
||||
<div class="modal" style="width:1080px;">
|
||||
<div class="modal__header">
|
||||
<div class="modal__title">净菜订单生成历史</div>
|
||||
<button class="modal__close" onclick="closeModal('modal-gen-history')">✕</button>
|
||||
</div>
|
||||
<div class="modal__body">
|
||||
<!-- 筛选 + 订单切换 -->
|
||||
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px;">
|
||||
<div class="filter-item"><label>食谱日期</label><input class="filter-input" type="date" id="ghDate" onchange="renderGenHistory()" style="min-width:150px;"></div>
|
||||
<button class="btn btn-default" onclick="resetGenHistoryFilter()">重置</button>
|
||||
</div>
|
||||
<!-- 生成记录明细(订单信息与餐品明细合并) -->
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="ing-table">
|
||||
<thead><tr><th>食谱日期</th><th>餐次</th><th>餐品名称</th><th>就餐人数</th><th>建议熟重(kg)</th><th>内配料(是否)</th><th>规格</th><th>打包份数</th><th>预定配送时间</th><th>开始生成时间</th><th>生成成功时间</th></tr></thead>
|
||||
<tbody id="genHistoryInfoBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="genHistoryEmpty" style="display:none;text-align:center;color:var(--text-placeholder);padding:40px 0;">暂无符合条件的生成记录</div>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn-default" onclick="closeModal('modal-gen-history')">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ========== 弹窗开关 ========== */
|
||||
function openModal(id){
|
||||
@@ -786,98 +758,6 @@ function genTypeTag(type){
|
||||
:'<span class="tag tag-gray">✍ 手动</span>';
|
||||
}
|
||||
|
||||
/* ========== 净菜订单生成历史 ========== */
|
||||
var genHistory=[
|
||||
{
|
||||
orderNo:'CO-009',recipeDate:'2026-05-20',deliverTime:'2026-05-21 10:00',startTime:'2026-05-20 09:30:00',successTime:'2026-05-20 09:31:12',
|
||||
details:[
|
||||
{meal:'午餐',foodName:'宫保鸡丁',rawWeight:60,cookedWeight:51,dinerCount:120,needSeasoning:true,servings:120,avgWeight:0.5},
|
||||
{meal:'午餐',foodName:'清蒸鲈鱼',rawWeight:60,cookedWeight:52.8,dinerCount:100,needSeasoning:false,servings:100,avgWeight:0.6}
|
||||
]
|
||||
},
|
||||
{
|
||||
orderNo:'CO-010',recipeDate:'2026-05-20',deliverTime:'2026-05-21 06:30',startTime:'2026-05-20 05:30:00',successTime:'2026-05-20 05:31:03',
|
||||
details:[
|
||||
{meal:'早餐',foodName:'小米粥',rawWeight:24,cookedWeight:20.4,dinerCount:80,needSeasoning:false,servings:80,avgWeight:0.3},
|
||||
{meal:'早餐',foodName:'煮鸡蛋',rawWeight:4.8,cookedWeight:4.1,dinerCount:80,needSeasoning:false,servings:80,avgWeight:0.06}
|
||||
]
|
||||
},
|
||||
{
|
||||
orderNo:'CO-011',recipeDate:'2026-05-20',deliverTime:'2026-05-21 16:00',startTime:'2026-05-20 14:30:00',successTime:'2026-05-20 14:31:11',
|
||||
details:[
|
||||
{meal:'晚餐',foodName:'红烧肉',rawWeight:50,cookedWeight:42.5,dinerCount:100,needSeasoning:true,servings:100,avgWeight:0.5},
|
||||
{meal:'晚餐',foodName:'清蒸鲈鱼',rawWeight:60,cookedWeight:52.8,dinerCount:100,needSeasoning:false,servings:100,avgWeight:0.6}
|
||||
]
|
||||
},
|
||||
{
|
||||
orderNo:'CO-012',recipeDate:'2026-05-19',deliverTime:'2026-05-20 16:00',startTime:'2026-05-19 14:30:00',successTime:'2026-05-19 14:31:15',
|
||||
details:[
|
||||
{meal:'晚餐',foodName:'麻婆豆腐',rawWeight:36,cookedWeight:30.6,dinerCount:90,needSeasoning:true,servings:90,avgWeight:0.4}
|
||||
]
|
||||
},
|
||||
{
|
||||
orderNo:'CO-013',recipeDate:'2026-05-20',deliverTime:'2026-05-21 14:00',startTime:'2026-05-20 13:30:00',successTime:'2026-05-20 13:31:02',
|
||||
details:[
|
||||
{meal:'加餐',foodName:'水果拼盘',rawWeight:18,cookedWeight:18,dinerCount:60,needSeasoning:false,servings:60,avgWeight:0.3}
|
||||
]
|
||||
}
|
||||
];
|
||||
function pad2(n){return String(n).padStart(2,'0');}
|
||||
function formatDateTime(d){
|
||||
return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate())+' '+pad2(d.getHours())+':'+pad2(d.getMinutes())+':'+pad2(d.getSeconds());
|
||||
}
|
||||
function genSeasoningTag(needSeasoning){
|
||||
return needSeasoning
|
||||
?'<span class="tag tag-blue">✓ 是</span>'
|
||||
:'<span class="tag tag-gray">✗ 否</span>';
|
||||
}
|
||||
var genHistoryFiltered=[];
|
||||
function renderGenHistory(){
|
||||
var date=document.getElementById('ghDate').value;
|
||||
genHistoryFiltered=genHistory.filter(function(r){
|
||||
if(date&&r.recipeDate!==date)return false;
|
||||
return true;
|
||||
});
|
||||
var empty=document.getElementById('genHistoryEmpty');
|
||||
empty.style.display=genHistoryFiltered.length?'none':'block';
|
||||
/* 默认展示最近一次(数组首位) */
|
||||
if(genHistoryFiltered.length>0){
|
||||
renderGenHistoryOrder(genHistoryFiltered[0].orderNo);
|
||||
}else{
|
||||
document.getElementById('genHistoryInfoBody').innerHTML='';
|
||||
}
|
||||
}
|
||||
function renderGenHistoryOrder(orderNo){
|
||||
var rec=genHistoryFiltered.find(function(r){return r.orderNo===orderNo;});
|
||||
if(!rec)rec=genHistory.find(function(r){return r.orderNo===orderNo;});
|
||||
if(!rec)return;
|
||||
/* 订单信息与餐品明细合并为一张表:每条明细一行,重复订单级字段 */
|
||||
var infoBody=document.getElementById('genHistoryInfoBody');
|
||||
var html='';
|
||||
rec.details.forEach(function(d){
|
||||
var spec=getDishSpecForRecipe(d.foodName);
|
||||
var specLabel=spec.baseGram+'g/'+spec.portion;
|
||||
html+='<tr>'
|
||||
+'<td>'+rec.recipeDate+'</td>'
|
||||
+'<td>'+d.meal+'</td>'
|
||||
+'<td style="font-weight:500;">'+d.foodName+'</td>'
|
||||
+'<td>'+d.dinerCount+'</td>'
|
||||
+'<td>'+(parseFloat(d.cookedWeight).toFixed(2))+' kg</td>'
|
||||
+'<td>'+genSeasoningTag(d.needSeasoning)+'</td>'
|
||||
+'<td>'+specLabel+'</td>'
|
||||
+'<td>'+d.servings+'</td>'
|
||||
+'<td>'+rec.deliverTime+'</td>'
|
||||
+'<td>'+rec.startTime+'</td>'
|
||||
+'<td>'+rec.successTime+'</td>'
|
||||
+'</tr>';
|
||||
});
|
||||
infoBody.innerHTML=html;
|
||||
}
|
||||
function resetGenHistoryFilter(){
|
||||
document.getElementById('ghDate').value='';
|
||||
renderGenHistory();
|
||||
}
|
||||
|
||||
/* ========== 渲染订单表格 ========== */
|
||||
function renderOrderTable(){
|
||||
var tbody=document.getElementById('orderTableBody');
|
||||
@@ -1596,11 +1476,6 @@ function submitRecipeOrder(){
|
||||
if(missing.length>0){alert('以下餐次尚未填写就餐人数:'+missing.join('、'));return;}
|
||||
/* 由食谱创建一个包含所选餐次全部菜品的订单,订单初始状态为「生成中」 */
|
||||
var daily=getDailyMenu(recipeDate);
|
||||
/* 记录本次生成历史:开始时间与模拟成功时间(1~2 分钟后) */
|
||||
var genStart=new Date();
|
||||
var genStartStr=formatDateTime(genStart);
|
||||
var genSuccessStr=formatDateTime(new Date(genStart.getTime()+(60+Math.round(Math.random()*60))*1000));
|
||||
var genDetails=[];
|
||||
var dishMap={};
|
||||
mealIndices.forEach(function(mi){
|
||||
var input=document.querySelector('#dailyMenuBody .meal-diner-input[data-meal-idx="'+mi+'"]');
|
||||
@@ -1612,25 +1487,9 @@ function submitRecipeOrder(){
|
||||
/* 同菜品跨餐次:累加人数(按总人数核算) */
|
||||
dishMap[dishName].dinerCount+=dinerCount;
|
||||
}
|
||||
/* 收集生成历史明细 */
|
||||
var ghSpec=getDishSpecForRecipe(dishName);
|
||||
var ghBaseGram=ghSpec.baseGram||500;
|
||||
var ghServings=ghBaseGram>4000?Math.ceil(ghBaseGram*dinerCount/4000):dinerCount;
|
||||
var ghRawWeight=parseFloat((ghBaseGram*dinerCount/1000).toFixed(2));
|
||||
genDetails.push({
|
||||
meal:MEAL_NAMES[mi],foodName:dishName,
|
||||
rawWeight:ghRawWeight,cookedWeight:parseFloat((ghRawWeight*0.85).toFixed(2)),
|
||||
dinerCount:dinerCount,needSeasoning:recipeSeasoningMap[dishName]===true,
|
||||
servings:ghServings,avgWeight:parseFloat((ghRawWeight/ghServings).toFixed(2))
|
||||
});
|
||||
});
|
||||
});
|
||||
var orderNo='CO-'+String(orders.length+1).padStart(3,'0');
|
||||
/* 写入净菜订单生成历史(订单粒度) */
|
||||
genHistory.unshift({
|
||||
orderNo:orderNo,recipeDate:recipeDate,deliverTime:deliveryTime.replace('T',' '),
|
||||
startTime:genStartStr,successTime:genSuccessStr,details:genDetails
|
||||
});
|
||||
var newLines=[];
|
||||
var di=0;
|
||||
for(var name in dishMap){
|
||||
@@ -1691,7 +1550,6 @@ function sanitizeIntegerInput(el){el.value=el.value.replace(/[^\d]/g,'');}
|
||||
recipeSpecMap={};
|
||||
onRecipeDateChange();
|
||||
}
|
||||
if(id==='modal-gen-history'){document.getElementById('ghDate').value='';renderGenHistory();}
|
||||
document.getElementById(id).classList.add('show');
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user