yxh
yxh
5 天以前 796047fbe84d51816f44be535501415d3c66dd9d
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 @@
        /* 患者信息项 (固定高度,一行两个) */
        .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. 正在就诊 -->
        <div class="panel status-active" style="max-height: calc(60px * 2 + 20px + 20px);">
            <div class="panel-header">正在就诊</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">温馨提示:请过号患者到分诊台处理!</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.3.12: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解析失败: " + e.message); }
                    } else { updateDebugInfo("请求失败,状态码: " + xhr.status); }
                            updateDebugInfo("成功 | 患者: " + (response.data ? JSON.stringify(Object.keys(response.data)) : "无"));
                        } catch (e) { updateDebugInfo("处理失败: " + e.message); }
                    } else if (xhr.status === 0) {
                        updateDebugInfo("网络错误(status=0) | " + url + " | 请检查API服务/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;
            // 兜底:接口未返回 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;">暂无患者</div>';
                container.innerHTML = '<div style="text-align:center; color:#ccc; width:100%; height:70px; line-height:70px; font-size:18px;">暂无患者</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>&nbsp;<span class="p-name">' + (item.patName ? desensitizeName(item.patName) : '未知') + '</span>';
                container.appendChild(div);
            }
        }