feat: PAAI 자동인쇄 기능 완성 (EUC-KR 텍스트 방식)
추가: - 자동인쇄 ON/OFF 토글 (헤더) - ESC/POS 영수증 인쇄 (EUC-KR 인코딩) - ESCPOS_TROUBLESHOOTING.md 트러블슈팅 문서 핵심 변경: - 이미지 방식 -> 텍스트 방식 (socket 직접 전송) - UTF-8 -> EUC-KR 인코딩 - 이모지 제거 ([V], [!], >> 사용) - 48자 기준 줄바꿈 인쇄 흐름: 1. PAAI 분석 완료 2. 자동인쇄 ON이면 /pmr/api/paai/print 호출 3. _format_paai_receipt()로 텍스트 생성 4. _print_escpos_text()로 프린터 전송 참고: docs/ESCPOS_TROUBLESHOOTING.md
This commit is contained in:
@@ -33,6 +33,59 @@
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
.auto-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.status-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.status-badge .status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.status-badge.disconnected {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
}
|
||||
.status-badge.disconnected .status-dot {
|
||||
background: #ef4444;
|
||||
}
|
||||
.status-badge.connected {
|
||||
background: #ecfdf5;
|
||||
color: #059669;
|
||||
}
|
||||
.status-badge.connected .status-dot {
|
||||
background: #10b981;
|
||||
}
|
||||
.status-badge.auto-print-off {
|
||||
background: #f3f4f6;
|
||||
color: #6b7280;
|
||||
}
|
||||
.status-badge.auto-print-off .status-dot {
|
||||
background: #9ca3af;
|
||||
}
|
||||
.status-badge.auto-print-on {
|
||||
background: #dbeafe;
|
||||
color: #2563eb;
|
||||
}
|
||||
.status-badge.auto-print-on .status-dot {
|
||||
background: #3b82f6;
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
.date-picker {
|
||||
padding: 8px 15px;
|
||||
border: 2px solid #8b5cf6;
|
||||
@@ -940,6 +993,17 @@
|
||||
<header class="header">
|
||||
<h1>💊 조제관리 <span>청춘라벨 v2</span></h1>
|
||||
<div class="controls">
|
||||
<!-- 자동감지/자동인쇄 상태 -->
|
||||
<div class="auto-controls">
|
||||
<div id="triggerIndicator" class="status-badge disconnected">
|
||||
<span class="status-dot"></span>
|
||||
자동감지 OFF
|
||||
</div>
|
||||
<div id="autoPrintToggle" class="status-badge auto-print-off" onclick="toggleAutoPrint()">
|
||||
<span class="status-dot"></span>
|
||||
자동인쇄 OFF
|
||||
</div>
|
||||
</div>
|
||||
<input type="date" id="dateSelect" class="date-picker">
|
||||
<div class="stats-box">
|
||||
<div class="stat-item">
|
||||
@@ -1947,6 +2011,9 @@
|
||||
|
||||
// 토스트 알림
|
||||
showPaaiToast(patientName, 'PAAI 분석 완료! 클릭하여 확인', 'success', preSerial);
|
||||
|
||||
// 자동인쇄 (활성화된 경우)
|
||||
printPaaiResult(preSerial, patientName, result);
|
||||
} else {
|
||||
showPaaiToast(patientName, '분석 실패: ' + (result.error || '알 수 없는 오류'), 'error', preSerial);
|
||||
}
|
||||
@@ -2435,6 +2502,13 @@
|
||||
true // clickable
|
||||
);
|
||||
playTriggerSound();
|
||||
|
||||
// 자동인쇄 (활성화된 경우)
|
||||
printPaaiResult(data.pre_serial, data.patient_name, {
|
||||
success: true,
|
||||
analysis: data.analysis,
|
||||
kims_summary: data.kims_summary
|
||||
});
|
||||
break;
|
||||
case 'analysis_failed':
|
||||
showTriggerToast(data.pre_serial, data.patient_name, 'failed', data.error || '분석 실패', '❌');
|
||||
@@ -2562,39 +2636,75 @@
|
||||
|
||||
// 연결 상태 표시
|
||||
function updateTriggerIndicator(isConnected) {
|
||||
let indicator = document.getElementById('triggerIndicator');
|
||||
if (!indicator) {
|
||||
const controls = document.querySelector('.header .controls');
|
||||
if (controls) {
|
||||
indicator = document.createElement('div');
|
||||
indicator.id = 'triggerIndicator';
|
||||
indicator.style.cssText = `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background: #f1f5f9;
|
||||
border-radius: 8px;
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
`;
|
||||
controls.insertBefore(indicator, controls.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
const indicator = document.getElementById('triggerIndicator');
|
||||
if (indicator) {
|
||||
indicator.className = `status-badge ${isConnected ? 'connected' : 'disconnected'}`;
|
||||
indicator.innerHTML = `
|
||||
<span style="
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: ${isConnected ? '#10b981' : '#ef4444'};
|
||||
"></span>
|
||||
${isConnected ? '자동감지 ON' : '자동감지 OFF'}
|
||||
<span class="status-dot"></span>
|
||||
자동감지 ${isConnected ? 'ON' : 'OFF'}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 자동인쇄 기능
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
let autoPrintEnabled = localStorage.getItem('pmr_auto_print') === 'true';
|
||||
|
||||
// 초기화
|
||||
function initAutoPrint() {
|
||||
updateAutoPrintIndicator();
|
||||
}
|
||||
|
||||
// 토글
|
||||
function toggleAutoPrint() {
|
||||
autoPrintEnabled = !autoPrintEnabled;
|
||||
localStorage.setItem('pmr_auto_print', autoPrintEnabled);
|
||||
updateAutoPrintIndicator();
|
||||
showToast(autoPrintEnabled ? '🖨️ 자동인쇄 ON' : '🖨️ 자동인쇄 OFF', autoPrintEnabled ? 'success' : 'info');
|
||||
}
|
||||
|
||||
// 표시 업데이트
|
||||
function updateAutoPrintIndicator() {
|
||||
const toggle = document.getElementById('autoPrintToggle');
|
||||
if (toggle) {
|
||||
toggle.className = `status-badge ${autoPrintEnabled ? 'auto-print-on' : 'auto-print-off'}`;
|
||||
toggle.innerHTML = `
|
||||
<span class="status-dot"></span>
|
||||
자동인쇄 ${autoPrintEnabled ? 'ON' : 'OFF'}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// PAAI 결과 인쇄
|
||||
async function printPaaiResult(preSerial, patientName, result) {
|
||||
if (!autoPrintEnabled) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/pmr/api/paai/print', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
pre_serial: preSerial,
|
||||
patient_name: patientName,
|
||||
result: result
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
console.log('[AutoPrint] 인쇄 완료:', preSerial);
|
||||
} else {
|
||||
console.error('[AutoPrint] 인쇄 실패:', data.error);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[AutoPrint] 오류:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 페이지 로드 시 초기화
|
||||
document.addEventListener('DOMContentLoaded', initAutoPrint);
|
||||
|
||||
// 알림 소리
|
||||
function playTriggerSound() {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user