<!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;
|
-webkit-tap-highlight-color: transparent;
|
}
|
|
body {
|
background: linear-gradient(135deg, #e6f0f8, #d9e4f0);
|
color: #333;
|
line-height: 1.5;
|
overflow: hidden;
|
height: 100vh;
|
touch-action: manipulation;
|
}
|
|
#app {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
max-width: 100%;
|
overflow: hidden;
|
padding: 10px;
|
}
|
|
.search-bar {
|
background: rgba(255, 255, 255, 0.8);
|
border-radius: 16px;
|
padding: 12px 15px;
|
margin-bottom: 10px;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
display: flex;
|
align-items: center;
|
}
|
|
.search-input {
|
flex: 1;
|
border: none;
|
background: rgba(240, 244, 249, 0.7);
|
border-radius: 12px;
|
padding: 8px 12px;
|
font-size: 0.9rem;
|
outline: none;
|
color: #4a5568;
|
}
|
|
.search-btn {
|
margin-left: 8px;
|
background: #5b8cff;
|
color: white;
|
border: none;
|
border-radius: 12px;
|
padding: 8px 15px;
|
font-size: 0.9rem;
|
cursor: pointer;
|
transition: all 0.3s;
|
}
|
|
.search-btn:hover {
|
background: #3a7bff;
|
}
|
|
.header {
|
background: rgba(255, 255, 255, 0.8);
|
border-radius: 16px;
|
padding: 12px 15px;
|
margin-bottom: 10px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.clinic-title {
|
font-size: 1.4rem;
|
font-weight: bold;
|
text-align: center;
|
margin-bottom: 5px;
|
color: #4a7dff;
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
}
|
|
.clinic-info {
|
display: flex;
|
justify-content: space-between;
|
width: 100%;
|
font-size: 0.9rem;
|
color: #555;
|
}
|
|
.room-name {
|
background: rgba(91, 140, 255, 0.1);
|
padding: 4px 10px;
|
border-radius: 20px;
|
min-width: 120px;
|
text-align: center;
|
color: #4a7dff;
|
}
|
|
.screen-type {
|
background: rgba(91, 140, 255, 0.1);
|
padding: 4px 10px;
|
border-radius: 20px;
|
color: #4a7dff;
|
}
|
|
.main-content {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
overflow: hidden;
|
}
|
|
.panel {
|
background: rgba(255, 255, 255, 0.95);
|
border-radius: 14px;
|
padding: 12px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
display: flex;
|
flex-direction: column;
|
overflow: hidden;
|
flex: 1;
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
}
|
|
.panel-header {
|
background: linear-gradient(90deg, #a8c4ff, #c0d3ff);
|
color: #2c3e50;
|
padding: 8px 12px;
|
border-radius: 8px;
|
margin-bottom: 10px;
|
font-size: 1rem;
|
font-weight: bold;
|
text-align: center;
|
}
|
|
.patient-list {
|
flex: 1;
|
overflow-y: auto;
|
-webkit-overflow-scrolling: touch;
|
}
|
|
.patient-item {
|
display: flex;
|
align-items: center;
|
padding: 10px 8px;
|
border-bottom: 1px solid #eee;
|
transition: all 0.3s;
|
}
|
|
.patient-item:last-child {
|
border-bottom: none;
|
}
|
|
.patient-item.warning {
|
background-color: #fdf6ec;
|
}
|
|
.patient-item.in-progress {
|
background-color: #f0f9eb;
|
}
|
|
.patient-item.completed {
|
background-color: #f4f4f5;
|
}
|
|
.patient-info {
|
display: flex;
|
flex: 1;
|
min-width: 0;
|
}
|
|
.patient-number {
|
width: 80px;
|
font-weight: bold;
|
color: #5b8cff;
|
font-size: 0.95rem;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
.patient-name {
|
width: 100px;
|
font-size: 0.95rem;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
.patient-check-type {
|
flex: 1;
|
font-size: 0.95rem;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
.patient-status {
|
width: 70px;
|
font-size: 0.8rem;
|
font-weight: bold;
|
text-align: center;
|
padding: 3px 8px;
|
border-radius: 10px;
|
}
|
|
.status-waiting {
|
background-color: #fdf6ec;
|
color: #e6a23c;
|
}
|
|
.status-in-progress {
|
background-color: #f0f9eb;
|
color: #67c23a;
|
}
|
|
.status-completed {
|
background-color: #f4f4f5;
|
color: #909399;
|
}
|
|
.patient-bed {
|
width: 60px;
|
font-size: 0.85rem;
|
text-align: right;
|
color: #666;
|
}
|
|
.footer {
|
background: rgba(255, 255, 255, 0.8);
|
border-radius: 16px;
|
padding: 12px 15px;
|
margin-top: 10px;
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
}
|
|
.announcement {
|
background: rgba(91, 140, 255, 0.1);
|
padding: 8px 15px;
|
border-radius: 20px;
|
font-size: 0.9rem;
|
text-align: center;
|
margin-bottom: 12px;
|
min-height: 20px;
|
color: #4a7dff;
|
}
|
|
|
|
.controls {
|
display: flex;
|
justify-content: space-between;
|
gap: 8px;
|
}
|
|
.control-btn {
|
padding: 8px 5px;
|
background: rgba(91, 140, 255, 0.1);
|
flex: 1; /* 平均分配宽度 */
|
min-width: 0; /* 防止内容溢出 */
|
border: none;
|
border-radius: 12px;
|
color: #4a7dff;
|
font-size: 0.85rem;
|
cursor: pointer;
|
transition: all 0.3s;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.control-btn i {
|
font-size: 1.2rem;
|
margin-bottom: 3px;
|
}
|
|
.control-btn:hover {
|
background: rgba(91, 140, 255, 0.2);
|
}
|
|
.control-btn:active {
|
transform: scale(0.95);
|
}
|
|
.empty-state {
|
text-align: center;
|
color: #999;
|
padding: 20px;
|
font-size: 0.9rem;
|
}
|
|
/* 滚动条样式 */
|
.patient-list::-webkit-scrollbar {
|
width: 5px;
|
}
|
|
.patient-list::-webkit-scrollbar-track {
|
background: #f1f1f1;
|
border-radius: 4px;
|
}
|
|
.patient-list::-webkit-scrollbar-thumb {
|
background: #c0c4cc;
|
border-radius: 4px;
|
}
|
|
.patient-list::-webkit-scrollbar-thumb:hover {
|
background: #909399;
|
}
|
|
/* 动画效果 */
|
@keyframes pulse {
|
0% {
|
transform: scale(1);
|
}
|
|
50% {
|
transform: scale(1.05);
|
}
|
|
100% {
|
transform: scale(1);
|
}
|
}
|
|
.pulse {
|
animation: pulse 2s infinite;
|
}
|
|
/* 响应式调整 */
|
@media (max-width: 480px) {
|
.header {
|
padding: 10px 12px;
|
}
|
|
.clinic-title {
|
font-size: 1.2rem;
|
}
|
|
.clinic-info {
|
font-size: 0.8rem;
|
}
|
|
.panel {
|
padding: 10px;
|
}
|
|
.panel-header {
|
font-size: 0.9rem;
|
padding: 6px 10px;
|
}
|
|
.patient-item {
|
padding: 8px 6px;
|
}
|
|
.patient-number,
|
.patient-name {
|
width: 60px;
|
font-size: 0.9rem;
|
}
|
|
.patient-status {
|
width: 60px;
|
font-size: 0.7rem;
|
}
|
|
.patient-bed {
|
width: 50px;
|
}
|
}
|
|
@media (max-height: 600px) {
|
.header {
|
padding: 8px 10px;
|
}
|
|
.panel {
|
padding: 8px;
|
}
|
|
.patient-item {
|
padding: 6px 4px;
|
}
|
}
|
</style>
|
</head>
|
|
<body>
|
<div id="app">
|
<div class="search-bar">
|
<input class="search-input" type="text" placeholder="请输入房间号查询" id="searchRoomInput">
|
<button class="search-btn" id="searchRoomBtn">查询</button>
|
</div>
|
|
<div class="header">
|
<div class="clinic-title">心电图诊间叫号系统</div>
|
<div class="clinic-info">
|
<div class="room-name" id="roomName">诊间加载中...</div>
|
<div class="screen-type" id="screenType">模式:加载中...</div>
|
</div>
|
</div>
|
|
<div class="main-content">
|
<div class="panel">
|
<div class="panel-header">检查队列</div>
|
<div class="patient-list" id="checkPatientList">
|
<div class="empty-state">
|
暂无等待检查的患者
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<div class="footer">
|
<div class="announcement" id="announcementText">
|
系统运行中...
|
</div>
|
<div class="controls">
|
<button class="control-btn pulse" id="callBtn">
|
<i>📢</i>
|
<span>叫号</span>
|
</button>
|
<button class="flex-1 control-btn" id="testSpeakBtn">
|
<i>🔊</i>
|
<span>测试播音</span>
|
</button>
|
<button class="flex-1 control-btn" id="changeRoomBtn">
|
<i>🔄</i>
|
<span>切换诊间</span>
|
</button>
|
</div>
|
</div>
|
</div>
|
|
<!-- 使用本地 jQuery 文件 -->
|
<script src="./static/jquery.min.js"></script>
|
<script>
|
// 应用状态
|
var appState = {
|
roomProfile: {
|
roomName: '心电图诊室 01',
|
callingScreenType: 40
|
},
|
checkRelatedPatientList: [],
|
curSpeakPat: null,
|
roomId: 1,
|
timer: null,
|
speechSynthesis: window.speechSynthesis || null,
|
apiBaseUrl: 'http://10.0.2.193/admin-api' // 根据实际情况修改
|
};
|
|
// 页面加载完成后初始化
|
$(document).ready(function () {
|
// 初始化事件监听
|
$('#searchRoomBtn').click(searchRoom);
|
$('#callBtn').click(initiateSpeak);
|
$('#testSpeakBtn').click(function () { speak('欢迎使用诊间叫号系统'); });
|
$('#changeRoomBtn').click(changeRoom);
|
|
// 初始化数据
|
getRoomByIp();
|
startScrolling();
|
|
// 初始化语音合成
|
if (appState.speechSynthesis) {
|
appState.speechSynthesis.onend = onSpeachEndEvent;
|
}
|
});
|
|
// 搜索房间
|
function searchRoom() {
|
var searchInput = $('#searchRoomInput').val().trim();
|
if (!searchInput) {
|
updateAnnouncement('请输入有效的房间号');
|
return;
|
}
|
|
updateAnnouncement('正在查询 ' + searchInput + ' 房间信息...');
|
|
// 真实API调用
|
$.ajax({
|
url: appState.apiBaseUrl + '/clinic/room/get-room-by-ip',
|
data: { roomId: searchInput },
|
type: 'GET',
|
dataType: 'json',
|
success: function (response) {
|
appState.roomProfile = response.data || response;
|
updateRoomInfo();
|
updateAnnouncement('已加载 ' + appState.roomProfile.roomName + ' 信息');
|
getList(); // 获取该房间的患者列表
|
},
|
error: function (xhr, status, error) {
|
updateAnnouncement('查询房间失败: ' + (xhr.responseJSON?.message || error));
|
console.error('查询房间失败:', error);
|
}
|
});
|
}
|
|
// 获取房间信息
|
// 获取房间信息 - 使用真实API
|
function getRoomByIp() {
|
var searchInput = $('#searchRoomInput').val().trim();
|
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/screen/room-screen-data',
|
data: { roomId: searchInput },
|
type: 'GET',
|
dataType: 'json',
|
success: function (response) {
|
appState.roomProfile = response.data || response;
|
updateRoomInfo();
|
|
// 检查是否有需要叫号的患者
|
if (response.data && response.data.called === 0) {
|
appState.curSpeakPat = response.data;
|
speak('请' + response.data.patName + '到' + appState.roomProfile.roomName + '装机');
|
}
|
},
|
error: function (xhr, status, error) {
|
console.error('获取房间信息失败:', error);
|
// 使用模拟数据作为后备
|
appState.roomProfile = {
|
roomName: '心电图诊室 ' + (appState.roomId + 1),
|
callingScreenType: [40, 10, 30][appState.roomId % 3]
|
};
|
updateRoomInfo();
|
}
|
});
|
}
|
|
// 更新房间信息显示
|
function updateRoomInfo() {
|
$('#roomName').text(appState.roomProfile.roomName || '诊间加载中...');
|
|
var screenTypeText = '未知模式';
|
switch (appState.roomProfile.callingScreenType) {
|
case 10:
|
case 20:
|
screenTypeText = '仅检查队列';
|
break;
|
case 30:
|
screenTypeText = '仅装机队列';
|
break;
|
case 40:
|
case 50:
|
screenTypeText = '双队列模式';
|
break;
|
}
|
|
$('#screenType').text('模式:' + screenTypeText);
|
}
|
|
function getList() {
|
var searchInput = $('#searchRoomInput').val().trim();
|
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/screen/room-screen-data',
|
data: { roomId: searchInput },
|
type: 'GET',
|
dataType: 'json',
|
success: function (response) {
|
// 假设返回数据结构为 { data: [检查队列数据, 装机队列数据] }
|
if (response.data) {
|
console.log(response.data);
|
console.log(response.data[1]);
|
|
appState.checkRelatedPatientList = response.data[1] || [];
|
console.log();
|
|
} else {
|
// 模拟数据作为后备
|
// appState.checkRelatedPatientList = generateMockPatients();
|
}
|
updatePatientList();
|
},
|
error: function (xhr, status, error) {
|
console.error('获取患者列表失败:', error);
|
// 使用模拟数据作为后备
|
// appState.checkRelatedPatientList = generateMockPatients();
|
// updatePatientList();
|
}
|
});
|
}
|
|
// 开始定时刷新
|
function startScrolling() {
|
getList();
|
appState.timer = setInterval(getList, 5000);
|
}
|
|
// 更新患者列表显示
|
function updatePatientList() {
|
var $list = $('#checkPatientList');
|
$list.empty();
|
|
if (appState.checkRelatedPatientList.length === 0) {
|
$list.append('<div class="empty-state">暂无等待检查的患者</div>');
|
return;
|
}
|
|
for (var i = 0; i < appState.checkRelatedPatientList.length; i++) {
|
var patient = appState.checkRelatedPatientList[i];
|
var statusClass = getStatusClass(patient.status);
|
|
var $item = $('<div class="patient-item ' + statusClass + '">' +
|
'<div class="patient-info">' +
|
'<div class="patient-number">' + getSeqPrefix(patient) + patient.bedNo + '</div>' +
|
'<div class="patient-name">' + nameDesensitize(patient.patName) + '</div>' +
|
'<div class="patient-check-type">' + getCheckTypeName(patient.bookCheckType) + '</div>' +
|
'<div class="patient-status status-' + statusClass + '">' +
|
queueStatusConvert(patient.status) +
|
'</div>' +
|
'</div>' +
|
'</div>');
|
|
$list.append($item);
|
}
|
}
|
|
// 更新公告信息
|
function updateAnnouncement(text) {
|
$('#announcementText').text(text);
|
}
|
|
// 工具函数
|
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 getStatusClass(status) {
|
if (status === 10 || status === 40 || status === 33 || status === 20 || status === 10) return 'waiting';
|
if (status === 30) return 'in-progress';
|
if (status === 7 || status === 3 || status === 5) return 'completed';
|
return '';
|
}
|
|
function queueStatusConvert(status) {
|
var statusMap = {
|
3: '已过号-排队',
|
5: '已过号',
|
7: '已过号-安装',
|
10: '排队中',
|
12: '亲和',
|
13: '亲和-安装',
|
15: '已召回',
|
20: '候诊中',
|
30: '就诊中',
|
33: '已领用',
|
34: '已召回-安装',
|
36: '安装中',
|
40: '已就诊'
|
};
|
return statusMap[status] || '未知状态';
|
}
|
|
function getCheckTypeName(type) {
|
var types = {
|
1: '常规心电图',
|
2: '动态心电图',
|
3: '运动试验',
|
4: '心电监护'
|
};
|
return types[type] || '未知检查';
|
}
|
|
function getSeqPrefix(patient) {
|
var types = {
|
1: 'A001',
|
2: 'A002',
|
3: 'A003',
|
4: 'A004'
|
};
|
return types[patient.bookCheckType] || '';
|
}
|
|
// 叫号功能
|
// 叫号功能(使用真实API)
|
function initiateSpeak() {
|
// 找出等待中的患者
|
var waitingPatients = [];
|
for (var i = 0; i < appState.checkRelatedPatientList.length; i++) {
|
if (appState.checkRelatedPatientList[i].status === 5 || appState.checkRelatedPatientList[i].called === 0) {
|
waitingPatients.push(appState.checkRelatedPatientList[i]);
|
}
|
}
|
|
if (waitingPatients.length === 0) {
|
updateAnnouncement('当前没有等待装机的患者');
|
return;
|
}
|
|
var patient = waitingPatients[0];
|
|
// 调用API标记为已叫号
|
$.ajax({
|
url: appState.apiBaseUrl + '/ecg/screen/mark-patient-called',
|
method: 'POST',
|
data: { patientId: patient.id },
|
success: function() {
|
appState.curSpeakPat = {
|
patName: patient.patName,
|
roomName: appState.roomProfile.roomName
|
};
|
speak('请' + patient.patName + '到' + appState.roomProfile.roomName + '装机');
|
},
|
error: function(xhr, status, error) {
|
updateAnnouncement('叫号失败: ' + (xhr.responseJSON?.message || error));
|
console.error('叫号失败:', error);
|
}
|
});
|
}
|
|
// 语音播报
|
function speak(msg) {
|
updateAnnouncement('正在呼叫: ' + 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 = 1;
|
speech.lang = 'zh-CN';
|
|
appState.speechSynthesis.speak(speech);
|
}
|
|
// 语音结束事件
|
function onSpeachEndEvent(event) {
|
appState.curSpeakPat = null;
|
updateAnnouncement('系统运行中...');
|
}
|
|
// 切换房间
|
function changeRoom() {
|
appState.roomId = (appState.roomId + 1) % 3;
|
getRoomByIp();
|
updateAnnouncement('正在切换诊间...');
|
}
|
</script>
|
</body>
|
|
</html>
|