430 lines
15 KiB
Vue
Raw Normal View History

<template>
<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">共
<CountTo :value="list1.length" />个标本
</span>
</div>
<div class="table-box">
<el-table :data="list1" border stripe header-cell-class-name="table-header" class="table" height="35vh"
ref="table1Ref" :header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
:row-style="tableRowStyle">
<el-table-column prop="dept" label="科室" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="item" label="标本/项目" />
<el-table-column prop="time" label="采样/送检时间" />
</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">共
<CountTo :value="list2.length" />个标本
</span>
</div>
<div class="table-box">
<el-table :data="list2" border stripe header-cell-class-name="table-header" class="table" height="35vh"
ref="table2Ref" :header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
:row-style="tableRowStyle">
<el-table-column prop="barcode" label="条码" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="item" label="检查项目" />
<el-table-column prop="time" label="签收时间" />
</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 header-cell-class-name="table-header" class="table" height="35vh"
ref="table3Ref" :header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
:row-style="tableRowStyle">
<el-table-column prop="machine" label="仪器" />
<el-table-column prop="sampleNo" label="样本号" />
<el-table-column prop="item" label="检查项目" />
<el-table-column prop="time" label="签收时间" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<span :class="row.status === '超时' ? 'timeout' : ''">
{{ row.status == '超时' ? `剩${row.Timeout}超时` : '' }}
</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 header-cell-class-name="table-header" class="table" height="35vh"
ref="table4Ref" :header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
:row-style="tableRowStyle">
<el-table-column prop="machine" label="仪器" />
<el-table-column prop="sampleNo" label="样本号" />
<el-table-column prop="item" label="项目" />
<el-table-column prop="result" label="结果" />
<el-table-column prop="confirm" label="确认超时" />
</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>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import CountTo from '@/components/countTo/index.vue'
import dayjs from 'dayjs'
import { templateRef } from '@vueuse/core'
import { set } from 'lodash'
// 填充模拟数据(保持原有逻辑)
const list1 = ref([
{ dept: '1111', name: '张三', item: '血常规', time: '2024-05-20 08:15:30' },
{ dept: '呼吸内科', name: '李四', item: '生化全套', time: '2024-05-20 09:20:15' },
{ dept: '心内科', name: '王五', item: '凝血功能', time: '2024-05-20 10:05:40' },
{ dept: '消化内科', name: '赵六', item: '尿常规', time: '2024-05-20 11:10:25' },
{ dept: '神经内科', name: '孙七', item: '脑脊液', time: '2024-05-20 12:00:10' },
{ dept: 'patient', name: '周八', item: '肾功能', time: '2024-05-20 13:00:00' },
{ dept: 'patient', name: '王八', item: '肝功能', time: '2024-05-20 13:15:30' },
{ dept: '急诊科', name: '张三', item: '血常规', time: '2024-05-20 08:15:30' },
{ dept: '呼吸内科', name: '李四', item: '生化全套', time: '2024-05-20 09:20:15' },
{ dept: '心内科', name: '王五', item: '凝血功能', time: '2024-05-20 10:05:40' },
{ dept: '消化内科', name: '赵六', item: '尿常规', time: '2024-05-20 11:10:25' },
{ dept: '神经内科', name: '孙七', item: '脑脊液', time: '2024-05-20 12:00:10' },
{ dept: 'patient', name: '周八', item: '肾功能', time: '2024-05-20 13:00:00' },
{ dept: '完结', name: '王八', item: '肝功能', time: '2024-05-20 13:15:30' },
])
const list2 = ref([
{ barcode: 'start', name: '周八', item: '肝功能', time: '2024-05-20 08:30:00' },
{ barcode: '8800123457', name: '吴九', item: '肾功能', time: '2024-05-20 09:10:00' },
{ barcode: '8800123458', name: '郑十', item: '电解质', time: '2024-05-20 10:20:00' },
{ barcode: '8800123459', name: '钱十一', item: '血糖', time: '2024-05-20 11:05:00' },
{ barcode: '8800123456', name: '周八', item: '肝功能', time: '2024-05-20 08:30:00' },
{ barcode: '8800123457', name: '吴九', item: '肾功能', time: '2024-05-20 09:10:00' },
{ barcode: '8800123458', name: '郑十', item: '电解质', time: '2024-05-20 10:20:00' },
{ barcode: '8800123459', name: '钱十一', item: '血糖', time: '2024-05-20 11:05:00' },
{ barcode: '8800123456', name: '周八', item: '肝功能', time: '2024-05-20 08:30:00' },
{ barcode: '8800123457', name: '吴九', item: '肾功能', time: '2024-05-20 09:10:00' },
{ barcode: '8800123458', name: '郑十', item: '电解质', time: '2024-05-20 10:20:00' },
{ barcode: 'end', name: '钱十一', item: '血糖', time: '2024-05-20 11:05:00' },
])
const list3 = ref([
{ machine: 'start', sampleNo: 'S20240520001', item: '血常规', time: '2024-05-20 07:15:00', status: '正常', Timeout: '15分钟' },
{ machine: '罗氏Cobas 8000', sampleNo: 'S20240520002', item: '生化全套', time: '2024-05-20 08:20:00', status: '超时', Timeout: '20分钟' },
{ machine: '希森美康XN-9000', sampleNo: 'S20240520003', item: '凝血功能', time: '2024-05-20 09:00:00', status: '正常', Timeout: '10分钟' },
{ machine: '雅培i2000', sampleNo: 'S20240520004', item: '肿瘤标志物', time: '2024-05-20 10:10:00', status: '超时', Timeout: '25分钟' },
{ machine: '贝克曼AU5800', sampleNo: 'S20240520001', item: '血常规', time: '2024-05-20 07:15:00', status: '正常', Timeout: '15分钟' },
])
const list4 = ref([
{ machine: '贝克曼AU5800', sampleNo: 'S20240520010', item: '血钾', result: '7.8mmol/L', confirm: '15分钟' },
{ machine: '罗氏Cobas 8000', sampleNo: 'S20240520011', item: '血糖', result: '28.5mmol/L', confirm: '10分钟' },
{ machine: '希森美康XN-9000', sampleNo: 'S20240520012', item: '血小板', result: '25×10^9/L', confirm: '20分钟' },
])
// 表格样式配置(贴合大屏蓝色背景)
const tableHeaderStyle = {
background: 'rgba(30, 104, 195, 0.8)', // 表头半透深蓝(匹配截图表头)
color: '#ffffff', // 表头文字白色
fontSize: '16px', // 表头字体偏大
fontWeight: 'bold', // 表头加粗
borderColor: '#0099ff' // 表头边框浅蓝
}
const tableCellStyle = {
background: 'transparent', // 单元格完全透明(继承大屏背景)
color: '#ffffff', // 单元格文字白色
fontSize: '16px', // 单元格字体清晰
borderColor: '#0099ff' // 单元格边框浅蓝(区分度高)
}
const tableRowStyle = ({ rowIndex }: { rowIndex: number }) => {
// 斑马纹用极浅的半透蓝(贴合背景,无灰白)
return {
background: rowIndex % 2 === 0 ? 'transparent' : 'rgba(0, 153, 255, 0.1)',
hover: 'rgba(0, 153, 255, 0.2)' // 悬浮行浅蓝高亮
}
}
const table1Ref = templateRef('table1Ref')
const table2Ref = templateRef('table2Ref')
const table3Ref = templateRef('table3Ref')
const table4Ref = templateRef('table4Ref')
// 封装滚动函数,接收表格ref和数据列表(用于判断是否需要刷新)
const createTableScroll = (tableRef: Ref, dataList: Ref) => {
let scrollTimer: any = null;
let refreshTimer: any = null;
const clear = () => {
clearInterval(scrollTimer);
clearInterval(refreshTimer);
scrollTimer = null;
refreshTimer = null;
};
const start = () => {
clear();
if (!tableRef.value) return;
const table = tableRef.value.layout.table.refs;
const tableWrapper = table.bodyWrapper.firstElementChild?.firstElementChild;
if (!tableWrapper) return;
// 只有当内容超出可视区域时才启动滚动和定时刷新 异步判断(确保DOM渲染完成)
setTimeout(() => {
if (tableWrapper.clientHeight == tableWrapper.scrollHeight) {
// 内容未超出可视区域,无需滚动和定时刷新
clear()
refreshTimer = setInterval(() => {
refreshSingleTableData(dataList);
}, 5000);
}
}, 50);
scrollTimer = setInterval(() => {
tableWrapper.scrollTop += 1;
// 仅当前表格滚到底时,刷新当前表格数据(而非全部)
if (tableWrapper.clientHeight + tableWrapper.scrollTop == tableWrapper.scrollHeight) {
tableWrapper.scrollTop = 0;
// 按需刷新单个表格数据,而非全局refreshData
refreshSingleTableData(dataList);
}
}, 100);
};
return { start, clear };
};
// 每个表格独立实例化滚动
const table1Scroll = createTableScroll(table1Ref, list1);
const table2Scroll = createTableScroll(table2Ref, list2);
const table3Scroll = createTableScroll(table3Ref, list3);
const table4Scroll = createTableScroll(table4Ref, list4);
// 单个表格数据刷新(避免全局刷新)
const refreshSingleTableData = (dataList: Ref) => {
// console.log(`刷新表格数据: ${dataList === list1 ? 'list1' : dataList === list2 ? 'list2' : dataList === list3 ? 'list3' : 'list4'}`);
if (dataList === list1) {
// list1.value = 新数据(保留原有滚动位置逻辑)
}
};
// 定时刷新
let timeTimer: any
// 动态时间
const nowTime = ref('')
const updateTime = () => {
nowTime.value = dayjs().format('YYYY年MM月DD日 HH:mm:ss')
}
// 挂载/卸载时独立控制
onMounted(async () => {
updateTime();
timeTimer = setInterval(updateTime, 1000);
await nextTick()
table1Scroll.start();
table2Scroll.start();
table3Scroll.start();
table4Scroll.start();
});
onUnmounted(() => {
clearInterval(timeTimer);
table1Scroll.clear();
table2Scroll.clear();
table3Scroll.clear();
table4Scroll.clear();
});
</script>
<style scoped>
.screen-container {
width: 100%;
height: 100vh;
background: url(@/assets/images/bg2.jpg);
background-size: 100% 100%;
padding: 20px 40px 20px 50px;
}
.screen-header {
position: relative;
width: 100%;
margin-bottom: 20px;
}
.screen-title {
font-size: 1.875rem;
color: rgba(14, 253, 255, 1);
text-align: center;
font-weight: bold;
text-align: center;
line-height: 30px;
}
.screen-time {
position: absolute;
right: 30px;
font-size: 20px;
color: #0ef;
font-weight: 700;
top: 0;
}
.grid-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 35px;
}
.card {
position: relative;
background: transparent;
border: 1px solid #0efdff80;
padding: 10px 20px 20px;
height: 42vh;
}
.border_bg_leftTop {
position: absolute;
left: -0.125rem;
top: -0.625rem;
width: 5.78125rem;
height: 0.78125rem;
display: block;
background: #01279d url(@/assets/images/title_left_bg.png) no-repeat;
background-size: cover;
}
.border_bg_rightTop {
position: absolute;
right: -0.15625rem;
top: -0.15625rem;
width: 1.5625rem;
height: 1.5625rem;
display: block;
background: url(@/assets/images/border_bg.jpg) no-repeat;
background-size: cover;
}
.border_bg_leftBottom {
position: absolute;
left: -0.125rem;
bottom: -0.125rem;
width: 1.5625rem;
height: 1.5625rem;
display: block;
background: url(@/assets/images/border_bg.jpg) no-repeat;
background-size: cover;
}
.border_bg_rightBottom {
position: absolute;
right: -0.15625rem;
bottom: -0.15625rem;
width: 1.25rem;
height: 1.25rem;
display: block;
background: url(@/assets/images/title_right_bg.png) no-repeat;
background-size: cover;
}
:deep(.div__body) {
padding: 12px;
}
.card-header {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
color: #fff;
display: flex;
justify-content: space-between;
}
.red-text {
color: #ff4d4f;
}
.red-title {
color: #ff4d4f !important;
}
/* 表格全局样式重置 */
:deep(.table) {
--el-table-bg-color: transparent;
--el-table-border-color: #0099ff;
--el-table-row-hover-bg-color: rgba(0, 153, 255, 0.2);
--el-table--striped-row-bg-color: rgba(0, 153, 255, 0.1);
}
/* 移除Element默认的表格背景/边框样式 */
:deep(.el-table) {
background: transparent !important;
border: none !important;
}
:deep(.el-table th) {
border-right: 1px solid #0099ff !important;
}
:deep(.el-table td) {
border-right: 1px solid #0099ff !important;
}
:deep(.el-table__header-wrapper) {
background: transparent !important;
}
:deep(.el-table__body-wrapper) {
background: transparent !important;
}
.timeout {
color: #00ff00;
font-weight: bold;
font-size: 16px;
}
.tips {
margin-top: 8px;
font-size: 12px;
color: #ff4d4f;
}
</style>