feat: PMR OTC 구매 이력 기능
- /pmr/api/patient/<cus_code>/otc: OTC 구매 이력 API
- SALE_MAIN + SALE_SUB (PRESERIAL='V' = OTC)
- 💊 OTC 뱃지 클릭 → 모달로 구매 이력 표시
- 자주 구매하는 품목 요약
- 방문/금액 통계
This commit is contained in:
@@ -150,6 +150,125 @@
|
||||
color: #92400e !important;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.detail-header .rx-info .otc-badge {
|
||||
background: #dbeafe !important;
|
||||
color: #1e40af !important;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.detail-header .rx-info .otc-badge:hover {
|
||||
background: #bfdbfe !important;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* OTC 모달 */
|
||||
.otc-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.6);
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.otc-modal-content {
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
.otc-modal-header {
|
||||
background: linear-gradient(135deg, #3b82f6, #60a5fa);
|
||||
color: #fff;
|
||||
padding: 20px 25px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.otc-modal-header h3 { margin: 0; font-size: 1.2rem; }
|
||||
.otc-modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.otc-modal-close:hover { opacity: 1; }
|
||||
.otc-summary {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 15px 25px;
|
||||
background: #f8fafc;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.otc-summary-item {
|
||||
text-align: center;
|
||||
}
|
||||
.otc-summary-item .num {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #1e40af;
|
||||
}
|
||||
.otc-summary-item .label {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
}
|
||||
.otc-frequent {
|
||||
padding: 15px 25px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.otc-frequent h4 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 0.9rem;
|
||||
color: #475569;
|
||||
}
|
||||
.otc-frequent-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.otc-frequent-item {
|
||||
background: #eff6ff;
|
||||
color: #1e40af;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.otc-purchases {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 15px 25px;
|
||||
}
|
||||
.otc-purchase {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.otc-purchase-header {
|
||||
background: #f1f5f9;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.otc-purchase-header .date { color: #475569; font-weight: 600; }
|
||||
.otc-purchase-header .amount { color: #1e40af; font-weight: 600; }
|
||||
.otc-purchase-items {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.otc-purchase-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 5px 0;
|
||||
font-size: 0.85rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
.otc-purchase-item:last-child { border-bottom: none; }
|
||||
.otc-purchase-item .name { color: #1e293b; }
|
||||
.otc-purchase-item .qty { color: #64748b; }
|
||||
|
||||
/* 약품 목록 */
|
||||
.medication-list {
|
||||
@@ -443,6 +562,19 @@
|
||||
<button class="btn btn-primary" onclick="printLabels()">🖨️ 라벨 인쇄</button>
|
||||
</div>
|
||||
|
||||
<!-- OTC 구매 이력 모달 -->
|
||||
<div class="otc-modal" id="otcModal">
|
||||
<div class="otc-modal-content">
|
||||
<div class="otc-modal-header">
|
||||
<h3>💊 OTC 구매 이력</h3>
|
||||
<button class="otc-modal-close" onclick="closeOtcModal()">×</button>
|
||||
</div>
|
||||
<div class="otc-summary" id="otcSummary"></div>
|
||||
<div class="otc-frequent" id="otcFrequent"></div>
|
||||
<div class="otc-purchases" id="otcPurchases"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 미리보기 모달 -->
|
||||
<div id="previewModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:1000;overflow-y:auto;">
|
||||
<div style="max-width:400px;margin:50px auto;background:#fff;border-radius:12px;padding:20px;">
|
||||
@@ -480,6 +612,7 @@
|
||||
let historyData = [];
|
||||
let historyIndex = 0;
|
||||
let compareMode = false;
|
||||
let otcData = null;
|
||||
|
||||
// HTML 이스케이프
|
||||
function escapeHtml(text) {
|
||||
@@ -644,6 +777,7 @@
|
||||
currentPatientCode = data.patient.code;
|
||||
if (currentPatientCode) {
|
||||
loadPatientHistory(currentPatientCode, prescriptionId);
|
||||
checkOtcHistory(currentPatientCode);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -917,6 +1051,89 @@
|
||||
`;
|
||||
}
|
||||
|
||||
// OTC 구매 이력 체크
|
||||
async function checkOtcHistory(cusCode) {
|
||||
try {
|
||||
const res = await fetch(`/pmr/api/patient/${cusCode}/otc?limit=20`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success && data.count > 0) {
|
||||
otcData = data;
|
||||
// OTC 뱃지 추가 (질병 뱃지 앞에)
|
||||
const rxInfo = document.getElementById('rxInfo');
|
||||
const otcBadge = `<span class="otc-badge" onclick="showOtcModal()">💊 OTC ${data.count}건</span>`;
|
||||
rxInfo.innerHTML = otcBadge + rxInfo.innerHTML;
|
||||
} else {
|
||||
otcData = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('OTC check error:', err);
|
||||
otcData = null;
|
||||
}
|
||||
}
|
||||
|
||||
// OTC 모달 표시
|
||||
function showOtcModal() {
|
||||
if (!otcData) return;
|
||||
|
||||
const modal = document.getElementById('otcModal');
|
||||
const summary = document.getElementById('otcSummary');
|
||||
const frequent = document.getElementById('otcFrequent');
|
||||
const purchases = document.getElementById('otcPurchases');
|
||||
|
||||
// 요약
|
||||
summary.innerHTML = `
|
||||
<div class="otc-summary-item">
|
||||
<div class="num">${otcData.summary.total_visits}</div>
|
||||
<div class="label">방문</div>
|
||||
</div>
|
||||
<div class="otc-summary-item">
|
||||
<div class="num">${(otcData.summary.total_amount / 10000).toFixed(1)}만</div>
|
||||
<div class="label">총 구매액</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 자주 구매하는 품목
|
||||
if (otcData.summary.frequent_items && otcData.summary.frequent_items.length > 0) {
|
||||
frequent.innerHTML = `
|
||||
<h4>🔥 자주 구매하는 품목</h4>
|
||||
<div class="otc-frequent-list">
|
||||
${otcData.summary.frequent_items.map(item =>
|
||||
`<span class="otc-frequent-item">${item.name} (${item.count}회)</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
`;
|
||||
frequent.style.display = 'block';
|
||||
} else {
|
||||
frequent.style.display = 'none';
|
||||
}
|
||||
|
||||
// 구매 이력
|
||||
purchases.innerHTML = otcData.purchases.map(p => `
|
||||
<div class="otc-purchase">
|
||||
<div class="otc-purchase-header">
|
||||
<span class="date">📅 ${p.date?.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3') || p.datetime}</span>
|
||||
<span class="amount">${p.amount.toLocaleString()}원</span>
|
||||
</div>
|
||||
<div class="otc-purchase-items">
|
||||
${p.items.map(item => `
|
||||
<div class="otc-purchase-item">
|
||||
<span class="name">${item.name}</span>
|
||||
<span class="qty">${item.quantity}개 / ${item.price.toLocaleString()}원</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
modal.style.display = 'block';
|
||||
}
|
||||
|
||||
// OTC 모달 닫기
|
||||
function closeOtcModal() {
|
||||
document.getElementById('otcModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 상세 초기화
|
||||
function clearDetail() {
|
||||
document.getElementById('detailHeader').style.display = 'none';
|
||||
@@ -925,6 +1142,7 @@
|
||||
document.getElementById('compareToggle').style.display = 'none';
|
||||
document.getElementById('compareMode').checked = false;
|
||||
document.getElementById('compareLegend').style.display = 'none';
|
||||
document.getElementById('otcModal').style.display = 'none';
|
||||
document.getElementById('medicationList').innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="icon">👈</div>
|
||||
@@ -937,6 +1155,7 @@
|
||||
historyData = [];
|
||||
historyIndex = 0;
|
||||
compareMode = false;
|
||||
otcData = null;
|
||||
}
|
||||
|
||||
// 전체 선택 토글
|
||||
|
||||
Reference in New Issue
Block a user