| | |
| | | |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi" /> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge" /> |
| | | <title>大å
</title> |
| | | <style> |
| | |
| | | margin: 0; |
| | | padding: 0; |
| | | box-sizing: border-box; |
| | | font-family: "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; |
| | | /* Android 6 å¯ç¨ä¸æåä½ */; |
| | | font-family: "Droid Sans Fallback", "Noto Sans CJK SC", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; |
| | | } |
| | | |
| | | /* 1. å
¨å±æ·±è²èæ¯ */ |
| | | body { |
| | | background: #001f3f; |
| | | /* æ·±èè²èæ¯ï¼éåå¤§å± */ |
| | | color: #fff; |
| | | height: 100vh; |
| | | overflow: hidden; |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-orient: vertical; |
| | | -webkit-box-direction: normal; |
| | | -webkit-flex-direction: column; |
| | | flex-direction: column; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | /* 2. 顶鍿 - ç®åè¾¹æ¡ï¼æ·±è²ä¸»é¢ */ |
| | | /* 2. 顶鍿 */ |
| | | .top-header { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-align: center; |
| | | -webkit-align-items: center; |
| | | align-items: center; |
| | | padding: 15px 30px; |
| | | background: rgba(0, 30, 60, 0.9); |
| | | /* æ·±è²åéæ */ |
| | | white-space: nowrap; |
| | | position: relative; |
| | | border-bottom: 1px solid #003366; |
| | |
| | | .top-header .title-text { |
| | | position: absolute; |
| | | left: 50%; |
| | | -webkit-transform: translateX(-50%); |
| | | transform: translateX(-50%); |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | |
| | | z-index: 2; |
| | | } |
| | | |
| | | /* 3. 主ä½å
容 */ |
| | | /* 3. 主ä½å
容ï¼gap æ¿æ¢ä¸º margin å
¼å®¹æ§ Chromeï¼ */ |
| | | .main-content { |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | padding: 15px 20px; |
| | | overflow: hidden; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* åå®¹å¨ - ç§»é¤é´å½±ååè§ï¼æ´ç®æ´ */ |
| | | .main-content > .column-box { |
| | | margin-left: 5px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .main-content > .column-box:first-child { |
| | | margin-left: 0; |
| | | } |
| | | |
| | | .main-content > .column-box:last-child { |
| | | margin-right: 0; |
| | | } |
| | | |
| | | /* åå®¹å¨ */ |
| | | .column-box { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-orient: vertical; |
| | | -webkit-box-direction: normal; |
| | | -webkit-flex-direction: column; |
| | | flex-direction: column; |
| | | background: rgba(10, 40, 80, 0.5); |
| | | /* æ·±è²åéæèæ¯ */ |
| | | border-radius: 0; |
| | | padding: 10px; |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | min-width: 0; |
| | | border: 1px solid #003366; |
| | | } |
| | | |
| | | .column-box.col-wide { |
| | | -webkit-box-flex: 2.5; |
| | | -webkit-flex: 2.5; |
| | | flex: 2.5; |
| | | } |
| | | |
| | | .column-box.col-normal { |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | } |
| | | |
| | | /* æ 颿 ·å¼ - ç®åä¸å线 */ |
| | | /* æ é¢è¡ */ |
| | | .col-title-line { |
| | | font-size: 22px; |
| | | font-weight: bold; |
| | | color: #4da6ff; |
| | | text-align: center; |
| | | } |
| | | |
| | | .col-title { |
| | | font-size: 22px; |
| | | font-weight: bold; |
| | |
| | | |
| | | /* æ£è
å表 */ |
| | | .patient-list { |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | overflow-y: auto; |
| | | padding-right: 5px; |
| | | } |
| | | |
| | | /* ãå
³é®ä¿®æ¹ã第1æ 强å¶ä¸è¡2个 */ |
| | | /* 第1æ ï¼ä¸è¡ä¸¤ä¸ª */ |
| | | #col-0 { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-flex-wrap: wrap; |
| | | flex-wrap: wrap; |
| | | -webkit-align-content: flex-start; |
| | | align-content: flex-start; |
| | | } |
| | | |
| | | /* 第2-5æ ä¿æåå */ |
| | | .col-normal .patient-list { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-orient: vertical; |
| | | -webkit-box-direction: normal; |
| | | -webkit-flex-direction: column; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | /* 4. æ£è
é¡¹ç® - æç®æ¨¡å¼ */ |
| | | /* æ£è
é¡¹ç® */ |
| | | .patient-item { |
| | | font-size: 22px; |
| | | padding: 8px 5px; |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-align: center; |
| | | -webkit-align-items: center; |
| | | align-items: center; |
| | | color: #fff; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | /* ãä¼åä¿®æ¹ã第1æ ï¼å¢å åé´è·ï¼æ¹åæ¥æ¤æ */ |
| | | #col-0 .patient-item { |
| | | width: 45%; |
| | | /* 1. 缩å宽度ï¼ä¸ºé´éçåºç©ºé´ */ |
| | | font-size: 20px; |
| | | margin: 0 2.5%; |
| | | /* 2. æ·»å å·¦å³å¤è¾¹è·ï¼ä¸¤åä¹é´æ»é´é为5% */ |
| | | } |
| | | |
| | | .p-number { |
| | |
| | | } |
| | | |
| | | .p-name { |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | /* 5. åºé¨æ - å±
䏿¾ç¤º */ |
| | | /* åºé¨æ */ |
| | | .bottom-footer { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-pack: center; |
| | | -webkit-justify-content: center; |
| | | justify-content: center; |
| | | /* å±
ä¸ */ |
| | | -webkit-box-align: center; |
| | | -webkit-align-items: center; |
| | | align-items: center; |
| | | padding: 10px 30px; |
| | | background: rgba(0, 30, 60, 0.9); |
| | |
| | | text-align: center; |
| | | } |
| | | |
| | | /* æ»å¨æ¡ç¾å - ç»ä¸ç¹ */ |
| | | /* æ»å¨æ¡ */ |
| | | .patient-list::-webkit-scrollbar { |
| | | width: 4px; |
| | | } |
| | | |
| | | .patient-list::-webkit-scrollbar-thumb { |
| | | background: #444; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | /* 6. è°è¯æ¡æ ·å¼ */ |
| | | /* è°è¯é¢æ¿ */ |
| | | .debug-panel { |
| | | position: fixed; |
| | | right: 15px; |
| | |
| | | } |
| | | |
| | | .debug-header { |
| | | display: -webkit-box; |
| | | display: -webkit-flex; |
| | | display: flex; |
| | | -webkit-box-pack: justify; |
| | | -webkit-justify-content: space-between; |
| | | justify-content: space-between; |
| | | -webkit-box-align: center; |
| | | -webkit-align-items: center; |
| | | align-items: center; |
| | | padding: 5px; |
| | | background: #333; |
| | |
| | | } |
| | | |
| | | .debug-body { |
| | | -webkit-box-flex: 1; |
| | | -webkit-flex: 1; |
| | | flex: 1; |
| | | padding: 5px; |
| | | overflow-y: auto; |
| | |
| | | |
| | | <!-- åºé¨æ --> |
| | | <div class="bottom-footer"> |
| | | <!-- 温馨æç¤ºè¯å±
ä¸ --> |
| | | <div class="footer-tip">温馨æç¤ºï¼è¯·å¬å°å¼å«ååå¾å¯¹åºè¯å®¤</div> |
| | | </div> |
| | | |
| | | <!-- è°è¯é¢æ¿ --> |
| | | <div class="debug-panel" id="debugPanel"> |
| | | <div class="debug-header"> |
| | | <span>ð ï¸ è¿è¡æ¥å¿</span> |
| | | <span>[è°è¯] è¿è¡æ¥å¿</span> |
| | | <button onclick="document.getElementById('debugBody').innerHTML=''">æ¸
空</button> |
| | | </div> |
| | | <div class="debug-body" id="debugBody"></div> |
| | |
| | | |
| | | <script src="./static/jquery.min.js"></script> |
| | | <script> |
| | | // ================= è°è¯æ¥å¿å½æ° ================= |
| | | // ================= è°è¯æ¥å¿ ================= |
| | | function logDebug(msg) { |
| | | var now = new Date(); |
| | | var timeStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); |
| | | var logHtml = '<div class="debug-line"><span class="debug-time">[' + timeStr + ']</span>' + msg + '</div>'; |
| | | function pad(num) { return num < 10 ? '0' + num : '' + num; } |
| | | var timeStr = pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds()); |
| | | var logHtml = '<div class="debug-line"><span class="debug-time">[' + timeStr + ']</span> ' + msg + '</div>'; |
| | | console.log("[" + timeStr + "] " + msg); |
| | | var $body = $("#debugBody"); |
| | | $body.append(logHtml); |
| | | $body.scrollTop($body[0].scrollHeight); |
| | | var body = document.getElementById("debugBody"); |
| | | if (body) { |
| | | body.insertAdjacentHTML("beforeend", logHtml); |
| | | body.scrollTop = body.scrollHeight; |
| | | } |
| | | } |
| | | |
| | | // ================= åºç¨ç¶æ ================= |
| | |
| | | columnTitles: ["常è§å¿çµå¾", "卿å¿çµ", "å¹³æ¿è¿å¨å¿çµ", "é£éçµçç", "å¨è硬åçæµ"], |
| | | columnSubTitles: ["åºè¾¹å¿çµå¾(常è§+é¢è°±)M / å¿çµåéå¾N", "卿è¡åC", "", "", ""], |
| | | patients: [], |
| | | apiBaseUrl: "http://192.168.3.12/admin-api", |
| | | apiBaseUrl: "http://192.168.100.110/admin-api", |
| | | pollTimer: null, |
| | | callRepeatTimes: 2, |
| | | spokenPatients: {}, |
| | |
| | | if (appState.isSpeaking || appState.ttsQueue.length === 0) return; |
| | | appState.isSpeaking = true; |
| | | var text = appState.ttsQueue.shift(); |
| | | logDebug("ð å¼å§ææ¥: " + text); |
| | | logDebug("[ææ¥] " + text); |
| | | |
| | | var utterance = new SpeechSynthesisUtterance(text); |
| | | // Android 6 é»è®¤è¯éå¯è½å¾å¿«ï¼éå½è°æ
¢ |
| | | utterance.rate = 0.85; |
| | | utterance.onend = function () { |
| | | appState.isSpeaking = false; |
| | | processTtsQueue(); |
| | | }; |
| | | utterance.onerror = function () { |
| | | utterance.onerror = function (e) { |
| | | logDebug("[TTSé误] " + (e.error || "unknown")); |
| | | appState.isSpeaking = false; |
| | | processTtsQueue(); |
| | | }; |
| | | |
| | | // ä¼å
å°è¯è®¾å¤åç TTS æ¥å£ |
| | | if (typeof wowjoy !== 'undefined' && typeof wowjoy.speek === 'function') { |
| | | try { |
| | | wowjoy.speek(text); |
| | | setTimeout(function () { appState.isSpeaking = false; processTtsQueue(); }, 4000); |
| | | return; |
| | | } catch (e) { logDebug("â wowjoy è°ç¨å¤±è´¥: " + e.message); } |
| | | } catch (e) { logDebug("[wowjoy失败] " + e.message); } |
| | | } |
| | | |
| | | if (window.speechSynthesis) { window.speechSynthesis.speak(utterance); } |
| | | else { appState.isSpeaking = false; processTtsQueue(); } |
| | | if (window.speechSynthesis) { |
| | | // Android 6 WebView ææ¶éè¦å
cancel å speak |
| | | window.speechSynthesis.cancel(); |
| | | window.speechSynthesis.speak(utterance); |
| | | } else { |
| | | logDebug("[TTS] æµè§å¨ä¸æ¯æè¯é³åæ"); |
| | | appState.isSpeaking = false; |
| | | processTtsQueue(); |
| | | } |
| | | } |
| | | |
| | | function speak(text) { |
| | |
| | | processTtsQueue(); |
| | | } |
| | | |
| | | // ================= 渲æå½æ° ================= |
| | | // ================= 渲æ ================= |
| | | function renderMainContent() { |
| | | var $main = $("#mainContent"); |
| | | $main.empty(); |
| | | var main = document.getElementById("mainContent"); |
| | | main.innerHTML = ""; |
| | | |
| | | for (var i = 0; i < appState.columnTitles.length; i++) { |
| | | var titleHtml = '<div class="col-title-line">' + appState.columnTitles[i] + '</div>'; |
| | |
| | | } |
| | | |
| | | var colClass = (i === 0) ? 'col-wide' : 'col-normal'; |
| | | // 注æï¼è¿éä¸åç»ç¬¬1æ å col-flexï¼ç± CSS #col-0 å¼ºå¶æ§å¶ |
| | | var colHtml = '<div class="column-box ' + colClass + '">' + |
| | | '<div class="col-title">' + titleHtml + '</div>' + |
| | | '<div class="patient-list" id="col-' + i + '"></div>' + |
| | | '</div>'; |
| | | $main.append(colHtml); |
| | | main.insertAdjacentHTML("beforeend", colHtml); |
| | | } |
| | | } |
| | | |
| | | function renderPatients() { |
| | | for (var i = 0; i < appState.columnTitles.length; i++) { |
| | | $("#col-" + i).empty(); |
| | | var col = document.getElementById("col-" + i); |
| | | if (col) col.innerHTML = ""; |
| | | } |
| | | |
| | | for (var c = 0; c < appState.patients.length; c++) { |
| | | var colData = appState.patients[c]; |
| | | if (Array.isArray(colData)) { |
| | | var $col = $("#col-" + c); |
| | | var col = document.getElementById("col-" + c); |
| | | if (!col) continue; |
| | | for (var p = 0; p < colData.length; p++) { |
| | | var pat = colData[p]; |
| | | var roomHtml = pat.roomName ? '<span class="p-room">(' + pat.roomName + ')</span>' : ''; |
| | |
| | | '<span class="p-name">' + (pat.patName || '') + '</span>' + |
| | | roomHtml + |
| | | '</div>'; |
| | | $col.append(itemHtml); |
| | | col.insertAdjacentHTML("beforeend", itemHtml); |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | speak(repeatText); |
| | | appState.spokenPatients[newPat.patId] = true; |
| | | logDebug("ð å å
¥ææ¥éå: " + newPat.patName); |
| | | logDebug("[æé] " + newPat.patName + " -> " + newPat.roomName); |
| | | } |
| | | } |
| | | } |
| | |
| | | appState.patients = dataList; |
| | | renderPatients(); |
| | | |
| | | if (window.performance && window.performance.memory) { |
| | | var usedMB = (window.performance.memory.usedJSHeapSize / 1048576).toFixed(2); |
| | | logDebug("ð¾ å
å: " + usedMB + " MB"); |
| | | } |
| | | // Android 6 WebView é叏䏿¯æ performance.memoryï¼å®å
¨å®å« |
| | | try { |
| | | if (window.performance && window.performance.memory) { |
| | | var usedMB = (window.performance.memory.usedJSHeapSize / 1048576).toFixed(2); |
| | | logDebug("[å
å] " + usedMB + " MB"); |
| | | } |
| | | } catch (e) {} |
| | | }, |
| | | error: function (err) { |
| | | logDebug("â 请æ±å¤±è´¥: " + err.statusText); |
| | | logDebug("[请æ±å¤±è´¥] " + (err.statusText || "ç½ç»é误")); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // ================= åå§å ================= |
| | | $(document).ready(function () { |
| | | function onReady() { |
| | | renderMainContent(); |
| | | updateHeaderTime(); |
| | | setInterval(updateHeaderTime, 1000); |
| | | fetchQueueData(); |
| | | appState.pollTimer = setInterval(fetchQueueData, 5000); |
| | | logDebug("â
ç³»ç»å¯å¨"); |
| | | }); |
| | | logDebug("[ç³»ç»] å¯å¨å®æ - Android 6.0.1"); |
| | | } |
| | | |
| | | function updateHeaderTime() { |
| | | var now = new Date(); |
| | |
| | | function padZero(num) { return num < 10 ? '0' + num : '' + num; } |
| | | var dateStr = now.getFullYear() + "å¹´" + padZero(now.getMonth() + 1) + "æ" + padZero(now.getDate()) + "æ¥ " + weekDays[now.getDay()]; |
| | | var timeStr = padZero(now.getHours()) + ":" + padZero(now.getMinutes()); |
| | | $("#headerTime").text(dateStr + " " + timeStr); |
| | | var el = document.getElementById("headerTime"); |
| | | if (el) el.textContent = dateStr + " " + timeStr; |
| | | } |
| | | |
| | | // å
¼å®¹ DOM readyï¼Android 6 æäº WebView å¯è½æ²¡æ $ï¼ |
| | | if (typeof $ !== 'undefined') { |
| | | $(document).ready(onReady); |
| | | } else if (document.readyState === 'complete' || document.readyState === 'interactive') { |
| | | setTimeout(onReady, 1); |
| | | } else { |
| | | document.addEventListener('DOMContentLoaded', onReady); |
| | | } |
| | | </script> |
| | | </body> |
| | | |
| | | </html> |
| | | </html> |