<!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" style="display: none;"> 
 | 
      <input class="search-input" type="text" placeholder="请输入房间号查询" id="searchRoomInput"> 
 | 
      <button class="search-btn" id="searchRoomBtn">查询</button> 
 | 
    </div> 
 | 
  
 | 
    <div class="header" style="display: none;"> 
 | 
      <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" style="display: none;"> 
 | 
      <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'  
 | 
      // apiBaseUrl: 'http://localhost:48080/admin-api'  
 | 
    }; 
 | 
  
 | 
    // 页面加载完成后初始化 
 | 
    // 页面加载完成后初始化 
 | 
    document.addEventListener('DOMContentLoaded', function () { 
 | 
       // 从URL获取roomId参数 
 | 
  const urlParams = new URLSearchParams(window.location.search); 
 | 
  const roomId = urlParams.get('roomId') || '1'; // 默认值1 
 | 
   
 | 
  // 设置到搜索框(可选) 
 | 
  document.getElementById('searchRoomInput').value = roomId; 
 | 
      // 初始化事件监听 
 | 
      document.getElementById('searchRoomBtn').addEventListener('click', searchRoom); 
 | 
      document.getElementById('callBtn').addEventListener('click', initiateSpeak); 
 | 
      document.getElementById('testSpeakBtn').addEventListener('click', function () { 
 | 
        speak('欢迎使用诊间叫号系统'); 
 | 
      }); 
 | 
      document.getElementById('changeRoomBtn').addEventListener('click', changeRoom); 
 | 
  
 | 
      // 初始化数据 
 | 
      getRoomByIp(roomId); 
 | 
      startScrolling(); 
 | 
  
 | 
      // 初始化语音合成 
 | 
      if (appState.speechSynthesis) { 
 | 
        appState.speechSynthesis.onend = onSpeachEndEvent; 
 | 
      } 
 | 
    }); 
 | 
  
 | 
    // 搜索房间 
 | 
    function searchRoom() { 
 | 
      var searchInput = document.getElementById('searchRoomInput').value.trim(); 
 | 
      if (!searchInput) { 
 | 
        updateAnnouncement('请输入有效的房间号'); 
 | 
        return; 
 | 
      } 
 | 
  
 | 
      updateAnnouncement('正在查询 ' + searchInput + ' 房间信息...'); 
 | 
  
 | 
      var xhr = new XMLHttpRequest(); 
 | 
      xhr.open('GET', appState.apiBaseUrl + '/clinic/room/get-room-by-ip?roomId=' + encodeURIComponent(searchInput), true); 
 | 
      xhr.onreadystatechange = function () { 
 | 
        if (xhr.readyState === 4) { 
 | 
          if (xhr.status === 200) { 
 | 
            try { 
 | 
              var response = JSON.parse(xhr.responseText); 
 | 
              appState.roomProfile = response.data || response; 
 | 
              updateRoomInfo(); 
 | 
              updateAnnouncement('已加载 ' + appState.roomProfile.roomName + ' 信息'); 
 | 
              getList(); // 获取该房间的患者列表 
 | 
            } catch (e) { 
 | 
              updateAnnouncement('解析响应数据失败'); 
 | 
              console.error('解析响应失败:', e); 
 | 
            } 
 | 
          } else { 
 | 
            updateAnnouncement('查询房间失败: ' + xhr.status); 
 | 
            console.error('查询房间失败:', xhr.status); 
 | 
          } 
 | 
        } 
 | 
      }; 
 | 
      xhr.send(); 
 | 
    } 
 | 
  
 | 
    function getRoomByIp(roomId) { 
 | 
  // 如果未传递roomId,尝试从输入框获取 
 | 
  if (!roomId) { 
 | 
    roomId = document.getElementById('searchRoomInput').value.trim() || '1'; 
 | 
  } 
 | 
  
 | 
  var xhr = new XMLHttpRequest(); 
 | 
  xhr.open('GET', appState.apiBaseUrl + '/ecg/screen/room-screen-data?roomId=' + encodeURIComponent(roomId), true); 
 | 
  xhr.onreadystatechange = function() { 
 | 
    if (xhr.readyState === 4) { 
 | 
      if (xhr.status === 200) { 
 | 
        try { 
 | 
          var response = JSON.parse(xhr.responseText); 
 | 
          appState.roomProfile = response.data || response; 
 | 
          updateRoomInfo(); 
 | 
  
 | 
          // 更新当前roomId状态 
 | 
          appState.roomId = roomId; 
 | 
  
 | 
          // 检查是否需要叫号 
 | 
          if (response.data && response.data.called === 0) { 
 | 
            appState.curSpeakPat = response.data; 
 | 
            speak('请' + response.data.patName + '到' + appState.roomProfile.roomName + '装机'); 
 | 
          } 
 | 
        } catch (e) { 
 | 
          console.error('解析响应失败:', e); 
 | 
          fallbackRoomData(roomId); 
 | 
        } 
 | 
      } else { 
 | 
        console.error('获取房间信息失败:', xhr.status); 
 | 
        fallbackRoomData(roomId); 
 | 
      } 
 | 
    } 
 | 
  }; 
 | 
  xhr.send(); 
 | 
} 
 | 
  
 | 
