¶Ô±ÈÐÂÎļþ |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| | | <title>éå人æ°å»é¢å¤§å±å«å·ç³»ç»</title> |
| | | <style> |
| | | /* åºç¡æ ·å¼ */ |
| | | * { |
| | | margin: 0; |
| | | padding: 0; |
| | | box-sizing: border-box; |
| | | font-family: 'Helvetica Neue', Arial, sans-serif; |
| | | } |
| | | |
| | | body { |
| | | background: linear-gradient(135deg, #e6f0f8, #d9e4f0); |
| | | color: #4a5568; |
| | | height: 100vh; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .bigscreen-container { |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 8px; |
| | | } |
| | | |
| | | /* å»é¢æ 颿 ·å¼ */ |
| | | .hospital-header { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | margin-bottom: 8px; |
| | | font-size: 22px; |
| | | font-weight: bold; |
| | | color: #4a7dff; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .hospital-header img { |
| | | margin-right: 8px; |
| | | height: 40px; |
| | | } |
| | | |
| | | .running-indicator { |
| | | width: 12px; |
| | | height: 12px; |
| | | border-radius: 50%; |
| | | margin-left: 12px; |
| | | transition: background-color 0.3s; |
| | | } |
| | | |
| | | /* åæ é¢åºå */ |
| | | .column-header { |
| | | display: flex; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | border-radius: 12px; |
| | | padding: 12px 0; |
| | | margin-bottom: 8px; |
| | | font-size: 20px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .column-title { |
| | | flex: 1; |
| | | min-width: 0; |
| | | text-align: center; |
| | | padding: 0 12px; |
| | | border-left: 1px solid rgba(91, 140, 255, 0.2); |
| | | } |
| | | |
| | | .column-title:first-child { |
| | | border-left: none; |
| | | flex: 2; /* 第ä¸ä¸ªæ¿é´æ é¢å 两å宽度 */ |
| | | } |
| | | |
| | | /* æ£è
å表åºå */ |
| | | .patient-list-container { |
| | | display: flex; |
| | | flex-grow: 1; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | border-radius: 12px; |
| | | padding: 12px 0; |
| | | margin-bottom: 8px; |
| | | font-size: 18px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .patient-column { |
| | | flex: 1; |
| | | min-width: 0; |
| | | padding: 0 12px; |
| | | border-left: 1px solid rgba(91, 140, 255, 0.2); |
| | | } |
| | | |
| | | .patient-column:first-child { |
| | | border-left: none; |
| | | flex: 2; /* 第ä¸ä¸ªæ¿é´å 两å宽度 */ |
| | | } |
| | | |
| | | /* 第ä¸ä¸ªæ¿é´åæä¸¤åçç¹æ®æ ·å¼ */ |
| | | .first-room-columns { |
| | | display: flex; |
| | | flex: 2; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .first-room-column { |
| | | flex: 1; |
| | | min-width: 0; |
| | | padding: 0 12px; |
| | | } |
| | | |
| | | .first-room-column:first-child { |
| | | border-right: 1px solid rgba(91, 140, 255, 0.2); |
| | | } |
| | | |
| | | /* æ£è
é¡¹æ ·å¼ - ä¼ååçå¸å± */ |
| | | .patient-item { |
| | | padding: 10px 8px; |
| | | margin-bottom: 8px; |
| | | border-radius: 8px; |
| | | transition: all 0.3s; |
| | | background: rgba(255, 255, 255, 0.8); |
| | | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .patient-item:hover { |
| | | background: rgba(91, 140, 255, 0.1); |
| | | } |
| | | |
| | | .patient-info-row { |
| | | display: flex; |
| | | width: 100%; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .patient-number-name { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .patient-number { |
| | | color: #5b8cff; |
| | | font-weight: bold; |
| | | margin-right: 10px; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | .patient-name { |
| | | flex: 1; |
| | | min-width: 100px; |
| | | } |
| | | |
| | | .patient-status { |
| | | width: 100%; |
| | | color: #67c23a; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .warning-row { |
| | | background-color: rgba(230, 162, 60, 0.1); |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | /* åºé¨æ§å¶åºå */ |
| | | .footer-controls { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | border-radius: 12px; |
| | | padding: 12px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .welcome-btn { |
| | | background: rgba(91, 140, 255, 0.1); |
| | | color: #4a7dff; |
| | | border: none; |
| | | border-radius: 8px; |
| | | padding: 10px 20px; |
| | | font-size: 16px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .welcome-btn:hover { |
| | | background: rgba(91, 140, 255, 0.2); |
| | | } |
| | | |
| | | /* æ»å¨æ¡æ ·å¼ */ |
| | | .patient-list-container::-webkit-scrollbar { |
| | | width: 6px; |
| | | } |
| | | |
| | | .patient-list-container::-webkit-scrollbar-track { |
| | | background: rgba(0, 0, 0, 0.05); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .patient-list-container::-webkit-scrollbar-thumb { |
| | | background: rgba(91, 140, 255, 0.4); |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | /* ååºå¼è°æ´ */ |
| | | @media (max-width: 1200px) { |
| | | .hospital-header { |
| | | font-size: 18px; |
| | | } |
| | | .column-header { |
| | | font-size: 16px; |
| | | } |
| | | .patient-list-container { |
| | | font-size: 14px; |
| | | } |
| | | .patient-number { |
| | | min-width: 60px; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .hospital-header { |
| | | font-size: 16px; |
| | | padding: 8px; |
| | | } |
| | | .column-header { |
| | | flex-direction: column; |
| | | padding: 8px 0; |
| | | } |
| | | .column-title { |
| | | min-width: 100%; |
| | | padding: 8px 0; |
| | | border-left: none; |
| | | border-bottom: 1px solid rgba(91, 140, 255, 0.2); |
| | | } |
| | | .patient-list-container { |
| | | flex-direction: column; |
| | | } |
| | | .patient-column, .first-room-columns { |
| | | min-width: 100%; |
| | | padding: 8px 0; |
| | | border-left: none; |
| | | border-bottom: 1px solid rgba(91, 140, 255, 0.2); |
| | | } |
| | | .first-room-column { |
| | | padding: 0 8px; |
| | | } |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div class="bigscreen-container"> |
| | | <!-- é¡¶é¨å»é¢æ é¢ --> |
| | | <div class="hospital-header"> |
| | | <img src="./static/jinhua.png"/> |
| | | <span>éå人æ°å»é¢</span> |
| | | <div class="running-indicator" id="runningIndicator"></div> |
| | | </div> |
| | | |
| | | <!-- åæ é¢åºå --> |
| | | <div class="column-header" id="columnHeader"> |
| | | <!-- åæ é¢å°éè¿JSå¨æçæ --> |
| | | </div> |
| | | |
| | | <!-- æ£è
å表åºå --> |
| | | <div class="patient-list-container" id="patientListContainer"> |
| | | <!-- æ£è
å表å°éè¿JSå¨æçæ --> |
| | | </div> |
| | | |
| | | <div class="footer-controls"> |
| | | <button class="welcome-btn" id="welcomeBtn">欢è¿è¯</button> |
| | | </div> |
| | | </div> |
| | | |
| | | <script src="./static/jquery.min.js"></script> |
| | | <script> |
| | | // åºç¨ç¶æ |
| | | var appState = { |
| | | runningIndicator: 0, |
| | | displayColInfo: { |
| | | "0": ["常è§å¿çµå¾A", "åºè¾¹å¿çµå¾(常è§+é¢è°±)M", "常è§å¿çµå¾-å¿çµåéå¾N"], |
| | | "1": ["卿å¿çµB", "卿è¡åC"], |
| | | "2": ["å¹³æ¿è¿å¨å¿çµD"], |
| | | "3": ["é£éçµççF"], |
| | | "4": ["å¨è硬åçæµE"] |
| | | }, |
| | | mapColumnVsPatients: new Map(), |
| | | curSpeakPat: null, |
| | | timer: null, |
| | | speechSynthesis: window.speechSynthesis || null, |
| | | apiBaseUrl: 'http://localhost:48080/admin-api' |
| | | }; |
| | | |
| | | // 页é¢å è½½å®æååå§å |
| | | $(document).ready(function() { |
| | | // åå§åäºä»¶çå¬ |
| | | $('#welcomeBtn').click(function() { |
| | | speak('欢è¿ä½¿ç¨éå人æ°å»é¢å«å·ç³»ç»'); |
| | | }); |
| | | |
| | | // åå§åæ°æ® |
| | | updateColumnHeader(); |
| | | startScrolling(); |
| | | |
| | | // åå§åè¯é³åæ |
| | | if (appState.speechSynthesis) { |
| | | appState.speechSynthesis.onend = onSpeachEndEvent; |
| | | } |
| | | }); |
| | | |
| | | // æ´æ°åæ 颿¾ç¤º |
| | | function updateColumnHeader() { |
| | | var $header = $('#columnHeader'); |
| | | $header.empty(); |
| | | |
| | | // æç
§åºå®é¡ºåºæ¾ç¤ºäºä¸ªæ¿é´çæ é¢ |
| | | for (var i = 0; i < 5; i++) { |
| | | var disColId = i.toString(); |
| | | var disNameList = appState.displayColInfo[disColId] || []; |
| | | var isFirstColumn = disColId === "0"; |
| | | |
| | | var $title = $('<div class="column-title' + (isFirstColumn ? ' double-column' : '') + '"></div>'); |
| | | |
| | | disNameList.forEach(function(dispName) { |
| | | $title.append('<div>' + dispName + '</div>'); |
| | | }); |
| | | |
| | | $header.append($title); |
| | | } |
| | | } |
| | | |
| | | // è·åæ£è
å表 |
| | | function getList() { |
| | | // è¿éæ¿æ¢ä¸ºå®é
çAPIè°ç¨ |
| | | $.ajax({ |
| | | url: appState.apiBaseUrl + '/ecg/screen/big-screen-data', |
| | | type: 'GET', |
| | | dataType: 'json', |
| | | success: function(response) { |
| | | // ç¡®ä¿æ°æ®æç
§äºä¸ªæ¿é´çé¡ºåºæå |
| | | var data = response.data || response; |
| | | appState.mapColumnVsPatients = new Map(); |
| | | |
| | | // æç
§åºå®é¡ºåºå¤çäºä¸ªæ¿é´çæ°æ® |
| | | for (var i = 0; i < 5; i++) { |
| | | var roomId = i.toString(); |
| | | appState.mapColumnVsPatients.set(roomId, data[roomId] || []); |
| | | } |
| | | |
| | | updatePatientList(); |
| | | }, |
| | | error: function(xhr, status, error) { |
| | | console.error('è·åæ£è
å表失败:', error); |
| | | // ä½¿ç¨æ¨¡ææ°æ®ä½ä¸ºåå¤ |
| | | appState.mapColumnVsPatients = generateMockPatients(); |
| | | updatePatientList(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // æ´æ°æ£è
å表æ¾ç¤º |
| | | function updatePatientList() { |
| | | var $container = $('#patientListContainer'); |
| | | $container.empty(); |
| | | |
| | | // æç
§åºå®é¡ºåºæ¾ç¤ºäºä¸ªæ¿é´çæ£è
æ°æ® |
| | | for (var i = 0; i < 5; i++) { |
| | | var disColId = i.toString(); |
| | | var patients = appState.mapColumnVsPatients.get(disColId) || []; |
| | | var isFirstColumn = disColId === "0"; |
| | | |
| | | if (isFirstColumn) { |
| | | // 第ä¸ä¸ªæ¿é´ç¹æ®å¤ç |
| | | if (patients.length > 10) { |
| | | // è¶
è¿10人ï¼åæä¸¤å |
| | | var $firstRoomColumns = $('<div class="first-room-columns"></div>'); |
| | | |
| | | // 第ä¸å(å10æ¡) |
| | | var $firstCol = $('<div class="first-room-column"></div>'); |
| | | patients.slice(0, 10).forEach(function(item, itemIndex) { |
| | | $firstCol.append(createPatientItem(item)); |
| | | }); |
| | | $firstRoomColumns.append($firstCol); |
| | | |
| | | // 第äºå(å©ä½æ°æ®) |
| | | var $secondCol = $('<div class="first-room-column"></div>'); |
| | | patients.slice(10).forEach(function(item, itemIndex) { |
| | | $secondCol.append(createPatientItem(item, itemIndex + 10)); |
| | | }); |
| | | $firstRoomColumns.append($secondCol); |
| | | |
| | | $container.append($firstRoomColumns); |
| | | } else { |
| | | // ä¸è¶
è¿10人ï¼ååæ¾ç¤ºä½å 两å宽度 |
| | | var $col = $('<div class="patient-column" style="flex:2"></div>'); |
| | | patients.forEach(function(item, itemIndex) { |
| | | $col.append(createPatientItem(item, itemIndex)); |
| | | }); |
| | | $container.append($col); |
| | | } |
| | | } else { |
| | | // å
¶ä»æ¿é´ååæ¾ç¤º |
| | | var $col = $('<div class="patient-column"></div>'); |
| | | patients.forEach(function(item, itemIndex) { |
| | | $col.append(createPatientItem(item, itemIndex)); |
| | | }); |
| | | $container.append($col); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å建æ£è
项 - ä¼ååççæ¬ |
| | | function createPatientItem(item, index) { |
| | | var statusClass = getItemCssClass(item); |
| | | var $item = $('<div class="patient-item ' + statusClass + '"></div>'); |
| | | |
| | | // å·ç åå§åå¨ä¸è¡ |
| | | var $numberNameRow = $('<div class="patient-info-row patient-number-name"></div>'); |
| | | $numberNameRow.append('<span class="patient-number">' + getCheckTypeSeqPrefix(item.bookCheckType) + item.bookSeqNum + '</span>'); |
| | | $numberNameRow.append('<span class="patient-name">' + nameDesensitize(item.patName) + '</span>'); |
| | | $item.append($numberNameRow); |
| | | |
| | | // ç¶æå¨ä¸ä¸è¡ |
| | | var $statusRow = $('<div class="patient-info-row"></div>'); |
| | | $statusRow.append('<span class="patient-status">' + queueStatusConvert(item.status) + '</span>'); |
| | | $item.append($statusRow); |
| | | |
| | | return $item; |
| | | } |
| | | |
| | | // å¼å§å®æ¶å·æ° |
| | | function startScrolling() { |
| | | getList(); |
| | | appState.timer = setInterval(function() { |
| | | appState.runningIndicator++; |
| | | $('#runningIndicator').css('backgroundColor', appState.runningIndicator % 2 === 0 ? '#67c23a' : '#e6a23c'); |
| | | getList(); |
| | | |
| | | if (appState.curSpeakPat == null) { |
| | | initiateSpeak(); |
| | | } |
| | | }, 5000); |
| | | } |
| | | |
| | | // è¯é³ç»æäºä»¶ |
| | | function onSpeachEndEvent(event) { |
| | | console.log("Speech ended... " + event.currentTarget.text); |
| | | |
| | | // è¿éæ¿æ¢ä¸ºå®é
çAPIè°ç¨ |
| | | $.ajax({ |
| | | url: appState.apiBaseUrl + '/ecg/call/update', |
| | | method: 'PUT', |
| | | data: { |
| | | id: appState.curSpeakPat.id, |
| | | called: 1 |
| | | }, |
| | | success: function() { |
| | | initiateSpeak(); |
| | | }, |
| | | error: function(xhr, status, error) { |
| | | console.error('æ´æ°å«å·ç¶æå¤±è´¥:', error); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // å«å·åè½ |
| | | function initiateSpeak() { |
| | | // è¿éæ¿æ¢ä¸ºå®é
çAPIè°ç¨ |
| | | $.ajax({ |
| | | url: appState.apiBaseUrl + '/ecg/call/next', |
| | | type: 'GET', |
| | | dataType: 'json', |
| | | success: function(response) { |
| | | appState.curSpeakPat = response.data || response; |
| | | if (appState.curSpeakPat != null && appState.curSpeakPat.called === 0) { |
| | | speak("请ã" + appState.curSpeakPat.patName + "å°" + appState.curSpeakPat.roomName + "å°±è¯"); |
| | | } |
| | | }, |
| | | error: function(xhr, status, error) { |
| | | console.error('è·åä¸ä¸ä¸ªå«å·å¤±è´¥:', error); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // è¯é³ææ¥ |
| | | function speak(msg) { |
| | | console.info("speak " + msg); |
| | | |
| | | if (!appState.speechSynthesis) { |
| | | console.warn("å½åæµè§å¨ä¸æ¯æè¯é³åæ"); |
| | | return; |
| | | } |
| | | |
| | | // åæ¶å½åæ£å¨è¿è¡çè¯é³ |
| | | appState.speechSynthesis.cancel(); |
| | | |
| | | var speech = new SpeechSynthesisUtterance(); |
| | | speech.text = msg + "ããã" + msg + "ããã" + msg + "ããã"; |
| | | speech.pitch = 1; |
| | | speech.rate = 0.9; |
| | | speech.volume = 100; |
| | | speech.lang = 'zh-CN'; |
| | | speech.onend = onSpeachEndEvent; |
| | | |
| | | appState.speechSynthesis.speak(speech); |
| | | } |
| | | |
| | | // å·¥å
·å½æ° |
| | | function nameDesensitize(patName) { |
| | | if (!patName) return ''; |
| | | if (patName.length === 2) { |
| | | return patName.substring(0, 1) + '*'; |
| | | } else if (patName.length === 3) { |
| | | return patName.substring(0, 1) + '*' + patName.substring(2, 3); |
| | | } else if (patName.length > 3) { |
| | | return patName.substring(0, 1) + '*' + '*' + patName.substring(3, patName.length); |
| | | } |
| | | return patName; |
| | | } |
| | | |
| | | function getItemCssClass(item) { |
| | | if (item.status === 5 || item.status === 7) { |
| | | return "warning-row"; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | function queueStatusConvert(status) { |
| | | var statusMap = { |
| | | 3: 'å·²è¿å·-æé', |
| | | 5: 'å·²è¿å·', |
| | | 7: 'å·²è¿å·-å®è£
', |
| | | 10: 'æéä¸', |
| | | 12: '亲å', |
| | | 13: '亲å-å®è£
', |
| | | 15: 'å·²å¬å', |
| | | 20: 'åè¯ä¸', |
| | | 30: 'å°±è¯ä¸', |
| | | 33: 'å·²é¢ç¨', |
| | | 34: 'å·²å¬å-å®è£
', |
| | | 36: 'å®è£
ä¸', |
| | | 40: '已就è¯' |
| | | }; |
| | | return statusMap[status] || 'æªç¥ç¶æ'; |
| | | } |
| | | |
| | | function getCheckTypeSeqPrefix(type) { |
| | | var types = { |
| | | 1: 'A001', |
| | | 2: 'A002', |
| | | 3: 'A003', |
| | | 4: 'A004' |
| | | }; |
| | | return types[type] || ''; |
| | | } |
| | | |
| | | // çææ¨¡ææ£è
æ°æ® |
| | | function generateMockPatients() { |
| | | var mockData = { |
| | | "0": generateMockPatientsForRoom(15, "常è§å¿çµå¾A"), |
| | | "1": generateMockPatientsForRoom(8, "卿å¿çµB"), |
| | | "2": generateMockPatientsForRoom(5, "å¹³æ¿è¿å¨å¿çµD"), |
| | | "3": generateMockPatientsForRoom(3, "é£éçµççF"), |
| | | "4": generateMockPatientsForRoom(4, "å¨è硬åçæµE") |
| | | }; |
| | | return new Map(Object.entries(mockData)); |
| | | } |
| | | |
| | | function generateMockPatientsForRoom(count, checkTypeName) { |
| | | var mockPatients = []; |
| | | var names = ['å¼ ä¸', 'æå', 'çäº', 'èµµå
', 'é±ä¸', 'åå
«', 'å¨ä¹', 'å´å']; |
| | | var rooms = ['å¿çµå¾å®¤1', 'å¿çµå¾å®¤2', '卿å¿çµå®¤', 'å¿çµçæ¤å®¤']; |
| | | var statuses = [5, 7, 10, 12, 13, 15, 20, 30, 33, 34, 36, 40]; |
| | | var checkTypes = [1, 2, 3, 4]; |
| | | |
| | | for (var i = 0; i < count; i++) { |
| | | mockPatients.push({ |
| | | id: i + 1, |
| | | patName: names[Math.floor(Math.random() * names.length)], |
| | | roomName: rooms[Math.floor(Math.random() * rooms.length)], |
| | | bedNo: 'åº' + (Math.floor(Math.random() * 20) + 1), |
| | | status: statuses[Math.floor(Math.random() * statuses.length)], |
| | | bookCheckType: checkTypes[Math.floor(Math.random() * checkTypes.length)], |
| | | bookSeqNum: Math.floor(Math.random() * 50) + 1, |
| | | called: 0, |
| | | checkTypeName: checkTypeName |
| | | }); |
| | | } |
| | | |
| | | return mockPatients; |
| | | } |
| | | </script> |
| | | </body> |
| | | </html> |