From 796047fbe84d51816f44be535501415d3c66dd9d Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期日, 21 六月 2026 23:24:37 +0800
Subject: [PATCH] yxh

---
 big.html |  689 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 556 insertions(+), 133 deletions(-)

diff --git a/big.html b/big.html
index 48e44da..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" />
+    <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,28 +12,37 @@
             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;
@@ -47,6 +57,7 @@
         .top-header .title-text {
             position: absolute;
             left: 50%;
+            -webkit-transform: translateX(-50%);
             transform: translateX(-50%);
             font-size: 32px;
             font-weight: bold;
@@ -55,47 +66,125 @@
         }
 
         .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;
         }
 
-        /* 3. 涓讳綋鍐呭 */
-        .main-content {
-            flex: 1;
+        .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;
-            gap: 10px;
         }
 
-        /* 鍒楀鍣� - 绉婚櫎闃村奖鍜屽渾瑙掞紝鏇寸畝娲� */
-        .column-box {
+        /* 鍒楄瀹瑰櫒 */
+        .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: 3px;
+            margin-right: 3px;
+        }
+
+        .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;
+            padding: 6px;
+            -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 {
-            font-size: 22px;
+        /* 鏍囬琛� */
+        .col-title-line {
+            font-size: 28px;
             font-weight: bold;
             color: #4da6ff;
+            text-align: center;
+        }
+
+        .col-title {
             padding-bottom: 8px;
             border-bottom: 1px solid #003366;
             margin-bottom: 10px;
@@ -103,57 +192,85 @@
         }
 
         .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�呭垪琛� */
         .patient-list {
+            -webkit-box-flex: 1;
+            -webkit-flex: 1;
             flex: 1;
             overflow-y: auto;
-            padding-right: 5px;
-        }
-
-        /* 銆愬叧閿慨鏀广�戠1鏍忓己鍒朵竴琛�2涓� */
-        #col-0 {
+            padding-right: 2px;
+            display: -webkit-box;
+            display: -webkit-flex;
             display: flex;
-            flex-wrap: wrap;
+            -webkit-align-content: flex-start;
             align-content: flex-start;
         }
 
-        /* 绗�2-5鏍忎繚鎸佸崟鍒� */
-        .col-normal .patient-list {
-            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;
         }
 
-        /* 4. 鎮h�呴」鐩� - 鏋佺畝妯″紡 */
+        .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;
+            -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 {
             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;
             overflow: hidden;
             text-overflow: ellipsis;
@@ -163,15 +280,43 @@
         .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;
         }
 
-        /* 5. 搴曢儴鏍� - 灞呬腑鏄剧ず */
+        /* 杩囧彿鎮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;
+        }
+
+        /* 搴曢儴鏍� */
         .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);
@@ -185,7 +330,26 @@
             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;
         }
@@ -195,16 +359,23 @@
             border-radius: 2px;
         }
 
-        /* 6. 璋冭瘯妗嗘牱寮� */
+        /* 璋冭瘯闈㈡澘 */
         .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;
@@ -212,8 +383,14 @@
         }
 
         .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;
@@ -235,6 +412,8 @@
         }
 
         .debug-body {
+            -webkit-box-flex: 1;
+            -webkit-flex: 1;
             flex: 1;
             padding: 5px;
             overflow-y: auto;
@@ -255,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>
 
@@ -264,14 +443,14 @@
 
     <!-- 搴曢儴鏍� -->
     <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>
+            <span>[璋冭瘯] 杩愯鏃ュ織</span>
             <button onclick="document.getElementById('debugBody').innerHTML=''">娓呯┖</button>
         </div>
         <div class="debug-body" id="debugBody"></div>
@@ -279,57 +458,135 @@
 
     <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);
+                // 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.3.12/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);
+            logDebug("[鎾姤] " + text);
 
-            var utterance = new SpeechSynthesisUtterance(text);
-            utterance.onend = function () {
-                appState.isSpeaking = false;
-                processTtsQueue();
-            };
-            utterance.onerror = function () {
-                appState.isSpeaking = false;
-                processTtsQueue();
-            };
-
+            // 浼樺厛灏濊瘯璁惧鍘熺敓 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); }
+                } catch (e) { logDebug("[wowjoy澶辫触] " + e.message); }
             }
 
-            if (window.speechSynthesis) { window.speechSynthesis.speak(utterance); }
-            else { appState.isSpeaking = false; processTtsQueue(); }
+            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) {
@@ -338,46 +595,179 @@
             processTtsQueue();
         }
 
-        // ================= 娓叉煋鍑芥暟 =================
-        function renderMainContent() {
-            var $main = $("#mainContent");
-            $main.empty();
+        // ================= 璇煶娴嬭瘯 =================
+        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';
-                // 娉ㄦ剰锛氳繖閲屼笉鍐嶇粰绗�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);
+                // 濡傛灉 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++) {
-                $("#col-" + i).empty();
-            }
+        // ================= 娓叉煋锛氭爣棰樻鏋讹紙浠呭垵濮嬪寲涓�娆★級=================
+        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 = $("#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);
-                    }
+            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] || "&nbsp;";
+                // 涓�琛屼袱涓殑鍒楃敤 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涓嶅瓨鍦�(鍙兘宸查殣钘�)");
                 }
             }
         }
@@ -404,12 +794,12 @@
                             }
                             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);
+                                logDebug("[鎺掗槦] " + newPat.patName + " -> " + newPat.roomName);
                             }
                         }
                     }
@@ -420,51 +810,84 @@
         // ================= 鏁版嵁鑾峰彇 =================
         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();
 
-                    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("[璇锋眰澶辫触] status=" + err.status + " " + (err.statusText || "缃戠粶閿欒") + " url=" + url);
                 }
             });
         }
 
         // ================= 鍒濆鍖� =================
-        $(document).ready(function () {
-            renderMainContent();
+        function onReady() {
+            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("鉁� 绯荤粺鍚姩");
-        });
+            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());
-            $("#headerTime").text(dateStr + " " + timeStr);
+            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 鍙兘娌℃湁 $锛�
+        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>

--
Gitblit v1.9.3