feat: PMR 처방 비교 기능
- 비교 모드 토글 체크박스 추가 - 상태 분류: 🆕추가 / 🔄변경 / ❌중단 / ✓동일 - 변경된 값: '1정 → 2정' 형태로 표시 - 색상 코딩: 녹색(추가), 노랑(변경), 빨강(중단) - 이전 처방 < > 네비게이션 시 자동 비교
This commit is contained in:
parent
6192f635ca
commit
d8aa073564
@ -311,6 +311,66 @@
|
||||
text-align: center;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* 처방 비교 상태 */
|
||||
.med-status {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.status-added { background: #dcfce7; color: #166534; }
|
||||
.status-removed { background: #fee2e2; color: #991b1b; }
|
||||
.status-changed { background: #fef3c7; color: #92400e; }
|
||||
.status-same { background: #f1f5f9; color: #64748b; }
|
||||
|
||||
tr.row-added { background: #f0fdf4 !important; }
|
||||
tr.row-removed { background: #fef2f2 !important; opacity: 0.7; }
|
||||
tr.row-changed { background: #fffbeb !important; }
|
||||
|
||||
.change-arrow {
|
||||
color: #94a3b8;
|
||||
margin: 0 4px;
|
||||
}
|
||||
.change-from {
|
||||
text-decoration: line-through;
|
||||
color: #94a3b8;
|
||||
}
|
||||
.change-to {
|
||||
background: #fbbf24;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 비교 모드 토글 */
|
||||
.compare-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 15px;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.compare-toggle input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.compare-legend {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-left: auto;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.compare-legend span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -362,6 +422,16 @@
|
||||
<div class="text">환자를 선택하세요</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="compare-toggle" id="compareToggle" style="display:none;">
|
||||
<input type="checkbox" id="compareMode" onchange="toggleCompareMode()">
|
||||
<label for="compareMode">이전 처방과 비교</label>
|
||||
<div class="compare-legend" id="compareLegend" style="display:none;">
|
||||
<span><span class="med-status status-added">🆕 추가</span></span>
|
||||
<span><span class="med-status status-changed">🔄 변경</span></span>
|
||||
<span><span class="med-status status-removed">❌ 중단</span></span>
|
||||
<span><span class="med-status status-same">✓ 동일</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-bar" id="actionBar" style="display:none;">
|
||||
<button class="btn btn-secondary" onclick="selectAll()">전체 선택</button>
|
||||
<button class="btn btn-secondary" onclick="previewLabels()" style="background:#3b82f6;color:#fff;">👁️ 미리보기</button>
|
||||
@ -401,8 +471,10 @@
|
||||
<script>
|
||||
let currentPrescriptionId = null;
|
||||
let currentPatientCode = null;
|
||||
let currentMedications = [];
|
||||
let historyData = [];
|
||||
let historyIndex = 0;
|
||||
let compareMode = false;
|
||||
|
||||
// HTML 이스케이프
|
||||
function escapeHtml(text) {
|
||||
@ -547,6 +619,9 @@
|
||||
|
||||
document.getElementById('actionBar').style.display = 'flex';
|
||||
|
||||
// 현재 약품 저장
|
||||
currentMedications = data.medications;
|
||||
|
||||
// 이전 처방 로드
|
||||
currentPatientCode = data.patient.code;
|
||||
if (currentPatientCode) {
|
||||
@ -571,9 +646,11 @@
|
||||
historyData = data.history;
|
||||
historyIndex = 0;
|
||||
section.style.display = 'block';
|
||||
document.getElementById('compareToggle').style.display = 'flex';
|
||||
renderHistory();
|
||||
} else {
|
||||
section.style.display = 'none';
|
||||
document.getElementById('compareToggle').style.display = 'none';
|
||||
historyData = [];
|
||||
}
|
||||
} catch (err) {
|
||||
@ -642,6 +719,7 @@
|
||||
if (historyIndex > 0) {
|
||||
historyIndex--;
|
||||
renderHistory();
|
||||
if (compareMode) applyCompareMode();
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,14 +727,186 @@
|
||||
if (historyIndex < historyData.length - 1) {
|
||||
historyIndex++;
|
||||
renderHistory();
|
||||
if (compareMode) applyCompareMode();
|
||||
}
|
||||
}
|
||||
|
||||
// 비교 모드 토글
|
||||
function toggleCompareMode() {
|
||||
compareMode = document.getElementById('compareMode').checked;
|
||||
document.getElementById('compareLegend').style.display = compareMode ? 'flex' : 'none';
|
||||
|
||||
if (compareMode) {
|
||||
applyCompareMode();
|
||||
} else {
|
||||
// 비교 모드 해제 - 테이블 다시 렌더링
|
||||
rerenderMedicationTable();
|
||||
}
|
||||
}
|
||||
|
||||
// 처방 비교 로직
|
||||
function comparePrescriptions(current, previous) {
|
||||
const result = [];
|
||||
const prevMap = new Map(previous.map(m => [m.medication_code, m]));
|
||||
const currCodes = new Set(current.map(m => m.medication_code));
|
||||
|
||||
// 현재 처방 약품 처리
|
||||
for (const curr of current) {
|
||||
const prev = prevMap.get(curr.medication_code);
|
||||
if (!prev) {
|
||||
// 추가된 약
|
||||
result.push({ ...curr, status: 'added' });
|
||||
} else {
|
||||
// 비교
|
||||
const changes = [];
|
||||
if (parseFloat(curr.dosage) !== parseFloat(prev.dosage)) {
|
||||
changes.push({ field: 'dosage', from: prev.dosage, to: curr.dosage });
|
||||
}
|
||||
if (parseInt(curr.frequency) !== parseInt(prev.frequency)) {
|
||||
changes.push({ field: 'frequency', from: prev.frequency, to: curr.frequency });
|
||||
}
|
||||
if (parseInt(curr.duration) !== parseInt(prev.duration)) {
|
||||
changes.push({ field: 'duration', from: prev.duration, to: curr.duration });
|
||||
}
|
||||
|
||||
if (changes.length > 0) {
|
||||
result.push({ ...curr, status: 'changed', changes });
|
||||
} else {
|
||||
result.push({ ...curr, status: 'same' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 중단된 약 (이전에 있었는데 현재 없음)
|
||||
for (const prev of previous) {
|
||||
if (!currCodes.has(prev.medication_code)) {
|
||||
result.push({ ...prev, status: 'removed' });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 비교 모드 적용
|
||||
function applyCompareMode() {
|
||||
if (historyData.length === 0 || !currentMedications.length) return;
|
||||
|
||||
const prevMeds = historyData[historyIndex].medications || [];
|
||||
const compared = comparePrescriptions(currentMedications, prevMeds);
|
||||
|
||||
// 테이블 다시 렌더링
|
||||
renderComparedTable(compared);
|
||||
}
|
||||
|
||||
// 비교 결과 테이블 렌더링
|
||||
function renderComparedTable(compared) {
|
||||
const medList = document.getElementById('medicationList');
|
||||
|
||||
// 상태별 정렬: 추가 > 변경 > 동일 > 중단
|
||||
const order = { added: 0, changed: 1, same: 2, removed: 3 };
|
||||
compared.sort((a, b) => order[a.status] - order[b.status]);
|
||||
|
||||
medList.innerHTML = `
|
||||
<table class="med-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40px;"><input type="checkbox" id="checkAll" onchange="toggleAll(this)"></th>
|
||||
<th>약품명</th>
|
||||
<th>상태</th>
|
||||
<th>용량</th>
|
||||
<th>횟수</th>
|
||||
<th>일수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${compared.map(m => {
|
||||
const rowClass = 'row-' + m.status;
|
||||
const statusLabel = {
|
||||
added: '<span class="med-status status-added">🆕 추가</span>',
|
||||
removed: '<span class="med-status status-removed">❌ 중단</span>',
|
||||
changed: '<span class="med-status status-changed">🔄 변경</span>',
|
||||
same: '<span class="med-status status-same">✓ 동일</span>'
|
||||
}[m.status];
|
||||
|
||||
// 변경된 필드 찾기
|
||||
const getChangeValue = (field, value) => {
|
||||
if (m.status !== 'changed' || !m.changes) return value || '-';
|
||||
const change = m.changes.find(c => c.field === field);
|
||||
if (change) {
|
||||
return `<span class="change-from">${change.from || '-'}</span>` +
|
||||
`<span class="change-arrow">→</span>` +
|
||||
`<span class="change-to">${change.to || '-'}</span>`;
|
||||
}
|
||||
return value || '-';
|
||||
};
|
||||
|
||||
const disabled = m.status === 'removed' ? 'disabled' : '';
|
||||
|
||||
return `
|
||||
<tr class="${rowClass}" data-add-info="${escapeHtml(m.add_info || '')}">
|
||||
<td><input type="checkbox" class="med-check" data-code="${m.medication_code}" ${disabled}></td>
|
||||
<td>
|
||||
<div class="med-name">${m.med_name || m.medication_code}</div>
|
||||
<div class="med-code">${m.medication_code}</div>
|
||||
${m.add_info ? `<div style="font-size:0.75rem;color:#6b7280;">${escapeHtml(m.add_info)}</div>` : ''}
|
||||
</td>
|
||||
<td>${statusLabel}</td>
|
||||
<td>${m.status === 'changed' ? getChangeValue('dosage', m.dosage) : (m.dosage || '-')}</td>
|
||||
<td>${m.status === 'changed' ? getChangeValue('frequency', m.frequency) : (m.frequency || '-')}회</td>
|
||||
<td>${m.status === 'changed' ? getChangeValue('duration', m.duration) : (m.duration || '-')}일</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
// 일반 테이블로 복원
|
||||
function rerenderMedicationTable() {
|
||||
if (!currentMedications.length) return;
|
||||
|
||||
const medList = document.getElementById('medicationList');
|
||||
medList.innerHTML = `
|
||||
<table class="med-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40px;"><input type="checkbox" id="checkAll" onchange="toggleAll(this)"></th>
|
||||
<th>약품명</th>
|
||||
<th>제형</th>
|
||||
<th>용량</th>
|
||||
<th>횟수</th>
|
||||
<th>일수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${currentMedications.map(m => `
|
||||
<tr data-add-info="${escapeHtml(m.add_info || '')}">
|
||||
<td><input type="checkbox" class="med-check" data-code="${m.medication_code}"></td>
|
||||
<td>
|
||||
<div class="med-name">${m.med_name || m.medication_code}</div>
|
||||
<div class="med-code">${m.medication_code}</div>
|
||||
${m.add_info ? `<div style="font-size:0.75rem;color:#6b7280;">${escapeHtml(m.add_info)}</div>` : ''}
|
||||
</td>
|
||||
<td>${m.formulation ? `<span class="med-form">${m.formulation}</span>` : '-'}</td>
|
||||
<td><span class="med-dosage">${m.dosage || '-'}</span></td>
|
||||
<td>${m.frequency || '-'}회</td>
|
||||
<td>${m.duration || '-'}일</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
}
|
||||
|
||||
// 상세 초기화
|
||||
function clearDetail() {
|
||||
document.getElementById('detailHeader').style.display = 'none';
|
||||
document.getElementById('actionBar').style.display = 'none';
|
||||
document.getElementById('historySection').style.display = 'none';
|
||||
document.getElementById('compareToggle').style.display = 'none';
|
||||
document.getElementById('compareMode').checked = false;
|
||||
document.getElementById('compareLegend').style.display = 'none';
|
||||
document.getElementById('medicationList').innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="icon">👈</div>
|
||||
@ -665,8 +915,10 @@
|
||||
`;
|
||||
currentPrescriptionId = null;
|
||||
currentPatientCode = null;
|
||||
currentMedications = [];
|
||||
historyData = [];
|
||||
historyIndex = 0;
|
||||
compareMode = false;
|
||||
}
|
||||
|
||||
// 전체 선택 토글
|
||||
|
||||
Loading…
Reference in New Issue
Block a user