388 lines
14 KiB
HTML
388 lines
14 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>
|
||
<script src="./js/vue.global.prod.js"></script>
|
||
<link rel="stylesheet" href="./css/element-plus.css">
|
||
<script src="./js/element-plus.js"></script>
|
||
<script src="./js/axios.min.js"></script>
|
||
<script src="./js/dayjs.min.js"></script>
|
||
<!-- 自定义样式 -->
|
||
<link rel="stylesheet" href="./css/index.css">
|
||
</head>
|
||
|
||
<body>
|
||
<div id="app">
|
||
<div class="screen-container">
|
||
<!-- 顶部标题 + 右侧动态时间 -->
|
||
<div class="screen-header">
|
||
<div class="screen-title">TAT监控大屏</div>
|
||
<div class="screen-time">{{ nowTime }}</div>
|
||
</div>
|
||
|
||
<div class="grid-wrapper">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>已采集待收标本</span>
|
||
<span class="red-text">
|
||
共 {{list1.length}}个标本
|
||
</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<el-table :data="list1" border stripe class="table" height="35vh" ref="table1Ref"
|
||
:header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle" :row-style="tableRowStyle">
|
||
<el-table-column prop="ksdh" label="科室"></el-table-column>
|
||
<el-table-column label="姓名/条码">
|
||
<template #default="{ row }">
|
||
<span>{{ row.brxm }} / {{ row.sqh }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="标本/项目">
|
||
<template #default="{ row }">
|
||
<span>{{ row.yblx }} / {{ row.jymd }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="cysj" label="采样时间"></el-table-column>
|
||
<el-table-column label="等待时间">
|
||
<template #default="{ row }">
|
||
<span class="red-text">{{ row.holdtime }} 分钟</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<span class="border_bg_leftTop"></span>
|
||
<span class="border_bg_rightTop"></span>
|
||
<span class="border_bg_leftBottom"></span>
|
||
<span class="border_bg_rightBottom"></span>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>已核收还未上机标本</span>
|
||
<span class="red-text">
|
||
共 {{list2.length}}个标本
|
||
</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<el-table :data="list2" border stripe class="table" height="35vh" ref="table2Ref"
|
||
:header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle" :row-style="tableRowStyle">
|
||
<el-table-column prop="ksdh" label="科室"></el-table-column>
|
||
<el-table-column label="姓名/条码">
|
||
<template #default="{ row }">
|
||
<span>{{ row.brxm }} / {{ row.sqh }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="标本/项目">
|
||
<template #default="{ row }">
|
||
<span>{{ row.yblx }} / {{ row.jymd }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="jssj" label="签收时间"></el-table-column>
|
||
<el-table-column label="等待时间">
|
||
<template #default="{ row }">
|
||
<span class="red-text">{{ row.holdtime }} 分钟</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<span class="border_bg_leftTop"></span>
|
||
<span class="border_bg_rightTop"></span>
|
||
<span class="border_bg_leftBottom"></span>
|
||
<span class="border_bg_rightBottom"></span>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span>标本完成效率TAT</span>
|
||
</div>
|
||
<div class="table-box">
|
||
<el-table :data="list3" border stripe class="table" height="35vh" ref="table3Ref"
|
||
:header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle" :row-style="tableRowStyle">
|
||
<el-table-column prop="yq" label="仪器"></el-table-column>
|
||
<el-table-column label="样本号/条码号">
|
||
<template #default="{ row }">
|
||
<span>{{ row.ybh }} / {{ row.sqh }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="标本/项目">
|
||
<template #default="{ row }">
|
||
<span>{{ row.yblx }} / {{ row.jymd }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="sjsj" label="上机时间"></el-table-column>
|
||
<el-table-column label="等待时间">
|
||
<template #default="{ row }">
|
||
<span class="red-text">{{ row.holdtime }} 分钟</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<span class="border_bg_leftTop"></span>
|
||
<span class="border_bg_rightTop"></span>
|
||
<span class="border_bg_leftBottom"></span>
|
||
<span class="border_bg_rightBottom"></span>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header red-title">危急值结果:(请工作人员及时处理!)</div>
|
||
<div class="table-box">
|
||
<el-table :data="list4" border stripe class="table" height="35vh" ref="table4Ref"
|
||
:header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle" :row-style="tableRowStyle">
|
||
<el-table-column prop="yq" label="仪器"></el-table-column>
|
||
<el-table-column prop="ybh" label="样本号"></el-table-column>
|
||
<el-table-column prop="xmmc" label="项目"></el-table-column>
|
||
<el-table-column prop="csjg" label="结果"></el-table-column>
|
||
<el-table-column prop="confirm" label="确认超时"></el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<span class="border_bg_leftTop"></span>
|
||
<span class="border_bg_rightTop"></span>
|
||
<span class="border_bg_leftBottom"></span>
|
||
<span class="border_bg_rightBottom"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const { createApp, ref, onMounted, onUnmounted, nextTick } = Vue;
|
||
const app = createApp({
|
||
components: {
|
||
...ElementPlus.components
|
||
},
|
||
setup() {
|
||
// 创建axios实例
|
||
const request = axios.create({
|
||
baseURL: '',
|
||
timeout: 5000 // 请求超时时间
|
||
})
|
||
|
||
// 表格数据
|
||
const list1 = ref([]);
|
||
const list2 = ref([]);
|
||
const list3 = ref([]);
|
||
const list4 = ref([]);
|
||
|
||
// 表格样式配置
|
||
const tableHeaderStyle = {
|
||
background: 'rgba(30, 104, 195, 1)',
|
||
color: '#ffffff',
|
||
fontSize: '16px',
|
||
fontWeight: 'bold',
|
||
borderColor: '#0099ff',
|
||
padding: '4px 0px'
|
||
}
|
||
|
||
const tableCellStyle = {
|
||
background: 'transparent',
|
||
color: '#ffffff',
|
||
fontSize: '15px',
|
||
borderColor: '#0099ff',
|
||
padding: '2px 0px'
|
||
}
|
||
|
||
const tableRowStyle = ({ rowIndex }) => {
|
||
return {
|
||
background: rowIndex % 2 === 0 ? 'transparent' : 'rgba(0, 153, 255, 0.1)',
|
||
hover: 'rgba(0, 153, 255, 0.2)'
|
||
}
|
||
}
|
||
|
||
// 表格引用
|
||
const table1Ref = ref()
|
||
const table2Ref = ref()
|
||
const table3Ref = ref()
|
||
const table4Ref = ref()
|
||
|
||
// 定义各表格对应的接口地址
|
||
const tableApiMap = {
|
||
list1: '/api/list1',
|
||
list2: '/api/list2',
|
||
list3: '/api/list3',
|
||
list4: '/api/list4'
|
||
};
|
||
|
||
// 配置
|
||
const SCROLL_INTERVAL = 100; // 滚动速度
|
||
const SCROLL_PAUSE = 5000; // 到底停顿时间
|
||
const NO_SCROLL_REFRESH = 5000; // 无滚动自动刷新
|
||
const TOP_PAUSE = 3000; // 回到顶部后停顿时间
|
||
|
||
// 锁:防止重复触发
|
||
const locked = ref({
|
||
list1: false,
|
||
list2: false,
|
||
list3: false,
|
||
list4: false
|
||
});
|
||
|
||
const getScrollWrapper = (tableRef) => {
|
||
if (!tableRef.value) return null;
|
||
const bodyWrapper = tableRef.value.layout.table.refs.bodyWrapper;
|
||
return bodyWrapper?.firstElementChild?.firstElementChild || null;
|
||
};
|
||
|
||
// 获取单个表格数据
|
||
const fetchTableData = async (listKey) => {
|
||
try {
|
||
const response = await request.get(tableApiMap[listKey]);
|
||
// 根据列表key更新对应数据
|
||
if (listKey === 'list1') {
|
||
list1.value = response.data.data || [];
|
||
} else if (listKey === 'list2') {
|
||
list2.value = response.data.data || [];
|
||
} else if (listKey === 'list3') {
|
||
list3.value = response.data.data || [];
|
||
} else if (listKey === 'list4') {
|
||
list4.value = response.data.data || [];
|
||
}
|
||
console.log(`成功获取${listKey}数据`, response.data);
|
||
} catch (error) {
|
||
console.error(`获取${listKey}数据失败`, error);
|
||
}
|
||
};
|
||
|
||
// 封装滚动函数,接收表格ref、数据列表key、数据列表
|
||
const createTableScroll = (tableRef, listKey) => {
|
||
let scrollTimer = null;
|
||
let refreshTimer = null;
|
||
|
||
const clear = () => {
|
||
clearInterval(scrollTimer);
|
||
clearInterval(refreshTimer);
|
||
scrollTimer = null;
|
||
refreshTimer = null;
|
||
};
|
||
|
||
const start = () => {
|
||
clear();
|
||
|
||
// 定时检查:是否需要滚动
|
||
const checkAndScroll = () => {
|
||
const wrapper = getScrollWrapper(tableRef);
|
||
if (!wrapper) return;
|
||
|
||
// 内容没有溢出 → 只刷新,不滚动
|
||
if (wrapper.scrollHeight <= wrapper.clientHeight) {
|
||
return;
|
||
}
|
||
|
||
// 锁定中 → 不滚动
|
||
if (locked.value[listKey]) return;
|
||
|
||
// 正常滚动
|
||
wrapper.scrollTop += 1;
|
||
|
||
// 判断到底
|
||
const isBottom = wrapper.scrollTop + wrapper.clientHeight >= wrapper.scrollHeight - 2;
|
||
if (isBottom) {
|
||
locked.value[listKey] = true;
|
||
clearInterval(scrollTimer);
|
||
|
||
// 停顿后刷新 + 重置
|
||
setTimeout(async () => {
|
||
await fetchTableData(listKey);
|
||
await nextTick(); // 等待DOM更新
|
||
|
||
const newWrapper = getScrollWrapper(tableRef);
|
||
if (newWrapper) newWrapper.scrollTop = 0;
|
||
|
||
// 新增:回到顶部后停顿3秒再解锁并重启滚动
|
||
setTimeout(() => {
|
||
locked.value[listKey] = false;
|
||
start(); // 重新启动滚动
|
||
}, TOP_PAUSE);
|
||
}, SCROLL_PAUSE);
|
||
}
|
||
};
|
||
|
||
// 启动滚动定时器
|
||
scrollTimer = setInterval(checkAndScroll, SCROLL_INTERVAL);
|
||
|
||
// 无滚动时定时刷新
|
||
refreshTimer = setInterval(async () => {
|
||
const wrapper = getScrollWrapper(tableRef);
|
||
if (wrapper && wrapper.scrollHeight <= wrapper.clientHeight) {
|
||
await fetchTableData(listKey);
|
||
}
|
||
}, NO_SCROLL_REFRESH);
|
||
};
|
||
|
||
return { start, clear };
|
||
};
|
||
|
||
// 初始化所有表格数据
|
||
const initAllTableData = async () => {
|
||
await Promise.all([
|
||
fetchTableData('list1'),
|
||
fetchTableData('list2'),
|
||
fetchTableData('list3'),
|
||
fetchTableData('list4')
|
||
]);
|
||
};
|
||
|
||
// 每个表格独立实例化滚动
|
||
const table1Scroll = createTableScroll(table1Ref, 'list1');
|
||
const table2Scroll = createTableScroll(table2Ref, 'list2');
|
||
const table3Scroll = createTableScroll(table3Ref, 'list3');
|
||
const table4Scroll = createTableScroll(table4Ref, 'list4');
|
||
|
||
// 动态时间
|
||
const nowTime = ref('');
|
||
const updateTime = () => {
|
||
nowTime.value = dayjs().format('YYYY年MM月DD日 HH:mm:ss');
|
||
};
|
||
let timeTimer = null;
|
||
|
||
onMounted(async () => {
|
||
// 更新时间
|
||
updateTime();
|
||
timeTimer = setInterval(updateTime, 1000);
|
||
|
||
// 初始化加载所有表格数据
|
||
await initAllTableData();
|
||
|
||
// 等待DOM渲染完成后启动表格滚动/刷新
|
||
await nextTick();
|
||
table1Scroll.start();
|
||
table2Scroll.start();
|
||
table3Scroll.start();
|
||
table4Scroll.start();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
// 清理所有定时器
|
||
clearInterval(timeTimer);
|
||
table1Scroll.clear();
|
||
table2Scroll.clear();
|
||
table3Scroll.clear();
|
||
table4Scroll.clear();
|
||
});
|
||
|
||
return {
|
||
list1,
|
||
list2,
|
||
list3,
|
||
list4,
|
||
table1Ref,
|
||
table2Ref,
|
||
table3Ref,
|
||
table4Ref,
|
||
nowTime,
|
||
tableHeaderStyle,
|
||
tableCellStyle,
|
||
tableRowStyle
|
||
};
|
||
}
|
||
});
|
||
|
||
// 注册Element Plus
|
||
app.use(ElementPlus);
|
||
app.mount('#app');
|
||
</script>
|
||
</body>
|
||
|
||
</html> |