feat: 一线医疗导航顺序调整与各模块筛选交互升级

- 顶部导航「数据统计」前移、侧边栏「统计报表」前移,统一模块菜单顺序
- 床位统计/超时告警/救护车与门诊统计报表新增周/月/年/选时周期与单位部门级联筛选
- 健康小屋:轮播图配置重构、预约管理改员工预约记录、新增小屋数据管理页
- 健康咨询:新增年份/专家回复状态/用户发送状态筛选与同步/更多操作、短信模板弹窗
- 短信历史页同步改造
This commit is contained in:
liuyunqin
2026-09-22 18:05:08 +08:00
parent 7da544c58d
commit 173634c40e
50 changed files with 1832 additions and 594 deletions
@@ -85,8 +85,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html" class="active">异常事件</a>
</nav>
+1 -1
View File
@@ -92,8 +92,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html" class="active">异常事件</a>
</nav>
@@ -104,8 +104,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html" class="active">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -81,8 +81,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html" class="active">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -97,8 +97,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html" class="active">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -101,8 +101,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
+1 -1
View File
@@ -134,8 +134,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -134,8 +134,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -119,8 +119,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -116,8 +116,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
+1 -1
View File
@@ -119,8 +119,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -127,8 +127,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
+242 -19
View File
@@ -44,7 +44,45 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.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);}
/* [交互] 统计周期:周/月/年/选时 四选一,选时才展开时间范围 */
.period-value{display:inline-block;height:36px;line-height:34px;padding:0 12px;border:1px solid var(--border);border-radius:var(--radius-sm);font-size:var(--font-sm);color:var(--text-primary);background:#fff;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;}
/* [交互] 周选择日历:点击触发框打开日历,点选一周(周一至周日) */
.week-picker{position:relative;display:inline-block;}
.week-trigger{height:36px;min-width:220px;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;}
.week-trigger.open,.week-trigger:hover{border-color:var(--primary);}
.week-panel{display:none;position:absolute;top:40px;left:0;z-index:320;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);padding:12px;width:280px;}
.week-panel.open{display:block;}
.week-cal__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.week-cal__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
.week-cal__nav{width:24px;height:24px;border:none;background:none;color:var(--text-secondary);cursor:pointer;font-size:16px;line-height:1;border-radius:var(--radius-sm);}
.week-cal__nav:hover{color:var(--primary);background:var(--primary-light);}
.week-cal__weekdays{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px;}
.week-cal__weekdays span{text-align:center;font-size:var(--font-xs);color:var(--text-placeholder);padding:2px 0;}
.week-cal__body{display:flex;flex-direction:column;gap:2px;}
.week-row{display:grid;grid-template-columns:repeat(7,1fr);cursor:pointer;border-radius:var(--radius-sm);}
.week-row:hover{background:var(--primary-light);}
.week-row.active{background:var(--primary-light);}
.week-cell{height:28px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);}
.week-row.active .week-cell{color:var(--primary);font-weight:600;}
.week-cell--out{color:var(--text-placeholder);}
/* [交互] 年选择十年历 */
.year-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:4px;}
.year-cell{height:32px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);cursor:pointer;}
.year-cell:hover{background:var(--primary-light);color:var(--primary);}
.year-cell.active{background:var(--primary);color:#fff;font-weight:600;}
/* [交互] 单位部门级联筛选 */
.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;}
/* [交互] 统计周期:周/月/年/选时 四选一,各显示对应可选时间控件(带默认值) */
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
.seg__btn{height:32px;padding:0 14px;border:none;background:#fff;color:var(--text-primary);font-size:var(--font-sm);cursor:pointer;border-right:1px solid var(--border);}
.seg__btn:last-child{border-right:none;}
@@ -56,6 +94,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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-item.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
.tab-filter{display:flex;align-items:center;gap:16px;flex-wrap:wrap;background:#fff;padding:12px 16px;border-bottom:1px solid var(--border);box-shadow:var(--shadow-sm);}
.table-card{background:#fff;border-radius:0 0 var(--radius-md) 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;}
@@ -94,8 +133,8 @@ tfoot td{padding:10px 7px;text-align:center;font-size:var(--font-sm);font-weight
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -138,7 +177,7 @@ tfoot td{padding:10px 7px;text-align:center;font-size:var(--font-sm);font-weight
<!-- 主内容 -->
<main class="main">
<!-- [交互] 统计周期:周统计/月统计/年统计/选时统计,仅「选时统计」展开时间范围并手动查询 -->
<!-- [交互] 统计周期:周/月/年/选时 四选一,各带可选时间控件 -->
<div class="filter-bar">
<div class="filter-item">
<label>统计周期</label>
@@ -149,8 +188,41 @@ tfoot td{padding:10px 7px;text-align:center;font-size:var(--font-sm);font-weight
<button class="seg__btn" id="segCustom" onclick="setPeriod('custom')">选时统计</button>
</div>
</div>
<div class="filter-item" id="weekItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="weekTrigger" onclick="toggleWeekPanel()">2026-08-31 至 2026-09-06</div>
<div class="week-panel" id="weekPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(-1)"></button>
<span class="week-cal__title" id="weekCalTitle">2026年9月</span>
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(1)"></button>
</div>
<div class="week-cal__weekdays"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="week-cal__body" id="weekCalBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="monthItem" style="display:none;">
<label>统计时间</label>
<input class="filter-input" type="month" id="monthInput" value="2026-09" onchange="render()" style="min-width:150px;">
</div>
<div class="filter-item" id="yearItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="yearTrigger" onclick="toggleYearPanel()" style="min-width:120px;">2026年</div>
<div class="week-panel" id="yearPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftYearPage(-1)"></button>
<span class="week-cal__title" id="yearPageTitle">2020 - 2029</span>
<button type="button" class="week-cal__nav" onclick="shiftYearPage(1)"></button>
</div>
<div class="year-grid" id="yearPanelBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="rangeItem" style="display:none;">
<label>时间范围</label>
<label>统计时间</label>
<input class="filter-input" type="date" id="filterFrom" value="2026-08-31" onchange="render()" style="min-width:130px;">
<span style="color:var(--text-placeholder);font-size:var(--font-xs);"></span>
<input class="filter-input" type="date" id="filterTo" value="2026-09-06" onchange="render()" style="min-width:130px;">
@@ -167,6 +239,34 @@ tfoot td{padding:10px 7px;text-align:center;font-size:var(--font-sm);font-weight
<div class="tab-item" id="tabStation" onclick="switchDim('station')">站点统计</div>
</div>
<!-- [交互] TAB 下筛选:部门统计按单位、站点统计按单位部门级联 -->
<div class="tab-filter">
<div class="filter-item" id="unitFilterItem">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="render()" style="min-width:150px;"><option value="">全部单位</option></select>
</div>
<div class="filter-item" id="orgFilterItem" style="display:none;">
<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>
<div class="table-card">
<div class="table-card__head">
<div style="display:flex;gap:8px;">
@@ -212,48 +312,171 @@ var rows=[
];
var dim='dep';
var period='week';
/* 各周期对应的统计区间(演示口径:周=上一自然周,月=本月,年=本年度) */
/* 单位→部门两级映射,与站点管理/告警记录页口径一致 */
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
var selUnit='',selDept='';
function initFilters(){
var us=[];rows.forEach(function(r){if(us.indexOf(r.unit)<0)us.push(r.unit);});
document.getElementById('filterUnit').innerHTML='<option value="">全部单位</option>'+us.map(function(u){return '<option>'+u+'</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='';
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
render();
}
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||'全部部门');
render();
document.getElementById('orgPanel').classList.remove('open');
document.getElementById('orgTrigger').classList.remove('open');
}
/* 各周期统计区间:周从日历选周取值,月/年/选时来自对应可选控件(带默认值) */
function pad2(n){return ('0'+n).slice(-2);}
function fmtDate(d){return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate());}
function monthRange(m){
var mm=String(m||'').match(/^(\d{4})-(\d{2})$/);
if(!mm)return [m,m];
var y=+mm[1],mo=+mm[2];
return [y+'-'+mm[2]+'-01',fmtDate(new Date(y,mo,0))];
}
/* [交互] 周选择:点击「统计时间」打开日历,点选一周(周一至周日),展示为日期范围 */
var weekSelStart='2026-08-31'; // 选中周的周一
var calYear=2026,calMonth=8; // 日历当前显示的月份(0 起,8=9月)
function parseDate(s){var p=s.split('-');return new Date(+p[0],+p[1]-1,+p[2]);}
function weekEndOf(start){return fmtDate(new Date(start.getFullYear(),start.getMonth(),start.getDate()+6));}
function updateWeekTrigger(){
var d=parseDate(weekSelStart);
document.getElementById('weekTrigger').textContent=fmtDate(d)+' 至 '+weekEndOf(d);
}
function toggleWeekPanel(){
var open=document.getElementById('weekPanel').classList.toggle('open');
document.getElementById('weekTrigger').classList.toggle('open',open);
if(open)renderWeekCalendar();
}
function shiftWeekMonth(delta){
calMonth+=delta;
if(calMonth<0){calMonth=11;calYear--;}
if(calMonth>11){calMonth=0;calYear++;}
renderWeekCalendar();
}
function renderWeekCalendar(){
document.getElementById('weekCalTitle').textContent=calYear+'年'+(calMonth+1)+'月';
var first=new Date(calYear,calMonth,1);
var start=new Date(calYear,calMonth,1-((first.getDay()+6)%7)); // 该月首日所在周的周一
var sel=parseDate(weekSelStart);
var html='';
for(var r=0;r<6;r++){
var ws=new Date(start.getFullYear(),start.getMonth(),start.getDate()+r*7);
var isSel=ws.getTime()===sel.getTime();
html+='<div class="week-row'+(isSel?' active':'')+'" onclick="selectWeek(\''+fmtDate(ws)+'\')">';
for(var c=0;c<7;c++){
var d=new Date(ws.getFullYear(),ws.getMonth(),ws.getDate()+c);
html+='<div class="week-cell'+(d.getMonth()!==calMonth?' week-cell--out':'')+'">'+d.getDate()+'</div>';
}
html+='</div>';
}
document.getElementById('weekCalBody').innerHTML=html;
}
function selectWeek(start){
weekSelStart=start;
var d=parseDate(start);
calYear=d.getFullYear();calMonth=d.getMonth();
updateWeekTrigger();
document.getElementById('weekPanel').classList.remove('open');
document.getElementById('weekTrigger').classList.remove('open');
render();
}
/* [交互] 年选择:点击「统计时间」打开十年历,点选年份 */
var yearSel=2026;
var yearPage=2020; // 当前展示的十年起始
function updateYearTrigger(){document.getElementById('yearTrigger').textContent=yearSel+'年';}
function toggleYearPanel(){
var open=document.getElementById('yearPanel').classList.toggle('open');
document.getElementById('yearTrigger').classList.toggle('open',open);
if(open)renderYearPanel();
}
function shiftYearPage(delta){yearPage+=delta*10;renderYearPanel();}
function renderYearPanel(){
document.getElementById('yearPageTitle').textContent=yearPage+' - '+(yearPage+9);
var html='';
for(var i=0;i<10;i++){
var y=yearPage+i;
html+='<div class="year-cell'+(y===yearSel?' active':'')+'" onclick="selectYear('+y+')">'+y+'</div>';
}
document.getElementById('yearPanelBody').innerHTML=html;
}
function selectYear(y){
yearSel=y;
updateYearTrigger();
document.getElementById('yearPanel').classList.remove('open');
document.getElementById('yearTrigger').classList.remove('open');
render();
}
function curRange(){
if(period==='week')return ['2026-09-01','2026-09-07'];
if(period==='month')return ['2026-09-01','2026-09-30'];
if(period==='year')return ['2026-01-01','2026-12-31'];
if(period==='week'){var d=parseDate(weekSelStart);return [fmtDate(d),weekEndOf(d)];}
if(period==='month')return monthRange(document.getElementById('monthInput').value);
if(period==='year')return [yearSel+'-01-01',yearSel+'-12-31'];
return [document.getElementById('filterFrom').value,document.getElementById('filterTo').value];
}
function head(){
var dimCol=dim==='dep'?'部门':'站点名称';
return '<tr><th style="width:56px;">序号</th><th style="min-width:100px;">单位</th><th style="min-width:120px;">'+dimCol+'</th>'
var depTh=dim==='station'?'<th style="min-width:110px;">部门</th>':'';
return '<tr><th style="width:56px;">序号</th><th style="min-width:100px;">单位</th>'+depTh+'<th style="min-width:120px;">'+dimCol+'</th>'
+'<th>呼叫设备数量</th><th>已分配数量</th><th>总呼叫次数</th><th>已接通次数</th><th>未接通次数</th>'
+'<th>已急救处理次数</th><th>未急救处理次数</th><th>告警次数</th></tr>';
}
function num(v,color){return '<td'+(color?' style="color:'+color+';"':'')+'>'+v+'</td>';}
function num(v){return '<td>'+v+'</td>';}
function render(){
var rg=curRange(),f=rg[0],t=rg[1];
var inRange=!!(f&&t)&&!(t<'2026-09-01'||f>'2026-09-30');
var list=inRange?rows.slice():[];
var list=inRange?rows.filter(function(r){
if(dim==='dep'){var u=document.getElementById('filterUnit').value;return !u||r.unit===u;}
return (!selUnit||r.unit===selUnit)&&(!selDept||r.dep===selDept);
}):[];
document.getElementById('thead').innerHTML=head();
var depExtra=dim==='station';
document.getElementById('tbody').innerHTML=list.map(function(r,i){
var dimCell=dim==='dep'?r.dep:r.station;
return '<tr><td>'+(i+1)+'</td><td class="td-name">'+r.unit+'</td><td class="td-name">'+dimCell+'</td>'
+num(r.dev)+num(r.alloc,r.alloc<r.dev?'#D48806':'')+num(r.calls)+num(r.ok,'#389E0D')+num(r.miss,r.miss?'#CF1322':'')
+num(r.done,'#389E0D')+num(r.undone,r.undone?'#D48806':'')+num(r.alarm,r.alarm?'#CF1322':'')+'</tr>';
}).join('')||'<tr><td class="td-empty" colspan="11">'+(inRange?'暂无符合条件的统计数据':'所选周期内无统计数据(演示数据仅覆盖 2026-09)')+'</td></tr>';
return '<tr><td>'+(i+1)+'</td><td class="td-name">'+r.unit+'</td>'+(depExtra?'<td class="td-name">'+r.dep+'</td>':'')+'<td class="td-name">'+dimCell+'</td>'
+num(r.dev)+num(r.alloc)+num(r.calls)+num(r.ok)+num(r.miss)
+num(r.done)+num(r.undone)+num(r.alarm)+'</tr>';
}).join('')||'<tr><td class="td-empty" colspan="'+(depExtra?12:11)+'">'+(inRange?'暂无符合条件的统计数据':'所选周期内无统计数据(演示数据仅覆盖 2026-09)')+'</td></tr>';
var sum=function(k){return list.reduce(function(s,r){return s+r[k];},0);};
var tv=list.length?'<tr><td></td><td class="td-name">合计</td><td class="td-name">'+list.length+' 个'+(dim==='dep'?'部门':'站点')+'</td>'
var tv=list.length?'<tr><td></td><td class="td-name">合计</td>'+(depExtra?'<td></td>':'')+'<td class="td-name">'+list.length+' 个'+(dim==='dep'?'部门':'站点')+'</td>'
+num(sum('dev'))+num(sum('alloc'))+num(sum('calls'))+num(sum('ok'))+num(sum('miss'))+num(sum('done'))+num(sum('undone'))+num(sum('alarm'))+'</tr>':'';
document.getElementById('tfoot').innerHTML=tv;
document.getElementById('totalTip').textContent='共 '+list.length+' 行 · 统计区间 '+(f||'—')+' 至 '+(t||'—');
}
/* [交互] 周期切换:非选时隐藏时间范围并即时出数,选时默认展示上一周期区间 */
/* [交互] 周期切换:周/月/年显示对应可选时间控件(带默认值),选时统计展开起止日期 */
function setPeriod(p){
period=p;
['Week','Month','Year','Custom'].forEach(function(k){document.getElementById('seg'+k).classList.toggle('active',p===k.toLowerCase());});
document.getElementById('rangeItem').style.display=p==='custom'?'flex':'none';
document.getElementById('weekItem').style.display=(p==='week')?'flex':'none';
document.getElementById('monthItem').style.display=(p==='month')?'flex':'none';
document.getElementById('yearItem').style.display=(p==='year')?'flex':'none';
document.getElementById('rangeItem').style.display=(p==='custom')?'flex':'none';
render();
}
function switchDim(d){
dim=d;
document.getElementById('tabDep').classList.toggle('active',d==='dep');
document.getElementById('tabStation').classList.toggle('active',d==='station');
document.getElementById('unitFilterItem').style.display=(d==='dep')?'flex':'none';
document.getElementById('orgFilterItem').style.display=(d==='station')?'flex':'none';
render();
}
function doExport(){showToast('导出任务已提交,完成后可在「查看导出任务」下载');}
@@ -277,7 +500,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);
}
render();
initFilters();updateWeekTrigger();updateYearTrigger();setPeriod('week');
</script>
</body>
</html>
+1 -1
View File
@@ -115,8 +115,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -44,6 +44,19 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.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-select{height:36px;min-width:150px;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: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);}
@@ -88,8 +101,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html" class="active">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -132,15 +145,35 @@ tbody tr:hover{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- [交互] 筛选栏:告警设备/宿舍名称/告警日期 -->
<!-- [交互] 筛选栏:所属单位部门级联 + 所属站点/设备编号/告警日期 -->
<div class="filter-bar">
<div class="filter-item">
<label>告警设备</label>
<input class="filter-input" id="filterDev" placeholder="请输入" oninput="render()">
<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="filterDorm" placeholder="关键字" oninput="render()">
<label>所属站点</label>
<select class="filter-select" id="filterStation" onchange="render()"><option value="">全部站点</option></select>
</div>
<div class="filter-item">
<label>设备编号</label>
<input class="filter-input" id="filterDev" placeholder="请输入设备编号" oninput="render()">
</div>
<div class="filter-item">
<label>告警日期</label>
@@ -166,9 +199,9 @@ tbody tr:hover{background:#F8FBFF;}
<div class="table-card__content">
<table>
<thead><tr>
<th style="width:56px;">序号</th><th style="min-width:140px;">告警时间</th><th style="min-width:130px;">告警类型</th>
<th style="min-width:340px;">告警原因</th><th style="min-width:110px;">告警设备</th>
<th style="min-width:220px;">宿舍名称</th>
<th style="width:56px;">序号</th><th style="min-width:110px;">设备编号</th><th style="min-width:170px;">所属单位部门</th>
<th style="min-width:130px;">所属站点</th><th style="min-width:120px;">告警类型</th><th style="min-width:140px;">告警时间</th>
<th style="min-width:340px;">告警原因</th><th style="min-width:220px;">宿舍信息(楼栋+宿舍)</th>
</tr></thead>
<tbody id="tbody"></tbody>
</table>
@@ -194,31 +227,91 @@ tbody tr:hover{background:#F8FBFF;}
<div class="toast" id="toast"></div>
<script>
/* 告警数据:与呼叫记录/设备列表跨页勾稽——徐立成未接通升级、杨慧敏超时未派单、10003 与 10016 离线 */
var alarms=[
{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:'西峰基地值班室'}
/* 告警数据:超时未归——超过 24h 未归寝且未监测到宿舍人员进出 */
/* unit/depart/station 与「站点管理」口径一致 */
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
var stationList=[
{unit:'采油一厂',depart:'樊学作业区',name:'樊学站点基地'},
{unit:'采油一厂',depart:'麻子山作业区',name:'麻子山站点'},
{unit:'采油二厂',depart:'桐园作业区',name:'桐园站点基地'},
{unit:'采油二厂',depart:'姬圪台作业区',name:'姬圪台站点'},
{unit:'第四采油厂',depart:'西峰作业区',name:'西峰基地'},
{unit:'采气一厂',depart:'靖边气田',name:'靖边气田站点'}
];
var typeBadge={'呼叫超时未接听':'badge--danger','设备离线告警':'badge--warning','救助处置超时':'badge--info'};
var alarms=[
{time:'2026-09-07 06:14',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10006',unit:'采油二厂',depart:'桐园作业区',station:'桐园站点基地',dorm:'桐园倒班宿舍1号楼 · 301室'},
{time:'2026-09-06 20:16',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10005',unit:'采油一厂',depart:'麻子山作业区',station:'麻子山站点',dorm:'麻子山倒班宿舍楼 · 101室'},
{time:'2026-09-04 18:51',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10009',unit:'第四采油厂',depart:'西峰作业区',station:'西峰基地',dorm:'西峰基地综合宿舍楼 · 302室'},
{time:'2026-09-04 09:20',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10003',unit:'采油一厂',depart:'樊学作业区',station:'樊学站点基地',dorm:'樊学倒班宿舍1号楼 · 102室'},
{time:'2026-09-03 20:08',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10005',unit:'采油一厂',depart:'麻子山作业区',station:'麻子山站点',dorm:'麻子山倒班宿舍楼 · 101室'},
{time:'2026-09-02 23:47',type:'超时未归',reason:'超过合理时长(24h)仍未归寝,未监测到宿舍人员进出',dev:'10016',unit:'第四采油厂',depart:'西峰作业区',station:'西峰基地',dorm:'西峰基地值班室'}
];
var typeBadge={'超时未归':'badge--danger'};
var selUnit='',selDept='';
function day(dt){return dt.slice(0,10);}
function initFilters(){
document.getElementById('filterStation').innerHTML='<option value="">全部站点</option>'+stationList.map(function(s){return '<option>'+s.name+'</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='';
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(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 render(){
var kd=document.getElementById('filterDev').value.trim().toUpperCase();
var km=document.getElementById('filterDorm').value.trim();
var ks=document.getElementById('filterStation').value;
var f=document.getElementById('filterFrom').value,t=document.getElementById('filterTo').value;
var list=alarms.filter(function(a){return a.dev.toUpperCase().indexOf(kd)>=0&&a.dorm.indexOf(km)>=0&&day(a.time)>=f&&day(a.time)<=t;});
var list=alarms.filter(function(a){
return a.dev.toUpperCase().indexOf(kd)>=0
&& (!ks||a.station===ks)
&& (!selUnit||a.unit===selUnit)
&& (!selDept||a.depart===selDept)
&& day(a.time)>=f&&day(a.time)<=t;
});
document.getElementById('tbody').innerHTML=list.map(function(a){
var i=alarms.indexOf(a);
return '<tr><td>'+(i+1)+'</td><td>'+a.time+'</td><td><span class="badge '+typeBadge[a.type]+'">'+a.type+'</span></td>'
return '<tr><td>'+(i+1)+'</td>'
+'<td style="font-family:monospace;">'+a.dev+'</td>'
+'<td>'+a.unit+' / '+a.depart+'</td>'
+'<td>'+a.station+'</td>'
+'<td><span class="badge '+typeBadge[a.type]+'">'+a.type+'</span></td>'
+'<td>'+a.time+'</td>'
+'<td style="text-align:left;white-space:normal;max-width:400px;">'+a.reason+'</td>'
+'<td style="font-family:monospace;">'+a.dev+'</td><td>'+a.dorm+'</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="6">没有符合条件的告警记录</td></tr>';
+'<td>'+a.dorm+'</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="8">没有符合条件的告警记录</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 条记录';
}
function resetFilter(){['filterDev','filterDorm'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterFrom').value='2026-09-01';document.getElementById('filterTo').value='2026-09-07';render();}
function resetFilter(){
resetOrg();
document.getElementById('filterDev').value='';
document.getElementById('filterStation').value='';
document.getElementById('filterFrom').value='2026-09-01';
document.getElementById('filterTo').value='2026-09-07';
render();
}
function doExport(){showToast('导出任务已提交,完成后可在「查看导出任务」下载');}
/* 异步导出任务记录 */
var taskRecords=[
@@ -240,7 +333,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);
}
render();
initFilters();render();
</script>
</body>
</html>
@@ -130,8 +130,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -151,8 +151,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup">
@@ -161,8 +161,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item">救助物品统计</a>
@@ -172,8 +172,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -170,8 +170,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -197,8 +197,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -147,8 +147,8 @@ textarea.form-control{height:72px;padding:8px 10px;resize:vertical;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -146,8 +146,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -158,8 +158,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -159,8 +159,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html" class="active">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -141,8 +141,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -113,8 +113,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -129,8 +129,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -151,8 +151,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -177,8 +177,8 @@ tbody tr:hover{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -112,8 +112,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -174,8 +174,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -170,8 +170,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -138,8 +138,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -181,8 +181,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -172,8 +172,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html" class="active">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
+185 -104
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>预约管理 | 一线医疗</title>
<title>员工预约记录 | 一线医疗</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;font-size:var(--font-sm);background:#F5F7FA;color:#333;min-width:1280px;}
@@ -44,6 +44,17 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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;}
.filter-select:disabled{background-color:#F5F5F5;color:var(--text-placeholder);cursor:not-allowed;}
.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;}
.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);}
.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;}
@@ -55,8 +66,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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:space-between;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-sm);font-weight:600;}
.table-toolbar{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__content{overflow-x:auto;-webkit-overflow-scrolling:touch;}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:1560px;}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:1760px;}
thead th{background:#FAFAFA;padding:10px 14px;text-align:center;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 14px;text-align:center;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid #F0F0F0;white-space:nowrap;}
tbody tr:last-child td{border-bottom:none;}
@@ -101,8 +113,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -123,13 +135,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub active">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub active">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -142,33 +154,44 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- [交互] 筛选栏(单位部门级联) -->
<!-- [交互] 筛选栏(单位部门级联动,预约日期范围) -->
<div class="filter-bar">
<div class="filter-item">
<label>单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()">
<option value="">全部单位</option>
<option>采油一厂</option><option>采油二厂</option><option>第四采油厂</option><option>采气一厂</option>
</select>
<label>姓名</label>
<input class="filter-input" id="filterName" placeholder="请输入" oninput="render()" style="min-width:90px;">
</div>
<div class="filter-item">
<label>部门</label>
<select class="filter-select" id="filterDepar" onchange="render()" disabled>
<option value="">全部部门</option>
</select>
<label>员工编号</label>
<input class="filter-input" id="filterEmpNo" placeholder="请输入" oninput="render()" style="min-width:90px;">
</div>
<div class="filter-item">
<label>用户姓名</label>
<input class="filter-input" id="filterName" placeholder="关键字" oninput="render()" style="min-width:90px;">
<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>
<select class="filter-select" id="filterStatus" onchange="render()" style="min-width:96px;">
<option value="">全部</option><option>待使用</option><option>使用中</option><option>已完成</option><option>取消</option><option>爽约</option>
<option value="">全部</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>
<label>预约健康室</label>
<select class="filter-select" id="filterRoom" onchange="render()">
<option value="">全部健康室</option>
<option>樊学站点健康小屋</option><option>麻子山站点健康小屋</option><option>桐园站点健康小屋</option><option>西峰基地健康小屋</option><option>靖边气田健康小屋</option>
@@ -176,11 +199,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
<div class="filter-item">
<label>设备名称</label>
<input class="filter-input" id="filterDev" placeholder="关键字" oninput="render()" style="min-width:90px;">
<input class="filter-input" id="filterDev" placeholder="请输入" oninput="render()" style="min-width:90px;">
</div>
<div class="filter-item">
<label>预约日期</label>
<input class="filter-input" type="date" id="filterDate" onchange="render()" style="min-width:130px;">
<input class="filter-input" type="date" id="filterDateStart" onchange="render()" style="min-width:130px;">
<span style="color:var(--text-placeholder);"></span>
<input class="filter-input" type="date" id="filterDateEnd" onchange="render()" style="min-width:130px;">
</div>
<button class="btn btn-primary" onclick="render();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>查询
@@ -189,20 +214,23 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
<!-- [交互] 列表 -->
<!-- [交互] 列表(操作按钮置于列表上方,与列表左对齐) -->
<div class="table-card">
<div class="table-card__head">
<span class="table-card__title">预约记录列表</span>
<button class="btn btn-danger" id="btnBatchDel" onclick="openBatchDel()">批量删除</button>
<div class="table-toolbar">
<button class="btn btn-default" onclick="exportList()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 1v7M4.5 5.5L7 8l2.5-2.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 11h9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>导出列表数据
</button>
<button class="btn btn-default" onclick="viewExportTasks()">查看导出任务</button>
</div>
<div class="table-card__content">
<table>
<thead><tr>
<th style="width:40px;"><input type="checkbox" id="ckAll" onchange="toggleAll(this)"></th>
<th style="min-width:150px;">预约单号</th><th>用户姓名</th><th>性别</th><th style="min-width:110px;">所属部门</th>
<th style="min-width:110px;">联系方式</th><th>预约状态</th><th style="min-width:150px;">预约健康室</th>
<th style="min-width:120px;">设备名称</th><th>预约日期</th><th style="min-width:150px;">开始时间</th>
<th style="min-width:150px;">完成时间</th><th style="min-width:150px;">创建时间</th><th style="width:120px;">操作</th>
<th style="width:56px;">序号</th>
<th style="min-width:150px;">预约单号</th><th>姓名</th><th style="min-width:110px;">员工编号</th>
<th>性别</th><th>年龄</th><th style="min-width:110px;">单位</th><th style="min-width:110px;">部门</th>
<th style="min-width:120px;">手机号</th><th>预约状态</th><th style="min-width:190px;">预约时段</th>
<th style="min-width:150px;">预约健康室</th><th style="min-width:120px;">设备名称</th>
<th style="min-width:150px;">创建时间</th><th style="width:90px;">操作</th>
</tr></thead>
<tbody id="tbody"></tbody>
</table>
@@ -220,15 +248,22 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
</div>
<!-- [交互] 删除确认(单条/批量共用) -->
<div class="modal-mask" id="delMask">
<div class="modal">
<div class="modal__header"><span>删除确认</span><button class="modal__close" onclick="closeDel()">&times;</button></div>
<div class="modal__body" id="delBody"></div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeDel()">取消</button>
<button class="btn btn-danger" onclick="confirmDel()">确认删除</button>
<!-- [交互] 导出任务弹窗 -->
<div class="modal-mask" id="taskMask">
<div class="modal--wide">
<div class="modal__header"><span>导出任务</span><button class="modal__close" onclick="closeTasks()">&times;</button></div>
<div class="modal__body">
<div style="overflow-x:auto;">
<table style="min-width:auto;width:100%;">
<thead><tr>
<th style="min-width:180px;">任务编号</th><th style="min-width:240px;">文件名</th><th>条数</th>
<th>状态</th><th style="min-width:150px;">创建时间</th><th style="width:80px;">操作</th>
</tr></thead>
<tbody id="taskBody"></tbody>
</table>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeTasks()">关闭</button></div>
</div>
</div>
@@ -238,108 +273,154 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
/* 单位→部门共享池(与健康室/床头呼叫域一致) */
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
/* 预约状态徽标配色 */
var stBadge={'待使用':'badge--info','使用中':'badge--warning','已完成':'badge--success','已取消':'badge--muted','爽约':'badge--danger'};
var stBadge={'预约成功':'badge--info','使用中':'badge--warning','已完成':'badge--success','已过期':'badge--danger','已取消':'badge--muted','申请撤回':'badge--danger','待审核':'badge--warning','无效状态':'badge--muted'};
/* 预约记录:健康室与设备与 hut-room 勾稽;监测设备不产生预约 */
var appts=[
{code:'YY-20260901-001',name:'石维国',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'13821622211',status:'已完成',room:'樊学站点健康小屋',dev:'血压检测仪',date:'2026-09-01',start:'2026-09-01 09:00',end:'2026-09-01 09:32',created:'2026-08-30 16:22',useTime:'2026-09-01 09:02',backTime:'2026-09-01 09:32'},
{code:'YY-20260901-002',name:'李桂芳',sex:'女',unit:'采油一厂',depar:'樊学作业区',phone:'13937440834',status:'已完成',room:'樊学站点健康小屋',dev:'血糖分析仪',date:'2026-09-01',start:'2026-09-01 10:00',end:'2026-09-01 10:18',created:'2026-08-31 08:40',useTime:'2026-09-01 10:01',backTime:'2026-09-01 10:18'},
{code:'YY-20260902-003',name:'曲志明',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'13708495520',status:'已取消',room:'樊学站点健康小屋',dev:'心理测评系统',date:'2026-09-02',start:'2026-09-02 14:00',end:'—',created:'2026-09-01 11:05',cancelTime:'2026-09-02 08:12'},
{code:'YY-20260903-004',name:'刘玉梅',sex:'女',unit:'采气一厂',depar:'靖边气田',phone:'13607587712',status:'已完成',room:'靖边气田健康小屋',dev:'体成分分析仪',date:'2026-09-03',start:'2026-09-03 15:00',end:'2026-09-03 15:21',created:'2026-09-02 17:33',useTime:'2026-09-03 15:00',backTime:'2026-09-03 15:21'},
{code:'YY-20260904-005',name:'高建军',sex:'男',unit:'第四采油厂',depar:'西峰作业区',phone:'13589259086',status:'爽约',room:'西峰基地健康小屋',dev:'激光治疗仪',date:'2026-09-04',start:'2026-09-04 14:30',end:'—',created:'2026-09-03 09:12',cancelTime:'2026-09-04 15:10'},
{code:'YY-20260905-006',name:'徐立成',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'13848934417',status:'已完成',room:'桐园站点健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:30',end:'2026-09-05 10:02',created:'2026-09-04 15:48',useTime:'2026-09-05 09:31',backTime:'2026-09-05 10:02'},
{code:'YY-20260905-007',name:'呼建国',sex:'男',unit:'采气一厂',depar:'靖边气田',phone:'13976362680',status:'已取消',room:'靖边气田健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:00',end:'—',created:'2026-09-04 18:02',cancelTime:'2026-09-04 20:15'},
{code:'YY-20260906-008',name:'白瑞',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13720283341',status:'已取消',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-06',start:'2026-09-06 16:00',end:'—',created:'2026-09-05 10:26',cancelTime:'2026-09-06 07:50'},
{code:'YY-20260907-009',name:'赵万福',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'13661218123',status:'使用中',room:'桐园站点健康小屋',dev:'音乐放松舱',date:'2026-09-07',start:'2026-09-07 10:00',end:'—',created:'2026-09-06 14:19',useTime:'2026-09-07 10:03',backTime:'—'},
{code:'YY-20260907-010',name:'闫婷',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13547386208',status:'使用中',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-07',start:'2026-09-07 11:00',end:'—',created:'2026-09-06 16:55',useTime:'2026-09-07 11:01',backTime:'—'},
{code:'YY-20260908-011',name:'杨慧敏',sex:'女',unit:'采油一厂',depar:'麻子山作业区',phone:'13856939975',status:'待使用',room:'麻子山站点健康小屋',dev:'体成分分析仪',date:'2026-09-08',start:'2026-09-08 15:00',end:'—',created:'2026-09-07 08:31'},
{code:'YY-20260908-012',name:'张秀兰',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13916355402',status:'待使用',room:'西峰基地健康小屋',dev:'血压检测仪',date:'2026-09-08',start:'2026-09-08 10:00',end:'—',created:'2026-09-07 09:04'}
{code:'YY-20260901-001',name:'石维国',empNo:'100236',sex:'男',age:45,unit:'采油一厂',depar:'樊学作业区',phone:'13821622211',status:'已完成',room:'樊学站点健康小屋',dev:'血压检测仪',date:'2026-09-01',slot:'2026-09-01 09:00~09:30',created:'2026-08-30 16:22'},
{code:'YY-20260901-002',name:'李桂芳',empNo:'100254',sex:'女',age:39,unit:'采油一厂',depar:'樊学作业区',phone:'13937440834',status:'已完成',room:'樊学站点健康小屋',dev:'血糖分析仪',date:'2026-09-01',slot:'2026-09-01 10:00~10:30',created:'2026-08-31 08:40'},
{code:'YY-20260902-003',name:'曲志明',empNo:'100912',sex:'男',age:51,unit:'采油一厂',depar:'樊学作业区',phone:'13708495520',status:'已取消',room:'樊学站点健康小屋',dev:'心理测评系统',date:'2026-09-02',slot:'2026-09-02 14:00~14:30',created:'2026-09-01 11:05'},
{code:'YY-20260903-004',name:'刘玉梅',empNo:'102118',sex:'女',age:44,unit:'采气一厂',depar:'靖边气田',phone:'13607587712',status:'已完成',room:'靖边气田健康小屋',dev:'体成分分析仪',date:'2026-09-03',slot:'2026-09-03 15:00~15:30',created:'2026-09-02 17:33'},
{code:'YY-20260904-005',name:'高建军',empNo:'101577',sex:'男',age:47,unit:'第四采油厂',depar:'西峰作业区',phone:'13589259086',status:'已过期',room:'西峰基地健康小屋',dev:'激光治疗仪',date:'2026-09-04',slot:'2026-09-04 14:30~15:00',created:'2026-09-03 09:12'},
{code:'YY-20260905-006',name:'徐立成',empNo:'101863',sex:'男',age:42,unit:'采油二厂',depar:'桐园作业区',phone:'13848934417',status:'已完成',room:'桐园站点健康小屋',dev:'血压检测仪',date:'2026-09-05',slot:'2026-09-05 09:30~10:00',created:'2026-09-04 15:48'},
{code:'YY-20260905-007',name:'呼建国',empNo:'100476',sex:'男',age:55,unit:'采气一厂',depar:'靖边气田',phone:'13976362680',status:'申请撤回',room:'靖边气田健康小屋',dev:'血压检测仪',date:'2026-09-05',slot:'2026-09-05 09:00~09:30',created:'2026-09-04 18:02'},
{code:'YY-20260906-008',name:'白瑞',empNo:'102351',sex:'女',age:33,unit:'第四采油厂',depar:'西峰作业区',phone:'13720283341',status:'已取消',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-06',slot:'2026-09-06 16:00~16:30',created:'2026-09-05 10:26'},
{code:'YY-20260907-009',name:'赵万福',empNo:'101204',sex:'男',age:49,unit:'采油二厂',depar:'桐园作业区',phone:'13661218123',status:'使用中',room:'桐园站点健康小屋',dev:'音乐放松舱',date:'2026-09-07',slot:'2026-09-07 10:00~10:30',created:'2026-09-06 14:19'},
{code:'YY-20260907-010',name:'闫婷',empNo:'102108',sex:'女',age:36,unit:'第四采油厂',depar:'西峰作业区',phone:'13547386208',status:'使用中',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-07',slot:'2026-09-07 11:00~11:30',created:'2026-09-06 16:55'},
{code:'YY-20260908-011',name:'杨慧敏',empNo:'101945',sex:'女',age:41,unit:'采油一厂',depar:'麻子山作业区',phone:'13856939975',status:'预约成功',room:'麻子山站点健康小屋',dev:'体成分分析仪',date:'2026-09-08',slot:'2026-09-08 15:00~15:30',created:'2026-09-07 08:31'},
{code:'YY-20260908-012',name:'张秀兰',empNo:'100863',sex:'女',age:52,unit:'第四采油厂',depar:'西峰作业区',phone:'13916355402',status:'待审核',room:'西峰基地健康小屋',dev:'血压检测仪',date:'2026-09-08',slot:'2026-09-08 10:00~10:30',created:'2026-09-07 09:04'},
{code:'YY-20260908-013',name:'康永强',empNo:'101629',sex:'男',age:38,unit:'采油二厂',depar:'姬圪台作业区',phone:'13591472208',status:'待审核',room:'桐园站点健康小屋',dev:'心理测评系统',date:'2026-09-09',slot:'2026-09-09 09:00~09:30',created:'2026-09-08 10:47'},
{code:'YY-20260909-014',name:'丁晓燕',empNo:'102247',sex:'女',age:29,unit:'采气一厂',depar:'靖边气田',phone:'13849026617',status:'无效状态',room:'靖边气田健康小屋',dev:'音乐放松舱',date:'2026-09-09',slot:'2026-09-09 15:30~16:00',created:'2026-09-08 17:10'}
];
/* 导出任务记录(查看导出任务弹窗) */
var exportTasks=[
{no:'EX-20260921-0003',filename:'员工预约记录_20260921(12).csv',count:12,status:'已完成',time:'2026-09-21 16:40'},
{no:'EX-20260921-0002',filename:'员工预约记录_20260921(8).xlsx',count:8,status:'已完成',time:'2026-09-21 15:12'},
{no:'EX-20260921-0001',filename:'员工预约记录_20260921(5).xlsx',count:5,status:'处理中',time:'2026-09-21 14:05'}
];
var delCodes=[];/* 当前待删除单号集合 */
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var sel=document.getElementById('filterDepar');
sel.innerHTML='<option value="">全部部门</option>'+(u?unitDeparts[u].map(function(d){return '<option>'+d+'</option>';}).join(''):'');
sel.disabled=!u;render();
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='';
document.getElementById('orgTrigger').textContent=(selUnit||'全部单位')+' / '+(selDept||'全部部门');
render();
}
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||'全部部门');
render();
}
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 currentList(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepar').value;
var kn=document.getElementById('filterName').value.trim(),st=document.getElementById('filterStatus').value;
var u=selUnit,d=selDept;
var kn=document.getElementById('filterName').value.trim(),en=document.getElementById('filterEmpNo').value.trim();
var st=document.getElementById('filterStatus').value;
var rm=document.getElementById('filterRoom').value,dv=document.getElementById('filterDev').value.trim();
var dt=document.getElementById('filterDate').value;
var ds=document.getElementById('filterDateStart').value,de=document.getElementById('filterDateEnd').value;
return appts.filter(function(r){
return (!u||r.unit===u)&&(!d||r.depar===d)&&r.name.indexOf(kn)>=0&&(!st||r.status===st)
&&(!rm||r.room===rm)&&r.dev.indexOf(dv)>=0&&(!dt||r.date===dt);
return (!u||r.unit===u)&&(!d||r.depar===d)&&r.name.indexOf(kn)>=0&&r.empNo.indexOf(en)>=0
&&(!st||r.status===st)&&(!rm||r.room===rm)&&r.dev.indexOf(dv)>=0
&&(!ds||r.date>=ds)&&(!de||r.date<=de);
});
}
function render(){
var list=currentList();
document.getElementById('tbody').innerHTML=list.map(function(r){
var i=appts.indexOf(r);
return '<tr><td><input type="checkbox" class="ck" value="'+i+'" onchange="syncBatch()"></td>'
document.getElementById('tbody').innerHTML=list.map(function(r,i){
var idx=appts.indexOf(r);
return '<tr><td>'+(i+1)+'</td>'
+'<td style="font-family:monospace;">'+r.code+'</td>'
+'<td style="font-weight:500;">'+r.name+'</td><td>'+r.sex+'</td><td>'+r.depar+'</td><td>'+r.phone+'</td>'
+'<td style="font-weight:500;">'+r.name+'</td><td style="font-family:monospace;">'+r.empNo+'</td>'
+'<td>'+r.sex+'</td><td>'+r.age+'</td><td>'+r.unit+'</td><td>'+r.depar+'</td><td>'+r.phone+'</td>'
+'<td><span class="badge '+stBadge[r.status]+'">'+r.status+'</span></td>'
+'<td style="text-align:left;padding-left:12px;">'+r.room+'</td><td>'+r.dev+'</td><td>'+r.date+'</td>'
+'<td>'+r.start+'</td><td>'+r.end+'</td><td>'+r.created+'</td>'
+'<td><button class="btn btn-link" onclick="openDetail('+i+')">详情</button>'
+'<button class="btn btn-link" style="color:var(--danger);" onclick="openDel(['+i+'])">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="14">没有符合条件的预约记录</td></tr>';
+'<td style="font-family:monospace;">'+r.slot+'</td>'
+'<td style="text-align:left;padding-left:12px;">'+r.room+'</td><td>'+r.dev+'</td><td>'+r.created+'</td>'
+'<td><button class="btn btn-link" onclick="openDetail('+idx+')">详情</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="15">没有符合条件的预约记录</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 条 · 全部记录 '+appts.length+' 条';
document.getElementById('ckAll').checked=false;syncBatch();
}
function resetFilter(){['filterUnit','filterDepar','filterName','filterStatus','filterRoom','filterDev','filterDate'].forEach(function(id){document.getElementById(id).value='';});document.getElementById('filterDepar').disabled=true;render();}
function toggleAll(el){document.querySelectorAll('#tbody .ck').forEach(function(c){c.checked=el.checked;});syncBatch();}
function syncBatch(){
var n=document.querySelectorAll('#tbody .ck:checked').length;
var b=document.getElementById('btnBatchDel');
b.disabled=n===0;b.textContent='批量删除'+(n?'('+n+')':'');
function resetFilter(){
['filterName','filterEmpNo','filterStatus','filterRoom','filterDev','filterDateStart','filterDateEnd'].forEach(function(id){document.getElementById(id).value='';});
resetOrg();render();
}
function di(k,v,full){return '<div class="detail-item'+(full?' detail-item--full':'')+'"><span class="dk">'+k+'</span><span class="dv">'+v+'</span></div>';}
function openDetail(i){
var r=appts[i];
var h='<div class="sec-title">预约信息</div><div class="detail-grid">'
+di('预约单号',r.code,true)+di('用户姓名',r.name+' / '+r.sex)+di('联系方式',r.phone)
+di('所属部门',r.unit+' / '+r.depar)+di('预约状态','<span class="badge '+stBadge[r.status]+'">'+r.status+'</span>')
+di('预约健康室',r.room)+di('预约设备',r.dev)
+di('预约日期',r.date)+di('预约时段',r.start+' ~ '+(r.status==='已完成'?'归还即止':r.status==='使用中'?'使用中':'计划结束时刻'))
+di('预约单号',r.code)+di('预约状态','<span class="badge '+stBadge[r.status]+'">'+r.status+'</span>')
+di('姓名',r.name)+di('性别',r.sex)
+di('年龄',r.age+' 岁')+di('员工编号',r.empNo)
+di('单位',r.unit)+di('部门',r.depar)
+di('手机号',r.phone)+di('预约时段',r.slot)
+di('预约健康室',r.room)+di('设备名称',r.dev)
+di('创建时间',r.created,true)+'</div>';
h+='<div class="sec-title">履约情况</div><div class="detail-grid">';
if(r.status==='使用中'){h+=di('开始使用时间',r.useTime)+di('归还时间','<span style="color:var(--text-placeholder);">尚未归还</span>');}
else if(r.status==='已完成'){h+=di('开始使用时间',r.useTime)+di('归还时间',r.backTime);}
else if(r.status==='已取消'||r.status==='爽约'){h+=di(r.status==='爽约'?'超时未归时间':'取消时间',r.cancelTime,true);}
else{h+='<div class="detail-item detail-item--full"><span class="dk">履约情况</span><span class="dv" style="color:var(--text-placeholder);">尚未开始使用,可按计划时段到岗使用设备</span></div>';}
h+='</div>';
document.getElementById('detailBody').innerHTML=h;
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
/* ---- 删除(单条与批量共用确认弹窗) ---- */
function openDel(idxs){delCodes=idxs;
document.getElementById('delBody').innerHTML=idxs.length===1
?'确认删除预约单 <b style="font-family:monospace;color:var(--text-primary);">'+appts[idxs[0]].code+'</b>?删除后该记录不再计入小屋预约统计,操作不可恢复。'
:'已勾选 <b>'+idxs.length+'</b> 条预约记录,确认批量删除?删除后不再计入预约统计,操作不可恢复。';
document.getElementById('delMask').classList.add('open');
/* ---- 导出列表数据 ---- */
function pad(n){return n<10?'0'+n:''+n;}
function todayStr(){var d=new Date();return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate());}
function nowStr(){var d=new Date();return todayStr()+' '+pad(d.getHours())+':'+pad(d.getMinutes())+':'+pad(d.getSeconds());}
function exportList(){
var list=currentList();
if(!list.length){showToast('当前筛选条件下没有可导出的数据');return;}
var head=['序号','预约单号','姓名','员工编号','性别','年龄','单位','部门','手机号','预约状态','预约时段','预约健康室','设备名称','创建时间'];
var esc=function(v){return '"'+String(v).replace(/"/g,'""')+'"';};
var rows=list.map(function(r,i){return [i+1,r.code,r.name,r.empNo,r.sex,r.age,r.unit,r.depar,r.phone,r.status,r.slot,r.room,r.dev,r.created].map(esc).join(',');});
var csv='\ufeff'+head.map(esc).join(',')+'\r\n'+rows.join('\r\n');
var filename='员工预约记录_'+todayStr()+'('+list.length+').csv';
var blob=new Blob([csv],{type:'text/csv;charset=utf-8;'});
var a=document.createElement('a');
a.href=URL.createObjectURL(blob);a.download=filename;
document.body.appendChild(a);a.click();document.body.removeChild(a);
URL.revokeObjectURL(a.href);
exportTasks.unshift({no:'EX-'+Date.now(),filename:filename,count:list.length,status:'已完成',time:nowStr()});
showToast('已导出 '+list.length+' 条预约记录');
}
function openBatchDel(){
var idxs=Array.prototype.slice.call(document.querySelectorAll('#tbody .ck:checked')).map(function(c){return +c.value;});
if(!idxs.length){showToast('请先勾选要删除的预约记录');return;}
openDel(idxs);
}
function closeDel(){document.getElementById('delMask').classList.remove('open');}
function confirmDel(){
/* 按下标从大到小删除,避免前面的删除影响后面的下标 */
delCodes.slice().sort(function(a,b){return b-a;}).forEach(function(i){appts.splice(i,1);});
closeDel();render();showToast('已删除 '+delCodes.length+' 条预约记录');
/* ---- 查看导出任务 ---- */
function viewExportTasks(){renderExportTasks();document.getElementById('taskMask').classList.add('open');}
function closeTasks(){document.getElementById('taskMask').classList.remove('open');}
function renderExportTasks(){
document.getElementById('taskBody').innerHTML=exportTasks.map(function(t){
var b=t.status==='已完成'?'badge--success':(t.status==='处理中'?'badge--warning':'badge--muted');
return '<tr><td style="font-family:monospace;">'+t.no+'</td>'
+'<td style="text-align:left;padding-left:12px;">'+t.filename+'</td>'
+'<td>'+t.count+'</td><td><span class="badge '+b+'">'+t.status+'</span></td>'
+'<td>'+t.time+'</td>'
+'<td><button class="btn btn-link" onclick="downloadTask(\''+t.no+'\')">下载</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="6">暂无导出任务</td></tr>';
}
function downloadTask(no){showToast('导出任务 '+no+' 已开始下载');}
/* [交互] 侧栏分组展开/收起 */
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
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);
}
resetOrg();
render();
</script>
</body>
+15 -67
View File
@@ -53,6 +53,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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:space-between;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-sm);font-weight:600;}
.table-toolbar{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__content{overflow-x:auto;-webkit-overflow-scrolling:touch;}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:1280px;}
thead th{background:#FAFAFA;padding:10px 14px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
@@ -109,8 +110,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -131,13 +132,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub active">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -150,35 +151,9 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>轮播图标题</label>
<input class="filter-input" id="filterTitle" placeholder="关键字" oninput="render()">
</div>
<div class="filter-item">
<label>所属模块</label>
<select class="filter-select" id="filterMod" onchange="render()">
<option value="">全部</option><option>小屋首页</option><option>健康室详情</option><option>预约页</option>
</select>
</div>
<div class="filter-item">
<label>是否有效</label>
<select class="filter-select" id="filterValid" onchange="render()" style="min-width:90px;">
<option value="">全部</option><option></option><option></option>
</select>
</div>
<button class="btn btn-primary" onclick="render();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>查询
</button>
<button class="btn btn-default" onclick="resetFilter()">重置</button>
</div>
<!-- [交互] 列表 -->
<!-- [交互] 列表(操作按钮置于列表上方,与列表左对齐) -->
<div class="table-card">
<div class="table-card__head">
<span class="table-card__title">轮播图列表</span>
<div class="table-toolbar">
<button class="btn btn-primary" onclick="openForm(-1)">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 1v10M1 6h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>新增
</button>
@@ -206,9 +181,9 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<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="fValid" value="是" checked></label><label><input type="radio" name="fValid" value="否"></label></div>
</div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>类型</div><div class="form-row__control">
<div class="form-row"><div class="form-row__label"><i>*</i>所属模块</div><div class="form-row__control">
<select class="filter-select" id="fMod" style="width:100%;height:34px;">
<option value="">请选择所属模块</option><option>小屋首页</option><option>健康室详情</option><option>预约页</option>
<option value="">请选择所属模块</option><option>健康小屋</option><option>报道终端</option>
</select>
</div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>轮播图</div><div class="form-row__control">
@@ -225,15 +200,6 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
</div>
<!-- [交互] 详情弹窗 -->
<div class="modal-mask" id="detailMask">
<div class="modal--wide">
<div class="modal__header"><span>轮播图详情</span><button class="modal__close" onclick="closeDetail()">&times;</button></div>
<div class="modal__body" id="detailBody"></div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeDetail()">关闭</button></div>
</div>
</div>
<!-- [交互] 删除确认 -->
<div class="modal-mask" id="delMask">
<div class="modal">
@@ -252,19 +218,16 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
var gradients=['linear-gradient(135deg,#1890FF,#36CFC9)','linear-gradient(135deg,#722ED1,#1890FF)','linear-gradient(135deg,#FA8C16,#F5222D)','linear-gradient(135deg,#52C41A,#13C2C2)','linear-gradient(135deg,#EB2F96,#722ED1)','linear-gradient(135deg,#2F54EB,#B37FEB)'];
/* banners:编号规则 HB-年份-序号;与小屋管理各健康室终端展示对应 */
var banners=[
{no:1,title:'关爱健康 从心开始',code:'HB-2026-001',mod:'小屋首页',valid:'是',date:'2026-08-21 10:12'},
{no:2,title:'CQ油田员工健康工程',code:'HB-2026-002',mod:'小屋首页',valid:'是',date:'2026-08-21 10:30'},
{no:3,title:'体检季 · 健康室开放时间表',code:'HB-2026-003',mod:'预约页',valid:'是',date:'2026-09-01 09:05'},
{no:4,title:'心理健康月 · 减压训练指南',code:'HB-2026-004',mod:'健康室详情',valid:'是',date:'2026-09-03 15:47'},
{no:5,title:'三减三健 健康饮食宣传',code:'HB-2026-005',mod:'小屋首页',valid:'否',date:'2026-06-12 11:20'},
{no:6,title:'旧版 · 健康小屋使用须知',code:'HB-2025-011',mod:'小屋首页',valid:'否',date:'2025-11-03 16:02'}
{no:1,title:'关爱健康 从心开始',code:'HB-2026-001',mod:'健康小屋',valid:'是',date:'2026-08-21 10:12'},
{no:2,title:'CQ油田员工健康工程',code:'HB-2026-002',mod:'健康小屋',valid:'是',date:'2026-08-21 10:30'},
{no:3,title:'体检季 · 健康室开放时间表',code:'HB-2026-003',mod:'报道终端',valid:'是',date:'2026-09-01 09:05'},
{no:4,title:'心理健康月 · 减压训练指南',code:'HB-2026-004',mod:'健康小屋',valid:'是',date:'2026-09-03 15:47'},
{no:5,title:'三减三健 健康饮食宣传',code:'HB-2026-005',mod:'健康小屋',valid:'否',date:'2026-06-12 11:20'},
{no:6,title:'旧版 · 健康小屋使用须知',code:'HB-2025-011',mod:'健康小屋',valid:'否',date:'2025-11-03 16:02'}
];
var editIdx=-1;
function render(){
var kt=document.getElementById('filterTitle').value.trim();
var km=document.getElementById('filterMod').value,kv=document.getElementById('filterValid').value;
var list=banners.slice().sort(function(a,b){return a.no-b.no;}).filter(function(b){
return b.title.indexOf(kt)>=0&&(!km||b.mod===km)&&(!kv||b.valid===kv);});
var list=banners.slice().sort(function(a,b){return a.no-b.no;});
document.getElementById('tbody').innerHTML=list.map(function(b){
var i=banners.indexOf(b);
return '<tr><td>'+b.no+'</td><td style="text-align:left;padding-left:14px;">'+b.title+'</td><td style="font-family:monospace;">'+b.code+'</td>'
@@ -272,12 +235,10 @@ function render(){
+'<td><span class="badge '+(b.valid==='是'?'badge--success':'badge--muted')+'">'+b.valid+'</span></td>'
+'<td><span class="badge badge--info">'+b.mod+'</span></td><td>'+b.date+'</td>'
+'<td><button class="btn btn-link" onclick="openForm('+i+')">编辑</button>'
+'<button class="btn btn-link" onclick="openDetail('+i+')">详情</button>'
+'<button class="btn btn-link" style="color:var(--danger);" onclick="openDel('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="8">没有符合条件的轮播图</td></tr>';
document.getElementById('totalTip').textContent='共 '+list.length+' 张 · 生效 '+banners.filter(function(b){return b.valid==='是';}).length+' 张';
}
function resetFilter(){['filterTitle','filterMod','filterValid'].forEach(function(id){document.getElementById(id).value='';});render();}
function pickImage(){document.getElementById('uploadText').textContent='已选择 banner-'+Date.now()%1000+'.jpg(原型示意)';showToast('图片已暂存,保存后生效');}
function openForm(i){
editIdx=i;
@@ -298,19 +259,6 @@ function saveForm(){
else{var b=banners[editIdx];b.no=no;b.title=title;b.mod=mod;b.valid=valid;}
closeForm();render();showToast(editIdx<0?'新增成功,已排入轮播队列':'保存成功,终端缓存 5 分钟内刷新');
}
function di(k,v,full){return '<div class="detail-item'+(full?' detail-item--full':'')+'"><span class="dk">'+k+'</span><span class="dv">'+v+'</span></div>';}
function openDetail(i){
var b=banners[i];
document.getElementById('detailBody').innerHTML='<div class="detail-grid">'
+di('序号',b.no)+di('编号',b.code)
+di('标题',b.title,true)
+di('是否有效',b.valid)+di('所属模块',b.mod)
+di('创建时间',b.date,true)
+di('轮播图预览','<div class="banner-thumb" style="background:'+gradients[i%gradients.length]+';width:280px;margin-top:4px;">'+b.title.slice(0,12)+'</div>',true)
+'</div>';
document.getElementById('detailMask').classList.add('open');
}
function closeDetail(){document.getElementById('detailMask').classList.remove('open');}
var delIdx=-1;
function openDel(i){
delIdx=i;var b=banners[i];
@@ -0,0 +1,309 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小屋数据管理 | 一线医疗</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;font-size:var(--font-sm);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;
--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.10);
}
.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:200px;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 14px;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);}
.btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.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;padding-bottom:24px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;letter-spacing:.5px;}
.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);}
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.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);}
.page-head{display:flex;align-items:center;gap:12px;margin-bottom:16px;}
.page-head__title{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
.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-danger{background:var(--danger);color:#fff;}.btn-danger:hover{background:#ff7875;}
.btn-link{background:none;color:var(--primary);padding:0 4px;height:auto;}.btn-link:hover{color:#40a9ff;}
.info-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:20px 24px;margin-bottom:16px;}
.info-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border);}
.info-card__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px 32px;}
.info-item{display:flex;align-items:center;gap:10px;font-size:var(--font-sm);}
.info-item label{color:var(--text-secondary);white-space:nowrap;min-width:84px;}
.info-item span{color:var(--text-primary);font-weight:500;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__tools{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__content{overflow-x:auto;-webkit-overflow-scrolling:touch;}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:760px;}
thead th{background:#FAFAFA;padding:10px 14px;text-align:center;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 14px;text-align:center;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid #F0F0F0;white-space:nowrap;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
.tfoot td{background:#FAFAFA;font-weight:600;border-top:1px solid var(--border);font-size:var(--font-sm);}
.td-empty{text-align:center;padding:48px 0;color:var(--text-placeholder);}
.toast{position:fixed;top:80px;left:50%;transform:translateX(-50%);background:#333;color:#fff;padding:10px 24px;border-radius:var(--radius-sm);font-size:var(--font-sm);z-index:9999;opacity:0;transition:opacity .2s;pointer-events:none;}
.toast.show{opacity:1;}
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:950;align-items:center;justify-content:center;}
.modal-mask.open{display:flex;}
.modal{width:440px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:24px;}
.modal__header{display:flex;align-items:center;justify-content:space-between;font-size:var(--font-base);font-weight:600;padding-bottom:12px;border-bottom:1px solid var(--border);}
.modal__close{cursor:pointer;color:var(--text-placeholder);font-size:20px;line-height:1;border:none;background:none;}
.modal__close:hover{color:var(--text-primary);}
.modal__body{flex:1;overflow-y:auto;padding:16px 2px 4px;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;margin-top:16px;}
.drop-zone{border:1px dashed var(--border);border-radius:var(--radius-sm);padding:16px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);cursor:pointer;line-height:1.8;}
.drop-zone:hover{border-color:var(--primary);color:var(--primary);background:#F8FBFF;}
.form-row{display:flex;align-items:flex-start;gap:12px;margin-bottom:16px;}
.form-row__label{width:110px;flex-shrink:0;text-align:right;font-size:var(--font-sm);color:var(--text-secondary);padding-top:7px;}
.form-row__label i{color:var(--danger);font-style:normal;margin-right:2px;}
.form-row__control{flex:1;display:flex;align-items:center;gap:8px;}
.form-input{width:100%;height:34px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);outline:none;}
.form-input:focus{border-color:var(--primary);}
.modal--wide{width:680px;max-height:86vh;display:flex;flex-direction:column;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:24px;}
.inner-table{width:100%;min-width:0;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;border-collapse:collapse;}
.inner-table th{background:#FAFAFA;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;padding:7px;border-bottom:1px solid var(--border);}
.inner-table td{font-size:var(--font-sm);padding:8px 7px;text-align:center;border-bottom:1px solid #F0F0F0;}
</style>
</head>
<body>
<!-- 顶部导航栏 -->
<div 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="M14 6.5l7 3.5v4.5c0 4.2-2.9 6.8-7 7.5-4.1-.7-7-3.3-7-7.5V10l7-3.5z" stroke="#fff" stroke-width="1.8" fill="none"/><path d="M11 13.5h6M14 10.5v6" stroke="#fff" stroke-width="1.8" stroke-linecap="round"/></svg> 一线医疗</div>
<nav class="top-bar__nav">
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8l5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg> 返回首页</a>
<div class="avatar"></div>
<span style="color:rgba(255,255,255,0.65);">系统管理员 ▾</span>
</div>
</div>
<!-- 侧边栏(当前一级分组「健康小屋」的二级菜单) -->
<aside class="sidebar">
<div class="sidebar-menu__title">健康小屋</div>
<div class="sidebar-group" id="grpCollect">
<div class="sidebar-group__label" onclick="toggleGroup('grpCollect')">智能采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group open" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="hut-cabin.html" class="sidebar-item sidebar-item--sub active">小屋管理</a>
<a href="hut-cabin-stat.html" class="sidebar-item sidebar-item--sub">小屋统计</a>
</div>
</div>
</aside>
<!-- 主内容 -->
<main class="main">
<div class="page-head">
<a class="btn btn-default" href="hut-cabin.html"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M9 3L5 7l4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg> 返回小屋管理</a>
<span class="page-head__title" id="pageTitle">小屋数据管理</span>
</div>
<!-- 基本信息卡片 -->
<div class="info-card">
<div class="info-card__title">健康小屋基本信息</div>
<div class="info-card__grid">
<div class="info-item"><label>小屋名称</label><span id="cName"></span></div>
<div class="info-item"><label>所属单位</label><span id="cUnit"></span></div>
<div class="info-item"><label>所属部门</label><span id="cDepar"></span></div>
<div class="info-item"><label>办公室电话</label><span id="cOffice"></span></div>
<div class="info-item"><label>负责人</label><span id="cMgr"></span></div>
<div class="info-item"><label>负责人电话</label><span id="cMobile"></span></div>
<div class="info-item"><label>建成日期</label><span id="cDate"></span></div>
<div class="info-item"><label>配备金额</label><span id="cMoney"></span></div>
<div class="info-item"><label>配备设备数</label><span id="cDevNum"></span></div>
</div>
</div>
<!-- 记录列表 -->
<div class="table-card">
<div class="table-card__tools">
<button class="btn btn-primary" onclick="openAdd()">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 1v10M1 6h10" stroke="#fff" stroke-width="1.6" stroke-linecap="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>
</div>
<div class="table-card__content">
<table>
<thead><tr>
<th style="width:56px;">序号</th><th>日期</th><th>血压测量人次</th><th>血糖测量人次</th><th style="width:120px;">操作</th>
</tr></thead>
<tbody id="tbody"></tbody>
</table>
</div>
</div>
</main>
<!-- [交互] 单个新增弹窗 -->
<div class="modal-mask" id="addMask">
<div class="modal">
<div class="modal__header"><span>新增测量记录</span><button class="modal__close" onclick="closeAdd()">&times;</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" type="date" id="aDate"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>血压测量人次</div><div class="form-row__control"><input class="form-input" type="number" id="aBp" placeholder="请输入"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>血糖测量人次</div><div class="form-row__control"><input class="form-input" type="number" id="aBg" placeholder="请输入"></div></div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeAdd()">取消</button>
<button class="btn btn-primary" onclick="saveAdd()">保存</button>
</div>
</div>
</div>
<!-- [交互] 批量导入弹窗 -->
<div class="modal-mask" id="importMask">
<div class="modal">
<div class="modal__header"><span>批量导入</span><button class="modal__close" onclick="closeImport()">&times;</button></div>
<div class="modal__body">
<div class="drop-zone" onclick="showToast('演示环境请选择本地文件(仅支持.xlsx)')">点击选择或拖拽文件到此处上传<br>测量记录导入模板.xlsx</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeImport()">取消</button>
<button class="btn btn-primary" onclick="doImport()">开始导入</button>
</div>
</div>
</div>
<!-- [交互] 导入任务:异步任务列表 -->
<div class="modal-mask" id="importTaskMask">
<div class="modal--wide">
<div class="modal__header"><span>导入任务</span><button class="modal__close" onclick="closeImportTasks()">&times;</button></div>
<div class="modal__body">
<table class="inner-table">
<thead><tr><th>文件名称</th><th>提交时间</th><th>结果</th><th>操作</th></tr></thead>
<tbody>
<tr><td style="font-family:monospace;">record_ledger_20260905.xlsx</td><td>2026-09-05 14:22</td><td style="color:#389E0D;">成功:新增 3 条、更新 1 条</td><td><button class="btn btn-link" onclick="showToast('导入明细下载中…')">下载明细</button></td></tr>
<tr><td style="font-family:monospace;">record_ledger_20260820.xlsx</td><td>2026-08-20 10:05</td><td style="color:#D48806;">部分成功:新增 2 条、失败 1 条</td><td><button class="btn btn-link" onclick="showToast('导入明细下载中…')">下载明细</button></td></tr>
</tbody>
</table>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeImportTasks()">关闭</button></div>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
/* 单位→部门共享池(与 hut-cabin 一致) */
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
/* 小屋台账(与 hut-cabin 勾稽);records 为每日测量人次(血压/血糖) */
var cabins=[
{name:'樊学基地健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'王守正',office:'0963-88201',mobile:'13821622211',date:'2024-05-18',money:86000,devNum:6},
{name:'麻子山站点健康小屋',unit:'采油一厂',depar:'麻子山作业区',mgr:'安伟',office:'0963-88302',mobile:'13937440834',date:'2024-06-02',money:75000,devNum:5},
{name:'桐园基地健康小屋',unit:'采油二厂',depar:'桐园作业区',mgr:'陈国栋',office:'0963-88401',mobile:'13708495520',date:'2024-07-11',money:86000,devNum:6},
{name:'姬圪台站点健康小屋',unit:'采油二厂',depar:'姬圪台作业区',mgr:'刘志强',office:'0963-88402',mobile:'13607587712',date:'2024-09-20',money:68000,devNum:4},
{name:'西峰基地健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'马文秀',office:'0963-88501',mobile:'13589259086',date:'2024-08-15',money:92000,devNum:7},
{name:'靖边气田健康小屋',unit:'采气一厂',depar:'靖边气田',mgr:'周铁祥',office:'0963-88601',mobile:'13848934417',date:'2025-03-06',money:86000,devNum:6},
{name:'樊学作业区第二健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'张国亮',office:'0963-88203',mobile:'13976362680',date:'2025-04-22',money:75000,devNum:5},
{name:'西峰前线健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'冯建军',office:'0963-88502',mobile:'13720283341',date:'2025-06-10',money:68000,devNum:4}
];
/* 每座小屋生成 09-01~09-07 七日测量记录(血压人次=基准±波动,血糖≈血压×0.8) */
(function(){
var bases=[28,22,26,18,31,25,20,16],delta=[0,3,-2,5,1,7,-6];
cabins.forEach(function(c,ci){
c.records=[0,1,2,3,4,5,6].map(function(k){
var bp=bases[ci]+delta[k];
return {d:'2026-09-0'+(k+1),bp:bp,bg:Math.round(bp*0.8)};
});
});
})();
/* 通过 URL 参数 id 定位小屋 */
var idx=parseInt(new URLSearchParams(location.search).get('id')||'0',10);
if(isNaN(idx)||idx<0||idx>=cabins.length)idx=0;
var c=cabins[idx];
function money(n){return '¥'+n.toLocaleString('en-US');}
function renderInfo(){
document.getElementById('pageTitle').textContent='数据管理 · '+c.name;
document.getElementById('cName').textContent=c.name;
document.getElementById('cUnit').textContent=c.unit;
document.getElementById('cDepar').textContent=c.depar||'—';
document.getElementById('cOffice').textContent=c.office;
document.getElementById('cMgr').textContent=c.mgr;
document.getElementById('cMobile').textContent=c.mobile;
document.getElementById('cDate').textContent=c.date;
document.getElementById('cMoney').textContent=money(c.money);
document.getElementById('cDevNum').textContent=c.devNum+' 台';
}
function renderList(){
document.getElementById('tbody').innerHTML=c.records.map(function(r,i){
return '<tr><td>'+(i+1)+'</td><td>'+r.d+'</td><td>'+r.bp+'</td><td>'+r.bg+'</td>'
+'<td><button class="btn btn-link" style="color:var(--danger);" onclick="delRec('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="5">暂无测量记录</td></tr>';
}
/* ---- 单个新增 ---- */
function openAdd(){
document.getElementById('aDate').value='';document.getElementById('aBp').value='';document.getElementById('aBg').value='';
document.getElementById('addMask').classList.add('open');
}
function closeAdd(){document.getElementById('addMask').classList.remove('open');}
function saveAdd(){
var d=document.getElementById('aDate').value,bp=document.getElementById('aBp').value,bg=document.getElementById('aBg').value;
if(!d||bp===''||bg===''){showToast('请填写日期与血压/血糖人次');return;}
var rec={d:d,bp:+bp,bg:+bg};
var old=-1;c.records.forEach(function(r,i){if(r.d===d)old=i;});
if(old>=0)c.records[old]=rec;else c.records.push(rec);
c.records.sort(function(a,b){return a.d<b.d?-1:1;});
closeAdd();renderList();showToast(old>=0?'该日期已有记录,已按最新录入覆盖':'测量记录已新增');
}
function delRec(i){c.records.splice(i,1);renderList();showToast('记录已删除');}
/* ---- 批量导入 ---- */
function openImport(){document.getElementById('importMask').classList.add('open');}
function closeImport(){document.getElementById('importMask').classList.remove('open');}
function doImport(){closeImport();showToast('导入任务已提交,完成后将推送站内通知');}
function openImportTasks(){document.getElementById('importTaskMask').classList.add('open');}
function closeImportTasks(){document.getElementById('importTaskMask').classList.remove('open');}
/* [交互] 侧栏分组展开/收起 */
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
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);
}
renderInfo();renderList();
</script>
</body>
</html>
+192 -61
View File
@@ -47,12 +47,35 @@ 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);}
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
.seg span{padding:6px 14px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;background:#fff;border-right:1px solid var(--border);}
.seg span:last-child{border-right:none;}
.seg span.on{background:var(--primary);color:#fff;}
.seg__btn{height:32px;padding:0 14px;border:none;background:#fff;color:var(--text-primary);font-size:var(--font-sm);cursor:pointer;border-right:1px solid var(--border);}
.seg__btn:last-child{border-right:none;}
.seg__btn.active{background:var(--primary);color:#fff;}
.week-picker{position:relative;display:inline-block;}
.week-trigger{height:36px;min-width:220px;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;}
.week-trigger.open,.week-trigger:hover{border-color:var(--primary);}
.week-panel{display:none;position:absolute;top:40px;left:0;z-index:320;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);padding:12px;width:280px;}
.week-panel.open{display:block;}
.week-cal__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.week-cal__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
.week-cal__nav{width:24px;height:24px;border:none;background:none;color:var(--text-secondary);cursor:pointer;font-size:16px;line-height:1;border-radius:var(--radius-sm);}
.week-cal__nav:hover{color:var(--primary);background:var(--primary-light);}
.week-cal__weekdays{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px;}
.week-cal__weekdays span{text-align:center;font-size:var(--font-xs);color:var(--text-placeholder);padding:2px 0;}
.week-cal__body{display:flex;flex-direction:column;gap:2px;}
.week-row{display:grid;grid-template-columns:repeat(7,1fr);cursor:pointer;border-radius:var(--radius-sm);}
.week-row:hover{background:var(--primary-light);}
.week-row.active{background:var(--primary-light);}
.week-cell{height:28px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);}
.week-row.active .week-cell{color:var(--primary);font-weight:600;}
.week-cell--out{color:var(--text-placeholder);}
.year-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:4px;}
.year-cell{height:32px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);cursor:pointer;}
.year-cell:hover{background:var(--primary-light);color:var(--primary);}
.year-cell.active{background:var(--primary);color:#fff;font-weight:600;}
.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);}
.action-bar{display:flex;justify-content:flex-start;gap:8px;margin-top:16px;}
.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:space-between;padding:12px 16px;border-bottom:1px solid var(--border);gap:10px;flex-wrap:wrap;}
.table-card__title{font-size:var(--font-sm);font-weight:600;}
@@ -83,8 +106,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -105,13 +128,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group open" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -124,41 +147,78 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- [交互] 周期切换 + 单位级联筛选 -->
<!-- [交互] 统计周期:周/月/年/选时 四选一 -->
<div class="filter-bar">
<div class="filter-item">
<label>统计周期</label>
<div class="seg" id="segPeriod">
<span data-p="week" class="on" onclick="setPeriod('week')"></span>
<span data-p="month" onclick="setPeriod('month')"></span>
<span data-p="year" onclick="setPeriod('year')"></span>
<span data-p="custom" onclick="setPeriod('custom')">自定义</span>
<div class="seg">
<button class="seg__btn active" id="segWeek" onclick="setPeriod('week')">统计</button>
<button class="seg__btn" id="segMonth" onclick="setPeriod('month')">统计</button>
<button class="seg__btn" id="segYear" onclick="setPeriod('year')">统计</button>
<button class="seg__btn" id="segCustom" onclick="setPeriod('custom')">选时统计</button>
</div>
</div>
<div class="filter-item" id="customRange" style="display:none;">
<input class="filter-input" type="date" id="cFrom" value="2026-08-25" onchange="render()">
<span style="color:var(--text-placeholder);">~</span>
<input class="filter-input" type="date" id="cTo" value="2026-09-05" onchange="render()">
</div>
<!-- [交互] 筛选条件:统计时间 + 所属单位 + 查询 -->
<div class="filter-bar" style="margin-top:16px;">
<div class="filter-item" id="weekItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="weekTrigger" onclick="toggleWeekPanel()">2026-08-31 至 2026-09-06</div>
<div class="week-panel" id="weekPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(-1)"></button>
<span class="week-cal__title" id="weekCalTitle">2026年9月</span>
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(1)"></button>
</div>
<div class="week-cal__weekdays"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="week-cal__body" id="weekCalBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="monthItem" style="display:none;">
<label>统计时间</label>
<input class="filter-input" type="month" id="monthInput" value="2026-09" onchange="render()" style="min-width:150px;">
</div>
<div class="filter-item" id="yearItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="yearTrigger" onclick="toggleYearPanel()" style="min-width:120px;">2026年</div>
<div class="week-panel" id="yearPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftYearPage(-1)"></button>
<span class="week-cal__title" id="yearPageTitle">2020 - 2029</span>
<button type="button" class="week-cal__nav" onclick="shiftYearPage(1)"></button>
</div>
<div class="year-grid" id="yearPanelBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="rangeItem" style="display:none;">
<label>统计时间</label>
<input class="filter-input" type="date" id="filterFrom" value="2026-08-31" onchange="render()" style="min-width:130px;">
<span style="color:var(--text-placeholder);font-size:var(--font-xs);"></span>
<input class="filter-input" type="date" id="filterTo" value="2026-09-06" onchange="render()" style="min-width:130px;">
</div>
<div class="filter-item">
<label>单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()">
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="render()">
<option value="">全部单位</option>
<option>采油一厂</option><option>采油二厂</option><option>第四采油厂</option><option>采气一厂</option>
</select>
</div>
<div class="filter-item">
<label>部门</label>
<select class="filter-select" id="filterDepar" onchange="render()" disabled>
<option value="">全部部门</option>
</select>
<div class="filter-item" style="margin-left:auto;">
<button class="btn btn-primary" onclick="render();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>查询
</button>
</div>
<button class="btn btn-primary" onclick="render();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>统计
</button>
<button class="btn btn-default" onclick="resetAll()">重置</button>
</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 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
</div>
<!-- [交互] 列表 -->
<div class="table-card">
@@ -169,7 +229,6 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<table>
<thead id="thead"></thead>
<tbody id="tbody"></tbody>
<tfoot id="tfoot"></tfoot>
</table>
</div>
<div class="pagination"><span id="totalTip"></span></div>
@@ -200,28 +259,103 @@ function genStat(from,to,base){
return {bp:s,bg:g};
}
var period='week',sortKey='',sortDir='desc';
function periodRange(){
if(period==='week')return{label:'本周',from:'2026-09-01',to:'2026-09-07'};
if(period==='month')return{label:'本月至今',from:'2026-09-01',to:'2026-09-07'};
if(period==='year')return{label:'本年',from:'2026-01-01',to:'2026-09-07'};
return{label:'自定义区间',from:document.getElementById('cFrom').value,to:document.getElementById('cTo').value};
/* 各周期统计区间:周从日历选周取值,月/年/选时来自对应可选控件(带默认值) */
function pad2(n){return ('0'+n).slice(-2);}
function fmtDate(d){return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate());}
function monthRange(m){
var mm=String(m||'').match(/^(\d{4})-(\d{2})$/);
if(!mm)return [m,m];
var y=+mm[1],mo=+mm[2];
return [y+'-'+mm[2]+'-01',fmtDate(new Date(y,mo,0))];
}
function setPeriod(p){
period=p;
document.querySelectorAll('#segPeriod span').forEach(function(el){el.classList.toggle('on',el.getAttribute('data-p')===p);});
document.getElementById('customRange').style.display=p==='custom'?'flex':'none';
/* [交互] 周选择:点击「统计时间」打开日历,点选一周(周一至周日),展示为日期范围 */
var weekSelStart='2026-08-31'; // 选中周的周一
var calYear=2026,calMonth=8; // 日历当前显示的月份(0 起,8=9月)
function parseDate(s){var p=s.split('-');return new Date(+p[0],+p[1]-1,+p[2]);}
function weekEndOf(start){return fmtDate(new Date(start.getFullYear(),start.getMonth(),start.getDate()+6));}
function updateWeekTrigger(){
var d=parseDate(weekSelStart);
document.getElementById('weekTrigger').textContent=fmtDate(d)+' 至 '+weekEndOf(d);
}
function toggleWeekPanel(){
var open=document.getElementById('weekPanel').classList.toggle('open');
document.getElementById('weekTrigger').classList.toggle('open',open);
if(open)renderWeekCalendar();
}
function shiftWeekMonth(delta){
calMonth+=delta;
if(calMonth<0){calMonth=11;calYear--;}
if(calMonth>11){calMonth=0;calYear++;}
renderWeekCalendar();
}
function renderWeekCalendar(){
document.getElementById('weekCalTitle').textContent=calYear+'年'+(calMonth+1)+'月';
var first=new Date(calYear,calMonth,1);
var start=new Date(calYear,calMonth,1-((first.getDay()+6)%7)); // 该月首日所在周的周一
var sel=parseDate(weekSelStart);
var html='';
for(var r=0;r<6;r++){
var ws=new Date(start.getFullYear(),start.getMonth(),start.getDate()+r*7);
var isSel=ws.getTime()===sel.getTime();
html+='<div class="week-row'+(isSel?' active':'')+'" onclick="selectWeek(\''+fmtDate(ws)+'\')">';
for(var c=0;c<7;c++){
var d=new Date(ws.getFullYear(),ws.getMonth(),ws.getDate()+c);
html+='<div class="week-cell'+(d.getMonth()!==calMonth?' week-cell--out':'')+'">'+d.getDate()+'</div>';
}
html+='</div>';
}
document.getElementById('weekCalBody').innerHTML=html;
}
function selectWeek(start){
weekSelStart=start;
var d=parseDate(start);
calYear=d.getFullYear();calMonth=d.getMonth();
updateWeekTrigger();
document.getElementById('weekPanel').classList.remove('open');
document.getElementById('weekTrigger').classList.remove('open');
render();
}
function onFilterUnit(){
var u=document.getElementById('filterUnit').value;
var sel=document.getElementById('filterDepar');
sel.innerHTML='<option value="">全部部门</option>'+(u?unitDeparts[u].map(function(d){return '<option>'+d+'</option>';}).join(''):'');
sel.disabled=!u;render();
/* [交互] 年选择:点击「统计时间」打开十年历,点选年份 */
var yearSel=2026;
var yearPage=2020; // 当前展示的十年起始
function updateYearTrigger(){document.getElementById('yearTrigger').textContent=yearSel+'年';}
function toggleYearPanel(){
var open=document.getElementById('yearPanel').classList.toggle('open');
document.getElementById('yearTrigger').classList.toggle('open',open);
if(open)renderYearPanel();
}
function resetAll(){
['filterUnit','filterDepar'].forEach(function(id){document.getElementById(id).value='';});
document.getElementById('filterDepar').disabled=true;
setPeriod('week');
function shiftYearPage(delta){yearPage+=delta*10;renderYearPanel();}
function renderYearPanel(){
document.getElementById('yearPageTitle').textContent=yearPage+' - '+(yearPage+9);
var html='';
for(var i=0;i<10;i++){
var y=yearPage+i;
html+='<div class="year-cell'+(y===yearSel?' active':'')+'" onclick="selectYear('+y+')">'+y+'</div>';
}
document.getElementById('yearPanelBody').innerHTML=html;
}
function selectYear(y){
yearSel=y;
updateYearTrigger();
document.getElementById('yearPanel').classList.remove('open');
document.getElementById('yearTrigger').classList.remove('open');
render();
}
function curRange(){
if(period==='week'){var d=parseDate(weekSelStart);return {label:'本周',from:fmtDate(d),to:weekEndOf(d)};}
if(period==='month'){var m=monthRange(document.getElementById('monthInput').value);return {label:'本月',from:m[0],to:m[1]};}
if(period==='year')return {label:'本年',from:yearSel+'-01-01',to:yearSel+'-12-31'};
return {label:'自定义区间',from:document.getElementById('filterFrom').value,to:document.getElementById('filterTo').value};
}
/* [交互] 周期切换:周/月/年显示对应可选时间控件(带默认值),选时统计展开起止日期 */
function setPeriod(p){
period=p;
['Week','Month','Year','Custom'].forEach(function(k){document.getElementById('seg'+k).classList.toggle('active',p===k.toLowerCase());});
document.getElementById('weekItem').style.display=(p==='week')?'flex':'none';
document.getElementById('monthItem').style.display=(p==='month')?'flex':'none';
document.getElementById('yearItem').style.display=(p==='year')?'flex':'none';
document.getElementById('rangeItem').style.display=(p==='custom')?'flex':'none';
render();
}
function sortBy(k){
if(sortKey===k){sortDir=sortDir==='desc'?'asc':'desc';}else{sortKey=k;sortDir='desc';}
@@ -229,13 +363,13 @@ function sortBy(k){
}
function arrow(k){return sortKey!==k?' ⇅':(sortDir==='desc'?' ↓':' ↑');}
function calcRows(){
var u=document.getElementById('filterUnit').value,d=document.getElementById('filterDepar').value;
var pr=periodRange();
var rows=cabins.filter(function(c){return (!u||c.unit===u)&&(!d||c.depar===d);}).map(function(c){
var u=document.getElementById('filterUnit').value;
var pr=curRange();
var rows=cabins.filter(function(c){return !u||c.unit===u;}).map(function(c){
var p=genStat(pr.from,pr.to,c.base),t=genStat(c.date,'2026-09-07',c.base);
return{c:c,pbp:p.bp,pbg:p.bg,tbp:t.bp,tbg:t.bg};
});
if(sortKey){rows.sort(function(a,b){var va=sortKey==='bp'?a.pbp:a.pbg,vb=sortKey==='bp'?b.pbp:b.pbg;return sortDir==='desc'?vb-va:va-vb;});}
if(sortKey){rows.sort(function(a,b){var va=a[sortKey],vb=b[sortKey];return sortDir==='desc'?vb-va:va-vb;});}
return{rows:rows,pr:pr};
}
function render(){
@@ -243,28 +377,25 @@ function render(){
document.getElementById('cardTitle').textContent='小屋测量统计 · '+pr.label;
document.getElementById('thead').innerHTML='<tr><th style="width:56px;">序号</th><th style="min-width:190px;">小屋名称</th>'
+'<th style="min-width:110px;">单位名称</th><th style="min-width:110px;">部门名称</th><th>建成日期</th><th>配备设备数</th>'
+'<th class="sortable" onclick="sortBy(\'bp\')" style="min-width:150px;">'+pr.label+'血压测量人次'+arrow('bp')+'</th>'
+'<th class="sortable" onclick="sortBy(\'bg\')" style="min-width:150px;">'+pr.label+'血糖测量人次'+arrow('bg')+'</th>'
+'<th style="min-width:130px;">累计血压测量人次</th><th style="min-width:130px;">累计血测量人次</th><th style="width:90px;">操作</th></tr>';
+'<th class="sortable" onclick="sortBy(\'pbp\')" style="min-width:150px;">'+pr.label+'血压测量人次'+arrow('pbp')+'</th>'
+'<th class="sortable" onclick="sortBy(\'pbg\')" style="min-width:150px;">'+pr.label+'血糖测量人次'+arrow('pbg')+'</th>'
+'<th class="sortable" onclick="sortBy(\'tbp\')" style="min-width:130px;">累计血测量人次'+arrow('tbp')+'</th>'
+'<th class="sortable" onclick="sortBy(\'tbg\')" style="min-width:130px;">累计血糖测量人次'+arrow('tbg')+'</th></tr>';
document.getElementById('tbody').innerHTML=rows.map(function(r){
return '<tr><td>'+(cabins.indexOf(r.c)+1)+'</td><td style="text-align:left;padding-left:12px;font-weight:500;">'+r.c.name+'</td>'
+'<td>'+r.c.unit+'</td><td>'+r.c.depar+'</td><td>'+r.c.date+'</td><td>'+r.c.devNum+' 台</td>'
+'<td>'+r.pbp+'</td><td>'+r.pbg+'</td><td>'+r.tbp+'</td><td>'+r.tbg+'</td>'
+'<td><button class="btn btn-link" onclick="showToast(\'明细请在「小屋管理·数据管理」中查看\')">查看明细</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="11">所选范围内没有健康小屋</td></tr>';
var sbp=rows.reduce(function(s,r){return s+r.pbp;},0),sbg=rows.reduce(function(s,r){return s+r.pbg;},0);
var stbp=rows.reduce(function(s,r){return s+r.tbp;},0),stbg=rows.reduce(function(s,r){return s+r.tbg;},0);
document.getElementById('tfoot').innerHTML=rows.length?'<tr class="tfoot"><td colspan="5" style="text-align:right;padding-right:12px;">合计('+rows.length+' 座)</td><td>'+rows.reduce(function(s,r){return s+r.c.devNum;},0)+' 台</td>'
+'<td>'+sbp+'</td><td>'+sbg+'</td><td>'+stbp+'</td><td>'+stbg+'</td><td></td></tr>':'';
+'<td>'+r.pbp+'</td><td>'+r.pbg+'</td><td>'+r.tbp+'</td><td>'+r.tbg+'</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">所选范围内没有健康小屋</td></tr>';
document.getElementById('totalTip').textContent='区间 '+pr.from+' ~ '+pr.to+' · 共 '+rows.length+' 座小屋';
}
function doExport(){showToast('小屋统计已导出(演示)');}
/* [交互] 侧栏分组展开/收起 */
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
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);
}
render();
updateWeekTrigger();updateYearTrigger();setPeriod('week');
</script>
</body>
</html>
+33 -123
View File
@@ -54,7 +54,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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:space-between;padding:12px 16px;border-bottom:1px solid var(--border);gap:10px;flex-wrap:wrap;}
.table-card__title{font-size:var(--font-sm);font-weight:600;}
.table-card__tools{display:flex;align-items:center;gap:8px;}
.table-card__tools{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--border);}
.table-card__content{overflow-x:auto;-webkit-overflow-scrolling:touch;}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:1560px;}
thead th{background:#FAFAFA;padding:10px 14px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
@@ -101,8 +101,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -123,13 +123,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group open" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -142,24 +142,24 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- [交互] 筛选栏(单位→部门级联) -->
<!-- [交互] 筛选栏(所属单位→所属部门级联) -->
<div class="filter-bar">
<div class="filter-item">
<label>单位</label>
<label>所属单位</label>
<select class="filter-select" id="filterUnit" onchange="onFilterUnit()">
<option value="">全部单位</option>
<option>采油一厂</option><option>采油二厂</option><option>第四采油厂</option><option>采气一厂</option>
</select>
</div>
<div class="filter-item">
<label>部门</label>
<label>所属部门</label>
<select class="filter-select" id="filterDepar" onchange="render()" disabled>
<option value="">全部部门</option>
</select>
</div>
<div class="filter-item">
<label>小屋名称</label>
<input class="filter-input" id="filterName" placeholder="关键字" oninput="render()">
<input class="filter-input" id="filterName" placeholder="请输入" oninput="render()">
</div>
<button class="btn btn-primary" onclick="render();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>查询
@@ -168,25 +168,22 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
<!-- [交互] 列表 -->
<!-- [交互] 列表(操作按钮在列表上方左对齐) -->
<div class="table-card">
<div class="table-card__head">
<span class="table-card__title">健康小屋台账</span>
<div class="table-card__tools">
<button class="btn btn-primary" onclick="openForm(-1)">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 1v10M1 6h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>新增小屋
</button>
<button class="btn btn-primary" onclick="openCabinImport()"><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-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>
</div>
<div class="table-card__tools">
<button class="btn btn-primary" onclick="openForm(-1)">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 1v10M1 6h10" stroke="#fff" stroke-width="1.6" 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 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="showToast('查看导入任务(演示)')"><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>
</div>
<div class="table-card__content">
<table>
<thead><tr>
<th style="width:56px;">序号</th><th style="min-width:170px;">小屋名称</th><th style="min-width:110px;">所属单位</th>
<th style="min-width:110px;">所属部门</th><th>负责人</th><th style="min-width:120px;">办公室电话</th>
<th style="min-width:120px;">手机</th><th>建成日期</th><th style="min-width:110px;">配备金额</th>
<th style="width:56px;">序号</th><th style="min-width:170px;">小屋名称</th><th style="min-width:150px;">所属单位部门</th>
<th style="min-width:120px;">办公室电话</th><th>负责人</th><th style="min-width:120px;">负责人电话</th>
<th>建成日期</th><th style="min-width:110px;">配备金额</th>
<th>配备设备数</th><th style="width:190px;">操作</th>
</tr></thead>
<tbody id="tbody"></tbody>
@@ -201,17 +198,19 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="modal--wide">
<div class="modal__header"><span id="formTitle">新增小屋</span><button class="modal__close" onclick="closeForm()">&times;</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="fName" 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="fName" 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="fUnit" style="width:160px;" onchange="onFormUnit()"><option value="">选择单位</option><option>采油一厂</option><option>采油二厂</option><option>第四采油厂</option><option>采气一厂</option></select>
</div></div>
<div class="form-row"><div class="form-row__label">所属部门</div><div class="form-row__control">
<select class="filter-select" id="fDepar" style="width:170px;" disabled><option value="">选择部门</option></select>
</div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>负责人</div><div class="form-row__control"><input class="form-input" id="fMgr" placeholder="姓名" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>办公室电话</div><div class="form-row__control"><input class="form-input" id="fOffice" placeholder="如:0963-88201" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>手机</div><div class="form-row__control"><input class="form-input" id="fMobile" placeholder="11位手机号" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>办公室电话</div><div class="form-row__control"><input class="form-input" id="fOffice" placeholder="请输入" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>负责人</div><div class="form-row__control"><input class="form-input" id="fMgr" placeholder="请输入" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>负责人电话</div><div class="form-row__control"><input class="form-input" id="fMobile" placeholder="请输入" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>建成日期</div><div class="form-row__control"><input class="form-input" type="date" id="fDate" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>配备金额(元)</div><div class="form-row__control"><input class="form-input" type="number" id="fMoney" placeholder="含设备与安装费用" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label">配备设备数</div><div class="form-row__control"><input class="form-input" type="number" id="fDevNum" placeholder="默认 0,建成验收后回填" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label"><i>*</i>配备金额(元)</div><div class="form-row__control"><input class="form-input" type="number" id="fMoney" placeholder="请输入" style="max-width:200px;"></div></div>
<div class="form-row"><div class="form-row__label">配备设备数</div><div class="form-row__control"><input class="form-input" type="number" id="fDevNum" placeholder="请输入" style="max-width:200px;"></div></div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeForm()">取消</button>
@@ -220,63 +219,6 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
</div>
<!-- [交互] 数据管理弹窗 -->
<div class="modal-mask" id="dataMask">
<div class="modal--wide">
<div class="modal__header"><span id="dataTitle">数据管理</span><button class="modal__close" onclick="closeData()">&times;</button></div>
<div class="modal__body">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap;">
<label style="font-size:var(--font-xs);color:var(--text-secondary);">日期区间</label>
<input class="filter-input" type="date" id="dFrom" value="2026-09-01" onchange="renderData()">
<span style="color:var(--text-placeholder);">~</span>
<input class="filter-input" type="date" id="dTo" value="2026-09-07" onchange="renderData()">
<span style="flex:1;"></span>
<input class="filter-input" type="date" id="nDate" value="2026-09-07" style="min-width:120px;">
<input class="filter-input" type="number" id="nBp" placeholder="血压人次" style="min-width:84px;">
<input class="filter-input" type="number" id="nBg" placeholder="血糖人次" style="min-width:84px;">
<button class="btn btn-primary" onclick="addRecord()">+ 录入</button>
</div>
<table class="inner-table">
<thead><tr><th>日期</th><th>血压测量人次</th><th>血糖测量人次</th></tr></thead>
<tbody id="dataBody"></tbody>
<tfoot id="dataFoot"></tfoot>
</table>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeData()">关闭</button></div>
</div>
</div>
<!-- [交互] 批量导入小屋弹窗(异步) -->
<div class="modal-mask" id="cabinImportMask">
<div class="modal">
<div class="modal__header"><span>批量导入数据</span><button class="modal__close" onclick="closeCabinImport()">&times;</button></div>
<div class="modal__body">
<div class="drop-zone" onclick="showToast('演示环境请选择本地文件(仅支持.xlsx)')">点击选择或拖拽文件到此处上传<br>健康小屋导入模板.xlsx</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeCabinImport()">取消</button>
<button class="btn btn-primary" onclick="doCabinImport()">开始导入</button>
</div>
</div>
</div>
<!-- [交互] 导入任务:异步任务列表 -->
<div class="modal-mask" id="importTaskMask">
<div class="modal--wide">
<div class="modal__header"><span>小屋导入任务</span><button class="modal__close" onclick="closeImportTasks()">&times;</button></div>
<div class="modal__body">
<table class="inner-table">
<thead><tr><th>文件名称</th><th>提交时间</th><th>结果</th><th>操作</th></tr></thead>
<tbody>
<tr><td style="font-family:monospace;">cabin_ledger_20260905.xlsx</td><td>2026-09-05 14:22</td><td style="color:#389E0D;">成功:新增 2 座、更新 1 座</td><td><button class="btn btn-link" onclick="showToast('导入明细下载中…')">下载明细</button></td></tr>
<tr><td style="font-family:monospace;">cabin_ledger_20260820.xlsx</td><td>2026-08-20 10:05</td><td style="color:#D48806;">部分成功:新增 1 座、失败 1 座</td><td><button class="btn btn-link" onclick="showToast('导入明细下载中…')">下载明细</button></td></tr>
</tbody>
</table>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeImportTasks()">关闭</button></div>
</div>
</div>
<!-- [交互] 删除确认 -->
<div class="modal-mask" id="delMask">
<div class="modal">
@@ -315,7 +257,7 @@ var cabins=[
});
});
})();
var formIdx=-1,delIdx=-1,dataIdx=-1;
var formIdx=-1,delIdx=-1;
function money(n){return '¥'+n.toLocaleString('en-US');}
function onFilterUnit(){
@@ -331,12 +273,12 @@ function render(){
document.getElementById('tbody').innerHTML=list.map(function(c){
var i=cabins.indexOf(c);
return '<tr><td>'+(i+1)+'</td><td style="text-align:left;padding-left:12px;font-weight:500;">'+c.name+'</td>'
+'<td>'+c.unit+'</td><td>'+c.depar+'</td><td>'+c.mgr+'</td><td>'+c.office+'</td><td>'+c.mobile+'</td>'
+'<td>'+c.unit+(c.depar?' / '+c.depar:'')+'</td><td>'+c.office+'</td><td>'+c.mgr+'</td><td>'+c.mobile+'</td>'
+'<td>'+c.date+'</td><td>'+money(c.money)+'</td><td>'+c.devNum+' 台</td>'
+'<td><button class="btn btn-link" onclick="openData('+i+')">数据管理</button>'
+'<button class="btn btn-link" onclick="openForm('+i+')">编辑</button>'
+'<button class="btn btn-link" style="color:var(--danger);" onclick="openDel('+i+')">删除</button></td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="11">没有符合条件的健康小屋</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="10">没有符合条件的健康小屋</td></tr>';
var m=cabins.reduce(function(s,c){return s+c.money;},0);
var dv=cabins.reduce(function(s,c){return s+c.devNum;},0);
document.getElementById('totalTip').textContent='共 '+cabins.length+' 座 · 配备金额合计 '+money(m)+' · 设备合计 '+dv+' 台';
@@ -370,7 +312,7 @@ function saveForm(){
var name=document.getElementById('fName').value.trim(),u=document.getElementById('fUnit').value,d=document.getElementById('fDepar').value;
var mgr=document.getElementById('fMgr').value.trim(),of=document.getElementById('fOffice').value.trim(),mb=document.getElementById('fMobile').value.trim();
var dt=document.getElementById('fDate').value,mn=document.getElementById('fMoney').value,dv=document.getElementById('fDevNum').value;
if(!name||!u||!d||!mgr||!of||!mb||!dt||mn===''){showToast('请完整填写带 * 的必填项');return;}
if(!name||!u||!mgr||!of||!mb||!dt||mn===''){showToast('请完整填写带 * 的必填项');return;}
if(!/^1[3-9]\d{9}$/.test(mb)&&!/^1\d{2}\*{4}\d{4}$/.test(mb)){showToast('手机号格式不正确');return;}
var obj={name:name,unit:u,depar:d,mgr:mgr,office:of,mobile:mb,date:dt,money:+mn||0,devNum:+dv||0};
if(formIdx<0){obj.records=[];cabins.push(obj);}
@@ -378,41 +320,9 @@ function saveForm(){
closeForm();render();showToast(formIdx<0?'小屋已建档,请在数据管理中录入测量数据':'保存成功');
}
/* ---- 数据管理 ---- */
function openData(i){
dataIdx=i;
document.getElementById('dFrom').value='2026-09-01';document.getElementById('dTo').value='2026-09-07';
document.getElementById('dataTitle').textContent='数据管理 · '+cabins[i].name;
renderData();document.getElementById('dataMask').classList.add('open');
}
function closeData(){document.getElementById('dataMask').classList.remove('open');}
function renderData(){
var f=document.getElementById('dFrom').value,t=document.getElementById('dTo').value;
var list=cabins[dataIdx].records.filter(function(r){return (!f||r.d>=f)&&(!t||r.d<=t);});
document.getElementById('dataBody').innerHTML=list.map(function(r){
return '<tr><td>'+r.d+'</td><td>'+r.bp+'</td><td>'+r.bg+'</td></tr>';
}).join('')||'<tr><td class="td-empty" colspan="3">所选区间暂无测量记录</td></tr>';
var sb=list.reduce(function(s,r){return s+r.bp;},0),sg=list.reduce(function(s,r){return s+r.bg;},0);
document.getElementById('dataFoot').innerHTML='<tr class="tfoot"><td>合计('+list.length+' 天)</td><td>'+sb+'</td><td>'+sg+'</td></tr>';
}
function addRecord(){
var d=document.getElementById('nDate').value,bp=document.getElementById('nBp').value,bg=document.getElementById('nBg').value;
if(!d||bp===''||bg===''){showToast('请填写日期与血压/血糖人次');return;}
var rec={d:d,bp:+bp,bg:+bg};
var c=cabins[dataIdx];
var old=-1;c.records.forEach(function(r,idx){if(r.d===d)old=idx;});
if(old>=0)c.records[old]=rec;else c.records.push(rec);
c.records.sort(function(a,b){return a.d<b.d?-1:1;});
document.getElementById('nBp').value='';document.getElementById('nBg').value='';
renderData();showToast(old>=0?'该日期已有记录,已按最新录入覆盖':'测量记录已录入');
}
/* ---- 数据管理(跳转二级页) ---- */
function openData(i){location.href='hut-cabin-data.html?id='+i;}
/* ---- 批量导入(异步)与导入任务 ---- */
function openCabinImport(){document.getElementById('cabinImportMask').classList.add('open');}
function closeCabinImport(){document.getElementById('cabinImportMask').classList.remove('open');}
function doCabinImport(){closeCabinImport();showToast('导入任务已提交,完成后将推送站内通知');}
function openImportTasks(){document.getElementById('importTaskMask').classList.add('open');}
function closeImportTasks(){document.getElementById('importTaskMask').classList.remove('open');}
/* ---- 删除 ---- */
function openDel(i){
delIdx=i;var c=cabins[i];
+3 -3
View File
@@ -139,8 +139,8 @@ tbody td:last-child:has(.more-wrap:hover){z-index:30;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -161,13 +161,13 @@ tbody td:last-child:has(.more-wrap:hover){z-index:30;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub active">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -95,8 +95,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -117,13 +117,13 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub active">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
+3 -3
View File
@@ -72,8 +72,8 @@ td.ro{color:var(--text-placeholder);font-weight:400;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html" class="active">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -94,13 +94,13 @@ td.ro{color:var(--text-placeholder);font-weight:400;}
<div class="sidebar-group__items">
<a href="hut-banner.html" class="sidebar-item sidebar-item--sub">轮播图配置</a>
<a href="hut-room.html" class="sidebar-item sidebar-item--sub">健康室管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">预约管理</a>
<a href="hut-appointment.html" class="sidebar-item sidebar-item--sub">员工预约记录</a>
<a href="hut-schedule.html" class="sidebar-item sidebar-item--sub">默认排班</a>
<a href="hut-screen.html" class="sidebar-item sidebar-item--sub active">大屏数据配置</a>
</div>
</div>
<div class="sidebar-group" id="grpInput">
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">基础录入
<div class="sidebar-group__label" onclick="toggleGroup('grpInput')">手动采集
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
@@ -83,8 +83,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -104,8 +104,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup open">
@@ -114,8 +114,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item active">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item active">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item">救助物品统计</a>
@@ -38,12 +38,33 @@ 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-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;}
.date-switch span.on{background:var(--primary);color:#fff;}
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
.seg__btn{height:32px;padding:0 14px;border:none;background:#fff;color:var(--text-primary);font-size:var(--font-sm);cursor:pointer;border-right:1px solid var(--border);}
.seg__btn:last-child{border-right:none;}
.seg__btn.active{background:var(--primary);color:#fff;}
.week-picker{position:relative;display:inline-block;}
.week-trigger{height:36px;min-width:220px;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;}
.week-trigger.open,.week-trigger:hover{border-color:var(--primary);}
.week-panel{display:none;position:absolute;top:40px;left:0;z-index:320;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);padding:12px;width:280px;}
.week-panel.open{display:block;}
.week-cal__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.week-cal__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
.week-cal__nav{width:24px;height:24px;border:none;background:none;color:var(--text-secondary);cursor:pointer;font-size:16px;line-height:1;border-radius:var(--radius-sm);}
.week-cal__nav:hover{color:var(--primary);background:var(--primary-light);}
.week-cal__weekdays{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px;}
.week-cal__weekdays span{text-align:center;font-size:var(--font-xs);color:var(--text-placeholder);padding:2px 0;}
.week-cal__body{display:flex;flex-direction:column;gap:2px;}
.week-row{display:grid;grid-template-columns:repeat(7,1fr);cursor:pointer;border-radius:var(--radius-sm);}
.week-row:hover{background:var(--primary-light);}
.week-row.active{background:var(--primary-light);}
.week-cell{height:28px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);}
.week-row.active .week-cell{color:var(--primary);font-weight:600;}
.week-cell--out{color:var(--text-placeholder);}
.year-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:4px;}
.year-cell{height:32px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);cursor:pointer;}
.year-cell:hover{background:var(--primary-light);color:var(--primary);}
.year-cell.active{background:var(--primary);color:#fff;font-weight:600;}
.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-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
@@ -90,8 +111,8 @@ tbody tr:hover td{background:#F8FBFF;}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -111,8 +132,8 @@ tbody tr:hover td{background:#F8FBFF;}
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup open">
@@ -121,8 +142,8 @@ tbody tr:hover td{background:#F8FBFF;}
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item active">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item">救助物品统计</a>
@@ -131,20 +152,62 @@ tbody tr:hover td{background:#F8FBFF;}
<!-- 主内容 -->
<main class="main">
<!-- 时间切换(照搬老系统 DateButtonSelector) -->
<div class="toolbar">
<div class="date-switch" id="dateSwitch">
<span class="on" data-type="week" onclick="switchDateType('week')">本周</span>
<span data-type="month" onclick="switchDateType('month')">本月</span>
<span data-type="year" onclick="switchDateType('year')">本年</span>
<span data-type="picker" onclick="switchDateType('picker')">自定义</span>
<!-- [交互] 统计周期:周/月/年/选时 四选一 -->
<div class="filter-bar">
<div class="filter-item">
<label>统计周期</label>
<div class="seg">
<button class="seg__btn active" id="segWeek" onclick="setPeriod('week')">周统计</button>
<button class="seg__btn" id="segMonth" onclick="setPeriod('month')">月统计</button>
<button class="seg__btn" id="segYear" onclick="setPeriod('year')">年统计</button>
<button class="seg__btn" id="segCustom" onclick="setPeriod('custom')">选时统计</button>
</div>
</div>
</div>
<!-- 筛选栏:单位→医疗点级联 + 救护类型 + 时间 -->
<div class="filter-bar">
<!-- [交互] 筛选条件:统计时间 + 所属单位/所属医疗点/救护类型 + 查询 -->
<div class="filter-bar" style="margin-top:16px;">
<div class="filter-item" id="weekItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="weekTrigger" onclick="toggleWeekPanel()">2026-08-31 至 2026-09-06</div>
<div class="week-panel" id="weekPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(-1)"></button>
<span class="week-cal__title" id="weekCalTitle">2026年9月</span>
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(1)"></button>
</div>
<div class="week-cal__weekdays"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="week-cal__body" id="weekCalBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="monthItem" style="display:none;">
<label>统计时间</label>
<input type="month" class="filter-date" id="monthInput" value="2026-09" onchange="renderTable()" style="min-width:150px;">
</div>
<div class="filter-item" id="yearItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="yearTrigger" onclick="toggleYearPanel()" style="min-width:120px;">2026年</div>
<div class="week-panel" id="yearPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftYearPage(-1)"></button>
<span class="week-cal__title" id="yearPageTitle">2020 - 2029</span>
<button type="button" class="week-cal__nav" onclick="shiftYearPage(1)"></button>
</div>
<div class="year-grid" id="yearPanelBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="rangeItem" style="display:none;">
<label>统计时间</label>
<input type="date" class="filter-date" id="filterFrom" value="2026-08-31" onchange="renderTable()" style="min-width:130px;">
<span style="color:var(--text-placeholder);font-size:var(--font-xs);"></span>
<input type="date" class="filter-date" id="filterTo" value="2026-09-06" onchange="renderTable()" style="min-width:130px;">
</div>
<div class="filter-item">
<label>单位名称</label>
<label>所属单位</label>
<select class="filter-select" id="selUnit" onchange="onUnitChange()" style="min-width:160px;">
<option value="">全部单位</option>
</select>
@@ -163,15 +226,10 @@ tbody tr:hover td{background:#F8FBFF;}
<option>转运型</option>
</select>
</div>
<div class="filter-item" id="dateSingle">
<label>时间</label>
<input type="date" class="filter-date" id="dateSingleInput" value="2026-09-07">
</div>
<div class="filter-item" id="dateRange" style="display:none;">
<label>时间</label>
<input type="date" class="filter-date" id="dateStart" value="2026-09-01">
<span style="color:var(--text-placeholder);"></span>
<input type="date" class="filter-date" id="dateEnd" value="2026-09-07">
<div class="filter-item" style="margin-left:auto;">
<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>查询
</button>
</div>
</div>
@@ -254,8 +312,8 @@ var cars=[
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','ct','qt'];
var dateType='week';
var PERIOD_TEXT={week:'本周',month:'本月',year:'本年',picker:'时段内'};
var period='week';
var PERIOD_TEXT={week:'本周',month:'本月',year:'本年',custom:'时段内'};
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;');}
function scope(){
var u=document.getElementById('selUnit').value;
@@ -268,13 +326,92 @@ function scope(){
return true;
});
}
/* 时间切换:更新分组表头前缀 + 时间控件形态 */
function switchDateType(type){
dateType=type;
document.querySelectorAll('#dateSwitch span').forEach(function(s){s.classList.toggle('on',s.dataset.type===type);});
document.querySelectorAll('.period-text').forEach(function(e){e.textContent=PERIOD_TEXT[type];});
document.getElementById('dateSingle').style.display=(type==='picker')?'none':'';
document.getElementById('dateRange').style.display=(type==='picker')?'':'none';
/* 统计周期:周/月/年/选时,周/年用日历选值,月/选时用可选控件 */
function pad2(n){return ('0'+n).slice(-2);}
function fmtDate(d){return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate());}
function parseDate(s){var p=s.split('-');return new Date(+p[0],+p[1]-1,+p[2]);}
function weekEndOf(start){return fmtDate(new Date(start.getFullYear(),start.getMonth(),start.getDate()+6));}
/* [交互] 周选择:点选一周(周一至周日) */
var weekSelStart='2026-08-31';
var calYear=2026,calMonth=8;
function updateWeekTrigger(){
var d=parseDate(weekSelStart);
document.getElementById('weekTrigger').textContent=fmtDate(d)+' 至 '+weekEndOf(d);
}
function toggleWeekPanel(){
var open=document.getElementById('weekPanel').classList.toggle('open');
document.getElementById('weekTrigger').classList.toggle('open',open);
if(open)renderWeekCalendar();
}
function shiftWeekMonth(delta){
calMonth+=delta;
if(calMonth<0){calMonth=11;calYear--;}
if(calMonth>11){calMonth=0;calYear++;}
renderWeekCalendar();
}
function renderWeekCalendar(){
document.getElementById('weekCalTitle').textContent=calYear+'年'+(calMonth+1)+'月';
var first=new Date(calYear,calMonth,1);
var start=new Date(calYear,calMonth,1-((first.getDay()+6)%7));
var sel=parseDate(weekSelStart);
var html='';
for(var r=0;r<6;r++){
var ws=new Date(start.getFullYear(),start.getMonth(),start.getDate()+r*7);
var isSel=ws.getTime()===sel.getTime();
html+='<div class="week-row'+(isSel?' active':'')+'" onclick="selectWeek(\''+fmtDate(ws)+'\')">';
for(var c=0;c<7;c++){
var d=new Date(ws.getFullYear(),ws.getMonth(),ws.getDate()+c);
html+='<div class="week-cell'+(d.getMonth()!==calMonth?' week-cell--out':'')+'">'+d.getDate()+'</div>';
}
html+='</div>';
}
document.getElementById('weekCalBody').innerHTML=html;
}
function selectWeek(start){
weekSelStart=start;
var d=parseDate(start);
calYear=d.getFullYear();calMonth=d.getMonth();
updateWeekTrigger();
document.getElementById('weekPanel').classList.remove('open');
document.getElementById('weekTrigger').classList.remove('open');
renderTable();
}
/* [交互] 年选择:十年历点选年份 */
var yearSel=2026;
var yearPage=2020;
function updateYearTrigger(){document.getElementById('yearTrigger').textContent=yearSel+'年';}
function toggleYearPanel(){
var open=document.getElementById('yearPanel').classList.toggle('open');
document.getElementById('yearTrigger').classList.toggle('open',open);
if(open)renderYearPanel();
}
function shiftYearPage(delta){yearPage+=delta*10;renderYearPanel();}
function renderYearPanel(){
document.getElementById('yearPageTitle').textContent=yearPage+' - '+(yearPage+9);
var html='';
for(var i=0;i<10;i++){
var y=yearPage+i;
html+='<div class="year-cell'+(y===yearSel?' active':'')+'" onclick="selectYear('+y+')">'+y+'</div>';
}
document.getElementById('yearPanelBody').innerHTML=html;
}
function selectYear(y){
yearSel=y;
updateYearTrigger();
document.getElementById('yearPanel').classList.remove('open');
document.getElementById('yearTrigger').classList.remove('open');
renderTable();
}
/* [交互] 周期切换:周/月/年显示对应可选时间控件(带默认值),选时统计展开起止日期 */
function setPeriod(p){
period=p;
['Week','Month','Year','Custom'].forEach(function(k){document.getElementById('seg'+k).classList.toggle('active',p===k.toLowerCase());});
document.getElementById('weekItem').style.display=(p==='week')?'flex':'none';
document.getElementById('monthItem').style.display=(p==='month')?'flex':'none';
document.getElementById('yearItem').style.display=(p==='year')?'flex':'none';
document.getElementById('rangeItem').style.display=(p==='custom')?'flex':'none';
document.querySelectorAll('.period-text').forEach(function(e){e.textContent=PERIOD_TEXT[p];});
renderTable();
}
function renderTable(){
var list=scope();
@@ -321,7 +458,7 @@ function showToast(msg){
document.getElementById('selUnit').innerHTML='<option value="">全部单位</option>'+us.map(function(u){return '<option>'+esc(u)+'</option>';}).join('');
})();
document.getElementById('exportMask').addEventListener('click',function(e){if(e.target===this)closeExportLog();});
renderTable();
updateWeekTrigger();updateYearTrigger();setPeriod('week');
</script>
</body>
</html>
@@ -92,8 +92,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -113,8 +113,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item active">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item active">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup">
@@ -123,8 +123,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item">救助物品统计</a>
@@ -39,12 +39,33 @@ 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;}
.date-switch span.on{background:var(--primary);color:#fff;}
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
.seg__btn{height:32px;padding:0 14px;border:none;background:#fff;color:var(--text-primary);font-size:var(--font-sm);cursor:pointer;border-right:1px solid var(--border);}
.seg__btn:last-child{border-right:none;}
.seg__btn.active{background:var(--primary);color:#fff;}
.week-picker{position:relative;display:inline-block;}
.week-trigger{height:36px;min-width:220px;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;}
.week-trigger.open,.week-trigger:hover{border-color:var(--primary);}
.week-panel{display:none;position:absolute;top:40px;left:0;z-index:320;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);padding:12px;width:280px;}
.week-panel.open{display:block;}
.week-cal__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.week-cal__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
.week-cal__nav{width:24px;height:24px;border:none;background:none;color:var(--text-secondary);cursor:pointer;font-size:16px;line-height:1;border-radius:var(--radius-sm);}
.week-cal__nav:hover{color:var(--primary);background:var(--primary-light);}
.week-cal__weekdays{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px;}
.week-cal__weekdays span{text-align:center;font-size:var(--font-xs);color:var(--text-placeholder);padding:2px 0;}
.week-cal__body{display:flex;flex-direction:column;gap:2px;}
.week-row{display:grid;grid-template-columns:repeat(7,1fr);cursor:pointer;border-radius:var(--radius-sm);}
.week-row:hover{background:var(--primary-light);}
.week-row.active{background:var(--primary-light);}
.week-cell{height:28px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);}
.week-row.active .week-cell{color:var(--primary);font-weight:600;}
.week-cell--out{color:var(--text-placeholder);}
.year-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:4px;}
.year-cell{height:32px;display:flex;align-items:center;justify-content:center;font-size:var(--font-sm);color:var(--text-primary);border-radius:var(--radius-sm);cursor:pointer;}
.year-cell:hover{background:var(--primary-light);color:var(--primary);}
.year-cell.active{background:var(--primary);color:#fff;font-weight:600;}
.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-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
@@ -94,8 +115,8 @@ th.sortable:hover{color:var(--primary);}
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -115,8 +136,8 @@ th.sortable:hover{color:var(--primary);}
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item active">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup">
@@ -125,8 +146,8 @@ th.sortable:hover{color:var(--primary);}
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item">救助物品统计</a>
@@ -135,37 +156,70 @@ th.sortable:hover{color:var(--primary);}
<!-- 主内容 -->
<main class="main">
<!-- 时间切换(照搬老系统 DateButtonSelector) -->
<div class="toolbar">
<div class="date-switch" id="dateSwitch">
<span class="on" data-type="week" onclick="switchDateType('week')">本周</span>
<span data-type="month" onclick="switchDateType('month')">本月</span>
<span data-type="year" onclick="switchDateType('year')">本年</span>
<span data-type="picker" onclick="switchDateType('picker')">自定义</span>
<!-- [交互] 统计周期:周/月/年/选时 四选一 -->
<div class="filter-bar">
<div class="filter-item">
<label>统计周期</label>
<div class="seg">
<button class="seg__btn active" id="segWeek" onclick="setPeriod('week')">周统计</button>
<button class="seg__btn" id="segMonth" onclick="setPeriod('month')">月统计</button>
<button class="seg__btn" id="segYear" onclick="setPeriod('year')">年统计</button>
<button class="seg__btn" id="segCustom" onclick="setPeriod('custom')">选时统计</button>
</div>
</div>
</div>
<!-- 筛选栏:所属单位 + 时间 -->
<div class="filter-bar">
<!-- [交互] 筛选条件:统计时间 + 所属单位 + 查询 -->
<div class="filter-bar" style="margin-top:16px;">
<div class="filter-item" id="weekItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="weekTrigger" onclick="toggleWeekPanel()">2026-08-31 至 2026-09-06</div>
<div class="week-panel" id="weekPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(-1)"></button>
<span class="week-cal__title" id="weekCalTitle">2026年9月</span>
<button type="button" class="week-cal__nav" onclick="shiftWeekMonth(1)"></button>
</div>
<div class="week-cal__weekdays"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="week-cal__body" id="weekCalBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="monthItem" style="display:none;">
<label>统计时间</label>
<input type="month" class="filter-date" id="monthInput" value="2026-09" onchange="renderTable()" style="min-width:150px;">
</div>
<div class="filter-item" id="yearItem" style="display:none;">
<label>统计时间</label>
<div class="week-picker">
<div class="week-trigger" id="yearTrigger" onclick="toggleYearPanel()" style="min-width:120px;">2026年</div>
<div class="week-panel" id="yearPanel">
<div class="week-cal__head">
<button type="button" class="week-cal__nav" onclick="shiftYearPage(-1)"></button>
<span class="week-cal__title" id="yearPageTitle">2020 - 2029</span>
<button type="button" class="week-cal__nav" onclick="shiftYearPage(1)"></button>
</div>
<div class="year-grid" id="yearPanelBody"></div>
</div>
</div>
</div>
<div class="filter-item" id="rangeItem" style="display:none;">
<label>统计时间</label>
<input type="date" class="filter-date" id="filterFrom" value="2026-08-31" onchange="renderTable()" style="min-width:130px;">
<span style="color:var(--text-placeholder);font-size:var(--font-xs);"></span>
<input type="date" class="filter-date" id="filterTo" value="2026-09-06" onchange="renderTable()" style="min-width:130px;">
</div>
<div class="filter-item">
<label>所属单位</label>
<select class="filter-select" id="selUnit" onchange="renderTable()" style="min-width:160px;">
<select class="filter-select" id="selUnit" onchange="renderTable()" style="min-width:150px;">
<option value="">全部单位</option>
</select>
</div>
<div class="filter-item" id="dateSingle" style="display:none;">
<label>时间</label>
<span class="period-value" id="periodSingle"></span>
</div>
<div class="filter-item" id="dateRange">
<label>时间</label>
<span class="period-value" id="periodRange"></span>
</div>
<div class="filter-item" id="datePicker" style="display:none;">
<label>时间</label>
<input type="date" class="filter-date" id="dateStart">
<span style="color:var(--text-placeholder);"></span>
<input type="date" class="filter-date" id="dateEnd">
<div class="filter-item" style="margin-left:auto;">
<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>查询
</button>
</div>
</div>
@@ -267,44 +321,99 @@ var points=[
visitW:104,visitT:2480,chrNum:90,chrShould:12,chrDone:9,chrTotal:210,
trainSW:1,trainST:5,trainPW:36,trainPT:280,eduSW:1,eduST:8,eduPW:75,eduPT:590,one:40,group:10}
];
var dateType='week';
var PERIOD_TEXT={week:'本周',month:'本月',year:'本年',picker:'当前'};
var period='week';
var PERIOD_TEXT={week:'本周',month:'本月',year:'本年',custom:'当前'};
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;');}
function scope(){
var u=document.getElementById('selUnit').value;
return u?points.filter(function(p){return p.unit===u;}):points;
}
/* 时间切换:更新分组表头前缀 + 时间显示 */
/* 统计周期:周/月/年/选时,周/年用日历选值,月/选时用可选控件 */
function pad2(n){return ('0'+n).slice(-2);}
function fmtDate(d){return d.getFullYear()+'-'+pad2(d.getMonth()+1)+'-'+pad2(d.getDate());}
function weekRange(){
var now=new Date();
var off=now.getDay()===0?-6:1-now.getDay();
var monday=new Date(now.getFullYear(),now.getMonth(),now.getDate()+off);
var sunday=new Date(monday.getFullYear(),monday.getMonth(),monday.getDate()+6);
return {start:fmtDate(monday),end:fmtDate(sunday)};
function parseDate(s){var p=s.split('-');return new Date(+p[0],+p[1]-1,+p[2]);}
function weekEndOf(start){return fmtDate(new Date(start.getFullYear(),start.getMonth(),start.getDate()+6));}
/* [交互] 周选择:点选一周(周一至周日) */
var weekSelStart='2026-08-31';
var calYear=2026,calMonth=8;
function updateWeekTrigger(){
var d=parseDate(weekSelStart);
document.getElementById('weekTrigger').textContent=fmtDate(d)+' 至 '+weekEndOf(d);
}
function switchDateType(type){
dateType=type;
document.querySelectorAll('#dateSwitch span').forEach(function(s){s.classList.toggle('on',s.dataset.type===type);});
document.querySelectorAll('.period-text').forEach(function(e){e.textContent=PERIOD_TEXT[type];});
var now=new Date();
var yr=now.getFullYear(),mon=pad2(now.getMonth()+1);
if(type==='week'){
var r=weekRange();
document.getElementById('periodRange').textContent=r.start+' 至 '+r.end;
}else if(type==='month'){
document.getElementById('periodSingle').textContent=yr+'-'+mon;
}else if(type==='year'){
document.getElementById('periodSingle').textContent=String(yr);
}else{
var r2=weekRange();
document.getElementById('dateStart').value=r2.start;
document.getElementById('dateEnd').value=r2.end;
function toggleWeekPanel(){
var open=document.getElementById('weekPanel').classList.toggle('open');
document.getElementById('weekTrigger').classList.toggle('open',open);
if(open)renderWeekCalendar();
}
function shiftWeekMonth(delta){
calMonth+=delta;
if(calMonth<0){calMonth=11;calYear--;}
if(calMonth>11){calMonth=0;calYear++;}
renderWeekCalendar();
}
function renderWeekCalendar(){
document.getElementById('weekCalTitle').textContent=calYear+'年'+(calMonth+1)+'月';
var first=new Date(calYear,calMonth,1);
var start=new Date(calYear,calMonth,1-((first.getDay()+6)%7));
var sel=parseDate(weekSelStart);
var html='';
for(var r=0;r<6;r++){
var ws=new Date(start.getFullYear(),start.getMonth(),start.getDate()+r*7);
var isSel=ws.getTime()===sel.getTime();
html+='<div class="week-row'+(isSel?' active':'')+'" onclick="selectWeek(\''+fmtDate(ws)+'\')">';
for(var c=0;c<7;c++){
var d=new Date(ws.getFullYear(),ws.getMonth(),ws.getDate()+c);
html+='<div class="week-cell'+(d.getMonth()!==calMonth?' week-cell--out':'')+'">'+d.getDate()+'</div>';
}
html+='</div>';
}
document.getElementById('dateSingle').style.display=(type==='month'||type==='year')?'':'none';
document.getElementById('dateRange').style.display=(type==='week')?'':'none';
document.getElementById('datePicker').style.display=(type==='picker')?'':'none';
document.getElementById('weekCalBody').innerHTML=html;
}
function selectWeek(start){
weekSelStart=start;
var d=parseDate(start);
calYear=d.getFullYear();calMonth=d.getMonth();
updateWeekTrigger();
document.getElementById('weekPanel').classList.remove('open');
document.getElementById('weekTrigger').classList.remove('open');
renderTable();
}
/* [交互] 年选择:十年历点选年份 */
var yearSel=2026;
var yearPage=2020;
function updateYearTrigger(){document.getElementById('yearTrigger').textContent=yearSel+'年';}
function toggleYearPanel(){
var open=document.getElementById('yearPanel').classList.toggle('open');
document.getElementById('yearTrigger').classList.toggle('open',open);
if(open)renderYearPanel();
}
function shiftYearPage(delta){yearPage+=delta*10;renderYearPanel();}
function renderYearPanel(){
document.getElementById('yearPageTitle').textContent=yearPage+' - '+(yearPage+9);
var html='';
for(var i=0;i<10;i++){
var y=yearPage+i;
html+='<div class="year-cell'+(y===yearSel?' active':'')+'" onclick="selectYear('+y+')">'+y+'</div>';
}
document.getElementById('yearPanelBody').innerHTML=html;
}
function selectYear(y){
yearSel=y;
updateYearTrigger();
document.getElementById('yearPanel').classList.remove('open');
document.getElementById('yearTrigger').classList.remove('open');
renderTable();
}
/* [交互] 周期切换:周/月/年显示对应可选时间控件(带默认值),选时统计展开起止日期 */
function setPeriod(p){
period=p;
['Week','Month','Year','Custom'].forEach(function(k){document.getElementById('seg'+k).classList.toggle('active',p===k.toLowerCase());});
document.getElementById('weekItem').style.display=(p==='week')?'flex':'none';
document.getElementById('monthItem').style.display=(p==='month')?'flex':'none';
document.getElementById('yearItem').style.display=(p==='year')?'flex':'none';
document.getElementById('rangeItem').style.display=(p==='custom')?'flex':'none';
document.querySelectorAll('.period-text').forEach(function(e){e.textContent=PERIOD_TEXT[p];});
renderTable();
}
var sortKey=null,sortDir=1;
function sortBy(key){
@@ -350,8 +459,7 @@ function showToast(msg){
document.getElementById('selUnit').innerHTML='<option value="">全部单位</option>'+us.map(function(u){return '<option>'+esc(u)+'</option>';}).join('');
})();
document.getElementById('exportMask').addEventListener('click',function(e){if(e.target===this)closeExportLog();});
switchDateType('week');
renderTable();
updateWeekTrigger();updateYearTrigger();setPeriod('week');
</script>
</body>
</html>
@@ -86,8 +86,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="archive-visit-data.html">档案</a>
<a href="config-medical-point.html">基础配置</a>
<a href="daily-treatment.html">日常业务</a>
<a href="bed-device.html">床头呼叫</a>
<a href="stat-clinic-chart.html" class="active">数据统计</a>
<a href="bed-device.html">床头呼叫</a>
<a href="hut-banner.html">健康小屋</a>
<a href="alert-dirty.html">异常事件</a>
</nav>
@@ -107,8 +107,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
医疗点统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-clinic-table.html" class="sidebar-item">统计报表</a>
<a href="stat-clinic-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<div class="sidebar-subgroup">
@@ -117,8 +117,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
救护车统计
</div>
<div class="sidebar-subgroup__body">
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
<a href="stat-ambulance-table.html" class="sidebar-item">统计报表</a>
<a href="stat-ambulance-chart.html" class="sidebar-item">统计图表</a>
</div>
</div>
<a href="stat-rescue-chart.html" class="sidebar-item active">救助物品统计</a>
@@ -98,6 +98,30 @@
.overlay-modal__close{position:absolute;top:12px;right:12px;width:32px;height:32px;border-radius:50%;background:rgba(0,0,0,0.04);border:none;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#999;z-index:10;transition:all .15s;}
.overlay-modal__close:hover{background:rgba(0,0,0,0.08);color:#333;}
.overlay-modal iframe{border:none;display:block;}
/* 更多操作菜单 */
.more-menu{display:none;position:fixed;z-index:1200;background:#fff;border:1px solid var(--border);border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,0.15);min-width:150px;padding:4px 0;}
.more-menu.open{display:block;}
.more-menu__item{padding:9px 16px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;white-space:nowrap;}
.more-menu__item:hover{background:#F0F8FF;color:var(--primary);}
/* 短信模板弹窗 */
.sms-modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:1300;align-items:center;justify-content:center;}
.sms-modal.open{display:flex;}
.sms-modal__box{background:#fff;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,0.15);width:640px;max-width:92%;max-height:90vh;overflow-y:auto;}
.sms-modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.sms-modal__title{font-size:var(--font-base);font-weight:600;}
.sms-modal__close{background:none;border:none;font-size:20px;cursor:pointer;color:var(--text-secondary);padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;}
.sms-modal__close:hover{color:var(--text-primary);}
.sms-modal__body{padding:24px;}
.sms-expert-info{background:#FAFAFA;border:1px solid var(--border);border-radius:6px;padding:12px 16px;margin-bottom:16px;display:flex;gap:24px;flex-wrap:wrap;}
.sms-expert-info__item{display:flex;gap:6px;font-size:var(--font-sm);}
.sms-expert-info__label{color:var(--text-secondary);}
.sms-expert-info__value{color:var(--text-primary);font-weight:500;}
.sms-template{width:100%;min-height:150px;border:1px solid var(--border);border-radius:6px;padding:12px;font-size:var(--font-sm);line-height:1.8;color:var(--text-primary);background:#fff;resize:vertical;box-sizing:border-box;}
.sms-field{margin-top:16px;}
.sms-field__label{display:block;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:6px;}
.sms-field__input{width:100%;height:36px;border:1px solid var(--border);border-radius:4px;padding:0 12px;font-size:var(--font-sm);outline:none;box-sizing:border-box;}
.sms-field__input:focus{border-color:var(--primary);}
.sms-modal__footer{display:flex;justify-content:flex-end;gap:12px;margin-top:24px;}
</style>
</head>
<body>
@@ -134,9 +158,36 @@
<div class="filter-bar" style="flex-direction:column;align-items:flex-start;">
<div class="filter-bar__rows">
<div class="filter-bar__row">
<div class="filter-item">
<label>年份</label>
<select class="filter-select">
<option value="2026" selected>2026年</option>
<option value="2025">2025年</option>
<option value="2024">2024年</option>
<option value="">全部年份</option>
</select>
</div>
<div class="filter-item"><label>咨询单号</label><input class="filter-input" type="text" placeholder="请输入咨询单号"></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="text" placeholder="请输入员工编号" style="min-width:140px;"></div>
<div class="filter-item">
<label>咨询类型</label>
<select class="filter-select" id="expertTypeSelect" onchange="updateExpertStatus()">
<option value="">全部类型</option><option value="text">图文咨询</option><option value="video">视频咨询</option>
</select>
</div>
<div class="filter-item">
<label>咨询状态</label>
<select class="filter-select" id="expertStatusSelect"><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>已发送</option><option>未发送</option></select>
</div>
<div class="filter-item">
<label style="white-space:nowrap;">所属单位部门</label>
<div class="org-select-wrap" id="orgWrap2">
@@ -158,16 +209,6 @@
</div>
</div>
</div>
<div class="filter-item">
<label>咨询类型</label>
<select class="filter-select" id="expertTypeSelect" onchange="updateExpertStatus()">
<option value="">全部类型</option><option value="text">图文咨询</option><option value="video">视频咨询</option>
</select>
</div>
<div class="filter-item">
<label>咨询状态</label>
<select class="filter-select" id="expertStatusSelect"><option>全部状态</option></select>
</div>
</div>
<div class="filter-bar__row hidden" id="expertFilterExpanded">
<div class="filter-item"><label>专家姓名</label><input class="filter-input" type="text" placeholder="请输入专家姓名" style="min-width:140px;"></div>
@@ -183,6 +224,12 @@
<span style="color:var(--text-secondary);font-size:var(--font-sm);"></span>
<input class="filter-input" type="date" placeholder="请选择" style="min-width:140px;">
</div>
<div class="filter-item">
<label>首次回复时间</label>
<input class="filter-input" type="date" placeholder="请选择" style="min-width:140px;">
<span style="color:var(--text-secondary);font-size:var(--font-sm);"></span>
<input class="filter-input" type="date" placeholder="请选择" style="min-width:140px;">
</div>
</div>
<div class="filter-bar__row">
<button class="filter-bar__expand-btn" id="expertExpandBtn" onclick="toggleExpertFilter()">
@@ -208,7 +255,7 @@
<thead>
<tr>
<th class="col-fixed col-f1">序号</th><th class="col-fixed col-f2">咨询单号</th><th class="col-fixed col-f3">姓名</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>专家科室</th><th>专家类型</th>
<th>发起咨询时间</th><th>首次回复时间</th><th>咨询完成时间</th><th>咨询时长</th><th>评分</th><th class="col-fixed col-fend">操作</th>
</tr>
</thead>
@@ -217,95 +264,115 @@
<td class="col-fixed col-f1">1</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409001</td><td class="col-fixed col-f3">张伟</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP001</td><td>采油一厂</td><td>心血管科</td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>李医生</td><td>主任医师</td><td>CQ医院</td><td>心血管内科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-09 09:12</td><td>2026-04-09 09:25</td><td>2026-04-09 09:35</td><td>23分钟</td>
<td><span class="stars">★★★★★</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">2</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409002</td><td class="col-fixed col-f3">李明</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP002</td><td>采油二厂</td><td>内分泌科</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>王医生</td><td>副主任医师</td><td>CQ医院</td><td>内分泌科</td>
<td><span class="tag tag-green">免费专家</span></td>
<td>2026-04-09 10:30</td><td>2026-04-09 10:45</td><td>2026-04-09 11:15</td><td>45分钟</td>
<td><span class="stars">★★★★☆</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('李明','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 10:30','已完成')">查看详情</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('李明','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 10:30','已完成')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">3</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409003</td><td class="col-fixed col-f3">王芳</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP003</td><td>勘探开发研究院</td><td>医学研究部</td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-blue">进行中</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>张医生</td><td>主治医师</td><td>CQ医院</td><td>神经内科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-09 11:05</td><td>2026-04-09 11:15</td><td></td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">4</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260408004</td><td class="col-fixed col-f3">刘洋</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP004</td><td>采油三厂</td><td>生产部</td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>陈医生</td><td>副主任医师</td><td>CQ医院</td><td>骨科</td>
<td><span class="tag tag-green">其他专家</span></td>
<td>2026-04-08 14:22</td><td>2026-04-08 14:35</td><td>2026-04-08 14:40</td><td>18分钟</td>
<td><span class="stars">★★★☆☆</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">5</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260408005</td><td class="col-fixed col-f3">赵静</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP005</td><td>机关事务服务中心</td><td>行政部</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-red">已拒绝</span></td>
<td><span class="tag tag-orange">未回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>孙医生</td><td>主任医师</td><td>CQ医院</td><td>消化内科</td>
<td><span class="tag tag-green">免费专家</span></td>
<td>2026-04-08 16:40</td><td></td><td>2026-04-08 16:42</td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('赵静','孙医生','主任医师','CQ医院','消化内科','2026-04-08 16:40','已拒绝')">查看详情</span></td>
<td>2026-04-08 16:40</td><td></td><td>2026-04-08 16:42</td><td>32分钟</td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('赵静','孙医生','主任医师','CQ医院','消化内科','2026-04-08 16:40','已拒绝')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">6</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260407006</td><td class="col-fixed col-f3">孙丽</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP010</td><td>采油二厂</td><td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-gray">已取消</span></td>
<td><span class="tag tag-orange">未回复</span></td><td><span class="tag tag-gray">未发送</span></td>
<td>陈医生</td><td>副主任医师</td><td>CQ医院</td><td>骨科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-07 08:20</td><td></td><td>2026-04-07 08:45</td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('孙丽','陈医生','副主任医师','CQ医院','骨科','2026-04-07 08:20','已取消')">查看详情</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('孙丽','陈医生','副主任医师','CQ医院','骨科','2026-04-07 08:20','已取消')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">7</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260407007</td><td class="col-fixed col-f3">陈磊</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP006</td><td>采油一厂</td><td>技术部</td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-green">完成</span></td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-gray">超时</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>李医生</td><td>主任医师</td><td>CQ医院</td><td>心血管内科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-07 09:55</td><td>2026-04-07 10:05</td><td>2026-04-07 10:20</td><td>15分钟</td>
<td><span class="stars">★★★★★</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">8</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409008</td><td class="col-fixed col-f3">周华</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP007</td><td>采油一厂</td><td>安全部</td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-orange">已超时</span></td>
<td><span class="tag tag-blue">图文咨询</span></td><td><span class="tag tag-orange">待回复</span></td>
<td><span class="tag tag-orange">未回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>刘医生</td><td>主治医师</td><td>CQ医院</td><td>呼吸科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-09 14:30</td><td></td><td></td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openOverlay('chat-detail-dialog.html',720,680)">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">9</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409009</td><td class="col-fixed col-f3">吴娟</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP008</td><td>采油二厂</td><td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-blue">待确认</span></td>
<td><span class="tag tag-orange">未回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>王医生</td><td>副主任医师</td><td>CQ医院</td><td>内分泌科</td>
<td><span class="tag tag-green">免费专家</span></td>
<td>2026-04-09 15:10</td><td></td><td></td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('吴娟','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 15:10','待确认')">查看详情</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('吴娟','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 15:10','待确认')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">10</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409010</td><td class="col-fixed col-f3">何军</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP009</td><td>勘探开发研究院</td><td>数据分析部</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-green">待开始</span></td>
<td><span class="tag tag-orange">未回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>张医生</td><td>主治医师</td><td>CQ医院</td><td>神经内科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-09 16:00</td><td>2026-04-09 16:05</td><td></td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('何军','张医生','主治医师','CQ医院','神经内科','2026-04-09 16:00','待开始')">查看详情</span></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('何军','张医生','主治医师','CQ医院','神经内科','2026-04-09 16:00','待开始')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
<tr>
<td class="col-fixed col-f1">11</td><td class="col-fixed col-f2" style="font-family:monospace;color:var(--text-secondary);">ZJ20260409011</td><td class="col-fixed col-f3">马丽</td>
<td style="font-family:monospace;color:var(--text-secondary);">EMP011</td><td>采油三厂</td><td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td><td><span class="tag tag-blue">进行中</span></td>
<td><span class="tag tag-green">已回复</span></td><td><span class="tag tag-green">已发送</span></td>
<td>李医生</td><td>主任医师</td><td>CQ医院</td><td>心血管内科</td>
<td><span class="tag tag-green">收费专家</span></td>
<td>2026-04-09 16:30</td><td>2026-04-09 16:35</td><td></td><td></td><td></td>
<td class="col-fixed col-fend"><span class="link" onclick="openDetail('马丽','李医生','主任医师','CQ医院','心血管内科','2026-04-09 16:30','进行中')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span><span class="link" style="margin-left:12px;" onclick="showMoreMenu(event, this)">更多</span></td>
</tr>
</tbody>
</table>
@@ -470,6 +537,64 @@
<iframe id="overlayFrame" src=""></iframe>
</div>
</div>
<!-- 更多操作菜单 -->
<div class="more-menu" id="moreMenu">
<div class="more-menu__item" onclick="openSmsModal()">发送短信</div>
<div class="more-menu__item" onclick="openSmsRecord()">发送短信记录</div>
</div>
<!-- 发送短信弹窗 -->
<div class="sms-modal" id="smsModal" onclick="if(event.target===this)closeSmsModal()">
<div class="sms-modal__box">
<div class="sms-modal__header">
<div class="sms-modal__title">发送短信</div>
<button class="sms-modal__close" onclick="closeSmsModal()"></button>
</div>
<div class="sms-modal__body">
<div class="sms-expert-info" id="smsExpertInfo"></div>
<textarea class="sms-template" id="smsTemplate" readonly></textarea>
<div class="sms-field">
<label class="sms-field__label">专家称呼</label>
<input class="sms-field__input" id="smsFieldTitle" oninput="updateSmsPreview()">
</div>
<div class="sms-field">
<label class="sms-field__label">账号</label>
<input class="sms-field__input" id="smsFieldAccount" oninput="updateSmsPreview()">
</div>
<div class="sms-field">
<label class="sms-field__label">密码</label>
<input class="sms-field__input" id="smsFieldPassword" oninput="updateSmsPreview()">
</div>
<div class="sms-field">
<label class="sms-field__label">密码提示</label>
<input class="sms-field__input" id="smsFieldPasswordHint" oninput="updateSmsPreview()">
</div>
<div class="sms-modal__footer">
<button class="btn btn-default" onclick="closeSmsModal()">取消</button>
<button class="btn btn-primary" onclick="sendSms()">发送</button>
</div>
</div>
</div>
</div>
<!-- 发送短信记录弹窗 -->
<div class="sms-modal" id="smsRecordModal" onclick="if(event.target===this)closeSmsRecord()">
<div class="sms-modal__box" style="width:760px;">
<div class="sms-modal__header">
<div class="sms-modal__title">发送短信记录</div>
<button class="sms-modal__close" onclick="closeSmsRecord()"></button>
</div>
<div class="sms-modal__body">
<div class="sms-expert-info" id="smsRecordExpertInfo"></div>
<table>
<thead>
<tr>
<th>序号</th><th>发送时间</th><th>接收人</th><th>接收号码</th><th>短信内容</th><th>状态</th>
</tr>
</thead>
<tbody id="smsRecordTbody"></tbody>
</table>
</div>
</div>
</div>
<script>
/* Tab 切换 */
function switchTab(tab) {
@@ -491,8 +616,8 @@ function toggleExpertFilter() {
/* 专家咨询状态联动 */
var expertStatusMap = {
'text': ['已超时','进行中','已完成'],
'video': ['待确认','待开始','进行中','已完成','已取消','已拒绝']
'text': ['待回复','进行中','已完成','已超时'],
'video': ['待确认','待开始','进行中','已取消','已完成','已拒绝']
};
function updateExpertStatus() {
var type = document.getElementById('expertTypeSelect').value;
@@ -565,6 +690,7 @@ function openDetail(name, expert, title, hospital, dept, time, status) {
var fileMap = {
'待确认': '../video-detail-pending-confirm.html',
'待开始': '../video-detail-pending-start.html',
'进行中': '../video-detail-in-progress.html',
'已完成': '../video-detail-completed.html',
'已拒绝': '../video-detail-rejected.html',
'已取消': '../video-detail-cancelled.html'
@@ -590,6 +716,132 @@ function closeOverlay() {
document.getElementById('overlayFrame').src = '';
document.body.style.overflow = '';
}
function syncRecord() {
alert('已同步');
}
/* ===== 更多操作菜单 & 发送短信 ===== */
var surnamePinyin = {
'李':'li', '王':'wang', '张':'zhang', '陈':'chen', '孙':'sun', '刘':'liu'
};
var currentExpert = { name:'', title:'', hospital:'', dept:'' };
function showMoreMenu(event, btn) {
// 咨询专家 Tab 列序:0序号 1单号 2姓名 3员工编号 4单位 5部门 6类型 7状态 8回复 9发送 10专家 11职称 12医院 13科室
var tds = btn.closest('tr').querySelectorAll('td');
currentExpert.name = tds[10].textContent.trim();
currentExpert.title = tds[11].textContent.trim();
currentExpert.hospital = tds[12].textContent.trim();
currentExpert.dept = tds[13].textContent.trim();
var menu = document.getElementById('moreMenu');
var rect = btn.getBoundingClientRect();
menu.style.top = (rect.bottom + 4) + 'px';
menu.style.left = Math.min(rect.left, window.innerWidth - 160) + 'px';
menu.classList.add('open');
event.stopPropagation();
}
function closeMoreMenu() {
document.getElementById('moreMenu').classList.remove('open');
}
function openSmsModal() {
closeMoreMenu();
var surname = currentExpert.name.charAt(0);
var pinyin = surnamePinyin[surname] || 'zhang';
var account = pinyin + 'yanchun';
var password = pinyin.charAt(0).toUpperCase() + 'yc@2024135790';
var passwordHint = pinyin.charAt(0).toUpperCase() + '大写';
document.getElementById('smsExpertInfo').innerHTML =
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">姓名</span><span class="sms-expert-info__value">' + currentExpert.name + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">职称</span><span class="sms-expert-info__value">' + currentExpert.title + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">医院</span><span class="sms-expert-info__value">' + currentExpert.hospital + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">科室</span><span class="sms-expert-info__value">' + currentExpert.dept + '</span></div>';
document.getElementById('smsFieldTitle').value = surname + '教授';
document.getElementById('smsFieldAccount').value = account;
document.getElementById('smsFieldPassword').value = password;
document.getElementById('smsFieldPasswordHint').value = passwordHint;
updateSmsPreview();
document.getElementById('smsModal').classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeSmsModal() {
document.getElementById('smsModal').classList.remove('open');
document.body.style.overflow = '';
}
function updateSmsPreview() {
var title = document.getElementById('smsFieldTitle').value;
var account = document.getElementById('smsFieldAccount').value;
var password = document.getElementById('smsFieldPassword').value;
var passwordHint = document.getElementById('smsFieldPasswordHint').value;
document.getElementById('smsTemplate').value =
title + '您好!我是长庆专家库咨询小助手(电话是86565197)。我们在后台看到有员工咨询您尚未进行回复,请您根据您的时间尽快予以回复,谢谢!\n(账号:' + account + '\n密码:' + password + ' 密码提示:' + passwordHint + '';
}
function sendSms() {
alert('短信已发送');
closeSmsModal();
}
function openSmsRecord() {
closeMoreMenu();
var surname = currentExpert.name.charAt(0);
var pinyin = surnamePinyin[surname] || 'zhang';
var title = surname + '教授';
var account = pinyin + 'yanchun';
var password = pinyin.charAt(0).toUpperCase() + 'yc@2024135790';
var passwordHint = pinyin.charAt(0).toUpperCase() + '大写';
var phone = '13812345678';
var content = title + '您好!我是长庆专家库咨询小助手(电话是86565197)。我们在后台看到有员工咨询您尚未进行回复,请您根据您的时间尽快予以回复,谢谢!(账号:' + account + ' 密码:' + password + ' 密码提示:' + passwordHint + '';
document.getElementById('smsRecordExpertInfo').innerHTML =
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">姓名</span><span class="sms-expert-info__value">' + currentExpert.name + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">职称</span><span class="sms-expert-info__value">' + currentExpert.title + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">医院</span><span class="sms-expert-info__value">' + currentExpert.hospital + '</span></div>' +
'<div class="sms-expert-info__item"><span class="sms-expert-info__label">科室</span><span class="sms-expert-info__value">' + currentExpert.dept + '</span></div>';
var records = [
{ time: '2026-04-09 16:42', status: 'success' },
{ time: '2026-04-08 11:20', status: 'success' },
{ time: '2026-04-07 09:05', status: 'fail' }
];
var html = '';
records.forEach(function(r, i) {
var statusTag = r.status === 'success'
? '<span class="tag tag-green">发送成功</span>'
: '<span class="tag tag-red">发送失败</span>';
html += '<tr><td>' + (i + 1) + '</td>' +
'<td>' + r.time + '</td>' +
'<td>' + currentExpert.name + '</td>' +
'<td>' + phone + '</td>' +
'<td style="white-space:normal;word-break:break-all;max-width:340px;">' + content + '</td>' +
'<td>' + statusTag + '</td></tr>';
});
document.getElementById('smsRecordTbody').innerHTML = html;
document.getElementById('smsRecordModal').classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeSmsRecord() {
document.getElementById('smsRecordModal').classList.remove('open');
document.body.style.overflow = '';
}
// 点击外部关闭更多菜单
document.addEventListener('click', function(e) {
var menu = document.getElementById('moreMenu');
if (menu && menu.classList.contains('open') && !menu.contains(e.target)) {
menu.classList.remove('open');
}
});
window.addEventListener('message', function(e) {
if (e.data === 'close-dialog') closeOverlay();
});
+55 -11
View File
@@ -210,6 +210,22 @@
<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>已发送</option>
<option>未发送</option>
</select>
</div>
<div class="filter-item">
<label style="white-space:nowrap;">所属单位部门</label>
<div class="org-select-wrap" id="orgWrap">
@@ -322,6 +338,8 @@
<th>所属部门</th>
<th>咨询类型</th>
<th>咨询状态</th>
<th>专家回复状态</th>
<th>用户发送状态</th>
<th>咨询专家</th>
<th>专家职称</th>
<th>专家医院</th>
@@ -345,6 +363,8 @@
<td>心血管科</td>
<td><span class="tag tag-blue">图文咨询</span></td>
<td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>李医生</td>
<td>主任医师</td>
<td>CQ医院</td>
@@ -355,7 +375,7 @@
<td>2026-04-09 09:35</td>
<td>23分钟</td>
<td><span class="stars">★★★★★</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('张伟','李医生','CQ医院','心血管内科','2026-04-09 09:12')">查看对话</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('张伟','李医生','CQ医院','心血管内科','2026-04-09 09:12')">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">2</td>
@@ -366,6 +386,8 @@
<td>内分泌科</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>王医生</td>
<td>副主任医师</td>
<td>CQ医院</td>
@@ -376,7 +398,7 @@
<td>2026-04-09 11:15</td>
<td>45分钟</td>
<td><span class="stars">★★★★☆</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('李明','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 10:30','已完成')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('李明','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 10:30','已完成')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">3</td>
@@ -387,6 +409,8 @@
<td>医学研究部</td>
<td><span class="tag tag-blue">图文咨询</span></td>
<td><span class="tag tag-blue">进行中</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>张医生</td>
<td>主治医师</td>
<td>CQ医院</td>
@@ -397,7 +421,7 @@
<td></td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('王芳','张医生','CQ医院','神经内科','2026-04-09 11:05')">查看对话</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('王芳','张医生','CQ医院','神经内科','2026-04-09 11:05')">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">4</td>
@@ -408,6 +432,8 @@
<td>生产部</td>
<td><span class="tag tag-blue">图文咨询</span></td>
<td><span class="tag tag-green">已完成</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>陈医生</td>
<td>副主任医师</td>
<td>CQ医院</td>
@@ -418,7 +444,7 @@
<td>2026-04-08 14:40</td>
<td>18分钟</td>
<td><span class="stars">★★★☆☆</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('刘洋','陈医生','CQ医院','骨科','2026-04-08 14:22')">查看对话</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('刘洋','陈医生','CQ医院','骨科','2026-04-08 14:22')">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">5</td>
@@ -429,6 +455,8 @@
<td>行政部</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-red">已拒绝</span></td>
<td><span class="tag tag-orange">未回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>孙医生</td>
<td>主任医师</td>
<td>CQ医院</td>
@@ -439,7 +467,7 @@
<td>2026-04-08 16:42</td>
<td>32分钟</td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('赵静','孙医生','主任医师','CQ医院','消化内科','2026-04-08 16:40','已拒绝')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('赵静','孙医生','主任医师','CQ医院','消化内科','2026-04-08 16:40','已拒绝')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">6</td>
@@ -450,6 +478,8 @@
<td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-gray">已取消</span></td>
<td><span class="tag tag-orange">未回复</span></td>
<td><span class="tag tag-gray">未发送</span></td>
<td>陈医生</td>
<td>副主任医师</td>
<td>CQ医院</td>
@@ -460,7 +490,7 @@
<td>2026-04-07 08:45</td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('孙丽','陈医生','副主任医师','CQ医院','骨科','2026-04-07 08:20','已取消')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('孙丽','陈医生','副主任医师','CQ医院','骨科','2026-04-07 08:20','已取消')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">7</td>
@@ -471,6 +501,8 @@
<td>技术部</td>
<td><span class="tag tag-blue">图文咨询</span></td>
<td><span class="tag tag-gray">已超时</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>李医生</td>
<td>主任医师</td>
<td>CQ医院</td>
@@ -481,7 +513,7 @@
<td>2026-04-07 10:20</td>
<td>15分钟</td>
<td><span class="stars">★★★★★</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('陈磊','李医生','CQ医院','心血管内科','2026-04-07 09:55')">查看对话</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('陈磊','李医生','CQ医院','心血管内科','2026-04-07 09:55')">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">8</td>
@@ -492,6 +524,8 @@
<td>安全部</td>
<td><span class="tag tag-blue">图文咨询</span></td>
<td><span class="tag tag-orange">待回复</span></td>
<td><span class="tag tag-orange">未回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>刘医生</td>
<td>主治医师</td>
<td>CQ医院</td>
@@ -502,7 +536,7 @@
<td></td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('周华','刘医生','CQ医院','呼吸科','2026-04-09 14:30')">查看对话</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openChat('周华','刘医生','CQ医院','呼吸科','2026-04-09 14:30')">查看对话</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">9</td>
@@ -513,6 +547,8 @@
<td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-blue">待确认</span></td>
<td><span class="tag tag-orange">未回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>王医生</td>
<td>副主任医师</td>
<td>CQ医院</td>
@@ -523,7 +559,7 @@
<td></td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('吴娟','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 15:10','待确认')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('吴娟','王医生','副主任医师','CQ医院','内分泌科','2026-04-09 15:10','待确认')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">10</td>
@@ -534,6 +570,8 @@
<td>数据分析部</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-green">待开始</span></td>
<td><span class="tag tag-orange">未回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>张医生</td>
<td>主治医师</td>
<td>CQ医院</td>
@@ -544,7 +582,7 @@
<td></td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('何军','张医生','主治医师','CQ医院','神经内科','2026-04-09 16:00','待开始')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('何军','张医生','主治医师','CQ医院','神经内科','2026-04-09 16:00','待开始')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
<tr>
<td class="col-fixed col-fixed-1">11</td>
@@ -555,6 +593,8 @@
<td>技术部</td>
<td><span class="tag tag-orange">视频咨询</span></td>
<td><span class="tag tag-blue">进行中</span></td>
<td><span class="tag tag-green">已回复</span></td>
<td><span class="tag tag-green">已发送</span></td>
<td>李医生</td>
<td>主任医师</td>
<td>CQ医院</td>
@@ -565,7 +605,7 @@
<td></td>
<td></td>
<td></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('马丽','李医生','主任医师','CQ医院','心血管内科','2026-04-09 16:30','进行中')">查看详情</span></td>
<td class="col-fixed col-fixed-end"><span class="link" onclick="openDetail('马丽','李医生','主任医师','CQ医院','心血管内科','2026-04-09 16:30','进行中')">查看详情</span><span class="link" style="margin-left:12px;" onclick="syncRecord()">同步</span></td>
</tr>
</tbody>
</table>
@@ -777,6 +817,10 @@ function closeChat() {
document.getElementById('chatModal').classList.remove('open');
}
function syncRecord() {
alert('已同步');
}
// 点击外部关闭下拉框
document.addEventListener('click', function(e) {
const orgWrap = document.getElementById('orgWrap');
+9 -7
View File
@@ -102,6 +102,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="filter-bar">
<div class="filter-item"><label>标题</label><input class="filter-input" id="filterTitle" placeholder="请输入"></div>
<div class="filter-item"><label>接收人</label><input class="filter-input" id="filterReceiver" placeholder="请输入"></div>
<div class="filter-item"><label>接收号码</label><input class="filter-input" id="filterPhone" placeholder="请输入"></div>
<div class="filter-item"><label>发送状态</label><select class="filter-select" id="filterStatus"><option value="">请选择</option><option>发送成功</option><option>发送失败</option></select></div>
<div class="filter-item"><label>发送方式</label><select class="filter-select" id="filterType"><option value="">请选择</option><option>短信</option><option>站内信</option></select></div>
<button class="btn btn-primary" onclick="searchQuery()"><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>查询</button>
@@ -110,7 +111,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="table-card">
<div class="table-card__content">
<table>
<thead><tr><th style="width:60px">序号</th><th style="width:150px">标题</th><th style="width:280px">发送内容</th><th style="width:120px">接收人</th><th style="width:100px">发送状态</th><th style="width:180px">发送时间</th><th style="width:90px">发送方式</th><th style="width:80px">操作</th></tr></thead>
<thead><tr><th style="width:60px">序号</th><th style="width:150px">标题</th><th style="width:280px">发送内容</th><th style="width:120px">接收人</th><th style="width:130px">接收号码</th><th style="width:100px">发送状态</th><th style="width:180px">发送时间</th><th style="width:90px">发送方式</th><th style="width:80px">操作</th></tr></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
@@ -129,26 +130,27 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<script>
var history=[
{id:'1',title:'体检提醒',content:'【健康CQ】您本年度的健康体检报告已出具,请及时登录平台查看。',receiver:'张伟',status:'发送成功',time:'2026-09-01 09:00',type:'短信'},
{id:'2',title:'体检提醒',content:'【健康CQ】您本年度的健康体检报告已出具,请及时登录平台查看。',receiver:'李艳',status:'发送成功',time:'2026-09-01 09:00',type:'短信'},
{id:'3',title:'健康讲座通知',content:'【健康CQ】本周五下午举办健康讲座,欢迎参加。',receiver:'王强',status:'发送成功',time:'2026-09-05 10:00',type:'站内信'},
{id:'4',title:'随访提醒',content:'【健康CQ】您有一项健康随访待完成。',receiver:'赵敏',status:'发送失败',time:'2026-09-11 08:30',type:'短信'}
{id:'1',title:'体检提醒',content:'【健康CQ】您本年度的健康体检报告已出具,请及时登录平台查看。',receiver:'张伟',phone:'138****0001',status:'发送成功',time:'2026-09-01 09:00',type:'短信'},
{id:'2',title:'体检提醒',content:'【健康CQ】您本年度的健康体检报告已出具,请及时登录平台查看。',receiver:'李艳',phone:'138****0002',status:'发送成功',time:'2026-09-01 09:00',type:'短信'},
{id:'3',title:'健康讲座通知',content:'【健康CQ】本周五下午举办健康讲座,欢迎参加。',receiver:'王强',phone:'138****0003',status:'发送成功',time:'2026-09-05 10:00',type:'站内信'},
{id:'4',title:'随访提醒',content:'【健康CQ】您有一项健康随访待完成。',receiver:'赵敏',phone:'138****0004',status:'发送失败',time:'2026-09-11 08:30',type:'短信'}
];
function renderTable(){
var list=history;
document.getElementById('totalCount').textContent=list.length;
document.getElementById('tableBody').innerHTML=list.map(function(h,i){
return '<tr><td>'+(i+1)+'</td><td style="text-align:left;">'+h.title+'</td><td style="text-align:left;">'+h.content+'</td><td>'+h.receiver+'</td><td><span class="badge '+(h.status==='发送成功'?'badge--success':'badge--danger')+'">'+h.status+'</span></td><td>'+h.time+'</td><td>'+h.type+'</td><td><button class="btn btn-link" onclick="openDetail('+h.id+')">详情</button></td></tr>';
return '<tr><td>'+(i+1)+'</td><td style="text-align:left;">'+h.title+'</td><td style="text-align:left;">'+h.content+'</td><td>'+h.receiver+'</td><td>'+h.phone+'</td><td><span class="badge '+(h.status==='发送成功'?'badge--success':'badge--danger')+'">'+h.status+'</span></td><td>'+h.time+'</td><td>'+h.type+'</td><td><button class="btn btn-link" onclick="openDetail('+h.id+')">详情</button></td></tr>';
}).join('');
}
function searchQuery(){renderTable();showToast('查询完成');}
function resetQuery(){document.getElementById('filterTitle').value='';document.getElementById('filterReceiver').value='';document.getElementById('filterStatus').value='';document.getElementById('filterType').value='';renderTable();}
function resetQuery(){document.getElementById('filterTitle').value='';document.getElementById('filterReceiver').value='';document.getElementById('filterPhone').value='';document.getElementById('filterStatus').value='';document.getElementById('filterType').value='';renderTable();}
function openDetail(id){
var h=history.find(function(x){return x.id===id;});
document.getElementById('detailBody').innerHTML=
'<div class="detail-row"><label>标题</label><div class="val">'+h.title+'</div></div>'+
'<div class="detail-row"><label>发送内容</label><div class="val">'+h.content+'</div></div>'+
'<div class="detail-row"><label>接收人</label><div class="val">'+h.receiver+'</div></div>'+
'<div class="detail-row"><label>接收号码</label><div class="val">'+h.phone+'</div></div>'+
'<div class="detail-row"><label>发送状态</label><div class="val">'+h.status+'</div></div>'+
'<div class="detail-row"><label>发送时间</label><div class="val">'+h.time+'</div></div>'+
'<div class="detail-row"><label>发送方式</label><div class="val">'+h.type+'</div></div>';