From 796047fbe84d51816f44be535501415d3c66dd9d Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期日, 21 六月 2026 23:24:37 +0800
Subject: [PATCH] yxh
---
/dev/null | 472 ----------
big.html | 563 +++++++++--
logo0.png | 0
small.html | 189 +++
big滚动.html | 823 +++++++++++++++++
big1.html | 680 ++++++++++++++
6 files changed, 2,095 insertions(+), 632 deletions(-)
diff --git a/big.html b/big.html
index 54c827d..a2fb0ab 100644
--- a/big.html
+++ b/big.html
@@ -1,9 +1,10 @@
-<!doctype html>
+锘�<!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, target-densitydpi=device-dpi" />
+ <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>
@@ -11,7 +12,8 @@
margin: 0;
padding: 0;
box-sizing: border-box;
- /* Android 6 鍙敤涓枃瀛椾綋 */;
+ /* Android 6 鍙敤涓枃瀛椾綋 */
+ ;
font-family: "Droid Sans Fallback", "Noto Sans CJK SC", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
}
@@ -64,10 +66,43 @@
}
.top-header .time-info {
- font-size: 24px;
+ font-size: 20px;
color: #aaa;
margin-left: auto;
z-index: 2;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-info-inner {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .time-left {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-right {
+ margin-left: 8px;
+ }
+
+ .top-header .time-info .time-clock {
+ font-size: 36px;
+ font-weight: bold;
+ color: #ffcc00;
+ line-height: 1.1;
}
/* 3. 涓讳綋鍐呭锛坓ap 鏇挎崲涓� margin 鍏煎鏃� Chrome锛� */
@@ -78,20 +113,35 @@
display: -webkit-box;
display: -webkit-flex;
display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -webkit-flex-direction: column;
+ flex-direction: column;
padding: 15px 20px;
overflow: hidden;
}
- .main-content > .column-box {
- margin-left: 5px;
- margin-right: 5px;
+ /* 鍒楄瀹瑰櫒 */
+ .columns-row {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ overflow: hidden;
}
- .main-content > .column-box:first-child {
+ .columns-row>.column-box {
+ margin-left: 3px;
+ margin-right: 3px;
+ }
+
+ .columns-row>.column-box:first-child {
margin-left: 0;
}
- .main-content > .column-box:last-child {
+ .columns-row>.column-box:last-child {
margin-right: 0;
}
@@ -106,7 +156,7 @@
flex-direction: column;
background: rgba(10, 40, 80, 0.5);
border-radius: 0;
- padding: 10px;
+ padding: 6px;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
@@ -128,16 +178,13 @@
/* 鏍囬琛� */
.col-title-line {
- font-size: 22px;
+ font-size: 28px;
font-weight: bold;
color: #4da6ff;
text-align: center;
}
.col-title {
- font-size: 22px;
- font-weight: bold;
- color: #4da6ff;
padding-bottom: 8px;
border-bottom: 1px solid #003366;
margin-bottom: 10px;
@@ -145,9 +192,14 @@
}
.col-subtitle {
- font-size: 14px;
- color: #999;
+ font-size: 24px;
+ font-weight: bold;
+ color: #ffcc00;
text-align: center;
+ min-height: 20px;
+ /* 鍗充娇涓虹┖涔熶繚鐣欓珮搴︼紝闃叉鏍囬鏍忛珮浣庝笉骞� */
+ line-height: 1.4;
+ word-break: keep-all;
}
/* 鎮h�呭垪琛� */
@@ -156,34 +208,41 @@
-webkit-flex: 1;
flex: 1;
overflow-y: auto;
- padding-right: 5px;
- }
-
- /* 绗�1鏍忥細涓�琛屼袱涓� */
- #col-0 {
+ padding-right: 2px;
display: -webkit-box;
display: -webkit-flex;
display: flex;
- -webkit-flex-wrap: wrap;
- flex-wrap: wrap;
-webkit-align-content: flex-start;
align-content: flex-start;
}
- .col-normal .patient-list {
- display: -webkit-box;
- display: -webkit-flex;
- display: flex;
+ /* 涓�琛屼袱涓紙甯歌蹇冪數鍥俱�佸姩鎬佸績鐢碉級 */
+ .patient-list.two-per-row {
+ -webkit-flex-wrap: wrap;
+ flex-wrap: wrap;
+ }
+
+ .patient-list.two-per-row .patient-item {
+ width: 46%;
+ margin: 0 2%;
+ }
+
+ /* 涓�琛屼竴涓� */
+ .patient-list.one-per-row {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
flex-direction: column;
}
+ .patient-list.one-per-row .patient-item {
+ width: 100%;
+ }
+
/* 鎮h�呴」鐩� */
.patient-item {
- font-size: 22px;
- padding: 8px 5px;
+ font-size: 28px;
+ padding: 4px 3px;
display: -webkit-box;
display: -webkit-flex;
display: flex;
@@ -194,19 +253,22 @@
line-height: 1.4;
}
- #col-0 .patient-item {
- width: 45%;
- font-size: 20px;
- margin: 0 2.5%;
- }
-
.p-number {
color: #ffcc00;
font-weight: bold;
- margin-right: 8px;
+ margin-right: 6px;
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
}
.p-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+
+ .p-name-wrap {
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
@@ -218,8 +280,31 @@
.p-room {
color: #4da6ff;
font-weight: bold;
- font-size: 18px;
- margin-left: 5px;
+ font-size: 22px;
+ margin-left: 4px;
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
+ }
+
+ /* 杩囧彿鎮h�咃細鎺掑湪鍒楀唴锛岃瑙夊急鍖� */
+ .patient-item.missed {
+ color: #aa8888;
+ }
+
+ .patient-item.missed .p-number {
+ color: #cc9966;
+ }
+
+ .patient-item.missed .p-name {
+ color: #aa8888;
+ }
+
+ .patient-item.missed .p-missed-tag {
+ color: #ff6666;
+ font-size: 22px;
+ margin-left: 4px;
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
}
/* 搴曢儴鏍� */
@@ -245,10 +330,30 @@
text-align: center;
}
+ /* 娴嬭瘯璇煶鎸夐挳 */
+ #test-voice-btn {
+ margin-left: 20px;
+ background-color: #007bff;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 6px;
+ font-size: 16px;
+ cursor: pointer;
+ font-weight: bold;
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
+ }
+
+ #test-voice-btn:hover {
+ background-color: #0056b3;
+ }
+
/* 婊氬姩鏉� */
.patient-list::-webkit-scrollbar {
width: 4px;
}
+
.patient-list::-webkit-scrollbar-thumb {
background: #444;
border-radius: 2px;
@@ -256,14 +361,21 @@
/* 璋冭瘯闈㈡澘 */
.debug-panel {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -webkit-flex-direction: column;
+ flex-direction: column;
position: fixed;
right: 15px;
bottom: 70px;
- width: 350px;
- height: 200px;
- background: rgba(0, 0, 0, 0.9);
+ width: 380px;
+ height: 220px;
+ background: rgba(0, 0, 0, 0.92);
color: #0f0;
- border-radius: 4px;
+ border-radius: 6px;
font-family: monospace;
font-size: 12px;
z-index: 9999;
@@ -322,7 +434,7 @@
<!-- 椤堕儴鏍� -->
<div class="top-header">
<img src="logo.png" alt="logo" />
- <span class="title-text">鏈嶅姟澶у巺鎺掑垪</span>
+ <span class="title-text">蹇冪數璇婂尯澶у巺</span>
<span class="time-info" id="headerTime"></span>
</div>
@@ -331,11 +443,12 @@
<!-- 搴曢儴鏍� -->
<div class="bottom-footer">
- <div class="footer-tip">娓╅Θ鎻愮ず锛氳鍚埌鍛煎彨鍚庡墠寰�瀵瑰簲璇婂</div>
+ <div class="footer-tip">娓╅Θ鎻愮ず锛氬惉鍒板懠鍙悗璇峰墠寰�瀵瑰簲璇婂锛岃繃鍙锋偅鑰呭埌瀵艰瘖鍙板鐞嗭紒</div>
+ <button id="test-voice-btn" onclick="testVoice()">娴嬭瘯璇煶</button>
</div>
<!-- 璋冭瘯闈㈡澘 -->
- <div class="debug-panel" id="debugPanel">
+ <div class="debug-panel" id="debugPanel" >
<div class="debug-header">
<span>[璋冭瘯] 杩愯鏃ュ織</span>
<button onclick="document.getElementById('debugBody').innerHTML=''">娓呯┖</button>
@@ -355,61 +468,125 @@
var body = document.getElementById("debugBody");
if (body) {
body.insertAdjacentHTML("beforeend", logHtml);
- body.scrollTop = body.scrollHeight;
+ // requestAnimationFrame 纭繚 DOM 鏇存柊鍚庡啀婊氬姩
+ requestAnimationFrame(function () {
+ body.scrollTop = body.scrollHeight;
+ });
}
}
// ================= 搴旂敤鐘舵�� =================
+ // 鍛煎彨鍙彿娆℃暟鍙傛暟锛氬懠鍙偅鑰呭氨璇婃椂閲嶅鎾姤鐨勬鏁帮紝榛樿 2 娆�
+ var CALL_TIMES = 2;
+ // 涓�琛屼袱涓偅鑰呯殑鍒楃储寮曪紙甯歌蹇冪數鍥俱�佸姩鎬佸績鐢碉級
+ var TWO_PER_ROW_COLS = [0, 1];
+ // 椋熼亾鐢电敓鐞嗘槸鍚︽樉绀猴紙false 闅愯棌锛宼rue 鏄剧ず锛�
+ var SHOW_COL_ESOPHAGEAL = false;
var appState = {
- columnTitles: ["甯歌蹇冪數鍥�", "鍔ㄦ�佸績鐢�", "骞虫澘杩愬姩蹇冪數", "椋熼亾鐢电敓鐞�", "鍔ㄨ剦纭寲鐩戞祴"],
- columnSubTitles: ["搴婅竟蹇冪數鍥�(甯歌+棰戣氨)M / 蹇冪數鍚戦噺鍥綨", "鍔ㄦ�佽鍘婥", "", "", ""],
+ columnTitles: ["甯歌蹇冪數鍥�/蹇冪數鍚戦噺鍥�", "鍔ㄦ�佸績鐢�/琛�鍘�", "骞虫澘杩愬姩蹇冪數", "椋熼亾鐢电敓鐞�", "鍔ㄨ剦纭寲鐩戞祴"],
+ columnSubTitles: ["1鍙�-2鍙疯瘖瀹�", "2鍙疯瘖瀹�", "4鍙疯瘖瀹�", "6鍙疯瘖瀹�", "5鍙疯瘖瀹�"],
patients: [],
- apiBaseUrl: "http://192.168.100.110/admin-api",
+ // apiBaseUrl: "http://192.168.3.12/admin-api",
+ apiBaseUrl: "http://10.0.2.193/admin-api",
pollTimer: null,
- callRepeatTimes: 2,
spokenPatients: {},
ttsQueue: [],
- isSpeaking: false
+ isSpeaking: false,
+ };
+
+ var patStatus = {
+ 3: "宸茶繃鍙�-鎺掗槦", 5: "宸茶繃鍙�", 7: "宸茶繃鍙�-瀹夎", 10: "鎺掗槦涓�", 12: "浜插拰",
+ 13: "浜插拰-瀹夎", 15: "宸插彫鍥�", 20: "鍊欒瘖涓�", 30: "灏辫瘖涓�", 33: "宸查鐢�",
+ 34: "宸插彫鍥�-瀹夎", 36: "瀹夎涓�", 40: "宸插氨璇�"
};
// ================= 璇煶鎾姤 =================
+ // Android WebView GC 淇濇姢锛氬叏灞�鎸佹湁 utterance 寮曠敤锛岄槻姝㈣鍥炴敹瀵艰嚧鏃犲0
+ var _gCurrentUtterance = null;
+
function processTtsQueue() {
if (appState.isSpeaking || appState.ttsQueue.length === 0) return;
appState.isSpeaking = true;
var text = appState.ttsQueue.shift();
logDebug("[鎾姤] " + text);
- var utterance = new SpeechSynthesisUtterance(text);
- // Android 6 榛樿璇�熷彲鑳藉緢蹇紝閫傚綋璋冩參
- utterance.rate = 0.85;
- utterance.onend = function () {
- appState.isSpeaking = false;
- processTtsQueue();
- };
- utterance.onerror = function (e) {
- logDebug("[TTS閿欒] " + (e.error || "unknown"));
- appState.isSpeaking = false;
- processTtsQueue();
- };
-
- // 浼樺厛灏濊瘯璁惧鍘熺敓 TTS 鎺ュ彛
+ // 浼樺厛灏濊瘯璁惧鍘熺敓 TTS 鎺ュ彛 (wowjoy)
if (typeof wowjoy !== 'undefined' && typeof wowjoy.speek === 'function') {
try {
wowjoy.speek(text);
+ logDebug("[鎾姤] wowjoy.speek 宸茶皟鐢�");
setTimeout(function () { appState.isSpeaking = false; processTtsQueue(); }, 4000);
return;
} catch (e) { logDebug("[wowjoy澶辫触] " + e.message); }
}
- if (window.speechSynthesis) {
- // Android 6 WebView 鏈夋椂闇�瑕佸厛 cancel 鍐� speak
- window.speechSynthesis.cancel();
- window.speechSynthesis.speak(utterance);
- } else {
+ if (!window.speechSynthesis) {
logDebug("[TTS] 娴忚鍣ㄤ笉鏀寔璇煶鍚堟垚");
appState.isSpeaking = false;
processTtsQueue();
+ return;
}
+
+ // 浠呭湪鏈夎闊虫鍦ㄦ挱鏀炬椂鎵� cancel锛岄伩鍏嶄笉蹇呰鐨勪腑鏂�
+ if (window.speechSynthesis.speaking) {
+ logDebug("[鎾姤] cancel 褰撳墠鎾斁");
+ window.speechSynthesis.cancel();
+ }
+
+ var utterance = new SpeechSynthesisUtterance(text);
+ utterance.rate = 0.85;
+ utterance.volume = 1.0;
+ // Android WebView GC 淇濇姢锛氬瓨鍏ㄥ眬寮曠敤
+ _gCurrentUtterance = utterance;
+
+ utterance.onstart = function () {
+ logDebug("[鎾姤] onstart 瑙﹀彂");
+ };
+ utterance.onend = function () {
+ logDebug("[鎾姤] onend 瑙﹀彂");
+ _gCurrentUtterance = null;
+ appState.isSpeaking = false;
+ // 寤惰繜涓�涓嬭寮曟搸瀹屽叏閲婃斁
+ setTimeout(function () { processTtsQueue(); }, 200);
+ };
+ utterance.onerror = function (e) {
+ logDebug("[TTS閿欒] " + (e.error || "unknown"));
+ _gCurrentUtterance = null;
+ appState.isSpeaking = false;
+ setTimeout(function () { processTtsQueue(); }, 200);
+ };
+
+ // 纭繚璇煶鍖呭凡鍔犺浇 (Android 涓� getVoices 鍙兘鍒濆涓虹┖)
+ var voices = window.speechSynthesis.getVoices();
+ if (voices.length > 0) {
+ utterance.voice = voices[0];
+ logDebug("[鎾姤] 浣跨敤璇煶: " + voices[0].name + " lang=" + voices[0].lang);
+ } else {
+ logDebug("[鎾姤] getVoices 涓虹┖, 绛夊緟 voiceschanged 浜嬩欢");
+ var voicesLoaded = false;
+ var onVoicesChange = function () {
+ if (voicesLoaded) return;
+ voicesLoaded = true;
+ var v2 = window.speechSynthesis.getVoices();
+ if (v2.length > 0) {
+ utterance.voice = v2[0];
+ logDebug("[鎾姤] 璇煶鍔犺浇瀹屾垚: " + v2[0].name);
+ }
+ window.speechSynthesis.speak(utterance);
+ };
+ window.speechSynthesis.addEventListener('voiceschanged', onVoicesChange);
+ // 濡傛灉 1s 鍐呮病瑙﹀彂锛岀洿鎺� speak
+ setTimeout(function () {
+ if (!voicesLoaded) {
+ logDebug("[鎾姤] voiceschanged 瓒呮椂, 鐩存帴 speak");
+ window.speechSynthesis.speak(utterance);
+ }
+ }, 1000);
+ return;
+ }
+
+ window.speechSynthesis.speak(utterance);
+ logDebug("[鎾姤] speak 宸茶皟鐢�");
}
function speak(text) {
@@ -418,47 +595,179 @@
processTtsQueue();
}
- // ================= 娓叉煋 =================
- function renderMainContent() {
- var main = document.getElementById("mainContent");
- main.innerHTML = "";
+ // ================= 璇煶娴嬭瘯 =================
+ function testVoice() {
+ logDebug("[璇煶娴嬭瘯] ========== 寮�濮� ==========");
+ var testText = "娴嬭瘯璇煶鎾姤";
- for (var i = 0; i < appState.columnTitles.length; i++) {
- var titleHtml = '<div class="col-title-line">' + appState.columnTitles[i] + '</div>';
- if (appState.columnSubTitles[i]) {
- titleHtml += '<div class="col-subtitle">' + appState.columnSubTitles[i] + '</div>';
+ // 1. 妫�娴� wowjoy 鍘熺敓鎺ュ彛
+ var hasWowjoy = (typeof wowjoy !== 'undefined');
+ logDebug("[璇煶娴嬭瘯] wowjoy 瀵硅薄瀛樺湪=" + hasWowjoy);
+ if (hasWowjoy) {
+ logDebug("[璇煶娴嬭瘯] wowjoy.speek 绫诲瀷=" + (typeof wowjoy.speek));
+ if (typeof wowjoy.speek === 'function') {
+ try {
+ wowjoy.speek(testText);
+ logDebug("[璇煶娴嬭瘯] wowjoy.speek 宸茶皟鐢紝娉ㄦ剰鍚澶囧0闊�");
+ } catch (e) {
+ logDebug("[璇煶娴嬭瘯] wowjoy.speek 寮傚父: " + e.message);
+ }
+ }
+ }
+
+ // 2. 妫�娴� SpeechSynthesis
+ var hasSpeech = (typeof window.speechSynthesis !== 'undefined');
+ logDebug("[璇煶娴嬭瘯] speechSynthesis 瀵硅薄瀛樺湪=" + hasSpeech);
+
+ if (hasSpeech) {
+ var syn = window.speechSynthesis;
+ logDebug("[璇煶娴嬭瘯] speaking=" + syn.speaking + " pending=" + syn.pending + " paused=" + syn.paused);
+
+ var voices = syn.getVoices();
+ logDebug("[璇煶娴嬭瘯] getVoices 杩斿洖 " + voices.length + " 涓闊冲寘");
+ for (var v = 0; v < Math.min(voices.length, 5); v++) {
+ logDebug("[璇煶娴嬭瘯] 璇煶[" + v + "] " + voices[v].name + " lang=" + voices[v].lang + " local=" + voices[v].localService);
}
- var colClass = (i === 0) ? 'col-wide' : 'col-normal';
- var colHtml = '<div class="column-box ' + colClass + '">' +
- '<div class="col-title">' + titleHtml + '</div>' +
- '<div class="patient-list" id="col-' + i + '"></div>' +
- '</div>';
- main.insertAdjacentHTML("beforeend", colHtml);
+ // 濡傛灉 voices 涓虹┖锛岀瓑寰呭姞杞藉悗鍐嶆挱
+ function doSpeakTest() {
+ var v2 = syn.getVoices();
+ var u = new SpeechSynthesisUtterance(testText);
+ u.rate = 0.85;
+ u.volume = 1.0;
+ if (v2.length > 0) {
+ u.voice = v2[0];
+ logDebug("[璇煶娴嬭瘯] 閫夋嫨璇煶: " + v2[0].name);
+ }
+ // GC 淇濇姢
+ window._testUtterance = u;
+ u.onstart = function() { logDebug("[璇煶娴嬭瘯] onstart 瑙﹀彂 鉁�"); };
+ u.onend = function() { logDebug("[璇煶娴嬭瘯] onend 瑙﹀彂 鉁�"); window._testUtterance = null; };
+ u.onerror = function(e) { logDebug("[璇煶娴嬭瘯] onerror: " + (e.error || "unknown")); window._testUtterance = null; };
+
+ if (syn.speaking) {
+ logDebug("[璇煶娴嬭瘯] 鏈夎闊虫鍦ㄦ挱鏀撅紝鍏� cancel");
+ syn.cancel();
+ setTimeout(function () { syn.speak(u); logDebug("[璇煶娴嬭瘯] speak 宸茶皟鐢�(寤惰繜)"); }, 300);
+ } else {
+ syn.speak(u);
+ logDebug("[璇煶娴嬭瘯] speak 宸茶皟鐢�");
+ }
+ }
+
+ if (voices.length === 0) {
+ logDebug("[璇煶娴嬭瘯] 璇煶鍒楄〃涓虹┖锛岀瓑寰� voiceschanged...");
+ var loaded = false;
+ var handler = function () {
+ if (loaded) return;
+ loaded = true;
+ logDebug("[璇煶娴嬭瘯] voiceschanged 瑙﹀彂, 璇煶鏁�=" + syn.getVoices().length);
+ doSpeakTest();
+ };
+ syn.addEventListener('voiceschanged', handler);
+ setTimeout(function () {
+ if (!loaded) {
+ logDebug("[璇煶娴嬭瘯] voiceschanged 瓒呮椂(2s), 寮哄埗娴嬭瘯");
+ doSpeakTest();
+ }
+ }, 2000);
+ } else {
+ doSpeakTest();
+ }
}
+
+ if (!hasWowjoy && !hasSpeech) {
+ logDebug("[璇煶娴嬭瘯] 鏃犱换浣� TTS 寮曟搸鍙敤锛�");
+ }
+ logDebug("[璇煶娴嬭瘯] ========== 缁撴潫 ==========");
}
- function renderPatients() {
- for (var i = 0; i < appState.columnTitles.length; i++) {
- var col = document.getElementById("col-" + i);
- if (col) col.innerHTML = "";
- }
+ // ================= 娓叉煋锛氭爣棰樻鏋讹紙浠呭垵濮嬪寲涓�娆★級=================
+ function initLayout() {
+ logDebug("[甯冨眬] initLayout 寮�濮�, 鍒楁暟=" + appState.columnTitles.length);
+ var main = document.getElementById("mainContent");
+ if (!main) { logDebug("[甯冨眬] mainContent 鍏冪礌鏈壘鍒�!"); return; }
+ var html = '<div class="columns-row">';
+ var visibleCount = 0;
- for (var c = 0; c < appState.patients.length; c++) {
- var colData = appState.patients[c];
- if (Array.isArray(colData)) {
- 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>' : '';
- var itemHtml = '<div class="patient-item">' +
- '<span class="p-number">' + (pat.bookSeqNum || '') + '</span>' +
- '<span class="p-name">' + (pat.patName || '') + '</span>' +
- roomHtml +
- '</div>';
- col.insertAdjacentHTML("beforeend", itemHtml);
- }
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ // 椋熼亾鐢电敓鐞嗭細閫氳繃 SHOW_COL_ESOPHAGEAL 鎺у埗鏄鹃殣
+ if (i === 3 && !SHOW_COL_ESOPHAGEAL) {
+ logDebug("[甯冨眬] 鍒�" + i + " 椋熼亾鐢电敓鐞� 宸查殣钘�");
+ continue;
+ }
+
+ var subtitle = appState.columnSubTitles[i] || " ";
+ // 涓�琛屼袱涓殑鍒楃敤 col-wide锛屽惁鍒� col-normal
+ var isTwoPerRow = TWO_PER_ROW_COLS.indexOf(i) !== -1;
+ var colClass = isTwoPerRow ? 'col-wide' : 'col-normal';
+ var rowClass = isTwoPerRow ? 'two-per-row' : 'one-per-row';
+ logDebug("[甯冨眬] 鍒�" + i + " " + appState.columnTitles[i] + " class=" + colClass + " row=" + rowClass);
+ html += '<div class="column-box ' + colClass + '">' +
+ '<div class="col-title">' +
+ '<div class="col-title-line">' + appState.columnTitles[i] + '</div>' +
+ '<div class="col-subtitle">' + subtitle + '</div>' +
+ '</div>' +
+ '<div class="patient-list ' + rowClass + '" id="col-' + i + '"></div>' +
+ '</div>';
+ visibleCount++;
+ }
+ html += '</div>';
+ main.innerHTML = html;
+ logDebug("[甯冨眬] initLayout 瀹屾垚, 鍙鍒�=" + visibleCount);
+ }
+
+ // ================= 娓叉煋锛氭偅鑰呭垪琛紙diff 鏇存柊锛屽彧鏀瑰彉鍖栫殑鍒楋級=================
+ function desensitizeName(name) {
+ if (!name || name.length < 2) return name || '';
+ return name.charAt(0) + '*' + name.substring(2);
+ }
+
+ function isMissedStatus(status) {
+ var s = parseInt(status, 10);
+ return s === 3 || s === 5 || s === 7;
+ }
+
+ function buildColumnHtml(colData) {
+ if (!Array.isArray(colData) || colData.length === 0) return "";
+ var h = "";
+ for (var p = 0; p < colData.length; p++) {
+ var pat = colData[p];
+ var missedClass = isMissedStatus(pat.status) ? ' missed' : '';
+ var roomHtml = (!isMissedStatus(pat.status) && pat.roomName) ? '<span class="p-room">' + pat.roomName + '</span>' : '';
+ h += '<div class="patient-item' + missedClass + '">' +
+ '<span class="p-number">' + (pat.seqNum || '') + '</span>' +
+ '<span class="p-name-wrap"><span class="p-name">' + desensitizeName(pat.patName) + '</span>' + roomHtml +
+ (missedClass ? '<span class="p-missed-tag">杩囧彿</span>' : '') + '</span>' +
+ '</div>';
+ }
+ return h;
+ }
+ function isSameColumn(a, b) {
+ if (!Array.isArray(a) || !Array.isArray(b)) return a === b;
+ if (a.length !== b.length) return false;
+ for (var i = 0; i < a.length; i++) {
+ if (a[i].patId !== b[i].patId) return false;
+ if (a[i].roomName !== b[i].roomName) return false;
+ if (parseInt(a[i].status, 10) !== parseInt(b[i].status, 10)) return false;
+ }
+ return true;
+ }
+
+ function updatePatients(dataList) {
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var newData = (dataList && dataList[i]) ? dataList[i] : [];
+ var oldData = appState.patients[i];
+
+ // diff锛氭暟鎹湭鍙樺寲鍒欒烦杩囪鍒� DOM 鎿嶄綔
+ if (isSameColumn(oldData, newData)) continue;
+
+ var col = document.getElementById("col-" + i);
+ if (col) {
+ col.innerHTML = buildColumnHtml(newData);
+ logDebug("[鏇存柊] 鍒�" + i + " " + appState.columnTitles[i] + " 鍒锋柊 " + newData.length + " 浜�");
+ } else {
+ logDebug("[鏇存柊] 鍒�" + i + " DOM涓嶅瓨鍦�(鍙兘宸查殣钘�)");
}
}
}
@@ -485,10 +794,10 @@
}
if (!oldPat || !oldPat.roomName) {
var repeatText = "";
- for (var r = 0; r < appState.callRepeatTimes; r++) {
- repeatText += "璇� " + newPat.bookSeqNum + " 鍙� " + newPat.patName + " 鍒� " + newPat.roomName + " 灏辫瘖銆�";
- }
- speak(repeatText);
+ repeatText += "璇� " + newPat.seqNum + " 鍙� " + newPat.patName + " 鍒� " + newPat.roomName + " 灏辫瘖銆�";
+ for (var r = 0; r < CALL_TIMES; r++) {
+ speak(repeatText);
+ }
appState.spokenPatients[newPat.patId] = true;
logDebug("[鎺掗槦] " + newPat.patName + " -> " + newPat.roomName);
}
@@ -501,22 +810,28 @@
// ================= 鏁版嵁鑾峰彇 =================
function fetchQueueData() {
var url = appState.apiBaseUrl + "/ecg/screen/big-screen-data";
+ logDebug("[璇锋眰] GET " + url);
$.ajax({
url: url,
type: "GET",
dataType: "json",
timeout: 5000,
success: function (res) {
+ logDebug("[鍝嶅簲] code=" + (res ? res.code : "null"));
var dataList = [];
if (res && res.code === 0 && res.data) {
for (var i = 0; i < appState.columnTitles.length; i++) {
var key = i.toString();
dataList[i] = (res.data[key] && Array.isArray(res.data[key])) ? res.data[key] : [];
+ logDebug("[鏁版嵁] 鍒�" + i + " " + appState.columnTitles[i] + " 鎮h�呮暟=" + dataList[i].length);
}
+ } else {
+ logDebug("[鍝嶅簲] 寮傚父 code=" + (res ? res.code : "null") + " msg=" + (res ? res.msg : ""));
}
+ // 鍏� diff 鏇存柊 DOM锛屽啀鎾姤锛屾渶鍚庝繚瀛樻暟鎹敤浜庝笅娆″姣�
+ updatePatients(dataList);
checkAndSpeakNewRooms(appState.patients, dataList);
appState.patients = dataList;
- renderPatients();
// Android 6 WebView 閫氬父涓嶆敮鎸� performance.memory锛屽畨鍏ㄥ畧鍗�
try {
@@ -524,43 +839,57 @@
var usedMB = (window.performance.memory.usedJSHeapSize / 1048576).toFixed(2);
logDebug("[鍐呭瓨] " + usedMB + " MB");
}
- } catch (e) {}
+ } catch (e) { }
},
error: function (err) {
- logDebug("[璇锋眰澶辫触] " + (err.statusText || "缃戠粶閿欒"));
+ logDebug("[璇锋眰澶辫触] status=" + err.status + " " + (err.statusText || "缃戠粶閿欒") + " url=" + url);
}
});
}
// ================= 鍒濆鍖� =================
function onReady() {
- renderMainContent();
+ logDebug("[鍒濆鍖朷 onReady 寮�濮�");
+ logDebug("[鐜] speechSynthesis=" + (typeof window.speechSynthesis !== 'undefined') + " wowjoy=" + (typeof wowjoy !== 'undefined'));
+ try {
+ initLayout();
+ logDebug("[鍒濆鍖朷 initLayout 瀹屾垚");
+ } catch (e) { logDebug("[鍒濆鍖朷 initLayout 寮傚父: " + e.message); }
updateHeaderTime();
setInterval(updateHeaderTime, 1000);
- fetchQueueData();
+ try {
+ fetchQueueData();
+ logDebug("[鍒濆鍖朷 棣栨 fetchQueueData 宸插彂璧�");
+ } catch (e) { logDebug("[鍒濆鍖朷 fetchQueueData 寮傚父: " + e.message); }
appState.pollTimer = setInterval(fetchQueueData, 5000);
- logDebug("[绯荤粺] 鍚姩瀹屾垚 - Android 6.0.1");
+ logDebug("[绯荤粺] 鍚姩瀹屾垚 - Android 6.0.1 杞闂撮殧5s");
}
function updateHeaderTime() {
var now = new Date();
var weekDays = ["鏄熸湡鏃�", "鏄熸湡涓�", "鏄熸湡浜�", "鏄熸湡涓�", "鏄熸湡鍥�", "鏄熸湡浜�", "鏄熸湡鍏�"];
function padZero(num) { return num < 10 ? '0' + num : '' + num; }
- var dateStr = now.getFullYear() + "骞�" + padZero(now.getMonth() + 1) + "鏈�" + padZero(now.getDate()) + "鏃� " + weekDays[now.getDay()];
+ var dateStr = now.getFullYear() + "骞�" + padZero(now.getMonth() + 1) + "鏈�" + padZero(now.getDate()) + "鏃�";
+ var weekStr = weekDays[now.getDay()];
var timeStr = padZero(now.getHours()) + ":" + padZero(now.getMinutes());
var el = document.getElementById("headerTime");
- if (el) el.textContent = dateStr + " " + timeStr;
+ // 鏄熸湡鍜屾棩鏈熷悇鍗犱竴琛屽湪宸︼紝鏃堕棿澶у瓧鍦ㄥ彸璺ㄤ袱琛�
+ if (el) el.innerHTML = '<div class="time-info-inner"><div class="time-left"><div>' + weekStr + '</div><div>' + dateStr + '</div></div><div class="time-right"><span class="time-clock">' + timeStr + '</span></div></div>';
}
// 鍏煎 DOM ready锛圓ndroid 6 鏌愪簺 WebView 鍙兘娌℃湁 $锛�
+ logDebug("[鍏ュ彛] readyState=" + document.readyState + " jQuery=" + (typeof $ !== 'undefined'));
if (typeof $ !== 'undefined') {
+ logDebug("[鍏ュ彛] 浣跨敤 jQuery.ready");
$(document).ready(onReady);
} else if (document.readyState === 'complete' || document.readyState === 'interactive') {
+ logDebug("[鍏ュ彛] DOM宸插氨缁�, setTimeout瑙﹀彂");
setTimeout(onReady, 1);
} else {
+ logDebug("[鍏ュ彛] 鐩戝惉 DOMContentLoaded");
document.addEventListener('DOMContentLoaded', onReady);
}
</script>
</body>
-</html>
+</html>
\ No newline at end of file
diff --git a/big.html.bak b/big.html.bak
deleted file mode 100644
index 871e128..0000000
--- a/big.html.bak
+++ /dev/null
@@ -1,472 +0,0 @@
-<!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" />
- <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;
- }
-
- /* 1. 鍏ㄥ眬娣辫壊鑳屾櫙 */
- body {
- background: #001f3f;
- /* 娣辫摑鑹茶儗鏅紝閫傚悎澶у睆 */
- color: #fff;
- height: 100vh;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- font-size: 14px;
- }
-
- /* 2. 椤堕儴鏍� - 绠�鍖栬竟妗嗭紝娣辫壊涓婚 */
- .top-header {
- display: flex;
- 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 img {
- height: 50px;
- margin-right: 20px;
- z-index: 2;
- }
-
- .top-header .title-text {
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
- font-size: 32px;
- font-weight: bold;
- color: #fff;
- z-index: 1;
- }
-
- .top-header .time-info {
- font-size: 24px;
- color: #aaa;
- margin-left: auto;
- z-index: 2;
- }
-
- /* 3. 涓讳綋鍐呭 */
- .main-content {
- flex: 1;
- display: flex;
- padding: 15px 20px;
- overflow: hidden;
- gap: 10px;
- }
-
- /* 鍒楀鍣� - 绉婚櫎闃村奖鍜屽渾瑙掞紝鏇寸畝娲� */
- .column-box {
- display: flex;
- flex-direction: column;
- background: rgba(10, 40, 80, 0.5);
- /* 娣辫壊鍗婇�忔槑鑳屾櫙 */
- border-radius: 0;
- padding: 10px;
- flex: 1;
- min-width: 0;
- border: 1px solid #003366;
- }
-
- .column-box.col-wide {
- flex: 2.5;
- }
-
- .column-box.col-normal {
- flex: 1;
- }
-
- /* 鏍囬鏍峰紡 - 绠�鍖栦笅鍒掔嚎 */
- .col-title {
- font-size: 22px;
- font-weight: bold;
- color: #4da6ff;
- padding-bottom: 8px;
- border-bottom: 1px solid #003366;
- margin-bottom: 10px;
- text-align: center;
- }
-
- .col-subtitle {
- font-size: 14px;
- color: #999;
- text-align: center;
- }
-
- /* 鎮h�呭垪琛� */
- .patient-list {
- flex: 1;
- overflow-y: auto;
- padding-right: 5px;
- }
-
- /* 銆愬叧閿慨鏀广�戠1鏍忓己鍒朵竴琛�2涓� */
- #col-0 {
- display: flex;
- flex-wrap: wrap;
- align-content: flex-start;
- }
-
- /* 绗�2-5鏍忎繚鎸佸崟鍒� */
- .col-normal .patient-list {
- display: flex;
- flex-direction: column;
- }
-
- /* 4. 鎮h�呴」鐩� - 鏋佺畝妯″紡 */
- .patient-item {
- font-size: 22px;
- padding: 8px 5px;
- display: flex;
- 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 {
- color: #ffcc00;
- font-weight: bold;
- margin-right: 8px;
- }
-
- .p-name {
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .p-room {
- color: #4da6ff;
- font-weight: bold;
- font-size: 18px;
- margin-left: 5px;
- }
-
- /* 5. 搴曢儴鏍� - 灞呬腑鏄剧ず */
- .bottom-footer {
- display: flex;
- justify-content: center;
- /* 灞呬腑 */
- align-items: center;
- padding: 10px 30px;
- background: rgba(0, 30, 60, 0.9);
- border-top: 1px solid #003366;
- }
-
- .footer-tip {
- font-size: 22px;
- color: #ffcc00;
- font-weight: bold;
- 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;
- bottom: 70px;
- width: 350px;
- height: 200px;
- background: rgba(0, 0, 0, 0.9);
- color: #0f0;
- border-radius: 4px;
- font-family: monospace;
- font-size: 12px;
- z-index: 9999;
- overflow: hidden;
- }
-
- .debug-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 5px;
- background: #333;
- border-bottom: 1px solid #000;
- }
-
- .debug-header span {
- font-weight: bold;
- font-size: 13px;
- }
-
- .debug-header button {
- background: #c00;
- color: #fff;
- border: none;
- padding: 2px 6px;
- font-size: 10px;
- cursor: pointer;
- }
-
- .debug-body {
- flex: 1;
- padding: 5px;
- overflow-y: auto;
- font-size: 11px;
- }
-
- .debug-line {
- margin-bottom: 2px;
- }
-
- .debug-time {
- color: #888;
- }
- </style>
-</head>
-
-<body>
- <!-- 椤堕儴鏍� -->
- <div class="top-header">
- <img src="logo.png" alt="logo" />
- <span class="title-text">鏈嶅姟澶у巺鎺掑垪</span>
- <span class="time-info" id="headerTime"></span>
- </div>
-
- <!-- 涓讳綋鍐呭 -->
- <div class="main-content" id="mainContent"></div>
-
- <!-- 搴曢儴鏍� -->
- <div class="bottom-footer">
- <!-- 娓╅Θ鎻愮ず璇眳涓� -->
- <div class="footer-tip">娓╅Θ鎻愮ず锛氳鍚埌鍛煎彨鍚庡墠寰�瀵瑰簲璇婂</div>
- </div>
-
- <!-- 璋冭瘯闈㈡澘 -->
- <div class="debug-panel" id="debugPanel">
- <div class="debug-header">
- <span>馃洜锔� 杩愯鏃ュ織</span>
- <button onclick="document.getElementById('debugBody').innerHTML=''">娓呯┖</button>
- </div>
- <div class="debug-body" id="debugBody"></div>
- </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>';
- console.log("[" + timeStr + "] " + msg);
- var $body = $("#debugBody");
- $body.append(logHtml);
- $body.scrollTop($body[0].scrollHeight);
- }
-
- // ================= 搴旂敤鐘舵�� =================
- var appState = {
- columnTitles: ["甯歌蹇冪數鍥�", "鍔ㄦ�佸績鐢�", "骞虫澘杩愬姩蹇冪數", "椋熼亾鐢电敓鐞�", "鍔ㄨ剦纭寲鐩戞祴"],
- columnSubTitles: ["搴婅竟蹇冪數鍥�(甯歌+棰戣氨)M / 蹇冪數鍚戦噺鍥綨", "鍔ㄦ�佽鍘婥", "", "", ""],
- patients: [],
- apiBaseUrl: "http://192.168.100.110/admin-api",
- pollTimer: null,
- callRepeatTimes: 2,
- spokenPatients: {},
- ttsQueue: [],
- isSpeaking: false
- };
-
- // ================= 璇煶鎾姤 =================
- function processTtsQueue() {
- if (appState.isSpeaking || appState.ttsQueue.length === 0) return;
- appState.isSpeaking = true;
- var text = appState.ttsQueue.shift();
- logDebug("馃攰 寮�濮嬫挱鎶�: " + text);
-
- var utterance = new SpeechSynthesisUtterance(text);
- utterance.onend = function () {
- appState.isSpeaking = false;
- processTtsQueue();
- };
- utterance.onerror = function () {
- appState.isSpeaking = false;
- processTtsQueue();
- };
-
- 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); }
- }
-
- if (window.speechSynthesis) { window.speechSynthesis.speak(utterance); }
- else { appState.isSpeaking = false; processTtsQueue(); }
- }
-
- function speak(text) {
- if (!text) return;
- appState.ttsQueue.push(text);
- processTtsQueue();
- }
-
- // ================= 娓叉煋鍑芥暟 =================
- function renderMainContent() {
- var $main = $("#mainContent");
- $main.empty();
-
- for (var i = 0; i < appState.columnTitles.length; i++) {
- var titleHtml = '<div class="col-title-line">' + appState.columnTitles[i] + '</div>';
- if (appState.columnSubTitles[i]) {
- titleHtml += '<div class="col-subtitle">' + appState.columnSubTitles[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);
- }
- }
-
- function renderPatients() {
- for (var i = 0; i < appState.columnTitles.length; i++) {
- $("#col-" + i).empty();
- }
-
- for (var c = 0; c < appState.patients.length; c++) {
- var colData = appState.patients[c];
- if (Array.isArray(colData)) {
- var $col = $("#col-" + c);
- for (var p = 0; p < colData.length; p++) {
- var pat = colData[p];
- var roomHtml = pat.roomName ? '<span class="p-room">(' + pat.roomName + ')</span>' : '';
- var itemHtml = '<div class="patient-item">' +
- '<span class="p-number">' + (pat.bookSeqNum || '') + '</span>' +
- '<span class="p-name">' + (pat.patName || '') + '</span>' +
- roomHtml +
- '</div>';
- $col.append(itemHtml);
- }
- }
- }
- }
-
- // ================= 鏁版嵁瀵规瘮涓庢挱鎶� =================
- function checkAndSpeakNewRooms(oldData, newData) {
- if (!oldData || oldData.length === 0) return;
- for (var c = 0; c < newData.length; c++) {
- var newCol = newData[c];
- var oldCol = oldData[c];
- if (Array.isArray(newCol)) {
- for (var p = 0; p < newCol.length; p++) {
- var newPat = newCol[p];
- if (newPat.roomName) {
- if (appState.spokenPatients[newPat.patId]) continue;
- var oldPat = null;
- if (Array.isArray(oldCol)) {
- for (var k = 0; k < oldCol.length; k++) {
- if (oldCol[k].patId === newPat.patId) {
- oldPat = oldCol[k];
- break;
- }
- }
- }
- if (!oldPat || !oldPat.roomName) {
- var repeatText = "";
- for (var r = 0; r < appState.callRepeatTimes; r++) {
- repeatText += "璇� " + newPat.bookSeqNum + " 鍙� " + newPat.patName + " 鍒� " + newPat.roomName + " 灏辫瘖銆�";
- }
- speak(repeatText);
- appState.spokenPatients[newPat.patId] = true;
- logDebug("馃敂 鍔犲叆鎾姤闃熷垪: " + newPat.patName);
- }
- }
- }
- }
- }
- }
-
- // ================= 鏁版嵁鑾峰彇 =================
- function fetchQueueData() {
- var url = appState.apiBaseUrl + "/ecg/screen/big-screen-data";
- $.ajax({
- url: url,
- type: "GET",
- dataType: "json",
- timeout: 5000,
- success: function (res) {
- var dataList = [];
- if (res && res.code === 0 && res.data) {
- for (var i = 0; i < appState.columnTitles.length; i++) {
- var key = i.toString();
- dataList[i] = (res.data[key] && Array.isArray(res.data[key])) ? res.data[key] : [];
- }
- }
- checkAndSpeakNewRooms(appState.patients, dataList);
- appState.patients = dataList;
- renderPatients();
-
- if (window.performance && window.performance.memory) {
- var usedMB = (window.performance.memory.usedJSHeapSize / 1048576).toFixed(2);
- logDebug("馃捑 鍐呭瓨: " + usedMB + " MB");
- }
- },
- error: function (err) {
- logDebug("鉂� 璇锋眰澶辫触: " + err.statusText);
- }
- });
- }
-
- // ================= 鍒濆鍖� =================
- $(document).ready(function () {
- renderMainContent();
- updateHeaderTime();
- setInterval(updateHeaderTime, 1000);
- fetchQueueData();
- appState.pollTimer = setInterval(fetchQueueData, 5000);
- logDebug("鉁� 绯荤粺鍚姩");
- });
-
- function updateHeaderTime() {
- var now = new Date();
- var weekDays = ["鏄熸湡鏃�", "鏄熸湡涓�", "鏄熸湡浜�", "鏄熸湡涓�", "鏄熸湡鍥�", "鏄熸湡浜�", "鏄熸湡鍏�"];
- 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);
- }
- </script>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/big1.html b/big1.html
new file mode 100644
index 0000000..848b08d
--- /dev/null
+++ b/big1.html
@@ -0,0 +1,680 @@
+锘�<!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, target-densitydpi=device-dpi" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <title>澶у巺</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ /* 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. 椤堕儴鏍� */
+ .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 img {
+ height: 50px;
+ margin-right: 20px;
+ z-index: 2;
+ }
+
+ .top-header .title-text {
+ position: absolute;
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ font-size: 32px;
+ font-weight: bold;
+ color: #fff;
+ z-index: 1;
+ }
+
+ .top-header .time-info {
+ font-size: 20px;
+ color: #aaa;
+ margin-left: auto;
+ z-index: 2;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-info-inner {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .time-left {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-right {
+ margin-left: 8px;
+ }
+
+ .top-header .time-info .time-clock {
+ font-size: 36px;
+ font-weight: bold;
+ color: #ffcc00;
+ line-height: 1.1;
+ }
+
+ /* 3. 涓讳綋鍐呭锛坓ap 鏇挎崲涓� margin 鍏煎鏃� Chrome锛� */
+ .main-content {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ padding: 15px 20px;
+ overflow: hidden;
+ }
+
+ /* 鍒楄瀹瑰櫒 */
+ .columns-row {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ overflow: hidden;
+ }
+
+ .columns-row>.column-box {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+
+ .columns-row>.column-box:first-child {
+ margin-left: 0;
+ }
+
+ .columns-row>.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 {
+ padding-bottom: 8px;
+ border-bottom: 1px solid #003366;
+ margin-bottom: 10px;
+ text-align: center;
+ }
+
+ .col-subtitle {
+ font-size: 14px;
+ color: #999;
+ text-align: center;
+ min-height: 20px;
+ /* 鍗充娇涓虹┖涔熶繚鐣欓珮搴︼紝闃叉鏍囬鏍忛珮浣庝笉骞� */
+ ;
+ line-height: 1.4;
+ word-break: keep-all;
+ }
+
+ /* 鎮h�呭垪琛� */
+ .patient-list {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ overflow-y: auto;
+ padding-right: 5px;
+ }
+
+ /* 绗�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;
+ }
+
+ .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;
+ }
+
+ /* 鎮h�呴」鐩� */
+ .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;
+ }
+
+ #col-0 .patient-item {
+ width: 45%;
+ font-size: 20px;
+ margin: 0 2.5%;
+ }
+
+ .p-number {
+ color: #ffcc00;
+ font-weight: bold;
+ margin-right: 8px;
+ }
+
+ .p-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .p-name-wrap {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .p-room {
+ color: #4da6ff;
+ font-weight: bold;
+ font-size: 18px;
+ margin-left: 4px;
+ }
+
+ /* 杩囧彿鎮h�咃細鎺掑湪鍒楀唴锛岃瑙夊急鍖� */
+ .patient-item.missed {
+ color: #aa8888;
+ }
+
+ .patient-item.missed .p-number {
+ color: #cc9966;
+ }
+
+ .patient-item.missed .p-missed-tag {
+ color: #ff6666;
+ font-size: 16px;
+ margin-left: 4px;
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
+ }
+
+ /* 搴曢儴鏍� */
+ .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);
+ border-top: 1px solid #003366;
+ }
+
+ .footer-tip {
+ font-size: 22px;
+ color: #ffcc00;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ /* 婊氬姩鏉� */
+ .patient-list::-webkit-scrollbar {
+ width: 4px;
+ }
+
+ .patient-list::-webkit-scrollbar-thumb {
+ background: #444;
+ border-radius: 2px;
+ }
+
+ /* 璋冭瘯闈㈡澘 */
+ .debug-panel {
+ display: none; /* 闇�瑕佽皟璇曟椂鍒犳帀姝よ鍗冲彲鏄剧ず */
+ position: fixed;
+ right: 15px;
+ bottom: 70px;
+ width: 350px;
+ height: 200px;
+ background: rgba(0, 0, 0, 0.9);
+ color: #0f0;
+ border-radius: 4px;
+ font-family: monospace;
+ font-size: 12px;
+ z-index: 9999;
+ overflow: visible;
+ }
+
+ .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;
+ border-bottom: 1px solid #000;
+ }
+
+ .debug-header span {
+ font-weight: bold;
+ font-size: 13px;
+ }
+
+ .debug-header button {
+ background: #c00;
+ color: #fff;
+ border: none;
+ padding: 2px 6px;
+ font-size: 10px;
+ cursor: pointer;
+ }
+
+ .debug-body {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ padding: 5px;
+ overflow-y: auto;
+ font-size: 11px;
+ }
+
+ .debug-line {
+ margin-bottom: 2px;
+ }
+
+ .debug-time {
+ color: #888;
+ }
+ </style>
+</head>
+
+<body>
+ <!-- 椤堕儴鏍� -->
+ <div class="top-header">
+ <img src="logo.png" alt="logo" />
+ <span class="title-text">蹇冪數妫�鏌ユ湇鍔¤瘖鍖哄ぇ鍘�</span>
+ <span class="time-info" id="headerTime"></span>
+ </div>
+
+ <!-- 涓讳綋鍐呭 -->
+ <div class="main-content" id="mainContent"></div>
+
+ <!-- 搴曢儴鏍� -->
+ <div class="bottom-footer">
+ <div class="footer-tip">娓╅Θ鎻愮ず锛氬惉鍒板懠鍙悗璇峰墠寰�瀵瑰簲璇婂锛岃繃鍙锋偅鑰呭埌瀵艰瘖鍙板鐞嗭紒</div>
+ </div>
+
+ <!-- 璋冭瘯闈㈡澘 -->
+ <div class="debug-panel" id="debugPanel" >
+ <div class="debug-header">
+ <span>[璋冭瘯] 杩愯鏃ュ織</span>
+ <button onclick="document.getElementById('debugBody').innerHTML=''">娓呯┖</button>
+ </div>
+ <div class="debug-body" id="debugBody"></div>
+ </div>
+
+ <script src="./static/jquery.min.js"></script>
+ <script>
+ // ================= 璋冭瘯鏃ュ織 =================
+ function logDebug(msg) {
+ var now = new Date();
+ 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 = document.getElementById("debugBody");
+ if (body) {
+ body.insertAdjacentHTML("beforeend", logHtml);
+ body.scrollTop = body.scrollHeight;
+ }
+ }
+
+ // ================= 搴旂敤鐘舵�� =================
+ // 鍛煎彨鍙彿娆℃暟鍙傛暟锛氬懠鍙偅鑰呭氨璇婃椂閲嶅鎾姤鐨勬鏁帮紝榛樿 2 娆�
+ var CALL_REPEAT_TIMES = 2;
+ var appState = {
+ columnTitles: ["甯歌蹇冪數鍥�", "鍔ㄦ�佸績鐢靛浘/琛�鍘�", "蹇冪數鍥捐繍鍔ㄨ瘯楠�", "椋熼亾鐢电敓鐞�", "鍔ㄨ剦纭寲鐩戞祴"],
+ columnSubTitles: ["搴婅竟蹇冪數鍥�(甯歌+棰戣氨) / 蹇冪數鍚戦噺鍥�", "", "", "", ""],
+ patients: [],
+ apiBaseUrl: "http://localhost/admin-api",
+ // apiBaseUrl: "http://10.0.2.193/admin-api",
+ pollTimer: null,
+ spokenPatients: {},
+ ttsQueue: [],
+ isSpeaking: false,
+ };
+
+ var patStatus = {
+ 3: "宸茶繃鍙�-鎺掗槦", 5: "宸茶繃鍙�", 7: "宸茶繃鍙�-瀹夎", 10: "鎺掗槦涓�", 12: "浜插拰",
+ 13: "浜插拰-瀹夎", 15: "宸插彫鍥�", 20: "鍊欒瘖涓�", 30: "灏辫瘖涓�", 33: "宸查鐢�",
+ 34: "宸插彫鍥�-瀹夎", 36: "瀹夎涓�", 40: "宸插氨璇�"
+ };
+
+ // ================= 璇煶鎾姤 =================
+ function processTtsQueue() {
+ if (appState.isSpeaking || appState.ttsQueue.length === 0) return;
+ appState.isSpeaking = true;
+ var text = appState.ttsQueue.shift();
+ logDebug("[鎾姤] " + text);
+
+ var utterance = new SpeechSynthesisUtterance(text);
+ // Android 6 榛樿璇�熷彲鑳藉緢蹇紝閫傚綋璋冩參
+ utterance.rate = 0.85;
+ utterance.onend = function () {
+ appState.isSpeaking = false;
+ processTtsQueue();
+ };
+ 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); }
+ }
+
+ 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) {
+ if (!text) return;
+ appState.ttsQueue.push(text);
+ processTtsQueue();
+ }
+
+ // ================= 娓叉煋锛氭爣棰樻鏋讹紙浠呭垵濮嬪寲涓�娆★級=================
+ function initLayout() {
+ var main = document.getElementById("mainContent");
+ var html = '<div class="columns-row">';
+
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ // 鏍囬鍜屽壇鏍囬閮芥湁鍥哄畾鍗犱綅锛岄伩鍏嶆湁/鏃犲壇鏍囬鏃堕珮搴﹁烦鍔�
+ var subtitle = appState.columnSubTitles[i] || " ";
+ var colClass = (i === 0) ? 'col-wide' : 'col-normal';
+ html += '<div class="column-box ' + colClass + '">' +
+ '<div class="col-title">' +
+ '<div class="col-title-line">' + appState.columnTitles[i] + '</div>' +
+ '<div class="col-subtitle">' + subtitle + '</div>' +
+ '</div>' +
+ '<div class="patient-list" id="col-' + i + '"></div>' +
+ '</div>';
+ }
+ html += '</div>';
+ main.innerHTML = html;
+ }
+
+ // ================= 娓叉煋锛氭偅鑰呭垪琛紙diff 鏇存柊锛屽彧鏀瑰彉鍖栫殑鍒楋級=================
+ function desensitizeName(name) {
+ if (!name || name.length < 2) return name || '';
+ return name.charAt(0) + '*' + name.substring(2);
+ }
+
+ function isMissedStatus(status) {
+ var s = parseInt(status, 10);
+ return s === 3 || s === 5 || s === 7;
+ }
+
+ function buildColumnHtml(colData) {
+ if (!Array.isArray(colData) || colData.length === 0) return "";
+ var h = "";
+ for (var p = 0; p < colData.length; p++) {
+ var pat = colData[p];
+ var missedClass = isMissedStatus(pat.status) ? ' missed' : '';
+ var roomHtml = (!isMissedStatus(pat.status) && pat.roomName) ? '<span class="p-room">(' + pat.roomName + ')</span>' : '';
+ h += '<div class="patient-item' + missedClass + '">' +
+ '<span class="p-number">' + (pat.seqNum || '') + '</span>' +
+ '<span class="p-name-wrap"><span class="p-name">' + desensitizeName(pat.patName) + '</span>' + roomHtml +
+ (missedClass ? '<span class="p-missed-tag">锛堣繃鍙凤級</span>' : '') + '</span>' +
+ '</div>';
+ }
+ return h;
+ }
+ function isSameColumn(a, b) {
+ if (!Array.isArray(a) || !Array.isArray(b)) return a === b;
+ if (a.length !== b.length) return false;
+ for (var i = 0; i < a.length; i++) {
+ if (a[i].patId !== b[i].patId) return false;
+ if (a[i].roomName !== b[i].roomName) return false;
+ if (parseInt(a[i].status, 10) !== parseInt(b[i].status, 10)) return false;
+ }
+ return true;
+ }
+
+ function updatePatients(dataList) {
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var newData = (dataList && dataList[i]) ? dataList[i] : [];
+ var oldData = appState.patients[i];
+
+ // diff锛氭暟鎹湭鍙樺寲鍒欒烦杩囪鍒� DOM 鎿嶄綔
+ if (isSameColumn(oldData, newData)) continue;
+
+ var col = document.getElementById("col-" + i);
+ if (col) col.innerHTML = buildColumnHtml(newData);
+ }
+ }
+
+ // ================= 鏁版嵁瀵规瘮涓庢挱鎶� =================
+ function checkAndSpeakNewRooms(oldData, newData) {
+ if (!oldData || oldData.length === 0) return;
+ for (var c = 0; c < newData.length; c++) {
+ var newCol = newData[c];
+ var oldCol = oldData[c];
+ if (Array.isArray(newCol)) {
+ for (var p = 0; p < newCol.length; p++) {
+ var newPat = newCol[p];
+ if (newPat.roomName) {
+ if (appState.spokenPatients[newPat.patId]) continue;
+ var oldPat = null;
+ if (Array.isArray(oldCol)) {
+ for (var k = 0; k < oldCol.length; k++) {
+ if (oldCol[k].patId === newPat.patId) {
+ oldPat = oldCol[k];
+ break;
+ }
+ }
+ }
+ if (!oldPat || !oldPat.roomName) {
+ var repeatText = "";
+ repeatText += "璇� " + newPat.seqNum + " 鍙� " + newPat.patName + " 鍒� " + newPat.roomName + " 灏辫瘖銆�";
+ for (var r = 0; r < CALL_REPEAT_TIMES; r++) {
+ speak(repeatText);
+ }
+ appState.spokenPatients[newPat.patId] = true;
+ logDebug("[鎺掗槦] " + newPat.patName + " -> " + newPat.roomName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // ================= 鏁版嵁鑾峰彇 =================
+ function fetchQueueData() {
+ var url = appState.apiBaseUrl + "/ecg/screen/big-screen-data";
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+ timeout: 5000,
+ success: function (res) {
+ var dataList = [];
+ if (res && res.code === 0 && res.data) {
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var key = i.toString();
+ dataList[i] = (res.data[key] && Array.isArray(res.data[key])) ? res.data[key] : [];
+ }
+ }
+ // 鍏� diff 鏇存柊 DOM锛屽啀鎾姤锛屾渶鍚庝繚瀛樻暟鎹敤浜庝笅娆″姣�
+ updatePatients(dataList);
+ checkAndSpeakNewRooms(appState.patients, dataList);
+ appState.patients = dataList;
+
+ // 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 || "缃戠粶閿欒"));
+ }
+ });
+ }
+
+ // ================= 鍒濆鍖� =================
+ function onReady() {
+ initLayout();
+ updateHeaderTime();
+ setInterval(updateHeaderTime, 1000);
+ fetchQueueData();
+ appState.pollTimer = setInterval(fetchQueueData, 5000);
+ logDebug("[绯荤粺] 鍚姩瀹屾垚 - Android 6.0.1");
+ }
+
+ function updateHeaderTime() {
+ var now = new Date();
+ var weekDays = ["鏄熸湡鏃�", "鏄熸湡涓�", "鏄熸湡浜�", "鏄熸湡涓�", "鏄熸湡鍥�", "鏄熸湡浜�", "鏄熸湡鍏�"];
+ function padZero(num) { return num < 10 ? '0' + num : '' + num; }
+ var dateStr = now.getFullYear() + "骞�" + padZero(now.getMonth() + 1) + "鏈�" + padZero(now.getDate()) + "鏃�";
+ var weekStr = weekDays[now.getDay()];
+ var timeStr = padZero(now.getHours()) + ":" + padZero(now.getMinutes());
+ var el = document.getElementById("headerTime");
+ // 鏄熸湡鍜屾棩鏈熷悇鍗犱竴琛屽湪宸︼紝鏃堕棿澶у瓧鍦ㄥ彸璺ㄤ袱琛�
+ if (el) el.innerHTML = '<div class="time-info-inner"><div class="time-left"><div>' + weekStr + '</div><div>' + dateStr + '</div></div><div class="time-right"><span class="time-clock">' + timeStr + '</span></div></div>';
+ }
+
+ // 鍏煎 DOM ready锛圓ndroid 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>
\ No newline at end of file
diff --git "a/big\346\273\232\345\212\250.html" "b/big\346\273\232\345\212\250.html"
new file mode 100644
index 0000000..c4b8d04
--- /dev/null
+++ "b/big\346\273\232\345\212\250.html"
@@ -0,0 +1,823 @@
+<!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, target-densitydpi=device-dpi" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <title>澶у巺</title>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ /* 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. 椤堕儴鏍� */
+ .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 img {
+ height: 50px;
+ margin-right: 20px;
+ z-index: 2;
+ }
+
+ .top-header .title-text {
+ position: absolute;
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ font-size: 32px;
+ font-weight: bold;
+ color: #fff;
+ z-index: 1;
+ }
+
+ .top-header .time-info {
+ font-size: 20px;
+ color: #aaa;
+ margin-left: auto;
+ z-index: 2;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-info-inner {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ }
+
+ .time-left {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ text-align: right;
+ line-height: 1.3;
+ }
+
+ .time-right {
+ margin-left: 8px;
+ }
+
+ .top-header .time-info .time-clock {
+ font-size: 36px;
+ font-weight: bold;
+ color: #ffcc00;
+ line-height: 1.1;
+ }
+
+ /* 3. 涓讳綋鍐呭锛坓ap 鏇挎崲涓� margin 鍏煎鏃� Chrome锛� */
+ .main-content {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ padding: 15px 20px;
+ overflow: hidden;
+ }
+
+ /* 鍒楄瀹瑰櫒 */
+ .columns-row {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ overflow: hidden;
+ }
+
+ .columns-row > .column-box {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+
+ .columns-row > .column-box:first-child {
+ margin-left: 0;
+ }
+
+ .columns-row > .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 {
+ padding-bottom: 8px;
+ border-bottom: 1px solid #003366;
+ margin-bottom: 10px;
+ text-align: center;
+ }
+
+ .col-subtitle {
+ font-size: 14px;
+ color: #999;
+ text-align: center;
+ min-height: 20px;
+ /* 鍗充娇涓虹┖涔熶繚鐣欓珮搴︼紝闃叉鏍囬鏍忛珮浣庝笉骞� */;
+ line-height: 1.4;
+ word-break: keep-all;
+ }
+
+ /* 鎮h�呭垪琛� */
+ .patient-list {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ overflow-y: auto;
+ padding-right: 5px;
+ }
+
+ /* 绗�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;
+ }
+
+ .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;
+ }
+
+ /* 鎮h�呴」鐩� */
+ .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;
+ }
+
+ #col-0 .patient-item {
+ width: 45%;
+ font-size: 20px;
+ margin: 0 2.5%;
+ }
+
+ .p-number {
+ color: #ffcc00;
+ font-weight: bold;
+ margin-right: 8px;
+ }
+
+ .p-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .p-name-wrap {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .p-room {
+ color: #4da6ff;
+ font-weight: bold;
+ font-size: 18px;
+ margin-left: 4px;
+ }
+
+ /* 杩囧彿鏍忥紙鍥哄畾鍦� main-content 搴曢儴锛岃嚜鍔ㄦ粴鍔級 */
+ .missed-bar {
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-box-align: center;
+ -webkit-align-items: center;
+ align-items: center;
+ background: rgba(80, 20, 20, 0.7);
+ border: 1px solid #993333;
+ padding: 6px 10px;
+ margin-top: 8px;
+ }
+
+ .missed-bar-empty {
+ display: none;
+ }
+
+ .missed-bar-title {
+ -webkit-flex-shrink: 0;
+ flex-shrink: 0;
+ color: #ff6666;
+ font-size: 18px;
+ font-weight: bold;
+ margin-right: 16px;
+ line-height: 30px;
+ }
+
+ /* 婊氬姩瑁佸壀鍖� */
+ .missed-bar-scroll-area {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+
+ /* 婊氬姩杞ㄩ亾 */
+ .missed-bar-track {
+ display: inline-block;
+ white-space: nowrap;
+ will-change: transform;
+ }
+
+ .missed-bar-scroll .missed-bar-track {
+ /* 鍔ㄧ敾鏃堕暱鐢� JS 鍔ㄦ�佽缃紝瑙� updateMissedBarSpeed */
+ -webkit-animation-name: missed-scroll;
+ animation-name: missed-scroll;
+ -webkit-animation-timing-function: linear;
+ animation-timing-function: linear;
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite;
+ }
+
+ @-webkit-keyframes missed-scroll {
+ 0% { -webkit-transform: translateX(0); transform: translateX(0); }
+ 100% { -webkit-transform: translateX(-50%); transform: translateX(-50%); }
+ }
+ @keyframes missed-scroll {
+ 0% { transform: translateX(0); }
+ 100% { transform: translateX(-50%); }
+ }
+
+ .missed-bar .patient-item {
+ display: -webkit-inline-box;
+ display: -webkit-inline-flex;
+ display: inline-flex;
+ font-size: 17px;
+ padding: 3px 12px;
+ margin-right: 8px;
+ border-right: 1px solid #664444;
+ color: #cc9999;
+ }
+
+ .missed-bar .patient-item:last-child {
+ border-right: none;
+ }
+
+ .missed-bar .p-number {
+ color: #ff9966;
+ font-weight: bold;
+ margin-right: 6px;
+ }
+
+ .missed-bar .p-name {
+ color: #bbaaaa;
+ }
+
+ .missed-bar .p-room {
+ color: #ff6666;
+ font-size: 15px;
+ }
+
+ /* 搴曢儴鏍� */
+ .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);
+ border-top: 1px solid #003366;
+ }
+
+ .footer-tip {
+ font-size: 22px;
+ color: #ffcc00;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ /* 婊氬姩鏉� */
+ .patient-list::-webkit-scrollbar {
+ width: 4px;
+ }
+ .patient-list::-webkit-scrollbar-thumb {
+ background: #444;
+ border-radius: 2px;
+ }
+
+ /* 璋冭瘯闈㈡澘 */
+ .debug-panel {
+ position: fixed;
+ right: 15px;
+ bottom: 70px;
+ width: 350px;
+ height: 200px;
+ background: rgba(0, 0, 0, 0.9);
+ color: #0f0;
+ border-radius: 4px;
+ font-family: monospace;
+ font-size: 12px;
+ z-index: 9999;
+ overflow: hidden;
+ }
+
+ .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;
+ border-bottom: 1px solid #000;
+ }
+
+ .debug-header span {
+ font-weight: bold;
+ font-size: 13px;
+ }
+
+ .debug-header button {
+ background: #c00;
+ color: #fff;
+ border: none;
+ padding: 2px 6px;
+ font-size: 10px;
+ cursor: pointer;
+ }
+
+ .debug-body {
+ -webkit-box-flex: 1;
+ -webkit-flex: 1;
+ flex: 1;
+ padding: 5px;
+ overflow-y: auto;
+ font-size: 11px;
+ }
+
+ .debug-line {
+ margin-bottom: 2px;
+ }
+
+ .debug-time {
+ color: #888;
+ }
+ </style>
+</head>
+
+<body>
+ <!-- 椤堕儴鏍� -->
+ <div class="top-header">
+ <img src="logo.png" alt="logo" />
+ <span class="title-text">蹇冪數妫�鏌ユ湇鍔¤瘖鍖哄ぇ鍘�</span>
+ <span class="time-info" id="headerTime"></span>
+ </div>
+
+ <!-- 涓讳綋鍐呭 -->
+ <div class="main-content" id="mainContent"></div>
+
+ <!-- 搴曢儴鏍� -->
+ <div class="bottom-footer">
+ <div class="footer-tip">娓╅Θ鎻愮ず锛氳鍚埌鍛煎彨鍚庡墠寰�瀵瑰簲璇婂锛岃繃鍙锋偅鑰呰鍒板璇婂彴澶勭悊锛�</div>
+ </div>
+
+ <!-- 璋冭瘯闈㈡澘 -->
+ <div class="debug-panel" id="debugPanel">
+ <div class="debug-header">
+ <span>[璋冭瘯] 杩愯鏃ュ織</span>
+ <button onclick="document.getElementById('debugBody').innerHTML=''">娓呯┖</button>
+ </div>
+ <div class="debug-body" id="debugBody"></div>
+ </div>
+
+ <script src="./static/jquery.min.js"></script>
+ <script>
+ // ================= 璋冭瘯鏃ュ織 =================
+ function logDebug(msg) {
+ var now = new Date();
+ 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 = document.getElementById("debugBody");
+ if (body) {
+ body.insertAdjacentHTML("beforeend", logHtml);
+ body.scrollTop = body.scrollHeight;
+ }
+ }
+
+ // ================= 搴旂敤鐘舵�� =================
+ var appState = {
+ columnTitles: ["甯歌蹇冪數鍥�", "鍔ㄦ�佸績鐢靛浘/琛�鍘�", "蹇冪數鍥捐繍鍔ㄨ瘯楠�", "椋熼亾鐢电敓鐞�", "鍔ㄨ剦纭寲鐩戞祴"],
+ columnSubTitles: ["搴婅竟蹇冪數鍥�(甯歌+棰戣氨) / 蹇冪數鍚戦噺鍥�", "", "", "", ""],
+ patients: [],
+ apiBaseUrl: "http://localhost/admin-api",
+ // apiBaseUrl: "http://192.168.100.110/admin-api",
+ pollTimer: null,
+ callRepeatTimes: 2,
+ spokenPatients: {},
+ ttsQueue: [],
+ isSpeaking: false,
+ // 杩囧彿鎮h�呮粴鍔ㄩ�熷害閰嶇疆锛氭暟鍊间负鍔ㄧ敾鍛ㄦ湡绉掓暟锛岃秺澶ц秺鎱�
+ missedScrollSpeedSlowest: 10, // 鏈�鎱㈤�熷害锛堟偅鑰呭皯鏃朵娇鐢紝绉掓暟澶�=鎱級
+ missedScrollSpeedFastest: 5 // 鏈�蹇�熷害锛堟偅鑰呭鏃朵笂闄愶紝绉掓暟灏�=蹇級
+ };
+
+ // ================= 璇煶鎾姤 =================
+ function processTtsQueue() {
+ if (appState.isSpeaking || appState.ttsQueue.length === 0) return;
+ appState.isSpeaking = true;
+ var text = appState.ttsQueue.shift();
+ logDebug("[鎾姤] " + text);
+
+ var utterance = new SpeechSynthesisUtterance(text);
+ // Android 6 榛樿璇�熷彲鑳藉緢蹇紝閫傚綋璋冩參
+ utterance.rate = 0.85;
+ utterance.onend = function () {
+ appState.isSpeaking = false;
+ processTtsQueue();
+ };
+ 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); }
+ }
+
+ 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) {
+ if (!text) return;
+ appState.ttsQueue.push(text);
+ processTtsQueue();
+ }
+
+ // ================= 娓叉煋锛氭爣棰樻鏋讹紙浠呭垵濮嬪寲涓�娆★級=================
+ function initLayout() {
+ var main = document.getElementById("mainContent");
+ var html = '<div class="columns-row">';
+
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ // 鏍囬鍜屽壇鏍囬閮芥湁鍥哄畾鍗犱綅锛岄伩鍏嶆湁/鏃犲壇鏍囬鏃堕珮搴﹁烦鍔�
+ var subtitle = appState.columnSubTitles[i] || " ";
+ var colClass = (i === 0) ? 'col-wide' : 'col-normal';
+ html += '<div class="column-box ' + colClass + '">' +
+ '<div class="col-title">' +
+ '<div class="col-title-line">' + appState.columnTitles[i] + '</div>' +
+ '<div class="col-subtitle">' + subtitle + '</div>' +
+ '</div>' +
+ '<div class="patient-list" id="col-' + i + '"></div>' +
+ '</div>';
+ }
+ html += '</div>';
+ html += '<div class="missed-bar missed-bar-empty" id="missedBar">' +
+ '<span class="missed-bar-title">杩囧彿鎮h��</span>' +
+ '<div class="missed-bar-scroll-area"><div class="missed-bar-track" id="missedBarTrack"></div></div>' +
+ '</div>';
+ main.innerHTML = html;
+ }
+
+ // ================= 娓叉煋锛氭偅鑰呭垪琛紙diff 鏇存柊锛屽彧鏀瑰彉鍖栫殑鍒楋級=================
+ function desensitizeName(name) {
+ if (!name || name.length < 2) return name || '';
+ return name.charAt(0) + '*' + name.substring(2);
+ }
+
+ function isMissedStatus(status) {
+ var s = parseInt(status, 10);
+ return s === 3 || s === 5 || s === 7;
+ }
+
+ function buildColumnHtml(colData) {
+ if (!Array.isArray(colData) || colData.length === 0) return "";
+ var h = "";
+ for (var p = 0; p < colData.length; p++) {
+ var pat = colData[p];
+ // 杩囧彿鎮h�呬笉鏄剧ず鍦ㄥ垪鍐咃紝缁熶竴鍦ㄥ簳閮ㄨ繃鍙锋爮灞曠ず
+ if (isMissedStatus(pat.status)) continue;
+ var roomHtml = pat.roomName ? '<span class="p-room">(' + pat.roomName + ')</span>' : '';
+ h += '<div class="patient-item">' +
+ '<span class="p-number">' + (pat.seqNum || '') + '</span>' +
+ '<span class="p-name-wrap"><span class="p-name">' + desensitizeName(pat.patName) + '</span>' + roomHtml + '</span>' +
+ '</div>';
+ }
+ return h;
+ }
+
+ function buildMissedBarHtml(dataList) {
+ var allMissed = [];
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var colData = (dataList && dataList[i]) ? dataList[i] : [];
+ for (var p = 0; p < colData.length; p++) {
+ var pat = colData[p];
+ if (isMissedStatus(pat.status)) {
+ allMissed.push({ patient: pat, colIndex: i });
+ }
+ }
+ }
+ if (allMissed.length === 0) return "";
+ var h = "";
+ for (var m = 0; m < allMissed.length; m++) {
+ var item = allMissed[m];
+ var pat = item.patient;
+ var colName = appState.columnTitles[item.colIndex];
+ var roomHtml = pat.roomName ? ' <span class="p-room">' + pat.roomName + '</span>' : '';
+ h += '<span class="patient-item">' +
+ '<span class="p-number">[' + colName + '] ' + (pat.seqNum || '') + '</span>' +
+ '<span class="p-name">' + desensitizeName(pat.patName) + '</span>' + roomHtml +
+ '</span>';
+ }
+ // 澶嶅埗涓�浠藉唴瀹瑰疄鐜版棤缂濆惊鐜粴鍔�
+ return h + h;
+ }
+
+ function isSameColumn(a, b) {
+ if (!Array.isArray(a) || !Array.isArray(b)) return a === b;
+ if (a.length !== b.length) return false;
+ for (var i = 0; i < a.length; i++) {
+ if (a[i].patId !== b[i].patId) return false;
+ if (a[i].roomName !== b[i].roomName) return false;
+ if (parseInt(a[i].status, 10) !== parseInt(b[i].status, 10)) return false;
+ }
+ return true;
+ }
+
+ function updatePatients(dataList) {
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var newData = (dataList && dataList[i]) ? dataList[i] : [];
+ var oldData = appState.patients[i];
+
+ // diff锛氭暟鎹湭鍙樺寲鍒欒烦杩囪鍒� DOM 鎿嶄綔
+ if (isSameColumn(oldData, newData)) continue;
+
+ var col = document.getElementById("col-" + i);
+ if (col) col.innerHTML = buildColumnHtml(newData);
+ }
+
+ // 鏇存柊搴曢儴杩囧彿鏍�
+ var missedBar = document.getElementById("missedBar");
+ var missedTrack = document.getElementById("missedBarTrack");
+ if (missedBar && missedTrack) {
+ var missedHtml = buildMissedBarHtml(dataList);
+ if (missedHtml) {
+ missedTrack.innerHTML = missedHtml;
+ missedBar.className = "missed-bar missed-bar-scroll";
+ // 鏍规嵁杩囧彿鎮h�呬汉鏁板姩鎬佽皟鏁存粴鍔ㄩ�熷害
+ updateMissedBarSpeed(missedTrack, dataList);
+ } else {
+ missedBar.className = "missed-bar missed-bar-empty";
+ }
+ }
+ }
+
+ // ================= 杩囧彿鏍忔粴鍔ㄩ�熷害 =================
+ // 缁熻杩囧彿鎮h�呮�绘暟锛屽湪 0 鍒� 20 浜轰箣闂寸嚎鎬ф彃鍊煎姩鐢绘椂闀�
+ // 浜哄皯鏃剁敤鏈�鎱㈤�熷害锛堢鏁板ぇ锛夛紝浜哄鏃堕�艰繎鏈�蹇�熷害锛堢鏁板皬锛夛紝鏈�澶� 20 浜鸿揪鍒颁笂闄�
+ function updateMissedBarSpeed(trackEl, dataList) {
+ var count = 0;
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var colData = (dataList && dataList[i]) ? dataList[i] : [];
+ for (var p = 0; p < colData.length; p++) {
+ if (isMissedStatus(colData[p].status)) count++;
+ }
+ }
+ if (count === 0) return;
+
+ var slowest = appState.missedScrollSpeedSlowest;
+ var fastest = appState.missedScrollSpeedFastest;
+ // 浜烘暟闃堝�硷細瓒呰繃姝や汉鏁板嵆浣跨敤鏈�蹇�熷害
+ var maxCount = 20;
+ // 绾挎�ф彃鍊硷細count=1 -> slowest, count>=maxCount -> fastest
+ var ratio = Math.min(count, maxCount) / maxCount;
+ var duration = slowest - (slowest - fastest) * ratio;
+ // 鍙栨暣鍒颁竴浣嶅皬鏁帮紝鍏煎叿绮惧害鍜岀畝娲�
+ duration = Math.round(duration * 10) / 10;
+
+ var animValue = 'missed-scroll ' + duration + 's linear infinite';
+ trackEl.style.webkitAnimation = animValue;
+ trackEl.style.animation = animValue;
+ }
+
+ // ================= 鏁版嵁瀵规瘮涓庢挱鎶� =================
+ function checkAndSpeakNewRooms(oldData, newData) {
+ if (!oldData || oldData.length === 0) return;
+ for (var c = 0; c < newData.length; c++) {
+ var newCol = newData[c];
+ var oldCol = oldData[c];
+ if (Array.isArray(newCol)) {
+ for (var p = 0; p < newCol.length; p++) {
+ var newPat = newCol[p];
+ if (newPat.roomName) {
+ if (appState.spokenPatients[newPat.patId]) continue;
+ var oldPat = null;
+ if (Array.isArray(oldCol)) {
+ for (var k = 0; k < oldCol.length; k++) {
+ if (oldCol[k].patId === newPat.patId) {
+ oldPat = oldCol[k];
+ break;
+ }
+ }
+ }
+ if (!oldPat || !oldPat.roomName) {
+ var repeatText = "";
+ for (var r = 0; r < appState.callRepeatTimes; r++) {
+ repeatText += "璇� " + newPat.seqNum + " 鍙� " + newPat.patName + " 鍒� " + newPat.roomName + " 灏辫瘖銆�";
+ }
+ speak(repeatText);
+ appState.spokenPatients[newPat.patId] = true;
+ logDebug("[鎺掗槦] " + newPat.patName + " -> " + newPat.roomName);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // ================= 鏁版嵁鑾峰彇 =================
+ function fetchQueueData() {
+ var url = appState.apiBaseUrl + "/ecg/screen/big-screen-data";
+ $.ajax({
+ url: url,
+ type: "GET",
+ dataType: "json",
+ timeout: 5000,
+ success: function (res) {
+ var dataList = [];
+ if (res && res.code === 0 && res.data) {
+ for (var i = 0; i < appState.columnTitles.length; i++) {
+ var key = i.toString();
+ dataList[i] = (res.data[key] && Array.isArray(res.data[key])) ? res.data[key] : [];
+ }
+ }
+ // 鍏� diff 鏇存柊 DOM锛屽啀鎾姤锛屾渶鍚庝繚瀛樻暟鎹敤浜庝笅娆″姣�
+ updatePatients(dataList);
+ checkAndSpeakNewRooms(appState.patients, dataList);
+ appState.patients = dataList;
+
+ // 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 || "缃戠粶閿欒"));
+ }
+ });
+ }
+
+ // ================= 鍒濆鍖� =================
+ function onReady() {
+ initLayout();
+ updateHeaderTime();
+ setInterval(updateHeaderTime, 1000);
+ fetchQueueData();
+ appState.pollTimer = setInterval(fetchQueueData, 5000);
+ logDebug("[绯荤粺] 鍚姩瀹屾垚 - Android 6.0.1");
+ }
+
+ function updateHeaderTime() {
+ var now = new Date();
+ var weekDays = ["鏄熸湡鏃�", "鏄熸湡涓�", "鏄熸湡浜�", "鏄熸湡涓�", "鏄熸湡鍥�", "鏄熸湡浜�", "鏄熸湡鍏�"];
+ function padZero(num) { return num < 10 ? '0' + num : '' + num; }
+ var dateStr = now.getFullYear() + "骞�" + padZero(now.getMonth() + 1) + "鏈�" + padZero(now.getDate()) + "鏃�";
+ var weekStr = weekDays[now.getDay()];
+ var timeStr = padZero(now.getHours()) + ":" + padZero(now.getMinutes());
+ var el = document.getElementById("headerTime");
+ // 鏄熸湡鍜屾棩鏈熷悇鍗犱竴琛屽湪宸︼紝鏃堕棿澶у瓧鍦ㄥ彸璺ㄤ袱琛�
+ if (el) el.innerHTML = '<div class="time-info-inner"><div class="time-left"><div>' + weekStr + '</div><div>' + dateStr + '</div></div><div class="time-right"><span class="time-clock">' + timeStr + '</span></div></div>';
+ }
+
+ // 鍏煎 DOM ready锛圓ndroid 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>
diff --git a/logo0.png b/logo0.png
new file mode 100644
index 0000000..35e084e
--- /dev/null
+++ b/logo0.png
Binary files differ
diff --git a/small.html b/small.html
index e3515a3..9a166ec 100644
--- a/small.html
+++ b/small.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+锘�<!DOCTYPE html>
<html lang="zh-CN">
<head>
@@ -36,8 +36,7 @@
.header-top {
display: flex;
align-items: center;
- justify-content: center;
- gap: 30px;
+ justify-content: space-between;
}
.logo {
@@ -48,13 +47,19 @@
display: flex;
flex-direction: column;
line-height: 1.2;
- text-align: center;
+ text-align: right;
}
.week-day,
.full-date {
font-size: 16px;
color: #666;
+ }
+
+ .header-right {
+ display: flex;
+ align-items: center;
+ gap: 15px;
}
.clock {
@@ -87,22 +92,35 @@
border-radius: 12px;
display: flex;
flex-direction: row;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
flex: 1;
+ border: 2px solid #e0e0e0;
+ }
+
+ .status-active {
+ border-color: #67c23a;
+ }
+
+ .status-waiting {
+ border-color: #e6a23c;
+ }
+
+ .status-missed {
+ border-color: #f56c6c;
}
/* 宸︿晶鐘舵�佹爣绛� (绔栨帓) */
.panel-header {
- width: 60px;
+ width: 70px;
display: flex;
align-items: center;
justify-content: center;
- font-size: 22px;
+ font-size: 32px;
font-weight: bold;
color: #fff;
writing-mode: vertical-rl;
- letter-spacing: 6px;
+ letter-spacing: 12px;
text-align: center;
flex-shrink: 0;
}
@@ -134,27 +152,33 @@
/* 鎮h�呬俊鎭」 (鍥哄畾楂樺害锛屼竴琛屼袱涓�) */
.patient-item {
width: calc(50% - 5px);
- height: 60px;
+ height: 70px;
display: flex;
- justify-content: space-between;
+ justify-content: flex-start;
align-items: center;
- padding: 0 15px;
+ padding: 0 8px;
background: #ffffff;
border: 1px solid #eee;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ white-space: nowrap;
+ overflow: hidden;
}
.p-name {
- font-size: 28px;
+ font-size: 36px;
font-weight: bold;
color: #303133;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.p-num {
- font-size: 24px;
+ font-size: 30px;
color: #909399;
font-weight: bold;
+ flex-shrink: 0;
}
/* ================= 搴曢儴鎺у埗鍖� ================= */
@@ -177,6 +201,7 @@
}
#test-voice-btn {
+ display: none;
position: absolute;
right: 20px;
background-color: #007bff;
@@ -195,6 +220,7 @@
/* 璋冭瘯淇℃伅鍖� */
.debug-info {
+ display: none;
font-size: 14px;
color: #999;
background: #f0f0f0;
@@ -222,11 +248,13 @@
<div class="header">
<div class="header-top">
<img src="logo.png" alt="Logo" class="logo">
- <div class="time-box">
- <span class="week-day" id="weekDay">鏄熸湡鏃�</span>
- <span class="full-date" id="fullDate">2024骞�01鏈�01鏃�</span>
+ <div class="header-right">
+ <div class="time-box">
+ <span class="week-day" id="weekDay">鏄熸湡鏃�</span>
+ <span class="full-date" id="fullDate">2024骞�01鏈�01鏃�</span>
+ </div>
+ <div class="clock" id="clock">12:00</div>
</div>
- <div class="clock" id="clock">12:00</div>
</div>
<div class="room-line"><span id="currentRoomId">--</span></div>
</div>
@@ -234,19 +262,19 @@
<!-- 涓讳綋鍐呭 -->
<div class="main-container">
<!-- 1. 姝e湪灏辫瘖 -->
- <div class="panel status-active" style="max-height: calc(60px * 2 + 20px + 20px);">
- <div class="panel-header">姝e湪灏辫瘖</div>
+ <div class="panel status-active" style="max-height: calc(70px * 2 + 20px + 20px);">
+ <div class="panel-header">璇婁腑</div>
<div class="list-content" id="inProgressList"></div>
</div>
<!-- 2. 鍊欒瘖涓� -->
- <div class="panel status-waiting" style="max-height: calc(60px * 4 + 30px + 20px);">
- <div class="panel-header">鍊欒瘖涓�</div>
+ <div class="panel status-waiting" style="max-height: calc(70px * 4 + 30px + 20px);">
+ <div class="panel-header">绛夊��</div>
<div class="list-content" id="waitingList"></div>
</div>
<!-- 3. 杩囧彿 -->
- <div class="panel status-missed" style="max-height: calc(60px * 3 + 20px + 20px);">
+ <div class="panel status-missed" style="max-height: calc(70px * 3 + 20px + 20px);">
<div class="panel-header">杩囧彿</div>
<div class="list-content" id="missedList"></div>
</div>
@@ -254,7 +282,7 @@
<!-- 搴曢儴鎺у埗鏍� -->
<div class="footer">
- <span class="footer-text">娓╅Θ鎻愮ず锛氳鑰愬績绛夊緟锛屼繚鎸佸畨闈欙紒</span>
+ <span class="footer-text">娓╅Θ鎻愮ず锛氳杩囧彿鎮h�呭埌鍒嗚瘖鍙板鐞嗭紒</span>
<button id="test-voice-btn">娴嬭瘯璇煶</button>
</div>
@@ -262,63 +290,130 @@
<div class="debug-info" id="debugInfo">璋冭瘯鐘舵�侊細绛夊緟鏁版嵁...</div>
<script>
+ // ================= URL 鍙傛暟璇诲彇 =================
+ function getUrlParam(name) {
+ var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
+ var r = window.location.search.substr(1).match(reg);
+ if (r != null) return decodeURIComponent(r[2]);
+ return null;
+ }
+
// ================= 閰嶇疆鍙傛暟 =================
var CONFIG = {
- apiBaseUrl: "http://192.168.100.110:48080/admin-api",
- // apiBaseUrl: "http://192.168.100.110:48080/admin-api",
- roomId: "116",
+ // apiBaseUrl: "http://192.168.3.12/admin-api",
+ apiBaseUrl: "http://10.0.2.193/admin-api",
+ roomId: getUrlParam("roomID") || "116",
refreshRate: 5000
};
- var CALL_TIMES = 2; // 鍙彿娆℃暟
+ // 璇婂缂栧彿 鈫� 鍚嶇О鏄犲皠锛氬綋鎺ュ彛鏈繑鍥� roomName 鏃跺厹搴�
+ var ROOM_NAME_MAP = {
+ "116": "1鍙疯瘖瀹�",
+ "117": "2鍙疯瘖瀹�",
+ "118": "3鍙疯瘖瀹�",
+ "119": "4鍙疯瘖瀹�",
+ "121": "6鍙疯瘖瀹�",
+ "123": "8鍙疯瘖瀹�",
+ "125": "鍒嗚瘖鍙�"
+ };
+ var CALL_TIMES = 2; // 鍙彿娆℃暟
- var appState = { roomName: '', lastSpokenPatient: null };
+ var appState = { roomName: '', lastSpokenPatient: null, serverTimeOffset: 0 };
function $(id) { return document.getElementById(id); }
// ================= 鏃堕棿妯″潡 =================
+ function getNow() {
+ return new Date(Date.now() + (appState.serverTimeOffset || 0));
+ }
function updateClock() {
- var now = new Date();
+ var now = getNow();
$('clock').innerText = ('0' + now.getHours()).slice(-2) + ':' + ('0' + now.getMinutes()).slice(-2);
$('fullDate').innerText = now.getFullYear() + '骞�' + ('0' + (now.getMonth() + 1)).slice(-2) + '鏈�' + ('0' + now.getDate()).slice(-2) + '鏃�';
$('weekDay').innerText = ['鏄熸湡鏃�', '鏄熸湡涓�', '鏄熸湡浜�', '鏄熸湡涓�', '鏄熸湡鍥�', '鏄熸湡浜�', '鏄熸湡鍏�'][now.getDay()];
}
+ function syncServerTime(serverTimeStr) {
+ if (!serverTimeStr) return;
+ try {
+ var serverTime = new Date(serverTimeStr);
+ if (!isNaN(serverTime.getTime())) {
+ appState.serverTimeOffset = serverTime.getTime() - Date.now();
+ updateDebugInfo("鏃堕棿宸插悓姝� | 鍋忕Щ=" + (appState.serverTimeOffset / 1000).toFixed(1) + "s");
+ }
+ } catch (e) { updateDebugInfo("鏃堕棿鍚屾澶辫触: " + e.message); }
+ }
setInterval(updateClock, 1000); updateClock();
// ================= 璇煶鎾姤 =================
+ var _gUtterance = null; // GC 淇濇姢
function speakText(text, times) {
times = times || CALL_TIMES;
var isAndroid = /Android/i.test(navigator.userAgent);
if (isAndroid && window.wowjoy && typeof window.wowjoy.speek === 'function') {
- for (var i = 0; i < times; i++) setTimeout(function () { window.wowjoy.speek(text); }, i * 1500);
+ updateDebugInfo("TTS: wowjoy妯″紡");
+ for (var i = 0; i < times; i++) {
+ setTimeout(function () { window.wowjoy.speek(text); }, i * 1500);
+ }
} else if (window.speechSynthesis) {
- window.speechSynthesis.cancel();
- var utterance = new window.SpeechSynthesisUtterance(text);
- utterance.lang = 'zh-CN'; utterance.rate = 1.0;
- window.speechSynthesis.speak(utterance);
- for (var j = 1; j < times; j++) setTimeout(function () { window.speechSynthesis.speak(utterance); }, j * 1500);
+ updateDebugInfo("TTS: speechSynthesis妯″紡");
+ if (window.speechSynthesis.speaking) {
+ window.speechSynthesis.cancel();
+ }
+ function doSpeak(idx) {
+ var u = new window.SpeechSynthesisUtterance(text);
+ u.lang = 'zh-CN';
+ u.rate = 1.0;
+ u.volume = 1.0;
+ _gUtterance = u;
+ u.onend = function () { _gUtterance = null; };
+ u.onerror = function (e) { _gUtterance = null; updateDebugInfo("TTS閿欒: " + (e.error || "unknown")); };
+ window.speechSynthesis.speak(u);
+ }
+ doSpeak(0);
+ for (var j = 1; j < times; j++) {
+ setTimeout((function (idx) { return function () { doSpeak(idx); }; })(j), j * 1500);
+ }
+ } else {
+ updateDebugInfo("TTS: 鏃犲紩鎿庡彲鐢�");
}
}
// ================= 鏁版嵁璇锋眰 =================
function fetchData() {
var url = CONFIG.apiBaseUrl + "/ecg/screen/room-screen-data?roomId=" + CONFIG.roomId;
+ updateDebugInfo("璇锋眰: " + url);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
+ xhr.timeout = 8000;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
var response = JSON.parse(xhr.responseText);
processData(response);
- updateDebugInfo("鑾峰彇鏁版嵁鎴愬姛 | 鍘熷鏁版嵁闀垮害: " + (response.data ? Object.keys(response.data).length : 0));
- } catch (e) { updateDebugInfo("JSON瑙f瀽澶辫触: " + e.message); }
- } else { updateDebugInfo("璇锋眰澶辫触锛岀姸鎬佺爜: " + xhr.status); }
+ updateDebugInfo("鎴愬姛 | 鎮h��: " + (response.data ? JSON.stringify(Object.keys(response.data)) : "鏃�"));
+ } catch (e) { updateDebugInfo("澶勭悊澶辫触: " + e.message); }
+ } else if (xhr.status === 0) {
+ updateDebugInfo("缃戠粶閿欒(status=0) | " + url + " | 璇锋鏌PI鏈嶅姟/URL鍙揪鎬�/璺ㄥ煙");
+ } else {
+ updateDebugInfo("璇锋眰澶辫触 | status=" + xhr.status + " | " + url);
+ }
}
};
- xhr.send();
+ xhr.onerror = function () {
+ updateDebugInfo("缃戠粶寮傚父(onerror) | " + url + " | 璁惧鍙兘鏃犳硶璁块棶璇ュ湴鍧�");
+ };
+ xhr.ontimeout = function () {
+ updateDebugInfo("璇锋眰瓒呮椂 | " + url);
+ };
+ try {
+ xhr.send();
+ } catch (e) {
+ updateDebugInfo("send寮傚父: " + e.message);
+ }
}
// ================= 鏍稿績涓氬姟閫昏緫澶勭悊 =================
function processData(res) {
+ syncServerTime(res.serverTime || (res.data && res.data.serverTime) || null);
var data = res.data || res;
// 1. 鏇存柊璇婂鍚嶇О (浠庣涓�鏉℃暟鎹腑鑾峰彇)
@@ -330,6 +425,9 @@
if (waitingArr.length > 0) currentRoomName = waitingArr[0].roomName || currentRoomName;
else if (inProgressArr.length > 0) currentRoomName = inProgressArr[0].roomName || currentRoomName;
else if (missedArr.length > 0) currentRoomName = missedArr[0].roomName || currentRoomName;
+
+ // 鍏滃簳锛氭帴鍙f湭杩斿洖 roomName 鏃讹紝浠庢湰鍦版槧灏勮〃鍙栬瘖瀹ゅ悕绉�
+ currentRoomName = ROOM_NAME_MAP[currentRoomName] || currentRoomName;
appState.roomName = currentRoomName;
$('currentRoomId').innerText = currentRoomName;
@@ -375,7 +473,7 @@
var patientId = currentPatient.patId || currentPatient.seqNum;
if (appState.lastSpokenPatient !== patientId) {
appState.lastSpokenPatient = patientId;
- speakText(currentPatient.patName + "锛岃鍒�" + appState.roomName, CALL_TIMES);
+ speakText(currentPatient.patName + "锛岃鍒�" + appState.roomName + "灏辫瘖", CALL_TIMES);
}
} else {
appState.lastSpokenPatient = null;
@@ -383,19 +481,24 @@
}
// ================= 娓叉煋鍒楄〃 =================
+ function desensitizeName(name) {
+ if (!name || name.length < 2) return name || '';
+ return name.charAt(0) + '*' + name.substring(2);
+ }
+
function renderList(containerId, listData) {
var container = $(containerId);
+ if (!container) return;
container.innerHTML = "";
if (listData.length === 0) {
- container.innerHTML = '<div style="text-align:center; color:#ccc; width:100%; height:60px; line-height:60px; font-size:18px;">鏆傛棤鎮h��</div>';
+ container.innerHTML = '<div style="text-align:center; color:#ccc; width:100%; height:70px; line-height:70px; font-size:18px;">鏆傛棤鎮h��</div>';
return;
}
for (var i = 0; i < listData.length; i++) {
var item = listData[i];
var div = document.createElement('div');
div.className = 'patient-item';
- div.innerHTML = '<span class="p-name">' + (item.patName || '鏈煡') + '</span>' +
- '<span class="p-num">' + (item.seqNum || item.bookSeqNum || '--') + '鍙�</span>';
+ div.innerHTML = '<span class="p-num">' + (item.seqNum || '--') + '鍙�</span> <span class="p-name">' + (item.patName ? desensitizeName(item.patName) : '鏈煡') + '</span>';
container.appendChild(div);
}
}
--
Gitblit v1.9.3