<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<title>金华人民医院大屏叫号系统</title>
|
<style>
|
/* 基础样式 */
|
* {
|
margin: 0;
|
padding: 0;
|
box-sizing: border-box;
|
font-family: 'Helvetica Neue', Arial, sans-serif;
|
}
|
|
body {
|
background: linear-gradient(135deg, #e6f0f8, #d9e4f0);
|
color: #4a5568;
|
height: 100vh;
|
overflow: hidden;
|
}
|
|
.bigscreen-container {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
padding: 8px;
|
}
|
|
/* 医院标题样式 */
|
.hospital-header {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px;
|
margin-bottom: 8px;
|
font-size: 22px;
|
font-weight: bold;
|
color: #4a7dff;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.hospital-header img {
|
margin-right: 8px;
|
height: 40px;
|
}
|
|
.running-indicator {
|
width: 12px;
|
height: 12px;
|
border-radius: 50%;
|
margin-left: 12px;
|
transition: background-color 0.3s;
|
}
|
|
/* 列标题区域 */
|
.column-header {
|
display: flex;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px 0;
|
margin-bottom: 8px;
|
font-size: 20px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.column-title {
|
flex: 1;
|
min-width: 0;
|
text-align: center;
|
padding: 0 12px;
|
border-left: 1px solid rgba(91, 140, 255, 0.2);
|
}
|
|
.column-title:first-child {
|
border-left: none;
|
flex: 2; /* 第一个房间标题占两列宽度 */
|
}
|
|
/* 患者列表区域 */
|
.patient-list-container {
|
display: flex;
|
flex-grow: 1;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px 0;
|
margin-bottom: 8px;
|
font-size: 18px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
overflow-y: auto;
|
}
|
|
.patient-column {
|
flex: 1;
|
min-width: 0;
|
padding: 0 12px;
|
border-left: 1px solid rgba(91, 140, 255, 0.2);
|
}
|
|
.patient-column:first-child {
|
border-left: none;
|
flex: 2; /* 第一个房间占两列宽度 */
|
}
|
|
/* 第一个房间分成两列的特殊样式 */
|
.first-room-columns {
|
display: flex;
|
flex: 2;
|
min-width: 0;
|
}
|
|
.first-room-column {
|
flex: 1;
|
min-width: 0;
|
padding: 0 12px;
|
}
|
|
.first-room-column:first-child {
|
border-right: 1px solid rgba(91, 140, 255, 0.2);
|
}
|
|
/* 患者项样式 - 优化后的布局 */
|
.patient-item {
|
padding: 10px 8px;
|
margin-bottom: 8px;
|
border-radius: 8px;
|
transition: all 0.3s;
|
background: rgba(255, 255, 255, 0.8);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
}
|
|
.patient-item:hover {
|
background: rgba(91, 140, 255, 0.1);
|
}
|
|
.patient-info-row {
|
display: flex;
|
width: 100%;
|
margin-bottom: 4px;
|
}
|
|
.patient-number-name {
|
display: flex;
|
flex-wrap: wrap;
|
}
|
|
.patient-number {
|
color: #5b8cff;
|
font-weight: bold;
|
margin-right: 10px;
|
min-width: 80px;
|
}
|
|
.patient-name {
|
flex: 1;
|
min-width: 100px;
|
}
|
|
.patient-status {
|
width: 100%;
|
color: #67c23a;
|
margin-top: 4px;
|
}
|
|
.warning-row {
|
background-color: rgba(230, 162, 60, 0.1);
|
color: #e6a23c;
|
}
|
|
/* 底部控制区域 */
|
.footer-controls {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
background: rgba(255, 255, 255, 0.9);
|
border-radius: 12px;
|
padding: 12px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
}
|
|
.welcome-btn {
|
background: rgba(91, 140, 255, 0.1);
|
color: #4a7dff;
|
border: none;
|
border-radius: 8px;
|
padding: 10px 20px;
|
font-size: 16px;
|
cursor: pointer;
|
}
|
|
.welcome-btn:hover {
|
background: rgba(91, 140, 255, 0.2);
|
}
|
|
/* 滚动条样式 */
|
.patient-list-container::-webkit-scrollbar {
|
width: 6px;
|
}
|
|
.patient-list-container::-webkit-scrollbar-track {
|
background: rgba(0, 0, 0, 0.05);
|
border-radius: 4px;
|
}
|
|
.patient-list-container::-webkit-scrollbar-thumb {
|
background: rgba(91, 140, 255, 0.4);
|
border-radius: 4px;
|
}
|
|
/* 响应式调整 */
|
@media (max-width: 1200px) {
|
.hospital-header {
|
font-size: 18px;
|
}
|
.column-header {
|
font-size: 16px;
|
}
|
.patient-list-container {
|
font-size: 14px;
|
}
|
.patient-number {
|
min-width: 60px;
|
}
|
}
|
|
@media (max-width: 768px) {
|
.hospital-header {
|
font-size: 16px;
|
padding: 8px;
|
}
|
.column-header {
|
flex-direction: column;
|
padding: 8px 0;
|
}
|
.column-title {
|
min-width: 100%;
|
padding: 8px 0;
|
border-left: none;
|
border-bottom: 1px solid rgba(91, 140, 255, 0.2);
|
}
|
.patient-list-container {
|
flex-direction: column;
|
}
|
.patient-column, .first-room-columns {
|
min-width: 100%;
|
padding: 8px 0;
|
border-left: none;
|
border-bottom: 1px solid rgba(91, 140, 255, 0.2);
|
}
|
.first-room-column {
|
padding: 0 8px;
|
}
|
}
|
</style>
|
</head>
|
<body>
|
<div class="bigscreen-container">
|
<!-- 顶部医院标题 -->
|
<div class="hospital-header">
|
<img src="./static/jinhua.png"/>
|
<span>金华人民医院</span>
|
<div class="running-indicator" id="runningIndicator"></div>
|
</div>
|
|
<!-- 列标题区域 -->
|
<div class="column-header" id="columnHeader">
|
<!-- 列标题将通过JS动态生成 -->
|
</div>
|
|
<!-- 患者列表区域 -->
|
<div class="patient-list-container" id="patientListContainer">
|
<!-- 患者列表将通过JS动态生成 -->
|
</div>
|
|
<div class="footer-controls">
|
<button class="welcome-btn" id="welcomeBtn">欢迎语</button>
|
</div>
|
</div>
|
|
<script src="./static/jquery.min.js"></script>
|
<script>
|
// 应用状态
|
var appState = {
|
runningIndicator: 0,
|
displayColInfo: {
|
"0": ["常规心电图A", "床边心电图(常规+频谱)M", "常规心电图-心电向量图N"],
|
"1": ["动态心电B", "动态血压C"],
|
"2": ["平板运动心电D"],
|
"3": ["食道电生理F"],
|
"4": ["动脉硬化监测E"]
|
},
|
mapColumnVsPatients: new Map(),
|
curSpeakPat: null,
|
timer: null,
|
speechSynthesis: window.speechSynthesis || null,
|
apiBaseUrl: 'http://localhost:48080/admin-api'
|
};
|
|
// 页面加载完成后初始化
|
$(document).ready(function() {
|
// 初始化事件监听
|
$('#welcomeBtn').click(function() {
|
speak('欢迎使用金华人民医院叫号系统');
|
});
|
|
// 初始化数据
|
updateColumnHeader();
|
startScrolling();
|
|
// 初始化语音合成
|
if (appState.speechSynthesis) {
|
appState.speechSynthesis.onend = onSpeachEndEvent;
|
}
|
});
|
|
// 更新列标题显示
|
function updateColumnHeader() {
|
var $header = $('#columnHeader');
|
$header.empty();
|
|
// 按照固定顺序显示五个房间的标题
|
for (var i = 0; i < 5; i++) {
|
var disColId = i.toString();
|
var disNameList = appState.displayColInfo[disColId] || [];
|
var isFirstColumn = disColId === "0";
|
|
var $title = $('<div class="column-title' + (isFirstColumn ? ' double-column' : '') + '"></div>');
|
|
disNameList.forEach(function(dispName) {
|
$title.append('<div>' + dispName + '</div>');
|
});
|
|
$header.append($title);
|
}
|
}
|
|
// 获取患者列表
|
function getList() {
|
// 这里替换为实际的API调用
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/screen/big-screen-data',
|
type: 'GET',
|
dataType: 'json',
|
success: function(response) {
|
// 确保数据按照五个房间的顺序排列
|
var data = response.data || response;
|
appState.mapColumnVsPatients = new Map();
|
|
// 按照固定顺序处理五个房间的数据
|
for (var i = 0; i < 5; i++) {
|
var roomId = i.toString();
|
appState.mapColumnVsPatients.set(roomId, data[roomId] || []);
|
}
|
|
updatePatientList();
|
},
|
error: function(xhr, status, error) {
|
console.error('获取患者列表失败:', error);
|
// 使用模拟数据作为后备
|
appState.mapColumnVsPatients = generateMockPatients();
|
updatePatientList();
|
}
|
});
|
}
|
|
// 更新患者列表显示
|
function updatePatientList() {
|
var $container = $('#patientListContainer');
|
$container.empty();
|
|
// 按照固定顺序显示五个房间的患者数据
|
for (var i = 0; i < 5; i++) {
|
var disColId = i.toString();
|
var patients = appState.mapColumnVsPatients.get(disColId) || [];
|
var isFirstColumn = disColId === "0";
|
|
if (isFirstColumn) {
|
// 第一个房间特殊处理
|
if (patients.length > 10) {
|
// 超过10人,分成两列
|
var $firstRoomColumns = $('<div class="first-room-columns"></div>');
|
|
// 第一列(前10条)
|
var $firstCol = $('<div class="first-room-column"></div>');
|
patients.slice(0, 10).forEach(function(item, itemIndex) {
|
$firstCol.append(createPatientItem(item));
|
});
|
$firstRoomColumns.append($firstCol);
|
|
// 第二列(剩余数据)
|
var $secondCol = $('<div class="first-room-column"></div>');
|
patients.slice(10).forEach(function(item, itemIndex) {
|
$secondCol.append(createPatientItem(item, itemIndex + 10));
|
});
|
$firstRoomColumns.append($secondCol);
|
|
$container.append($firstRoomColumns);
|
} else {
|
// 不超过10人,单列显示但占两列宽度
|
var $col = $('<div class="patient-column" style="flex:2"></div>');
|
patients.forEach(function(item, itemIndex) {
|
$col.append(createPatientItem(item, itemIndex));
|
});
|
$container.append($col);
|
}
|
} else {
|
// 其他房间单列显示
|
var $col = $('<div class="patient-column"></div>');
|
patients.forEach(function(item, itemIndex) {
|
$col.append(createPatientItem(item, itemIndex));
|
});
|
$container.append($col);
|
}
|
}
|
}
|
|
// 创建患者项 - 优化后的版本
|
function createPatientItem(item, index) {
|
var statusClass = getItemCssClass(item);
|
var $item = $('<div class="patient-item ' + statusClass + '"></div>');
|
|
// 号码和姓名在一行
|
var $numberNameRow = $('<div class="patient-info-row patient-number-name"></div>');
|
$numberNameRow.append('<span class="patient-number">' + getCheckTypeSeqPrefix(item.bookCheckType) + item.bookSeqNum + '</span>');
|
$numberNameRow.append('<span class="patient-name">' + nameDesensitize(item.patName) + '</span>');
|
$item.append($numberNameRow);
|
|
// 状态在下一行
|
var $statusRow = $('<div class="patient-info-row"></div>');
|
$statusRow.append('<span class="patient-status">' + queueStatusConvert(item.status) + '</span>');
|
$item.append($statusRow);
|
|
return $item;
|
}
|
|
// 开始定时刷新
|
function startScrolling() {
|
getList();
|
appState.timer = setInterval(function() {
|
appState.runningIndicator++;
|
$('#runningIndicator').css('backgroundColor', appState.runningIndicator % 2 === 0 ? '#67c23a' : '#e6a23c');
|
getList();
|
|
if (appState.curSpeakPat == null) {
|
initiateSpeak();
|
}
|
}, 5000);
|
}
|
|
// 语音结束事件
|
function onSpeachEndEvent(event) {
|
console.log("Speech ended... " + event.currentTarget.text);
|
|
// 这里替换为实际的API调用
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/call/update',
|
method: 'PUT',
|
data: {
|
id: appState.curSpeakPat.id,
|
called: 1
|
},
|
success: function() {
|
initiateSpeak();
|
},
|
error: function(xhr, status, error) {
|
console.error('更新叫号状态失败:', error);
|
}
|
});
|
}
|
|
// 叫号功能
|
function initiateSpeak() {
|
// 这里替换为实际的API调用
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/call/next',
|
type: 'GET',
|
dataType: 'json',
|
success: function(response) {
|
appState.curSpeakPat = response.data || response;
|
if (appState.curSpeakPat != null && appState.curSpeakPat.called === 0) {
|
speak("请、" + appState.curSpeakPat.patName + "到" + appState.curSpeakPat.roomName + "就诊");
|
}
|
},
|
error: function(xhr, status, error) {
|
console.error('获取下一个叫号失败:', error);
|
}
|
});
|
}
|
|
// 语音播报
|
function speak(msg) {
|
console.info("speak " + msg);
|
|
if (!appState.speechSynthesis) {
|
console.warn("当前浏览器不支持语音合成");
|
return;
|
}
|
|
// 取消当前正在进行的语音
|
appState.speechSynthesis.cancel();
|
|
var speech = new SpeechSynthesisUtterance();
|
speech.text = msg + "。。。" + msg + "。。。" + msg + "。。。";
|
speech.pitch = 1;
|
speech.rate = 0.9;
|
speech.volume = 100;
|
speech.lang = 'zh-CN';
|
speech.onend = onSpeachEndEvent;
|
|
appState.speechSynthesis.speak(speech);
|
}
|
|
// 工具函数
|
function nameDesensitize(patName) {
|
if (!patName) return '';
|
if (patName.length === 2) {
|
return patName.substring(0, 1) + '*';
|
} else if (patName.length === 3) {
|
return patName.substring(0, 1) + '*' + patName.substring(2, 3);
|
} else if (patName.length > 3) {
|
return patName.substring(0, 1) + '*' + '*' + patName.substring(3, patName.length);
|
}
|
return patName;
|
}
|
|
function getItemCssClass(item) {
|
if (item.status === 5 || item.status === 7) {
|
return "warning-row";
|
}
|
return "";
|
}
|
|
function queueStatusConvert(status) {
|
var statusMap = {
|
3: '已过号-排队',
|
5: '已过号',
|
7: '已过号-安装',
|
10: '排队中',
|
12: '亲和',
|
13: '亲和-安装',
|
15: '已召回',
|
20: '候诊中',
|
30: '就诊中',
|
33: '已领用',
|
34: '已召回-安装',
|
36: '安装中',
|
40: '已就诊'
|
};
|
return statusMap[status] || '未知状态';
|
}
|
|
function getCheckTypeSeqPrefix(type) {
|
var types = {
|
1: 'A001',
|
2: 'A002',
|
3: 'A003',
|
4: 'A004'
|
};
|
return types[type] || '';
|
}
|
|
// 生成模拟患者数据
|
function generateMockPatients() {
|
var mockData = {
|
"0": generateMockPatientsForRoom(15, "常规心电图A"),
|
"1": generateMockPatientsForRoom(8, "动态心电B"),
|
"2": generateMockPatientsForRoom(5, "平板运动心电D"),
|
"3": generateMockPatientsForRoom(3, "食道电生理F"),
|
"4": generateMockPatientsForRoom(4, "动脉硬化监测E")
|
};
|
return new Map(Object.entries(mockData));
|
}
|
|
function generateMockPatientsForRoom(count, checkTypeName) {
|
var mockPatients = [];
|
var names = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'];
|
var rooms = ['心电图室1', '心电图室2', '动态心电室', '心电监护室'];
|
var statuses = [5, 7, 10, 12, 13, 15, 20, 30, 33, 34, 36, 40];
|
var checkTypes = [1, 2, 3, 4];
|
|
for (var i = 0; i < count; i++) {
|
mockPatients.push({
|
id: i + 1,
|
patName: names[Math.floor(Math.random() * names.length)],
|
roomName: rooms[Math.floor(Math.random() * rooms.length)],
|
bedNo: '床' + (Math.floor(Math.random() * 20) + 1),
|
status: statuses[Math.floor(Math.random() * statuses.length)],
|
bookCheckType: checkTypes[Math.floor(Math.random() * checkTypes.length)],
|
bookSeqNum: Math.floor(Math.random() * 50) + 1,
|
called: 0,
|
checkTypeName: checkTypeName
|
});
|
}
|
|
return mockPatients;
|
}
|
</script>
|
</body>
|
</html>
|