// 后备数据函数 
 | 
function fallbackRoomData(roomId) { 
 | 
  appState.roomProfile = { 
 | 
    roomName: '心电图诊室 ' + roomId, 
 | 
    callingScreenType: [40, 10, 30][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 = document.getElementById('searchRoomInput').value.trim(); 
 | 
  
 | 
      var xhr = new XMLHttpRequest(); 
 | 
      xhr.open('GET', appState.apiBaseUrl + '/ecg/screen/room-screen-data?roomId=' + encodeURIComponent(searchInput), true); 
 | 
      xhr.onreadystatechange = function () { 
 | 
        if (xhr.readyState === 4) { 
 | 
          if (xhr.status === 200) { 
 | 
            try { 
 | 
              var response = JSON.parse(xhr.responseText); 
 | 
              if (response.data) { 
 | 
                appState.checkRelatedPatientList = response.data[1] || []; 
 | 
              } 
 | 
              updatePatientList(); 
 | 
            } catch (e) { 
 | 
              console.error('解析患者列表失败:', e); 
 | 
            } 
 | 
          } else { 
 | 
            console.error('获取患者列表失败:', xhr.status); 
 | 
          } 
 | 
        } 
 | 
      }; 
 | 
      xhr.send(); 
 | 
    } 
 | 
  
 | 
    // 开始定时刷新 
 | 
    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] || ''; 
 | 
    } 
 | 
  
 | 
    // 叫号功能 
 | 
    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标记为已叫号 
 | 
      var xhr = new XMLHttpRequest(); 
 | 
      xhr.open('POST', appState.apiBaseUrl + '/ecg/screen/mark-patient-called', true); 
 | 
      xhr.setRequestHeader('Content-Type', 'application/json'); 
 | 
      xhr.onreadystatechange = function () { 
 | 
        if (xhr.readyState === 4) { 
 | 
          if (xhr.status === 200) { 
 | 
            appState.curSpeakPat = { 
 | 
              patName: patient.patName, 
 | 
              roomName: appState.roomProfile.roomName 
 | 
            }; 
 | 
            speak('请' + patient.patName + '到' + appState.roomProfile.roomName + '装机'); 
 | 
          } else { 
 | 
            updateAnnouncement('叫号失败: ' + xhr.status); 
 | 
            console.error('叫号失败:', xhr.status); 
 | 
          } 
 | 
        } 
 | 
      }; 
 | 
      xhr.send(JSON.stringify({ patientId: patient.id })); 
 | 
    } 
 | 
  
 | 
    // 语音播报 
 | 
    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() { 
 | 
  // 轮换roomId(示例:1→2→3→1) 
 | 
  const newRoomId = (parseInt(appState.roomId) % 3) + 1; 
 | 
   
 | 
  // 更新URL但不刷新页面 
 | 
  window.history.pushState({}, '', `?roomId=${newRoomId}`); 
 | 
   
 | 
  // 重新加载数据 
 | 
  getRoomByIp(newRoomId.toString()); 
 | 
  updateAnnouncement('正在切换到诊间 ' + newRoomId); 
 | 
} 
 | 
  </script> 
 | 
</body> 
 | 
  
 | 
</html> 
 |