374 lines
12 KiB
HTML
374 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>TAT监控大屏</title>
|
||
<link rel="stylesheet" href="./css/index.css">
|
||
</head>
|
||
|
||
<body>
|
||
<div class="screen-container">
|
||
<div class="screen-header">
|
||
<div class="screen-title">TAT超时提醒大屏</div>
|
||
<div class="screen-time" id="timeDom"></div>
|
||
</div>
|
||
<div class="grid-wrapper">
|
||
<!-- 表格1 已采集待收标本超时 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>已采集待收标本超时</span>
|
||
<span class="red-text" id="count1">共 0个标本</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<table id="table1">
|
||
<thead>
|
||
<tr>
|
||
<th>科室</th>
|
||
<th>姓名/条码</th>
|
||
<th>标本/项目</th>
|
||
<th>采样时间</th>
|
||
<th width="120">等待时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<!-- 表格2 已核收未上机超时 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>已核收未上机超时</span>
|
||
<span class="red-text" id="count2">共 0个标本</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<table id="table2">
|
||
<thead>
|
||
<tr>
|
||
<th>科室</th>
|
||
<th>姓名/条码</th>
|
||
<th>标本/项目</th>
|
||
<th>签收时间</th>
|
||
<th width="120">等待时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<!-- 表格3 标本报告未完成超时 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>标本报告未完成超时</span>
|
||
<span class="red-text" id="count3">共 0个标本</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<table id="table3">
|
||
<thead>
|
||
<tr>
|
||
<th>仪器</th>
|
||
<th>样本号/条码号</th>
|
||
<th>标本/项目</th>
|
||
<th>上机时间</th>
|
||
<th width="120">等待时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<!-- 表格4 危急值未处理超时 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>危急值未处理超时</span>
|
||
<span class="red-text" id="count4">共 0个标本</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<table id="table4">
|
||
<thead>
|
||
<tr>
|
||
<th>仪器</th>
|
||
<th>样本号</th>
|
||
<th>项目</th>
|
||
<th>结果</th>
|
||
<th width="120">等待时间</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 右上角关闭按钮 -->
|
||
<div class="close-btn-box">
|
||
<div class="close-icon-btn" onclick="closeCurrentPage()">×</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ===================== 全局配置 =====================
|
||
// 从url获取每页行数
|
||
function getPageSizeFromUrl() {
|
||
const search = window.location.search.slice(1);
|
||
const params = new URLSearchParams(search);
|
||
const rows = parseInt(params.get('rows'));
|
||
return !isNaN(rows) && rows > 0 ? rows : 13;
|
||
}
|
||
const PAGE_SIZE = getPageSizeFromUrl();
|
||
const PAGE_DELAY = 5000; // 翻页间隔
|
||
const NO_PAGE_DELAY = 5000; // 不足一页/无数据刷新间隔
|
||
|
||
// 全局数据存储
|
||
const globalData = {
|
||
list1: [], list2: [], list3: [], list4: [],
|
||
pageList1: [], pageList2: [], pageList3: [], pageList4: []
|
||
};
|
||
// 定时器存储,页面卸载统一清空
|
||
const timerPool = {
|
||
timeClock: null,
|
||
carousel1: null, carousel2: null, carousel3: null, carousel4: null,
|
||
load1: null, load2: null, load3: null, load4: null
|
||
};
|
||
|
||
// ===================== 工具函数 =====================
|
||
// 安全清除定时器
|
||
function safeClear(timer) {
|
||
if (timer) clearTimeout(timer);
|
||
return null;
|
||
}
|
||
// 关闭页面
|
||
function closeCurrentPage() {
|
||
window.close();
|
||
}
|
||
// 更新顶部时间
|
||
function updateNowTime() {
|
||
const dom = document.getElementById('timeDom');
|
||
const d = new Date();
|
||
const pad = n => String(n).padStart(2, '0');
|
||
const year = d.getFullYear();
|
||
const month = pad(d.getMonth() + 1);
|
||
const day = pad(d.getDate());
|
||
const h = pad(d.getHours());
|
||
const m = pad(d.getMinutes());
|
||
const s = pad(d.getSeconds());
|
||
dom.innerText = `${year}年${month}月${day}日 ${h}:${m}:${s}`;
|
||
}
|
||
// 原生Ajax GET请求
|
||
function ajaxGet(url, callback) {
|
||
const xhr = new XMLHttpRequest();
|
||
xhr.open('GET', url, true);
|
||
xhr.onload = function () {
|
||
if (xhr.status >= 200 && xhr.status < 300) {
|
||
let res;
|
||
try {
|
||
res = JSON.parse(xhr.responseText);
|
||
} catch (err) {
|
||
res = {};
|
||
}
|
||
callback(null, res);
|
||
} else {
|
||
callback(new Error('请求失败'), null);
|
||
}
|
||
};
|
||
xhr.onerror = function () {
|
||
callback(new Error('网络错误'), null);
|
||
};
|
||
xhr.send();
|
||
}
|
||
|
||
|
||
// 渲染表格1 已采集待收标本超时
|
||
function renderTable1(dataArr) {
|
||
const tbody = document.querySelector('#table1 tbody');
|
||
const countDom = document.getElementById('count1');
|
||
tbody.innerHTML = '';
|
||
countDom.innerText = `共 ${globalData.list1.length}个标本`;
|
||
dataArr.forEach(row => {
|
||
const tr = document.createElement('tr');
|
||
const tds = [
|
||
{ text: row.ksdh },
|
||
{ text: `${row.brxm} / ${row.sqh}` },
|
||
{ text: `${row.yblx} / ${row.jymd}` },
|
||
{ text: row.cysj },
|
||
{ text: `${row.holdtime || 0} 分钟`, red: true }
|
||
];
|
||
tds.forEach(item => {
|
||
const td = document.createElement('td');
|
||
td.innerText = item.text;
|
||
td.setAttribute('data-tip', item.text);
|
||
if (item.red) td.className = 'red-text';
|
||
tr.appendChild(td);
|
||
});
|
||
tbody.appendChild(tr);
|
||
});
|
||
}
|
||
// 渲染表格2 已核收未上机超时
|
||
function renderTable2(dataArr) {
|
||
const tbody = document.querySelector('#table2 tbody');
|
||
const countDom = document.getElementById('count2');
|
||
tbody.innerHTML = '';
|
||
countDom.innerText = `共 ${globalData.list2.length}个标本`;
|
||
dataArr.forEach(row => {
|
||
const tr = document.createElement('tr');
|
||
const tds = [
|
||
{ text: row.ksdh },
|
||
{ text: `${row.brxm} / ${row.sqh}` },
|
||
{ text: `${row.yblx} / ${row.jymd}` },
|
||
{ text: row.jssj },
|
||
{ text: `${row.holdtime || 0} 分钟`, red: true }
|
||
];
|
||
tds.forEach(item => {
|
||
const td = document.createElement('td');
|
||
td.innerText = item.text;
|
||
td.setAttribute('data-tip', item.text);
|
||
if (item.red) td.className = 'red-text';
|
||
tr.appendChild(td);
|
||
});
|
||
tbody.appendChild(tr);
|
||
});
|
||
}
|
||
// 渲染表格3 标本报告未完成超时
|
||
function renderTable3(dataArr) {
|
||
const tbody = document.querySelector('#table3 tbody');
|
||
const countDom = document.getElementById('count3');
|
||
tbody.innerHTML = '';
|
||
countDom.innerText = `共 ${globalData.list3.length}个标本`;
|
||
dataArr.forEach(row => {
|
||
const tr = document.createElement('tr');
|
||
const tds = [
|
||
{ text: row.yq },
|
||
{ text: `${row.ybh} / ${row.sqh}` },
|
||
{ text: `${row.yblx} / ${row.jymd}` },
|
||
{ text: row.sjsj },
|
||
{ text: `${row.holdtime || 0} 分钟`, red: true }
|
||
];
|
||
tds.forEach(item => {
|
||
const td = document.createElement('td');
|
||
td.innerText = item.text;
|
||
td.setAttribute('data-tip', item.text);
|
||
if (item.red) td.className = 'red-text';
|
||
tr.appendChild(td);
|
||
});
|
||
tbody.appendChild(tr);
|
||
});
|
||
}
|
||
// 渲染表格4 危急值未处理超时
|
||
function renderTable4(dataArr) {
|
||
const tbody = document.querySelector('#table4 tbody');
|
||
const countDom = document.getElementById('count4');
|
||
tbody.innerHTML = '';
|
||
countDom.innerText = `共 ${globalData.list4.length}个标本`;
|
||
dataArr.forEach(row => {
|
||
const tr = document.createElement('tr');
|
||
const tds = [
|
||
{ text: row.yq },
|
||
{ text: row.ybh },
|
||
{ text: row.xmmc },
|
||
{ text: row.csjg },
|
||
{ text: `${row.holdtime || 0} 分钟`, red: true }
|
||
];
|
||
tds.forEach(item => {
|
||
const td = document.createElement('td');
|
||
td.innerText = item.text;
|
||
td.setAttribute('data-tip', item.text);
|
||
if (item.red) td.className = 'red-text';
|
||
tr.appendChild(td);
|
||
});
|
||
tbody.appendChild(tr);
|
||
});
|
||
}
|
||
|
||
// ===================== 分页轮播核心逻辑 =====================
|
||
/**
|
||
* @param listKey 全局原始数据key list1/list2...
|
||
* @param pageKey 分页数据key pageList1...
|
||
* @param renderFn 渲染函数
|
||
* @param timerKey 定时器存储key
|
||
* @param reloadFn 接口重载函数
|
||
*/
|
||
function startCarousel(listKey, pageKey, renderFn, timerKey, reloadFn) {
|
||
let currentPage = 0;
|
||
function runCarousel() {
|
||
timerPool[timerKey] = safeClear(timerPool[timerKey]);
|
||
const total = globalData[listKey].length;
|
||
if (total === 0) {
|
||
globalData[pageKey] = [];
|
||
renderFn([]);
|
||
timerPool[timerKey] = setTimeout(runCarousel, NO_PAGE_DELAY);
|
||
return;
|
||
}
|
||
// 数据不足一页,直接全部展示,定时刷新
|
||
if (total <= PAGE_SIZE) {
|
||
globalData[pageKey] = [...globalData[listKey]];
|
||
renderFn(globalData[pageKey]);
|
||
timerPool[timerKey] = setTimeout(runCarousel, NO_PAGE_DELAY);
|
||
return;
|
||
}
|
||
// 分页切片
|
||
const start = currentPage * PAGE_SIZE;
|
||
const end = start + PAGE_SIZE;
|
||
globalData[pageKey] = globalData[listKey].slice(start, end);
|
||
renderFn(globalData[pageKey]);
|
||
const isLastPage = end >= total;
|
||
if (isLastPage) {
|
||
// 最后一页停留后重新拉取接口数据
|
||
timerPool[timerKey] = setTimeout(() => {
|
||
currentPage = 0;
|
||
reloadFn();
|
||
}, PAGE_DELAY);
|
||
} else {
|
||
// 下一页
|
||
timerPool[timerKey] = setTimeout(() => {
|
||
currentPage++;
|
||
runCarousel();
|
||
}, PAGE_DELAY);
|
||
}
|
||
}
|
||
runCarousel();
|
||
}
|
||
|
||
// ===================== 接口加载封装 =====================
|
||
function createLoader(url, listKey, pageKey, renderFn, carouselTimerKey, loaderTimerKey) {
|
||
function loadData() {
|
||
timerPool[loaderTimerKey] = safeClear(timerPool[loaderTimerKey]);
|
||
// 停止当前轮播
|
||
timerPool[carouselTimerKey] = safeClear(timerPool[carouselTimerKey]);
|
||
ajaxGet(url, function (err, res) {
|
||
if (err) {
|
||
console.log('接口加载失败', url, err);
|
||
timerPool[loaderTimerKey] = setTimeout(loadData, NO_PAGE_DELAY);
|
||
return;
|
||
}
|
||
globalData[listKey] = res.data || [];
|
||
// 重启轮播
|
||
startCarousel(listKey, pageKey, renderFn, carouselTimerKey, loadData);
|
||
});
|
||
}
|
||
loadData();
|
||
}
|
||
|
||
// ===================== 页面初始化 =====================
|
||
function pageInit() {
|
||
// 初始化时间
|
||
updateNowTime();
|
||
timerPool.timeClock = setInterval(updateNowTime, 1000);
|
||
// 启动4个表格加载+轮播
|
||
createLoader('/api/list1', 'list1', 'pageList1', renderTable1, 'carousel1', 'load1');
|
||
createLoader('/api/list2', 'list2', 'pageList2', renderTable2, 'carousel2', 'load2');
|
||
createLoader('/api/list3', 'list3', 'pageList3', renderTable3, 'carousel3', 'load3');
|
||
createLoader('/api/list4', 'list4', 'pageList4', renderTable4, 'carousel4', 'load4');
|
||
}
|
||
// 页面卸载/关闭 清空全部定时器,释放资源
|
||
function clearAllTimer() {
|
||
Object.keys(timerPool).forEach(key => {
|
||
timerPool[key] = safeClear(timerPool[key]);
|
||
});
|
||
}
|
||
window.onbeforeunload = clearAllTimer;
|
||
window.onload = pageInit;
|
||
</script>
|
||
</body>
|
||
|
||
</html> |