541 lines
24 KiB
HTML
541 lines
24 KiB
HTML
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body { font-family: var(--font-mono, monospace); }
|
||
|
||
.his-shell {
|
||
background: var(--color-background-primary);
|
||
border: 0.5px solid var(--color-border-tertiary);
|
||
border-radius: var(--border-radius-lg);
|
||
overflow: hidden;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.his-titlebar {
|
||
background: var(--color-background-secondary);
|
||
border-bottom: 0.5px solid var(--color-border-tertiary);
|
||
padding: 8px 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.dot { width: 10px; height: 10px; border-radius: 50%; }
|
||
.dot-r { background: #E24B4A; }
|
||
.dot-y { background: #EF9F27; }
|
||
.dot-g { background: #639922; }
|
||
.his-title { font-size: 12px; color: var(--color-text-secondary); margin-left: 4px; }
|
||
|
||
.his-nav {
|
||
display: flex;
|
||
gap: 0;
|
||
border-bottom: 0.5px solid var(--color-border-tertiary);
|
||
background: var(--color-background-secondary);
|
||
}
|
||
.nav-tab {
|
||
padding: 8px 16px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
color: var(--color-text-secondary);
|
||
border-bottom: 2px solid transparent;
|
||
transition: all 0.15s;
|
||
font-family: var(--font-sans);
|
||
background: none;
|
||
border-top: none;
|
||
border-left: none;
|
||
border-right: none;
|
||
}
|
||
.nav-tab:hover { color: var(--color-text-primary); background: var(--color-background-primary); }
|
||
.nav-tab.active {
|
||
color: var(--color-text-primary);
|
||
border-bottom: 2px solid var(--color-text-primary);
|
||
background: var(--color-background-primary);
|
||
}
|
||
|
||
.his-body { display: flex; height: 420px; }
|
||
|
||
.his-sidebar {
|
||
width: 180px;
|
||
border-right: 0.5px solid var(--color-border-tertiary);
|
||
overflow-y: auto;
|
||
background: var(--color-background-secondary);
|
||
padding: 8px 0;
|
||
flex-shrink: 0;
|
||
}
|
||
.sidebar-section {
|
||
padding: 4px 12px 2px;
|
||
font-size: 10px;
|
||
color: var(--color-text-tertiary);
|
||
letter-spacing: 0.06em;
|
||
font-family: var(--font-sans);
|
||
text-transform: uppercase;
|
||
margin-top: 6px;
|
||
}
|
||
.sidebar-item {
|
||
padding: 5px 14px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
color: var(--color-text-secondary);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.1s;
|
||
font-family: var(--font-sans);
|
||
border-radius: 0;
|
||
}
|
||
.sidebar-item:hover { background: var(--color-background-primary); color: var(--color-text-primary); }
|
||
.sidebar-item.active { background: var(--color-background-primary); color: var(--color-text-primary); font-weight: 500; }
|
||
.sidebar-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
|
||
|
||
.his-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
|
||
|
||
.his-terminal {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 12px 16px;
|
||
background: var(--color-background-primary);
|
||
font-family: var(--font-mono, monospace);
|
||
font-size: 12.5px;
|
||
line-height: 1.65;
|
||
}
|
||
|
||
.line-out { color: var(--color-text-primary); }
|
||
.line-err { color: var(--color-text-danger); }
|
||
.line-ok { color: var(--color-text-success); }
|
||
.line-dim { color: var(--color-text-tertiary); }
|
||
.line-hdr { color: var(--color-text-info); font-weight: 500; }
|
||
.line-prompt { color: var(--color-text-secondary); }
|
||
.line-cmd { color: var(--color-text-primary); }
|
||
|
||
.his-input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 8px 12px;
|
||
border-top: 0.5px solid var(--color-border-tertiary);
|
||
gap: 8px;
|
||
background: var(--color-background-secondary);
|
||
}
|
||
.his-prompt-label { color: var(--color-text-tertiary); font-size: 12px; white-space: nowrap; }
|
||
.his-input {
|
||
flex: 1;
|
||
background: none;
|
||
border: none;
|
||
outline: none;
|
||
font-size: 12.5px;
|
||
font-family: var(--font-mono, monospace);
|
||
color: var(--color-text-primary);
|
||
}
|
||
.his-run {
|
||
font-size: 11px;
|
||
padding: 4px 10px;
|
||
border-radius: var(--border-radius-md);
|
||
background: none;
|
||
border: 0.5px solid var(--color-border-secondary);
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
font-family: var(--font-sans);
|
||
}
|
||
.his-run:hover { background: var(--color-background-primary); color: var(--color-text-primary); }
|
||
|
||
.quick-btn-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 5px;
|
||
padding: 8px 12px;
|
||
border-top: 0.5px solid var(--color-border-tertiary);
|
||
background: var(--color-background-secondary);
|
||
}
|
||
.qbtn {
|
||
font-size: 11px;
|
||
padding: 3px 8px;
|
||
border-radius: var(--border-radius-md);
|
||
background: none;
|
||
border: 0.5px solid var(--color-border-tertiary);
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
font-family: var(--font-mono, monospace);
|
||
}
|
||
.qbtn:hover { background: var(--color-background-primary); color: var(--color-text-primary); border-color: var(--color-border-secondary); }
|
||
|
||
.stats-panel {
|
||
padding: 16px;
|
||
font-family: var(--font-sans);
|
||
overflow-y: auto;
|
||
flex: 1;
|
||
}
|
||
.stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 16px; }
|
||
.stat-card {
|
||
background: var(--color-background-secondary);
|
||
border-radius: var(--border-radius-md);
|
||
padding: 12px;
|
||
}
|
||
.stat-label { font-size: 11px; color: var(--color-text-secondary); margin-bottom: 4px; }
|
||
.stat-value { font-size: 22px; font-weight: 500; color: var(--color-text-primary); }
|
||
.stat-sub { font-size: 11px; color: var(--color-text-tertiary); margin-top: 2px; }
|
||
|
||
.table-wrap { border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-md); overflow: hidden; }
|
||
.his-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||
.his-table th {
|
||
text-align: left;
|
||
padding: 7px 12px;
|
||
background: var(--color-background-secondary);
|
||
color: var(--color-text-secondary);
|
||
font-weight: 500;
|
||
border-bottom: 0.5px solid var(--color-border-tertiary);
|
||
}
|
||
.his-table td {
|
||
padding: 7px 12px;
|
||
border-bottom: 0.5px solid var(--color-border-tertiary);
|
||
color: var(--color-text-primary);
|
||
}
|
||
.his-table tr:last-child td { border-bottom: none; }
|
||
.his-table tr:hover td { background: var(--color-background-secondary); }
|
||
.badge {
|
||
display: inline-block;
|
||
font-size: 10px;
|
||
padding: 2px 7px;
|
||
border-radius: 99px;
|
||
}
|
||
.badge-ok { background: var(--color-background-success); color: var(--color-text-success); }
|
||
.badge-warn { background: var(--color-background-warning); color: var(--color-text-warning); }
|
||
.badge-err { background: var(--color-background-danger); color: var(--color-text-danger); }
|
||
.badge-info { background: var(--color-background-info); color: var(--color-text-info); }
|
||
|
||
.ward-panel { padding: 16px; font-family: var(--font-sans); overflow-y: auto; flex: 1; }
|
||
.ward-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin-bottom: 16px; }
|
||
.bed-cell {
|
||
border: 0.5px solid var(--color-border-tertiary);
|
||
border-radius: var(--border-radius-md);
|
||
padding: 8px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.12s;
|
||
}
|
||
.bed-cell:hover { border-color: var(--color-border-secondary); }
|
||
.bed-cell.occupied { background: var(--color-background-info); border-color: var(--color-border-info); }
|
||
.bed-cell.empty { background: var(--color-background-secondary); }
|
||
.bed-num { font-size: 11px; font-weight: 500; color: var(--color-text-secondary); }
|
||
.bed-cell.occupied .bed-num { color: var(--color-text-info); }
|
||
.bed-name { font-size: 10px; color: var(--color-text-tertiary); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
|
||
.section-label { font-size: 11px; color: var(--color-text-secondary); margin-bottom: 8px; font-weight: 500; }
|
||
.ward-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
|
||
.ward-name { font-size: 12px; width: 80px; color: var(--color-text-secondary); }
|
||
.bed-mini-row { display: flex; gap: 4px; flex-wrap: wrap; }
|
||
.bed-mini { width: 16px; height: 16px; border-radius: 3px; border: 0.5px solid var(--color-border-tertiary); background: var(--color-background-secondary); }
|
||
.bed-mini.occ { background: var(--color-background-info); border-color: var(--color-border-info); }
|
||
</style>
|
||
|
||
<div class="his-shell">
|
||
<div class="his-titlebar">
|
||
<div class="dot dot-r"></div>
|
||
<div class="dot dot-y"></div>
|
||
<div class="dot dot-g"></div>
|
||
<span class="his-title">HIS — 轻量级医疗信息管理系统 v1.0 | 演示模式</span>
|
||
</div>
|
||
<div class="his-nav">
|
||
<button class="nav-tab active" onclick="switchTab('cli')">命令行</button>
|
||
<button class="nav-tab" onclick="switchTab('patients')">患者管理</button>
|
||
<button class="nav-tab" onclick="switchTab('wards')">病房管理</button>
|
||
<button class="nav-tab" onclick="switchTab('report')">统计报表</button>
|
||
</div>
|
||
|
||
<!-- CLI Tab -->
|
||
<div id="tab-cli" class="his-body">
|
||
<div class="his-sidebar">
|
||
<div class="sidebar-section">常用命令</div>
|
||
<div class="sidebar-item" onclick="fillCmd('help')"><div class="sidebar-dot" style="background:#639922"></div>help</div>
|
||
<div class="sidebar-item" onclick="fillCmd('list patient')"><div class="sidebar-dot" style="background:#378ADD"></div>list patient</div>
|
||
<div class="sidebar-item" onclick="fillCmd('find patient --name 张')"><div class="sidebar-dot" style="background:#378ADD"></div>find patient</div>
|
||
<div class="sidebar-item" onclick="fillCmd('add patient')"><div class="sidebar-dot" style="background:#1D9E75"></div>add patient</div>
|
||
<div class="sidebar-item" onclick="fillCmd('admit P001')"><div class="sidebar-dot" style="background:#EF9F27"></div>admit</div>
|
||
<div class="sidebar-item" onclick="fillCmd('discharge P003')"><div class="sidebar-dot" style="background:#E24B4A"></div>discharge</div>
|
||
<div class="sidebar-section">药房</div>
|
||
<div class="sidebar-item" onclick="fillCmd('dispense M001 --qty 3')"><div class="sidebar-dot" style="background:#7F77DD"></div>dispense</div>
|
||
<div class="sidebar-item" onclick="fillCmd('stock M001')"><div class="sidebar-dot" style="background:#7F77DD"></div>stock</div>
|
||
<div class="sidebar-section">报表</div>
|
||
<div class="sidebar-item" onclick="fillCmd('report doctor')"><div class="sidebar-dot" style="background:#D85A30"></div>report doctor</div>
|
||
<div class="sidebar-item" onclick="fillCmd('report revenue')"><div class="sidebar-dot" style="background:#D85A30"></div>report revenue</div>
|
||
</div>
|
||
<div class="his-main">
|
||
<div class="his-terminal" id="terminal"></div>
|
||
<div class="quick-btn-row">
|
||
<button class="qbtn" onclick="runCmd('help')">help</button>
|
||
<button class="qbtn" onclick="runCmd('list patient')">list patient</button>
|
||
<button class="qbtn" onclick="runCmd('find patient --name 张三')">find --name 张三</button>
|
||
<button class="qbtn" onclick="runCmd('report revenue')">report revenue</button>
|
||
<button class="qbtn" onclick="runCmd('admit P001')">admit P001</button>
|
||
<button class="qbtn" onclick="runCmd('dispense M001 --qty 99')">dispense 99(测试异常)</button>
|
||
</div>
|
||
<div class="his-input-row">
|
||
<span class="his-prompt-label">his ></span>
|
||
<input class="his-input" id="cmd-input" placeholder="输入命令..." onkeydown="if(event.key==='Enter') runCmd()" />
|
||
<button class="his-run" onclick="runCmd()">执行 ↵</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Patients Tab -->
|
||
<div id="tab-patients" class="his-body" style="display:none;">
|
||
<div class="his-main">
|
||
<div class="stats-panel">
|
||
<div class="stats-grid">
|
||
<div class="stat-card"><div class="stat-label">门诊患者</div><div class="stat-value">87</div><div class="stat-sub">容量 100</div></div>
|
||
<div class="stat-card"><div class="stat-label">住院患者</div><div class="stat-value">23</div><div class="stat-sub">容量 30</div></div>
|
||
<div class="stat-card"><div class="stat-label">今日挂号</div><div class="stat-value">14</div><div class="stat-sub">较昨日 +3</div></div>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="his-table">
|
||
<thead><tr><th>患者ID</th><th>姓名</th><th>年龄</th><th>科室</th><th>状态</th><th>主治医生</th></tr></thead>
|
||
<tbody id="patient-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Wards Tab -->
|
||
<div id="tab-wards" class="his-body" style="display:none;">
|
||
<div class="his-main">
|
||
<div class="ward-panel">
|
||
<div class="stats-grid" style="grid-template-columns:repeat(3,1fr);margin-bottom:16px;">
|
||
<div class="stat-card"><div class="stat-label">总床位</div><div class="stat-value">60</div><div class="stat-sub">3个病房</div></div>
|
||
<div class="stat-card"><div class="stat-label">已占用</div><div class="stat-value">23</div><div class="stat-sub">使用率 38%</div></div>
|
||
<div class="stat-card"><div class="stat-label">空余</div><div class="stat-value">37</div><div class="stat-sub">可立即入住</div></div>
|
||
</div>
|
||
<div class="section-label">内科病房(20床)</div>
|
||
<div class="bed-mini-row" id="ward-nei" style="margin-bottom:14px;"></div>
|
||
<div class="section-label">外科病房(20床)</div>
|
||
<div class="bed-mini-row" id="ward-wai" style="margin-bottom:14px;"></div>
|
||
<div class="section-label">特护病房(20床)</div>
|
||
<div class="bed-mini-row" id="ward-te" style="margin-bottom:14px;"></div>
|
||
<div style="display:flex;align-items:center;gap:12px;font-size:11px;color:var(--color-text-secondary);margin-top:8px;">
|
||
<div style="display:flex;align-items:center;gap:4px;"><div class="bed-mini occ"></div>已占用</div>
|
||
<div style="display:flex;align-items:center;gap:4px;"><div class="bed-mini"></div>空余</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Report Tab -->
|
||
<div id="tab-report" class="his-body" style="display:none;">
|
||
<div class="his-main">
|
||
<div class="stats-panel">
|
||
<div class="stats-grid" style="grid-template-columns:repeat(2,1fr);margin-bottom:16px;">
|
||
<div class="stat-card"><div class="stat-label">本月营业额</div><div class="stat-value">¥128,400</div><div class="stat-sub">检查+药品+住院</div></div>
|
||
<div class="stat-card"><div class="stat-label">药品出库</div><div class="stat-value">312</div><div class="stat-sub">本月处方次数</div></div>
|
||
</div>
|
||
<div class="section-label" style="margin-bottom:8px;">医生工作量排行</div>
|
||
<div class="table-wrap" style="margin-bottom:14px;">
|
||
<table class="his-table">
|
||
<thead><tr><th>医生</th><th>科室</th><th>职称</th><th>本月诊疗</th><th>住院管理</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>李建国</td><td>内科</td><td>主任医师</td><td>89</td><td>12</td></tr>
|
||
<tr><td>王秀梅</td><td>外科</td><td>副主任医师</td><td>74</td><td>8</td></tr>
|
||
<tr><td>张伟</td><td>内科</td><td>主治医师</td><td>67</td><td>5</td></tr>
|
||
<tr><td>刘芳</td><td>儿科</td><td>主治医师</td><td>61</td><td>3</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="section-label" style="margin-bottom:8px;">药品库存预警</div>
|
||
<div class="table-wrap">
|
||
<table class="his-table">
|
||
<thead><tr><th>药品ID</th><th>通用名</th><th>商品名</th><th>库存</th><th>状态</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>M003</td><td>阿莫西林</td><td>阿莫仙</td><td>8</td><td><span class="badge badge-err">库存紧张</span></td></tr>
|
||
<tr><td>M007</td><td>布洛芬</td><td>芬必得</td><td>31</td><td><span class="badge badge-ok">正常</span></td></tr>
|
||
<tr><td>M011</td><td>氯化钠注射液</td><td>生理盐水</td><td>15</td><td><span class="badge badge-warn">偏低</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const patients = [
|
||
{id:'P001',name:'张三',age:34,dept:'内科',status:'门诊',doctor:'李建国'},
|
||
{id:'P002',name:'张三',age:52,dept:'外科',status:'住院',doctor:'王秀梅'},
|
||
{id:'P003',name:'李梅',age:28,dept:'儿科',status:'门诊',doctor:'刘芳'},
|
||
{id:'P004',name:'王建',age:67,dept:'内科',status:'住院',doctor:'李建国'},
|
||
{id:'P005',name:'陈晓燕',age:41,dept:'外科',status:'已出院',doctor:'王秀梅'},
|
||
{id:'P006',name:'赵磊',age:19,dept:'骨科',status:'门诊',doctor:'张伟'},
|
||
];
|
||
|
||
function statusBadge(s){
|
||
if(s==='住院') return `<span class="badge badge-info">${s}</span>`;
|
||
if(s==='门诊') return `<span class="badge badge-ok">${s}</span>`;
|
||
return `<span class="badge badge-warn">${s}</span>`;
|
||
}
|
||
|
||
function renderPatients(){
|
||
const tb = document.getElementById('patient-tbody');
|
||
tb.innerHTML = patients.map(p=>`<tr><td>${p.id}</td><td>${p.name}</td><td>${p.age}</td><td>${p.dept}</td><td>${statusBadge(p.status)}</td><td>${p.doctor}</td></tr>`).join('');
|
||
}
|
||
|
||
function renderWards(){
|
||
const occ = [1,2,4,6,8,9,11,14,15,16,17,19,3,5,7,10,12,13,18,20,22,24,27];
|
||
['nei','wai','te'].forEach((id,wi)=>{
|
||
const el = document.getElementById('ward-'+id);
|
||
el.innerHTML = Array.from({length:20},(_,i)=>{
|
||
const n = wi*20+i+1;
|
||
return `<div class="bed-mini ${occ.includes(n)?'occ':''}"></div>`;
|
||
}).join('');
|
||
});
|
||
}
|
||
|
||
const cmdHistory = [];
|
||
let histIdx = -1;
|
||
|
||
const termOutput = [
|
||
{cls:'line-hdr', text:'HIS 医疗管理系统 v1.0 [演示模式]'},
|
||
{cls:'line-dim', text:'加载数据: patients(87) doctors(20) wards(3) medicines(24)'},
|
||
{cls:'line-dim', text:'输入 help 查看可用命令。'},
|
||
{cls:'line-out', text:''},
|
||
];
|
||
|
||
function renderTerminal(){
|
||
const t = document.getElementById('terminal');
|
||
t.innerHTML = termOutput.map(l=>`<div class="${l.cls}">${l.text}</div>`).join('');
|
||
t.scrollTop = t.scrollHeight;
|
||
}
|
||
|
||
function append(cls, text){ termOutput.push({cls,text}); renderTerminal(); }
|
||
|
||
function fillCmd(cmd){ document.getElementById('cmd-input').value = cmd; document.getElementById('cmd-input').focus(); }
|
||
|
||
function runCmd(preset){
|
||
const input = document.getElementById('cmd-input');
|
||
const cmd = (preset !== undefined ? preset : input.value).trim();
|
||
if(!cmd) return;
|
||
if(!preset) input.value = '';
|
||
cmdHistory.unshift(cmd); histIdx = -1;
|
||
|
||
append('line-prompt', `his > ${cmd}`);
|
||
|
||
const parts = cmd.split(/\s+/);
|
||
const c = parts[0], sub = parts[1];
|
||
|
||
if(c === 'help'){
|
||
append('line-hdr', '可用命令:');
|
||
[
|
||
' list patient|doctor|medicine|ward',
|
||
' find patient --name <姓名> | --id <ID>',
|
||
' add patient|doctor|medicine',
|
||
' admit <PatientID> — 患者住院分配床位',
|
||
' discharge <PatientID> — 患者出院',
|
||
' dispense <MedID> --qty <数量>',
|
||
' stock <MedID> — 查询药品库存',
|
||
' report doctor|revenue|ward',
|
||
' help | exit',
|
||
].forEach(l=>append('line-out', l));
|
||
|
||
} else if(c==='list' && sub==='patient'){
|
||
append('line-hdr', 'PatientID 姓名 年龄 科室 状态');
|
||
append('line-dim', '─────────────────────────────────────────');
|
||
patients.forEach(p=>{
|
||
const st = p.status==='住院'?'\x1b[34m住院\x1b[0m':p.status;
|
||
append('line-out', `${p.id.padEnd(10)} ${p.name.padEnd(10)} ${String(p.age).padEnd(5)} ${p.dept.padEnd(7)} ${p.status}`);
|
||
});
|
||
append('line-ok', `共 ${patients.length} 条记录(演示数据)`);
|
||
|
||
} else if(c==='find' && sub==='patient'){
|
||
const nm = (cmd.match(/--name\s+(\S+)/)||[])[1];
|
||
const id = (cmd.match(/--id\s+(\S+)/)||[])[1];
|
||
const res = patients.filter(p=>(nm && p.name.includes(nm))||(id && p.id===id));
|
||
if(!nm && !id){ append('line-err','[ERR] 需要 --name 或 --id 参数'); return; }
|
||
if(res.length===0){ append('line-err','[NOT FOUND] 无匹配患者'); return; }
|
||
append('line-hdr', `找到 ${res.length} 名患者:`);
|
||
res.forEach(p=>append('line-ok', ` ${p.id} ${p.name} ${p.age}岁 ${p.dept} ${p.status} 主治:${p.doctor}`));
|
||
|
||
} else if(c==='admit'){
|
||
const pid = parts[1];
|
||
if(!pid){ append('line-err','[ERR] 用法: admit <PatientID>'); return; }
|
||
const p = patients.find(x=>x.id===pid);
|
||
if(!p){ append('line-err',`[ERR] 患者 ${pid} 不存在`); return; }
|
||
if(p.status==='住院'){ append('line-err',`[ERR] 患者 ${p.name} 当前已在住院`); return; }
|
||
append('line-ok', `[OK] 正在为患者 ${p.name}(${pid}) 分配床位...`);
|
||
append('line-ok', `[OK] 分配成功: 内科病房 床位 A-07`);
|
||
p.status='住院';
|
||
|
||
} else if(c==='discharge'){
|
||
const pid = parts[1];
|
||
if(!pid){ append('line-err','[ERR] 用法: discharge <PatientID>'); return; }
|
||
const p = patients.find(x=>x.id===pid);
|
||
if(!p){ append('line-err',`[ERR] 患者 ${pid} 不存在`); return; }
|
||
if(p.status!=='住院'){ append('line-err',`[ERR] 患者 ${p.name} 当前未住院`); return; }
|
||
p.status='已出院';
|
||
append('line-ok', `[OK] 患者 ${p.name}(${pid}) 已办理出院,床位已释放`);
|
||
|
||
} else if(c==='dispense'){
|
||
const mid = parts[1];
|
||
const qty = parseInt((cmd.match(/--qty\s+(\d+)/)||[])[1]||'0');
|
||
if(!mid||!qty){ append('line-err','[ERR] 用法: dispense <MedID> --qty <数量>'); return; }
|
||
if(qty > 50){ append('line-err',`[ERR] 库存不足。当前库存: 12,申请数量: ${qty},拒绝发药`); return; }
|
||
if(qty <= 0){ append('line-err','[ERR] 数量必须为正整数'); return; }
|
||
append('line-ok', `[OK] 发药成功: ${mid} × ${qty},剩余库存: ${12-qty}`);
|
||
|
||
} else if(c==='stock'){
|
||
const mid = parts[1];
|
||
if(!mid){ append('line-err','[ERR] 用法: stock <MedID>'); return; }
|
||
append('line-out', `药品ID: ${mid}`);
|
||
append('line-out', '通用名: 阿莫西林 | 商品名: 阿莫仙 | 别名: AMX');
|
||
append('line-out', '当前库存: 12 | 单价: ¥3.50 | 关联科室: 内科, 儿科');
|
||
append('line-warn'?'line-err':'line-err', '[WARN] 库存低于警戒线(20),建议补货');
|
||
|
||
} else if(c==='report'){
|
||
if(sub==='doctor'){
|
||
append('line-hdr','医生工作量报表');
|
||
append('line-dim', '────────────────────────────────');
|
||
[['李建国','内科','主任','89次诊疗 12住院'],['王秀梅','外科','副主任','74次诊疗 8住院'],
|
||
['张伟','内科','主治','67次诊疗 5住院'],['刘芳','儿科','主治','61次诊疗 3住院']]
|
||
.forEach(r=>append('line-out',` ${r[0].padEnd(6)} ${r[1].padEnd(5)} ${r[2].padEnd(5)} ${r[3]}`));
|
||
} else if(sub==='revenue'){
|
||
append('line-hdr','营业额报表(本月)');
|
||
append('line-out',' 检查费用: ¥48,200');
|
||
append('line-out',' 药品收入: ¥31,600');
|
||
append('line-out',' 住院费用: ¥48,600');
|
||
append('line-dim',' ─────────────────');
|
||
append('line-ok', ' 合计: ¥128,400');
|
||
} else if(sub==='ward'){
|
||
append('line-hdr','床位使用率报表');
|
||
append('line-out',' 内科病房: 14/20 (70%)');
|
||
append('line-out',' 外科病房: 7/20 (35%)');
|
||
append('line-out',' 特护病房: 2/20 (10%)');
|
||
append('line-ok', ' 整体使用率: 38%');
|
||
} else {
|
||
append('line-err','[ERR] 未知报表类型。可用: doctor | revenue | ward');
|
||
}
|
||
|
||
} else if(c==='add'){
|
||
append('line-ok','[交互模式] 演示中省略输入步骤。实际系统将逐字段提示输入并校验。');
|
||
|
||
} else if(c==='exit'){
|
||
append('line-dim','[保存数据到文件...] patients.txt doctors.txt records.txt');
|
||
append('line-ok', '再见!');
|
||
|
||
} else if(cmd===''){
|
||
// nothing
|
||
} else {
|
||
append('line-err', `[ERR] 未知命令: ${c}。输入 help 查看帮助。`);
|
||
}
|
||
}
|
||
|
||
function switchTab(tab){
|
||
document.querySelectorAll('[id^="tab-"]').forEach(el=>el.style.display='none');
|
||
document.querySelectorAll('.nav-tab').forEach(el=>el.classList.remove('active'));
|
||
document.getElementById('tab-'+tab).style.display='flex';
|
||
const tabs = ['cli','patients','wards','report'];
|
||
document.querySelectorAll('.nav-tab')[tabs.indexOf(tab)].classList.add('active');
|
||
if(tab==='patients') renderPatients();
|
||
if(tab==='wards') renderWards();
|
||
}
|
||
|
||
document.getElementById('cmd-input').addEventListener('keydown', e=>{
|
||
if(e.key==='ArrowUp'){ histIdx = Math.min(histIdx+1, cmdHistory.length-1); e.target.value = cmdHistory[histIdx]||''; e.preventDefault(); }
|
||
if(e.key==='ArrowDown'){ histIdx = Math.max(histIdx-1, -1); e.target.value = histIdx>=0 ? cmdHistory[histIdx] : ''; e.preventDefault(); }
|
||
});
|
||
|
||
renderTerminal();
|
||
</script>
|