Compare commits

...
5 Commits
Author SHA1 Message Date
liuyunqin cbf21d1817 feat: 医疗PAD健康宣教抽屉移除时长字段 2026-09-17 18:10:30 +08:00
liuyunqin f1f412d897 feat: 体重管理基础信息移除身份证号与手机号展示 2026-09-17 18:10:30 +08:00
liuyunqin 05f83885ac feat: 健康体检后台单位体检费用统计按单位×体检医院拆行
- 列表增加体检医院/签约医院列并去掉总人数列,原6条单位汇总拆为16条医院明细行
- 缺少项目弹窗增加项目费用/折扣后费用列,统计明细与员工明细的缺少项目数标蓝可点击
- 员工费用明细弹窗新增筛选栏与导出列表数据按钮
- 同步更新 PRD 与 CHANGELOG
2026-09-17 18:10:24 +08:00
liuyunqin 9cd970a31e feat: 一线医疗身份证与联系电话去除脱敏 · 员工分配统计概览卡改员工口径
- 全站演示数据身份证号与联系电话由脱敏改为完整展示
- config-allocate-stat 概览卡改为员工总数量/已分配/未分配三卡,名册统计文案上移
- 同步更新 CHANGELOG
2026-09-17 18:10:16 +08:00
liuyunqin a54d933841 feat: 健康体检员工端取消预约二次确认、胃肠镜预选可重填及报告筛选调整
- 预约日期页取消预约改为二次确认
- 胃肠镜医院预选支持重新填写调整(至少2家/最多3家)
- 现场体检状态口径改为已预约/无预约
- 报告列表新增年份/类型筛选
- 同步更新 PRD 与演示截图
2026-09-17 18:10:07 +08:00
68 changed files with 2469 additions and 1318 deletions
@@ -136,23 +136,6 @@
background: #f7f9fb; border-radius: 12px; padding: 10px 14px; margin-bottom: 16px;
font-size: 13px; font-weight: 600; color: var(--text-secondary); text-align: center;
}
.cancel-modal__label { font-size: 13px; font-weight: 600; margin-bottom: 10px; }
.cancel-modal__label em { color: var(--danger); font-style: normal; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.chip {
padding: 7px 14px; border-radius: 16px; border: 1px solid #e8e8e8; background: #fafafa;
font-size: 13px; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; user-select: none;
}
.chip.selected { background: var(--primary-light); border-color: var(--primary); color: var(--primary-dark); font-weight: 600; }
.other-input {
display: none; width: 100%; border: 1px solid #e8e8e8; border-radius: 12px;
padding: 10px 12px; font-size: 13px; font-family: inherit; line-height: 1.6;
resize: none; outline: none; margin-bottom: 6px; color: var(--text-primary);
}
.other-input.show { display: block; }
.other-input:focus { border-color: var(--primary); }
.cancel-modal__err { display: none; font-size: 12px; color: var(--danger); margin-bottom: 6px; }
.cancel-modal__err.show { display: block; }
.cancel-modal__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.cancel-modal__btn { height: 44px; border-radius: 22px; font-size: 15px; font-weight: 600; border: none; cursor: pointer; }
.cancel-modal__btn--ghost { background: #f5f5f5; color: var(--text-secondary); }
@@ -250,7 +233,7 @@
</div>
<div>
<div class="result-banner__title" id="resultTitle">预约成功</div>
<div class="result-banner__sub" id="resultSub">已为您锁定体检名额,请按预约日期准时到检</div>
<div class="result-banner__sub" id="resultSub">请按预约日期准时到检</div>
<div class="result-banner__reason">
<span>失败原因:所选日期号源已约满</span>
<span class="reason-help" onclick="openReasonModal()">?</span>
@@ -296,10 +279,10 @@
<!-- [交互] 预约成功态底部按钮 -->
<div class="bottom-actions" id="actionsSuccess">
<!-- [交互] 取消预约改为二次确认弹窗,需选择或填写取消原因 -->
<!-- [交互] 取消预约改为二次确认弹窗 -->
<button class="bottom-actions__btn bottom-actions__btn--ghost" onclick="openCancelModal()">取消预约</button>
<!-- [交互] 查看预约详情,跳转至 my-appointments-all.html(常规·待体检) -->
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="window.location.href='my-appointments-all.html?type=routine&status=pending'">查看预约详情</button>
<!-- [交互] 现场体检,跳转至 my-appointments-all.html(常规·待体检,含签到码 -->
<button class="bottom-actions__btn bottom-actions__btn--primary" onclick="window.location.href='my-appointments-all.html?type=routine&status=pending'">现场体检</button>
</div>
<!-- [交互] 预约失败态底部按钮:重新预约,跳回预约日期页 -->
<div class="bottom-actions" id="actionsFail" style="display: none;">
@@ -319,30 +302,16 @@
<div class="reason-modal__label">发生时间</div>
<div class="reason-modal__text">2026-03-16 12:00:35</div>
</div>
<div class="reason-modal__item">
<div class="reason-modal__label">处理建议</div>
<div class="reason-modal__text">请点击「重新预约」选择其他可约日期;若长期无可用号源,可联系健康管理专员协调加号。</div>
</div>
<button class="reason-modal__btn" onclick="closeReasonModal()">知道了</button>
</div>
</div>
<!-- [交互] 取消预约二次确认:单选原因chips,选「其他」时填写具体原因 -->
<!-- [交互] 取消预约二次确认弹窗 -->
<div class="modal-overlay hidden" id="cancelModal">
<div class="cancel-modal">
<div class="cancel-modal__title">确认取消预约</div>
<div class="cancel-modal__desc">取消后名额将释放,如仍需体检请重新预约</div>
<div class="cancel-modal__order">宁夏宝石花医院 · 2026年3月23日</div>
<div class="cancel-modal__label">请选择取消原因 <em>*</em></div>
<div class="chips" id="cancelChips">
<span class="chip" data-reason="时间冲突" onclick="selectChip(this)">时间冲突</span>
<span class="chip" data-reason="医院选择不当" onclick="selectChip(this)">医院选择不当</span>
<span class="chip" data-reason="日期选择不当" onclick="selectChip(this)">日期选择不当</span>
<span class="chip" data-reason="个人健康原因" onclick="selectChip(this)">个人健康原因</span>
<span class="chip" data-reason="other" onclick="selectChip(this)">其他</span>
</div>
<textarea class="other-input" id="otherInput" rows="2" maxlength="100" placeholder="请填写取消原因(必填)"></textarea>
<div class="cancel-modal__err" id="cancelErr">请选择或填写取消原因</div>
<div class="cancel-modal__actions">
<button class="cancel-modal__btn cancel-modal__btn--ghost" onclick="closeCancelModal()">再想想</button>
<button class="cancel-modal__btn cancel-modal__btn--danger" onclick="confirmCancel()">确认取消</button>
@@ -383,36 +352,14 @@
document.getElementById('reasonModal').classList.add('hidden');
}
// [交互] 取消预约二次确认:原因单选 + 其他原因填写 + 校验 + 确认后跳转重新预约
let cancelReason = null;
function selectChip(el) {
document.querySelectorAll('#cancelChips .chip').forEach(c => c.classList.remove('selected'));
el.classList.add('selected');
cancelReason = el.dataset.reason;
document.getElementById('otherInput').classList.toggle('show', cancelReason === 'other');
hideCancelErr();
}
function hideCancelErr() {
document.getElementById('cancelErr').classList.remove('show');
}
// [交互] 取消预约二次确认:确认后提示已取消,稍后跳回预约日期页
function openCancelModal() {
cancelReason = null;
document.querySelectorAll('#cancelChips .chip').forEach(c => c.classList.remove('selected'));
const input = document.getElementById('otherInput');
input.value = '';
input.classList.remove('show');
hideCancelErr();
document.getElementById('cancelModal').classList.remove('hidden');
}
function closeCancelModal() {
document.getElementById('cancelModal').classList.add('hidden');
}
function confirmCancel() {
const input = document.getElementById('otherInput');
if (!cancelReason || (cancelReason === 'other' && !input.value.trim())) {
document.getElementById('cancelErr').classList.add('show');
return;
}
closeCancelModal();
const toast = document.getElementById('stepToast');
toast.textContent = '预约已取消';
@@ -384,7 +384,7 @@
</div>
<div class="modal-field">
<span class="modal-field__label">身份证号</span>
<span class="modal-field__value">6101021994********</span>
<span class="modal-field__value">610102199403154729</span>
</div>
<div class="modal-field">
<span class="modal-field__label">性别</span>
+222 -39
View File
@@ -130,6 +130,7 @@
.project-list-panel::-webkit-scrollbar { display: none; }
.project-grid { display: flex; flex-direction: column; gap: 10px; }
.project-card {
position: relative;
background: #fff; border-radius: 14px; padding: 14px 16px; border: 2px solid #eef5f5;
cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
@@ -140,6 +141,9 @@
cursor: default; background: #fff; border-color: #eef5f5;
}
.project-card.project-card--readonly:hover { border-color: #eef5f5; }
/* 额度已满后剩余项目置灰,不可再选 */
.project-card--disabled { opacity: 0.45; cursor: not-allowed; }
.project-card--disabled:hover { border-color: #eef5f5; background: #fff; }
.project-card__check {
width: 22px; height: 22px; border-radius: 6px; border: 2px solid #d9d9d9;
display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: all 0.2s;
@@ -151,16 +155,22 @@
.project-card.selected .project-card__check { background: var(--primary); border-color: var(--primary); }
.project-card.selected .project-card__check::after { opacity: 1; }
.project-card__info { flex: 1; min-width: 0; }
.project-card__name { font-size: 14px; font-weight: 600; margin-bottom: 2px; }
.project-card__desc { font-size: 12px; color: var(--text-muted); }
.project-card__name { font-size: 15px; font-weight: 600; margin-bottom: 6px; }
/* 项目示意图标块:沿用医院卡渐变底 + emoji 占位风格 */
.project-card__img {
width: 52px; height: 52px; border-radius: 14px;
background: linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%);
display: flex; align-items: center; justify-content: center;
font-size: 24px; flex-shrink: 0;
}
.project-card.selected .project-card__img {
background: linear-gradient(135deg, #b5f5ec 0%, #87e8de 100%);
}
.project-card__side {
flex-shrink: 0; display: flex; align-items: center; gap: 10px;
margin-left: auto; padding-left: 8px;
}
.project-card__tags {
display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
.project-card__price { font-size: 13px; color: var(--primary-dark); font-weight: 700; }
.project-card__price { font-size: 14px; color: var(--primary-dark); font-weight: 700; }
.project-card__badge {
display: inline-flex; align-items: center; justify-content: center; height: 22px;
padding: 0 10px; border-radius: 11px; background: #f5f5f5;
@@ -170,7 +180,7 @@
background: #fff1f0; color: #ff4d4f;
}
.project-card__meta {
display: flex; align-items: center; gap: 6px;
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
/* AI 推荐标签分三档,字号阶梯放大 + 深色实底强化等级对比:
强烈推荐 12px 红橙实底 > 建议加做 11px 紫蓝实底 > 可选参考 10px 浅蓝描边 */
@@ -191,6 +201,10 @@
height: 20px; padding: 0 8px; border-radius: 10px; font-size: 10px; font-weight: 600;
background: #f0f5ff; color: #597ef7; border: 1px solid #d6e4ff;
}
/* 列表卡片上的 AI 推荐标签固定在卡片右上角(弹窗内的同名标签不受影响) */
.project-card__badge--ai-corner {
position: absolute; top: 6px; right: 16px;
}
.project-card__arrow {
flex-shrink: 0; color: #d0d0d0; font-size: 16px; cursor: pointer;
transition: color 0.2s;
@@ -247,6 +261,26 @@
.confirm-modal__btn--primary {
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%); color: #fff;
}
/* 互斥项目弹窗:二选一选项行 */
.mutex-options { margin-bottom: 18px; }
.mutex-option {
display: flex; align-items: center; gap: 10px;
padding: 12px 14px; border: 2px solid #eef5f5; border-radius: 14px;
background: #fff; cursor: pointer; transition: all 0.2s;
}
.mutex-option + .mutex-option { margin-top: 10px; }
.mutex-option.active { border-color: var(--primary); background: var(--primary-light); }
.mutex-option__radio {
width: 18px; height: 18px; border-radius: 50%; border: 2px solid #d9d9d9;
flex-shrink: 0; position: relative; transition: all 0.2s;
}
.mutex-option.active .mutex-option__radio { border-color: var(--primary); }
.mutex-option.active .mutex-option__radio::after {
content: ''; position: absolute; left: 3px; top: 3px; width: 8px; height: 8px;
border-radius: 50%; background: var(--primary);
}
.mutex-option__name { flex: 1; min-width: 0; font-size: 14px; font-weight: 600; }
.mutex-option__price { font-size: 13px; font-weight: 700; color: var(--primary-dark); }
/* AI 推荐理由弹窗 */
.ai-modal { padding: 24px 20px 18px; }
.ai-modal__project {
@@ -383,6 +417,43 @@
<button class="confirm-modal__btn confirm-modal__btn--primary ai-modal__close" onclick="closeAiReason()">知道了</button>
</div>
</div>
<!-- [交互] 关联项目提醒:同步选中 / 取消选择 -->
<div class="modal-overlay hidden" id="linkModal">
<div class="confirm-modal">
<div class="confirm-modal__title">🔗 关联项目提醒</div>
<div class="confirm-modal__desc" id="linkModalDesc"></div>
<div class="confirm-modal__info" id="linkModalInfo"></div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--ghost" onclick="closeLinkModal()">取消选择</button>
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="confirmLinkSelect()">同步选中</button>
</div>
</div>
</div>
<!-- [交互] 互斥项目提醒:两项展示出来二选一,确定后替换已选项 -->
<div class="modal-overlay hidden" id="mutexModal">
<div class="confirm-modal">
<div class="confirm-modal__title">⚠️ 互斥项目提醒</div>
<div class="confirm-modal__desc" id="mutexModalDesc"></div>
<div class="mutex-options" id="mutexOptions"></div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--ghost" onclick="closeMutexModal()">取消</button>
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="confirmMutexSelect()">确定</button>
</div>
</div>
</div>
<!-- [交互] 额度不足提醒 -->
<div class="modal-overlay hidden" id="exceedModal">
<div class="confirm-modal">
<div class="confirm-modal__title">💰 额度不足提醒</div>
<div class="confirm-modal__desc" id="exceedModalDesc"></div>
<div class="confirm-modal__actions">
<button class="confirm-modal__btn confirm-modal__btn--primary" onclick="closeExceedModal()">知道了</button>
</div>
</div>
</div>
</div>
<script>
// 年度体检总额度(可用额度 = 总额度 − 已选项目费用,原型模拟数据)
@@ -394,20 +465,20 @@
1: { label: '可选参考', cls: 'low', icon: '✨', desc: '按年龄段与常见风险的一般性补充筛查,可结合剩余额度自行选择。' }
};
const projects = [
{ id: 'general', name: '一般检查', desc: '身高、体重、血压、视力', price: 120, type: 'required', level: 'normal' },
{ id: 'blood', name: '血常规', desc: '血红蛋白、白细胞、血小板等', price: 80, type: 'required', level: 'normal' },
{ id: 'urine', name: '尿常规', desc: '尿蛋白、尿糖、尿潜血等', price: 45, type: 'required', level: 'normal' },
{ id: 'liver', name: '肝功能', desc: 'ALT、AST、总胆红素等', price: 160, type: 'required', level: 'key' },
{ id: 'kidney', name: '肾功能', desc: '肌酐、尿素氮、尿酸等', price: 110, type: 'required', level: 'key' },
{ id: 'lipid', name: '血脂四项', desc: '总胆固醇、甘油三酯、HDL、LDL', price: 90, type: 'required', level: 'normal' },
{ id: 'glucose', name: '空腹血糖', desc: '空腹静脉血糖检测', price: 40, type: 'required', level: 'normal' },
{ id: 'ecg', name: '心电图', desc: '十二导联心电图检查', price: 75, type: 'required', level: 'key' },
{ id: 'xray', name: '胸部X光', desc: '胸部正位X线摄影', price: 140, type: 'optional', level: 'normal' },
{ id: 'ultrasound', name: '腹部B超', desc: '肝胆脾胰肾超声检查', price: 180, type: 'optional', level: 'key', aiLevel: 3, aiReason: '健康画像显示 BMI 26.8(超重),且近年血脂持续偏高,属于脂肪肝与胆囊病变的高风险人群,建议本年度务必完成腹部超声重点筛查。预计费用 ¥180,在本年度体检额度内。' },
{ id: 'thyroid', name: '甲状腺彩超', desc: '甲状腺结构与结节筛查', price: 160, type: 'optional', level: 'key', aiLevel: 2, aiReason: '既往史显示 2024 年体检发现甲状腺结节(TI-RADS 2 类),建议每年做一次超声随访复查,观察结节变化。预计费用 ¥160,在本年度体检额度内。' },
{ id: 'tumor', name: '肿瘤标志物', desc: '基础肿瘤风险筛查', price: 220, type: 'optional', level: 'key', aiLevel: 2, aiReason: '家族史提示直系亲属有肿瘤病史,结合您所处年龄段(45 岁以上),建议今年纳入基础肿瘤标志物筛查,做到早发现早干预。预计费用 ¥220,在本年度体检额度内。' },
{ id: 'echo', name: '心脏彩超', desc: '心脏结构与瓣膜功能超声', price: 360, type: 'optional', level: 'key', aiLevel: 1, aiReason: '近两次体检血压处于正常高值,可完成一次心脏彩超,建立心脏结构与瓣膜功能的基线资料,便于日后对比随访。预计费用 ¥360,请注意控制项目总额。' },
{ id: 'gastro', name: '胃幽门螺杆菌检测', desc: 'C13 呼气试验,筛查胃部风险', price: 480, type: 'optional', level: 'normal', aiLevel: 1, aiReason: '幽门螺杆菌是胃癌明确的危险因素,国内感染率较高。如从未检测过,建议先做一次 C13 呼气试验筛查,阳性可规范四联根除治疗。预计费用 ¥480,可结合剩余额度自行选择。' }
{ id: 'general', name: '一般检查', icon: '📏', price: 120, type: 'required', level: 'normal' },
{ id: 'blood', name: '血常规', icon: '🩸', price: 80, type: 'required', level: 'normal' },
{ id: 'urine', name: '尿常规', icon: '🧪', price: 45, type: 'required', level: 'normal' },
{ id: 'liver', name: '肝功能', icon: '⚗️', price: 160, type: 'required', level: 'key' },
{ id: 'kidney', name: '肾功能', icon: '🧫', price: 110, type: 'required', level: 'key' },
{ id: 'lipid', name: '血脂四项', icon: '🧬', price: 90, type: 'required', level: 'normal' },
{ id: 'glucose', name: '空腹血糖', icon: '🍬', price: 40, type: 'required', level: 'normal' },
{ id: 'ecg', name: '心电图', icon: '📈', price: 75, type: 'required', level: 'key' },
{ id: 'xray', name: '胸部X光', icon: '🦴', price: 140, type: 'optional', level: 'normal' },
{ id: 'ultrasound', name: '腹部B超', icon: '🔬', price: 180, type: 'optional', level: 'key', aiLevel: 3, linkId: 'thyroid', aiReason: '健康画像显示 BMI 26.8(超重),且近年血脂持续偏高,属于脂肪肝与胆囊病变的高风险人群,建议本年度务必完成腹部超声重点筛查。预计费用 ¥180,在本年度体检额度内。' },
{ id: 'thyroid', name: '甲状腺彩超', icon: '🦋', price: 160, type: 'optional', level: 'key', aiLevel: 2, linkId: 'ultrasound', aiReason: '既往史显示 2024 年体检发现甲状腺结节(TI-RADS 2 类),建议每年做一次超声随访复查,观察结节变化。预计费用 ¥160,在本年度体检额度内。' },
{ id: 'tumor', name: '肿瘤标志物', icon: '🎗️', price: 220, type: 'optional', level: 'key', aiLevel: 2, mutexId: 'echo', aiReason: '家族史提示直系亲属有肿瘤病史,结合您所处年龄段(45 岁以上),建议今年纳入基础肿瘤标志物筛查,做到早发现早干预。预计费用 ¥220,在本年度体检额度内。' },
{ id: 'echo', name: '心脏彩超', icon: '🫀', price: 360, type: 'optional', level: 'key', aiLevel: 1, mutexId: 'tumor', aiReason: '近两次体检血压处于正常高值,可完成一次心脏彩超,建立心脏结构与瓣膜功能的基线资料,便于日后对比随访。预计费用 ¥360,请注意控制项目总额。' },
{ id: 'gastro', name: '胃幽门螺杆菌检测', icon: '🦠', price: 480, type: 'optional', level: 'normal', aiLevel: 1, aiReason: '幽门螺杆菌是胃癌明确的危险因素,国内感染率较高。如从未检测过,建议先做一次 C13 呼气试验筛查,阳性可规范四联根除治疗。预计费用 ¥480,可结合剩余额度自行选择。' }
];
let activeTab = 'optional';
@@ -478,25 +549,25 @@
const card = document.createElement('div');
const isReadonly = project.type === 'required';
const isSelected = selectedOptionalIds.has(project.id);
// 额度已满时,未选中的可选项目置灰不可再选
const isDisabled = !isReadonly && !isSelected && getAvailableFee() <= 0;
const projectLevelText = project.level === 'key' ? '重点项目' : '一般项目';
const aiLevel = AI_LEVELS[project.aiLevel];
card.className = `project-card${isReadonly ? ' project-card--readonly' : ''}${isSelected ? ' selected' : ''}`;
card.className = `project-card${isReadonly ? ' project-card--readonly' : ''}${isSelected ? ' selected' : ''}${isDisabled ? ' project-card--disabled' : ''}`;
card.innerHTML = `
<div class="project-card__check${isReadonly ? ' project-card__check--hidden' : ''}"></div>
<div class="project-card__img">${project.icon}</div>
<div class="project-card__info">
<div class="project-card__name">${project.name}</div>
<div class="project-card__desc">${project.desc}</div>
<div class="project-card__meta">
<span class="project-card__badge${project.level === 'key' ? ' project-card__badge--key' : ''}">${projectLevelText}</span>
</div>
</div>
<div class="project-card__side">
<div class="project-card__tags">
<span class="project-card__price">${formatPrice(project.price)}</span>
<div class="project-card__meta">
<span class="project-card__badge${project.level === 'key' ? ' project-card__badge--key' : ''}">${projectLevelText}</span>
${aiLevel ? `<span class="project-card__badge project-card__badge--ai project-card__badge--ai-${aiLevel.cls}">${aiLevel.icon} ${aiLevel.label}</span>` : ''}
</div>
</div>
<span class="project-card__price">${formatPrice(project.price)}</span>
<span class="project-card__arrow" data-detail="${project.id}">&#8250;</span>
</div>
${aiLevel ? `<span class="project-card__badge project-card__badge--ai project-card__badge--ai-${aiLevel.cls} project-card__badge--ai-corner">${aiLevel.icon} ${aiLevel.label}</span>` : ''}
`;
// [交互] 箭头点击跳转项目详情
@@ -515,21 +586,133 @@
}
if (!isReadonly) {
card.addEventListener('click', () => {
if (selectedOptionalIds.has(project.id)) {
selectedOptionalIds.delete(project.id);
} else {
selectedOptionalIds.add(project.id);
}
renderProjects();
updateSummary();
});
card.addEventListener('click', () => trySelectProject(project));
}
grid.appendChild(card);
});
}
// ===== 选择流程:关联项目同步选中 / 互斥项目二选一 / 额度校验 =====
// 关联:ultrasound ↔ thyroid;互斥:tumor ↔ echo
let pendingLinkProject = null;
let pendingMutex = null;
function trySelectProject(project) {
// 已选中 → 取消勾选,关联项目同步取消
if (selectedOptionalIds.has(project.id)) {
selectedOptionalIds.delete(project.id);
const linked = project.linkId && projects.find(p => p.id === project.linkId);
if (linked && selectedOptionalIds.has(linked.id)) selectedOptionalIds.delete(linked.id);
renderProjects();
updateSummary();
return;
}
// 额度已满:剩余项目置灰,点击不响应
if (getAvailableFee() <= 0) return;
// 关联项目:对方未选中 → 弹窗提醒同步选中
const linked = project.linkId && projects.find(p => p.id === project.linkId);
if (linked && !selectedOptionalIds.has(linked.id)) {
openLinkModal(project, linked);
return;
}
// 互斥项目:对方已选中 → 弹窗二选一
const mutex = project.mutexId && projects.find(p => p.id === project.mutexId);
if (mutex && selectedOptionalIds.has(mutex.id)) {
openMutexModal(project, mutex);
return;
}
// 单选超额 → 弹窗提示,不生效
if (getAvailableFee() < project.price) {
openExceedModal(`${project.name}」预计费用 ${formatPrice(project.price)},选择后项目总额将超出年度体检额度,请调整已选项目后再试`);
return;
}
selectedOptionalIds.add(project.id);
renderProjects();
updateSummary();
}
// 关联项目弹窗
function openLinkModal(project, linked) {
pendingLinkProject = project;
document.getElementById('linkModalDesc').innerHTML =
`${project.name}」与「${linked.name}」为关联项目,<br>选中其中一项将同步选中另一项`;
document.getElementById('linkModalInfo').innerHTML = [project, linked].map(p =>
`<div class="confirm-modal__row"><span>${p.name}</span><span class="confirm-modal__value">${formatPrice(p.price)}</span></div>`
).join('');
document.getElementById('linkModal').classList.remove('hidden');
}
function closeLinkModal() {
document.getElementById('linkModal').classList.add('hidden');
}
function confirmLinkSelect() {
const project = pendingLinkProject;
const linked = projects.find(p => p.id === project.linkId);
const cost = project.price + linked.price;
// 两项合计超出剩余额度 → 转额度不足提醒
if (getAvailableFee() < cost) {
closeLinkModal();
openExceedModal(`${project.name}」与「${linked.name}」需同步选中,合计 ${formatPrice(cost)},选择后项目总额将超出年度体检额度,请调整已选项目后再试`);
return;
}
selectedOptionalIds.add(project.id);
selectedOptionalIds.add(linked.id);
closeLinkModal();
renderProjects();
updateSummary();
}
// 互斥项目弹窗:展示两项做单选,确定后替换已选项
function openMutexModal(clicked, other) {
pendingMutex = { clicked, other, chosenId: clicked.id };
document.getElementById('mutexModalDesc').innerHTML =
`${clicked.name}」与「${other.name}」互斥,只能同时体检其中一个项目,请选择本次要做的项目`;
renderMutexOptions();
document.getElementById('mutexModal').classList.remove('hidden');
}
function renderMutexOptions() {
document.getElementById('mutexOptions').innerHTML = [pendingMutex.clicked, pendingMutex.other].map(p => `
<div class="mutex-option${pendingMutex.chosenId === p.id ? ' active' : ''}" onclick="chooseMutexOption('${p.id}')">
<span class="mutex-option__radio"></span>
<span class="mutex-option__name">${p.name}</span>
<span class="mutex-option__price">${formatPrice(p.price)}</span>
</div>
`).join('');
}
function chooseMutexOption(id) {
pendingMutex.chosenId = id;
renderMutexOptions();
}
function closeMutexModal() {
document.getElementById('mutexModal').classList.add('hidden');
}
function confirmMutexSelect() {
const { clicked, other, chosenId } = pendingMutex;
const chosen = chosenId === clicked.id ? clicked : other;
const dropped = chosenId === clicked.id ? other : clicked;
// 替换后的可用额度 = 当前可用 + 被替换项目释放额度 - 新选项目费用
if (getAvailableFee() + dropped.price - chosen.price < 0) {
closeMutexModal();
openExceedModal(`选择「${chosen.name}」后项目总额将超出年度体检额度,请调整已选项目后再试`);
return;
}
selectedOptionalIds.delete(clicked.id);
selectedOptionalIds.delete(other.id);
selectedOptionalIds.add(chosen.id);
closeMutexModal();
renderProjects();
updateSummary();
}
// 额度不足提醒弹窗
function openExceedModal(desc) {
document.getElementById('exceedModalDesc').innerHTML = desc;
document.getElementById('exceedModal').classList.remove('hidden');
}
function closeExceedModal() {
document.getElementById('exceedModal').classList.add('hidden');
}
tabButtons.forEach(button => {
button.addEventListener('click', () => {
activeTab = button.dataset.tab;
+16
View File
@@ -166,6 +166,18 @@
.consult-table--gastro thead th:nth-child(2) { width: 70%; }
.is-abnormal { color: var(--danger); font-weight: 600; }
.consult-table tbody td:nth-child(2) { color: var(--danger); font-weight: 600; }
/* 顶部体检信息:年份 + 类型 */
.info-bar {
background: #fff;
display: flex;
align-items: center;
padding: 13px 16px;
border-bottom: 1px solid #f0f0f0;
}
.info-bar__item { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.info-bar__label { color: var(--text-muted); flex-shrink: 0; }
.info-bar__value { color: var(--text-primary); font-weight: 600; }
</style>
</head>
<body>
@@ -185,6 +197,10 @@
<div class="scroll-content">
<div class="page-body">
<!-- 顶部体检信息:当前咨询报告的年份 -->
<div class="info-bar">
<div class="info-bar__item"><span class="info-bar__label">体检年份</span><span class="info-bar__value">2025年</span></div>
</div>
<!-- 化学检查 -->
<div class="category">
<div class="category__title">化学检查</div>
+75 -67
View File
@@ -16,19 +16,19 @@
│ │ ├── 选择项目(年度额度 · 必检/选检 · AI 智能推荐)→ 项目详情
│ │ ├── 已选项目确认
│ │ ├── 预约日期(月历号源 · 近30天可约)
│ │ └── 预约结果(成功/失败 · 取消预约需填原因
│ │ └── 预约结果(成功/失败 · 取消预约二次确认
│ ├── 胃肠镜流程(步骤条:1 选择医院 → 2 填问卷 → 3 预约日期)
│ │ ├── 选择医院(唐都医院需就诊卡号)
│ │ ├── 病史问卷(是/否单选 · 就诊卡条件题)
│ │ ├── 预约日期(7~30 天可约窗口)
│ │ └── 预约结果(成功/失败 · 取消预约)
│ └── 冠脉CTA流程(免问卷,两步:选择医院 → 预约日期 → 预约结果)
├── 胃肠镜医院预选(征集期一次性活动,与正式预约解耦)
│ ├── 医院预选填写(多选意向医院 · 仅可提交一次
│ └── 预选提交完成(只读回显,不可修改
├── 现场体检(Tab:常规/胃肠镜/冠脉CTA × 状态:待体检/已完成/无预约)
├── 胃肠镜医院预选(征集期活动,与正式预约解耦)
│ ├── 医院预选填写(多选意向医院 · 至少2家/最多3家 · 可重新填写调整
│ └── 预选提交完成(回显当前提交的意向医院 · 可重新填写
├── 现场体检(Tab:常规/胃肠镜/冠脉CTA × 状态:已预约/无预约)
├── 体检报告
│ ├── 报告列表(累计年份统计 · 历年体检趋势入口 · 历年报告)
│ ├── 报告列表(累计年份统计 · 历年体检趋势入口 · 年份/类型筛选 · 历年报告)
│ ├── 常规体检报告(AI 解读/结论/建议 · 14 科室明细)
│ ├── 冠脉CTA报告(钙化积分 · 冠脉分段 · 影像诊断)
│ ├── 胃肠镜报告(胃镜/肠镜/病理诊断)
@@ -38,7 +38,7 @@
└── 操作说明(标题搜索过滤)→ 讲解详情(图文教程 / 视频播放)
```
> 胃肠镜医院预选为独立的一次性问卷收集活动:征集期内「未提交 → 已提交」,提交后不可修改;征集期结束通知条隐藏。
> 胃肠镜医院预选为独立于正式预约的意向征集活动:征集期内员工多选意向医院(至少 2 家、最多 3 家),提交后可在完成页「重新填写」再次调整提交;征集期结束通知条隐藏。
---
@@ -121,7 +121,7 @@
| 31 | 操作说明 | process-guide.html | 页面 |
| 32 | 讲解详情(图文/视频) | process-guide-detail.html | 页面 |
> 预约结果页(10/16/21)、现场体检(22)的「成功/失败/待体检/已完成/无预约」等状态由原型内演示控件切换,实际由数据驱动,逐页说明中一并描述。
> 预约结果页(9/15/20)、现场体检(21)的「成功/失败/已预约/无预约」等状态由原型内演示控件切换,实际由数据驱动,逐页说明中一并描述。
---
@@ -140,17 +140,17 @@
**主要UI元素:**
- 导航栏:返回箭头(回 APP 首页)、标题「健康体检」、消息铃铛(红色角标显示未读消息数)
- 年度体检计划 Banner 轮播(4 张滑动切换,圆点指示器随内容变色):
- 常规体检:计划标题、体检日期范围、体检费用额度胶囊、右下角状态(未预约、已预约、已参检)
- 胃肠镜体检:计划标题、体检日期范围、右下角状态(未预约、已预约、已参检)
- 冠脉CTA体检:计划标题、体检日期范围、右下角状态(未预约、已预约、已参检)
- 常规体检:计划标题、起止日期、体检费用额度胶囊、右下角状态(未预约、已预约、已参检)
- 胃肠镜体检:计划标题、起止日期、右下角状态(未预约、已预约、已参检)
- 冠脉CTA体检:计划标题、起止日期、右下角状态(未预约、已预约、已参检)
- 未开启计划占位:🌱 图标 + 占位文案
- 胃肠镜医院预选通知条(橙色,仅征集期展示):📢 图标、标题、截止日期与「每人仅可填写一次」说明、「去选择」按钮
- 胃肠镜医院预选通知条(橙色,仅征集期展示):📢 图标、标题、截止日期与「去选择」按钮
- 体检服务入口卡片:体检预约(选医院和项目,预约日期)、现场体检(到院扫码/体检进度查询)、体检报告(查看报告/解读结果)
- 其他服务宫格:操作说明、一键咨询
**业务规则:**
- Banner 按员工实际计划状态渲染:有计划显示对应类型卡片并按进度跳转,无计划的类型不出现;单位未开启任何计划时仅显示占位图
- 预选通知条三态:征集期未提交(「去选择」,进入预选填写)、已提交(标题改「预选已完成」、绿色对勾、「查看结果」,进入完成页)
- 预选通知条三态:征集期未提交(「去选择」,进入预选填写)、已提交(标题改「预选已完成」、绿色对勾、「查看结果」,进入完成页),仅预选日期范围内展示
**交互说明:**
- 左右滑动切换 Banner,点击圆点跳转到对应banner
@@ -213,13 +213,13 @@
![确认个人信息弹窗](screenshots/booking-hospital-dialog.jpg)
**确认个人信息弹窗:** 标题「确认个人信息」+ 副标题;字段:姓名、单位、身份证号(脱敏显示)、性别、年龄、体检类型(「已婚体检 / 未婚体检」胶囊切换,默认已婚);「确认」按钮关闭弹窗。
**确认个人信息弹窗:** 标题「确认个人信息」+ 副标题;字段:姓名、单位、身份证号、性别、年龄、体检类型(「已婚体检 / 未婚体检」胶囊切换,默认已婚);「确认」按钮关闭弹窗。
**业务规则:**
- 从选择体检类型页进入时自动弹出个人信息确认弹窗;也可从导航栏「个人信息」手动打开
- 已婚/未婚体检切换仅影响本次预约的检查套餐
- 可选择的医院范围:后台开展计划选择的医院&职业体检支持的医院(如需参加职业体检)&指定员工的医院(如被指定医院体检)
- 提交选择医院时需要判断当前员工选择的医院是否符合最终条件,例如已被纳入指定医院体检/已被纳入职业体检,医院需开启职业体检
**交互说明:**
- 点击医院卡片单选选中
- 点击「个人信息」打开确认个人信息弹窗,点击「确认」关闭
@@ -237,11 +237,15 @@
**主要UI元素:**
- 体检额度卡片:「年度体检总额度」、「已选项目费用」(随勾选联动)、额度进度条(超支变红)、三格统计:必检项目 / 选检项目 / 可用额度(超支标红)
- 项目 Tab:「选检项目」(默认)/「必检项目」
- 项目卡片:示意图标(渐变底 emoji 占位块)+ 项目名 +「重点项目/一般项目」徽章 + 价格;AI 推荐徽章固定于卡片右上角
- 必检项目列表(无勾选框、整卡只读)
- 选检项目列表(按 AI 推荐等级降序)
- AI 推荐徽章三档:强烈推荐、建议加做、可选参考
- 「确认提交」弹窗:体检项目数、项目总额、必检项目费用、体检总额度、可用额度;「取消 / 确认提交」
- 「✨ 智能推荐」弹窗:项目名、推荐等级徽章、等级说明、推荐理由、按钮「知道了」
- 「🔗 关联项目提醒」弹窗:关联说明 + 两项目费用;「取消选择 / 同步选中」
- 「⚠️ 互斥项目提醒」弹窗:互斥说明 + 两项目单选列表;「取消 / 确定」
- 「💰 额度不足提醒」弹窗:超额说明;按钮「知道了」
- 底部双按钮:「上一步」/「提交项目」
![确认提交弹窗](screenshots/booking-project-submit.jpg)
@@ -249,15 +253,18 @@
![智能推荐弹窗](screenshots/booking-project-ai.jpg)
**业务规则:**
- 可用额度 = 年度总额度 − 必检合计 − 选检已选合计;可用额度为负时显示 ¥0.00 并标红,进度条封顶并变红(仅视觉警示,不阻止提交
- 可用额度 = 年度总额度(实际个人总额度+所属项目模板中的折扣金额,折扣金额是隐藏的,不体现) − 必检合计 − 选检已选合计;单项选择后总额将超出额度时弹「额度不足提醒」且选择不生效;可用额度为 0 后剩余未选项目置灰不可再选(额度进度条超支变红仅作视觉警示
- 关联项目:选中其一时若另一项未选,弹「关联项目提醒」,「同步选中」后两项一起勾选(两项合计超出剩余额度则转额度不足提醒);取消其一时另一项同步取消
- 互斥项目:只能同时体检其中一个。选中其一时若另一项已选,弹「互斥项目提醒」列出两项单选,确定后替换已选项(替换后超出额度则提示额度不足)
- 必检项目固定包含,不可取消;选检项目点击整卡切换勾选,默认选中胸部X光
- 选检列表按 AI 推荐等级降序排列(强烈推荐 > 建议加做 > 可选参考 > 无推荐)
- AI 推荐理由基于员工健康画像生成,示例:腹部B超(BMI 超重+血脂偏高,脂肪肝高风险)、甲状腺彩超(既往甲状腺结节随访)、肿瘤标志物(家族史+年龄段)、心脏彩超(血压正常高值建立基线)、幽门螺杆菌(首次筛查建议)
- 金额千分位两位小数格式化
- 提交项目时需要判断当前员工选择的医院是否符合最终条件,例如已被纳入指定医院体检/已被纳入职业体检,医院需开启职业体检
**交互说明:**
- 点击 Tab 切换选检/必检列表
- 点击选检项目卡勾选/取消勾选,额度卡片金额即时联动
- 点击选检项目卡勾选/取消勾选,额度卡片金额即时联动;额度已满时点击置灰项目卡无响应
- 选择关联/互斥项目时按弹窗指引确认;「取消选择」/「取消」不改变当前勾选状态
- 点击卡片右侧箭头跳转到项目详情页
- 点击 AI 推荐徽章打开智能推荐理由弹窗(不触发勾选)
- 点击「提交项目」打开确认提交弹窗,「确认提交」跳转到已选项目确认页
@@ -328,12 +335,13 @@
![确认预约信息弹窗](screenshots/booking-date-dialog.jpg)
**业务规则:**
- 可约范围 = 当天 ~ 近 30 天;已过日期与超范围日期不可选
- 可约范围 = 当天 ~ 近 30 天(该30天作为动态字段,不是写死的,通过后台接口传递);已过日期与超范围日期不可选
- 周末不可约(显示「不可约」);工作日号源随机模拟,约满日显示「不可约」且禁点
- 不能翻到当月之前或可约范围所在月份之后
- 默认选中当天;未选中日期时「确定预约」不响应
- 可供员工预约的日期范围是医院承检日期+单位允许日期范围的交叉日期,并且在可预约日期的前一天/当天(当天可能是刚创建体检计划,但是预约日期也是当天)给员工发送短信,提醒员工进行预约体检
- 未开启预约时,隐藏日历,确定预约按钮置灰
- 提交预约时需要判断当前员工选择的医院是否符合最终条件,例如已被纳入指定医院体检/已被纳入职业体检,医院需开启职业体检
**交互说明:**
- 点击翻月按钮切换月份
@@ -348,18 +356,18 @@
![页面截图](screenshots/booking-date-selected.jpg)
**功能说明:** 预约提交结果页,含「预约成功 / 预约失败」两态。成功态展示预约信息与体检须知,支持取消预约(必填原因);失败态展示失败原因并可重新预约。
**功能说明:** 预约提交结果页,含「预约成功 / 预约失败」两态。成功态展示预约信息与体检须知,支持取消预约(二次确认);失败态展示失败原因并可重新预约。
**主要UI元素:**
- 步骤条(三步全部完成;点击已完成步骤弹 Toast 阻止回跳)
- 结果横幅:
- 成功态(绿色,圆形对勾图标):「预约成功」
- 成功态(绿色,圆形对勾图标):「预约成功」+ 提示「请按预约日期准时到检」
- 失败态(红色,圆形叉图标):「预约失败」+ 失败原因 + 红色「?」帮助按钮
- 预约信息卡片:体检医院、体检员工、体检地址、体检日期(高亮)、体检项目数
- 体检须知卡片(仅成功态显示)
- 成功态按钮:「取消预约」(红色)/「查看预约详情」(主色);失败态按钮:「重新预约」(独占一行)
- 「失败原因详情」弹窗:失败原因、发生时间、处理建议
- 「确认取消预约」弹窗(见下方表单字段表)
- 成功态按钮:「取消预约」(红色)/「现场体检」(主色);失败态按钮:「重新预约」(独占一行)
- 「失败原因详情」弹窗:失败原因、发生时间
- 「确认取消预约」弹窗:说明文案 + 医院与日期信息;「再想想 / 确认取消」
![失败原因详情弹窗](screenshots/booking-date-selected-reason.jpg)
@@ -370,24 +378,16 @@
**业务规则:**
- 失败语义为号源争抢失败(所选日期号源已被其他员工约满);失败态隐藏体检须知,引导重新预约
- 已预约状态下点击步骤条弹 Toast「当前已预约体检,无法重新选择,请先取消预约」;失败态 Toast 文案切换为「预约未成功,请点击下方『重新预约』按钮重新预约」
- 取消预约必须选择原因;选择「其他」时必须填写文字说明(上限 100 字)
- 取消成功后 Toast「预约已取消」并返回预约日期页重新预约
- 取消预约为二次确认弹窗,无需填写原因;确认后名额释放,Toast「预约已取消」并返回预约日期页重新预约
- 取消时限约束:至少提前一天操作
**交互说明:**
- 失败态点击「?」打开失败原因详情弹窗
- 成功态点击「取消预约」打开取消预约弹窗,校验通过确认后返回预约日期页
- 成功态点击「查看预约详情」跳转到「现场体检」(常规体检·待体检
- 成功态点击「取消预约」打开二次确认弹窗,「确认取消」后返回预约日期页
- 成功态点击「现场体检」跳转到「现场体检」(常规体检·已预约,含签到码
- 失败态点击「重新预约」跳转到预约日期页
- 点击返回回到健康体检主页
**表单字段表(取消预约弹窗):**
| 字段名 | 类型 | 是否必填 | 校验规则 |
|--------|------|----------|----------|
| 取消原因 | chip 单选(时间冲突/医院选择不当/日期选择不当/个人健康原因/其他) | 是 | 未选择时提示「请选择或填写取消原因」 |
| 其他原因说明 | 多行文本(maxlength=100) | 选「其他」时必填 | 去空格后非空,否则提示同上 |
---
## 四、胃肠镜体检预约流程
@@ -499,10 +499,11 @@
- 体检须知卡片(仅成功态)
- 成功态按钮「取消预约 / 查看预约详情」;失败态按钮「重新预约」
- 「失败原因详情」弹窗:失败原因说明 + 处理建议
- 「确认取消预约」弹窗原因 chips + 其他说明,与常规流程一致)
- 「确认取消预约」弹窗:取消原因 chip 单选(时间冲突/医院选择不当/日期选择不当/个人健康原因/其他),选「其他」需填写文字说明
**业务规则:**
- 与常规预约结果页一致:失败态隐藏须知、取消必填原因、取消成功后返回预约日期页
- 失败态隐藏体检须知,引导重新预约
- 取消预约必须选择原因,选择「其他」时必须填写文字说明;取消成功后返回预约日期页
- 已预约状态下点击步骤条弹 Toast 阻止回跳
**交互说明:**
@@ -514,31 +515,32 @@
## 五、胃肠镜医院预选
> 独立于正式预约的意向征集活动:征集期内员工对下一年度承检医院进行意向多选,每人仅可填写一次、提交后不可修改
> 独立于正式预约的意向征集活动:征集期内员工对下一年度承检医院进行意向多选(至少 2 家、最多 3 家),提交后可在完成页重新填写调整
### 13. 医院预选填写(gastro-preselect.html
![页面截图](screenshots/gastro-preselect.jpg)
**功能说明:** 胃肠镜体检医院意向征集填写页,员工多选意向医院后经二次确认提交。
**功能说明:** 胃肠镜体检医院意向征集填写页,员工多选意向医院(至少 2 家、最多 3 家)后经二次确认提交,可再次进入调整重新提交。
**主要UI元素:**
- 征集说明卡:征集标题、预选日期、红色警示条
- 区块标题「选择意向医院」+「点击卡片可多选,当前已选 N 家」(计数实时更新)
- 医院多选卡片(方形复选框,选中态青色对勾):各带省市信息与类型标签
- 「确认提交预选」弹窗:警示文案 + 已选医院数量与名称列表;「返回修改 / 确认提交」
- 征集说明卡:征集标题、预选日期
- 区块标题「选择意向医院」+「点击卡片可多选,至少选择2家、最多3家,当前已选 N 家」(计数实时更新)
- 医院多选卡片(方形复选框,选中态青色对勾):各带省市信息
- 「确认提交预选」弹窗:已选医院数量与名称列表;「返回修改 / 确认提交」
- Toast 轻提示:数量不满足 2~3 家时的校验提醒
- 底部「提交预选」按钮(选中后文案带已选数量)
![确认提交预选弹窗](screenshots/gastro-preselect-dialog.jpg)
**业务规则:**
- 多选互不影响,已选 0 家时提交按钮禁用
- 提交前强制二次确认(因仅可填写一次)
- 每人限提交一次,提交后不可修改
- 数量校验:少于 2 家提示「至少选择2家意向医院,请继续选择」,多于 3 家提示「最多选择3家意向医院,请调整后再提交」;校验通过才进入二次确认
- 提交前二次确认核对已选医院;支持重复提交,再次提交覆盖上一次意向
**交互说明:**
- 点击医院卡片勾选/取消勾选,按钮文案与计数联动
- 点击「提交预选」打开确认弹窗(未选医院时按钮禁用)
- 点击「提交预选」先校验数量(2~3 家),通过后打开确认弹窗(未选医院时按钮禁用)
- 弹窗「返回修改」关闭弹窗,「确认提交」跳转到预选提交完成页
- 点击返回回到健康体检主页
@@ -548,19 +550,20 @@
![页面截图](screenshots/gastro-preselect-done.jpg)
**功能说明:** 预选提交成功后的只读回显页。
**功能说明:** 预选提交成功后的回显页,展示当前提交的意向医院,支持重新填写调整
**主要UI元素:**
- 成功卡:绿色对勾图标、「预选提交成功」、提交时间、绿色提示框
- 「我选择的意向医院」区块:只读回显说明 + 只读已选医院卡片(固定青色描边 + 对勾)
- 底部「返回首页」按钮
- 成功卡:绿色对勾图标、「预选提交成功」、提交时间
- 「我选择的意向医院」区块:当前提交的意向医院卡片(固定青色描边 + 对勾)
- 底部「重新填写」按钮
**业务规则:**
- 纯只读展示,医院卡片不可点击
- 展示当前提交的意向医院,医院卡片不可直接点击
- 返回主页后,主页预选通知条变为「预选已完成」态
**交互说明:**
- 点击「返回首页」或返回箭头回到健康体检主页
- 点击「重新填写」回到预选填写页,可修改意向医院后再次提交
- 点击返回箭头回到健康体检主页
---
@@ -627,10 +630,12 @@
- 预约信息卡片:体检医院、体检员工、体检地址、体检日期、体检项目
- 体检须知卡片(仅成功态)
- 成功态按钮「取消预约 / 查看预约详情」;失败态按钮「重新预约」
- 「失败原因详情」弹窗与「确认取消预约」弹窗(与常规流程一致)
- 「失败原因详情」弹窗:失败原因说明 + 处理建议
- 「确认取消预约」弹窗:取消原因 chip 单选(时间冲突/医院选择不当/日期选择不当/个人健康原因/其他),选「其他」需填写文字说明
**业务规则:**
- 与常规预约结果页一致:失败态隐藏须知、取消必填原因、取消成功后返回预约日期页
- 失败态隐藏体检须知,引导重新预约
- 取消预约必须选择原因,选择「其他」时必须填写文字说明;取消成功后返回预约日期页
**交互说明:**
- 成功态「查看预约详情」跳转到「现场体检」(冠脉CTA体检页签)
@@ -645,17 +650,17 @@
![页面截图](screenshots/my-appointments-all.jpg)
**功能说明:** 查看各类体检的预约详情与现场体检信息:体检签到码(条形码/二维码)、预约概要、体检须知、已检/未检项目进度;无预约时提供立即预约入口。
**功能说明:** 查看各类体检的预约详情与现场体检信息:体检签到码(条形码)、预约概要、体检须知、已检/未检项目进度;无预约时提供立即预约入口。
![胃肠镜体检页签](screenshots/my-appointments-all-gastro.jpg)
**主要UI元素:**
- 体检类型 Tab(吸顶,3 等分):常规体检 / 胃肠镜体检 / 冠脉CTA体检
- 常规体检·待体检视图:
- 「体检签到码」卡片:条形码/二维码双选项卡切换,条形码下编号,提示「点击条码可放大查看」;点击打开放大遮罩
- 预约概要卡:体检医院、预约日期、体检员工、单位部门、状态徽章(待体检=橙 / 已参检=绿)
- 常规体检·已预约视图:
- 「体检签到码」卡片:条形码 + 编号,提示「点击条码可放大查看」;点击打开放大遮罩
- 预约概要卡:体检医院、体检地址、预约日期、体检员工、单位部门
- 体检进度卡:Tab「已检项目 (N) / 未检项目 (M)」,常规项目列表,已检=绿色✓「已参检」、未检=橙色●「未检」
- 胃肠镜/冠脉CTA视图:预约概要卡 + 体检须知卡(6 条,与预约结果页一致)
- 胃肠镜/冠脉CTA视图:预约概要卡(体检员工、体检地址、体检日期、体检项目)+ 体检须知卡(6 条,与预约结果页一致)
- 无预约空态:图标 +「暂无XX体检预约」+ 说明文案 +「立即预约」按钮
**业务规则:** 如未预约,显示未预约的提示页面
@@ -663,19 +668,17 @@
![冠脉CTA体检页签](screenshots/my-appointments-all-cta.jpg)
**业务规则:**
- 预约状态三态:待体检(橙)/ 已参检(绿)/ 无预约信息(空态);已完成视图无签到码卡片,进度为全部已检
- 签到码仅「常规体检·待体检」状态展示
- 预约状态两态演示:已预约 / 无预约信息(空态);已参检视图无签到码卡片,进度为全部已检
- 签到码仅「常规体检·已预约」状态展示
- 已检/未检列表空态文案:「暂无已检项目」/「当前预约项目已全部完成」
**交互说明:**
- 点击类型 Tab 切换对应体检面板
- 点击「条形码/二维码」切换签到码形态;点击码打开放大遮罩,点击遮罩空白关闭
- 点击签到码打开放大遮罩,点击遮罩空白关闭
- 点击已检/未检 Tab 切换项目列表
- 空态「立即预约」按类型分流:常规跳转到选择体检类型页,胃肠镜跳转到胃肠镜·选择医院,冠脉CTA跳转到冠脉CTA·选择医院
- 点击返回回到健康体检主页
**业务规则:** 如未预约,显示未预约的提示页面
---
## 八、体检报告与指标对比
@@ -684,19 +687,22 @@
![页面截图](screenshots/reports.jpg)
**功能说明:** 历年体检报告汇总入口页,展示体检年份统计、历年体检趋势分析入口和按年度/报告类型分类的历年报告列表。
**功能说明:** 历年体检报告汇总入口页,展示体检年份统计、历年体检趋势分析入口和历年报告列表,支持按年份与体检类型筛选
**主要UI元素:**
- 汇总卡(青色渐变):累计体检年份、起始年份、报告总数、年度时间轴装饰
- 历年体检趋势分析入口条:重点指标数目 · 纵向对比历年体检数据 + 右箭头
- 报告筛选栏:年份选择(日历面板:全部/2021~2025)+ 体检类型下拉(全部类型/常规体检/胃肠镜体检/冠脉CTA体检),默认「全部」
- 历年报告列表(图标 + 标题 + 类型标签 + 机构名 + 箭头):同一年份可有多类报告
**业务规则:**
- 报告类型标签三色:常规体检(蓝)、胃肠镜体检(青)、冠脉CTA体检(红)
- 报告按年度倒序排列
- 年份与类型两个筛选条件叠加过滤;选择非「全部」时对应入口按钮高亮
**交互说明:**
- 点击历年体检趋势分析条跳转到指标纵向对比页
- 点击年份/类型入口打开面板选择筛选条件,报告列表即时过滤;点击面板外部收起
- 点击常规体检报告跳转到常规体检报告页
- 点击胃肠镜体检报告跳转到胃肠镜报告页
- 点击冠脉CTA体检报告跳转到冠脉CTA报告页
@@ -765,7 +771,7 @@
- 报告头:报告标题 + 类型标签 + 基本信息
- 胃镜检查(橙色标题条,4 个可折叠字段):食管、胃、十二指肠所见描述;诊断结果
- 肠镜检查(青绿标题条,3 个字段):结肠所见;诊断结果;病理诊断
- 影像报告:胃镜报告、肠镜报告、活检报告「查看」入口
- 报告图片:胃镜报告、肠镜报告、活检报告「查看」入口
- 底部「返回报告列表」
**业务规则:**
@@ -867,11 +873,13 @@
**功能说明:** 集中展示体检报告中的异常指标(按检查大类分组表格呈现),每个子项提供「专家库一键咨询」入口。
**主要UI元素:**
- 顶部体检信息栏:当前咨询数据的体检年份
- 大分类区块,每区块下分子项(子项名 + 右侧红色「专家库一键咨询 ›」链接):化学检查、常规检查、影像检查、肠胃镜检查
- 异常结果表:四列(名称/结果/单位/参考值),结果列红色加粗;肠胃镜为两列(名称/结果)
**业务规则:**
- 仅展示异常项,结果列统一红色加粗标记异常
- 数据为员工最近一次的体检数据,顶部展示对应体检年份
**交互说明:**
- 点击「专家库一键咨询 ›」跳转到健康咨询模块的专家列表页,每个分类会关联科室或者疾病,优先跳对应科室,如没有科室,跳对应疾病,如两者都没,跳默认
+17 -1
View File
@@ -15,7 +15,7 @@ const docDir = __dirname;
const root = path.resolve(__dirname, '..');
// mode: auto(短页面最低 844 / 长页面真实高度)
const tasks = [
const baseTasks = [
// ========== 模块主页 ==========
{ html: 'index.html', out: 'index' },
@@ -29,6 +29,8 @@ const tasks = [
{ html: 'booking-date.html', out: 'booking-date' },
{ html: 'booking-date-selected.html', out: 'booking-date-selected' },
{ html: 'booking-date-selected.html', out: 'booking-date-selected-fail', demo: 'fail' },
{ html: 'booking-date-selected.html', out: 'booking-date-selected-reason', script: () => openReasonModal() },
{ html: 'booking-date-selected.html', out: 'booking-date-selected-cancel', script: () => openCancelModal() },
{ html: 'project-detail.html', out: 'project-detail', query: '?id=general' },
// ========== 胃肠镜体检流程 ==========
@@ -39,6 +41,7 @@ const tasks = [
{ html: 'gastro-date-selected.html', out: 'gastro-date-selected' },
{ html: 'gastro-date-selected.html', out: 'gastro-date-selected-fail', demo: 'fail' },
{ html: 'gastro-preselect.html', out: 'gastro-preselect' },
{ html: 'gastro-preselect.html', out: 'gastro-preselect-dialog', script: () => { document.querySelectorAll('.hospital-card')[0].click(); document.querySelectorAll('.hospital-card')[1].click(); openConfirm(); } },
{ html: 'gastro-preselect-done.html', out: 'gastro-preselect-done' },
// ========== 冠脉CTA体检流程 ==========
@@ -71,6 +74,13 @@ const tasks = [
];
(async () => {
// 支持命令行按 out 名称过滤,只重截部分截图:node screenshot.mjs index reports
const only = process.argv.slice(2);
const tasks = only.length ? baseTasks.filter(t => only.includes(t.out)) : baseTasks;
if (only.length && tasks.length === 0) {
console.error('未匹配到任何截图任务,可用名称:', baseTasks.map(t => t.out).join(', '));
process.exit(1);
}
const browser = await chromium.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const context = await browser.newContext({ viewport: { width: 390, height: 4000 } });
@@ -104,6 +114,12 @@ const tasks = [
await page.waitForTimeout(250);
}
// 自定义脚本(如打开弹窗后再截图)
if (t.script) {
await page.evaluate(t.script);
await page.waitForTimeout(250);
}
// 解除 phone-shell 高度限制:短页面保底 844,长页面按内容展开
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 56 KiB

@@ -48,7 +48,7 @@
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 220px, #f7f9fb 100%); padding-bottom: 120px; }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 提交成功卡:绿色对勾 + 提交时间 + 不可修改说明 */
/* 提交成功卡:绿色对勾 + 提交时间 */
.success-card {
background: #fff; border-radius: 18px; padding: 24px 16px 18px; margin-bottom: 18px;
box-shadow: var(--shadow); border: 1px solid var(--border); text-align: center;
@@ -60,15 +60,10 @@
box-shadow: 0 10px 24px rgba(82, 196, 26, 0.28);
}
.success-card__title { font-size: 18px; font-weight: 700; margin-bottom: 6px; }
.success-card__time { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
.success-card__note {
display: inline-block; padding: 6px 12px; border-radius: 12px;
background: #f6ffed; border: 1px solid #b7eb8f;
font-size: 12px; color: #52c41a; line-height: 1.6;
}
.success-card__time { font-size: 12px; color: var(--text-muted); }
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.section-sub { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
/* 已选医院只读卡片:不可点击,右侧固定绿色对勾 */
/* 已选医院回显卡片:右侧固定绿色对勾 */
.hospital-card {
background: #fff; border-radius: 18px; padding: 16px; margin-bottom: 12px;
border: 2px solid var(--primary); box-shadow: var(--shadow);
@@ -122,11 +117,10 @@
</div>
<div class="success-card__title">预选提交成功</div>
<div class="success-card__time">提交时间:2026-09-03 12:00</div>
<div class="success-card__note">感谢您的参与,您的意向将作为2027年度承检医院安排参考<br>预选结果提交后不可修改</div>
</div>
<div class="section-title">我选择的意向医院</div>
<div class="section-sub"><b style="color:#0f9b9b;">2</b> 家,以下为只读回显,不可再次编辑</div>
<div class="section-sub"><b style="color:#0f9b9b;">2</b> 家,以下为当前提交的意向医院,可重新填写调整</div>
<div class="hospital-card">
<div class="hospital-card__info">
@@ -163,8 +157,8 @@
</div>
<div class="bottom-actions">
<!-- [交互] 点击返回首页: index.html,首页通知条同步变为「预选已完成」态 -->
<button class="bottom-actions__btn" onclick="location.href='index.html'">返回首页</button>
<!-- [交互] 重新填写:返回预选表单 gastro-preselect.html,可修改后再次提交 -->
<button class="bottom-actions__btn" onclick="location.href='gastro-preselect.html'">重新填写</button>
</div>
</div>
</body>
@@ -48,7 +48,7 @@
.scroll-content { flex: 1; overflow-y: auto; background: linear-gradient(180deg, #effbfb 0%, #f7f9fb 180px, #f7f9fb 100%); padding-bottom: 120px; }
.scroll-content::-webkit-scrollbar { display: none; }
.page-body { padding: 16px; }
/* 征集说明卡:目的、周期、不可逆提示 */
/* 征集说明卡:征集活动说明与周期 */
.intro-card {
background: #fff; border-radius: 18px; padding: 16px; margin-bottom: 18px;
box-shadow: var(--shadow); border: 1px solid var(--border);
@@ -62,11 +62,6 @@
.intro-card__field span { color: var(--text-muted); }
.intro-card__field b { color: var(--text-primary); font-weight: 600; white-space: nowrap; }
.intro-card__field:last-of-type { border-bottom: none; padding-bottom: 2px; }
.intro-card__warn {
margin-top: 10px; padding: 8px 10px; border-radius: 10px;
background: #fff2f0; border: 1px solid #ffccc7;
font-size: 12px; color: #f5222d; line-height: 1.6;
}
.section-title { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.section-sub { font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
.hospital-card {
@@ -83,10 +78,6 @@
}
.hospital-card__name { font-size: 15px; font-weight: 600; color: var(--text-primary); margin-bottom: 2px; }
.hospital-card__addr { font-size: 12px; color: var(--text-muted); }
.hospital-card__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.htag { font-size: 10px; line-height: 16px; padding: 0 8px; border-radius: 8px; }
.htag--gastro { background: #f6ffed; color: #52c41a; }
.htag--routine { background: #e6f7ff; color: #1890ff; }
/* 多选复选框:区别于正式预约的单选圆点 */
.check-box {
width: 22px; height: 22px; border-radius: 6px; border: 2px solid #d9d9d9;
@@ -108,7 +99,7 @@
.bottom-actions__btn:disabled {
background: #d9d9d9; color: #fff; box-shadow: none; cursor: not-allowed;
}
/* 提交二次确认弹窗:因预选仅可填写一次,提交前强制确认 */
/* 提交二次确认弹窗:提交前列出已选医院供用户核对 */
.modal-overlay {
position: absolute; inset: 0; background: rgba(0,0,0,0.45); z-index: 100;
display: flex; align-items: center; justify-content: center;
@@ -117,7 +108,6 @@
.modal-card { width: 340px; background: #fff; border-radius: 24px; padding: 24px 20px; box-shadow: 0 20px 60px rgba(0,0,0,0.18); }
.modal-card__title { font-size: 18px; font-weight: 700; text-align: center; margin-bottom: 6px; }
.modal-card__sub { font-size: 13px; color: var(--text-muted); text-align: center; margin-bottom: 16px; line-height: 1.6; }
.modal-card__sub b { color: #f5222d; }
.modal-hospital-list { background: #f7f9fb; border-radius: 14px; padding: 12px 14px; margin-bottom: 20px; }
.modal-hospital-list__title { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; }
.modal-hospital-list__item {
@@ -135,6 +125,14 @@
background: linear-gradient(135deg, #13c2c2 0%, #36cfc9 100%);
color: #fff; box-shadow: 0 8px 20px rgba(19, 194, 194, 0.2);
}
/* 轻提示 toast:选择数量不满足 2~3 家时给出校验提醒 */
.toast {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.9);
background: rgba(0,0,0,0.75); color: #fff; padding: 14px 22px; border-radius: 12px;
font-size: 13px; line-height: 1.6; text-align: center; z-index: 200;
opacity: 0; pointer-events: none; transition: all 0.25s ease; max-width: 280px;
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
</style>
</head>
<body>
@@ -158,23 +156,17 @@
<div class="intro-card">
<div class="intro-card__title">📋 2027 年度胃肠镜体检医院意向征集</div>
<div class="intro-card__field"><span>预选日期: 2026-07-01至2026-10-20</span></div>
<div class="intro-card__warn">⚠️ 本次预选每人仅可填写一次,提交后不可修改,请谨慎选择。</div>
</div>
<div class="section-title">选择意向医院</div>
<div class="section-sub">点击卡片可多选,当前已选 <span id="selectedCount">0</span></div>
<div class="section-sub">点击卡片可多选,至少选择2家、最多3家,当前已选 <span id="selectedCount">0</span></div>
<div class="hospital-card" onclick="toggleHospital(this)">
<div class="hospital-card__info">
<div class="hospital-card__icon">🏥</div>
<div>
<div class="hospital-card__name">宁夏宝石花医院</div>
<div class="hospital-card__addr">宁夏银川市</div>
<div class="hospital-card__tags">
<span class="htag htag--gastro">胃肠镜体检</span>
<span class="htag htag--routine">常规体检</span>
</div>
</div>
<div class="hospital-card__addr">宁夏银川市</div> </div>
</div>
<div class="check-box"></div>
</div>
@@ -184,11 +176,7 @@
<div class="hospital-card__icon">🏥</div>
<div>
<div class="hospital-card__name">CQ星龙园医院</div>
<div class="hospital-card__addr">陕西西安市</div>
<div class="hospital-card__tags">
<span class="htag htag--gastro">胃肠镜体检</span>
</div>
</div>
<div class="hospital-card__addr">陕西西安市</div> </div>
</div>
<div class="check-box"></div>
</div>
@@ -198,11 +186,7 @@
<div class="hospital-card__icon">🏥</div>
<div>
<div class="hospital-card__name">唐都医院</div>
<div class="hospital-card__addr">陕西西安市</div>
<div class="hospital-card__tags">
<span class="htag htag--gastro">胃肠镜体检</span>
</div>
</div>
<div class="hospital-card__addr">陕西西安市</div> </div>
</div>
<div class="check-box"></div>
</div>
@@ -212,12 +196,7 @@
<div class="hospital-card__icon">🏥</div>
<div>
<div class="hospital-card__name">西安市第四医院</div>
<div class="hospital-card__addr">陕西西安市</div>
<div class="hospital-card__tags">
<span class="htag htag--gastro">胃肠镜体检</span>
<span class="htag htag--routine">常规体检</span>
</div>
</div>
<div class="hospital-card__addr">陕西西安市</div> </div>
</div>
<div class="check-box"></div>
</div>
@@ -227,11 +206,7 @@
<div class="hospital-card__icon">🏥</div>
<div>
<div class="hospital-card__name">银川市第一人民医院</div>
<div class="hospital-card__addr">宁夏银川市</div>
<div class="hospital-card__tags">
<span class="htag htag--gastro">胃肠镜体检</span>
</div>
</div>
<div class="hospital-card__addr">宁夏银川市</div> </div>
</div>
<div class="check-box"></div>
</div>
@@ -247,7 +222,7 @@
<div class="modal-overlay hidden" id="confirmModal">
<div class="modal-card">
<div class="modal-card__title">确认提交预选</div>
<div class="modal-card__sub">本次预选<b>仅可填写一次,提交后不可修改</b><br>请确认以下意向医院无误</div>
<div class="modal-card__sub">请确认以下意向医院无误</div>
<div class="modal-hospital-list">
<div class="modal-hospital-list__title">已选 <span id="modalCount">0</span> 家医院</div>
<div id="modalHospitalList"></div>
@@ -258,6 +233,8 @@
</div>
</div>
</div>
<div class="toast" id="toast"></div>
</div>
<script>
@@ -274,10 +251,21 @@
btn.disabled = count === 0;
btn.textContent = count === 0 ? '提交预选' : '提交预选(已选 ' + count + ' 家)';
}
// 打开二次确认弹窗前,将已选医院名称注入弹窗列表
// toast 轻提示:展示 2.2 秒后自动消失
var toastTimer = null;
function showToast(msg) {
var t = document.getElementById('toast');
t.textContent = msg;
t.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { t.classList.remove('show'); }, 2200);
}
// 打开二次确认弹窗前校验数量(至少2家、最多3家),并将已选医院名称注入弹窗列表
function openConfirm() {
var actives = document.querySelectorAll('.hospital-card.active');
if (actives.length === 0) return;
if (actives.length < 2) { showToast('至少选择2家意向医院,请继续选择'); return; }
if (actives.length > 3) { showToast('最多选择3家意向医院,请调整后再提交'); return; }
var html = '';
actives.forEach(function (card) {
html += '<div class="modal-hospital-list__item">' + card.querySelector('.hospital-card__name').textContent + '</div>';
+3 -3
View File
@@ -464,7 +464,7 @@
<span class="banner-slide__ring"></span>
<span class="banner-slide__deco">🩺</span>
<div class="banner-slide__title">单位已制定2026年度常规体检计划</div>
<div class="banner-slide__date">🗓 体检日期:2026-09-01 至 2026-12-31</div>
<div class="banner-slide__date">🗓 起止日期:2026-09-01 至 2026-12-31</div>
<div class="banner-slide__fee">
<span class="banner-chip">💰 体检费用额度 <b>¥1500.00</b></span>
</div>
@@ -483,7 +483,7 @@
<span class="banner-slide__ring"></span>
<span class="banner-slide__deco">🔬</span>
<div class="banner-slide__title">单位已拟定2026年度胃肠镜体检计划</div>
<div class="banner-slide__date">🗓 体检日期:2026-10-08 至 2026-12-20</div>
<div class="banner-slide__date">🗓 起止日期:2026-10-08 至 2026-12-20</div>
<!-- 圆圈进度(右下角):已预约 = 60%(已选项目20%→已预约60%→体检中80%→已参检100%),dashoffset = 131.9 × 40% ≈ 52.8 -->
<div class="banner-progress">
<svg width="52" height="52" viewBox="0 0 52 52" fill="none">
@@ -499,7 +499,7 @@
<span class="banner-slide__ring"></span>
<span class="banner-slide__deco">🫀</span>
<div class="banner-slide__title">单位已拟定2026年度冠脉CTA体检计划</div>
<div class="banner-slide__date">🗓 体检日期:2026-10-08 至 2026-12-20</div>
<div class="banner-slide__date">🗓 起止日期:2026-10-08 至 2026-12-20</div>
<!-- 圆圈进度(右下角):已预约 = 60%(已选项目20%→已预约60%→体检中80%→已参检100%),dashoffset = 131.9 × 40% ≈ 52.8 -->
<div class="banner-progress">
<svg width="52" height="52" viewBox="0 0 52 52" fill="none">
@@ -199,37 +199,6 @@
border: 1px solid #e4f7f5;
box-shadow: var(--shadow);
}
.person-card__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.code-switch {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 2px;
border-radius: 999px;
border: 1px solid #e4f7f5;
background: #f2fbfa;
}
.code-switch__opt {
border: none;
background: transparent;
font-size: 12px;
line-height: 1;
color: var(--text-secondary);
padding: 5px 10px;
border-radius: 999px;
cursor: pointer;
}
.code-switch__opt--active {
background: var(--primary);
color: #fff;
font-weight: 600;
box-shadow: 0 2px 6px rgba(19, 194, 194, 0.35);
}
.qrcode-wrap { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.code-img {
background: #fff;
@@ -239,14 +208,8 @@
transition: transform 0.2s;
}
.code-img:hover { transform: scale(1.04); }
.code-img--qr { width: 160px; height: 160px; }
.code-img--bar { width: 224px; height: 132px; }
.qrcode-wrap__hint { font-size: 12px; color: var(--text-muted); }
/* 条形码/二维码互斥显隐 */
.person-card .code-only--bar { display: flex; }
.person-card .code-only--qr { display: none; }
.person-card--qr .code-only--bar { display: none; }
.person-card--qr .code-only--qr { display: flex; }
/* 放大遮罩 */
.qr-overlay {
display: none;
@@ -259,7 +222,6 @@
cursor: pointer;
}
.qr-overlay--visible { display: flex; }
.qr-overlay__img--qr { width: 260px; height: 260px; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); background:#fff; }
.qr-overlay__img--bar { width: 300px; height: 176px; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); background:#fff; }
/* 体检须知 */
.tips-card {
@@ -376,8 +338,7 @@
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="demo-select__menu hidden" id="demoMenu">
<div class="demo-select__item demo-select__item--active" data-status="pending" onclick="setDemoStatus('pending')">待体检<span class="check"></span></div>
<div class="demo-select__item" data-status="done" onclick="setDemoStatus('done')">已完成<span class="check"></span></div>
<div class="demo-select__item demo-select__item--active" data-status="pending" onclick="setDemoStatus('pending')">已预约<span class="check"></span></div>
<div class="demo-select__item" data-status="none" onclick="setDemoStatus('none')">无预约信息<span class="check"></span></div>
</div>
</div>
@@ -394,54 +355,24 @@
<!-- ======================= 常规体检 · 待体检 ======================= -->
<div class="panel" data-type="routine" data-status="pending">
<div class="person-card" id="codeRoutine">
<div class="person-card__header">
<div class="section-title" style="margin-bottom:0">体检签到码</div>
<!-- [交互] 点击右上角「条形码/二维码」双选项卡切换签到码展示 -->
<div class="code-switch">
<button class="code-switch__opt code-switch__opt--active" type="button" id="codeRoutineTabBar" onclick="setCodeMode('codeRoutine', 'bar')">条形码</button>
<button class="code-switch__opt" type="button" id="codeRoutineTabQr" onclick="setCodeMode('codeRoutine', 'qr')">二维码</button>
</div>
</div>
<div class="qrcode-wrap code-only--bar">
<div class="qrcode-wrap">
<!-- [交互] 点击条码放大展示 -->
<svg class="code-img code-img--bar" onclick="openCodeOverlay('codeRoutine')" viewBox="0 0 200 110" xmlns="http://www.w3.org/2000/svg">
<svg class="code-img code-img--bar" onclick="openCodeOverlay()" viewBox="0 0 200 110" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="110" fill="#fff"/>
<rect x="10" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="16" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="20" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="25" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="32" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="36" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="41" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="45" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="50" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="54" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="58" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="65" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="69" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="74" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="78" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="83" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="87" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="94" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="98" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="102" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="107" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="111" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="118" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="122" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="126" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="131" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="136" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="140" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="147" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="151" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="155" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="160" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="164" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="169" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="176" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="180" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="184" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="189" y="10" width="2.5" height="72" fill="#1a1a1a"/>
<text x="100" y="97" text-anchor="middle" font-size="10" font-family="monospace" fill="#666">TJ2026032300871</text>
</svg>
<span class="qrcode-wrap__hint">点击条码可放大查看</span>
</div>
<div class="qrcode-wrap code-only--qr">
<!-- [交互] 点击二维码放大展示 -->
<svg class="code-img code-img--qr" onclick="openCodeOverlay('codeRoutine')" viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg">
<rect width="160" height="160" fill="#fff"/>
<rect x="12" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="108" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="114" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="120" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="12" y="108" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="114" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="120" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="60" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="12" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="24" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="24" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="36" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="36" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="28" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="60" width="8" height="8" fill="#1a1a1a"/>
<rect x="20" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="72" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="36" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="84" width="8" height="8" fill="#1a1a1a"/>
<rect x="28" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="52" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="96" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="108" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="120" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="132" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="140" width="8" height="8" fill="#1a1a1a"/>
</svg>
<span class="qrcode-wrap__hint">点击二维码可放大查看</span>
</div>
</div>
<div class="summary-card">
<div class="section-title">常规体检</div>
<div class="summary-list">
<div class="summary-item"><div class="summary-item__label">体检医院</div><div class="summary-item__value">宁夏宝石花医院</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">预约日期</div><div class="summary-item__value">2026-03-23</div></div>
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">张旭</div></div>
<div class="summary-item"><div class="summary-item__label">单位部门</div><div class="summary-item__value">采油一厂·xx作业区</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#fa8c16;">待体检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">单位部门</div><div class="summary-item__value">采油一厂·xx作业区</div></div> </div>
</div>
<div class="progress-card">
<!-- [交互] 点击切换已检/未检 Tab -->
@@ -460,11 +391,10 @@
<div class="section-title">常规体检</div>
<div class="summary-list">
<div class="summary-item"><div class="summary-item__label">体检医院</div><div class="summary-item__value">宁夏宝石花医院</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">预约日期</div><div class="summary-item__value">2026-03-23</div></div>
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">张旭</div></div>
<div class="summary-item"><div class="summary-item__label">单位部门</div><div class="summary-item__value">采油一厂·xx作业区</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#52c41a;">已参检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">单位部门</div><div class="summary-item__value">采油一厂·xx作业区</div></div> </div>
</div>
<div class="progress-card">
<!-- [交互] 点击切换已检/未检 Tab -->
@@ -499,9 +429,7 @@
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">刘韵琴</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">体检日期</div><div class="summary-item__value">2026年3月23日</div></div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">胃肠镜体检</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#fa8c16;">待体检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">胃肠镜体检</div></div> </div>
</div>
<div class="tips-card">
<div class="tips-card__title">体检须知</div>
@@ -523,9 +451,7 @@
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">刘韵琴</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">体检日期</div><div class="summary-item__value">2026年3月23日</div></div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">胃肠镜体检</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#52c41a;">已参检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">胃肠镜体检</div></div> </div>
</div>
</div>
@@ -551,9 +477,7 @@
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">刘韵琴</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">体检日期</div><div class="summary-item__value">2026年3月23日</div></div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">冠脉CTA体检</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#fa8c16;">待体检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">冠脉CTA体检</div></div> </div>
</div>
<div class="tips-card">
<div class="tips-card__title">体检须知</div>
@@ -575,9 +499,7 @@
<div class="summary-item"><div class="summary-item__label">体检员工</div><div class="summary-item__value">刘韵琴</div></div>
<div class="summary-item"><div class="summary-item__label">体检地址</div><div class="summary-item__value">银川市西夏区怀远路148号</div></div>
<div class="summary-item"><div class="summary-item__label">体检日期</div><div class="summary-item__value">2026年3月23日</div></div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">冠脉CTA体检</div></div>
<div class="summary-item"><div class="summary-item__label">状态</div><div class="summary-item__value" style="color:#52c41a;">已参检</div></div>
</div>
<div class="summary-item"><div class="summary-item__label">体检项目</div><div class="summary-item__value">冠脉CTA体检</div></div> </div>
</div>
</div>
@@ -599,28 +521,11 @@
<!-- 签到码放大遮罩 -->
<div class="qr-overlay" id="qrOverlay" onclick="this.classList.remove('qr-overlay--visible')">
<div onclick="event.stopPropagation()">
<svg class="qr-overlay__img--bar" id="ovlBarcode" viewBox="0 0 200 110" xmlns="http://www.w3.org/2000/svg" style="display:none;">
<svg class="qr-overlay__img--bar" id="ovlBarcode" viewBox="0 0 200 110" xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="110" fill="#fff"/>
<rect x="10" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="16" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="20" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="25" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="32" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="36" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="41" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="45" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="50" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="54" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="58" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="65" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="69" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="74" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="78" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="83" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="87" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="94" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="98" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="102" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="107" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="111" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="118" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="122" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="126" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="131" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="136" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="140" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="147" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="151" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="155" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="160" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="164" y="10" width="2.5" height="72" fill="#1a1a1a"/><rect x="169" y="10" width="4" height="72" fill="#1a1a1a"/><rect x="176" y="10" width="1.5" height="72" fill="#1a1a1a"/><rect x="180" y="10" width="2" height="72" fill="#1a1a1a"/><rect x="184" y="10" width="3" height="72" fill="#1a1a1a"/><rect x="189" y="10" width="2.5" height="72" fill="#1a1a1a"/>
<text x="100" y="97" text-anchor="middle" font-size="10" font-family="monospace" fill="#666">签到码编号</text>
</svg>
<svg class="qr-overlay__img--qr" id="ovlQr" viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" style="display:none;">
<rect width="160" height="160" fill="#fff"/>
<rect x="12" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="108" y="12" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="114" y="18" width="28" height="28" rx="2" fill="#fff"/><rect x="120" y="24" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="12" y="108" width="40" height="40" rx="4" fill="#1a1a1a"/><rect x="18" y="114" width="28" height="28" rx="2" fill="#fff"/><rect x="24" y="120" width="16" height="16" rx="2" fill="#1a1a1a"/>
<rect x="60" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="12" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="12" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="24" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="24" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="36" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="36" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="28" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="60" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="60" width="8" height="8" fill="#1a1a1a"/>
<rect x="20" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="44" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="72" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="72" width="8" height="8" fill="#1a1a1a"/>
<rect x="12" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="36" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="60" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="84" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="84" width="8" height="8" fill="#1a1a1a"/>
<rect x="28" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="52" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="68" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="96" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="96" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="108" width="8" height="8" fill="#1a1a1a"/><rect x="124" y="108" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="84" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="108" y="120" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="120" width="8" height="8" fill="#1a1a1a"/>
<rect x="60" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="76" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="92" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="116" y="132" width="8" height="8" fill="#1a1a1a"/><rect x="140" y="132" width="8" height="8" fill="#1a1a1a"/>
<rect x="68" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="100" y="140" width="8" height="8" fill="#1a1a1a"/><rect x="132" y="140" width="8" height="8" fill="#1a1a1a"/>
</svg>
</div>
</div>
@@ -668,19 +573,8 @@
}
});
// ===== 签到码:条形码 ↔ 二维码切换 =====
// [交互] 点击右上角双选项卡切换条形码/二维码,默认条形码,当前模式选项高亮
function setCodeMode(cardId, mode) {
var isQr = mode === 'qr';
document.getElementById(cardId).classList.toggle('person-card--qr', isQr);
document.getElementById(cardId + 'TabBar').classList.toggle('code-switch__opt--active', !isQr);
document.getElementById(cardId + 'TabQr').classList.toggle('code-switch__opt--active', isQr);
}
// [交互] 点击签到码放大查看(遮罩跟随当前类型)
function openCodeOverlay(cardId) {
var isQr = document.getElementById(cardId).classList.contains('person-card--qr');
document.getElementById('ovlQr').style.display = isQr ? 'block' : 'none';
document.getElementById('ovlBarcode').style.display = isQr ? 'none' : 'block';
// [交互] 点击条码放大查看
function openCodeOverlay() {
document.getElementById('qrOverlay').classList.add('qr-overlay--visible');
}
@@ -421,9 +421,9 @@
<div class="divider"></div>
<!-- ========== 影像报告 ========== -->
<!-- ========== 报告图片 ========== -->
<div class="report-section">
<div class="report-section__title">影像报告</div>
<div class="report-section__title">报告图片</div>
<div class="image-section__label">胃镜报告</div>
<div class="image-view-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
+123 -8
View File
@@ -83,6 +83,42 @@
.trend-entry__go svg { width: 14px; height: 14px; }
/* 报告列表 */
.section-title { font-size: 16px; font-weight: 700; color: #1a1a1a; margin: 22px 2px 12px; }
/* 报告筛选:年份日历 + 体检类型下拉,默认全部 */
.filter-bar { display: flex; gap: 10px; margin: 0 2px 14px; position: relative; z-index: 5; }
.filter-picker { position: relative; }
.filter-picker__trigger {
display: inline-flex; align-items: center; gap: 6px;
height: 32px; padding: 0 12px; border-radius: 16px;
border: 1px solid #e8e8e8; background: #fff;
font-size: 13px; color: #1a1a1a; cursor: pointer; transition: all 0.2s;
}
.filter-picker__trigger--active { border-color: #13c2c2; color: #0f9b9b; font-weight: 600; }
.year-panel {
position: absolute; top: 38px; left: 0; width: 236px;
background: #fff; border-radius: 16px; padding: 12px;
border: 1px solid #f0f0f0; box-shadow: 0 12px 32px rgba(0,0,0,0.12); z-index: 10;
}
.year-panel.hidden, .type-menu.hidden { display: none; }
.year-panel__title { font-size: 13px; font-weight: 700; text-align: center; margin-bottom: 10px; }
.year-panel__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.year-cell {
height: 34px; display: flex; align-items: center; justify-content: center;
border-radius: 10px; background: #f7f9fb; font-size: 13px; color: #1a1a1a; cursor: pointer; transition: all 0.2s;
}
.year-cell.selected { background: #e6fffb; color: #0f9b9b; font-weight: 700; }
.type-menu {
position: absolute; top: 38px; left: 0; min-width: 132px;
background: #fff; border-radius: 14px; padding: 6px;
border: 1px solid #f0f0f0; box-shadow: 0 12px 32px rgba(0,0,0,0.12); z-index: 10;
}
.type-menu__item {
display: flex; align-items: center; justify-content: space-between;
padding: 9px 12px; border-radius: 10px; font-size: 13px; color: #1a1a1a; cursor: pointer;
}
.type-menu__item:hover { background: #f7f9fb; }
.type-menu__item.selected { background: #e6fffb; color: #0f9b9b; font-weight: 700; }
.type-menu__item .check { visibility: hidden; font-size: 12px; }
.type-menu__item.selected .check { visibility: visible; }
.report-list { display: flex; flex-direction: column; gap: 12px; }
.report-item {
background: #fff; border-radius: 16px; padding: 16px; display: flex; align-items: center; gap: 14px;
@@ -165,9 +201,42 @@
<!-- 报告列表 -->
<div class="section-title">我的历年报告</div>
<div class="report-list">
<!-- [交互] 报告筛选:年份点开日历面板选择,体检类型下拉选择,默认「全部」,两个条件叠加过滤 -->
<div class="filter-bar">
<div class="filter-picker">
<button class="filter-picker__trigger" type="button" id="yearTrigger" onclick="toggleYearPanel(event)">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span id="yearTriggerText">全部年份</span>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="year-panel hidden" id="yearPanel">
<div class="year-panel__title">选择年份</div>
<div class="year-panel__grid">
<span class="year-cell selected" onclick="pickYear('all', this)">全部</span>
<span class="year-cell" onclick="pickYear('2025', this)">2025</span>
<span class="year-cell" onclick="pickYear('2024', this)">2024</span>
<span class="year-cell" onclick="pickYear('2023', this)">2023</span>
<span class="year-cell" onclick="pickYear('2022', this)">2022</span>
<span class="year-cell" onclick="pickYear('2021', this)">2021</span>
</div>
</div>
</div>
<div class="filter-picker">
<button class="filter-picker__trigger" type="button" id="typeTrigger" onclick="toggleTypeMenu(event)">
<span id="typeTriggerText">全部类型</span>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="type-menu hidden" id="typeMenu">
<div class="type-menu__item selected" onclick="pickType('all', this)">全部类型<span class="check"></span></div>
<div class="type-menu__item" onclick="pickType('regular', this)">常规体检<span class="check"></span></div>
<div class="type-menu__item" onclick="pickType('gastro', this)">胃肠镜体检<span class="check"></span></div>
<div class="type-menu__item" onclick="pickType('cta', this)">冠脉CTA体检<span class="check"></span></div>
</div>
</div>
</div>
<div class="report-list" id="reportList">
<!-- [交互] 点击跳转至: report-gastro.html(胃肠镜报告) -->
<div class="report-item" onclick="location.href='report-gastro.html'">
<div class="report-item" data-year="2025" data-type="gastro" onclick="location.href='report-gastro.html'">
<div class="report-item__icon" style="background:#e6fffb;color:#08979c;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</div>
@@ -181,7 +250,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-cta.html(冠脉CTA报告) -->
<div class="report-item" onclick="location.href='report-cta.html'">
<div class="report-item" data-year="2025" data-type="cta" onclick="location.href='report-cta.html'">
<div class="report-item__icon" style="background:#fff1f0;color:#cf1322;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/><path d="M3.2 12h5.3l1.5-3 2.5 6 1.5-3h5.3"/></svg>
</div>
@@ -195,7 +264,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html(常规体检) -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item" data-year="2025" data-type="regular" onclick="location.href='report-query.html'">
<div class="report-item__icon" style="background:#e6f7ff;color:#096dd9;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
@@ -209,7 +278,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html(常规体检) -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item" data-year="2024" data-type="regular" onclick="location.href='report-query.html'">
<div class="report-item__icon" style="background:#e6f7ff;color:#096dd9;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
@@ -223,7 +292,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html(常规体检) -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item" data-year="2023" data-type="regular" onclick="location.href='report-query.html'">
<div class="report-item__icon" style="background:#e6f7ff;color:#096dd9;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
@@ -237,7 +306,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html(常规体检) -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item" data-year="2022" data-type="regular" onclick="location.href='report-query.html'">
<div class="report-item__icon" style="background:#e6f7ff;color:#096dd9;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
@@ -251,7 +320,7 @@
<span class="report-item__arrow"></span>
</div>
<!-- [交互] 点击跳转至: report-query.html(常规体检) -->
<div class="report-item" onclick="location.href='report-query.html'">
<div class="report-item" data-year="2021" data-type="regular" onclick="location.href='report-query.html'">
<div class="report-item__icon" style="background:#e6f7ff;color:#096dd9;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="14" y2="17"/></svg>
</div>
@@ -269,5 +338,51 @@
</div>
</div>
</div>
<script>
// 报告筛选:年份日历 + 体检类型下拉,两个条件叠加过滤,默认「全部」
var filterYear = 'all';
var filterType = 'all';
function toggleYearPanel(e) {
e.stopPropagation();
closeTypeMenu();
document.getElementById('yearPanel').classList.toggle('hidden');
}
function toggleTypeMenu(e) {
e.stopPropagation();
closeYearPanel();
document.getElementById('typeMenu').classList.toggle('hidden');
}
function closeYearPanel() { document.getElementById('yearPanel').classList.add('hidden'); }
function closeTypeMenu() { document.getElementById('typeMenu').classList.add('hidden'); }
function pickYear(year, el) {
filterYear = year;
el.parentNode.querySelectorAll('.year-cell').forEach(function (c) { c.classList.remove('selected'); });
el.classList.add('selected');
document.getElementById('yearTriggerText').textContent = year === 'all' ? '全部年份' : year + '年';
document.getElementById('yearTrigger').classList.toggle('filter-picker__trigger--active', year !== 'all');
closeYearPanel();
applyReportFilter();
}
function pickType(type, el) {
filterType = type;
el.parentNode.querySelectorAll('.type-menu__item').forEach(function (i) { i.classList.remove('selected'); });
el.classList.add('selected');
document.getElementById('typeTriggerText').textContent = el.textContent.replace('✓', '').trim();
document.getElementById('typeTrigger').classList.toggle('filter-picker__trigger--active', type !== 'all');
closeTypeMenu();
applyReportFilter();
}
function applyReportFilter() {
document.querySelectorAll('#reportList .report-item').forEach(function (item) {
var show = (filterYear === 'all' || item.dataset.year === filterYear) &&
(filterType === 'all' || item.dataset.type === filterType);
item.style.display = show ? 'flex' : 'none';
});
}
// 点击弹层外部收起面板
document.addEventListener('click', function (e) {
if (!e.target.closest('.filter-picker')) { closeYearPanel(); closeTypeMenu(); }
});
</script>
</body>
</html>
@@ -140,14 +140,6 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<span class="info-row__label">基础代谢率 (kcal)</span>
<span class="info-row__value">1963.2</span>
</div>
<div class="info-row">
<span class="info-row__label">身份证号</span>
<span class="info-row__value">640382199006054618</span>
</div>
<div class="info-row">
<span class="info-row__label">手机号</span>
<span class="info-row__value">13581557014</span>
</div>
</div>
</div>
@@ -83,7 +83,6 @@
<div class="fld"><span class="fld__label">活动日期</span><input value="2026-09-04" readonly></div>
<div class="fld"><span class="fld__label"><span class="fld__req">*</span>参加人数</span><input value="40"><span class="fld__unit"></span></div>
<div class="fld"><span class="fld__label"><span class="fld__req">*</span>覆盖区域</span><input placeholder="如:管理四中心 / 全作业区" value="管理四中心"></div>
<div class="fld"><span class="fld__label">时长</span><input value="45"><span class="fld__unit">分钟</span></div>
<div class="fld fld--full"><span class="fld__label"><span class="fld__req">*</span>内容</span><textarea placeholder="请输入宣教/培训内容摘要">围绕秋冬季流感与肺炎支原体感染高发季,讲解通风消毒、疫苗接种指征与出现症状后的处置流程;现场演示正确洗手七步法,发放防护要点折页。</textarea></div>
</div>
</div>
+13 -13
View File
@@ -359,20 +359,20 @@ var dormMeta=[
{name:'401室',building:'靖边气田倒班宿舍楼',beds:4},
{name:'402室',building:'靖边气田倒班宿舍楼',beds:2}
];
/* empPool:与全站演示数据共享的员工池(已脱敏) */
/* empPool:与全站演示数据共享的员工池 */
var empPool=[
{no:'CQ021578',name:'石维国',org:'采油一厂',depart:'樊学作业区',phone:'139****3456'},
{no:'CQ032144',name:'李桂芳',org:'采油一厂',depart:'樊学作业区',phone:'139****7788'},
{no:'CQ045130',name:'杨慧敏',org:'采油一厂',depart:'麻子山作业区',phone:'139****5012'},
{no:'CQ019203',name:'曲志明',org:'采油一厂',depart:'樊学作业区',phone:'139****7208'},
{no:'CQ033096',name:'赵万福',org:'采油二厂',depart:'桐园作业区',phone:'139****6630'},
{no:'CQ027710',name:'张秀兰',org:'采油二厂',depart:'姬圪台作业区',phone:'139****4415'},
{no:'CQ010564',name:'高建军',org:'第四采油厂',depart:'西峰作业区',phone:'139****4320'},
{no:'CQ041822',name:'闫婷',org:'第四采油厂',depart:'西峰作业区',phone:'139****4531'},
{no:'CQ036441',name:'刘玉梅',org:'采气一厂',depart:'靖边气田',phone:'139****3745'},
{no:'CQ029905',name:'呼建国',org:'采气一厂',depart:'靖边气田',phone:'139****3870'},
{no:'CQ044871',name:'白瑞',org:'采气一厂',depart:'靖边气田(轮训)',phone:'139****2317'},
{no:'CQ022317',name:'徐立成',org:'第四采油厂',depart:'西峰作业区(轮训)',phone:'139****4906'}
{no:'CQ021578',name:'石维国',org:'采油一厂',depart:'樊学作业区',phone:'13961153456'},
{no:'CQ032144',name:'李桂芳',org:'采油一厂',depart:'樊学作业区',phone:'13927127788'},
{no:'CQ045130',name:'杨慧敏',org:'采油一厂',depart:'麻子山作业区',phone:'13941215012'},
{no:'CQ019203',name:'曲志明',org:'采油一厂',depart:'樊学作业区',phone:'13989577208'},
{no:'CQ033096',name:'赵万福',org:'采油二厂',depart:'桐园作业区',phone:'13969296630'},
{no:'CQ027710',name:'张秀兰',org:'采油二厂',depart:'姬圪台作业区',phone:'13943704415'},
{no:'CQ010564',name:'高建军',org:'第四采油厂',depart:'西峰作业区',phone:'13941064320'},
{no:'CQ041822',name:'闫婷',org:'第四采油厂',depart:'西峰作业区',phone:'13900024531'},
{no:'CQ036441',name:'刘玉梅',org:'采气一厂',depart:'靖边气田',phone:'13986823745'},
{no:'CQ029905',name:'呼建国',org:'采气一厂',depart:'靖边气田',phone:'13920263870'},
{no:'CQ044871',name:'白瑞',org:'采气一厂',depart:'靖边气田(轮训)',phone:'13928272317'},
{no:'CQ022317',name:'徐立成',org:'第四采油厂',depart:'西峰作业区(轮训)',phone:'13937154906'}
];
/* 床位→员工分配(key=楼栋|床位编号) */
var assigns={
@@ -253,15 +253,15 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
var curTab='in';
/* 员工档案简版:供工单详情「员工信息」区块取数 */
var empProfiles={
'CQ021578':{name:'石维国',sex:'男',age:52,unit:'采油一厂',dept:'樊学作业区',phone:'182****3367',chronic:'高血压(2级)'},
'CQ032144':{name:'李桂芳',sex:'女',age:47,unit:'采油一厂',dept:'樊学作业区',phone:'137****2208',chronic:'无明确慢性病史'},
'CQ033096':{name:'赵万福',sex:'男',age:58,unit:'采油二厂',dept:'桐园作业区',phone:'158****6620',chronic:'高血压(3级)、2型糖尿病'},
'CQ036441':{name:'刘玉梅',sex:'女',age:44,unit:'采气一厂',dept:'靖边气田',phone:'188****9032',chronic:'无明确慢性病史'},
'CQ010564':{name:'高建军',sex:'男',age:55,unit:'第四采油厂',dept:'西峰作业区',phone:'135****4471',chronic:'颈椎病史、高脂血症'},
'CQ045130':{name:'杨慧敏',sex:'女',age:38,unit:'采油一厂',dept:'麻子山作业区',phone:'176****5583',chronic:'无明确慢性病史'},
'CQ027710':{name:'张秀兰',sex:'女',age:50,unit:'第四采油厂',dept:'西峰作业区',phone:'139****2760',chronic:'冠心病'},
'CQ022317':{name:'徐立成',sex:'男',age:49,unit:'采油二厂',dept:'桐园作业区',phone:'150****8819',chronic:'慢性支气管炎'},
'CQ029905':{name:'呼建国',sex:'男',age:53,unit:'采气一厂',dept:'靖边气田',phone:'187****3345',chronic:'支气管哮喘'}
'CQ021578':{name:'石维国',sex:'男',age:52,unit:'采油一厂',dept:'樊学作业区',phone:'18238083367',chronic:'高血压(2级)'},
'CQ032144':{name:'李桂芳',sex:'女',age:47,unit:'采油一厂',dept:'樊学作业区',phone:'13798352208',chronic:'无明确慢性病史'},
'CQ033096':{name:'赵万福',sex:'男',age:58,unit:'采油二厂',dept:'桐园作业区',phone:'15869506620',chronic:'高血压(3级)、2型糖尿病'},
'CQ036441':{name:'刘玉梅',sex:'女',age:44,unit:'采气一厂',dept:'靖边气田',phone:'18814269032',chronic:'无明确慢性病史'},
'CQ010564':{name:'高建军',sex:'男',age:55,unit:'第四采油厂',dept:'西峰作业区',phone:'13515064471',chronic:'颈椎病史、高脂血症'},
'CQ045130':{name:'杨慧敏',sex:'女',age:38,unit:'采油一厂',dept:'麻子山作业区',phone:'17646095583',chronic:'无明确慢性病史'},
'CQ027710':{name:'张秀兰',sex:'女',age:50,unit:'第四采油厂',dept:'西峰作业区',phone:'13968902760',chronic:'冠心病'},
'CQ022317':{name:'徐立成',sex:'男',age:49,unit:'采油二厂',dept:'桐园作业区',phone:'15089628819',chronic:'慢性支气管炎'},
'CQ029905':{name:'呼建国',sex:'男',age:53,unit:'采气一厂',dept:'靖边气田',phone:'18730413345',chronic:'支气管哮喘'}
};
/* 员工呼入:主呼设备=床头/面板(HC-*),被呼设备=值班室终端(HD-*);赵万福(182/110)与结果异常 YC-20260905-05 同源事件 */
var incoming=[
+12 -12
View File
@@ -321,18 +321,18 @@ var dorms=[
];
/* [交互] 床位下钻演示数据:员工池与「床位信息」页一致(key=楼栋|床位编号) */
var empLite={
'CQ021578':{name:'石维国',unit:'采油一厂',depart:'樊学作业区',phone:'139****3456'},
'CQ032144':{name:'李桂芳',unit:'采油一厂',depart:'樊学作业区',phone:'139****7788'},
'CQ019203':{name:'曲志明',unit:'采油一厂',depart:'樊学作业区',phone:'139****7208'},
'CQ045130':{name:'杨慧敏',unit:'采油一厂',depart:'麻子山作业区',phone:'139****5012'},
'CQ033096':{name:'赵万福',unit:'采油二厂',depart:'桐园作业区',phone:'139****6630'},
'CQ022317':{name:'徐立成',unit:'第四采油厂',depart:'西峰作业区(轮训)',phone:'139****4906'},
'CQ010564':{name:'高建军',unit:'第四采油厂',depart:'西峰作业区',phone:'139****4320'},
'CQ041822':{name:'闫婷',unit:'第四采油厂',depart:'西峰作业区',phone:'139****4531'},
'CQ044871':{name:'白瑞',unit:'采气一厂',depart:'靖边气田(轮训)',phone:'139****2317'},
'CQ027710':{name:'张秀兰',unit:'采油二厂',depart:'姬圪台作业区',phone:'139****4415'},
'CQ036441':{name:'刘玉梅',unit:'采气一厂',depart:'靖边气田',phone:'139****3745'},
'CQ029905':{name:'呼建国',unit:'采气一厂',depart:'靖边气田',phone:'139****3870'}
'CQ021578':{name:'石维国',unit:'采油一厂',depart:'樊学作业区',phone:'13961153456'},
'CQ032144':{name:'李桂芳',unit:'采油一厂',depart:'樊学作业区',phone:'13927127788'},
'CQ019203':{name:'曲志明',unit:'采油一厂',depart:'樊学作业区',phone:'13989577208'},
'CQ045130':{name:'杨慧敏',unit:'采油一厂',depart:'麻子山作业区',phone:'13941215012'},
'CQ033096':{name:'赵万福',unit:'采油二厂',depart:'桐园作业区',phone:'13969296630'},
'CQ022317':{name:'徐立成',unit:'第四采油厂',depart:'西峰作业区(轮训)',phone:'13937154906'},
'CQ010564':{name:'高建军',unit:'第四采油厂',depart:'西峰作业区',phone:'13941064320'},
'CQ041822':{name:'闫婷',unit:'第四采油厂',depart:'西峰作业区',phone:'13900024531'},
'CQ044871':{name:'白瑞',unit:'采气一厂',depart:'靖边气田(轮训)',phone:'13928272317'},
'CQ027710':{name:'张秀兰',unit:'采油二厂',depart:'姬圪台作业区',phone:'13943704415'},
'CQ036441':{name:'刘玉梅',unit:'采气一厂',depart:'靖边气田',phone:'13986823745'},
'CQ029905':{name:'呼建国',unit:'采气一厂',depart:'靖边气田',phone:'13920263870'}
};
var bedAssigns={
'樊学倒班宿舍1号楼|101-1':'CQ021578','樊学倒班宿舍1号楼|101-2':'CQ032144','樊学倒班宿舍1号楼|102-1':'CQ019203',
@@ -46,7 +46,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 概览卡 */
.stat-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px;}
.stat-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:16px;}
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;display:flex;align-items:center;gap:14px;}
.stat-card__icon{width:44px;height:44px;border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0;}
.stat-card__icon--blue{background:var(--primary-light);}
@@ -113,8 +113,8 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
.drawer__close:hover{color:var(--text-primary);}
.drawer__body{flex:1;overflow-y:auto;padding:24px;}
.drawer__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 24px;border-top:1px solid var(--border);flex-shrink:0;}
.drawer__hint{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:14px;}
.drawer__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:14px;}
.drawer__toolbar .grow{margin-left:auto;font-size:var(--font-sm);color:var(--text-placeholder);}
/* 内嵌小表格 */
.mini-table{width:100%;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;min-width:0;}
@@ -227,12 +227,12 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<div class="drawer__header"><span id="rosterTitle">已分配员工名册</span><button class="drawer__close" onclick="closeDrawer()">×</button></div>
<div class="drawer__body">
<!-- [交互] 名册按姓名/员工编号即时过滤 -->
<div class="drawer__hint" id="rosterHint"></div>
<div class="drawer__toolbar">
<div class="filter-item"><label>姓名</label>
<input class="filter-input" id="rsName" placeholder="请输入姓名" style="min-width:100px;" oninput="renderRoster()"></div>
<div class="filter-item"><label>员工编号</label>
<input class="filter-input" id="rsCode" placeholder="请输入员工编号" style="min-width:100px;" oninput="renderRoster()"></div>
<span class="grow" id="rosterHint"></span>
</div>
<table class="mini-table" style="border-collapse:separate;">
<thead><tr><th>单位名称</th><th>部门名称</th><th>姓名</th><th>性别</th><th>年龄</th><th>员工编号</th></tr></thead>
@@ -269,6 +269,9 @@ var statPoints=[
roster:[{unit:'采气一厂',dept:'苏里格南作业区',name:'徐立成',sex:'男',age:44,code:'CQ022317'}]}
];
/* 未分配员工按单位汇总(与「员工分配」页未分配 Tab 演示数据一致,共 9 人) */
var unallocByUnit={'采油一厂':3,'采油二厂':2,'第四采油厂':2,'采气一厂':2};
/* ===== 单位→医疗点级联(由统计数据生成,保证单一数据源) ===== */
function initUnitSel(){
var sel=document.getElementById('filterUnit');
@@ -291,14 +294,15 @@ function currentData(){
return statPoints.filter(function(p){return (!unit||p.unit===unit)&&(!point||p.name===point);});
}
function renderStats(list){
var staff=0,alloc=0;
list.forEach(function(p){staff+=p.staff;alloc+=p.roster.length;});
var avg=list.length?(alloc/list.length).toFixed(1):'0.0';
var alloc=0;
list.forEach(function(p){alloc+=p.roster.length;});
var unit=document.getElementById('filterUnit').value,un=0;
for(var k in unallocByUnit){if(!unit||k===unit)un+=unallocByUnit[k];}
var total=alloc+un;
document.getElementById('statCards').innerHTML=
'<div class="stat-card"><div class="stat-card__icon stat-card__icon--blue">🏥</div><div><div class="stat-card__label">医疗点数量</div><div class="stat-card__value">'+list.length+'</div><div class="stat-card__extra">当前筛选范围内</div></div></div>'
+'<div class="stat-card"><div class="stat-card__icon stat-card__icon--green">👩‍⚕️</div><div><div class="stat-card__label">医护人员总数</div><div class="stat-card__value">'+staff+'</div><div class="stat-card__extra">来自系统管理医护角色统计</div></div></div>'
+'<div class="stat-card"><div class="stat-card__icon stat-card__icon--orange">👥</div><div><div class="stat-card__label">分配员工数</div><div class="stat-card__value">'+alloc+'</div><div class="stat-card__extra">在「员工分配」页维护</div></div></div>'
+'<div class="stat-card"><div class="stat-card__icon stat-card__icon--purple">📊</div><div><div class="stat-card__label">平均每点分配人数</div><div class="stat-card__value">'+avg+'</div><div class="stat-card__extra">已分配员工总数 ÷ 医疗点数</div></div></div>';
'<div class="stat-card"><div class="stat-card__icon stat-card__icon--blue">👥</div><div><div class="stat-card__label">员工总数量</div><div class="stat-card__value">'+total+'</div><div class="stat-card__extra">已分配 + 未分配</div></div></div>'
+'<div class="stat-card"><div class="stat-card__icon stat-card__icon--green"></div><div><div class="stat-card__label">已分配员工数量</div><div class="stat-card__value">'+alloc+'</div><div class="stat-card__extra">在「员工分配」页维护</div></div></div>'
+'<div class="stat-card"><div class="stat-card__icon stat-card__icon--orange">📋</div><div><div class="stat-card__label">分配员工数</div><div class="stat-card__value">'+un+'</div><div class="stat-card__extra">在「员工分配」页进行分配</div></div></div>';
}
function renderTable(){
var list=currentData();
+318 -64
View File
@@ -133,6 +133,35 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;-webkit-appearance:none;padding-right:28px;cursor:pointer;}
.drop-zone{border:1px dashed var(--border);border-radius:var(--radius-sm);padding:16px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);cursor:pointer;line-height:1.8;}
.drop-zone:hover{border-color:var(--primary);color:var(--primary);background:#F8FBFF;}
/* 分配员工弹窗(左右双栏) */
.modal--xl{width:1080px;}
.alloc-emp{display:flex;gap:16px;}
.alloc-emp__left,.alloc-emp__right{flex:1;min-width:0;}
.alloc-emp__panel-title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;display:flex;align-items:center;gap:6px;}
.alloc-emp__filter{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;}
.alloc-emp__filter .filter-input{flex:1;min-width:90px;height:32px;}
.alloc-emp__filter .org-trigger{height:32px;min-width:150px;}
.alloc-emp__filter .org-panel{top:32px;}
.alloc-emp__list{border:1px solid var(--border);border-radius:var(--radius-sm);height:300px;overflow-y:auto;overflow-x:hidden;}
.ae-table{width:100%;min-width:0;border-collapse:separate;border-spacing:0;font-size:var(--font-sm);}
.ae-table th:last-child,.ae-table td:last-child{position:static;box-shadow:none;}
.ae-table th,.ae-table td{padding:8px 10px;text-align:left;vertical-align:middle;border-bottom:1px solid #F0F0F0;}
.ae-table th{background:#FAFAFA;font-weight:500;color:var(--text-secondary);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:1;}
.ae-table td{color:var(--text-primary);}
.ae-table th:first-child,.ae-table td:first-child{width:44px;text-align:center;padding-left:4px;padding-right:4px;}
.ae-table tbody tr:hover{background:#F8FBFF;}
.ae-table tbody tr:last-child td{border-bottom:none;}
.alloc-emp__item{display:flex;align-items:center;gap:8px;padding:7px 12px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid #F0F0F0;cursor:pointer;}
.alloc-emp__item:hover{background:#F8FBFF;}
.alloc-emp__item:last-child{border-bottom:none;}
.alloc-emp__item input[type=checkbox]{cursor:pointer;flex-shrink:0;}
.alloc-emp__item .ae-meta{flex:1;min-width:0;color:var(--text-secondary);font-size:var(--font-xs);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.alloc-emp__remove{color:var(--danger);cursor:pointer;margin-left:auto;font-size:var(--font-xs);flex-shrink:0;}
.alloc-emp__empty{text-align:center;padding:40px 0;color:var(--text-placeholder);font-size:var(--font-sm);}
.alloc-emp__alloc{background:#FAFAFA;border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px;margin-bottom:10px;display:flex;gap:12px;}
.alloc-emp__alloc .form-item{flex:1;margin-bottom:0;}
.ae-cnt{display:inline-block;min-width:18px;padding:0 5px;height:18px;line-height:18px;border-radius:9px;background:var(--primary-light);color:var(--primary);font-size:var(--font-xs);text-align:center;font-weight:600;}
</style>
</head>
<body>
@@ -171,14 +200,22 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 状态Tab:已分配/未分配 -->
<div class="tabs">
<div class="tab active" id="tabAlloc" onclick="switchTab(true)">已分配<span class="tab__count" id="cntAlloc">0</span></div>
<div class="tab" id="tabUnalloc" onclick="switchTab(false)">未分配<span class="tab__count" id="cntUnalloc">0</span></div>
<div class="tab active" id="tabAlloc" onclick="switchTab(true)">已分配</div>
<div class="tab" id="tabUnalloc" onclick="switchTab(false)">未分配</div>
</div>
<!-- [交互] 筛选栏:单位部门级联 + 姓名/员工编号 + 医疗点(仅已分配) -->
<!-- [交互] 筛选栏:姓名/员工编号/单位部门 + 医疗点/分配日期(仅已分配) -->
<div class="filter-bar">
<div class="filter-item">
<label>员工所属单位</label>
<label>姓名</label>
<input class="filter-input" id="filterName" placeholder="请输入姓名">
</div>
<div class="filter-item">
<label>员工编号</label>
<input class="filter-input" id="filterCode" placeholder="请输入员工编号" style="min-width:100px;">
</div>
<div class="filter-item">
<label>单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
@@ -197,14 +234,6 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
</div>
</div>
<div class="filter-item">
<label>员工姓名</label>
<input class="filter-input" id="filterName" placeholder="请输入姓名">
</div>
<div class="filter-item">
<label>员工编号</label>
<input class="filter-input" id="filterCode" placeholder="请输入员工编号" style="min-width:100px;">
</div>
<div class="filter-item" id="mfUnitWrap">
<label>医疗点所属单位</label>
<select class="filter-select" id="filterMedUnit" onchange="onMedUnit()" style="min-width:110px;">
@@ -218,6 +247,12 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<option value="">全部医疗点</option>
</select>
</div>
<div class="filter-item" id="mfDateWrap">
<label>分配日期</label>
<input class="filter-input" type="date" id="filterFrom" style="min-width:130px;">
<span style="color:var(--text-placeholder);"></span>
<input class="filter-input" type="date" id="filterTo" style="min-width:130px;">
</div>
<button class="btn btn-primary" onclick="applyFilter()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="6" cy="6" r="4.5" stroke="#fff" stroke-width="1.3"/><path d="M9.5 9.5L13 13" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/></svg>查询
</button>
@@ -227,13 +262,13 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 操作栏:按钮随Tab切换 -->
<div class="action-bar">
<span id="allocBtns">
<button class="btn btn-primary" onclick="openImport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>批量导入数据</button>
<button class="btn btn-default" onclick="openImportTasks()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导入任务</button>
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
</span>
<span id="unallocBtns" style="display:none;">
<button class="btn btn-primary" onclick="batchAllocate()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7h10M7 2v10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg>分配医疗点</button>
<button class="btn btn-primary" onclick="openImport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>批量导入数据</button>
<button class="btn btn-primary" onclick="openAllocEmp()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="4" r="2.5" stroke="#fff" stroke-width="1.3"/><path d="M2.5 12c.5-2.5 2.3-4 4.5-4s4 1.5 4.5 4" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/></svg>分配员工</button>
<button class="btn btn-primary" onclick="openImport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>批量分配导入</button>
<button class="btn btn-default" onclick="openImportTasks()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导入任务</button>
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
</span>
</div>
@@ -271,6 +306,71 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
</div>
<!-- [交互] 分配员工弹窗(左右双栏:选择员工 + 已选择员工) -->
<div class="modal-mask" id="allocEmpMask">
<div class="modal modal--xl">
<div class="modal__title">分配员工</div>
<div class="alloc-emp">
<div class="alloc-emp__left">
<div class="alloc-emp__panel-title">选择员工</div>
<div class="alloc-emp__filter">
<input class="filter-input" id="aeName" placeholder="姓名" oninput="renderAeList()">
<input class="filter-input" id="aeCode" placeholder="员工编号" oninput="renderAeList()">
<div class="org-wrap" id="aeOrgWrap">
<div class="org-trigger" id="aeOrgTrigger" onclick="toggleAeOrg()">全部单位/部门</div>
<div class="org-panel" id="aeOrgPanel">
<div class="org-col" id="aeUnitCol">
<div class="org-col__title">单位</div>
<div class="org-item active" data-unit="" onclick="aeSelectUnit(this,'全部单位/部门','')">全部单位/部门</div>
<div class="org-item" data-unit="采油一厂" onclick="aeSelectUnit(this,'采油一厂','采油一厂')">采油一厂</div>
<div class="org-item" data-unit="采油二厂" onclick="aeSelectUnit(this,'采油二厂','采油二厂')">采油二厂</div>
<div class="org-item" data-unit="第四采油厂" onclick="aeSelectUnit(this,'第四采油厂','第四采油厂')">第四采油厂</div>
<div class="org-item" data-unit="采气一厂" onclick="aeSelectUnit(this,'采气一厂','采气一厂')">采气一厂</div>
</div>
<div class="org-col" id="aeDeptCol">
<div class="org-col__title">部门</div>
<div class="org-item active" data-dept="" onclick="aeSelectDept(this,'全部部门','')">全部部门</div>
</div>
</div>
</div>
</div>
<div class="alloc-emp__list">
<table class="ae-table">
<thead><tr><th><input type="checkbox" id="aeCheckAll" onchange="aeToggleAll(this)"></th><th>姓名</th><th>员工编号</th><th>所属单位</th><th>所属部门</th></tr></thead>
<tbody id="aeTbody"></tbody>
</table>
</div>
</div>
<div class="alloc-emp__right">
<div class="alloc-emp__panel-title">已选择员工 <span class="ae-cnt" id="aeCnt">0</span></div>
<div class="alloc-emp__alloc">
<div class="form-item"><span class="form-label"><span class="req">*</span> 医疗点所属单位</span>
<select class="form-control" id="aeUnit" onchange="onAeUnit()"><option value="">请选择单位</option><option>采油一厂</option><option>采油二厂</option><option>第四采油厂</option><option>采气一厂</option></select></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 分配医疗点</span>
<select class="form-control" id="aePoint"><option value="">请先选择医疗点所属单位</option></select></div>
</div>
<div class="alloc-emp__list" id="aeSelected"></div>
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal('allocEmpMask')">取消</button>
<button class="btn btn-primary" onclick="confirmAllocEmp()">确认分配</button>
</div>
</div>
</div>
<!-- [交互] 移除分配确认弹窗 -->
<div class="modal-mask" id="removeMask">
<div class="modal">
<div class="modal__title"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#FAAD14" stroke-width="1.6"/><path d="M10 5.5v6M10 13.5v1" stroke="#FAAD14" stroke-width="1.8" stroke-linecap="round"/></svg> 移除分配</div>
<div class="modal__text" id="removeText">确认移除该员工的分配?</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal('removeMask')">取消</button>
<button class="btn btn-primary" onclick="confirmRemoveAllocate()">确认移除</button>
</div>
</div>
</div>
<!-- [交互] 批量导入弹窗 -->
<div class="modal-mask" id="importMask">
<div class="modal">
@@ -280,7 +380,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="form-item"><span class="form-label"><span class="req">*</span> 分配医疗点</span>
<select class="form-control" id="iPoint"><option value="">请先选择单位</option></select></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 上传文件(员工编号清单)</span>
<div class="drop-zone" onclick="showToast('演示环境请选择本地文件(仅支持.xlsx)')">点击选择或拖拽文件到此处上传<br>员工分配导入模板.xlsx</div></div>
<div class="drop-zone" onclick="showToast('演示环境请选择本地文件(仅支持.xlsx)')">点击选择或拖拽文件到此处上传<br>员工分配导入模板.xlsx</div>
<button class="btn btn-default" onclick="showToast('员工分配导入模板下载中…')" style="align-self:flex-start;margin-top:8px;"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>下载Excel模板</button></div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal('importMask')">取消</button>
<button class="btn btn-primary" onclick="doImport()">开始导入</button>
@@ -306,7 +407,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 演示数据(脱敏) ===== */
/* ===== 演示数据 ===== */
var pointMap={
'采油一厂':['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点'],
'采油二厂':['姬圪台作业区医疗点','桐园作业区医疗点'],
@@ -321,30 +422,30 @@ var deptMap={
};
/* 已分配:unit/dept=员工所属,mUnit/mPoint=分配去向 */
var alloc=[
{id:1,name:'石维国',sex:'男',age:46,code:'CQ021578',tel:'139****2051',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'樊学作业区医疗点',date:'2026-03-02'},
{id:2,name:'李桂芳',sex:'女',age:38,code:'CQ032144',tel:'138****7742',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'樊学作业区医疗点',date:'2026-03-02'},
{id:3,name:'白瑞',sex:'男',age:31,code:'CQ044871',tel:'155****3316',unit:'采油一厂',dept:'生产技术部',mUnit:'采油一厂',mPoint:'安塞采油队长住点医疗室',date:'2026-04-15'},
{id:4,name:'曲志明',sex:'男',age:42,code:'CQ019203',tel:'137****8890',unit:'采油二厂',dept:'姬圪台联合站',mUnit:'采油二厂',mPoint:'姬圪台作业区医疗点',date:'2026-01-08'},
{id:5,name:'张秀兰',sex:'女',age:45,code:'CQ027710',tel:'136****2248',unit:'采油二厂',dept:'姬圪台联合站',mUnit:'采油二厂',mPoint:'姬圪台作业区医疗点',date:'2026-01-08'},
{id:6,name:'赵万福',sex:'男',age:39,code:'CQ033096',tel:'150****6127',unit:'采油二厂',dept:'桐园作业区',mUnit:'采油二厂',mPoint:'桐园作业区医疗点',date:'2026-02-20'},
{id:7,name:'高建军',sex:'男',age:50,code:'CQ010564',tel:'139****0086',unit:'第四采油厂',dept:'基地管理处',mUnit:'第四采油厂',mPoint:'西峰基地医疗点',date:'2025-12-01'},
{id:8,name:'闫婷',sex:'女',age:34,code:'CQ041822',tel:'186****5527',unit:'第四采油厂',dept:'健康管理部',mUnit:'第四采油厂',mPoint:'西峰基地医疗点',date:'2026-01-12'},
{id:9,name:'刘玉梅',sex:'女',age:40,code:'CQ036441',tel:'135****7719',unit:'采气一厂',dept:'靖边净化厂',mUnit:'采气一厂',mPoint:'靖边气田医疗点',date:'2026-03-25'},
{id:10,name:'呼建国',sex:'男',age:36,code:'CQ029905',tel:'147****9003',unit:'采气一厂',dept:'靖边净化厂',mUnit:'采气一厂',mPoint:'靖边气田医疗点',date:'2026-03-25'},
{id:11,name:'徐立成',sex:'男',age:44,code:'CQ022317',tel:'133****4860',unit:'采气一厂',dept:'苏里格南作业区',mUnit:'采气一厂',mPoint:'乌审旗采气区医疗点',date:'2026-05-10'},
{id:12,name:'杨慧敏',sex:'女',age:33,code:'CQ045130',tel:'188****7214',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'姬塬作业区医疗点',date:'2026-06-18'}
{id:1,name:'石维国',sex:'男',age:46,code:'CQ021578',tel:'13928562051',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'樊学作业区医疗点',date:'2026-03-02'},
{id:2,name:'李桂芳',sex:'女',age:38,code:'CQ032144',tel:'13836547742',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'樊学作业区医疗点',date:'2026-03-02'},
{id:3,name:'白瑞',sex:'男',age:31,code:'CQ044871',tel:'15529403316',unit:'采油一厂',dept:'生产技术部',mUnit:'采油一厂',mPoint:'安塞采油队长住点医疗室',date:'2026-04-15'},
{id:4,name:'曲志明',sex:'男',age:42,code:'CQ019203',tel:'13720568890',unit:'采油二厂',dept:'姬圪台联合站',mUnit:'采油二厂',mPoint:'姬圪台作业区医疗点',date:'2026-01-08'},
{id:5,name:'张秀兰',sex:'女',age:45,code:'CQ027710',tel:'13649162248',unit:'采油二厂',dept:'姬圪台联合站',mUnit:'采油二厂',mPoint:'姬圪台作业区医疗点',date:'2026-01-08'},
{id:6,name:'赵万福',sex:'男',age:39,code:'CQ033096',tel:'15029836127',unit:'采油二厂',dept:'桐园作业区',mUnit:'采油二厂',mPoint:'桐园作业区医疗点',date:'2026-02-20'},
{id:7,name:'高建军',sex:'男',age:50,code:'CQ010564',tel:'13956200086',unit:'第四采油厂',dept:'基地管理处',mUnit:'第四采油厂',mPoint:'西峰基地医疗点',date:'2025-12-01'},
{id:8,name:'闫婷',sex:'女',age:34,code:'CQ041822',tel:'18629135527',unit:'第四采油厂',dept:'健康管理部',mUnit:'第四采油厂',mPoint:'西峰基地医疗点',date:'2026-01-12'},
{id:9,name:'刘玉梅',sex:'女',age:40,code:'CQ036441',tel:'13572867719',unit:'采气一厂',dept:'靖边净化厂',mUnit:'采气一厂',mPoint:'靖边气田医疗点',date:'2026-03-25'},
{id:10,name:'呼建国',sex:'男',age:36,code:'CQ029905',tel:'14729539003',unit:'采气一厂',dept:'靖边净化厂',mUnit:'采气一厂',mPoint:'靖边气田医疗点',date:'2026-03-25'},
{id:11,name:'徐立成',sex:'男',age:44,code:'CQ022317',tel:'13319284860',unit:'采气一厂',dept:'苏里格南作业区',mUnit:'采气一厂',mPoint:'乌审旗采气区医疗点',date:'2026-05-10'},
{id:12,name:'杨慧敏',sex:'女',age:33,code:'CQ045130',tel:'18829587214',unit:'采油一厂',dept:'樊学作业区',mUnit:'采油一厂',mPoint:'姬塬作业区医疗点',date:'2026-06-18'}
];
/* 未分配:可被分配的驻点后备医护与协管人员 */
var unalloc=[
{id:101,name:'周瑞琪',sex:'女',age:29,code:'CQ049218',tel:'151****3072',unit:'采油一厂',dept:'生产技术部'},
{id:102,name:'田震',sex:'男',age:35,code:'CQ037744',tel:'139****5218',unit:'采油一厂',dept:'樊学作业区'},
{id:103,name:'韩梅',sex:'女',age:41,code:'CQ026109',tel:'137****0833',unit:'采油二厂',dept:'安全环保科'},
{id:104,name:'冯保山',sex:'男',age:48,code:'CQ015523',tel:'135****6174',unit:'采油二厂',dept:'桐园作业区'},
{id:105,name:'曹雪芹',sex:'女',age:30,code:'CQ047831',tel:'186****2940',unit:'第四采油厂',dept:'健康管理部'},
{id:106,name:'罗建平',sex:'男',age:37,code:'CQ033485',tel:'150****8357',unit:'第四采油厂',dept:'基地管理处'},
{id:107,name:'梁小燕',sex:'女',age:32,code:'CQ042296',tel:'188****1029',unit:'采气一厂',dept:'生产技术部'},
{id:108,name:'邓成',sex:'男',age:43,code:'CQ024778',tel:'133****9481',unit:'采气一厂',dept:'苏里格南作业区'},
{id:109,name:'苏文斌',sex:'男',age:27,code:'CQ051027',tel:'155****6703',unit:'采油一厂',dept:'工会办公室'}
{id:101,name:'周瑞琪',sex:'女',age:29,code:'CQ049218',tel:'15148943072',unit:'采油一厂',dept:'生产技术部'},
{id:102,name:'田震',sex:'男',age:35,code:'CQ037744',tel:'13936485218',unit:'采油一厂',dept:'樊学作业区'},
{id:103,name:'韩梅',sex:'女',age:41,code:'CQ026109',tel:'13761800833',unit:'采油二厂',dept:'安全环保科'},
{id:104,name:'冯保山',sex:'男',age:48,code:'CQ015523',tel:'13581136174',unit:'采油二厂',dept:'桐园作业区'},
{id:105,name:'曹雪芹',sex:'女',age:30,code:'CQ047831',tel:'18642802940',unit:'第四采油厂',dept:'健康管理部'},
{id:106,name:'罗建平',sex:'男',age:37,code:'CQ033485',tel:'15077088357',unit:'第四采油厂',dept:'基地管理处'},
{id:107,name:'梁小燕',sex:'女',age:32,code:'CQ042296',tel:'18894041029',unit:'采气一厂',dept:'生产技术部'},
{id:108,name:'邓成',sex:'男',age:43,code:'CQ024778',tel:'13392699481',unit:'采气一厂',dept:'苏里格南作业区'},
{id:109,name:'苏文斌',sex:'男',age:27,code:'CQ051027',tel:'15598486703',unit:'采油一厂',dept:'工会办公室'}
];
var nextId=200,curAllocTab=true,allocTargets=[],allocRealloc=false;
@@ -355,6 +456,7 @@ function switchTab(toAlloc){
document.getElementById('tabUnalloc').classList.toggle('active',!toAlloc);
document.getElementById('mfUnitWrap').style.display=toAlloc?'':'none';
document.getElementById('mfPointWrap').style.display=toAlloc?'':'none';
document.getElementById('mfDateWrap').style.display=toAlloc?'':'none';
document.getElementById('allocBtns').style.display=toAlloc?'':'none';
document.getElementById('unallocBtns').style.display=toAlloc?'none':'';
currentPage=1;renderTable();
@@ -400,17 +502,23 @@ function currentData(){
var code=document.getElementById('filterCode').value.trim();
var mUnit=document.getElementById('filterMedUnit').value;
var mPoint=document.getElementById('filterMedPoint').value;
var from=document.getElementById('filterFrom').value;
var to=document.getElementById('filterTo').value;
var list=(curAllocTab?alloc:unalloc).filter(function(p){
return (!name||p.name.indexOf(name)!==-1)
&&(!code||p.code.toLowerCase().indexOf(code.toLowerCase())!==-1)
&&(!selUnit||p.unit===selUnit)&&(!selDept||p.dept===selDept)
&&(!curAllocTab||((!mUnit||p.mUnit===mUnit)&&(!mPoint||p.mPoint===mPoint)));
&&(!curAllocTab||((!mUnit||p.mUnit===mUnit)&&(!mPoint||p.mPoint===mPoint)&&(!from||p.date>=from)&&(!to||p.date<=to)));
});
return list;
}
function renderHead(){
var h='<tr><th style="width:40px"><input type="checkbox" onchange="toggleAll(this)"></th><th>序号</th><th>员工所属单位</th><th>员工所属部门</th><th>员工姓名</th><th>性别</th><th>年龄</th><th>员工编号</th><th>联系电话</th>';
h+=curAllocTab?'<th>医疗点所属单位</th><th>分配医疗点</th><th>分配日期</th><th>操作</th></tr>':'<th>操作</th></tr>';
var h;
if(curAllocTab){
h='<tr><th>序号</th><th>姓名</th><th>员工编号</th><th>性别</th><th>年龄</th><th>联系电话</th><th>所属单位</th><th>所属部门</th><th>医疗点所属单位</th><th>分配医疗点</th><th>分配日期</th><th>操作</th></tr>';
}else{
h='<tr><th>序号</th><th>姓名</th><th>员工编号</th><th>性别</th><th>年龄</th><th>联系电话</th><th>所属单位</th><th>所属部门</th><th>操作</th></tr>';
}
document.getElementById('tableHead').innerHTML=h;
}
function renderTable(){
@@ -422,26 +530,26 @@ function renderTable(){
document.getElementById('pageNum').textContent=currentPage+'/'+totalPages;
document.getElementById('prevBtn').disabled=currentPage<=1;
document.getElementById('nextBtn').disabled=currentPage>=totalPages;
document.getElementById('cntAlloc').textContent=alloc.length;
document.getElementById('cntUnalloc').textContent=unalloc.length;
var colspan=curAllocTab?13:10;
var colspan=curAllocTab?12:9;
if(!pageData.length){document.getElementById('tableBody').innerHTML='<tr><td colspan="'+colspan+'" class="td-empty">暂无匹配数据</td></tr>';return;}
var html='';
pageData.forEach(function(p,i){
html+='<tr>'
+'<td><input type="checkbox" class="row-check" value="'+p.id+'"></td>'
+'<td>'+(start+i+1)+'</td>'
+'<td>'+p.unit+'</td><td>'+p.dept+'</td>'
+'<td style="font-weight:600;">'+p.name+'</td>'
+'<td><span class="badge '+(p.sex==='男'?'badge--muted':'badge--muted')+'">'+p.sex+'</span></td>'
+'<td>'+p.age+'</td><td>'+p.code+'</td><td>'+p.tel+'</td>';
if(curAllocTab){
html+='<td>'+p.mUnit+'</td><td>'+p.mPoint+'</td><td>'+p.date+'</td>'
+'<td class="td-actions"><button class="btn btn-link" onclick="openAllocate(['+p.id+'],true)">重新分配</button></td>';
html+='<tr>'
+'<td>'+(start+i+1)+'</td>'
+'<td style="font-weight:600;">'+p.name+'</td>'
+'<td>'+p.code+'</td><td>'+p.sex+'</td><td>'+p.age+'</td><td>'+p.tel+'</td>'
+'<td>'+p.unit+'</td><td>'+p.dept+'</td>'
+'<td>'+p.mUnit+'</td><td>'+p.mPoint+'</td><td>'+p.date+'</td>'
+'<td class="td-actions"><button class="btn btn-link" onclick="openAllocate(['+p.id+'],true)">重新分配</button><button class="btn btn-link btn-link--danger" onclick="askRemoveAllocate('+p.id+')">移除分配</button></td></tr>';
}else{
html+='<td class="td-actions"><button class="btn btn-link" onclick="openAllocate(['+p.id+'])">分配医疗点</button></td>';
html+='<tr>'
+'<td>'+(start+i+1)+'</td>'
+'<td style="font-weight:600;">'+p.name+'</td>'
+'<td>'+p.code+'</td><td>'+p.sex+'</td><td>'+p.age+'</td><td>'+p.tel+'</td>'
+'<td>'+p.unit+'</td><td>'+p.dept+'</td>'
+'<td class="td-actions"><button class="btn btn-link" onclick="openAllocate(['+p.id+'])">分配医疗点</button></td></tr>';
}
html+='</tr>';
});
document.getElementById('tableBody').innerHTML=html;
}
@@ -455,6 +563,8 @@ function resetFilter(){
document.getElementById('deptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="selectDept(this,\'全部部门\',\'\')">全部部门</div>';
document.getElementById('filterMedUnit').value='';
onMedUnit();
document.getElementById('filterFrom').value='';
document.getElementById('filterTo').value='';
applyFilter();
}
function turnPage(d){
@@ -463,15 +573,7 @@ function turnPage(d){
currentPage=Math.min(Math.max(1,currentPage+d),totalPages);
renderTable();
}
function toggleAll(el){document.querySelectorAll('.row-check').forEach(function(c){c.checked=el.checked;});}
/* ===== 分配 ===== */
function checkedIds(){return [].slice.call(document.querySelectorAll('.row-check:checked')).map(function(c){return +c.value;});}
function batchAllocate(){
var ids=checkedIds();
if(!ids.length){showToast('请先勾选未分配员工');return;}
openAllocate(ids);
}
function openAllocate(ids,realloc){
allocTargets=ids;allocRealloc=!!realloc;
var src=realloc?alloc:unalloc;
@@ -509,6 +611,155 @@ function confirmAllocate(){
closeModal('allocMask');renderTable();
showToast('已为 '+allocTargets.length+' 人'+(allocRealloc?'重新':'')+'分配「'+point+'」');
}
/* [交互] 移除分配(二次确认):已分配员工退回未分配 */
var pendingRemove=null;
function askRemoveAllocate(id){
var p=alloc.find(function(x){return x.id===id;});
if(!p)return;
pendingRemove=id;
document.getElementById('removeText').textContent='确认移除「'+p.name+'」的分配?移除后该员工将回到未分配列表。';
document.getElementById('removeMask').classList.add('open');
}
function confirmRemoveAllocate(){
if(pendingRemove===null)return;
var id=pendingRemove;
pendingRemove=null;
closeModal('removeMask');
removeAllocate(id);
}
function removeAllocate(id){
var idx=alloc.findIndex(function(x){return x.id===id;});
if(idx<0)return;
var p=alloc.splice(idx,1)[0];
delete p.mUnit;delete p.mPoint;delete p.date;
unalloc.push(p);
renderTable();
showToast('已移除「'+p.name+'」的分配');
}
/* ===== 分配员工弹窗(左右双栏) ===== */
var aeSelUnit='',aeSelDept='',aeSelectedIds=[];
function openAllocEmp(){
aeSelectedIds=[];aeSelUnit='';aeSelDept='';
document.getElementById('aeName').value='';
document.getElementById('aeCode').value='';
document.getElementById('aeOrgTrigger').textContent='全部单位/部门';
document.getElementById('aeOrgPanel').classList.remove('open');
document.getElementById('aeOrgTrigger').classList.remove('open');
document.querySelectorAll('#aeUnitCol .org-item').forEach(function(i,x){i.classList.toggle('active',x===0);});
document.getElementById('aeDeptCol').innerHTML='<div class="org-col__title">部门</div><div class="org-item active" onclick="aeSelectDept(this,\'全部部门\',\'\')">全部部门</div>';
document.getElementById('aeUnit').value='';
document.getElementById('aePoint').innerHTML='<option value="">请先选择医疗点所属单位</option>';
document.getElementById('allocEmpMask').classList.add('open');
renderAeList();renderAeSelected();
}
function toggleAeOrg(){
document.getElementById('aeOrgPanel').classList.toggle('open');
document.getElementById('aeOrgTrigger').classList.toggle('open');
}
function aeSelectUnit(elem,unitName,unitCode){
document.querySelectorAll('#aeUnitCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
aeSelUnit=unitCode;
var html='<div class="org-col__title">部门</div><div class="org-item active" onclick="aeSelectDept(this,\'全部部门\',\'\')">全部部门</div>';
(deptMap[unitCode]||[]).forEach(function(d){
html+='<div class="org-item" data-dept="'+d+'" onclick="aeSelectDept(this,\''+d+'\',\''+d+'\')">'+d+'</div>';
});
document.getElementById('aeDeptCol').innerHTML=html;
aeSelDept='';renderAeList();
}
function aeSelectDept(elem,deptName,deptCode){
document.querySelectorAll('#aeDeptCol .org-item').forEach(function(i){i.classList.remove('active');});
elem.classList.add('active');
aeSelDept=deptCode;
var label=(aeSelUnit||'全部单位')+(aeSelDept?' / '+aeSelDept:' / 全部部门');
document.getElementById('aeOrgTrigger').textContent=label;
renderAeList();
}
function aeFiltered(){
var name=document.getElementById('aeName').value.trim();
var code=document.getElementById('aeCode').value.trim();
return unalloc.filter(function(p){
return (!name||p.name.indexOf(name)!==-1)
&&(!code||p.code.toLowerCase().indexOf(code.toLowerCase())!==-1)
&&(!aeSelUnit||p.unit===aeSelUnit)&&(!aeSelDept||p.dept===aeSelDept);
});
}
function renderAeList(){
var list=aeFiltered();
var checkAll=document.getElementById('aeCheckAll');
var html='';
if(!list.length){
checkAll.checked=false;
html='<tr><td colspan="5" style="text-align:center;padding:48px 0;color:var(--text-placeholder);">暂无匹配员工</td></tr>';
}else{
var allChecked=list.every(function(p){return aeSelectedIds.indexOf(p.id)>-1;});
checkAll.checked=allChecked;
list.forEach(function(p){
html+='<tr>'
+'<td><input type="checkbox" data-id="'+p.id+'" '+(aeSelectedIds.indexOf(p.id)>-1?'checked':'')+' onchange="aeToggle(this)"></td>'
+'<td style="font-weight:600;">'+p.name+'</td>'
+'<td>'+p.code+'</td>'
+'<td>'+p.unit+'</td><td>'+p.dept+'</td>'
+'</tr>';
});
}
document.getElementById('aeTbody').innerHTML=html;
}
function aeToggleAll(el){
aeFiltered().forEach(function(p){
var idx=aeSelectedIds.indexOf(p.id);
if(el.checked&&idx<0)aeSelectedIds.push(p.id);
else if(!el.checked&&idx>-1)aeSelectedIds.splice(idx,1);
});
renderAeList();renderAeSelected();
}
function aeToggle(el){
var id=+el.getAttribute('data-id');
var idx=aeSelectedIds.indexOf(id);
if(el.checked&&idx<0)aeSelectedIds.push(id);
else if(!el.checked&&idx>-1)aeSelectedIds.splice(idx,1);
renderAeSelected();renderAeList();
}
function aeRemove(id){
var idx=aeSelectedIds.indexOf(id);
if(idx>-1)aeSelectedIds.splice(idx,1);
renderAeSelected();renderAeList();
}
function renderAeSelected(){
var sel=aeSelectedIds.map(function(id){return unalloc.find(function(x){return x.id===id;});}).filter(Boolean);
document.getElementById('aeCnt').textContent=sel.length;
var html='';
if(!sel.length){html='<div class="alloc-emp__empty">尚未选择员工</div>';}
else{
sel.forEach(function(p){
html+='<div class="alloc-emp__item"><span style="font-weight:600;">'+p.name+'</span><span class="ae-meta">'+p.code+' · '+p.unit+' · '+p.dept+'</span><span class="alloc-emp__remove" onclick="aeRemove('+p.id+')">移除</span></div>';
});
}
document.getElementById('aeSelected').innerHTML=html;
}
function onAeUnit(){
var unit=document.getElementById('aeUnit').value;
var sel=document.getElementById('aePoint');
sel.innerHTML='<option value="">请选择医疗点</option>';
if(unit){(pointMap[unit]||[]).forEach(function(p){var o=document.createElement('option');o.textContent=p;sel.appendChild(o);});}
}
function confirmAllocEmp(){
if(!aeSelectedIds.length){showToast('请先选择要分配的员工');return;}
var unit=document.getElementById('aeUnit').value;
var point=document.getElementById('aePoint').value;
if(!unit){showToast('请选择医疗点所属单位');return;}
if(!point){showToast('请选择分配医疗点');return;}
var n=aeSelectedIds.length;
aeSelectedIds.forEach(function(id){
var idx=unalloc.findIndex(function(x){return x.id===id;});
if(idx<0)return;
var p=unalloc.splice(idx,1)[0];
p.mUnit=unit;p.mPoint=point;p.date='2026-09-07';
alloc.push(p);
});
closeModal('allocEmpMask');renderTable();
showToast('已为 '+n+' 名员工分配「'+point+'」');
}
/* ===== 批量导入 ===== */
function openImport(){
document.getElementById('iUnit').value='';
@@ -530,6 +781,9 @@ function doImport(){
/* [交互] 查看导入任务(异步任务演示) */
function openImportTasks(){document.getElementById('taskMask').classList.add('open');}
/* [交互] 导出列表数据 */
function doExport(){showToast(curAllocTab?'已分配员工列表已导出(演示)':'未分配员工列表已导出(演示)');}
/* ===== 通用 ===== */
function closeModal(id){document.getElementById(id).classList.remove('open');}
function showToast(msg){
@@ -376,7 +376,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 演示数据(脱敏) ===== */
/* ===== 演示数据 ===== */
var pointMap={
'采油一厂':['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点'],
'采油二厂':['姬圪台作业区医疗点','桐园作业区医疗点'],
@@ -384,12 +384,12 @@ var pointMap={
'采气一厂':['靖边气田医疗点','乌审旗采气区医疗点']
};
var ambulances=[
{id:1,unit:'采油一厂',point:'樊学作业区医疗点',type:'急救型',model:'江铃轻型监护型救护车',plate:'CQ·0712A',area:'樊学作业区及沿线井站',people:860,driver:'田震',tel:'139****5218',gps:'是',fee:8.5,org:'CQ油田总医院',year:2021,link:'是',equip:'除颤仪、心电监护仪、氧气袋、创伤固定包',ability:'可承担院内心跳呼吸骤停复苏与创伤伤员的第一时间转运'},
{id:2,unit:'采油二厂',point:'姬圪台作业区医疗点',type:'急救型',model:'福田图雅诺监护型救护车',plate:'CQ·1366B',area:'姬圪台联合站及管线区',people:1240,driver:'冯保山',tel:'135****6174',gps:'是',fee:9.2,org:'CQ油田总医院',year:2020,link:'是',equip:'除颤仪、便携式彩超、呼吸机',ability:'具备危重伤员途中生命支持与心梗卒中协同转运能力'},
{id:3,unit:'第四采油厂',point:'西峰基地医疗点',type:'转运型',model:'福特全顺转运型救护车',plate:'CQ·0208C',area:'西峰基地至第四采油厂机关沿线',people:2100,driver:'罗建平',tel:'150****8357',gps:'是',fee:12,org:'第四采油厂总医院',year:2019,link:'是',equip:'担架系统、输液泵、供氧系统',ability:'承担慢性病患者往返医院透析、复查等非急救转运'},
{id:4,unit:'采气一厂',point:'靖边气田医疗点',type:'急救型',model:'东风轻型救护车',plate:'CQ·1533D',area:'靖边净化厂及井场',people:760,driver:'邓成',tel:'133****9481',gps:'否',fee:7.8,org:'CQ油田总医院',year:2022,link:'否',equip:'除颤监护仪、氧气瓶、急救药箱',ability:'满足井场外伤、中暑、心源性猝倒的现场急救与送医'},
{id:5,unit:'采油一厂',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',plate:'CQ·0917E',area:'安塞基地及队部常驻区',people:540,driver:'苏文斌',tel:'155****6703',gps:'是',fee:6.5,org:'吴起油田医院',year:2023,link:'是',equip:'自动上车担架、颈胸腰固定支具',ability:'常驻点员工常规转运与陪同就医'},
{id:6,unit:'采气一厂',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',plate:'CQ·2201F',area:'苏里格南作业区营地',people:680,driver:'曹雪芹',tel:'186****2940',gps:'是',fee:8,org:'庆阳市人民医院',year:2024,link:'否',equip:'除颤仪、心电监护仪、简易呼吸器',ability:'高寒沙尘环境下的急救响应与危重员转运'}
{id:1,unit:'采油一厂',point:'樊学作业区医疗点',type:'急救型',model:'江铃轻型监护型救护车',plate:'CQ·0712A',area:'樊学作业区及沿线井站',people:860,driver:'田震',tel:'13936485218',gps:'是',fee:8.5,org:'CQ油田总医院',year:2021,link:'是',equip:'除颤仪、心电监护仪、氧气袋、创伤固定包',ability:'可承担院内心跳呼吸骤停复苏与创伤伤员的第一时间转运'},
{id:2,unit:'采油二厂',point:'姬圪台作业区医疗点',type:'急救型',model:'福田图雅诺监护型救护车',plate:'CQ·1366B',area:'姬圪台联合站及管线区',people:1240,driver:'冯保山',tel:'13581136174',gps:'是',fee:9.2,org:'CQ油田总医院',year:2020,link:'是',equip:'除颤仪、便携式彩超、呼吸机',ability:'具备危重伤员途中生命支持与心梗卒中协同转运能力'},
{id:3,unit:'第四采油厂',point:'西峰基地医疗点',type:'转运型',model:'福特全顺转运型救护车',plate:'CQ·0208C',area:'西峰基地至第四采油厂机关沿线',people:2100,driver:'罗建平',tel:'15077088357',gps:'是',fee:12,org:'第四采油厂总医院',year:2019,link:'是',equip:'担架系统、输液泵、供氧系统',ability:'承担慢性病患者往返医院透析、复查等非急救转运'},
{id:4,unit:'采气一厂',point:'靖边气田医疗点',type:'急救型',model:'东风轻型救护车',plate:'CQ·1533D',area:'靖边净化厂及井场',people:760,driver:'邓成',tel:'13392699481',gps:'否',fee:7.8,org:'CQ油田总医院',year:2022,link:'否',equip:'除颤监护仪、氧气瓶、急救药箱',ability:'满足井场外伤、中暑、心源性猝倒的现场急救与送医'},
{id:5,unit:'采油一厂',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',plate:'CQ·0917E',area:'安塞基地及队部常驻区',people:540,driver:'苏文斌',tel:'15598486703',gps:'是',fee:6.5,org:'吴起油田医院',year:2023,link:'是',equip:'自动上车担架、颈胸腰固定支具',ability:'常驻点员工常规转运与陪同就医'},
{id:6,unit:'采气一厂',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',plate:'CQ·2201F',area:'苏里格南作业区营地',people:680,driver:'曹雪芹',tel:'18642802940',gps:'是',fee:8,org:'庆阳市人民医院',year:2024,link:'否',equip:'除颤仪、心电监护仪、简易呼吸器',ability:'高寒沙尘环境下的急救响应与危重员转运'}
];
var nextId=7,editingId=null,pendingDel=[];
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 统计卡片 */
.stat-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px;}
@@ -161,15 +162,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -356,16 +359,16 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 基础数据(脱敏) ===== */
/* ===== 基础数据 ===== */
var points=['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点','姬圪台作业区医疗点','桐园作业区医疗点','西峰基地医疗点','靖边气田医疗点','乌审旗采气区医疗点'];
/* 救护车台账:与「基础配置·救护车配置」一致 */
var ambulances=[
{plate:'CQ·0712A',point:'樊学作业区医疗点',type:'急救型',model:'江铃轻型监护型救护车',driver:'田震',tel:'139****5218'},
{plate:'CQ·1366B',point:'姬圪台作业区医疗点',type:'急救型',model:'福田图雅诺监护型救护车',driver:'冯保山',tel:'135****6174'},
{plate:'CQ·0208C',point:'西峰基地医疗点',type:'转运型',model:'福特全顺转运型救护车',driver:'罗建平',tel:'150****8357'},
{plate:'CQ·1533D',point:'靖边气田医疗点',type:'急救型',model:'东风轻型救护车',driver:'邓成',tel:'133****9481'},
{plate:'CQ·0917E',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',driver:'苏文斌',tel:'155****6703'},
{plate:'CQ·2201F',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',driver:'曹雪芹',tel:'186****2940'}
{plate:'CQ·0712A',point:'樊学作业区医疗点',type:'急救型',model:'江铃轻型监护型救护车',driver:'田震',tel:'13936485218'},
{plate:'CQ·1366B',point:'姬圪台作业区医疗点',type:'急救型',model:'福田图雅诺监护型救护车',driver:'冯保山',tel:'13581136174'},
{plate:'CQ·0208C',point:'西峰基地医疗点',type:'转运型',model:'福特全顺转运型救护车',driver:'罗建平',tel:'15077088357'},
{plate:'CQ·1533D',point:'靖边气田医疗点',type:'急救型',model:'东风轻型救护车',driver:'邓成',tel:'13392699481'},
{plate:'CQ·0917E',point:'安塞采油队长住点医疗室',type:'转运型',model:'江淮瑞风转运型救护车',driver:'苏文斌',tel:'15598486703'},
{plate:'CQ·2201F',point:'乌审旗采气区医疗点',type:'急救型',model:'红旗轻型监护型救护车',driver:'曹雪芹',tel:'18642802940'}
];
/* 救护车司机列表:从救护车台账驾驶员去重提取 */
var drivers=[];
@@ -392,18 +395,18 @@ var medStaff=[
];
/* 救护车出勤记录:多救护车混合,覆盖四类出勤类型 */
var attendances=[
{id:1,date:'2026-09-08',point:'樊学作业区医疗点',plate:'CQ·0712A',type:'急救型',dispatch:'病情急救',driver:'田震',tel:'139****5218',staff:['韩东学','兰小娟'],out:'樊学作业区 7#井站',to:'CQ油田总医院'},
{id:2,date:'2026-09-08',point:'姬圪台作业区医疗点',plate:'CQ·1366B',type:'急救型',dispatch:'伤情急救',driver:'冯保山',tel:'135****6174',staff:['窦志远','苗慧敏'],out:'姬圪台联合站',to:'庆阳市人民医院'},
{id:3,date:'2026-09-07',point:'西峰基地医疗点',plate:'CQ·0208C',type:'转运型',dispatch:'日常巡诊',driver:'罗建平',tel:'150****8357',staff:['霍静怡'],out:'西峰基地机关',to:'西峰基地卫生室'},
{id:4,date:'2026-09-07',point:'靖边气田医疗点',plate:'CQ·1533D',type:'急救型',dispatch:'应急戒备',driver:'邓成',tel:'133****9481',staff:['康永强','花灵芝'],out:'靖边净化厂',to:'现场应急指挥部'},
{id:5,date:'2026-09-06',point:'安塞采油队长住点医疗室',plate:'CQ·0917E',type:'转运型',dispatch:'日常巡诊',driver:'苏文斌',tel:'155****6703',staff:['白宇峰'],out:'安塞基地队部',to:'吴起油田医院'},
{id:6,date:'2026-09-06',point:'乌审旗采气区医疗点',plate:'CQ·2201F',type:'急救型',dispatch:'伤情急救',driver:'曹雪芹',tel:'186****2940',staff:['荣大海','花灵芝'],out:'苏里格南作业区营地',to:'乌审旗人民医院'},
{id:7,date:'2026-09-05',point:'樊学作业区医疗点',plate:'CQ·0712A',type:'急救型',dispatch:'应急戒备',driver:'田震',tel:'139****5218',staff:['韩东学'],out:'樊学作业区应急演练现场',to:'演练指挥部'},
{id:8,date:'2026-09-05',point:'姬圪台作业区医疗点',plate:'CQ·1366B',type:'急救型',dispatch:'病情急救',driver:'冯保山',tel:'135****6174',staff:['窦志远','屈二娃'],out:'姬圪台作业区 3#站',to:'庆阳市人民医院'},
{id:9,date:'2026-09-04',point:'西峰基地医疗点',plate:'CQ·0208C',type:'转运型',dispatch:'病情急救',driver:'罗建平',tel:'150****8357',staff:['霍静怡'],out:'西峰基地生活区',to:'第四采油厂总医院'},
{id:10,date:'2026-09-04',point:'靖边气田医疗点',plate:'CQ·1533D',type:'急救型',dispatch:'日常巡诊',driver:'邓成',tel:'133****9481',staff:['康永强'],out:'靖边气田井场巡诊',to:'靖边气田医疗点'},
{id:11,date:'2026-09-03',point:'安塞采油队长住点医疗室',plate:'CQ·0917E',type:'转运型',dispatch:'日常巡诊',driver:'苏文斌',tel:'155****6703',staff:['白宇峰','牛玉洁'],out:'安塞基地各井站',to:'安塞采油队长住点医疗室'},
{id:12,date:'2026-09-03',point:'乌审旗采气区医疗点',plate:'CQ·2201F',type:'急救型',dispatch:'病情急救',driver:'曹雪芹',tel:'186****2940',staff:['荣大海'],out:'乌审旗采气区集气站',to:'乌审旗人民医院'}
{id:1,date:'2026-09-08',point:'樊学作业区医疗点',plate:'CQ·0712A',type:'急救型',dispatch:'病情急救',driver:'田震',tel:'13936485218',staff:['韩东学','兰小娟'],out:'樊学作业区 7#井站',to:'CQ油田总医院'},
{id:2,date:'2026-09-08',point:'姬圪台作业区医疗点',plate:'CQ·1366B',type:'急救型',dispatch:'伤情急救',driver:'冯保山',tel:'13581136174',staff:['窦志远','苗慧敏'],out:'姬圪台联合站',to:'庆阳市人民医院'},
{id:3,date:'2026-09-07',point:'西峰基地医疗点',plate:'CQ·0208C',type:'转运型',dispatch:'日常巡诊',driver:'罗建平',tel:'15077088357',staff:['霍静怡'],out:'西峰基地机关',to:'西峰基地卫生室'},
{id:4,date:'2026-09-07',point:'靖边气田医疗点',plate:'CQ·1533D',type:'急救型',dispatch:'应急戒备',driver:'邓成',tel:'13392699481',staff:['康永强','花灵芝'],out:'靖边净化厂',to:'现场应急指挥部'},
{id:5,date:'2026-09-06',point:'安塞采油队长住点医疗室',plate:'CQ·0917E',type:'转运型',dispatch:'日常巡诊',driver:'苏文斌',tel:'15598486703',staff:['白宇峰'],out:'安塞基地队部',to:'吴起油田医院'},
{id:6,date:'2026-09-06',point:'乌审旗采气区医疗点',plate:'CQ·2201F',type:'急救型',dispatch:'伤情急救',driver:'曹雪芹',tel:'18642802940',staff:['荣大海','花灵芝'],out:'苏里格南作业区营地',to:'乌审旗人民医院'},
{id:7,date:'2026-09-05',point:'樊学作业区医疗点',plate:'CQ·0712A',type:'急救型',dispatch:'应急戒备',driver:'田震',tel:'13936485218',staff:['韩东学'],out:'樊学作业区应急演练现场',to:'演练指挥部'},
{id:8,date:'2026-09-05',point:'姬圪台作业区医疗点',plate:'CQ·1366B',type:'急救型',dispatch:'病情急救',driver:'冯保山',tel:'13581136174',staff:['窦志远','屈二娃'],out:'姬圪台作业区 3#站',to:'庆阳市人民医院'},
{id:9,date:'2026-09-04',point:'西峰基地医疗点',plate:'CQ·0208C',type:'转运型',dispatch:'病情急救',driver:'罗建平',tel:'15077088357',staff:['霍静怡'],out:'西峰基地生活区',to:'第四采油厂总医院'},
{id:10,date:'2026-09-04',point:'靖边气田医疗点',plate:'CQ·1533D',type:'急救型',dispatch:'日常巡诊',driver:'邓成',tel:'13392699481',staff:['康永强'],out:'靖边气田井场巡诊',to:'靖边气田医疗点'},
{id:11,date:'2026-09-03',point:'安塞采油队长住点医疗室',plate:'CQ·0917E',type:'转运型',dispatch:'日常巡诊',driver:'苏文斌',tel:'15598486703',staff:['白宇峰','牛玉洁'],out:'安塞基地各井站',to:'安塞采油队长住点医疗室'},
{id:12,date:'2026-09-03',point:'乌审旗采气区医疗点',plate:'CQ·2201F',type:'急救型',dispatch:'病情急救',driver:'曹雪芹',tel:'18642802940',staff:['荣大海'],out:'乌审旗采气区集气站',to:'乌审旗人民医院'}
];
var nextId=13,editingId=null,pendingDelId=null;
var dispatchBadge={'病情急救':'badge--danger','伤情急救':'badge--warning','日常巡诊':'badge--info','应急戒备':'badge--success'};
@@ -678,7 +681,7 @@ function showToast(msg){
}
onFilterPoint();renderStats();renderTable();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.page-header{margin-bottom:16px;display:flex;align-items:center;gap:12px;}
.page-header__back{display:inline-flex;align-items:center;gap:4px;font-size:var(--font-sm);color:var(--primary);text-decoration:none;padding:4px 10px;border:1px solid var(--primary);border-radius:var(--radius-sm);background:#fff;}
@@ -132,15 +133,17 @@ tbody tr:hover{background:#F8FBFF;}
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item active">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -390,7 +393,7 @@ function saveMed(){
showToast('新增用药已保存(演示)');
}
renderHead();renderAll();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
+18 -15
View File
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -148,15 +149,17 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item active">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -570,7 +573,7 @@ function showToast(msg){
renderTable();
document.addEventListener('click',function(){document.getElementById('orgPanel').classList.remove('open');});
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
+179 -110
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康宣教管理 | 一线医疗</title>
<title>健康宣教 | 一线医疗</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;font-size:var(--font-sm);background:#F5F7FA;color:#333;min-width:1280px;}
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -119,6 +120,27 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
.modal{width:440px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);padding:24px;}
.modal__title{font-size:var(--font-base);font-weight:600;margin-bottom:12px;display:flex;align-items:center;gap:8px;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;margin-top:8px;}
/* 负责人签字 */
.sign-box{border:1px dashed #B7C3D6;border-radius:8px;background:#F7F9FC;height:110px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#969EB0;font-size:13px;cursor:pointer;}
.sign-box svg{width:22px;height:22px;stroke:#969EB0;fill:none;stroke-width:2;}
.radio{display:flex;gap:18px;align-items:center;font-size:13px;color:#333;}
.radio input{accent-color:#1890FF;width:14px;height:14px;}
.sign-note{display:flex;align-items:center;gap:6px;font-size:12px;color:#4A5A7F;margin-top:10px;}
.sign-view{display:flex;flex-direction:column;align-items:flex-start;gap:6px;}
.sign-view__name{font-family:KaiTi,serif;font-size:26px;color:#333;}
.sign-view__note{font-size:11px;color:#969EB0;}
.sign-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:970;align-items:center;justify-content:center;}
.sign-mask.open{display:flex;}
.sign-modal{background:#fff;border-radius:12px;width:720px;display:flex;flex-direction:column;box-shadow:0 12px 40px rgba(0,0,0,.25);}
.sign-modal__head{padding:15px 20px;border-bottom:1px solid #F0F2F5;display:flex;align-items:center;}
.sign-modal__title{font-size:15px;font-weight:600;color:#333;flex:1;}
.sign-modal__close{cursor:pointer;color:#999;font-size:18px;border:none;background:none;}
.sign-modal__foot{padding:13px 20px;border-top:1px solid #F0F2F5;display:flex;justify-content:flex-end;gap:10px;}
.canvas-wrap{position:relative;margin:20px;border:1px solid #DDD;background:#EEE;border-radius:6px;overflow:hidden;}
.canvas-wrap canvas{display:block;width:100%;height:260px;cursor:crosshair;}
.canvas-hint{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#BBB;font-size:14px;pointer-events:none;}
.eraser{position:absolute;right:12px;bottom:12px;width:40px;height:40px;border-radius:8px;border:1px solid #D9DEE8;background:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.eraser svg{width:20px;height:20px;stroke:#4A5A7F;fill:none;stroke-width:2;}
</style>
</head>
<body>
@@ -149,15 +171,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item active">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item active">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -166,8 +190,8 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label id="lblFilterTheme">宣教主</label>
<input class="filter-input" id="filterTheme" placeholder="请输入主题关键字">
<label id="lblFilterTheme">活动标</label>
<input class="filter-input" id="filterTheme" placeholder="请输入活动标题">
</div>
<div class="filter-item">
<label>医疗点所属单位</label>
@@ -183,7 +207,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</select>
</div>
<div class="filter-item">
<label>宣教日期</label>
<label>开展日期</label>
<input class="filter-input" type="date" id="filterFrom" style="min-width:130px;">
<span style="color:var(--text-placeholder);"></span>
<input class="filter-input" type="date" id="filterTo" style="min-width:130px;">
@@ -194,7 +218,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<button class="btn btn-default" onclick="resetFilter()">重置</button>
</div>
<!-- [交互] Tab切换:健康宣教/急救培训(老系统为同一数据模型,publicityType=1/0 区分,仅文案与列名切换) -->
<!-- [交互] Tab切换:健康宣教/急救培训(仅筛选类型,字段文案统一) -->
<div class="tabs-bar" style="margin-top:16px;">
<button class="tab-btn active" id="tabEdu" onclick="switchTab(1)">健康宣教 <span class="cnt" id="cntEdu"></span></button>
<button class="tab-btn" id="tabTr" onclick="switchTab(0)">急救培训 <span class="cnt" id="cntTr"></span></button>
@@ -203,11 +227,9 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary" id="btnAdd" onclick="openForm()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg><span id="addBtnText">新增宣教记录</span>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/></svg><span id="addBtnText">新增活动</span>
</button>
<button class="btn btn-default btn-link--danger" onclick="askBatchDelete()">批量删除</button>
<button class="btn btn-primary" onclick="doExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default" onclick="openExport()"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
<!-- 表格 -->
@@ -216,7 +238,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<table>
<thead>
<tr>
<th style="width:40px"><input type="checkbox" id="checkAll" onchange="toggleAll(this)"></th><th>序号</th><th id="thTheme">宣教主</th><th id="thDate">宣教日期</th><th>开展医疗点</th><th>所属单位</th><th>覆盖区域</th><th>参加人数</th><th>主讲人</th><th id="thContent">宣教内容</th><th>操作</th>
<th style="width:40px"><input type="checkbox" id="checkAll" onchange="toggleAll(this)"></th><th>序号</th><th>活动标</th><th>开展日期</th><th>开展医疗点</th><th>所属单位</th><th>覆盖区域</th><th>参加人数</th><th>活动内容</th><th>操作</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
@@ -235,18 +257,19 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 新增/编辑宣教抽屉 -->
<div class="drawer-mask" id="formMask" onclick="closeDrawer('form')"></div>
<div class="drawer" id="formDrawer">
<div class="drawer__header"><span id="formTitle">新增宣教记录</span><button class="drawer__close" onclick="closeDrawer('form')">×</button></div>
<div class="drawer__header"><span id="formTitle">新增活动</span><button class="drawer__close" onclick="closeDrawer('form')">×</button></div>
<div class="drawer__body">
<div class="form-section" style="margin-top:0;" id="formSecTitle">宣教活动信息</div>
<div class="form-section" style="margin-top:0;" id="formSecTitle">活动信息</div>
<div class="form-grid">
<div class="form-item form-item--full"><span class="form-label"><span class="req">*</span> <span id="lblTheme">宣教主题</span></span><input class="form-control" id="eTheme" placeholder="如:全国高血压日专题宣教"></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> <span id="lblDate">宣教日期</span></span><input class="form-control" type="date" id="eDate"></div>
<div class="form-item"><span class="form-label">主讲人</span><input class="form-control" id="eLecturer" placeholder="医护人员姓名"></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 活动类型</span>
<span class="radio"><label><input type="radio" name="eType" value="1" checked>健康宣教</label><label><input type="radio" name="eType" value="0">急救培训</label></span></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> <span id="lblDate">开展日期</span></span><input class="form-control" type="date" id="eDate"></div>
<div class="form-item form-item--full"><span class="form-label"><span class="req">*</span> <span id="lblTheme">活动标题</span></span><input class="form-control" id="eTheme" placeholder="如:全国高血压日专题宣教"></div>
<div class="form-item"><span class="form-label"><span class="req">*</span> 开展医疗点</span>
<select class="form-control" id="ePoint"><option value="">请选择医疗点</option></select></div>
<div class="form-item"><span class="form-label">覆盖区域</span><input class="form-control" id="eArea" placeholder="如:桐园作业区各井站"></div>
<div class="form-item"><span class="form-label">参加人数</span><input class="form-control" type="number" id="eNum" placeholder="现场签到人数"></div>
<div class="form-item form-item--full"><span class="form-label"><span class="req">*</span> <span id="lblContent">宣教内容</span></span><textarea class="form-control" id="eContent" placeholder="活动内容与要点摘要"></textarea></div>
<div class="form-item form-item--full"><span class="form-label"><span class="req">*</span> <span id="lblContent">活动内容</span></span><textarea class="form-control" id="eContent" placeholder="活动内容与要点摘要"></textarea></div>
</div>
<div class="form-section" id="secPhoto">现场照片</div>
<div class="photo-row">
@@ -254,21 +277,28 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="photo-ph"><svg width="18" height="18" viewBox="0 0 18 18" fill="none"><rect x="1" y="4" width="16" height="12" rx="2" stroke="#bbb" stroke-width="1.3"/><circle cx="9" cy="10" r="3.2" stroke="#bbb" stroke-width="1.3"/><path d="M6 4l1.2-2h3.6L12 4" stroke="#bbb" stroke-width="1.3"/></svg>照片 2</div>
<div class="photo-ph" onclick="showToast('照片上传功能演示')"><svg width="18" height="18" viewBox="0 0 18 18" fill="none"><path d="M9 4v10M4 9h10" stroke="#bbb" stroke-width="1.5" stroke-linecap="round"/></svg>继续添加</div>
</div>
<div class="form-section" id="secMaterial">宣教资料</div>
<div class="form-section" id="secMaterial">活动资料</div>
<div class="upload-box" id="materialBox" onclick="showToast('资料上传功能演示')">
<svg width="18" height="18" viewBox="0 0 20 20" fill="none"><path d="M10 13V4m0 0L6.5 7.5M10 4l3.5 3.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M3 14v2a2 2 0 002 2h10a2 2 0 002-2v-2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
<span id="materialText">点击上传宣教手册 / PPT / 折页等电子资料(支持 PDF、PPT,单个不超过 20MB)</span>
<span id="materialText">点击上传活动手册 / PPT / 折页等电子资料(支持 PDF、Word、Excel、PPT,单个不超过 20MB)</span>
</div>
<div class="form-section">员工确认签字</div>
<div class="upload-box">
<svg width="18" height="18" viewBox="0 0 20 20" fill="none"><path d="M3 17c2-4 4 3 6-2s3 4 8-1" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
参加人员在 PAD 端签到屏完成电子确认签字,签到记录自动回传关联本宣教活动
</div>
<div class="form-section">签字图片</div>
<div class="photo-row" id="signImgRow">
<div class="photo-ph" id="signImgPh" onclick="showToast('签字图片上传功能演示')">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none"><path d="M9 4v10M4 9h10" stroke="#bbb" stroke-width="1.5" stroke-linecap="round"/></svg>上传签字图片
<div class="form-section">负责人签字</div>
<div class="form-item" style="margin-bottom:0;">
<span class="form-label"><span class="req">*</span> 签字</span>
<div class="radio">
<label><input type="radio" name="signType" value="hand" checked onclick="switchSignType('hand')">手写签名</label>
<label><input type="radio" name="signType" value="img" onclick="switchSignType('img')">签字图片</label>
</div>
<div class="sign-box" id="signHandBox" onclick="openSign()">
<svg viewBox="0 0 24 24"><path d="M17 3a2.8 2.8 0 014 4L7.5 20.5 2 22l1.5-5.5z"/></svg>
点击签名
</div>
<div class="sign-box" id="signImgBox" style="display:none;" onclick="document.getElementById('signImgInput').click()">
<svg viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5"/><path d="M4 20h16"/></svg>
上传签字图片
</div>
<input type="file" id="signImgInput" accept="image/*" style="display:none;" onchange="onSignImg(this)">
<div class="sign-note" style="margin-top:0;">签字确认该活动记录真实有效</div>
</div>
</div>
<div class="drawer__footer">
@@ -280,7 +310,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<!-- [交互] 查看详情抽屉 -->
<div class="drawer-mask" id="viewMask" onclick="closeDrawer('view')"></div>
<div class="drawer" id="viewDrawer">
<div class="drawer__header"><span id="viewTitle">宣教详情</span><button class="drawer__close" onclick="closeDrawer('view')">×</button></div>
<div class="drawer__header"><span id="viewTitle">活动详情</span><button class="drawer__close" onclick="closeDrawer('view')">×</button></div>
<div class="drawer__body" id="viewBody"></div>
<div class="drawer__footer"><button class="btn btn-default" onclick="closeDrawer('view')">关闭</button></div>
</div>
@@ -289,7 +319,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="modal-mask" id="delMask">
<div class="modal">
<div class="modal__title"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#FAAD14" stroke-width="1.6"/><path d="M10 5.5v6M10 13.5v1" stroke="#FAAD14" stroke-width="1.8" stroke-linecap="round"/></svg> 删除确认</div>
<div id="delText">确认删除该条宣教记录?</div>
<div id="delText">确认删除该条活动记录?</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal('delMask')">取消</button>
<button class="btn btn-primary" onclick="confirmDelete()">确认删除</button>
@@ -310,6 +340,22 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="drawer__footer"><button class="btn btn-default" onclick="closeDrawer('export')">关闭</button></div>
</div>
<!-- [交互] 手写签名弹窗 -->
<div class="sign-mask" id="signMask">
<div class="sign-modal">
<div class="sign-modal__head"><span class="sign-modal__title">负责人签字</span><button class="sign-modal__close" onclick="closeSign()"></button></div>
<div class="canvas-wrap">
<canvas id="signCanvas" width="780" height="260"></canvas>
<div class="canvas-hint" id="signHint">请在区域内签名<div style="font-size:12px;">保证字迹清晰可辨</div></div>
<button class="eraser" onclick="clearCanvas()" title="清空"><svg viewBox="0 0 24 24"><path d="M20 20H8.5l-4-4a2 2 0 010-2.8l8.2-8.2a2 2 0 012.8 0l5 5a2 2 0 010 2.8L13 20M8 11l6 6"/></svg></button>
</div>
<div class="sign-modal__foot">
<button class="btn btn-default" onclick="closeSign()">取消</button>
<button class="btn btn-primary" onclick="confirmSign()">确认</button>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
@@ -324,43 +370,21 @@ var pointMap={
/* 全量医疗点(新增/编辑直接选择,不依赖单位联动) */
var allPoints=[];
Object.keys(pointMap).forEach(function(u){pointMap[u].forEach(function(p){allPoints.push(p);});});
/* 签字图片回显与占位(纯静态原型,用内联 SVG 模拟签字图片) */
var SIGN_IMG_THUMB='<svg width="34" height="20" viewBox="0 0 40 22" fill="none"><path d="M4 17c4-6 8 4 11-3s6 5 12-1 5 2 9-2" stroke="#1890FF" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg><span>签字图片</span>';
var SIGN_IMG_PLACEHOLDER='<svg width="18" height="18" viewBox="0 0 18 18" fill="none"><path d="M9 4v10M4 9h10" stroke="#bbb" stroke-width="1.5" stroke-linecap="round"/></svg>上传签字图片';
/* type:1=健康宣教 0=急救培训(与老系统 publicityType 口径一致) */
/* type:1=健康宣教 0=急救培训 */
var typeMap={1:'健康宣教',0:'急救培训'};
var edus=[
{id:1,type:1,theme:'全国高血压日:减盐降血压专题宣教',date:'2026-09-02',point:'桐园作业区医疗点',area:'桐园作业区各井站、联合站',num:86,lecturer:'曲志明',content:'讲解高血压危害与低盐饮食要点,现场免费测血压42人次,发放限盐勺与宣教折页。'},
{id:2,type:1,theme:'联合国糖尿病日:吃动平衡控血糖',date:'2026-08-24',point:'西峰基地医疗点',area:'基地管理处及后勤单元',num:64,lecturer:'闫婷',content:'糖尿病饮食换算实操演示,餐后运动处方宣讲,血糖义务检测31人次。'},
{id:3,type:0,theme:'现场急救技能培训(心肺复苏+AED)',date:'2026-08-15',point:'樊学作业区医疗点',area:'樊学作业区倒班点',num:52,lecturer:'李桂芳',content:'CPR操作要领与AED使用流程分组的演练,逐人考核通过47人,发放急救明白卡。'},
{id:4,type:0,theme:'夏季高温防暑与中暑急救培训',date:'2026-07-18',point:'乌审旗采气区医疗点',area:'苏里格南作业区井场',num:73,lecturer:'刘玉梅',content:'先兆中暑识别与处置流程实操,防暑药品配备讲解,现场发放藿香正气类物资。'},
{id:5,type:1,theme:'职业人群颈椎健康与工间操推广',date:'2026-07-05',point:'姬圪台作业区医疗点',area:'姬圪台联合站中控、化验室',num:45,lecturer:'张秀兰',content:'久坐危害讲解,颈椎工间操带练教学,建立各班组工间操提醒机制。'},
{id:6,type:1,theme:'低盐低脂饮食与血脂管理讲座',date:'2026-06-20',point:'姬圪台作业区医疗点',area:'姬圪台作业区食堂',num:58,lecturer:'曲志明',content:'血脂异常人群配餐原则,食堂减油减盐行动倡议,现场答疑并发放食谱卡。'},
{id:7,type:0,theme:'创伤救护四项技术(止血包扎固定搬运)培训',date:'2026-06-06',point:'姬圪台作业区医疗点',area:'姬圪台联合站各班组',num:48,lecturer:'张秀兰',content:'绷带三角巾包扎、夹板固定与脊柱板搬运分项实操,逐组过关考核通过44人。'},
{id:8,type:1,theme:'无烟单位健康宣教周活动',date:'2026-05-30',point:'安塞采油队长住点医疗室',area:'安塞长住点全区域',num:91,lecturer:'白瑞',content:'世界无烟日主题宣讲,戒烟门诊转介渠道说明,签名承诺活动参与76人。'},
{id:9,type:1,theme:'女性健康两癌筛查知识讲座',date:'2026-05-14',point:'靖边气田医疗点',area:'靖边净化厂女工集中单元',num:39,lecturer:'刘玉梅',content:'乳腺癌宫颈癌筛查指征与周期讲解,预约转诊通道介绍,现场咨询12人次。'},
{id:10,type:0,theme:'受限空间窒息事故急救演练',date:'2026-04-22',point:'靖边气田医疗点',area:'靖边净化厂检修现场',num:35,lecturer:'李桂芳',content:'受限空间窒息事故情景模拟,救援佩戴与心肺复苏衔接流程演练,复盘讲评。'}
{id:1,type:1,theme:'全国高血压日:减盐降血压专题宣教',date:'2026-09-02',point:'桐园作业区医疗点',area:'桐园作业区各井站、联合站',num:86,content:'讲解高血压危害与低盐饮食要点,现场免费测血压42人次,发放限盐勺与宣教折页。'},
{id:2,type:1,theme:'联合国糖尿病日:吃动平衡控血糖',date:'2026-08-24',point:'西峰基地医疗点',area:'基地管理处及后勤单元',num:64,content:'糖尿病饮食换算实操演示,餐后运动处方宣讲,血糖义务检测31人次。'},
{id:3,type:0,theme:'现场急救技能培训(心肺复苏+AED)',date:'2026-08-15',point:'樊学作业区医疗点',area:'樊学作业区倒班点',num:52,content:'CPR操作要领与AED使用流程分组的演练,逐人考核通过47人,发放急救明白卡。'},
{id:4,type:0,theme:'夏季高温防暑与中暑急救培训',date:'2026-07-18',point:'乌审旗采气区医疗点',area:'苏里格南作业区井场',num:73,content:'先兆中暑识别与处置流程实操,防暑药品配备讲解,现场发放藿香正气类物资。'},
{id:5,type:1,theme:'职业人群颈椎健康与工间操推广',date:'2026-07-05',point:'姬圪台作业区医疗点',area:'姬圪台联合站中控、化验室',num:45,content:'久坐危害讲解,颈椎工间操带练教学,建立各班组工间操提醒机制。'},
{id:6,type:1,theme:'低盐低脂饮食与血脂管理讲座',date:'2026-06-20',point:'姬圪台作业区医疗点',area:'姬圪台作业区食堂',num:58,content:'血脂异常人群配餐原则,食堂减油减盐行动倡议,现场答疑并发放食谱卡。'},
{id:7,type:0,theme:'创伤救护四项技术(止血包扎固定搬运)培训',date:'2026-06-06',point:'姬圪台作业区医疗点',area:'姬圪台联合站各班组',num:48,content:'绷带三角巾包扎、夹板固定与脊柱板搬运分项实操,逐组过关考核通过44人。'},
{id:8,type:1,theme:'无烟单位健康宣教周活动',date:'2026-05-30',point:'安塞采油队长住点医疗室',area:'安塞长住点全区域',num:91,content:'世界无烟日主题宣讲,戒烟门诊转介渠道说明,签名承诺活动参与76人。'},
{id:9,type:1,theme:'女性健康两癌筛查知识讲座',date:'2026-05-14',point:'靖边气田医疗点',area:'靖边净化厂女工集中单元',num:39,content:'乳腺癌宫颈癌筛查指征与周期讲解,预约转诊通道介绍,现场咨询12人次。'},
{id:10,type:0,theme:'受限空间窒息事故急救演练',date:'2026-04-22',point:'靖边气田医疗点',area:'靖边净化厂检修现场',num:35,content:'受限空间窒息事故情景模拟,救援佩戴与心肺复苏衔接流程演练,复盘讲评。'}
];
var nextId=11,editingId=null,pendingDelId=null,curTab=1;
/* 当前Tab文案映射:宣教↔培训 */
function T(a,b){return curTab===1?a:b;}
function switchTab(t){
curTab=t;currentPage=1;
document.getElementById('tabEdu').classList.toggle('active',t===1);
document.getElementById('tabTr').classList.toggle('active',t===0);
document.getElementById('thTheme').textContent=T('宣教主题','培训标题');
document.getElementById('thDate').textContent=T('宣教日期','培训日期');
document.getElementById('thContent').textContent=T('宣教内容','培训内容');
document.getElementById('addBtnText').textContent=T('新增宣教记录','新增急救培训');
document.getElementById('lblFilterTheme').textContent=T('宣教主题','培训标题');
document.getElementById('formSecTitle').textContent=T('宣教活动信息','培训活动信息');
document.getElementById('lblTheme').textContent=T('宣教主题','培训标题');
document.getElementById('lblDate').textContent=T('宣教日期','培训日期');
document.getElementById('lblContent').textContent=T('宣教内容','培训内容');
document.getElementById('secPhoto').textContent=T('现场照片','现场培训照片');
document.getElementById('secMaterial').textContent=T('宣教资料','培训资料');
document.getElementById('materialText').textContent=T('点击上传宣教手册 / PPT / 折页等电子资料(支持 PDF、PPT,单个不超过 20MB)','点击上传培训课件 / 操作手册 / 明白卡等电子资料(支持 PDF、PPT,单个不超过 20MB)');
renderTable();
}
/* ===== 异步导出与导出任务 ===== */
var exportLogs=[
@@ -392,7 +416,7 @@ function nowHM(){
return '2026-09-07 '+p(d.getHours())+':'+p(d.getMinutes());
}
function doExport(){
exportLogs.unshift({file:T('健康教育台账导出_','急救培训台账导出_')+nowHM()+'.xlsx',status:'ing',time:'—'});
exportLogs.unshift({file:'活动台账导出_'+nowHM()+'.xlsx',status:'ing',time:'—'});
showToast('导出任务已提交,完成后可在「查看导出任务」下载');
if(document.getElementById('exportDrawer').classList.contains('open'))renderExportList();
setTimeout(function(){
@@ -418,7 +442,7 @@ function askBatchDelete(){
var ids=[].slice.call(document.querySelectorAll('.row-check:checked')).map(function(c){return parseInt(c.value);});
if(!ids.length){showToast('请先勾选要删除的记录');return;}
pendingDelId=null;delMode='batch';
document.getElementById('delText').textContent='确认删除勾选的 '+ids.length+' 条'+T('宣教记录','急救培训')+'?删除后关联的签到记录一并解除关联。';
document.getElementById('delText').textContent='确认删除勾选的 '+ids.length+' 条活动记录?删除后关联的签到记录一并解除关联。';
document.getElementById('delMask').classList.add('open');
window._batchIds=ids;
}
@@ -444,6 +468,12 @@ function currentData(){
&&(!from||e.date>=from)&&(!to||e.date<=to);
});
}
function switchTab(t){
curTab=t;currentPage=1;
document.getElementById('tabEdu').classList.toggle('active',t===1);
document.getElementById('tabTr').classList.toggle('active',t===0);
renderTable();
}
function renderTabCounts(){
document.getElementById('cntEdu').textContent=edus.filter(function(e){return e.type===1;}).length;
document.getElementById('cntTr').textContent=edus.filter(function(e){return e.type===0;}).length;
@@ -458,7 +488,7 @@ function renderTable(){
document.getElementById('prevBtn').disabled=currentPage<=1;
document.getElementById('nextBtn').disabled=currentPage>=totalPages;
var tbody=document.getElementById('tableBody');
if(!pageData.length){tbody.innerHTML='<tr><td colspan="11" class="td-empty">暂无匹配数据</td></tr>';return;}
if(!pageData.length){tbody.innerHTML='<tr><td colspan="10" class="td-empty">暂无匹配数据</td></tr>';return;}
var html='';
pageData.forEach(function(e,i){
html+='<tr>'
@@ -467,7 +497,7 @@ function renderTable(){
+'<td style="font-weight:600;text-align:left;max-width:230px;overflow:hidden;text-overflow:ellipsis;" title="'+e.theme+'">'+e.theme+'</td>'
+'<td>'+e.date+'</td><td>'+e.point+'</td><td>'+pointUnit[e.point]+'</td>'
+'<td style="max-width:170px;overflow:hidden;text-overflow:ellipsis;" title="'+e.area+'">'+e.area+'</td>'
+'<td><span class="badge badge--info">'+e.num+' 人</span></td><td>'+e.lecturer+'</td>'
+'<td><span class="badge badge--info">'+e.num+' 人</span></td>'
+'<td style="text-align:left;max-width:220px;overflow:hidden;text-overflow:ellipsis;" title="'+e.content+'">'+e.content+'</td>'
+'<td class="td-actions">'
+'<button class="btn btn-link" onclick="openView('+e.id+')">详情</button>'
@@ -497,27 +527,19 @@ function initPointOptions(){
sel.innerHTML='<option value="">请选择医疗点</option>';
allPoints.forEach(function(p){var o=document.createElement('option');o.textContent=p;sel.appendChild(o);});
}
function renderSignImg(hasImg){
var row=document.getElementById('signImgRow');
if(hasImg){
row.innerHTML='<div class="photo-ph" style="border-color:#1890FF;background:#fff;cursor:pointer;" onclick="showToast(\'签字图片查看演示\')">'+SIGN_IMG_THUMB+'</div>';
}else{
row.innerHTML='<div class="photo-ph" id="signImgPh" onclick="showToast(\'签字图片上传功能演示\')">'+SIGN_IMG_PLACEHOLDER+'</div>';
}
}
function openForm(id){
editingId=id||null;
var e=id?edus.find(function(x){return x.id===id;}):null;
document.getElementById('formTitle').textContent=id?((curTab===1?'编辑宣教记录 · ':'编辑急救培训 · ')+e.theme):T('新增宣教记录','新增急救培训');
document.getElementById('formTitle').textContent=id?'编辑活动':'新增活动';
document.querySelector('input[name="eType"][value="'+(e?String(e.type):String(curTab))+'"]').checked=true;
document.getElementById('eTheme').value=e?e.theme:'';
document.getElementById('eDate').value=e?e.date:'2026-09-07';
document.getElementById('eLecturer').value=e?e.lecturer:'';
initPointOptions();
document.getElementById('ePoint').value=e?e.point:'';
document.getElementById('eArea').value=e?e.area:'';
document.getElementById('eNum').value=e?e.num:'';
document.getElementById('eContent').value=e?e.content:'';
renderSignImg(!!e);
resetSign();
document.getElementById('formMask').classList.add('open');
document.getElementById('formDrawer').classList.add('open');
}
@@ -526,39 +548,36 @@ function saveForm(){
var date=document.getElementById('eDate').value;
var point=document.getElementById('ePoint').value;
var content=document.getElementById('eContent').value.trim();
if(!theme){showToast('请填写'+T('宣教主题','培训标题'));return;}
if(!date){showToast('请选择'+T('宣教日期','培训日期'));return;}
if(!theme){showToast('请填写活动标题');return;}
if(!date){showToast('请选择开展日期');return;}
if(!point){showToast('请选择开展医疗点');return;}
if(!content){showToast('请填写'+T('宣教内容','培训内容'));return;}
if(!content){showToast('请填写活动内容');return;}
var obj={
id:editingId||nextId++,type:curTab,theme:theme,date:date,point:point,
id:editingId||nextId++,type:parseInt(document.querySelector('input[name="eType"]:checked').value,10),theme:theme,date:date,point:point,
area:document.getElementById('eArea').value||'—',
num:parseInt(document.getElementById('eNum').value)||0,
lecturer:document.getElementById('eLecturer').value||'—',
content:content
};
if(editingId){edus.splice(edus.findIndex(function(x){return x.id===editingId;}),1,obj);}
else{edus.unshift(obj);}
closeDrawer('form');renderTable();
showToast(editingId?T('宣教记录已更新','急救培训已更新'):T('宣教记录已保存,PAD 端签到数据将自动关联','急救培训已保存,PAD 端签到数据将自动关联'));
showToast(editingId?'活动记录已更新':'活动记录已保存,PAD 端签到数据将自动关联');
}
/* ===== 详情 ===== */
function openView(id){
var e=edus.find(function(x){return x.id===id;});
document.getElementById('viewTitle').textContent=T('宣教详情','急救培训详情');
document.getElementById('viewTitle').textContent='活动详情';
function d(k,v,full){return '<div class="detail-item'+(full?' detail-item--full':'')+'"><div class="dk">'+k+'</div><div class="dv">'+(v||'—')+'</div></div>';}
document.getElementById('viewBody').innerHTML=
'<div class="form-section" style="margin-top:0;">活动信息</div>'
+'<div class="detail-grid">'+d(T('宣教主题','培训标题'),e.theme,true)+d(T('宣教日期','培训日期'),e.date)+d('主讲人',e.lecturer)+d('开展医疗点',e.point)+d('所属单位',pointUnit[e.point])+d('覆盖区域',e.area,true)+d('参加人数',e.num+' 人(PAD 端签到回传)')+d(T('宣教内容','培训内容'),e.content,true)+'</div>'
+'<div class="form-section">'+T('现场照片','现场培训照片')+'</div>'
+'<div class="detail-grid">'+d('活动类型',typeMap[e.type],true)+d('活动标题',e.theme,true)+d('开展日期',e.date)+d('开展医疗点',e.point)+d('所属单位',pointUnit[e.point])+d('覆盖区域',e.area,true)+d('参加人数',e.num+' 人(PAD 端签到回传)')+d('活动内容',e.content,true)+'</div>'
+'<div class="form-section">现场照片</div>'
+'<div class="photo-row"><div class="photo-ph">照片 1</div><div class="photo-ph">照片 2</div></div>'
+'<div class="form-section">'+T('宣教资料','培训资料')+'</div>'
+'<div class="upload-box"><svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="M4 2h8l4 4v12H4z" stroke="#999" stroke-width="1.4" fill="none"/><path d="M12 2v4h4" stroke="#999" stroke-width="1.4"/></svg>《'+e.theme+'》'+T('宣教手册','培训资料')+'.pdf(演示附件)</div>'
+'<div class="form-section">员工确认签字</div>'
+'<div class="upload-box"><svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="M3 17c2-4 4 3 6-2s3 4 8-1" stroke="#999" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>参加人员在 PAD 端签到屏完成电子确认签字,签到记录自动回传关联本'+T('宣教活动','培训活动')+'</div>'
+'<div class="form-section">签字图片</div>'
+'<div class="photo-row"><div class="photo-ph" style="border-color:#1890FF;background:#fff;cursor:pointer;" onclick="showToast(\'签字图片查看演示\')">'+SIGN_IMG_THUMB+'</div></div>';
+'<div class="form-section">活动资料</div>'
+'<div class="upload-box"><svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="M4 2h8l4 4v12H4z" stroke="#999" stroke-width="1.4" fill="none"/><path d="M12 2v4h4" stroke="#999" stroke-width="1.4"/></svg>《'+e.theme+'》活动资料.pdf(演示附件)</div>'
+'<div class="form-section">负责人签字</div>'
+'<div class="sign-view"><span class="sign-view__name">负责人</span><span class="sign-view__note">已签名</span></div>';
document.getElementById('viewMask').classList.add('open');
document.getElementById('viewDrawer').classList.add('open');
}
@@ -567,19 +586,68 @@ function openView(id){
function askDelete(id){
pendingDelId=id;delMode='single';
var e=edus.find(function(x){return x.id===id;});
document.getElementById('delText').textContent='确认删除'+T('宣教记录','急救培训')+'「'+e.theme+'」?删除后关联的签到记录一并解除关联。';
document.getElementById('delText').textContent='确认删除活动记录「'+e.theme+'」?删除后关联的签到记录一并解除关联。';
document.getElementById('delMask').classList.add('open');
}
function confirmDelete(){
if(delMode==='batch'){
edus=edus.filter(function(e){return window._batchIds.indexOf(e.id)===-1;});
closeModal('delMask');renderTable();
showToast('已批量删除 '+window._batchIds.length+' 条'+T('宣教记录','急救培训'));
showToast('已批量删除 '+window._batchIds.length+' 条活动记录');
return;
}
edus=edus.filter(function(e){return e.id!==pendingDelId;});
closeModal('delMask');renderTable();
showToast(T('宣教记录已删除','急救培训已删除'));
showToast('活动记录已删除');
}
/* ===== 负责人签字(手写/图片,参考 medical-pad/health-education-drawer) ===== */
var signType='hand',signDone=false;
function switchSignType(type){
signType=type;
document.getElementById('signHandBox').style.display=type==='hand'?'flex':'none';
document.getElementById('signImgBox').style.display=type==='hand'?'none':'flex';
}
function resetSign(){
signType='hand';signDone=false;
document.querySelector('input[name="signType"][value="hand"]').checked=true;
document.getElementById('signHandBox').style.display='flex';
document.getElementById('signImgBox').style.display='none';
document.getElementById('signHandBox').innerHTML='<svg viewBox="0 0 24 24"><path d="M17 3a2.8 2.8 0 014 4L7.5 20.5 2 22l1.5-5.5z"/></svg>点击签名';
document.getElementById('signImgBox').innerHTML='<svg viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5"/><path d="M4 20h16"/></svg>上传签字图片';
}
function openSign(){clearCanvas();document.getElementById('signMask').classList.add('open');}
function closeSign(){document.getElementById('signMask').classList.remove('open');}
function onSignImg(input){
var f=input.files&&input.files[0];
if(!f)return;
var r=new FileReader();
r.onload=function(e){
document.getElementById('signImgBox').innerHTML='<img src="'+e.target.result+'" style="max-width:100%;max-height:80px;object-fit:contain;"><span style="font-size:11px;color:#969EB0;">已上传 · 点击可重新上传</span>';
signDone=true;
};
r.readAsDataURL(f);
}
var signCanvas=null,signCtx=null,drawing=false,hasInk=false;
function initSignCanvas(){
signCanvas=document.getElementById('signCanvas');
signCtx=signCanvas.getContext('2d');
signCtx.lineWidth=3;signCtx.lineCap='round';signCtx.strokeStyle='#333';
signCanvas.onmousedown=function(e){drawing=true;var p=canvasPos(e);signCtx.beginPath();signCtx.moveTo(p.x,p.y);};
signCanvas.onmousemove=function(e){if(!drawing)return;var p=canvasPos(e);signCtx.lineTo(p.x,p.y);signCtx.stroke();hasInk=true;document.getElementById('signHint').style.display='none';};
window.addEventListener('mouseup',function(){drawing=false;});
}
function canvasPos(e){
var r=signCanvas.getBoundingClientRect();
return {x:(e.clientX-r.left)*(signCanvas.width/r.width),y:(e.clientY-r.top)*(signCanvas.height/r.height)};
}
function clearCanvas(){signCtx.clearRect(0,0,signCanvas.width,signCanvas.height);hasInk=false;document.getElementById('signHint').style.display='flex';}
function confirmSign(){
if(!hasInk){showToast('您还没有进行签字');return;}
var name='负责人';
document.getElementById('signHandBox').innerHTML='<span style="font-size:26px;font-family:KaiTi,serif;color:#333;">'+name+'</span><span style="font-size:11px;color:#969EB0;">已签名 · 点击可重签</span>';
signDone=true;
closeSign();
}
/* ===== 通用 ===== */
@@ -594,7 +662,8 @@ function showToast(msg){
}
renderTable();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
initSignCanvas();
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.page-header{margin-bottom:16px;display:flex;align-items:center;gap:12px;}
.page-header__back{display:inline-flex;align-items:center;gap:4px;font-size:var(--font-sm);color:var(--primary);text-decoration:none;padding:4px 10px;border:1px solid var(--primary);border-radius:var(--radius-sm);background:#fff;}
@@ -196,15 +197,17 @@ tbody tr:hover{background:#F8FBFF;}
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item active">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -631,7 +634,7 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
/* ===== 初始化 ===== */
renderHead();
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -131,15 +132,17 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item active">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -416,7 +419,7 @@ function showToast(msg){
renderTable();
document.addEventListener('click',function(){document.getElementById('orgPanel').classList.remove('open');});
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
@@ -30,17 +30,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 16px;display:flex;align-items:center;gap:12px;flex-wrap:nowrap;margin-top:16px;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow-x:auto;}
.filter-bar::-webkit-scrollbar{height:6px;}
.filter-bar::-webkit-scrollbar-thumb{background:#E0E0E0;border-radius:3px;}
.filter-bar{background:#fff;padding:16px 16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:16px;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:6px;flex-shrink:0;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-select{height:36px;min-width:110px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;outline:none;appearance:none;-webkit-appearance:none;}
@@ -195,15 +194,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item active">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group open" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub active">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -454,7 +455,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 演示数据(脱敏):与员工分配名册/慢病台账/随访管理勾稽 ===== */
/* ===== 演示数据:与员工分配名册/慢病台账/随访管理勾稽 ===== */
var pointUnit={'樊学作业区医疗点':'采油一厂','姬圪台作业区医疗点':'采油二厂','西峰基地医疗点':'第四采油厂','靖边气田医疗点':'采气一厂','桐园作业区医疗点':'采油二厂','安塞采油队长住点医疗室':'采油一厂','乌审旗采气区医疗点':'采气一厂','姬塬作业区医疗点':'采油一厂'};
var pointMap={
'采油一厂':['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点'],
@@ -463,16 +464,16 @@ var pointMap={
'采气一厂':['靖边气田医疗点','乌审旗采气区医疗点']
};
var empPool=[
{code:'CQ021578',name:'石维国',sex:'男',age:46,tel:'139****2051',idcard:'6101**********0011',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',group:'高血压'},
{code:'CQ027710',name:'张秀兰',sex:'女',age:45,tel:'136****2248',idcard:'6101**********0022',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',group:'肥胖'},
{code:'CQ019203',name:'曲志明',sex:'男',age:42,tel:'137****8890',idcard:'6101**********0033',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',group:'冠心病'},
{code:'CQ033096',name:'赵万福',sex:'男',age:39,tel:'150****6127',idcard:'6101**********0044',unit:'采油二厂',dept:'桐园作业区',point:'桐园作业区医疗点',group:'高血压'},
{code:'CQ010564',name:'高建军',sex:'男',age:50,tel:'139****0086',idcard:'6101**********0055',unit:'第四采油厂',dept:'基地管理处',point:'西峰基地医疗点',group:'糖尿病'},
{code:'CQ036441',name:'刘玉梅',sex:'女',age:40,tel:'135****7719',idcard:'6101**********0066',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',group:'高血压'},
{code:'CQ029905',name:'呼建国',sex:'男',age:36,tel:'147****9003',idcard:'6101**********0077',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',group:'其他'},
{code:'CQ044871',name:'白瑞',sex:'男',age:31,tel:'155****3316',idcard:'6101**********0088',unit:'采油一厂',dept:'生产技术部',point:'安塞采油队长住点医疗室',group:'其他'},
{code:'CQ022317',name:'徐立成',sex:'男',age:44,tel:'133****4860',idcard:'6101**********0099',unit:'采气一厂',dept:'苏里格南作业区',point:'乌审旗采气区医疗点',group:'肥胖'},
{code:'CQ041822',name:'闫婷',sex:'女',age:34,tel:'186****5527',idcard:'6101**********0100',unit:'第四采油厂',dept:'健康管理部',point:'西峰基地医疗点',group:'其他'}
{code:'CQ021578',name:'石维国',sex:'男',age:46,tel:'13928562051',idcard:'610102197804154213',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',group:'高血压'},
{code:'CQ027710',name:'张秀兰',sex:'女',age:45,tel:'13649162248',idcard:'610102197912064629',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',group:'肥胖'},
{code:'CQ019203',name:'曲志明',sex:'男',age:42,tel:'13720568890',idcard:'610102198209285310',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',group:'冠心病'},
{code:'CQ033096',name:'赵万福',sex:'男',age:39,tel:'15029836127',idcard:'610102198511224737',unit:'采油二厂',dept:'桐园作业区',point:'桐园作业区医疗点',group:'高血压'},
{code:'CQ010564',name:'高建军',sex:'男',age:50,tel:'13956200086',idcard:'610102197406304419',unit:'第四采油厂',dept:'基地管理处',point:'西峰基地医疗点',group:'糖尿病'},
{code:'CQ036441',name:'刘玉梅',sex:'女',age:40,tel:'13572867719',idcard:'610102198408214523',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',group:'高血压'},
{code:'CQ029905',name:'呼建国',sex:'男',age:36,tel:'14729539003',idcard:'610102198805174635',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',group:'其他'},
{code:'CQ044871',name:'白瑞',sex:'男',age:31,tel:'15529403316',idcard:'610102199307104517',unit:'采油一厂',dept:'生产技术部',point:'安塞采油队长住点医疗室',group:'其他'},
{code:'CQ022317',name:'徐立成',sex:'男',age:44,tel:'13319284860',idcard:'610102198207153412',unit:'采气一厂',dept:'苏里格南作业区',point:'乌审旗采气区医疗点',group:'肥胖'},
{code:'CQ041822',name:'闫婷',sex:'女',age:34,tel:'18629135527',idcard:'610102199001184928',unit:'第四采油厂',dept:'健康管理部',point:'西峰基地医疗点',group:'其他'}
];
function E(code){return empPool.find(function(e){return e.code===code;});}
/* 单次巡诊任务:status 1待随访 2已逾期 3已完成 */
@@ -891,7 +892,7 @@ function showToast(msg){
}
switchTab('once');
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
+31 -28
View File
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:12px;flex-wrap:nowrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -189,15 +190,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item active">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -487,7 +490,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 演示数据(脱敏):随访对象为已分配员工 ===== */
/* ===== 演示数据:随访对象为已分配员工 ===== */
var pointUnit={'樊学作业区医疗点':'采油一厂','姬圪台作业区医疗点':'采油二厂','西峰基地医疗点':'第四采油厂','靖边气田医疗点':'采气一厂','桐园作业区医疗点':'采油二厂','安塞采油队长住点医疗室':'采油一厂','乌审旗采气区医疗点':'采气一厂'};
var pointMap={
'采油一厂':['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点'],
@@ -498,18 +501,18 @@ var pointMap={
var doctorPool=['石维国','李桂芳','曲志明','张秀兰','高建军','闫婷','刘玉梅'];
/* 可选随访员工池(与员工查询/员工分配一致的12人已分配名册) */
var empPool=[
{code:'CQ021578',name:'石维国',sex:'男',age:46,unit:'采油一厂',dept:'樊学作业区',tel:'139****2051',point:'樊学作业区医疗点',chronic:'血压偏高'},
{code:'CQ032144',name:'李桂芳',sex:'女',age:38,unit:'采油一厂',dept:'樊学作业区',tel:'138****7742',point:'樊学作业区医疗点',chronic:''},
{code:'CQ045130',name:'杨慧敏',sex:'女',age:33,unit:'采油一厂',dept:'樊学作业区',tel:'188****7214',point:'樊学作业区医疗点',chronic:''},
{code:'CQ019203',name:'曲志明',sex:'男',age:42,unit:'采油二厂',dept:'姬圪台联合站',tel:'137****8890',point:'姬圪台作业区医疗点',chronic:''},
{code:'CQ027710',name:'张秀兰',sex:'女',age:45,unit:'采油二厂',dept:'姬圪台联合站',tel:'136****2248',point:'姬圪台作业区医疗点',chronic:'超重/血脂偏高'},
{code:'CQ033096',name:'赵万福',sex:'男',age:39,unit:'采油二厂',dept:'桐园作业区',tel:'150****6127',point:'桐园作业区医疗点',chronic:'高血压2级'},
{code:'CQ010564',name:'高建军',sex:'男',age:50,unit:'第四采油厂',dept:'基地管理处',tel:'139****0086',point:'西峰基地医疗点',chronic:'2型糖尿病/超重'},
{code:'CQ041822',name:'闫婷',sex:'女',age:34,unit:'第四采油厂',dept:'健康管理部',tel:'186****5527',point:'西峰基地医疗点',chronic:''},
{code:'CQ036441',name:'刘玉梅',sex:'女',age:40,unit:'采气一厂',dept:'靖边净化厂',tel:'135****7719',point:'靖边气田医疗点',chronic:''},
{code:'CQ029905',name:'呼建国',sex:'男',age:36,unit:'采气一厂',dept:'靖边净化厂',tel:'147****9003',point:'靖边气田医疗点',chronic:''},
{code:'CQ044871',name:'白瑞',sex:'男',age:31,unit:'采油一厂',dept:'生产技术部',tel:'155****3316',point:'安塞采油队长住点医疗室',chronic:''},
{code:'CQ022317',name:'徐立成',sex:'男',age:44,unit:'采气一厂',dept:'苏里格南作业区',tel:'133****4860',point:'乌审旗采气区医疗点',chronic:'超重'}
{code:'CQ021578',name:'石维国',sex:'男',age:46,unit:'采油一厂',dept:'樊学作业区',tel:'13928562051',point:'樊学作业区医疗点',chronic:'血压偏高'},
{code:'CQ032144',name:'李桂芳',sex:'女',age:38,unit:'采油一厂',dept:'樊学作业区',tel:'13836547742',point:'樊学作业区医疗点',chronic:''},
{code:'CQ045130',name:'杨慧敏',sex:'女',age:33,unit:'采油一厂',dept:'樊学作业区',tel:'18829587214',point:'樊学作业区医疗点',chronic:''},
{code:'CQ019203',name:'曲志明',sex:'男',age:42,unit:'采油二厂',dept:'姬圪台联合站',tel:'13720568890',point:'姬圪台作业区医疗点',chronic:''},
{code:'CQ027710',name:'张秀兰',sex:'女',age:45,unit:'采油二厂',dept:'姬圪台联合站',tel:'13649162248',point:'姬圪台作业区医疗点',chronic:'超重/血脂偏高'},
{code:'CQ033096',name:'赵万福',sex:'男',age:39,unit:'采油二厂',dept:'桐园作业区',tel:'15029836127',point:'桐园作业区医疗点',chronic:'高血压2级'},
{code:'CQ010564',name:'高建军',sex:'男',age:50,unit:'第四采油厂',dept:'基地管理处',tel:'13956200086',point:'西峰基地医疗点',chronic:'2型糖尿病/超重'},
{code:'CQ041822',name:'闫婷',sex:'女',age:34,unit:'第四采油厂',dept:'健康管理部',tel:'18629135527',point:'西峰基地医疗点',chronic:''},
{code:'CQ036441',name:'刘玉梅',sex:'女',age:40,unit:'采气一厂',dept:'靖边净化厂',tel:'13572867719',point:'靖边气田医疗点',chronic:''},
{code:'CQ029905',name:'呼建国',sex:'男',age:36,unit:'采气一厂',dept:'靖边净化厂',tel:'14729539003',point:'靖边气田医疗点',chronic:''},
{code:'CQ044871',name:'白瑞',sex:'男',age:31,unit:'采油一厂',dept:'生产技术部',tel:'15529403316',point:'安塞采油队长住点医疗室',chronic:''},
{code:'CQ022317',name:'徐立成',sex:'男',age:44,unit:'采气一厂',dept:'苏里格南作业区',tel:'13319284860',point:'乌审旗采气区医疗点',chronic:'超重'}
];
/* 随访记录:s=症状 form=形式 confirm=员工确认 */
var follows=[
@@ -961,7 +964,7 @@ renderTable();
var q=new URLSearchParams(location.search);
if(q.get('action')==='new'){ setTimeout(function(){ openForm(); },100); }
})();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>报告解读管理 | 一线医疗</title>
<title>报告解读记录 | 一线医疗</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;font-size:var(--font-sm);background:#F5F7FA;color:#333;min-width:1280px;}
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -105,33 +106,27 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
.modal__close:hover{color:var(--text-primary);}
.modal__body{flex:1;overflow-y:auto;padding:20px 24px;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 24px;border-top:1px solid var(--border);flex-shrink:0;}
/* 详情弹窗左右两栏 */
/* 解读详情弹窗左右两栏(参考 daily-interpret) */
.report-cols{display:flex;gap:0;height:calc(90vh - 130px);}
.report-cols__left{width:50%;overflow-y:auto;padding-right:20px;border-right:1px dashed var(--border);}
.report-cols__right{width:50%;overflow-y:auto;padding-left:20px;}
.report-title{font-size:var(--font-base);font-weight:700;padding:10px 0 10px 12px;position:relative;margin-top:4px;}
.report-title::before{content:'';position:absolute;left:0;top:13px;width:6px;height:18px;background:#E3CB2C;border-radius:2px;}
.report-sub{font-size:var(--font-sm);color:var(--text-secondary);line-height:1.7;padding:0 12px 8px;}
.report-group{margin-top:16px;}
.report-group__name{font-weight:700;font-size:var(--font-base);padding:10px 12px;background:#F6F6F6;border-radius:var(--radius-sm) var(--radius-sm) 0 0;}
.report-cat{padding-left:20px;}
.report-cat__name{font-size:var(--font-sm);font-weight:700;padding:10px 0 8px 12px;position:relative;}
.report-cat__name::before{content:'';position:absolute;left:0;top:12px;width:6px;height:16px;background:#02C2A6;border-radius:2px;}
.report-ul{padding:0;margin:0;width:100%;display:flex;}
.report-ul li{flex:1;text-align:center;padding:6px 0;font-size:var(--font-sm);border:1px solid #E6E6E6;border-left:none;border-top:none;}
.report-ul li:first-child{border-left:1px solid #E6E6E6;}
.report-ul.report-ul--head{font-weight:700;}
.report-ul.report-ul--head li{background:#F0F2FF;}
.report-ul.report-ul--head li:first-child{background:#F0F2FF;}
.report-ul:not(.report-ul--head) li:first-child{background:#F6F6F6;}
.emp-basic{display:flex;flex-wrap:wrap;gap:4px 0;}
.emp-basic div{font-size:var(--font-sm);color:var(--text-secondary);padding:4px 0;}
.emp-basic b{color:var(--text-primary);font-weight:600;}
.emp-basic .w25{width:25%;}
.emp-basic .w50{width:50%;}
.emp-basic .w100{width:100%;}
.advise-box{background:#F4F4F4;border-radius:var(--radius-sm);padding:10px;font-size:var(--font-sm);line-height:1.7;color:var(--text-primary);min-height:88px;}
.sign-box{border:1px dashed var(--border);border-radius:var(--radius-sm);padding:16px;text-align:center;min-height:100px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;}
.report-cols__left{width:50%;overflow-y:auto;padding:16px 18px;border-right:1px dashed var(--border);}
.report-cols__right{width:50%;overflow-y:auto;padding:16px 18px;background:#FAFBFD;}
.rpt-title{font-size:15px;font-weight:600;color:#333;margin:14px 0 8px;padding-left:8px;border-left:4px solid #E3CB2C;}
.rpt-title--sub{border-left-color:#02C2A6;}
.rpt-text{font-size:12px;color:#4A5A7F;line-height:1.9;}
.rpt-major{background:#F6F6F6;border-radius:8px;padding:8px 12px;font-size:13px;font-weight:600;color:#333;margin:12px 0 8px;}
.rpt-table{width:100%;min-width:0;border-collapse:collapse;font-size:12px;margin-bottom:6px;}
.rpt-table th{background:#F0F2FF;color:#4A5A7F;font-weight:500;padding:7px 10px;text-align:left;white-space:nowrap;}
.rpt-table td{padding:7px 10px;border-bottom:1px solid #F0F2F5;color:#333;white-space:normal;word-break:break-word;}
.rpt-table td.abnormal{color:#F5222D;font-weight:600;}
.rpt-table thead th:last-child{position:static;right:auto;z-index:auto;background:#F0F2FF;box-shadow:none;}
.rpt-table tbody td:last-child{position:static;right:auto;z-index:auto;background:transparent;box-shadow:none;}
.form-card{background:#fff;border-radius:10px;padding:14px 16px;margin-bottom:12px;box-shadow:0 2px 8px rgba(74,90,127,.05);}
.form-card__title{font-size:14px;font-weight:600;color:#333;margin-bottom:10px;padding-left:8px;border-left:3px solid #1890FF;}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 18px;font-size:12px;color:#4A5A7F;line-height:2.1;}
.form-grid b{color:#333;font-weight:500;}
.sign-view{display:flex;flex-direction:column;align-items:flex-start;gap:6px;}
.sign-view__name{font-family:KaiTi,serif;font-size:26px;color:#333;}
.sign-view__note{font-size:11px;color:#969EB0;}
.group-badge{display:inline-block;padding:2px 12px;border-radius:10px;font-size:var(--font-sm);font-weight:600;}
</style>
</head>
@@ -163,15 +158,17 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item active">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item active">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -180,7 +177,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<!-- [交互] 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>员工姓名</label>
<label>姓名</label>
<input class="filter-input" id="filterName" placeholder="请输入姓名">
</div>
<div class="filter-item">
@@ -188,7 +185,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<input class="filter-input" id="filterCode" placeholder="请输入员工编号" style="min-width:110px;">
</div>
<div class="filter-item">
<label>员工所属单位</label>
<label>单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
@@ -254,7 +251,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<table>
<thead>
<tr>
<th>序号</th><th>解读员工</th><th>员工编号</th><th>性别</th><th>年龄</th><th>员工所属单位</th><th>员工所属部门</th><th>分配医疗点</th><th>医疗点所属单位</th><th>体检年份</th><th>解读医生</th><th>解读日期</th><th>操作</th>
<th>序号</th><th>姓名</th><th>员工编号</th><th>性别</th><th>年龄</th><th>所属单位</th><th>所属部门</th><th>分配医疗点</th><th>医疗点所属单位</th><th>体检年份</th><th>解读医生</th><th>解读日期</th><th>操作</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
@@ -270,41 +267,42 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
</div>
</main>
<!-- [交互] 解读详情弹窗(左右两栏) -->
<!-- [交互] 解读详情弹窗(只读,参考 daily-interpret) -->
<div class="modal-mask" id="detailMask">
<div class="modal--wide">
<div class="modal__header"><span id="detailTitle">解读详情</span><button class="modal__close" onclick="closeModal('detailMask')">×</button></div>
<div class="modal__body" style="padding:0;">
<div class="report-cols">
<div class="report-cols__left">
<div class="report-title">体检结论</div>
<div class="report-sub" id="dConclusion"></div>
<div class="report-title">体检建议</div>
<div class="report-sub" id="dSuggest"></div>
<div class="rpt-title">体检结论</div>
<div class="rpt-text" id="dConclusion"></div>
<div class="rpt-title">体检建议</div>
<div class="rpt-text" id="dSuggest"></div>
<div id="dReport"></div>
</div>
<div class="report-cols__right">
<div class="report-title">员工基本信息</div>
<div class="emp-basic" id="dEmp"></div>
<div class="report-title">综合解读建议</div>
<div class="advise-box" id="dAdvise"></div>
<div class="report-title">五类人群重新分布</div>
<div style="display:flex;padding:6px 0 10px;gap:16px;">
<span id="dGroupBefore" style="font-size:var(--font-sm);color:var(--text-secondary);"></span>
<span id="dGroupAfter" style="font-size:var(--font-sm);color:var(--text-secondary);"></span>
<div class="form-card">
<div class="form-card__title">员工基本信息</div>
<div class="form-grid" id="dEmp"></div>
</div>
<div class="report-title">员工确认签字</div>
<div id="dSign"></div>
<div style="display:flex;width:100%;margin-top:16px;">
<div style="width:50%;">
<div class="report-title" style="margin-top:0;">解读医生</div>
<div style="padding-left:12px;" id="dDoctor"></div>
<div class="form-card">
<div class="form-card__title">解读信息</div>
<div class="form-grid">
<div>解读医生:<b id="dDoctor"></b></div><div>解读日期:<b id="dDate"></b></div>
</div>
<div style="width:50%;">
<div class="report-title" style="margin-top:0;">解读日期</div>
<div style="padding-left:12px;" id="dDate"></div>
</div>
<div class="form-card">
<div class="form-card__title">解读意见</div>
<div style="font-size:13px;color:#333;line-height:1.9;" id="dAdvise"></div>
<div style="display:flex;gap:16px;margin-top:10px;">
<span id="dGroupBefore" style="font-size:12px;color:#4A5A7F;"></span>
<span id="dGroupAfter" style="font-size:12px;color:#4A5A7F;"></span>
</div>
</div>
<div class="form-card">
<div class="form-card__title">员工确认签字</div>
<div id="dSign"></div>
</div>
</div>
</div>
</div>
@@ -351,19 +349,30 @@ var records=[
{id:7,code:'CQ022317',name:'徐立成',sex:'男',age:44,unit:'采气一厂',dept:'苏里格南作业区',point:'乌审旗采气区医疗点',year:'2025',doctor:'刘玉梅',date:'2025-10-28',summary:'BMI 26.8;尿酸 438μmol/L;血压正常高值',advise:'体重与尿酸共管,清淡饮食,多饮水。',group:'d',beforeGroup:'d'}
];
/* 体检报告明细模板 */
/* 体检报告明细模板(参考 daily-interpret) */
function buildReport(r){
return {
conclusion: r.summary,
suggest: '结合既往史与生活方式,建议定期复查并保持健康管理。',
groups: [
{analysisName:'一般检查', cats:[
{cls:'体格检查', rows:[{name:'血压',result:'128/82',unit:'mmHg',ref:'<140/90'},{name:'心率',result:'74',unit:'/',ref:'60-100'}]},
{cls:'人体测量', rows:[{name:'身高',result:'172',unit:'cm',ref:''},{name:'体重',result:'78',unit:'kg',ref:''},{name:'BMI',result:'26.4',unit:'',ref:'18.5-23.9'},{name:'腰围',result:'88',unit:'cm',ref:'<85'}]}
conclusion: r.summary || '各项指标未见明显异常',
suggest: '低盐低脂饮食,规律监测血压并记录;3个月内复查血脂、血糖、糖化血红蛋白;每周中等强度运动不少于150分钟;如出现头晕、心悸等症状及时就诊。',
majors: [
{name:'实验室检查', cats:[
{cls:'生化全套', rows:[
{name:'空腹血糖',result:'7.8 ↑',unit:'mmol/L',ref:'3.9~6.1',abnormal:true},
{name:'血清总胆固醇',result:'6.12',unit:'mmol/L',ref:'0~5.2',abnormal:true},
{name:'血清甘油三酯',result:'2.35',unit:'mmol/L',ref:'0~1.7',abnormal:true},
{name:'糖化血红蛋白',result:'6.9',unit:'%',ref:'4~6.5',abnormal:true}
]},
{cls:'血常规', rows:[
{name:'白细胞计数',result:'6.8',unit:'10^9/L',ref:'3.5~9.5'},
{name:'血红蛋白',result:'145',unit:'g/L',ref:'130~175'},
{name:'血小板计数',result:'210',unit:'10^9/L',ref:'125~350'}
]}
]},
{analysisName:'实验室检查', cats:[
{cls:'血常规', rows:[{name:'白细胞',result:'6.1',unit:'×10⁹/L',ref:'3.5-9.5'},{name:'血红蛋白',result:'142',unit:'g/L',ref:'130-175'},{name:'血小板',result:'235',unit:'×10⁹/L',ref:'125-350'}]},
{cls:'生化检验', rows:[{name:'空腹血糖',result:'5.8',unit:'mmol/L',ref:'3.9-6.1'},{name:'总胆固醇',result:'5.6',unit:'mmol/L',ref:'<5.2'},{name:'甘油三酯',result:'1.7',unit:'mmol/L',ref:'<1.7'},{name:'尿酸',result:'398',unit:'μmol/L',ref:'208-428'}]}
{name:'辅助检查', cats:[
{cls:'心电图', rows:[
{name:'窦性心律',result:'未见异常',unit:'—',ref:'—'},
{name:'ST-T 改变',result:'轻度异常',unit:'—',ref:'—',abnormal:true}
]}
]}
]
};
@@ -526,26 +535,26 @@ function turnPage(d){
/* ===== 详情弹窗 ===== */
function renderReportHtml(report){
if(!report.groups||!report.groups.length)return '<div class="td-empty">暂无体检数据</div>';
if(!report.majors||!report.majors.length)return '<div class="td-empty">暂无体检数据</div>';
var html='';
report.groups.forEach(function(g){
html+='<div class="report-group"><div class="report-group__name">'+g.analysisName+'</div>';
g.cats.forEach(function(c){
html+='<div class="report-cat"><div class="report-cat__name">'+c.cls+'</div>';
html+='<ul class="report-ul report-ul--head"><li>名称</li><li>结果</li><li>单位</li><li>参考值</li></ul>';
report.majors.forEach(function(m){
html+='<div class="rpt-major">'+m.name+'</div>';
m.cats.forEach(function(c){
html+='<div class="rpt-title rpt-title--sub">'+c.cls+'</div>';
html+='<table class="rpt-table"><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr>';
c.rows.forEach(function(row){
html+='<ul class="report-ul"><li>'+row.name+'</li><li>'+row.result+'</li><li>'+row.unit+'</li><li>'+row.ref+'</li></ul>';
html+='<tr><td>'+row.name+'</td><td class="'+(row.abnormal?'abnormal':'')+'">'+row.result+'</td><td>'+row.unit+'</td><td>'+row.ref+'</td></tr>';
});
html+='</div>';
html+='</table>';
});
html+='</div>';
});
return html;
}
function empBasicHtml(r){
return '<div class="w25">姓名<b> '+r.name+'</b></div><div class="w25">性别<b> '+r.sex+'</b></div><div class="w25">年龄<b> '+r.age+'</b></div><div class="w25">员工编号<b> '+r.code+'</b></div>'
+'<div class="w50">所属单位<b> '+r.unit+'</b></div><div class="w50">所属部门<b> '+r.dept+'</b></div>'
+'<div class="w100">慢病类型<b> '+(CHRONIC[r.code]||'无')+'</b></div>';
return '<div>姓名<b>'+r.name+'</b></div><div>性别<b>'+r.sex+'</b></div>'
+'<div>年龄:<b>'+r.age+' 岁</b></div><div>员工编号:<b>'+r.code+'</b></div>'
+'<div style="grid-column:1/-1;">所属单位:<b>'+r.unit+'</b> 所属部门:<b>'+r.dept+'</b></div>'
+'<div style="grid-column:1/-1;">体检年份:<b>'+r.year+'年</b> 报告:'+r.year+'年体检报告</div>';
}
function groupBadgeHtml(v){
var color=GROUP_BADGE[v]||'#999';
@@ -562,7 +571,7 @@ function openDetail(id){
document.getElementById('dAdvise').textContent=r.advise||'—';
document.getElementById('dGroupBefore').innerHTML='解读前:'+groupBadgeHtml(r.beforeGroup);
document.getElementById('dGroupAfter').innerHTML='解读后:'+groupBadgeHtml(r.group);
document.getElementById('dSign').innerHTML='<div class="sign-box" style="min-height:80px;"><svg width="180" height="52" viewBox="0 0 180 52"><path d="M10 38c14-20 20 12 32-6 9-14 14 16 26-2 8-12 12 10 22-2 8-12 14 8 26 2" stroke="#14213D" stroke-width="2.4" fill="none" stroke-linecap="round"/></svg><div style="font-size:var(--font-xs);color:var(--text-placeholder);">'+r.name+' · '+r.date+' 于 PAD 端确认</div></div>';
document.getElementById('dSign').innerHTML='<div class="sign-view"><span class="sign-view__name">'+r.name+'</span></div>';
document.getElementById('dDoctor').textContent=r.doctor||'—';
document.getElementById('dDate').textContent=r.date||'—';
document.getElementById('detailMask').classList.add('open');
@@ -576,7 +585,7 @@ function showToast(msg){
}
renderTable();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
+247 -124
View File
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;padding:16px 24px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.filter-item{display:flex;align-items:center;gap:8px;}
@@ -125,34 +126,51 @@ textarea.form-control{height:88px;padding:8px 10px;resize:vertical;line-height:1
select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;-webkit-appearance:none;padding-right:28px;cursor:pointer;}
.section-title{display:flex;align-items:center;gap:8px;font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin:16px 0 10px;}
.section-title::before{content:'';width:4px;height:16px;background:var(--primary);border-radius:2px;}
/* 解读弹窗左右两栏 */
/* 解读弹窗左右两栏(报告与表单样式参考 medical-pad/interpret-drawer) */
.report-cols{display:flex;gap:0;height:calc(90vh - 130px);}
.report-cols__left{width:50%;overflow-y:auto;padding-right:20px;border-right:1px dashed var(--border);}
.report-cols__right{width:50%;overflow-y:auto;padding-left:20px;}
.report-title{font-size:var(--font-base);font-weight:700;padding:10px 0 10px 12px;position:relative;margin-top:4px;}
.report-title::before{content:'';position:absolute;left:0;top:13px;width:6px;height:18px;background:#E3CB2C;border-radius:2px;}
.report-sub{font-size:var(--font-sm);color:var(--text-secondary);line-height:1.7;padding:0 12px 8px;}
.report-group{margin-top:16px;}
.report-group__name{font-weight:700;font-size:var(--font-base);padding:10px 12px;background:#F6F6F6;border-radius:var(--radius-sm) var(--radius-sm) 0 0;}
.report-cat{padding-left:20px;}
.report-cat__name{font-size:var(--font-sm);font-weight:700;padding:10px 0 8px 12px;position:relative;}
.report-cat__name::before{content:'';position:absolute;left:0;top:12px;width:6px;height:16px;background:#02C2A6;border-radius:2px;}
.report-ul{padding:0;margin:0;width:100%;display:flex;}
.report-ul li{flex:1;text-align:center;padding:6px 0;font-size:var(--font-sm);border:1px solid #E6E6E6;border-left:none;border-top:none;}
.report-ul li:first-child{border-left:1px solid #E6E6E6;}
.report-ul.report-ul--head{font-weight:700;}
.report-ul.report-ul--head li{background:#F0F2FF;}
.report-ul.report-ul--head li:first-child{background:#F0F2FF;}
.report-ul:not(.report-ul--head) li:first-child{background:#F6F6F6;}
.emp-basic{display:flex;flex-wrap:wrap;gap:4px 0;}
.emp-basic div{font-size:var(--font-sm);color:var(--text-secondary);padding:4px 0;}
.emp-basic b{color:var(--text-primary);font-weight:600;}
.emp-basic .w25{width:25%;}
.emp-basic .w50{width:50%;}
.emp-basic .w100{width:100%;}
.advise-box{background:#F4F4F4;border-radius:var(--radius-sm);padding:10px;font-size:var(--font-sm);line-height:1.7;color:var(--text-primary);min-height:88px;}
.sign-box{border:1px dashed var(--border);border-radius:var(--radius-sm);padding:16px;text-align:center;min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;}
.report-cols__left{width:50%;overflow-y:auto;padding:16px 18px;border-right:1px dashed var(--border);}
.report-cols__right{width:50%;overflow-y:auto;padding:16px 18px;background:#FAFBFD;}
.rpt-title{font-size:15px;font-weight:600;color:#333;margin:14px 0 8px;padding-left:8px;border-left:4px solid #E3CB2C;}
.rpt-title--sub{border-left-color:#02C2A6;}
.rpt-text{font-size:12px;color:#4A5A7F;line-height:1.9;}
.rpt-major{background:#F6F6F6;border-radius:8px;padding:8px 12px;font-size:13px;font-weight:600;color:#333;margin:12px 0 8px;}
.rpt-table{width:100%;min-width:0;border-collapse:collapse;font-size:12px;margin-bottom:6px;}
.rpt-table th{background:#F0F2FF;color:#4A5A7F;font-weight:500;padding:7px 10px;text-align:left;white-space:nowrap;}
.rpt-table td{padding:7px 10px;border-bottom:1px solid #F0F2F5;color:#333;white-space:normal;word-break:break-word;}
.rpt-table td.abnormal{color:#F5222D;font-weight:600;}
.rpt-table thead th:last-child{position:static;right:auto;z-index:auto;background:#F0F2FF;box-shadow:none;}
.rpt-table tbody td:last-child{position:static;right:auto;z-index:auto;background:transparent;box-shadow:none;}
.form-card{background:#fff;border-radius:10px;padding:14px 16px;margin-bottom:12px;box-shadow:0 2px 8px rgba(74,90,127,.05);}
.form-card__title{font-size:14px;font-weight:600;color:#333;margin-bottom:10px;padding-left:8px;border-left:3px solid #1890FF;}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 18px;font-size:12px;color:#4A5A7F;line-height:2.1;}
.form-grid b{color:#333;font-weight:500;}
.fld{display:flex;align-items:flex-start;gap:8px;font-size:13px;color:#4A5A7F;margin-bottom:12px;}
.fld__label{width:96px;text-align:right;flex-shrink:0;padding-top:6px;}
.fld__req{color:#FD2121;}
.fld textarea,.fld select{flex:1;border:1px solid #D9DEE8;border-radius:6px;padding:7px 10px;font-size:13px;color:#333;outline:none;font-family:inherit;min-width:0;}
.fld textarea{resize:none;height:76px;}
.fld textarea:focus,.fld select:focus{border-color:#1890FF;}
.radio{display:flex;gap:16px;align-items:center;font-size:13px;color:#333;}
.radio input{accent-color:#1890FF;width:14px;height:14px;}
.sign-box{border:1px dashed #B7C3D6;border-radius:8px;background:#F7F9FC;height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#969EB0;font-size:13px;cursor:pointer;}
.sign-box svg{width:22px;height:22px;stroke:#969EB0;fill:none;stroke-width:2;}
.sign-view{display:flex;flex-direction:column;align-items:flex-start;gap:6px;}
.sign-view__name{font-family:KaiTi,serif;font-size:26px;color:#333;}
.sign-view__note{font-size:11px;color:#969EB0;}
.group-badge{display:inline-block;padding:2px 12px;border-radius:10px;font-size:var(--font-sm);font-weight:600;}
/* 手写签名弹窗 */
.sign-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:970;align-items:center;justify-content:center;}
.sign-mask.open{display:flex;}
.sign-modal{background:#fff;border-radius:12px;width:720px;display:flex;flex-direction:column;box-shadow:0 12px 40px rgba(0,0,0,.25);}
.sign-modal__head{padding:15px 20px;border-bottom:1px solid #F0F2F5;display:flex;align-items:center;}
.sign-modal__title{font-size:15px;font-weight:600;color:#333;flex:1;}
.sign-modal__close{cursor:pointer;color:#999;font-size:18px;border:none;background:none;}
.sign-modal__foot{padding:13px 20px;border-top:1px solid #F0F2F5;display:flex;justify-content:flex-end;gap:10px;}
.canvas-wrap{position:relative;margin:20px;border:1px solid #DDD;background:#EEE;border-radius:6px;overflow:hidden;}
.canvas-wrap canvas{display:block;width:100%;height:260px;cursor:crosshair;}
.canvas-hint{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;color:#BBB;font-size:14px;pointer-events:none;}
.eraser{position:absolute;right:12px;bottom:12px;width:40px;height:40px;border-radius:8px;border:1px solid #D9DEE8;background:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.eraser svg{width:20px;height:20px;stroke:#4A5A7F;fill:none;stroke-width:2;}
</style>
</head>
<body>
@@ -183,15 +201,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item active">解读任务</a>
<div class="sidebar-group open" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub active">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -204,7 +224,11 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<input class="filter-input" id="filterName" placeholder="请输入姓名">
</div>
<div class="filter-item">
<label>员工所属单位</label>
<label>员工编号</label>
<input class="filter-input" id="filterCode" placeholder="请输入员工编号">
</div>
<div class="filter-item">
<label>单位部门</label>
<div class="org-wrap" id="orgWrap">
<div class="org-trigger" id="orgTrigger" onclick="toggleOrg()">全部单位/部门</div>
<div class="org-panel" id="orgPanel">
@@ -263,8 +287,6 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<button class="btn btn-default" onclick="openExport()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出记录
</button>
<button class="btn btn-primary" onclick="openGenRecord()">生成记录</button>
<button class="btn btn-primary" id="btnResetTask" onclick="resetTask()">重新生成解读任务</button>
</div>
<div class="seg">
<button class="active" data-v="0" onclick="setStatusGroup(this,'0')">全部</button>
@@ -296,30 +318,50 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
</main>
<!-- [交互] 体检报告解读弹窗(可编辑) -->
<!-- [交互] 体检报告解读弹窗(可编辑,内容参考 medical-pad/interpret-drawer) -->
<div class="modal-mask" id="intMask">
<div class="modal--wide">
<div class="modal__header"><span id="intTitle">体检报告解读</span><button class="modal__close" onclick="closeModal('intMask')">×</button></div>
<div class="modal__body" style="padding:0;">
<div class="report-cols">
<div class="report-cols__left">
<div class="report-title">体检结论</div>
<div class="report-sub" id="intConclusion"></div>
<div class="report-title">体检建议</div>
<div class="report-sub" id="intSuggest"></div>
<div class="rpt-title">体检结论</div>
<div class="rpt-text" id="intConclusion"></div>
<div class="rpt-title">体检建议</div>
<div class="rpt-text" id="intSuggest"></div>
<div id="intReport"></div>
</div>
<div class="report-cols__right">
<div class="report-title">员工基本信息</div>
<div class="emp-basic" id="intEmp"></div>
<div class="report-title">综合解读建议</div>
<textarea class="form-control" id="intAdvise" placeholder="结合体检指标、既往史与生活方式,给出个体化综合解读与建议"></textarea>
<div class="report-title">五类人群重新分类</div>
<select class="form-control" id="intGroup" style="max-width:280px;"></select>
<div class="report-title">员工确认签字</div>
<div class="sign-box" id="signBox">
<div id="signHolder" style="color:var(--text-placeholder);">本人已确认健康随访</div>
<button class="btn btn-default" id="signBtn" onclick="doSign()">员工签名</button>
<div class="form-card">
<div class="form-card__title">员工基本信息</div>
<div class="form-grid" id="intEmp"></div>
</div>
<div class="form-card">
<div class="form-card__title">解读意见</div>
<div class="fld"><span class="fld__label"><span class="fld__req">*</span>综合解读建议</span><textarea id="intAdvise" placeholder="请输入综合解读建议"></textarea></div>
<div class="fld"><span class="fld__label">五类人群分类</span><select id="intGroup"></select></div>
</div>
<div class="form-card">
<div class="form-card__title">员工确认签字</div>
<div style="font-size:12px;color:#969EB0;margin-bottom:8px;">本人已确认健康随访</div>
<div class="fld"><span class="fld__label"><span class="fld__req">*</span>签字</span>
<div id="signField" style="flex:1;">
<div class="radio" style="margin-bottom:10px;">
<label><input type="radio" name="signType" value="hand" checked onclick="switchSignType('hand')">手写签名</label>
<label><input type="radio" name="signType" value="img" onclick="switchSignType('img')">签字图片</label>
</div>
<div class="sign-box" id="signHandBox" onclick="openSignModal()">
<svg viewBox="0 0 24 24"><path d="M17 3a2.8 2.8 0 014 4L7.5 20.5 2 22l1.5-5.5z"/></svg>
点击签名
</div>
<div class="sign-box" id="signImgBox" style="display:none;" onclick="document.getElementById('signImgInput').click()">
<svg viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5"/><path d="M4 20h16"/></svg>
上传签字图片
</div>
<input type="file" id="signImgInput" accept="image/*" style="display:none;" onchange="onSignImg(this)">
</div>
<div id="signLockView" style="flex:1;display:none;"></div>
</div>
</div>
</div>
</div>
@@ -331,6 +373,22 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
</div>
</div>
<!-- [交互] 手写签名弹窗 -->
<div class="sign-mask" id="signMask">
<div class="sign-modal">
<div class="sign-modal__head"><span class="sign-modal__title">签名</span><button class="sign-modal__close" onclick="closeSignModal()"></button></div>
<div class="canvas-wrap">
<canvas id="signCanvas" width="780" height="260"></canvas>
<div class="canvas-hint" id="signHint">请在区域内签名<div style="font-size:12px;">保证字迹清晰可辨</div></div>
<button class="eraser" onclick="clearCanvas()" title="清空"><svg viewBox="0 0 24 24"><path d="M20 20H8.5l-4-4a2 2 0 010-2.8l8.2-8.2a2 2 0 012.8 0l5 5a2 2 0 010 2.8L13 20M8 11l6 6"/></svg></button>
</div>
<div class="sign-modal__foot">
<button class="btn btn-default" onclick="closeSignModal()">取消</button>
<button class="btn btn-primary" onclick="confirmSign()">确认</button>
</div>
</div>
</div>
<!-- [交互] 解读详情弹窗(只读) -->
<div class="modal-mask" id="detailMask">
<div class="modal--wide">
@@ -338,34 +396,35 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="modal__body" style="padding:0;">
<div class="report-cols">
<div class="report-cols__left">
<div class="report-title">体检结论</div>
<div class="report-sub" id="dConclusion"></div>
<div class="report-title">体检建议</div>
<div class="report-sub" id="dSuggest"></div>
<div class="rpt-title">体检结论</div>
<div class="rpt-text" id="dConclusion"></div>
<div class="rpt-title">体检建议</div>
<div class="rpt-text" id="dSuggest"></div>
<div id="dReport"></div>
</div>
<div class="report-cols__right">
<div class="report-title">员工基本信息</div>
<div class="emp-basic" id="dEmp"></div>
<div class="report-title">综合解读建议</div>
<div class="advise-box" id="dAdvise"></div>
<div class="report-title">五类人群重新分布</div>
<div style="display:flex;padding:6px 0 10px;gap:16px;">
<span id="dGroupBefore" style="font-size:var(--font-sm);color:var(--text-secondary);"></span>
<span id="dGroupAfter" style="font-size:var(--font-sm);color:var(--text-secondary);"></span>
<div class="form-card">
<div class="form-card__title">员工基本信息</div>
<div class="form-grid" id="dEmp"></div>
</div>
<div class="report-title">员工确认签字</div>
<div id="dSign"></div>
<div style="display:flex;width:100%;margin-top:16px;">
<div style="width:50%;">
<div class="report-title" style="margin-top:0;">解读医生</div>
<div style="padding-left:12px;" id="dDoctor"></div>
<div class="form-card">
<div class="form-card__title">解读信息</div>
<div class="form-grid">
<div>解读医生:<b id="dDoctor"></b></div><div>解读日期:<b id="dDate"></b></div>
</div>
<div style="width:50%;">
<div class="report-title" style="margin-top:0;">解读日期</div>
<div style="padding-left:12px;" id="dDate"></div>
</div>
<div class="form-card">
<div class="form-card__title">解读意见</div>
<div style="font-size:13px;color:#333;line-height:1.9;" id="dAdvise"></div>
<div style="display:flex;gap:16px;margin-top:10px;">
<span id="dGroupBefore" style="font-size:12px;color:#4A5A7F;"></span>
<span id="dGroupAfter" style="font-size:12px;color:#4A5A7F;"></span>
</div>
</div>
<div class="form-card">
<div class="form-card__title">员工确认签字</div>
<div id="dSign"></div>
</div>
</div>
</div>
</div>
@@ -419,7 +478,7 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<div class="toast" id="toast"></div>
<script>
/* ===== 演示数据(脱敏):解读任务由年度体检报告回传生成 ===== */
/* ===== 演示数据:解读任务由年度体检报告回传生成 ===== */
var pointUnit={'樊学作业区医疗点':'采油一厂','姬圪台作业区医疗点':'采油二厂','西峰基地医疗点':'第四采油厂','靖边气田医疗点':'采气一厂','桐园作业区医疗点':'采油二厂','安塞采油队长住点医疗室':'采油一厂','乌审旗采气区医疗点':'采气一厂','姬塬作业区医疗点':'采油一厂'};
var pointMap={
'采油一厂':['樊学作业区医疗点','安塞采油队长住点医疗室','姬塬作业区医疗点'],
@@ -433,17 +492,17 @@ var GROUP_BADGE={a:'#CF1322',b:'#FA8C16',c:'#D48806',d:'#096DD9',e:'#389E0D'};
var CHRONIC={'CQ033096':'高血压','CQ010564':'糖尿病','CQ019203':'冠心病','CQ021578':'高血压','CQ027710':'肥胖','CQ036441':'高血压','CQ022317':'肥胖','CQ029905':'无','CQ044871':'无','CQ041822':'无','CQ032144':'无','CQ045130':'无'};
/* status: 1待解读 2已逾期 3已完成 ; interpretDate 为解读完成日期 */
var tasks=[
{id:1,code:'CQ033096',name:'赵万福',sex:'男',age:39,tel:'150****6127',unit:'采油二厂',dept:'桐园作业区',point:'桐园作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-07',doctor:'曲志明',status:3,advise:'高血压2级合并超重,已纳入慢病管理;建议低盐减重、规律服药并每月随访。',group:'b',beforeGroup:'d'},
{id:2,code:'CQ010564',name:'高建军',sex:'男',age:50,tel:'139****0086',unit:'第四采油厂',dept:'基地管理处',point:'西峰基地医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-08-30',doctor:'闫婷',status:3,advise:'2型糖尿病诊断明确,转内分泌专科复核用药方案;双周随访控制血糖。',group:'b',beforeGroup:'c'},
{id:3,code:'CQ027710',name:'张秀兰',sex:'女',age:45,tel:'136****2248',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:4,code:'CQ021578',name:'石维国',sex:'男',age:46,tel:'139****2051',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:5,code:'CQ022317',name:'徐立成',sex:'男',age:44,tel:'133****4860',unit:'采气一厂',dept:'苏里格南作业区',point:'乌审旗采气区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:6,code:'CQ036441',name:'刘玉梅',sex:'女',age:40,tel:'135****7719',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:2,advise:'',group:'',beforeGroup:''},
{id:7,code:'CQ019203',name:'曲志明',sex:'男',age:42,tel:'137****8890',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:2,advise:'',group:'',beforeGroup:''},
{id:8,code:'CQ044871',name:'白瑞',sex:'男',age:31,tel:'155****3316',unit:'采油一厂',dept:'生产技术部',point:'安塞采油队长住点医疗室',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:9,code:'CQ032144',name:'李桂芳',sex:'女',age:38,tel:'138****7742',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-02',doctor:'李桂芳',status:3,advise:'健康状况良好,补充维生素D并保持运动习惯。',group:'e',beforeGroup:'e'},
{id:10,code:'CQ041822',name:'闫婷',sex:'女',age:34,tel:'186****5527',unit:'第四采油厂',dept:'健康管理部',point:'西峰基地医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:11,code:'CQ045130',name:'杨慧敏',sex:'女',age:33,tel:'188****7214',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-04',doctor:'石维国',status:3,advise:'轻度缺铁性贫血,加强营养,三个月复查血常规。',group:'c',beforeGroup:'e'}
{id:1,code:'CQ033096',name:'赵万福',sex:'男',age:39,tel:'15029836127',unit:'采油二厂',dept:'桐园作业区',point:'桐园作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-07',doctor:'曲志明',status:3,advise:'高血压2级合并超重,已纳入慢病管理;建议低盐减重、规律服药并每月随访。',group:'b',beforeGroup:'d'},
{id:2,code:'CQ010564',name:'高建军',sex:'男',age:50,tel:'13956200086',unit:'第四采油厂',dept:'基地管理处',point:'西峰基地医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-08-30',doctor:'闫婷',status:3,advise:'2型糖尿病诊断明确,转内分泌专科复核用药方案;双周随访控制血糖。',group:'b',beforeGroup:'c'},
{id:3,code:'CQ027710',name:'张秀兰',sex:'女',age:45,tel:'13649162248',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:4,code:'CQ021578',name:'石维国',sex:'男',age:46,tel:'13928562051',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:5,code:'CQ022317',name:'徐立成',sex:'男',age:44,tel:'13319284860',unit:'采气一厂',dept:'苏里格南作业区',point:'乌审旗采气区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:6,code:'CQ036441',name:'刘玉梅',sex:'女',age:40,tel:'13572867719',unit:'采气一厂',dept:'靖边净化厂',point:'靖边气田医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:2,advise:'',group:'',beforeGroup:''},
{id:7,code:'CQ019203',name:'曲志明',sex:'男',age:42,tel:'13720568890',unit:'采油二厂',dept:'姬圪台联合站',point:'姬圪台作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:2,advise:'',group:'',beforeGroup:''},
{id:8,code:'CQ044871',name:'白瑞',sex:'男',age:31,tel:'15529403316',unit:'采油一厂',dept:'生产技术部',point:'安塞采油队长住点医疗室',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:9,code:'CQ032144',name:'李桂芳',sex:'女',age:38,tel:'13836547742',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-02',doctor:'李桂芳',status:3,advise:'健康状况良好,补充维生素D并保持运动习惯。',group:'e',beforeGroup:'e'},
{id:10,code:'CQ041822',name:'闫婷',sex:'女',age:34,tel:'18629135527',unit:'第四采油厂',dept:'健康管理部',point:'西峰基地医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'',doctor:'',status:1,advise:'',group:'',beforeGroup:''},
{id:11,code:'CQ045130',name:'杨慧敏',sex:'女',age:33,tel:'18829587214',unit:'采油一厂',dept:'樊学作业区',point:'樊学作业区医疗点',year:'2026',deadline:'2026-10-31',interpretDate:'2026-09-04',doctor:'石维国',status:3,advise:'轻度缺铁性贫血,加强营养,三个月复查血常规。',group:'c',beforeGroup:'e'}
];
var nextId=12,curInterpId=null,statusGroup='0',today='2026-09-07';
var statusMap={1:'待解读',2:'已逾期',3:'已完成'};
@@ -453,15 +512,26 @@ var statusBadge={1:'badge--warning',2:'badge--danger',3:'badge--success'};
function buildReport(t){
return {
conclusion: t.summary || '各项指标未见明显异常',
suggest: '结合既往史与生活方式,建议定期复查并保持健康管理。',
groups: [
{analysisName:'一般检查', cats:[
{cls:'体格检查', rows:[{name:'血压',result:'128/82',unit:'mmHg',ref:'<140/90'},{name:'心率',result:'74',unit:'/',ref:'60-100'}]},
{cls:'人体测量', rows:[{name:'身高',result:'172',unit:'cm',ref:''},{name:'体重',result:'78',unit:'kg',ref:''},{name:'BMI',result:'26.4',unit:'',ref:'18.5-23.9'},{name:'腰围',result:'88',unit:'cm',ref:'<85'}]}
suggest: '低盐低脂饮食,规律监测血压并记录;3个月内复查血脂、血糖、糖化血红蛋白;每周中等强度运动不少于150分钟;如出现头晕、心悸等症状及时就诊。',
majors: [
{name:'实验室检查', cats:[
{cls:'生化全套', rows:[
{name:'空腹血糖',result:'7.8 ↑',unit:'mmol/L',ref:'3.9~6.1',abnormal:true},
{name:'血清总胆固醇',result:'6.12',unit:'mmol/L',ref:'0~5.2',abnormal:true},
{name:'血清甘油三酯',result:'2.35',unit:'mmol/L',ref:'0~1.7',abnormal:true},
{name:'糖化血红蛋白',result:'6.9',unit:'%',ref:'4~6.5',abnormal:true}
]},
{cls:'血常规', rows:[
{name:'白细胞计数',result:'6.8',unit:'10^9/L',ref:'3.5~9.5'},
{name:'血红蛋白',result:'145',unit:'g/L',ref:'130~175'},
{name:'血小板计数',result:'210',unit:'10^9/L',ref:'125~350'}
]}
]},
{analysisName:'实验室检查', cats:[
{cls:'血常规', rows:[{name:'白细胞',result:'6.1',unit:'×10⁹/L',ref:'3.5-9.5'},{name:'血红蛋白',result:'142',unit:'g/L',ref:'130-175'},{name:'血小板',result:'235',unit:'×10⁹/L',ref:'125-350'}]},
{cls:'生化检验', rows:[{name:'空腹血糖',result:'5.8',unit:'mmol/L',ref:'3.9-6.1'},{name:'总胆固醇',result:'5.6',unit:'mmol/L',ref:'<5.2'},{name:'甘油三酯',result:'1.7',unit:'mmol/L',ref:'<1.7'},{name:'尿酸',result:'398',unit:'μmol/L',ref:'208-428'}]}
{name:'辅助检查', cats:[
{cls:'心电图', rows:[
{name:'窦性心律',result:'未见异常',unit:'—',ref:'—'},
{name:'ST-T 改变',result:'轻度异常',unit:'—',ref:'—',abnormal:true}
]}
]}
]
};
@@ -574,13 +644,15 @@ function onFilterUnit(){
}
function currentData(){
var q=document.getElementById('filterName').value.trim();
var code=document.getElementById('filterCode').value.trim();
var unit=document.getElementById('filterUnit').value;
var point=document.getElementById('filterPoint').value;
var year=document.getElementById('filterYear').value;
var from=document.getElementById('filterFrom').value;
var to=document.getElementById('filterTo').value;
return tasks.filter(function(t){
return (!q||t.name.indexOf(q)!==-1||t.code.toLowerCase().indexOf(q.toLowerCase())!==-1)
return (!q||t.name.indexOf(q)!==-1)
&&(!code||t.code.toLowerCase().indexOf(code.toLowerCase())!==-1)
&&(!selUnit||t.unit===selUnit)&&(!selDept||t.dept===selDept)
&&(!unit||pointUnit[t.point]===unit)&&(!point||t.point===point)
&&(!year||t.year===year)
@@ -611,14 +683,13 @@ function renderTable(){
tbody.innerHTML=html;
}
/* 行操作:体检报告解读/重新解读/编辑/解读详情 */
function canRedo(t){ return t.status===3 && t.interpretDate>=today; }
function actionHtml(t){
var h='';
if(t.status!==3){
h+='<button class="btn btn-link" onclick="openInterpret('+t.id+')">体检报告解读</button>';
h+='<button class="btn btn-link" onclick="openEdit('+t.id+')">编辑</button>';
}else{
if(canRedo(t))h+='<button class="btn btn-link" onclick="openInterpret('+t.id+',true)">重新解读</button>';
h+='<button class="btn btn-link" onclick="openInterpret('+t.id+',true)">重新解读</button>';
h+='<button class="btn btn-link" onclick="openDetail('+t.id+')">解读详情</button>';
}
return h;
@@ -631,6 +702,7 @@ function setStatusGroup(btn,v){
function applyFilter(){currentPage=1;renderTable();}
function resetFilter(){
document.getElementById('filterName').value='';
document.getElementById('filterCode').value='';
document.getElementById('filterUnit').value='';onFilterUnit();
document.getElementById('filterYear').value='';
document.getElementById('filterFrom').value='';document.getElementById('filterTo').value='';
@@ -645,30 +717,30 @@ function turnPage(d){
/* ===== 体检报告渲染 ===== */
function renderReportHtml(report){
if(!report.groups||!report.groups.length)return '<div class="td-empty">暂无体检数据</div>';
if(!report.majors||!report.majors.length)return '<div class="td-empty">暂无体检数据</div>';
var html='';
report.groups.forEach(function(g){
html+='<div class="report-group"><div class="report-group__name">'+g.analysisName+'</div>';
g.cats.forEach(function(c){
html+='<div class="report-cat"><div class="report-cat__name">'+c.cls+'</div>';
html+='<ul class="report-ul report-ul--head"><li>名称</li><li>结果</li><li>单位</li><li>参考值</li></ul>';
report.majors.forEach(function(m){
html+='<div class="rpt-major">'+m.name+'</div>';
m.cats.forEach(function(c){
html+='<div class="rpt-title rpt-title--sub">'+c.cls+'</div>';
html+='<table class="rpt-table"><tr><th>名称</th><th>结果</th><th>单位</th><th>参考值</th></tr>';
c.rows.forEach(function(r){
html+='<ul class="report-ul"><li>'+r.name+'</li><li>'+r.result+'</li><li>'+r.unit+'</li><li>'+r.ref+'</li></ul>';
html+='<tr><td>'+r.name+'</td><td class="'+(r.abnormal?'abnormal':'')+'">'+r.result+'</td><td>'+r.unit+'</td><td>'+r.ref+'</td></tr>';
});
html+='</div>';
html+='</table>';
});
html+='</div>';
});
return html;
}
function empBasicHtml(t){
return '<div class="w25">姓名<b> '+t.name+'</b></div><div class="w25">性别<b> '+t.sex+'</b></div><div class="w25">年龄<b> '+t.age+'</b></div><div class="w25">员工编号<b> '+t.code+'</b></div>'
+'<div class="w50">所属单位<b> '+t.unit+'</b></div><div class="w50">所属部门<b> '+t.dept+'</b></div>'
+'<div class="w100">慢病类型<b> '+(CHRONIC[t.code]||'无')+'</b></div>';
return '<div>姓名<b>'+t.name+'</b></div><div>性别<b>'+t.sex+'</b></div>'
+'<div>年龄:<b>'+t.age+' 岁</b></div><div>员工编号:<b>'+t.code+'</b></div>'
+'<div style="grid-column:1/-1;">所属单位:<b>'+t.unit+'</b></div>'
+'<div style="grid-column:1/-1;">体检日期:<b>'+t.year+'-08-15</b> 报告:'+t.year+'年体检报告</div>';
}
function fillGroupSel(val){
var sel=document.getElementById('intGroup');
sel.innerHTML='<option value="">请选择分类</option>'+Object.keys(GROUP_MAP).map(function(k){
sel.innerHTML='<option value="">请选择五类人群分类</option>'+Object.keys(GROUP_MAP).map(function(k){
return '<option value="'+k+'"'+(k===val?' selected':'')+'>'+GROUP_MAP[k]+'</option>';
}).join('');
}
@@ -685,26 +757,76 @@ function openInterpret(id,redo){
document.getElementById('intEmp').innerHTML=empBasicHtml(t);
document.getElementById('intAdvise').value=redo?t.advise:'';
fillGroupSel(redo?t.group:'');
document.getElementById('signHolder').innerHTML='本人已确认健康随访';
if(redo){lockSign(t.name);}else{resetSign();}
document.getElementById('intMask').classList.add('open');
}
function doSign(){
/* ===== 员工签字(手写/图片,参考 medical-pad/interpret-drawer) ===== */
var signType='hand',signDone=false;
function switchSignType(type){
signType=type;
document.getElementById('signHandBox').style.display=type==='hand'?'flex':'none';
document.getElementById('signImgBox').style.display=type==='hand'?'none':'flex';
}
function resetSign(){
signType='hand';signDone=false;
document.getElementById('signField').style.display='block';
document.getElementById('signLockView').style.display='none';
document.querySelector('input[name="signType"][value="hand"]').checked=true;
document.getElementById('signHandBox').style.display='flex';
document.getElementById('signImgBox').style.display='none';
document.getElementById('signHandBox').innerHTML='<svg viewBox="0 0 24 24"><path d="M17 3a2.8 2.8 0 014 4L7.5 20.5 2 22l1.5-5.5z"/></svg>点击签名';
document.getElementById('signImgBox').innerHTML='<svg viewBox="0 0 24 24"><path d="M12 16V4M7 9l5-5 5 5"/><path d="M4 20h16"/></svg>上传签字图片';
}
function lockSign(name){
signType='hand';signDone=true;
document.getElementById('signField').style.display='none';
document.getElementById('signLockView').style.display='block';
document.getElementById('signLockView').innerHTML='<div class="sign-view"><span class="sign-view__name">'+name+'</span><span class="sign-view__note">已签名</span></div>';
}
function openSignModal(){clearCanvas();document.getElementById('signMask').classList.add('open');}
function closeSignModal(){document.getElementById('signMask').classList.remove('open');}
function onSignImg(input){
var f=input.files&&input.files[0];
if(!f)return;
var r=new FileReader();
r.onload=function(e){
document.getElementById('signImgBox').innerHTML='<img src="'+e.target.result+'" style="max-width:100%;max-height:80px;object-fit:contain;"><span style="font-size:11px;color:#969EB0;">已上传 · 点击可重新上传</span>';
signDone=true;
};
r.readAsDataURL(f);
}
var signCanvas=null,signCtx=null,drawing=false,hasInk=false;
function initSignCanvas(){
signCanvas=document.getElementById('signCanvas');
signCtx=signCanvas.getContext('2d');
signCtx.lineWidth=3;signCtx.lineCap='round';signCtx.strokeStyle='#333';
signCanvas.onmousedown=function(e){drawing=true;var p=canvasPos(e);signCtx.beginPath();signCtx.moveTo(p.x,p.y);};
signCanvas.onmousemove=function(e){if(!drawing)return;var p=canvasPos(e);signCtx.lineTo(p.x,p.y);signCtx.stroke();hasInk=true;document.getElementById('signHint').style.display='none';};
window.addEventListener('mouseup',function(){drawing=false;});
}
function canvasPos(e){
var r=signCanvas.getBoundingClientRect();
return {x:(e.clientX-r.left)*(signCanvas.width/r.width),y:(e.clientY-r.top)*(signCanvas.height/r.height)};
}
function clearCanvas(){signCtx.clearRect(0,0,signCanvas.width,signCanvas.height);hasInk=false;document.getElementById('signHint').style.display='flex';}
function confirmSign(){
if(!hasInk){showToast('您还没有进行签字');return;}
var t=tasks.find(function(x){return x.id===curInterpId;});
document.getElementById('signHolder').innerHTML='<svg width="200" height="60" viewBox="0 0 200 60"><path d="M14 42c16-24 22 14 36-8 10-18 16 20 30-2 9-16 14 12 24-2 8-14 14 10 26 4 12-6 24 8 38 2" stroke="#14213D" stroke-width="2.4" fill="none" stroke-linecap="round"/></svg>'
+'<div style="font-size:var(--font-xs);color:var(--text-placeholder);margin-top:6px;">'+t.name+' · '+today+' 电子签名存证</div>';
document.getElementById('signHandBox').innerHTML='<span style="font-size:26px;font-family:KaiTi,serif;color:#333;">'+t.name+'</span><span style="font-size:11px;color:#969EB0;">已签名 · 点击可重签</span>';
signDone=true;
closeSignModal();
}
function saveInterpret(){
var t=tasks.find(function(x){return x.id===curInterpId;});
var advise=document.getElementById('intAdvise').value.trim();
var group=document.getElementById('intGroup').value;
if(!advise){showToast('请填写综合解读建议');return;}
if(!group){showToast('请选择五类人群重新分类');return;}
var signed=document.getElementById('signHolder').textContent.indexOf('电子签名')!==-1;
if(!group){showToast('请选择五类人群分类');return;}
t.advise=advise;t.group=group;t.status=3;t.interpretDate=today;
t.beforeGroup=t.beforeGroup||'e';
t.doctor=t.doctor||'张秀兰';
closeModal('intMask');renderTable();
showToast(signed?'「'+t.name+'」报告解读已完成,结果同步至健康档案':'「'+t.name+'」报告解读已完成,员工签字将在 PAD 端确认');
showToast(signDone?'「'+t.name+'」报告解读已完成,结果同步至健康档案':'「'+t.name+'」报告解读已完成,员工签字将在 PAD 端确认');
}
/* ===== 解读详情弹窗 ===== */
@@ -719,7 +841,7 @@ function openDetail(id){
document.getElementById('dAdvise').textContent=t.advise||'—';
document.getElementById('dGroupBefore').innerHTML='解读前:<span class="group-badge" style="color:'+(GROUP_BADGE[t.beforeGroup]||'#999')+';background:'+(GROUP_BADGE[t.beforeGroup]||'#999')+'1a;">'+(GROUP_MAP[t.beforeGroup]||'健康-E类')+'</span>';
document.getElementById('dGroupAfter').innerHTML='解读后:<span class="group-badge" style="color:'+(GROUP_BADGE[t.group]||'#999')+';background:'+(GROUP_BADGE[t.group]||'#999')+'1a;">'+(GROUP_MAP[t.group]||'健康-E类')+'</span>';
document.getElementById('dSign').innerHTML='<div class="sign-box" style="min-height:80px;"><svg width="180" height="52" viewBox="0 0 180 52"><path d="M10 38c14-20 20 12 32-6 9-14 14 16 26-2 8-12 12 10 22-2 8-12 14 8 26 2" stroke="#14213D" stroke-width="2.4" fill="none" stroke-linecap="round"/></svg><div style="font-size:var(--font-xs);color:var(--text-placeholder);">'+t.name+' · '+t.interpretDate+' 于 PAD 端确认</div></div>';
document.getElementById('dSign').innerHTML='<div class="sign-view"><span class="sign-view__name">'+t.name+'</span></div>';
document.getElementById('dDoctor').textContent=t.doctor||'—';
document.getElementById('dDate').textContent=t.interpretDate||'—';
document.getElementById('detailMask').classList.add('open');
@@ -780,7 +902,8 @@ function showToast(msg){
}
renderTable();
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
initSignCanvas();
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
</script>
</body>
</html>
@@ -30,13 +30,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.sidebar-item{display:flex;align-items:center;gap:8px;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:9px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:40px;}
/* [交互] 二级分组菜单展开/收起(参考床头呼叫) */
.sidebar-group__label{display:flex;align-items:center;justify-content:space-between;padding:9px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;}
.sidebar-group__label:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-group__label svg{transition:transform .2s;flex-shrink:0;}
.sidebar-group.open .sidebar-group__label svg{transform:rotate(180deg);}
.sidebar-group__items{display:none;}
.sidebar-group.open .sidebar-group__items{display:block;}
.sidebar-item--sub{padding-left:44px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 顶部Tab:员工就诊 / 非员工就诊 */
.page-tabs{display:flex;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);margin-bottom:16px;overflow:hidden;}
@@ -191,15 +192,17 @@ select.form-control{background:#fff url("data:image/svg+xml,%3Csvg xmlns='http:/
<a href="daily-follow.html" class="sidebar-item">随访管理</a>
<a href="daily-chronic.html" class="sidebar-item">慢病管理</a>
<a href="daily-employee-search.html" class="sidebar-item">员工档案</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>任务管理</div>
<div class="sidebar-subgroup__body">
<a href="daily-follow-task.html" class="sidebar-item">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item">解读任务</a>
<div class="sidebar-group" id="grpTask">
<div class="sidebar-group__label" onclick="toggleGroup('grpTask')">任务管理
<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M2 3.5L5 6.5l3-3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</div>
<div class="sidebar-group__items">
<a href="daily-follow-task.html" class="sidebar-item sidebar-item--sub">随访任务</a>
<a href="daily-interpret.html" class="sidebar-item sidebar-item--sub">解读任务</a>
</div>
</div>
<a href="daily-education.html" class="sidebar-item">健康宣教管理</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读管理</a>
<a href="daily-education.html" class="sidebar-item">健康宣教</a>
<a href="daily-interpret-manage.html" class="sidebar-item">报告解读记录</a>
</aside>
<!-- 主内容 -->
@@ -633,7 +636,7 @@ function showToast(msg){
var t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2200);
}
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function toggleGroup(id){document.getElementById(id).classList.toggle('open');}
function switchTab(tab){
document.querySelectorAll('.page-tab').forEach(function(t){t.classList.toggle('active',t.getAttribute('data-tab')===tab);});
document.getElementById('panelStaff').classList.toggle('active',tab==='staff');
@@ -198,3 +198,23 @@
- `web-admin/frontline-medical/daily-treatment-staff.html` / `daily-treatment-nonstaff.html` — 删除
- `web-admin/frontline-medical/` 其余 45 页 — 侧边栏菜单与顶栏链接同步
- `D:\Work\platform-prototype\index.html` — 站点地图同步
## [2026-09-15] feat: 身份证与联系电话全站去除脱敏
按需求将一线医疗模块所有页面演示数据中的身份证号与联系电话由脱敏展示改为完整展示,员工数据与「日常诊疗管理」就诊员工池保持一致。
**涉及文件:**
- `bed-bed.html` / `bed-dormitory.html` / `bed-call-record.html` — 床位/宿舍/呼叫记录员工电话
- `config-allocate.html` / `config-ambulance.html` — 员工分配/救护车配置电话
- `daily-follow.html` / `daily-follow-task.html` / `daily-interpret.html` — 随访/解读任务电话,随访任务页身份证
- `daily-ambulance-attendance.html` — 救护车出勤电话
- `hut-appointment.html` / `hut-cabin.html` — 健康小屋预约/台账电话
## [2026-09-15] feat: 员工分配统计概览卡改为员工口径 · 名册统计文案上移
按需求调整员工分配统计页。
**涉及文件:**
- `config-allocate-stat.html` — 概览卡由四卡(医疗点数量/医护人员总数/已分配员工总数/平均每点分配人数)改为三卡:员工总数量、已分配员工数量、未分配员工数量(未分配按单位汇总,与「员工分配」页未分配 Tab 演示数据一致共 9 人,仍随筛选实时计算);已分配员工名册抽屉中「共 N 人 · 分配至『单位/医疗点』」统计文案由筛选框右侧移至筛选框上方
@@ -241,18 +241,18 @@ var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油
var stBadge={'待使用':'badge--info','使用中':'badge--warning','已完成':'badge--success','已取消':'badge--muted','爽约':'badge--danger'};
/* 预约记录:健康室与设备与 hut-room 勾稽;监测设备不产生预约 */
var appts=[
{code:'YY-20260901-001',name:'石维国',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'138****2211',status:'已完成',room:'樊学站点健康小屋',dev:'血压检测仪',date:'2026-09-01',start:'2026-09-01 09:00',end:'2026-09-01 09:32',created:'2026-08-30 16:22',useTime:'2026-09-01 09:02',backTime:'2026-09-01 09:32'},
{code:'YY-20260901-002',name:'李桂芳',sex:'女',unit:'采油一厂',depar:'樊学作业区',phone:'139****0834',status:'已完成',room:'樊学站点健康小屋',dev:'血糖分析仪',date:'2026-09-01',start:'2026-09-01 10:00',end:'2026-09-01 10:18',created:'2026-08-31 08:40',useTime:'2026-09-01 10:01',backTime:'2026-09-01 10:18'},
{code:'YY-20260902-003',name:'曲志明',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'137****5520',status:'已取消',room:'樊学站点健康小屋',dev:'心理测评系统',date:'2026-09-02',start:'2026-09-02 14:00',end:'—',created:'2026-09-01 11:05',cancelTime:'2026-09-02 08:12'},
{code:'YY-20260903-004',name:'刘玉梅',sex:'女',unit:'采气一厂',depar:'靖边气田',phone:'136****7712',status:'已完成',room:'靖边气田健康小屋',dev:'体成分分析仪',date:'2026-09-03',start:'2026-09-03 15:00',end:'2026-09-03 15:21',created:'2026-09-02 17:33',useTime:'2026-09-03 15:00',backTime:'2026-09-03 15:21'},
{code:'YY-20260904-005',name:'高建军',sex:'男',unit:'第四采油厂',depar:'西峰作业区',phone:'135****9086',status:'爽约',room:'西峰基地健康小屋',dev:'激光治疗仪',date:'2026-09-04',start:'2026-09-04 14:30',end:'—',created:'2026-09-03 09:12',cancelTime:'2026-09-04 15:10'},
{code:'YY-20260905-006',name:'徐立成',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'138****4417',status:'已完成',room:'桐园站点健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:30',end:'2026-09-05 10:02',created:'2026-09-04 15:48',useTime:'2026-09-05 09:31',backTime:'2026-09-05 10:02'},
{code:'YY-20260905-007',name:'呼建国',sex:'男',unit:'采气一厂',depar:'靖边气田',phone:'139****2680',status:'已取消',room:'靖边气田健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:00',end:'—',created:'2026-09-04 18:02',cancelTime:'2026-09-04 20:15'},
{code:'YY-20260906-008',name:'白瑞',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'137****3341',status:'已取消',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-06',start:'2026-09-06 16:00',end:'—',created:'2026-09-05 10:26',cancelTime:'2026-09-06 07:50'},
{code:'YY-20260907-009',name:'赵万福',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'136****8123',status:'使用中',room:'桐园站点健康小屋',dev:'音乐放松舱',date:'2026-09-07',start:'2026-09-07 10:00',end:'—',created:'2026-09-06 14:19',useTime:'2026-09-07 10:03',backTime:'—'},
{code:'YY-20260907-010',name:'闫婷',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'135****6208',status:'使用中',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-07',start:'2026-09-07 11:00',end:'—',created:'2026-09-06 16:55',useTime:'2026-09-07 11:01',backTime:'—'},
{code:'YY-20260908-011',name:'杨慧敏',sex:'女',unit:'采油一厂',depar:'麻子山作业区',phone:'138****9975',status:'待使用',room:'麻子山站点健康小屋',dev:'体成分分析仪',date:'2026-09-08',start:'2026-09-08 15:00',end:'—',created:'2026-09-07 08:31'},
{code:'YY-20260908-012',name:'张秀兰',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'139****5402',status:'待使用',room:'西峰基地健康小屋',dev:'血压检测仪',date:'2026-09-08',start:'2026-09-08 10:00',end:'—',created:'2026-09-07 09:04'}
{code:'YY-20260901-001',name:'石维国',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'13821622211',status:'已完成',room:'樊学站点健康小屋',dev:'血压检测仪',date:'2026-09-01',start:'2026-09-01 09:00',end:'2026-09-01 09:32',created:'2026-08-30 16:22',useTime:'2026-09-01 09:02',backTime:'2026-09-01 09:32'},
{code:'YY-20260901-002',name:'李桂芳',sex:'女',unit:'采油一厂',depar:'樊学作业区',phone:'13937440834',status:'已完成',room:'樊学站点健康小屋',dev:'血糖分析仪',date:'2026-09-01',start:'2026-09-01 10:00',end:'2026-09-01 10:18',created:'2026-08-31 08:40',useTime:'2026-09-01 10:01',backTime:'2026-09-01 10:18'},
{code:'YY-20260902-003',name:'曲志明',sex:'男',unit:'采油一厂',depar:'樊学作业区',phone:'13708495520',status:'已取消',room:'樊学站点健康小屋',dev:'心理测评系统',date:'2026-09-02',start:'2026-09-02 14:00',end:'—',created:'2026-09-01 11:05',cancelTime:'2026-09-02 08:12'},
{code:'YY-20260903-004',name:'刘玉梅',sex:'女',unit:'采气一厂',depar:'靖边气田',phone:'13607587712',status:'已完成',room:'靖边气田健康小屋',dev:'体成分分析仪',date:'2026-09-03',start:'2026-09-03 15:00',end:'2026-09-03 15:21',created:'2026-09-02 17:33',useTime:'2026-09-03 15:00',backTime:'2026-09-03 15:21'},
{code:'YY-20260904-005',name:'高建军',sex:'男',unit:'第四采油厂',depar:'西峰作业区',phone:'13589259086',status:'爽约',room:'西峰基地健康小屋',dev:'激光治疗仪',date:'2026-09-04',start:'2026-09-04 14:30',end:'—',created:'2026-09-03 09:12',cancelTime:'2026-09-04 15:10'},
{code:'YY-20260905-006',name:'徐立成',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'13848934417',status:'已完成',room:'桐园站点健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:30',end:'2026-09-05 10:02',created:'2026-09-04 15:48',useTime:'2026-09-05 09:31',backTime:'2026-09-05 10:02'},
{code:'YY-20260905-007',name:'呼建国',sex:'男',unit:'采气一厂',depar:'靖边气田',phone:'13976362680',status:'已取消',room:'靖边气田健康小屋',dev:'血压检测仪',date:'2026-09-05',start:'2026-09-05 09:00',end:'—',created:'2026-09-04 18:02',cancelTime:'2026-09-04 20:15'},
{code:'YY-20260906-008',name:'白瑞',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13720283341',status:'已取消',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-06',start:'2026-09-06 16:00',end:'—',created:'2026-09-05 10:26',cancelTime:'2026-09-06 07:50'},
{code:'YY-20260907-009',name:'赵万福',sex:'男',unit:'采油二厂',depar:'桐园作业区',phone:'13661218123',status:'使用中',room:'桐园站点健康小屋',dev:'音乐放松舱',date:'2026-09-07',start:'2026-09-07 10:00',end:'—',created:'2026-09-06 14:19',useTime:'2026-09-07 10:03',backTime:'—'},
{code:'YY-20260907-010',name:'闫婷',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13547386208',status:'使用中',room:'西峰基地健康小屋',dev:'血糖分析仪',date:'2026-09-07',start:'2026-09-07 11:00',end:'—',created:'2026-09-06 16:55',useTime:'2026-09-07 11:01',backTime:'—'},
{code:'YY-20260908-011',name:'杨慧敏',sex:'女',unit:'采油一厂',depar:'麻子山作业区',phone:'13856939975',status:'待使用',room:'麻子山站点健康小屋',dev:'体成分分析仪',date:'2026-09-08',start:'2026-09-08 15:00',end:'—',created:'2026-09-07 08:31'},
{code:'YY-20260908-012',name:'张秀兰',sex:'女',unit:'第四采油厂',depar:'西峰作业区',phone:'13916355402',status:'待使用',room:'西峰基地健康小屋',dev:'血压检测仪',date:'2026-09-08',start:'2026-09-08 10:00',end:'—',created:'2026-09-07 09:04'}
];
var delCodes=[];/* 当前待删除单号集合 */
+8 -8
View File
@@ -296,14 +296,14 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
var unitDeparts={'采油一厂':['樊学作业区','麻子山作业区'],'采油二厂':['桐园作业区','姬圪台作业区'],'第四采油厂':['西峰作业区'],'采气一厂':['靖边气田']};
/* 小屋台账:负责人沿用站点负责人池;records 为每日测量人次(血压/血糖) */
var cabins=[
{name:'樊学基地健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'王守正',office:'0963-88201',mobile:'138****2211',date:'2024-05-18',money:86000,devNum:6},
{name:'麻子山站点健康小屋',unit:'采油一厂',depar:'麻子山作业区',mgr:'安伟',office:'0963-88302',mobile:'139****0834',date:'2024-06-02',money:75000,devNum:5},
{name:'桐园基地健康小屋',unit:'采油二厂',depar:'桐园作业区',mgr:'陈国栋',office:'0963-88401',mobile:'137****5520',date:'2024-07-11',money:86000,devNum:6},
{name:'姬圪台站点健康小屋',unit:'采油二厂',depar:'姬圪台作业区',mgr:'刘志强',office:'0963-88402',mobile:'136****7712',date:'2024-09-20',money:68000,devNum:4},
{name:'西峰基地健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'马文秀',office:'0963-88501',mobile:'135****9086',date:'2024-08-15',money:92000,devNum:7},
{name:'靖边气田健康小屋',unit:'采气一厂',depar:'靖边气田',mgr:'周铁祥',office:'0963-88601',mobile:'138****4417',date:'2025-03-06',money:86000,devNum:6},
{name:'樊学作业区第二健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'张国亮',office:'0963-88203',mobile:'139****2680',date:'2025-04-22',money:75000,devNum:5},
{name:'西峰前线健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'冯建军',office:'0963-88502',mobile:'137****3341',date:'2025-06-10',money:68000,devNum:4}
{name:'樊学基地健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'王守正',office:'0963-88201',mobile:'13821622211',date:'2024-05-18',money:86000,devNum:6},
{name:'麻子山站点健康小屋',unit:'采油一厂',depar:'麻子山作业区',mgr:'安伟',office:'0963-88302',mobile:'13937440834',date:'2024-06-02',money:75000,devNum:5},
{name:'桐园基地健康小屋',unit:'采油二厂',depar:'桐园作业区',mgr:'陈国栋',office:'0963-88401',mobile:'13708495520',date:'2024-07-11',money:86000,devNum:6},
{name:'姬圪台站点健康小屋',unit:'采油二厂',depar:'姬圪台作业区',mgr:'刘志强',office:'0963-88402',mobile:'13607587712',date:'2024-09-20',money:68000,devNum:4},
{name:'西峰基地健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'马文秀',office:'0963-88501',mobile:'13589259086',date:'2024-08-15',money:92000,devNum:7},
{name:'靖边气田健康小屋',unit:'采气一厂',depar:'靖边气田',mgr:'周铁祥',office:'0963-88601',mobile:'13848934417',date:'2025-03-06',money:86000,devNum:6},
{name:'樊学作业区第二健康小屋',unit:'采油一厂',depar:'樊学作业区',mgr:'张国亮',office:'0963-88203',mobile:'13976362680',date:'2025-04-22',money:75000,devNum:5},
{name:'西峰前线健康小屋',unit:'第四采油厂',depar:'西峰作业区',mgr:'冯建军',office:'0963-88502',mobile:'13720283341',date:'2025-06-10',money:68000,devNum:4}
];
/* 每座小屋生成 09-01~09-07 七日测量记录(血压人次=基准±波动,血糖≈血压×0.8) */
(function(){
+230 -21
View File
@@ -113,12 +113,52 @@
.report-note{border-radius:var(--radius-sm);padding:12px 16px;font-size:var(--font-sm);line-height:1.9;margin-bottom:16px;}
.report-note--conclusion{border:1px solid #FFD591;background:#FFF7E6;color:#873800;}
.report-note--advice{border:1px solid #B7EB8F;background:#F6FFED;color:#389E0D;}
.report-section{margin-bottom:24px;}
.report-section:last-child{margin-bottom:0;}
.report-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:12px;line-height:1.4;}
.report-sub__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin:14px 0 8px;}
.report-sub:first-of-type .report-sub__title{margin-top:0;}
.result-abnormal{color:var(--danger);font-weight:600;}
/* 报告弹窗内 Tab 切换 */
.report-tabs{display:flex;gap:28px;padding:0 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.report-tab{position:relative;padding:12px 2px;font-size:var(--font-base);color:var(--text-secondary);cursor:pointer;transition:color .2s;}
.report-tab:hover{color:var(--primary);}
.report-tab.active{color:var(--primary);font-weight:600;}
.report-tab.active::after{content:'';position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--primary);border-radius:2px;}
.report-pane{display:none;}
.report-pane.active{display:block;}
/* 专业报告版式(冠脉CTA / 胃肠镜共用,与检后报告页一致) */
.rpt-empty{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
.cta-rpt{border:1px solid var(--border);border-radius:var(--radius-md);padding:0;overflow:hidden;}
.cta-rpt__band{background:#001529;color:#fff;padding:14px 20px;font-size:var(--font-base);font-weight:600;display:flex;align-items:center;justify-content:space-between;}
.cta-rpt__band small{font-size:var(--font-xs);font-weight:400;opacity:.7;}
.cta-rpt__meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px 20px;padding:14px 20px;border-bottom:1px dashed var(--border);font-size:var(--font-sm);}
.cta-rpt__meta b{color:var(--text-secondary);font-weight:400;}
.cta-rpt__sec{padding:14px 20px;border-bottom:1px solid var(--border);}
.cta-rpt__sec:last-child{border-bottom:none;}
.cta-rpt__sec-t{font-size:var(--font-sm);font-weight:600;color:var(--primary);margin-bottom:8px;}
.cta-rpt__txt{font-size:var(--font-sm);color:var(--text-primary);line-height:1.7;}
.cta-vessel{width:100%;border-collapse:collapse;}
.cta-vessel th,.cta-vessel td{border:1px solid var(--border);padding:8px 10px;font-size:var(--font-sm);text-align:left;vertical-align:top;}
.cta-vessel th{background:#FAFAFA;color:var(--text-secondary);font-weight:500;white-space:nowrap;}
.cta-vessel td.v-name{white-space:nowrap;font-weight:500;}
.cta-vessel td.v-grade{white-space:nowrap;text-align:center;}
.cta-plaque{display:inline-flex;align-items:center;padding:1px 8px;border-radius:10px;font-size:var(--font-xs);margin-right:6px;}
.cta-plaque--calc{background:#E6F7FF;color:#1890FF;}
.cta-plaque--noncalc{background:#FFF7E6;color:#FAAD14;}
.cta-plaque--mixed{background:#FFF1F0;color:#FF4D4F;}
.cta-plaque--none{background:#F6FFED;color:#52C41A;}
.cta-narrow{font-weight:600;}
.cta-narrow--0{color:#52C41A;}
.cta-narrow--1{color:#8CB800;}
.cta-narrow--2{color:#FAAD14;}
.cta-narrow--3{color:#FA8C16;}
.cta-narrow--4{color:#FF4D4F;}
.cta-imp{display:flex;align-items:center;gap:14px;padding:14px 20px;background:#F6FBFF;border-top:1px solid var(--border);}
.cta-cad{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;width:96px;height:64px;border-radius:var(--radius-md);color:#fff;font-weight:700;line-height:1.1;}
.cta-cad small{font-size:var(--font-xs);font-weight:400;opacity:.9;}
.cta-cad--0{background:#52C41A;}.cta-cad--1{background:#8CB800;}.cta-cad--2{background:#FAAD14;}.cta-cad--3{background:#FA8C16;}.cta-cad--4{background:#FF4D4F;}
.cta-imp__list{font-size:var(--font-sm);color:var(--text-primary);line-height:1.7;}
.cta-imgs{display:flex;gap:12px;flex-wrap:wrap;}
.cta-img{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border:1px dashed var(--border);border-radius:var(--radius-sm);background:#FAFAFA;font-size:var(--font-sm);color:var(--text-secondary);}
.cta-img .link{font-size:var(--font-xs);}
/* 体检年份选择控件 */
.year-picker{position:relative;}
.year-picker .filter-input{padding-right:30px;cursor:pointer;}
@@ -240,20 +280,30 @@
<div class="modal__title" id="report-title">体检报告</div>
<button class="modal__close" onclick="closeReport()"></button>
</div>
<!-- 报告类型切换 -->
<div class="report-tabs">
<div class="report-tab active" data-tab="regular" onclick="switchReportTab('regular')">常规体检</div>
<div class="report-tab" data-tab="gastro" onclick="switchReportTab('gastro')">胃肠镜体检</div>
<div class="report-tab" data-tab="cta" onclick="switchReportTab('cta')">冠脉CTA体检</div>
</div>
<div class="modal__body">
<div class="report-pane active" data-tab="regular">
<div class="cta-rpt">
<div class="cta-rpt__band">健康体检报告</div>
<div class="cta-rpt__meta" id="regular-meta"></div>
<!-- 体检结论 -->
<div class="report-section">
<div class="report-section__title">体检结论</div>
<div class="report-note report-note--conclusion">1. 血脂异常:甘油三酯轻度偏高;<br>2. 视力疲劳:双眼裸眼视力下降;<br>3. 其余各项体检指标未见明显异常。</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">体检结论</div>
<div class="report-note report-note--conclusion" style="margin-bottom:0;">1. 血脂异常:甘油三酯轻度偏高;<br>2. 视力疲劳:双眼裸眼视力下降;<br>3. 其余各项体检指标未见明显异常。</div>
</div>
<!-- 体检建议 -->
<div class="report-section">
<div class="report-section__title">体检建议</div>
<div class="report-note report-note--advice">1. 建议低脂清淡饮食,少油少糖,规律运动,3个月后复查血脂;<br>2. 注意用眼卫生,避免长时间使用电子屏幕,必要时眼科随诊;<br>3. 建议每年进行一次健康体检,动态关注各项指标变化。</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">体检建议</div>
<div class="report-note report-note--advice" style="margin-bottom:0;">1. 建议低脂清淡饮食,少油少糖,规律运动,3个月后复查血脂;<br>2. 注意用眼卫生,避免长时间使用电子屏幕,必要时眼科随诊;<br>3. 建议每年进行一次健康体检,动态关注各项指标变化。</div>
</div>
<!-- 物理检查 -->
<div class="report-section">
<div class="report-section__title">物理检查</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">物理检查</div>
<div class="report-sub">
<div class="report-sub__title">一般检查</div>
<table>
@@ -279,8 +329,8 @@
</div>
</div>
<!-- 内科 -->
<div class="report-section">
<div class="report-section__title">内科</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">内科</div>
<div class="report-sub">
<div class="report-sub__title">心肺听诊</div>
<table>
@@ -304,8 +354,8 @@
</div>
</div>
<!-- 外科 -->
<div class="report-section">
<div class="report-section__title">外科</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">外科</div>
<div class="report-sub">
<div class="report-sub__title">皮肤与淋巴结</div>
<table>
@@ -328,8 +378,8 @@
</div>
</div>
<!-- 化学检查 -->
<div class="report-section">
<div class="report-section__title">化学检查</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">化学检查</div>
<div class="report-sub">
<div class="report-sub__title">血常规</div>
<table>
@@ -374,6 +424,10 @@
</table>
</div>
</div>
</div>
</div>
<div class="report-pane" data-tab="gastro" id="gastro-pane"></div>
<div class="report-pane" data-tab="cta" id="cta-pane"></div>
</div>
<div class="modal__footer">
<button class="btn btn-primary" onclick="closeReport()">关闭</button>
@@ -521,7 +575,7 @@ function buildBody(){
if(!val)val=[(CAT_DEFAULTS[col.cat]||['-'])[0]];
cells+='<td class="item'+(val[1]?' res-abn':'')+'">'+val[0]+'</td>';
});
cells+='<td class="td-op"><span class="link" onclick="openReport(\''+r.name+'\')">查看报告</span></td>';
cells+='<td class="td-op"><span class="link" onclick="openReport('+MASTER_ROWS.indexOf(r)+')">查看报告</span></td>';
return '<tr>'+cells+'</tr>';
}).join('');
return '<tbody>'+rows+'</tbody></table>';
@@ -566,11 +620,166 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
var sub=active.closest('.sidebar-subgroup');
while(sub){sub.classList.add('open');sub=sub.parentElement.closest('.sidebar-subgroup');}
})();
/* 查看体检报告弹窗(版式参考常规体检报告页) */
function openReport(name){
document.getElementById('report-title').textContent=name+' · 体检报告';
/* 查看体检报告弹窗:常规体检 / 胃肠镜体检 / 冠脉CTA体检 三个页签 */
function openReport(idx){
var r=MASTER_ROWS[idx];
document.getElementById('report-title').textContent=r.name+' · 体检报告';
document.getElementById('regular-meta').innerHTML=
'<span><b>姓名:</b>'+r.name+'</span><span><b>性别:</b>'+r.gender+'</span><span><b>年龄:</b>'+r.age+'岁</span>'
+'<span><b>体检编号:</b>'+r.empNo+'</span><span><b>体检日期:</b>'+r.checkDate+'</span><span><b>体检医院:</b>'+r.hospital+'</span>';
document.getElementById('gastro-pane').innerHTML=gastroReportHtml(r);
document.getElementById('cta-pane').innerHTML=ctaReportHtml(r);
switchReportTab('regular');
document.getElementById('report-modal').classList.add('open');
}
function switchReportTab(key){
var tabs=document.querySelectorAll('#report-modal .report-tab');
for(var i=0;i<tabs.length;i++)tabs[i].classList.toggle('active',tabs[i].getAttribute('data-tab')===key);
var panes=document.querySelectorAll('#report-modal .report-pane');
for(var j=0;j<panes.length;j++)panes[j].classList.toggle('active',panes[j].getAttribute('data-tab')===key);
}
/* ============ 胃肠镜报告(内容参考员工端 report-gastro.html,版式与冠脉CTA报告一致) ============ */
var GASTRO_PROFILES=[
{esophagus:'食管黏膜光滑,色泽正常,血管纹理清晰,未见静脉曲张及占位性病变。贲门开闭良好。',
stomach:'胃底、胃体黏膜光滑,色泽橘红,黏液湖清亮。胃角切迹光滑,胃窦黏膜轻度充血水肿,未见糜烂及溃疡。幽门口圆,开闭良好。',
duodenum:'十二指肠球部及降部黏膜光滑,未见溃疡、糜烂及占位性病变。乳头形态正常。',
gastroDx:'慢性非萎缩性胃炎(胃窦)',
colon:'全结肠黏膜光滑,血管纹理清晰,未见充血、水肿、糜烂、溃疡及占位性病变。回盲瓣呈唇形,阑尾开口形态正常。结肠袋形态规整,蠕动良好。',
colonDx:'全结肠黏膜未见明显异常',
pathology:'(降结肠活检)送检肠黏膜组织,腺体排列整齐,间质少量淋巴细胞浸润,未见异型增生及癌细胞。病理诊断:黏膜慢性炎。'},
{esophagus:'食管黏膜光滑,色泽正常,血管纹理清晰,未见静脉曲张及占位性病变。贲门开闭良好。',
stomach:'胃底、胃体黏膜光滑,未见糜烂及溃疡;胃窦黏膜可见散在点状糜烂,覆薄白苔,周围黏膜充血水肿。幽门口圆,开闭良好。',
duodenum:'十二指肠球部及降部黏膜光滑,未见溃疡、糜烂及占位性病变。乳头形态正常。',
gastroDx:'糜烂性胃炎(胃窦)',
colon:'升结肠、横结肠黏膜光滑,血管纹理清晰;乙状结肠距肛门约25cm处可见一枚直径约0.4cm息肉,表面光滑,亚蒂。余结肠及直肠黏膜未见异常。',
colonDx:'乙状结肠息肉(内镜下已钳除,建议半年后复查)',
pathology:'(乙状结肠活检)送检黏膜组织呈管状腺瘤样改变,伴低级别上皮内瘤变,基底切缘干净。病理诊断:管状腺瘤伴低级别上皮内瘤变。'}
];
function gastroProfile(r){
var t=0;for(var i=0;i<r.empNo.length;i++)t+=r.empNo.charCodeAt(i);
return GASTRO_PROFILES[t%GASTRO_PROFILES.length];
}
function gastroReportHtml(r){
var p=gastroProfile(r);
var eye='<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
return '<div class="cta-rpt">'
+'<div class="cta-rpt__band">胃肠镜检查报告</div>'
+'<div class="cta-rpt__meta">'
+'<span><b>姓名:</b>'+r.name+'</span><span><b>性别:</b>'+r.gender+'</span><span><b>年龄:</b>'+r.age+'岁</span>'
+'<span><b>检查编号:</b>'+r.empNo+'</span><span><b>检查日期:</b>'+r.checkDate+'</span><span><b>检查医院:</b>'+r.hospital+'</span>'
+'</div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">胃镜检查</div>'
+'<table class="cta-vessel"><thead><tr><th style="width:110px;">项目</th><th>所见 / 结论</th></tr></thead><tbody>'
+'<tr><td class="v-name">食管</td><td>'+p.esophagus+'</td></tr>'
+'<tr><td class="v-name"></td><td>'+p.stomach+'</td></tr>'
+'<tr><td class="v-name">十二指肠</td><td>'+p.duodenum+'</td></tr>'
+'<tr><td class="v-name">诊断结果</td><td>'+p.gastroDx+'</td></tr>'
+'</tbody></table></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">肠镜检查</div>'
+'<table class="cta-vessel"><thead><tr><th style="width:110px;">项目</th><th>所见 / 结论</th></tr></thead><tbody>'
+'<tr><td class="v-name">结肠</td><td>'+p.colon+'</td></tr>'
+'<tr><td class="v-name">诊断结果</td><td>'+p.colonDx+'</td></tr>'
+'<tr><td class="v-name">病理诊断</td><td>'+p.pathology+'</td></tr>'
+'</tbody></table></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">报告图片</div><div class="cta-imgs">'
+'<span class="cta-img">'+eye+'胃镜报告<span class="link">查看</span></span>'
+'<span class="cta-img">'+eye+'肠镜报告<span class="link">查看</span></span>'
+'<span class="cta-img">'+eye+'活检报告<span class="link">查看</span></span>'
+'</div></div>'
+'</div>';
}
/* ============ 冠脉CTA报告(数据与版式取自检后-冠脉CTA体检报告页 post-cta-report.html ============ */
var CTA_PROFILES=[
{dominant:'右优势型',rate:'68',beta:true,agatston:86,cad:2,
vessels:[
{n:'左主干 LM',p:'none',g:0,d:'未见斑块及狭窄'},
{n:'左前降支 LAD(近段)',p:'mixed',g:2,d:'混合斑块,管腔狭窄约30%'},
{n:'左前降支 LAD(中段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'第一对角支 D1',p:'calc',g:1,d:'钙化斑块,管腔狭窄<25%'},
{n:'左回旋支 LCX(近段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'左回旋支 LCX(远段)',p:'calc',g:1,d:'少量钙化斑块,管腔未见明显狭窄'},
{n:'钝缘支 OM',p:'none',g:0,d:'未见异常'},
{n:'右冠状动脉 RCA(近段)',p:'calc',g:2,d:'钙化斑块,管腔狭窄约40%'},
{n:'右冠状动脉 RCA(中远段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'后降支 PDA',p:'none',g:0,d:'未见异常'}
],
cardiac:'各心腔大小未见扩大,左心耳未见血栓,主动脉及瓣膜未见明显钙化,心包少量积液。双肺下叶少许条索影。',
impression:['冠状动脉粥样硬化表现','LAD近段混合斑块伴管腔约30%狭窄;RCA近段钙化斑块伴约40%狭窄','余所见冠脉未见明显狭窄'],
advise:'建议心内科专科门诊评估,控制血压、血脂等危险因素,必要时行运动负荷心肌灌注或冠脉造影进一步检查。'}
,
{dominant:'右优势型',rate:'72',beta:true,agatston:0,cad:0,
vessels:[
{n:'左主干 LM',p:'none',g:0,d:'未见斑块及狭窄'},
{n:'左前降支 LAD(近段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'左前降支 LAD(中段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'第一对角支 D1',p:'none',g:0,d:'未见异常'},
{n:'左回旋支 LCX(近段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'左回旋支 LCX(远段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'钝缘支 OM',p:'none',g:0,d:'未见异常'},
{n:'右冠状动脉 RCA(近段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'右冠状动脉 RCA(中远段)',p:'none',g:0,d:'管壁光滑,未见狭窄'},
{n:'后降支 PDA',p:'none',g:0,d:'未见异常'}
],
cardiac:'各心腔大小、形态正常,左心耳未见血栓,瓣膜未见明显钙化,心包未见积液。双肺未见明显异常。',
impression:['冠脉CTA未见明显异常','未见确切斑块及管腔狭窄'],
advise:'本次冠脉CTA未见明显异常,建议结合临床,定期体检随访。'}
,
{dominant:'均衡型',rate:'65',beta:false,agatston:265,cad:3,
vessels:[
{n:'左主干 LM',p:'calc',g:1,d:'钙化斑块,管腔狭窄<25%'},
{n:'左前降支 LAD(近段)',p:'mixed',g:3,d:'混合斑块,管腔狭窄约60%'},
{n:'左前降支 LAD(中段)',p:'noncalc',g:2,d:'非钙化斑块,管腔狭窄约45%'},
{n:'第一对角支 D1',p:'calc',g:1,d:'钙化斑块,管腔狭窄<25%'},
{n:'左回旋支 LCX(近段)',p:'calc',g:2,d:'钙化斑块,管腔狭窄约35%'},
{n:'左回旋支 LCX(远段)',p:'none',g:0,d:'未见明显狭窄'},
{n:'钝缘支 OM',p:'calc',g:1,d:'少量钙化,管腔未见明显狭窄'},
{n:'右冠状动脉 RCA(近段)',p:'mixed',g:2,d:'混合斑块,管腔狭窄约45%'},
{n:'右冠状动脉 RCA(中远段)',p:'calc',g:3,d:'钙化斑块,管腔狭窄约55%'},
{n:'后降支 PDA',p:'none',g:0,d:'未见异常'}
],
cardiac:'左心室稍增大,各瓣膜未见明显钙化,心包未见积液。升主动脉增宽约38mm。双肺下叶纤维灶。',
impression:['多支冠状动脉粥样硬化并多发斑块形成','LAD近段混合斑块伴管腔中度狭窄(约60%);RCA远段钙化斑块伴中度狭窄(约55%','LAD中段、LCX近段、RCA近段轻-中度狭窄'],
advise:'建议尽快心内科专科就诊,行冠脉造影及必要时的血流储备分数(FFR)评估,规范抗血小板及降脂治疗。'}
];
function ctaProfile(r){
var t=0;for(var i=0;i<r.empNo.length;i++)t+=r.empNo.charCodeAt(i);
return CTA_PROFILES[t%CTA_PROFILES.length];
}
var PLAQUE_TXT={none:['未见斑块','none'],calc:['钙化斑块','calc'],noncalc:['非钙化斑块','noncalc'],mixed:['混合斑块','mixed']};
var NARROW_TXT=['未见','<25%','25%~49%','50%~69%','70%'];
var CAD_TXT=['CAD-RADS 0','CAD-RADS 1','CAD-RADS 2','CAD-RADS 3','CAD-RADS 4'];
function ctaReportHtml(r){
var p=ctaProfile(r);
var vrows=p.vessels.map(function(v){
var pl=PLAQUE_TXT[v.p];
var gcls=v.g>0?('cta-narrow--'+Math.min(v.g,4)):'';
return '<tr><td class="v-name">'+v.n+'</td>'
+'<td><span class="cta-plaque cta-plaque--'+pl[1]+'">'+pl[0]+'</span></td>'
+'<td class="v-grade">'+(v.g>0?('<span class="cta-narrow '+gcls+'">'+NARROW_TXT[v.g]+'</span>'):'<span class="cta-narrow cta-narrow--0">0%</span>')+'</td>'
+'<td>'+v.d+'</td></tr>';
}).join('');
return '<div class="cta-rpt">'
+'<div class="cta-rpt__band">冠状动脉CT血管成像报告</div>'
+'<div class="cta-rpt__meta">'
+'<span><b>姓名:</b>'+r.name+'</span><span><b>性别:</b>'+r.gender+'</span><span><b>年龄:</b>'+r.age+'岁</span>'
+'<span><b>检查编号:</b>'+r.empNo+'</span><span><b>检查日期:</b>'+r.checkDate+'</span><span><b>检查医院:</b>'+r.hospital+'</span>'
+'</div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">检查条件</div><div class="cta-rpt__txt">'
+'扫描时心率 '+p.rate+' 次/分'+(p.beta?'(已使用β受体阻滞剂控制心率)':'')+',屏气良好;图像质量满足诊断要求。冠脉分布:'+p.dominant+'。'
+'</div></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">冠状动脉钙化积分</div><div class="cta-rpt__txt">'
+'Agatston 总积分约 '+p.agatston+' 分'+(p.agatston===0?'(未见钙化)':(p.agatston<100?'轻度':(p.agatston<400?'中度':'重度')))+''
+'</div></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">冠状动脉分段所见</div>'
+'<table class="cta-vessel"><thead><tr><th style="width:170px;">血管 / 段</th><th style="width:110px;">斑块性质</th><th style="width:88px;">狭窄程度</th><th>描述</th></tr></thead><tbody>'
+vrows+'</tbody></table></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">心脏及冠脉外所见</div><div class="cta-rpt__txt">'+p.cardiac+'</div></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">结论与分级</div>'
+'<div class="cta-imp"><div class="cta-cad cta-cad--'+p.cad+'"><span style="font-size:22px;">'+p.cad+'</span><small>CAD-RADS 分级</small></div>'
+'<div class="cta-imp__list">'+p.impression.map(function(x,i){return ''+(i+1)+''+x;}).join('<br>')+'</div></div></div>'
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">建议</div><div class="cta-rpt__txt">'+p.advise+'</div></div>'
+'</div>';
}
function closeReport(){
document.getElementById('report-modal').classList.remove('open');
}
@@ -332,6 +332,7 @@
<img id="cover-preview" style="display:none;">
</div>
<input type="file" id="cover-file" accept="image/*" style="display:none;" onchange="onCoverChange(this)">
<div class="form-item__hint">支持 JPG/PNG 格式,大小不超过 5MB,建议尺寸 1280×72016:9</div>
</div>
<div class="form-item">
<label class="form-item__label">视频文件</label>
@@ -342,7 +343,8 @@
</div>
<span id="video-name" style="display:none;font-size:var(--font-sm);color:var(--text-primary);"></span>
</div>
<input type="file" id="video-file" accept="video/*" style="display:none;" onchange="onVideoChange(this)">
<input type="file" id="video-file" accept="video/mp4" style="display:none;" onchange="onVideoChange(this)">
<div class="form-item__hint">支持 MP4 格式,大小不超过 200MB,时长建议 10 分钟以内</div>
</div>
</div>
</div>
@@ -92,6 +92,11 @@
.form-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;}
.form-select:focus{border-color:var(--primary);}
/* 复选列表 */
.checkbox-list{border:1px solid var(--border);border-radius:var(--radius-sm);padding:4px 16px;}
.checkbox-item{display:flex;align-items:center;gap:8px;padding:9px 0;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;}
.checkbox-item + .checkbox-item{border-top:1px solid #F0F0F0;}
.checkbox-item input{accent-color:var(--primary);width:14px;height:14px;cursor:pointer;flex-shrink:0;}
</style>
</head>
<body>
@@ -296,6 +301,31 @@
</div>
</div>
<!-- 同步医院体检项目确认弹窗 -->
<div class="modal-overlay" id="sync-confirm-modal" onclick="if(event.target===this)closeSyncConfirmModal()">
<div class="modal">
<div class="modal__header">
<div class="modal__title">同步医院体检项目</div>
<button class="modal__close" onclick="closeSyncConfirmModal()"></button>
</div>
<div class="modal__body">
<div style="font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;">选择医院进行同步医院体检项目,结果可在查看同步任务中查看</div>
<div class="form-item">
<div class="form-label"><span class="required">*</span> 选择医院(可多选)</div>
<div class="checkbox-list" id="sync-hospital-list">
<label class="checkbox-item"><input type="checkbox" id="sync-hospital-all" onchange="toggleAllSyncHospitals(this)">全选</label>
<label class="checkbox-item"><input type="checkbox" value="泾河医院">泾河医院</label>
<label class="checkbox-item"><input type="checkbox" value="庆阳医院">庆阳医院</label>
</div>
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeSyncConfirmModal()">取消</button>
<button class="btn btn-primary" onclick="confirmSync()">确认同步</button>
</div>
</div>
</div>
<script>
/* 侧边栏展开/折叠交互 */
function toggleSubgroup(header){
@@ -395,16 +425,17 @@ var syncTasks = [
]}
];
/* 模拟一次同步拉取到的增量项目 */
function makeSyncItems(){
/* 模拟一次同步拉取到的增量项目(按所选医院过滤) */
function makeSyncItems(hospitals){
var pool = [
{ hospital: '泾河医院', code: 'JH-XM015', name: '糖类抗原199', basic: 'XM015(糖类抗原)', sys: '肿瘤标志物套餐', action: '新增' },
{ hospital: '泾河医院', code: 'JH-XM016', name: '甲胎蛋白', basic: 'XM016(甲胎蛋白)', sys: '肿瘤标志物套餐', action: '新增' },
{ hospital: '泾河医院', code: 'JH-XM001', name: '身高测量', basic: 'XM001(身高)', sys: '身高体重检测, 基础体检套餐', action: '更新' },
{ hospital: '庆阳医院', code: 'QY-XM008', name: '癌胚抗原', basic: 'XM008(癌胚抗原)', sys: '肿瘤标志物套餐', action: '新增' }
];
var n = 2 + Math.floor(Math.random() * 3);
return pool.slice(0, n);
var items = pool.filter(function(it){ return hospitals.indexOf(it.hospital) !== -1; });
var n = Math.min(items.length, 2 + Math.floor(Math.random() * 3));
return items.slice(0, n);
}
function nowTimeStr(){
@@ -456,9 +487,29 @@ function closeSyncDetailModal(){
document.getElementById('sync-detail-modal').classList.remove('open');
}
/* 打开同步确认弹窗 */
function syncHospitalItems(){
if(!confirm('是否从医院同步增量体检项目?')) return;
var task = { time: nowTimeStr(), user: '系统管理员', status: 'running', items: makeSyncItems() };
var boxes = document.querySelectorAll('#sync-hospital-list input[type=checkbox]');
boxes.forEach(function(c){ c.checked = false; });
document.getElementById('sync-confirm-modal').classList.add('open');
}
function closeSyncConfirmModal(){
document.getElementById('sync-confirm-modal').classList.remove('open');
}
function toggleAllSyncHospitals(master){
document.querySelectorAll('#sync-hospital-list input[type=checkbox]').forEach(function(c){
if(c.id !== 'sync-hospital-all') c.checked = master.checked;
});
}
/* 确认同步:校验医院后创建同步任务 */
function confirmSync(){
var selected = [];
document.querySelectorAll('#sync-hospital-list input[type=checkbox]').forEach(function(c){
if(c.checked && c.value) selected.push(c.value);
});
if(!selected.length){ alert('请选择医院'); return; }
closeSyncConfirmModal();
var task = { time: nowTimeStr(), user: '系统管理员', status: 'running', items: makeSyncItems(selected) };
syncTasks.unshift(task);
renderSyncTasks();
/* 模拟异步同步:2 秒后完成 */
@@ -1,5 +1,12 @@
# 健康体检模块 CHANGELOG
## [Unreleased] 2026-09-17
### Changed
- `post-unit-cost-stat.html`:单位体检费用统计列表增加"体检医院"列(部门列之后)并在"体检年份"后增加"签约医院"列(该单位签约的体检医院,可能存在多个,逗号相隔);因一个单位会存在多个体检医院,列表按"单位 × 体检医院"拆行——原 6 条单位年度汇总行拆为 16 条医院明细行(各分组内人数/报告/费用合计与原数据保持一致,计划费用仍按 2062.5 元/人 × 已体检人数口径),分页条数同步更新为 16 条,行配置 ROWS 补充 hospital/signed 字段供弹窗标题使用
- `post-unit-cost-stat.html`:列表去掉"总人数"列;"体检报告缺少体检项目信息"弹窗增加"项目费用(元)""折扣后费用(元)"两列(折扣后统一按 8 折示例);统计明细弹窗缺少项目口径的"项目总数"标蓝可点击,点击同样打开体检报告缺少体检项目信息弹窗;员工费用明细弹窗"报告缺少项目数"标蓝可点击(报告不完整时),点击同样打开体检报告缺少体检项目信息弹窗;员工费用明细弹窗顶部新增筛选栏(姓名/员工编号模糊查询、报告完整状态 全部/完整/不完整,查询/重置,打开弹窗时自动重置,无结果显示"暂无数据",合计行随筛选结果汇总)及"导出列表数据"按钮
- `PRD.md`:第 51 节"单位体检费用统计"功能说明改为按"单位 × 体检医院"统计(一行是一个体检医院),表格列说明同步补充"体检医院""签约医院"两列并去掉"总人数",统计明细弹窗/缺少项目弹窗说明同步更新
## [Unreleased] 2026-09-15
### Added
+5 -4
View File
@@ -785,6 +785,7 @@
**业务规则:**
- 添加为职业体检的员工,在移动端选择医院时,医院列表只显示支持职业体检的医院
- 导入和添加员工时,需判断当前员工是否已预约,如果已预约,则不能被添加
---
#### 22. 指定员工体检医院(pre-special-hospital.html
@@ -1297,7 +1298,7 @@
![页面截图](screenshots/post-unit-cost-stat.jpg)
**功能说明:** 按单位统计体检费用与报告完整性;最多可下钻 3 层弹窗:员工费用明细 → 单人应检/已检项目列表 或 报告缺少项目明细 → 员工缺少项目清单。
**功能说明:** 按"单位 × 体检医院"统计体检费用与报告完整性(一个单位会存在多个体检医院,一行是一个体检医院);最多可下钻 3 层弹窗:员工费用明细 → 单人应检/已检项目列表 或 报告缺少项目明细 → 员工缺少项目清单。
**Tab 切换:** 二级单位 / 三级单位(三级单位显示部门列)
@@ -1305,15 +1306,15 @@
**操作按钮:** 导出列表数据、导出员工明细、查看导出任务
**表格列:** 序号、单位、部门(仅三级单位 Tab)、总人数、已体检人数、报告完整人数、报告不完整人数、报告缺少项目总数、计划总体检费用(元)、实际总体检费用(元)、体检年份、操作(员工费用明细);已体检人数/报告完整人数/报告不完整人数/报告缺少项目总数 4 列为蓝色数字链接
**表格列:** 序号、单位、部门(仅三级单位 Tab)、体检医院、已体检人数、报告完整人数、报告不完整人数、报告缺少项目总数、计划总体检费用(元)、实际总体检费用(元)、体检年份、签约医院(该单位签约的体检医院,可能存在多个,逗号相隔,位于体检年份之后)、操作(员工费用明细);已体检人数/报告完整人数/报告不完整人数/报告缺少项目总数 4 列为蓝色数字链接
**员工费用明细弹窗:** 标题"「单位」YYYY年 员工费用明细";表格列:序号、姓名、员工编号、部门、体检日期、体检医院、应检项目数(蓝链)、已检项目数(蓝链,附灰色提示"含未检/弃检 N")、报告完整状态(绿"完整"/橙"不完整"标签)、报告缺少项目数、计划体检费用(元)、实际体检费用(元);底部合计行;关闭
**项目列表弹窗(560px,二层):** 标题"「姓名」应检项目/已检项目(YYYY年)";表头动态:序号、项目名称、项目类别、应检项目费用(元)/项目费用(元)、体检状态(绿"已检"/红"弃检"/橙"未检"标签);底部费用合计行。项目库为 4 档套餐,费用合计恰与额度档 1000/1500/2500/4000 元一致
**统计明细弹窗(三层):** 由主表 4 个数字链接打开,标题随口径(已体检人员明细/报告完整人员明细/报告不完整人员明细/报告缺少项目明细);前三种口径列:序号、姓名、员工编号、部门、体检日期、体检医院、已检、应检项目数、报告状态、(缺少项目数[蓝链]);缺少项目口径列:序号、姓名、员工编号、部门、体检医院、报告缺少项目、项目总数;底部"共 N 人"+ 关闭
**统计明细弹窗(三层):** 由主表 4 个数字链接打开,标题随口径(已体检人员明细/报告完整人员明细/报告不完整人员明细/报告缺少项目明细);前三种口径列:序号、姓名、员工编号、部门、体检日期、体检医院、已检、应检项目数、报告状态、(缺少项目数[蓝链]);缺少项目口径列:序号、姓名、员工编号、部门、体检医院、报告缺少项目、项目总数(蓝链,点击同样打开体检报告缺少体检项目信息弹窗);底部"共 N 人"+ 关闭
**体检报告缺少体检项目信息弹窗(四层):** 列:序号、项目名称、项目编码;底部"「姓名」共 N 项"+ 关闭
**体检报告缺少体检项目信息弹窗(四层):** 列:序号、项目名称、项目编码、项目费用(元)、折扣后费用(元)(折扣后费用统一按 8 折示例);底部"「姓名」共 N 项"+ 关闭
**业务规则:** 弃检项目费用≥240 标红;实际费用 = 应检合计 − 弃检/未检合计;金额千分位格式化;Esc/点遮罩逐层关闭(四层 z-index 递增)。
+7 -23
View File
@@ -84,9 +84,6 @@
.crumb-back:hover{background:#F5F5F5;color:var(--text-primary);}
.crumb-tag{display:inline-flex;align-items:center;height:28px;padding:0 12px;border-radius:var(--radius-sm);border:1px solid var(--border);font-size:var(--font-sm);color:var(--text-primary);background:#fff;}
.crumb-tag--active{color:var(--primary);border-color:#91Caff;background:#F0F7FF;}
/* 说明框 */
.desc-box{background:#FFFBE6;border:1px solid #FFE58F;border-radius:var(--radius-sm);padding:12px 16px;margin-bottom:16px;font-size:var(--font-sm);color:#7C5800;line-height:1.7;}
.desc-box strong{color:#5C3D00;}
/* 按钮行 */
.btn-row{display:flex;gap:12px;margin-bottom:16px;}
/* 体检年份选择控件 */
@@ -144,11 +141,6 @@
<span class="crumb-tag crumb-tag--active">结果异常</span>
</div>
<!-- 异常数据定义说明框 -->
<div class="desc-box">
<strong>异常数据定义:</strong>体检结果中存在指标超出正常参考范围的情形,包括:危急(指标严重偏离正常范围,需立即处理)、异常(指标超出参考范围但未达危急程度)等。系统自动检测并记录,供管理员跟进处理。
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
@@ -179,14 +171,6 @@
<span style="color:var(--text-secondary);font-size:var(--font-sm);"></span>
<input class="filter-input" type="date" style="min-width:140px;">
</div>
<div class="filter-item">
<label>异常等级:</label>
<select class="filter-select">
<option>全部</option>
<option>危急</option>
<option>异常</option>
</select>
</div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
@@ -213,16 +197,16 @@
<table>
<thead>
<tr>
<th>序号</th><th>异常等级</th><th>员工姓名</th><th>员工编号</th><th>单位</th><th>异常项目</th><th>异常值</th><th>参考范围</th><th>发现时间</th>
<th>序号</th><th>员工姓名</th><th>员工编号</th><th>单位</th><th>异常项目</th><th>异常值</th><th>参考范围</th><th>发现时间</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td><span class="tag tag-red">危急</span></td><td>张伟</td><td>CQ001234</td><td>采油一厂</td><td>空腹血糖</td><td>9.8 mmol/L</td><td>3.9~6.1 mmol/L</td><td>2026-05-01</td></tr>
<tr><td>2</td><td><span class="tag tag-red">危急</span></td><td>李明</td><td>CQ002571</td><td>采油二厂</td><td>血压(收缩压)</td><td>185 mmHg</td><td>90~140 mmHg</td><td>2026-05-01</td></tr>
<tr><td>3</td><td><span class="tag tag-orange">异常</span></td><td>刘洋</td><td>CQ004102</td><td>采油三厂</td><td>总胆固醇</td><td>7.2 mmol/L</td><td>2.8~5.7 mmol/L</td><td>2026-04-30</td></tr>
<tr><td>4</td><td><span class="tag tag-orange">异常</span></td><td>陈磊</td><td>CQ006890</td><td>采油一厂</td><td>肌酐</td><td>186 umol/L</td><td>44~133 umol/L</td><td>2026-04-30</td></tr>
<tr><td>5</td><td><span class="tag tag-orange">异常</span></td><td>周强</td><td>CQ008456</td><td>采油三厂</td><td>谷丙转氨酶</td><td>128 U/L</td><td>0~40 U/L</td><td>2026-04-29</td></tr>
<tr><td>6</td><td><span class="tag tag-red">危急</span></td><td>孙丽</td><td>CQ007234</td><td>采油二厂</td><td>心电图</td><td>ST段压低</td><td>正常心电图</td><td>2026-04-28</td></tr>
<tr><td>1</td><td>张伟</td><td>CQ001234</td><td>采油一厂</td><td>空腹血糖</td><td>9.8 mmol/L</td><td>3.9~6.1 mmol/L</td><td>2026-05-01</td></tr>
<tr><td>2</td><td>李明</td><td>CQ002571</td><td>采油二厂</td><td>血压(收缩压)</td><td>185 mmHg</td><td>90~140 mmHg</td><td>2026-05-01</td></tr>
<tr><td>3</td><td>刘洋</td><td>CQ004102</td><td>采油三厂</td><td>总胆固醇</td><td>7.2 mmol/L</td><td>2.8~5.7 mmol/L</td><td>2026-04-30</td></tr>
<tr><td>4</td><td>陈磊</td><td>CQ006890</td><td>采油一厂</td><td>肌酐</td><td>186 umol/L</td><td>44~133 umol/L</td><td>2026-04-30</td></tr>
<tr><td>5</td><td>周强</td><td>CQ008456</td><td>采油三厂</td><td>谷丙转氨酶</td><td>128 U/L</td><td>0~40 U/L</td><td>2026-04-29</td></tr>
<tr><td>6</td><td>孙丽</td><td>CQ007234</td><td>采油二厂</td><td>心电图</td><td>ST段压低</td><td>正常心电图</td><td>2026-04-28</td></tr>
</tbody>
</table>
</div>
@@ -304,7 +304,7 @@
<table>
<thead>
<tr>
<th rowspan="2" style="min-width:36px;"><input type="checkbox" id="checkAll" title="全选"></th><th rowspan="2">姓名</th><th rowspan="2">性别</th><th rowspan="2">年龄</th><th rowspan="2">年龄范围</th><th rowspan="2">身份证号</th><th rowspan="2">单位</th><th rowspan="2">部门</th><th rowspan="2">机构分类</th><th rowspan="2">员工编号</th><th rowspan="2">体检编码</th><th rowspan="2">用工形式</th><th rowspan="2">婚姻状态</th><th rowspan="2">体检类型</th><th rowspan="2">防癌项</th><th rowspan="2">是否上市员工</th><th rowspan="2">体检医院</th><th rowspan="2">体检预约信息</th><th rowspan="2">体检总费用</th><th colspan="4" style="text-align:center;">体检明细费用</th><th rowspan="2">预约状态</th><th rowspan="2">预约日期</th><th rowspan="2">体检状态</th><th rowspan="2">体检日期</th><th rowspan="2">体检年份</th><th rowspan="2">体检计划</th><th rowspan="2">操作</th>
<th rowspan="2" style="min-width:36px;"><input type="checkbox" id="checkAll" title="全选"></th><th rowspan="2">姓名</th><th rowspan="2">性别</th><th rowspan="2">年龄</th><th rowspan="2">年龄范围</th><th rowspan="2">身份证号</th><th rowspan="2">单位</th><th rowspan="2">部门</th><th rowspan="2">机构分类</th><th rowspan="2">员工编号</th><th rowspan="2">体检编码</th><th rowspan="2">用工形式</th><th rowspan="2">婚姻状态</th><th rowspan="2">体检类型</th><th rowspan="2">防癌项</th><th rowspan="2">是否上市员工</th><th rowspan="2">体检医院</th><th rowspan="2">体检总费用</th><th colspan="4" style="text-align:center;">体检明细费用</th><th rowspan="2">预约状态</th><th rowspan="2">体检预约信息</th><th rowspan="2">预约日期</th><th rowspan="2">体检状态</th><th rowspan="2">体检日期</th><th rowspan="2">体检年份</th><th rowspan="2">体检计划</th><th rowspan="2">操作</th>
</tr>
<tr>
<th>必检费用</th><th>选检费用</th><th>妇科费用</th><th>防癌筛查费用</th>
@@ -312,29 +312,29 @@
</thead>
<tbody>
<tr>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>王旭</td><td></td><td>41</td><td>40≤年龄&lt;45</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>子公司</td><td>342423</td><td>80038237</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>-</td><td></td><td>延安医院</td><td>已成功预约并更新项目</td><td class="fee-total">¥2,380.00</td>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>王旭</td><td></td><td>41</td><td>40≤年龄&lt;45</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>子公司</td><td>342423</td><td>80038237</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>-</td><td></td><td>延安医院</td><td class="fee-total">¥2,380.00</td>
<td class="fee-sub">¥1,200.00</td><td class="fee-sub">¥680.00</td><td class="fee-sub">-</td><td class="fee-sub">¥500.00</td>
<td><span class="tag tag-green">已预约</span></td><td>2026-03-10</td><td><span class="tag tag-blue">已体检</span></td><td>2026-03-12</td><td>2026</td><td>2026年度胃肠镜专项体检</td><td><span class="link">详情</span></td>
<td><span class="tag tag-green">已预约</span></td><td>已成功预约并更新项目</td><td>2026-03-10</td><td><span class="tag tag-blue">已体检</span></td><td>2026-03-12</td><td>2026</td><td>2026年度胃肠镜专项体检</td><td><span class="link">详情</span></td>
</tr>
<tr>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>张武</td><td></td><td>38</td><td>35≤年龄&lt;40</td><td>430626198806120037</td><td>公司机关</td><td>办公室</td><td>子公司</td><td>324321</td><td>80037100</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>Septin9</td><td></td><td>延安医院</td><td>体检预约成功</td><td class="fee-total">¥1,860.00</td>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>张武</td><td></td><td>38</td><td>35≤年龄&lt;40</td><td>430626198806120037</td><td>公司机关</td><td>办公室</td><td>子公司</td><td>324321</td><td>80037100</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>Septin9</td><td></td><td>延安医院</td><td class="fee-total">¥1,860.00</td>
<td class="fee-sub">¥1,100.00</td><td class="fee-sub">¥260.00</td><td class="fee-sub">-</td><td class="fee-sub">¥500.00</td>
<td><span class="tag tag-green">已预约</span></td><td>2026-03-10</td><td><span class="tag tag-blue">已体检</span></td><td>2026-03-11</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
<td><span class="tag tag-green">已预约</span></td><td>体检预约成功</td><td>2026-03-10</td><td><span class="tag tag-blue">已体检</span></td><td>2026-03-11</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
</tr>
<tr>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>李明</td><td></td><td>36</td><td>35≤年龄&lt;40</td><td>610402199003150045</td><td>第一采油厂</td><td>生产一队</td><td>三产公司</td><td>456789</td><td>80036211</td><td>劳务派遣</td><td>未婚</td><td>未婚体检</td><td>-</td><td></td><td>泾河医院</td><td>体检预约成功</td><td class="fee-total">¥1,560.00</td>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>李明</td><td></td><td>36</td><td>35≤年龄&lt;40</td><td>610402199003150045</td><td>第一采油厂</td><td>生产一队</td><td>三产公司</td><td>456789</td><td>80036211</td><td>劳务派遣</td><td>未婚</td><td>未婚体检</td><td>-</td><td></td><td>泾河医院</td><td class="fee-total">¥1,560.00</td>
<td class="fee-sub">¥1,100.00</td><td class="fee-sub">¥460.00</td><td class="fee-sub">-</td><td class="fee-sub">-</td>
<td><span class="tag tag-green">已预约</span></td><td>2026-03-15</td><td><span class="tag tag-blue">体检中</span></td><td>2026-03-16</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
<td><span class="tag tag-green">已预约</span></td><td>体检预约成功</td><td>2026-03-15</td><td><span class="tag tag-blue">体检中</span></td><td>2026-03-16</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
</tr>
<tr>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>赵芳</td><td></td><td>45</td><td>年龄≥45</td><td>610402198109230028</td><td>第二采油厂</td><td>地质研究所</td><td>子公司</td><td>512336</td><td>80035102</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>两项都有</td><td></td><td>-</td><td>-</td><td class="fee-total">¥1,920.00</td>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>赵芳</td><td></td><td>45</td><td>年龄≥45</td><td>610402198109230028</td><td>第二采油厂</td><td>地质研究所</td><td>子公司</td><td>512336</td><td>80035102</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>两项都有</td><td></td><td>-</td><td class="fee-total">¥1,920.00</td>
<td class="fee-sub">¥1,100.00</td><td class="fee-sub">¥320.00</td><td class="fee-sub">¥350.00</td><td class="fee-sub">¥150.00</td>
<td><span class="tag tag-orange">未预约</span></td><td>-</td><td><span class="tag tag-orange">未体检</span></td><td>-</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
<td><span class="tag tag-orange">未预约</span></td><td>-</td><td>-</td><td><span class="tag tag-orange">未体检</span></td><td>-</td><td>2026</td><td>2026年度职工常规体检</td><td><span class="link">详情</span></td>
</tr>
<tr>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>陈强</td><td></td><td>52</td><td>年龄≥45</td><td>610402197407080011</td><td>第三采油厂</td><td>集输大队</td><td>三产公司</td><td>601178</td><td>80034065</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>CA724</td><td></td><td>市中心医院</td><td>已取消预约</td><td class="fee-total">¥2,380.00</td>
<td class="row-check"><input type="checkbox" class="row-checkbox"></td><td>陈强</td><td></td><td>52</td><td>年龄≥45</td><td>610402197407080011</td><td>第三采油厂</td><td>集输大队</td><td>三产公司</td><td>601178</td><td>80034065</td><td>正式合同工</td><td>已婚</td><td>已婚体检</td><td>CA724</td><td></td><td>市中心医院</td><td class="fee-total">¥2,380.00</td>
<td class="fee-sub">¥1,200.00</td><td class="fee-sub">¥680.00</td><td class="fee-sub">-</td><td class="fee-sub">¥500.00</td>
<td><span class="tag tag-red">已取消预约</span></td><td>2026-03-08</td><td><span class="tag tag-orange">未体检</span></td><td>-</td><td>2026</td><td>2026年度胃肠镜专项体检</td><td><span class="link">详情</span></td>
<td><span class="tag tag-red">已取消预约</span></td><td>已取消预约</td><td>2026-03-08</td><td><span class="tag tag-orange">未体检</span></td><td>-</td><td>2026</td><td>2026年度胃肠镜专项体检</td><td><span class="link">详情</span></td>
</tr>
</tbody>
</table>
@@ -461,9 +461,9 @@ document.querySelectorAll('tbody .link:not(.edit-link)').forEach(function(link){
set('unit',txt(6));set('dept',txt(7));set('orgType',txt(8));set('examCode',txt(10));
set('empType',txt(11));set('marriage',txt(12));
set('examType',txt(13));set('cancer',txt(14));set('listed',txt(15));
set('hospital',txt(16));set('bookInfo',txt(17));set('feeTotal',txt(18));
set('fee1',txt(19));set('fee2',txt(20));set('fee3',txt(21));set('fee4',txt(22));
var bookStatus=txt(23);set('bookDate',txt(24));
set('hospital',txt(16));set('feeTotal',txt(17));
set('fee1',txt(18));set('fee2',txt(19));set('fee3',txt(20));set('fee4',txt(21));
var bookStatus=txt(22);set('bookInfo',txt(23));set('bookDate',txt(24));
var examStatus=txt(25);set('examDate',txt(26));
set('year',txt(27));set('plan',txt(28));
var bt=document.querySelector('[data-f="bookTag"]');
@@ -201,15 +201,15 @@
<table>
<thead>
<tr>
<th rowspan="2">序号</th><th rowspan="2">单位</th><th rowspan="2" class="col-dept">部门</th><th rowspan="2">应体检人数</th><th rowspan="2">填写问卷人数</th><th colspan="3" style="text-align:center;">已预约情况</th><th rowspan="2">未预约人数</th><th rowspan="2">体检年份</th>
<th rowspan="2">序号</th><th rowspan="2">单位</th><th rowspan="2" class="col-dept">部门</th><th rowspan="2">应体检人数</th><th colspan="3" style="text-align:center;">已预约情况</th><th rowspan="2">未预约人数</th><th rowspan="2">体检年份</th>
</tr>
<tr>
<th>已预约人数</th><th>已参检人数</th><th>未参检人数</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>5200</td><td>3600</td><td>4000</td><td>3000</td><td>1000</td><td>1000</td><td>2025</td></tr>
<tr><td>2</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>5200</td><td>3600</td><td>4000</td><td>3000</td><td>1000</td><td>1000</td><td>2025</td></tr>
<tr><td>1</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>5200</td><td>4000</td><td>3000</td><td>1000</td><td>1000</td><td>2025</td></tr>
<tr><td>2</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>5200</td><td>4000</td><td>3000</td><td>1000</td><td>1000</td><td>2025</td></tr>
</tbody>
</table>
</div>
@@ -253,14 +253,15 @@ function toggleSubgroup(header){
if(sub)sub.classList.add('open');
})();
/* 日期范围默认当 */
/* 日期范围默认当日到30日后 */
(function(){
function pad(n){return n<10?'0'+n:''+n;}
var now=new Date();
var first=now.getFullYear()+'-'+pad(now.getMonth()+1)+'-01';
var last=now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(new Date(now.getFullYear(),now.getMonth()+1,0).getDate());
document.getElementById('filter-date-start').value=first;
document.getElementById('filter-date-end').value=last;
function fmt(d){return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate());}
var start=new Date();
var end=new Date();
end.setDate(end.getDate()+30);
document.getElementById('filter-date-start').value=fmt(start);
document.getElementById('filter-date-end').value=fmt(end);
})();
/* 体检年份选择控件 */
function pickYear(el){
@@ -95,12 +95,17 @@
.report-note{border-radius:var(--radius-sm);padding:12px 16px;font-size:var(--font-sm);line-height:1.9;margin-bottom:16px;}
.report-note--conclusion{border:1px solid #FFD591;background:#FFF7E6;color:#873800;}
.report-note--advice{border:1px solid #B7EB8F;background:#F6FFED;color:#389E0D;}
.report-section{margin-bottom:24px;}
.report-section:last-child{margin-bottom:0;}
.report-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:12px;line-height:1.4;}
.report-sub__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin:14px 0 8px;}
.report-sub:first-of-type .report-sub__title{margin-top:0;}
.result-abnormal{color:var(--danger);font-weight:600;}
/* 专业报告版式(与个人体检数据-报告弹窗一致) */
.cta-rpt{border:1px solid var(--border);border-radius:var(--radius-md);padding:0;overflow:hidden;}
.cta-rpt__band{background:#001529;color:#fff;padding:14px 20px;font-size:var(--font-base);font-weight:600;display:flex;align-items:center;justify-content:space-between;}
.cta-rpt__meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px 20px;padding:14px 20px;border-bottom:1px dashed var(--border);font-size:var(--font-sm);}
.cta-rpt__meta b{color:var(--text-secondary);font-weight:400;}
.cta-rpt__sec{padding:14px 20px;border-bottom:1px solid var(--border);}
.cta-rpt__sec:last-child{border-bottom:none;}
.cta-rpt__sec-t{font-size:var(--font-sm);font-weight:600;color:var(--primary);margin-bottom:8px;}
/* 体检年份选择控件 */
.year-picker{position:relative;}
.year-picker .filter-input{padding-right:30px;cursor:pointer;}
@@ -244,18 +249,22 @@
<!-- 操作栏 -->
<div style="display:flex;flex-wrap:wrap;gap:12px;margin-bottom:16px;">
<button class="btn btn-primary">
<button class="btn btn-primary" onclick="updateReportData()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M11.5 4A4.5 4.5 0 0 0 3.2 2.8M2.5 10a4.5 4.5 0 0 0 8.3 1.2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><path d="M11.5 1.5v2.5H9M2.5 12.5V10h2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
更新体检报告数据
</button>
<button class="btn btn-primary">
<button class="btn btn-primary" onclick="openSuppModal()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.4"/><path d="M7 4.5v5M4.5 7h5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
体检报告补录
</button>
<button class="btn btn-primary">
<button class="btn btn-primary" onclick="fetchImageReport()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="9" rx="1.5" stroke="currentColor" stroke-width="1.4"/><circle cx="5" cy="5.5" r="1" fill="currentColor"/><path d="M1.5 9.5l3-2.5 2.5 2L9.5 6l3 3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
获取影像报告
</button>
<button class="btn btn-default">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 9V2M4.5 4.5L7 2l2.5 2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
报告模板导入
</button>
<button class="btn btn-default">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导出详细报告
@@ -275,12 +284,12 @@
<table>
<thead>
<tr>
<th>序号</th><th>姓名</th><th>员工编号</th><th>性别</th><th>年龄</th><th>身份证号</th><th>单位</th><th>部门</th><th>体检医院</th><th>体检类型</th><th>体检日期</th><th>终审日期</th><th>更新日期</th><th>是否已读</th><th>已读时间</th><th>操作</th>
<th style="width:40px;"><input type="checkbox" id="report-check-all" onchange="toggleAllReport(this)" title="全选"></th><th>序号</th><th>姓名</th><th>员工编号</th><th>性别</th><th>年龄</th><th>身份证号</th><th>单位</th><th>部门</th><th>体检医院</th><th>体检类型</th><th>体检日期</th><th>终审日期</th><th>更新日期</th><th>是否已读</th><th>已读时间</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>王旭</td><td>342423</td><td></td><td>41</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>延安医院</td><td>常规体检</td><td>2026-03-10</td><td>2026-03-10</td><td>2026-03-10</td><td></td><td>2025-06-06</td><td><span class="link" onclick="openReport('王旭')">查看报告</span><span class="link" style="margin-left:12px;">查看影像</span><span class="link" style="margin-left:12px;">下载报告</span></td></tr>
<tr><td>2</td><td>张丽娟</td><td>324321</td><td></td><td>36</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>延安医院</td><td>胃肠镜体检</td><td>2026-03-10</td><td>2026-03-10</td><td>2026-03-10</td><td></td><td>-</td><td><span class="link" onclick="openReport('张丽娟')">查看报告</span><span class="link" style="margin-left:12px;">查看影像</span><span class="link" style="margin-left:12px;">下载报告</span></td></tr>
<tr><td><input type="checkbox" class="report-checkbox" data-name="王旭"></td><td>1</td><td>王旭</td><td>342423</td><td></td><td>41</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>延安医院</td><td>常规体检</td><td>2026-03-10</td><td>2026-03-10</td><td>2026-03-10</td><td></td><td>2025-06-06</td><td><span class="link" onclick="openReport(0)">查看报告</span><span class="link" style="margin-left:12px;">查看影像</span><span class="link" style="margin-left:12px;">下载报告</span></td></tr>
<tr><td><input type="checkbox" class="report-checkbox" data-name="张丽娟"></td><td>2</td><td>张丽娟</td><td>324321</td><td></td><td>36</td><td>430626198502560029</td><td>公司机关</td><td>办公室</td><td>延安医院</td><td>胃肠镜体检</td><td>2026-03-10</td><td>2026-03-10</td><td>2026-03-10</td><td></td><td>-</td><td><span class="link" onclick="openReport(1)">查看报告</span><span class="link" style="margin-left:12px;">查看影像</span><span class="link" style="margin-left:12px;">下载报告</span></td></tr>
</tbody>
</table>
</div>
@@ -298,19 +307,22 @@
<button class="modal__close" onclick="closeReport()"></button>
</div>
<div class="modal__body">
<div class="cta-rpt">
<div class="cta-rpt__band">健康体检报告</div>
<div class="cta-rpt__meta" id="report-meta"></div>
<!-- 体检结论 -->
<div class="report-section">
<div class="report-section__title">体检结论</div>
<div class="report-note report-note--conclusion">1. 血脂异常:甘油三酯轻度偏高;<br>2. 视力疲劳:双眼裸眼视力下降;<br>3. 其余各项体检指标未见明显异常。</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">体检结论</div>
<div class="report-note report-note--conclusion" style="margin-bottom:0;">1. 血脂异常:甘油三酯轻度偏高;<br>2. 视力疲劳:双眼裸眼视力下降;<br>3. 其余各项体检指标未见明显异常。</div>
</div>
<!-- 体检建议 -->
<div class="report-section">
<div class="report-section__title">体检建议</div>
<div class="report-note report-note--advice">1. 建议低脂清淡饮食,少油少糖,规律运动,3个月后复查血脂;<br>2. 注意用眼卫生,避免长时间使用电子屏幕,必要时眼科随诊;<br>3. 建议每年进行一次健康体检,动态关注各项指标变化。</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">体检建议</div>
<div class="report-note report-note--advice" style="margin-bottom:0;">1. 建议低脂清淡饮食,少油少糖,规律运动,3个月后复查血脂;<br>2. 注意用眼卫生,避免长时间使用电子屏幕,必要时眼科随诊;<br>3. 建议每年进行一次健康体检,动态关注各项指标变化。</div>
</div>
<!-- 物理检查 -->
<div class="report-section">
<div class="report-section__title">物理检查</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">物理检查</div>
<div class="report-sub">
<div class="report-sub__title">一般检查</div>
<table>
@@ -336,8 +348,8 @@
</div>
</div>
<!-- 内科 -->
<div class="report-section">
<div class="report-section__title">内科</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">内科</div>
<div class="report-sub">
<div class="report-sub__title">心肺听诊</div>
<table>
@@ -361,8 +373,8 @@
</div>
</div>
<!-- 外科 -->
<div class="report-section">
<div class="report-section__title">外科</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">外科</div>
<div class="report-sub">
<div class="report-sub__title">皮肤与淋巴结</div>
<table>
@@ -385,8 +397,8 @@
</div>
</div>
<!-- 化学检查 -->
<div class="report-section">
<div class="report-section__title">化学检查</div>
<div class="cta-rpt__sec">
<div class="cta-rpt__sec-t">化学检查</div>
<div class="report-sub">
<div class="report-sub__title">血常规</div>
<table>
@@ -431,24 +443,168 @@
</table>
</div>
</div>
</div>
</div>
<div class="modal__footer">
<button class="btn btn-primary" onclick="closeReport()">关闭</button>
</div>
</div>
</div>
<!-- 体检报告补录弹窗 -->
<div class="modal-overlay" id="supp-modal" onclick="if(event.target===this)closeSuppModal()">
<div class="modal" style="width:480px;">
<div class="modal__header">
<div class="modal__title">体检报告补录</div>
<button class="modal__close" onclick="closeSuppModal()"></button>
</div>
<div class="modal__body">
<div style="display:flex;flex-direction:column;gap:14px;">
<div style="display:flex;align-items:center;gap:12px;">
<label style="width:80px;font-size:var(--font-sm);color:var(--text-secondary);flex-shrink:0;">体检医院</label>
<select class="filter-select" id="supp-hospital" style="flex:1;min-width:0;">
<option value="">请选择体检医院</option>
<option>延安医院</option>
<option>泾河医院</option>
<option>庆阳医院</option>
<option>市中心医院</option>
</select>
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="width:80px;font-size:var(--font-sm);color:var(--text-secondary);flex-shrink:0;">身份证号</label>
<input class="filter-input" id="supp-idno" type="text" placeholder="请输入身份证号" style="flex:1;min-width:0;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="width:80px;font-size:var(--font-sm);color:var(--text-secondary);flex-shrink:0;">体检编号</label>
<input class="filter-input" id="supp-examno" type="text" placeholder="请输入体检编号" style="flex:1;min-width:0;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="width:80px;font-size:var(--font-sm);color:var(--text-secondary);flex-shrink:0;">体检年份</label>
<div class="year-picker" id="supp-year-picker">
<input class="filter-input" id="supp-year" type="text" readonly style="min-width:90px;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div id="supp-hint" style="font-size:var(--font-xs);color:var(--text-secondary);">身份证号与体检编号必须至少填写一项</div>
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeSuppModal()">取消</button>
<button class="btn btn-primary" onclick="confirmSupp()">确定</button>
</div>
</div>
</div>
<!-- 多位员工操作确认弹窗 -->
<div class="modal-overlay" id="emp-confirm-modal" onclick="if(event.target===this)closeEmpConfirm()">
<div class="modal" style="width:420px;">
<div class="modal__header">
<div class="modal__title" id="emp-confirm-title">操作确认</div>
<button class="modal__close" onclick="closeEmpConfirm()"></button>
</div>
<div class="modal__body">
<div id="emp-confirm-text" style="font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:12px;"></div>
<div id="emp-confirm-names" style="font-size:var(--font-sm);color:var(--text-primary);line-height:1.8;"></div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeEmpConfirm()">取消</button>
<button class="btn btn-primary" onclick="confirmEmpOk()">确定</button>
</div>
</div>
</div>
</main>
<script>
/* 查看报告弹窗 */
function openReport(name){
document.getElementById('report-title').textContent=name+' · 常规体检报告';
/* 查看报告弹窗(专业报告版式,与个人体检数据-常规体检tab一致) */
var REPORT_ROWS=[
{name:'王旭',gender:'男',age:41,empNo:'342423',checkDate:'2026-03-10',hospital:'延安医院'},
{name:'张丽娟',gender:'女',age:36,empNo:'324321',checkDate:'2026-03-10',hospital:'延安医院'}
];
function openReport(idx){
var r=REPORT_ROWS[idx];
document.getElementById('report-title').textContent=r.name+' · 常规体检报告';
document.getElementById('report-meta').innerHTML=
'<span><b>姓名:</b>'+r.name+'</span><span><b>性别:</b>'+r.gender+'</span><span><b>年龄:</b>'+r.age+'岁</span>'
+'<span><b>体检编号:</b>'+r.empNo+'</span><span><b>体检日期:</b>'+r.checkDate+'</span><span><b>体检医院:</b>'+r.hospital+'</span>';
document.getElementById('report-modal').classList.add('open');
}
function closeReport(){
document.getElementById('report-modal').classList.remove('open');
}
/* 体检报告补录弹窗 */
function openSuppModal(){
document.getElementById('supp-hospital').value='';
document.getElementById('supp-idno').value='';
document.getElementById('supp-examno').value='';
/* 体检年份默认当年 */
var y=String(new Date().getFullYear());
document.getElementById('supp-year').value=y;
document.querySelectorAll('#supp-year-picker .year-picker__dropdown div').forEach(function(d){
d.classList.toggle('selected', d.textContent.trim()===y);
});
suppHintReset();
document.getElementById('supp-modal').classList.add('open');
}
function closeSuppModal(){
document.getElementById('supp-modal').classList.remove('open');
}
function suppHintReset(){
var h=document.getElementById('supp-hint');
h.style.color='var(--text-secondary)';
h.textContent='身份证号与体检编号必须至少填写一项';
}
function confirmSupp(){
var idno=document.getElementById('supp-idno').value.trim();
var examno=document.getElementById('supp-examno').value.trim();
var h=document.getElementById('supp-hint');
if(!idno && !examno){
h.style.color='var(--danger)';
h.textContent='身份证号与体检编号不能都为空,请至少填写一项';
return;
}
closeSuppModal();
}
/* ===== 员工选择校验(更新报告数据 / 获取影像报告) ===== */
function toggleAllReport(master){
document.querySelectorAll('.report-checkbox').forEach(function(cb){ cb.checked = master.checked; });
}
function getCheckedNames(){
var names=[];
document.querySelectorAll('.report-checkbox:checked').forEach(function(cb){ names.push(cb.getAttribute('data-name')); });
return names;
}
/* 至少选择一位员工;无论几位都弹窗确认已选员工 */
function checkEmployees(action, onOk){
var names = getCheckedNames();
if(!names.length){ alert('请先选择至少一位员工'); return; }
document.getElementById('emp-confirm-title').textContent = action + ' - 确认员工';
document.getElementById('emp-confirm-text').textContent = '已选择 ' + names.length + ' 位员工,请确认:';
document.getElementById('emp-confirm-names').textContent = names.join('、');
empConfirmCb = onOk;
document.getElementById('emp-confirm-modal').classList.add('open');
}
var empConfirmCb = null;
function closeEmpConfirm(){
document.getElementById('emp-confirm-modal').classList.remove('open');
empConfirmCb = null;
}
function confirmEmpOk(){
var cb = empConfirmCb;
closeEmpConfirm();
if(cb) cb();
}
function updateReportData(){
checkEmployees('更新体检报告数据', function(){ alert('已提交更新体检报告数据(原型演示)'); });
}
function fetchImageReport(){
checkEmployees('获取影像报告', function(){ alert('已提交获取影像报告(原型演示)'); });
}
/* 侧边栏展开/折叠交互 */
function toggleSubgroup(header){
header.parentElement.classList.toggle('open');
+120 -77
View File
@@ -122,14 +122,7 @@
.modal__close:hover{background:#F5F5F5;}
.modal__body{flex:1;overflow-y:auto;padding:24px;}
.modal__footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid var(--border);flex-shrink:0;}
/* 弹窗区块 */
.report-section{margin-bottom:24px;}
.report-section:last-child{margin-bottom:0;}
.report-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:12px;line-height:1.4;}
.info-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px 24px;}
.info-grid .info-label{font-size:var(--font-sm);color:var(--text-placeholder);}
.info-grid .info-value{font-size:var(--font-sm);color:var(--text-primary);margin-top:4px;}
/* 详情弹窗 */
/* 编辑弹窗 */
.dform{display:grid;grid-template-columns:1fr 1fr;gap:20px 32px;}
.dform__item{display:flex;align-items:flex-start;}
.dform__label{width:104px;flex:none;text-align:right;padding-right:8px;font-size:var(--font-sm);color:var(--text-primary);line-height:34px;white-space:nowrap;}
@@ -152,21 +145,23 @@
.dtab-pane.active{display:block;}
.dtab-pane td.res{max-width:460px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dempty{padding:48px 0;text-align:center;font-size:var(--font-sm);color:var(--text-placeholder);}
/* 报告大图 */
.report-large{width:380px;height:230px;border:1px solid var(--border);border-radius:4px;background:#fff;padding:16px 20px;}
.report-large__imgs{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;}
.report-large__imgs i{width:100%;aspect-ratio:1/1;border-radius:50%;background:radial-gradient(circle at 35% 32%,#F3B07C,#B45A28);}
.report-large__imgs i:nth-child(2n){background:radial-gradient(circle at 40% 35%,#E89A5F,#9C4E1F);}
.report-large__lines{margin-top:14px;}
.report-large__lines i{display:block;height:5px;border-radius:3px;background:#EAEAEA;}
.report-large__lines i+i{margin-top:8px;}
/* 编辑上传 */
.upload-row{display:flex;gap:16px;align-items:flex-start;}
.upload-box{width:380px;height:230px;border:1px dashed #D9D9D9;border-radius:4px;background:#FAFAFA;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--text-placeholder);font-size:var(--font-sm);cursor:pointer;transition:all .2s;}
.upload-box:hover{border-color:var(--primary);color:var(--primary);}
.upload-box--filled{border-style:solid;background:#fff;cursor:default;padding:12px 14px;}
.upload-box--filled .report-large{cursor:zoom-in;}
/* 编辑态图片操作 */
.upload-box__ops{display:flex;gap:12px;}
/* 查看报告弹窗(专业报告版式,与个人体检数据-胃肠镜体检tab一致) */
.cta-rpt{border:1px solid var(--border);border-radius:var(--radius-md);padding:0;overflow:hidden;}
.cta-rpt__band{background:#001529;color:#fff;padding:14px 20px;font-size:var(--font-base);font-weight:600;display:flex;align-items:center;justify-content:space-between;}
.cta-rpt__meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px 20px;padding:14px 20px;border-bottom:1px dashed var(--border);font-size:var(--font-sm);}
.cta-rpt__meta b{color:var(--text-secondary);font-weight:400;}
.cta-rpt__sec{padding:14px 20px;border-bottom:1px solid var(--border);}
.cta-rpt__sec:last-child{border-bottom:none;}
.cta-rpt__sec-t{font-size:var(--font-sm);font-weight:600;color:var(--primary);margin-bottom:8px;}
.cta-vessel{width:100%;border-collapse:collapse;}
.cta-vessel th,.cta-vessel td{border:1px solid var(--border);padding:8px 10px;font-size:var(--font-sm);text-align:left;vertical-align:top;}
.cta-vessel th{background:#FAFAFA;color:var(--text-secondary);font-weight:500;white-space:nowrap;}
.cta-vessel td.v-name{white-space:nowrap;font-weight:500;}
.cta-imgs{display:flex;gap:12px;flex-wrap:wrap;}
.cta-img{display:inline-flex;align-items:center;gap:8px;padding:7px 12px;border:1px dashed var(--border);border-radius:var(--radius-sm);background:#FAFAFA;font-size:var(--font-sm);color:var(--text-secondary);}
.cta-img .link{font-size:var(--font-xs);}
/* 轻提示 */
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-16px);background:#fff;box-shadow:var(--shadow-md);border-radius:var(--radius-sm);padding:10px 20px;font-size:var(--font-sm);color:var(--text-primary);opacity:0;pointer-events:none;transition:all .25s;z-index:1000;display:flex;align-items:center;gap:8px;}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
@@ -373,16 +368,16 @@
</div>
</div>
<!-- 详情弹窗 -->
<div class="modal-overlay" id="detail-modal" onclick="if(event.target===this)closeModal('detail-modal')">
<!-- 查看报告弹窗(专业报告版式,与个人体检数据-胃肠镜体检tab一致) -->
<div class="modal-overlay" id="report-modal" onclick="if(event.target===this)closeModal('report-modal')">
<div class="modal modal--wide">
<div class="modal__header">
<div class="modal__title" id="detail-title">报告详情</div>
<button class="modal__close" onclick="closeModal('detail-modal')"></button>
<div class="modal__title" id="report-title">胃肠镜检查报告</div>
<button class="modal__close" onclick="closeModal('report-modal')"></button>
</div>
<div class="modal__body" id="detail-body"></div>
<div class="modal__body" id="report-body"></div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal('detail-modal')">关闭</button>
<button class="btn btn-default" onclick="closeModal('report-modal')">关闭</button>
</div>
</div>
</div>
@@ -443,7 +438,7 @@ var editIdx=-1,editDraft=null;
/* ============ 表格渲染 ============ */
function visibleCols(){return cols.filter(function(c){return c.fixed||!c.hidden;});}
function thumbHtml(has,idx,key,label){
if(has)return '<div class="report-thumb" title="点击查看大图" onclick="openDetail('+idx+')">'
if(has)return '<div class="report-thumb" title="点击查看报告" onclick="openReport('+idx+')">'
+'<div class="report-thumb__imgs"><i></i><i></i><i></i><i></i></div>'
+'<div class="report-thumb__lines"><i style="width:92%"></i><i style="width:70%"></i><i style="width:82%"></i><i style="width:55%"></i></div></div>';
return '<div class="report-thumb report-thumb--empty" title="暂无'+label+'图片,点击编辑上传" onclick="openEdit('+idx+')">'
@@ -451,7 +446,7 @@ function thumbHtml(has,idx,key,label){
}
function cellHtml(c,r,idx){
if(c.type==='thumb')return thumbHtml(r[c.key],idx,c.key,c.label);
if(c.key==='op')return '<span class="link" onclick="openEdit('+idx+')">编辑</span><span class="link" style="margin-left:14px;" onclick="openDetail('+idx+')">详情</span>';
if(c.key==='op')return '<span class="link" onclick="openEdit('+idx+')">编辑</span><span class="link" style="margin-left:14px;" onclick="openReport('+idx+')">查看报告</span>';
return r[c.key];
}
function renderTable(){
@@ -534,7 +529,7 @@ function toggleColPop(e){
renderColPop();
pop.classList.add('open');
}
/* ============ 详情弹窗 ============ */
/* ============ 体检项目明细(编辑弹窗-体检项目页签数据) ============ */
function detailItems(r){
var items=[];
if(r.gastro){
@@ -547,18 +542,26 @@ function detailItems(r){
}
return items;
}
function openDetail(idx){
var r=rows[idx];
document.getElementById('detail-title').textContent=r.name+' · 胃肠镜报告详情';
/* 图片预览块(编辑表单用) */
function pickHtml(has){
if(!has)return '<div class="dpick dpick--empty">暂无图片</div>';
return '<div class="dpick"><div class="dpick__imgs"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>'
+'<div class="dpick__lines"><i style="width:94%"></i><i style="width:82%"></i><i style="width:60%"></i></div></div>';
}
/* 编辑态图片块:预览+操作链接 */
function editPickHtml(has,field){
var ops='<div class="upload-box__ops" style="margin-top:8px;">'+(has
?'<span class="link" onclick="fakeUpload(\''+field+'\')">重新上传</span><span class="link" style="color:#FF4D4F;" onclick="removeUpload(\''+field+'\')">删除</span>'
:'<span class="link" onclick="fakeUpload(\''+field+'\')">上传图片</span>')+'</div>';
return pickHtml(has)+ops;
}
/* 编辑弹窗主体(仅报告图片可修改) */
function reportBodyHtml(r){
var caret='<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 4.5L6 7.5 9 4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var cal='<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>';
var box=function(v,extra,cls){return '<div class="dbox'+(cls?' '+cls:'')+'"><span>'+v+'</span>'+(extra||'')+'</div>';};
var pick=function(has){
if(!has)return '<div class="dpick dpick--empty">暂无图片</div>';
return '<div class="dpick"><div class="dpick__imgs"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>'
+'<div class="dpick__lines"><i style="width:94%"></i><i style="width:82%"></i><i style="width:60%"></i></div></div>';
};
var item=function(label,html,req){return '<div class="dform__item"><div class="dform__label">'+(req?'<span class="req">*</span>':'')+label+'</div><div class="dform__box">'+html+'</div></div>';};
var img=function(has,field){return '<div id="edit-'+field+'">'+editPickHtml(has,field)+'</div>';};
var its=detailItems(r);
var tabRows=its.length?its.map(function(t,i){
return '<tr><td>'+(i+1)+'</td><td>'+t.cat+'</td><td>'+t.no+'</td><td>'+t.name+'</td><td class="res" title="'+t.res+'">'+t.res+'</td></tr>';
@@ -581,77 +584,117 @@ function openDetail(idx){
{no:'LE01.01',name:'胃黏膜糜烂',res:'1'},
{no:'LE01.02',name:'胃黏膜萎缩',res:'1'}
]:[];
document.getElementById('detail-body').innerHTML=
'<div class="dform">'
return '<div class="dform">'
+item('姓名',box(r.name))
+item('身份证号',box(r.idCard))
+item('单位',box(r.unit,caret))
+item('部门',box(r.dept,caret))
+item('承检医院',box(r.hospital))
+item('体检年份',box(r.year,cal,'dbox--year'))
+item('胃镜报告图片',pick(r.gastro),true)
+item('肠镜报告图片',pick(r.colon),true)
+item('活检报告图片',pick(false))
+item('胃镜报告图片',img(r.gastro,'gastro'),true)
+item('肠镜报告图片',img(r.colon,'colon'),true)
+item('活检报告图片',img(!!r.bx,'bx'))
+'</div>'
+'<div class="dtabs">'
+'<div class="dtabs__item active" data-tab="items" onclick="switchDetailTab(\'items\')">体检项目</div>'
+'<div class="dtabs__item" data-tab="disease" onclick="switchDetailTab(\'disease\')">病种勾选</div>'
+'<div class="dtabs__item" data-tab="handle" onclick="switchDetailTab(\'handle\')">体检处理情况</div>'
+'<div class="dtabs__item" data-tab="lesion" onclick="switchDetailTab(\'lesion\')">病变情况</div>'
+'<div class="dtabs__item active" data-tab="items" onclick="switchDetailTab(this,\'items\')">体检项目</div>'
+'<div class="dtabs__item" data-tab="disease" onclick="switchDetailTab(this,\'disease\')">病种勾选</div>'
+'<div class="dtabs__item" data-tab="handle" onclick="switchDetailTab(this,\'handle\')">体检处理情况</div>'
+'<div class="dtabs__item" data-tab="lesion" onclick="switchDetailTab(this,\'lesion\')">病变情况</div>'
+'</div>'
+'<div class="dtab-pane active" data-tab="items"><table style="table-layout:fixed;"><thead><tr><th style="width:56px;">#</th><th style="width:120px;">检查类别</th><th style="width:130px;">项目编号</th><th style="width:160px;">项目名称</th><th>结果</th></tr></thead><tbody>'+tabRows+'</tbody></table></div>'
+'<div class="dtab-pane" data-tab="disease">'+checkTable(diseases)+'</div>'
+'<div class="dtab-pane" data-tab="handle">'+checkTable(handles)+'</div>'
+'<div class="dtab-pane" data-tab="lesion">'+checkTable(lesions)+'</div>';
document.getElementById('detail-modal').classList.add('open');
}
function switchDetailTab(key){
var tabs=document.querySelectorAll('#detail-modal .dtabs__item');
/* ============ 查看报告弹窗(内容与样式参考个人体检数据-胃肠镜体检tab ============ */
var GASTRO_PROFILES=[
{esophagus:'食管黏膜光滑,色泽正常,血管纹理清晰,未见静脉曲张及占位性病变。贲门开闭良好。',
stomach:'胃底、胃体黏膜光滑,色泽橘红,黏液湖清亮。胃角切迹光滑,胃窦黏膜轻度充血水肿,未见糜烂及溃疡。幽门口圆,开闭良好。',
duodenum:'十二指肠球部及降部黏膜光滑,未见溃疡、糜烂及占位性病变。乳头形态正常。',
gastroDx:'慢性非萎缩性胃炎(胃窦)',
colon:'全结肠黏膜光滑,血管纹理清晰,未见充血、水肿、糜烂、溃疡及占位性病变。回盲瓣呈唇形,阑尾开口形态正常。结肠袋形态规整,蠕动良好。',
colonDx:'全结肠黏膜未见明显异常',
pathology:'(降结肠活检)送检肠黏膜组织,腺体排列整齐,间质少量淋巴细胞浸润,未见异型增生及癌细胞。病理诊断:黏膜慢性炎。'},
{esophagus:'食管黏膜光滑,色泽正常,血管纹理清晰,未见静脉曲张及占位性病变。贲门开闭良好。',
stomach:'胃底、胃体黏膜光滑,未见糜烂及溃疡;胃窦黏膜可见散在点状糜烂,覆薄白苔,周围黏膜充血水肿。幽门口圆,开闭良好。',
duodenum:'十二指肠球部及降部黏膜光滑,未见溃疡、糜烂及占位性病变。乳头形态正常。',
gastroDx:'糜烂性胃炎(胃窦)',
colon:'升结肠、横结肠黏膜光滑,血管纹理清晰;乙状结肠距肛门约25cm处可见一枚直径约0.4cm息肉,表面光滑,亚蒂。余结肠及直肠黏膜未见异常。',
colonDx:'乙状结肠息肉(内镜下已钳除,建议半年后复查)',
pathology:'(乙状结肠活检)送检黏膜组织呈管状腺瘤样改变,伴低级别上皮内瘤变,基底切缘干净。病理诊断:管状腺瘤伴低级别上皮内瘤变。'}
];
function gastroProfile(r){
var t=0;for(var i=0;i<r.empNo.length;i++)t+=r.empNo.charCodeAt(i);
return GASTRO_PROFILES[t%GASTRO_PROFILES.length];
}
function gastroReportHtml(r){
if(!r.gastro&&!r.colon)return '<div class="dempty">暂无报告数据</div>';
var p=gastroProfile(r);
var eye='<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>';
var imgs='<span class="cta-img">'+eye+'胃镜报告<span class="link">查看</span></span>'
+'<span class="cta-img">'+eye+'肠镜报告<span class="link">查看</span></span>'
+'<span class="cta-img">'+eye+'活检报告<span class="link">查看</span></span>';
return '<div class="cta-rpt">'
+'<div class="cta-rpt__band">胃肠镜检查报告</div>'
+'<div class="cta-rpt__meta">'
+'<span><b>姓名:</b>'+r.name+'</span><span><b>性别:</b>'+r.gender+'</span><span><b>年龄:</b>'+r.age+'岁</span>'
+'<span><b>检查编号:</b>'+r.empNo+'</span><span><b>检查日期:</b>'+r.checkDate+'</span><span><b>检查医院:</b>'+r.hospital+'</span>'
+'</div>'
+(r.gastro
?'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">胃镜检查</div>'
+'<table class="cta-vessel"><thead><tr><th style="width:110px;">项目</th><th>所见 / 结论</th></tr></thead><tbody>'
+'<tr><td class="v-name">食管</td><td>'+p.esophagus+'</td></tr>'
+'<tr><td class="v-name"></td><td>'+p.stomach+'</td></tr>'
+'<tr><td class="v-name">十二指肠</td><td>'+p.duodenum+'</td></tr>'
+'<tr><td class="v-name">诊断结果</td><td>'+p.gastroDx+'</td></tr>'
+'</tbody></table></div>'
:'')
+(r.colon
?'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">肠镜检查</div>'
+'<table class="cta-vessel"><thead><tr><th style="width:110px;">项目</th><th>所见 / 结论</th></tr></thead><tbody>'
+'<tr><td class="v-name">结肠</td><td>'+p.colon+'</td></tr>'
+'<tr><td class="v-name">诊断结果</td><td>'+p.colonDx+'</td></tr>'
+'<tr><td class="v-name">病理诊断</td><td>'+p.pathology+'</td></tr>'
+'</tbody></table></div>'
:'')
+'<div class="cta-rpt__sec"><div class="cta-rpt__sec-t">报告图片</div><div class="cta-imgs">'+imgs+'</div></div>'
+'</div>';
}
function openReport(idx){
var r=rows[idx];
document.getElementById('report-title').textContent=r.name+' · 胃肠镜检查报告';
document.getElementById('report-body').innerHTML=gastroReportHtml(r);
document.getElementById('report-modal').classList.add('open');
}
function switchDetailTab(el,key){
var root=el.closest('.modal-overlay');
var tabs=root.querySelectorAll('.dtabs__item');
for(var i=0;i<tabs.length;i++)tabs[i].classList.toggle('active',tabs[i].getAttribute('data-tab')===key);
var panes=document.querySelectorAll('#detail-modal .dtab-pane');
var panes=root.querySelectorAll('.dtab-pane');
for(var j=0;j<panes.length;j++)panes[j].classList.toggle('active',panes[j].getAttribute('data-tab')===key);
}
/* ============ 编辑弹窗 ============ */
function infoGridHtml(r){
var items=[['姓名',r.name],['性别',r.gender],['年龄',r.age],['员工编号',r.empNo],['身份证号',r.idCard],['单位',r.unit],['部门',r.dept],['体检医院',r.hospital],['检查日期',r.checkDate],['体检年份',r.year],['创建时间',r.createTime]];
return '<div class="info-grid">'+items.map(function(it){
return '<div><div class="info-label">'+it[0]+'</div><div class="info-value">'+it[1]+'</div></div>';
}).join('')+'</div>';
}
function uploadBoxHtml(has,label,field){
var inner=has
?'<div class="upload-box upload-box--filled"><div class="report-large" style="width:100%;height:100%;padding:12px 16px;">'
+'<div class="report-large__imgs" style="gap:8px;"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>'
+'<div class="report-large__lines" style="margin-top:12px;"><i style="width:96%"></i><i style="width:84%"></i><i style="width:60%"></i></div></div></div>'
:'<div class="upload-box" onclick="fakeUpload(\''+field+'\')"><svg width="34" height="34" viewBox="0 0 34 34" fill="none"><path d="M17 22V8M11.5 13L17 7.5 22.5 13" stroke="#999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6 22v3.5A2.5 2.5 0 0 0 8.5 28h17a2.5 2.5 0 0 0 2.5-2.5V22" stroke="#999" stroke-width="2" stroke-linecap="round"/></svg>点击上传'+label+'图片</div>';
var ops='<div class="upload-box__ops">'+(has
?'<span class="link" onclick="fakeUpload(\''+field+'\')">重新上传</span><span class="link" style="color:#FF4D4F;" onclick="removeUpload(\''+field+'\')">删除</span>'
:'<span class="link" onclick="fakeUpload(\''+field+'\')">上传图片</span>')+'</div>';
return inner+ops;
}
/* ============ 编辑弹窗(仅报告图片可修改) ============ */
function openEdit(idx){
editIdx=idx;
var r=rows[idx];
editDraft={gastro:r.gastro,colon:r.colon};
editDraft={gastro:r.gastro,colon:r.colon,bx:!!r.bx};
document.getElementById('edit-title').textContent='编辑报告 · '+r.name;
document.getElementById('edit-body').innerHTML=
'<div class="report-section"><div class="report-section__title">基本信息</div>'+infoGridHtml(r)+'</div>'
+'<div class="report-section"><div class="report-section__title">胃镜报告图片</div><div class="upload-row" id="edit-gastro">'+uploadBoxHtml(r.gastro,'胃镜','gastro')+'</div></div>'
+'<div class="report-section"><div class="report-section__title">肠镜报告图片</div><div class="upload-row" id="edit-colon">'+uploadBoxHtml(r.colon,'肠镜','colon')+'</div></div>';
document.getElementById('edit-body').innerHTML=reportBodyHtml(r);
document.getElementById('edit-modal').classList.add('open');
}
function fakeUpload(field){
editDraft[field]=true;
document.getElementById('edit-'+field).innerHTML=uploadBoxHtml(true,field==='gastro'?'胃镜':'肠镜',field);
document.getElementById('edit-'+field).innerHTML=editPickHtml(true,field);
showToast('已选择报告图片 '+field+'-report-'+rows[editIdx].empNo+'.jpg');
}
function removeUpload(field){
editDraft[field]=false;
document.getElementById('edit-'+field).innerHTML=uploadBoxHtml(false,field==='gastro'?'胃镜':'肠镜',field);
document.getElementById('edit-'+field).innerHTML=editPickHtml(false,field);
}
function saveEdit(){
rows[editIdx].gastro=editDraft.gastro;
rows[editIdx].colon=editDraft.colon;
rows[editIdx].bx=editDraft.bx;
closeModal('edit-modal');
renderTable();
showToast('保存成功');
+100 -30
View File
@@ -98,6 +98,8 @@
.modal__close{font-size:22px;color:var(--text-placeholder);cursor:pointer;line-height:1;}
.modal__close:hover{color:var(--text-secondary);}
.modal__body{padding:16px 24px;overflow:auto;}
/* 员工费用明细弹窗内筛选栏 */
.cost-filter{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px;}
.modal__footer{display:flex;justify-content:flex-end;padding:12px 24px;border-top:1px solid var(--border);flex-shrink:0;}
/* 体检年份选择控件 */
.year-picker{position:relative;}
@@ -260,7 +262,7 @@
<th>序号</th>
<th>单位</th>
<th class="col-dept">部门</th>
<th>总人数</th>
<th>体检医院</th>
<th>已体检人数</th>
<th>报告完整人数</th>
<th>报告不完整人数</th>
@@ -268,21 +270,32 @@
<th>计划总体检费用(元)</th>
<th>实际总体检费用(元)</th>
<th>体检年份</th>
<th>签约医院</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>8000</td><td><span class="link" onclick="showStatDetail(0,'checked')">7520</span></td><td><span class="link" onclick="showStatDetail(0,'complete')">7186</span></td><td><span class="link" onclick="showStatDetail(0,'incomplete')">334</span></td><td><span class="link" onclick="showStatDetail(0,'missing')">1503</span></td><td>15,510,000.00</td><td>13,432,600.00</td><td>2026</td><td><span class="link" onclick="showCostDetail(0)">员工费用明细</span></td></tr>
<tr><td>2</td><td>采油厂</td><td class="col-dept">采油作业</td><td>8000</td><td><span class="link" onclick="showStatDetail(1,'checked')">7310</span></td><td><span class="link" onclick="showStatDetail(1,'complete')">6987</span></td><td><span class="link" onclick="showStatDetail(1,'incomplete')">323</span></td><td><span class="link" onclick="showStatDetail(1,'missing')">1195</span></td><td>15,076,875.00</td><td>13,075,762.50</td><td>2026</td><td><span class="link" onclick="showCostDetail(1)">员工费用明细</span></td></tr>
<tr><td>3</td><td>采油厂</td><td class="col-dept">集输作业一区</td><td>6500</td><td><span class="link" onclick="showStatDetail(2,'checked')">5980</span></td><td><span class="link" onclick="showStatDetail(2,'complete')">5745</span></td><td><span class="link" onclick="showStatDetail(2,'incomplete')">235</span></td><td><span class="link" onclick="showStatDetail(2,'missing')">869</span></td><td>12,333,750.00</td><td>10,681,775.00</td><td>2026</td><td><span class="link" onclick="showCostDetail(2)">员工费用明细</span></td></tr>
<tr><td>4</td><td>采油厂</td><td class="col-dept">采油作业</td><td>8000</td><td><span class="link" onclick="showStatDetail(3,'checked')">7680</span></td><td><span class="link" onclick="showStatDetail(3,'complete')">7412</span></td><td><span class="link" onclick="showStatDetail(3,'incomplete')">268</span></td><td><span class="link" onclick="showStatDetail(3,'missing')">1099</span></td><td>15,840,000.00</td><td>13,737,600.00</td><td>2025</td><td><span class="link" onclick="showCostDetail(3)">员工费用明细</span></td></tr>
<tr><td>5</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>8000</td><td><span class="link" onclick="showStatDetail(4,'checked')">7450</span></td><td><span class="link" onclick="showStatDetail(4,'complete')">7181</span></td><td><span class="link" onclick="showStatDetail(4,'incomplete')">269</span></td><td><span class="link" onclick="showStatDetail(4,'missing')">995</span></td><td>15,365,625.00</td><td>13,307,562.50</td><td>2025</td><td><span class="link" onclick="showCostDetail(4)">员工费用明细</span></td></tr>
<tr><td>6</td><td>采油厂</td><td class="col-dept">集输作业</td><td>6500</td><td><span class="link" onclick="showStatDetail(5,'checked')">6120</span></td><td><span class="link" onclick="showStatDetail(5,'complete')">5925</span></td><td><span class="link" onclick="showStatDetail(5,'incomplete')">195</span></td><td><span class="link" onclick="showStatDetail(5,'missing')">722</span></td><td>12,622,500.00</td><td>10,947,150.00</td><td>2025</td><td><span class="link" onclick="showCostDetail(5)">员工费用明细</span></td></tr>
<tr><td>1</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>CQ总医院</td><td><span class="link" onclick="showStatDetail(0,'checked')">3950</span></td><td><span class="link" onclick="showStatDetail(0,'complete')">3770</span></td><td><span class="link" onclick="showStatDetail(0,'incomplete')">180</span></td><td><span class="link" onclick="showStatDetail(0,'missing')">810</span></td><td>8,146,875.00</td><td>7,052,000.00</td><td>2026</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(0)">员工费用明细</span></td></tr>
<tr><td>2</td><td>采油厂</td><td class="col-dept">采油作业</td><td>泾河医院</td><td><span class="link" onclick="showStatDetail(1,'checked')">2160</span></td><td><span class="link" onclick="showStatDetail(1,'complete')">2055</span></td><td><span class="link" onclick="showStatDetail(1,'incomplete')">105</span></td><td><span class="link" onclick="showStatDetail(1,'missing')">430</span></td><td>4,455,000.00</td><td>3,862,600.00</td><td>2026</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(1)">员工费用明细</span></td></tr>
<tr><td>3</td><td>采油厂</td><td class="col-dept">采油作业一区</td><td>庆阳医院</td><td><span class="link" onclick="showStatDetail(2,'checked')">1410</span></td><td><span class="link" onclick="showStatDetail(2,'complete')">1361</span></td><td><span class="link" onclick="showStatDetail(2,'incomplete')">49</span></td><td><span class="link" onclick="showStatDetail(2,'missing')">263</span></td><td>2,908,125.00</td><td>2,518,000.00</td><td>2026</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(2)">员工费用明细</span></td></tr>
<tr><td>4</td><td>采油厂</td><td class="col-dept">采油作业</td><td>CQ总医院</td><td><span class="link" onclick="showStatDetail(3,'checked')">3760</span></td><td><span class="link" onclick="showStatDetail(3,'complete')">3590</span></td><td><span class="link" onclick="showStatDetail(3,'incomplete')">170</span></td><td><span class="link" onclick="showStatDetail(3,'missing')">620</span></td><td>7,755,000.00</td><td>6,706,262.50</td><td>2026</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(3)">员工费用明细</span></td></tr>
<tr><td>5</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>延安医院</td><td><span class="link" onclick="showStatDetail(4,'checked')">2190</span></td><td><span class="link" onclick="showStatDetail(4,'complete')">2097</span></td><td><span class="link" onclick="showStatDetail(4,'incomplete')">93</span></td><td><span class="link" onclick="showStatDetail(4,'missing')">350</span></td><td>4,516,875.00</td><td>3,926,500.00</td><td>2026</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(4)">员工费用明细</span></td></tr>
<tr><td>6</td><td>采油厂</td><td class="col-dept">采油作业</td><td>泾河医院</td><td><span class="link" onclick="showStatDetail(5,'checked')">1360</span></td><td><span class="link" onclick="showStatDetail(5,'complete')">1300</span></td><td><span class="link" onclick="showStatDetail(5,'incomplete')">60</span></td><td><span class="link" onclick="showStatDetail(5,'missing')">225</span></td><td>2,805,000.00</td><td>2,443,000.00</td><td>2026</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(5)">员工费用明细</span></td></tr>
<tr><td>7</td><td>第三采油厂</td><td class="col-dept">集输作业一区</td><td>庆阳医院</td><td><span class="link" onclick="showStatDetail(6,'checked')">3120</span></td><td><span class="link" onclick="showStatDetail(6,'complete')">2995</span></td><td><span class="link" onclick="showStatDetail(6,'incomplete')">125</span></td><td><span class="link" onclick="showStatDetail(6,'missing')">455</span></td><td>6,435,000.00</td><td>5,586,775.00</td><td>2026</td><td>庆阳医院,延安医院</td><td><span class="link" onclick="showCostDetail(6)">员工费用明细</span></td></tr>
<tr><td>8</td><td>第三采油厂</td><td class="col-dept">集输作业一区</td><td>延安医院</td><td><span class="link" onclick="showStatDetail(7,'checked')">2860</span></td><td><span class="link" onclick="showStatDetail(7,'complete')">2750</span></td><td><span class="link" onclick="showStatDetail(7,'incomplete')">110</span></td><td><span class="link" onclick="showStatDetail(7,'missing')">414</span></td><td>5,898,750.00</td><td>5,095,000.00</td><td>2026</td><td>庆阳医院,延安医院</td><td><span class="link" onclick="showCostDetail(7)">员工费用明细</span></td></tr>
<tr><td>9</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>CQ总医院</td><td><span class="link" onclick="showStatDetail(8,'checked')">4130</span></td><td><span class="link" onclick="showStatDetail(8,'complete')">3990</span></td><td><span class="link" onclick="showStatDetail(8,'incomplete')">140</span></td><td><span class="link" onclick="showStatDetail(8,'missing')">590</span></td><td>8,518,125.00</td><td>7,384,600.00</td><td>2025</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(8)">员工费用明细</span></td></tr>
<tr><td>10</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>泾河医院</td><td><span class="link" onclick="showStatDetail(9,'checked')">2110</span></td><td><span class="link" onclick="showStatDetail(9,'complete')">2040</span></td><td><span class="link" onclick="showStatDetail(9,'incomplete')">70</span></td><td><span class="link" onclick="showStatDetail(9,'missing')">310</span></td><td>4,351,875.00</td><td>3,783,000.00</td><td>2025</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(9)">员工费用明细</span></td></tr>
<tr><td>11</td><td>第一采油厂</td><td class="col-dept">采油作业一区</td><td>庆阳医院</td><td><span class="link" onclick="showStatDetail(10,'checked')">1440</span></td><td><span class="link" onclick="showStatDetail(10,'complete')">1382</span></td><td><span class="link" onclick="showStatDetail(10,'incomplete')">58</span></td><td><span class="link" onclick="showStatDetail(10,'missing')">199</span></td><td>2,970,000.00</td><td>2,570,000.00</td><td>2025</td><td>CQ总医院,泾河医院,庆阳医院</td><td><span class="link" onclick="showCostDetail(10)">员工费用明细</span></td></tr>
<tr><td>12</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>CQ总医院</td><td><span class="link" onclick="showStatDetail(11,'checked')">3910</span></td><td><span class="link" onclick="showStatDetail(11,'complete')">3770</span></td><td><span class="link" onclick="showStatDetail(11,'incomplete')">140</span></td><td><span class="link" onclick="showStatDetail(11,'missing')">520</span></td><td>8,064,375.00</td><td>6,983,562.50</td><td>2025</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(11)">员工费用明细</span></td></tr>
<tr><td>13</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>延安医院</td><td><span class="link" onclick="showStatDetail(12,'checked')">2140</span></td><td><span class="link" onclick="showStatDetail(12,'complete')">2061</span></td><td><span class="link" onclick="showStatDetail(12,'incomplete')">79</span></td><td><span class="link" onclick="showStatDetail(12,'missing')">290</span></td><td>4,413,750.00</td><td>3,829,000.00</td><td>2025</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(12)">员工费用明细</span></td></tr>
<tr><td>14</td><td>第二采油厂</td><td class="col-dept">采油作业二区</td><td>泾河医院</td><td><span class="link" onclick="showStatDetail(13,'checked')">1400</span></td><td><span class="link" onclick="showStatDetail(13,'complete')">1350</span></td><td><span class="link" onclick="showStatDetail(13,'incomplete')">50</span></td><td><span class="link" onclick="showStatDetail(13,'missing')">185</span></td><td>2,887,500.00</td><td>2,495,000.00</td><td>2025</td><td>CQ总医院,延安医院,泾河医院</td><td><span class="link" onclick="showCostDetail(13)">员工费用明细</span></td></tr>
<tr><td>15</td><td>第三采油厂</td><td class="col-dept">集输作业一区</td><td>庆阳医院</td><td><span class="link" onclick="showStatDetail(14,'checked')">3300</span></td><td><span class="link" onclick="showStatDetail(14,'complete')">3190</span></td><td><span class="link" onclick="showStatDetail(14,'incomplete')">110</span></td><td><span class="link" onclick="showStatDetail(14,'missing')">390</span></td><td>6,806,250.00</td><td>5,888,150.00</td><td>2025</td><td>庆阳医院,延安医院</td><td><span class="link" onclick="showCostDetail(14)">员工费用明细</span></td></tr>
<tr><td>16</td><td>第三采油厂</td><td class="col-dept">集输作业一区</td><td>延安医院</td><td><span class="link" onclick="showStatDetail(15,'checked')">2820</span></td><td><span class="link" onclick="showStatDetail(15,'complete')">2735</span></td><td><span class="link" onclick="showStatDetail(15,'incomplete')">85</span></td><td><span class="link" onclick="showStatDetail(15,'missing')">332</span></td><td>5,816,250.00</td><td>5,059,000.00</td><td>2025</td><td>庆阳医院,延安医院</td><td><span class="link" onclick="showCostDetail(15)">员工费用明细</span></td></tr>
</tbody>
</table>
</div>
<div class="pagination">
<span>共 6 条</span>
<span>16 条</span>
<button class="page-btn active">1</button>
</div>
</div>
@@ -295,6 +308,26 @@
<span class="modal__close" onclick="hideCostDetail()">×</span>
</div>
<div class="modal__body">
<div class="cost-filter">
<div class="filter-item">
<label>姓名:</label>
<input class="filter-input" id="cost-filter-name" type="text" placeholder="请输入姓名" style="min-width:130px;">
</div>
<div class="filter-item">
<label>员工编号:</label>
<input class="filter-input" id="cost-filter-id" type="text" placeholder="请输入员工编号" style="min-width:150px;">
</div>
<div class="filter-item">
<label>报告完整状态:</label>
<select class="filter-select" id="cost-filter-status" style="min-width:100px;"><option>全部</option><option>完整</option><option>不完整</option></select>
</div>
<button class="btn btn-primary" onclick="renderCostDetail()">查询</button>
<button class="btn btn-default" onclick="resetCostFilter()">重置</button>
<button class="btn btn-default" style="margin-left:auto;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
导出列表数据
</button>
</div>
<table>
<thead>
<tr><th>序号</th><th>姓名</th><th>员工编号</th><th>部门</th><th>体检日期</th><th>体检医院</th><th>应检项目数</th><th>已检项目数</th><th>报告完整状态</th><th>报告缺少项目数</th><th>计划体检费用(元)</th><th>实际体检费用(元)</th></tr>
@@ -356,7 +389,7 @@
</div>
<div class="modal__body">
<table>
<thead><tr><th>序号</th><th>项目名称</th><th>项目编码</th></tr></thead>
<thead><tr><th>序号</th><th>项目名称</th><th>项目编码</th><th>项目费用(元)</th><th>折扣后费用(元)</th></tr></thead>
<tbody id="emp-missing-body"></tbody>
</table>
</div>
@@ -377,14 +410,24 @@ function switchUnitLevel(el){
el.classList.add('active');
document.body.classList.toggle('level-3', el.textContent.trim()==='三级单位');
}
/* 行配置:单位/部门/年份/总人数/已体检人数;计划与实际总体检费用为明细弹窗 8 名员工合计×(已体检人数/8),主表与明细人均口径一致 */
/* 行配置:单位/部门/体检医院/签约医院(一个单位可签约多家医院,逗号相隔)/年份/已体检人数;计划与实际总体检费用为明细弹窗 8 名员工合计×(已体检人数/8),主表与明细人均口径一致 */
var ROWS=[
{unit:'第一采油厂',dept:'采油作业一区',year:2026,total:8000,checked:7520},
{unit:'第采油厂',dept:'采油作业区',year:2026,total:8000,checked:7310},
{unit:'第采油厂',dept:'集输作业一区',year:2026,total:6500,checked:5980},
{unit:'第采油厂',dept:'采油作业区',year:2025,total:8000,checked:7680},
{unit:'第二采油厂',dept:'采油作业二区',year:2025,total:8000,checked:7450},
{unit:'第采油厂',dept:'集输作业区',year:2025,total:6500,checked:6120}
{unit:'第一采油厂',dept:'采油作业一区',hospital:'CQ总医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2026,checked:3950},
{unit:'第采油厂',dept:'采油作业区',hospital:'泾河医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2026,checked:2160},
{unit:'第采油厂',dept:'采油作业一区',hospital:'庆阳医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2026,checked:1410},
{unit:'第采油厂',dept:'采油作业区',hospital:'CQ总医院',signed:'CQ总医院,延安医院,泾河医院',year:2026,checked:3760},
{unit:'第二采油厂',dept:'采油作业二区',hospital:'延安医院',signed:'CQ总医院,延安医院,泾河医院',year:2026,checked:2190},
{unit:'第采油厂',dept:'采油作业区',hospital:'泾河医院',signed:'CQ总医院,延安医院,泾河医院',year:2026,checked:1360},
{unit:'第三采油厂',dept:'集输作业一区',hospital:'庆阳医院',signed:'庆阳医院,延安医院',year:2026,checked:3120},
{unit:'第三采油厂',dept:'集输作业一区',hospital:'延安医院',signed:'庆阳医院,延安医院',year:2026,checked:2860},
{unit:'第一采油厂',dept:'采油作业一区',hospital:'CQ总医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2025,checked:4130},
{unit:'第一采油厂',dept:'采油作业一区',hospital:'泾河医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2025,checked:2110},
{unit:'第一采油厂',dept:'采油作业一区',hospital:'庆阳医院',signed:'CQ总医院,泾河医院,庆阳医院',year:2025,checked:1440},
{unit:'第二采油厂',dept:'采油作业二区',hospital:'CQ总医院',signed:'CQ总医院,延安医院,泾河医院',year:2025,checked:3910},
{unit:'第二采油厂',dept:'采油作业二区',hospital:'延安医院',signed:'CQ总医院,延安医院,泾河医院',year:2025,checked:2140},
{unit:'第二采油厂',dept:'采油作业二区',hospital:'泾河医院',signed:'CQ总医院,延安医院,泾河医院',year:2025,checked:1400},
{unit:'第三采油厂',dept:'集输作业一区',hospital:'庆阳医院',signed:'庆阳医院,延安医院',year:2025,checked:3300},
{unit:'第三采油厂',dept:'集输作业一区',hospital:'延安医院',signed:'庆阳医院,延安医院',year:2025,checked:2820}
];
/* 体检项目字典:四档套餐项目费用合计恰为额度值 1000/1500/2500/4000 元 */
var TIERS=[
@@ -423,26 +466,52 @@ function buildPlan(rowIdx,empIdx){
pool.forEach(function(it){it.status=(it.fee>=240)?'弃检':'未检';});
return {items:items,plan:plan,actual:plan-sum,dropN:dropN,planN:items.length,checkedCnt:items.length-pool.length};
}
var CUR_ROW=null;
var CUR_ROW=null,COST_IDX=0;
function showCostDetail(rowIdx){
CUR_ROW=ROWS[rowIdx];
var html='',pSum=0,aSum=0,cnSum=0,pnSum=0,mnSum=0;
COST_IDX=rowIdx;
document.getElementById('cost-filter-name').value='';
document.getElementById('cost-filter-id').value='';
document.getElementById('cost-filter-status').value='全部';
renderCostDetail();
document.getElementById('cost-detail-title').textContent='「'+CUR_ROW.unit+'」'+CUR_ROW.year+'年 员工费用明细';
document.getElementById('cost-detail-modal').style.display='flex';
}
/* 弹窗内筛选重绘:姓名/员工编号模糊匹配,报告完整状态精确匹配,合计行随筛选结果汇总 */
function renderCostDetail(){
var kw=document.getElementById('cost-filter-name').value.trim();
var kid=document.getElementById('cost-filter-id').value.trim();
var st=document.getElementById('cost-filter-status').value;
var html='',pSum=0,aSum=0,cnSum=0,pnSum=0,mnSum=0,n=0;
EMP_POOL.forEach(function(e,i){
var d=buildPlan(rowIdx,i);
var complete=reportComplete(rowIdx,i);
var complete=reportComplete(COST_IDX,i);
if(kw&&e.name.indexOf(kw)<0)return;
if(kid&&e.id.indexOf(kid)<0)return;
if(st==='完整'&&!complete)return;
if(st==='不完整'&&complete)return;
var d=buildPlan(COST_IDX,i);
pSum+=d.plan;aSum+=d.actual;cnSum+=d.checkedCnt;pnSum+=d.planN;
if(!complete)mnSum+=d.dropN;
html+='<tr><td>'+(i+1)+'</td><td>'+e.name+'</td><td>'+e.id+'</td><td>'+CUR_ROW.dept+'</td><td>'+CUR_ROW.year+'-'+(6+(i%3))+'-'+(11+i*2)+'</td><td>'+e.hospital+'</td>'+
'<td><span class="link" onclick="showItems('+rowIdx+','+i+',\'plan\')">'+d.planN+'</span></td>'+
'<td><span class="link" onclick="showItems('+rowIdx+','+i+',\'checked\')">'+d.checkedCnt+' <span class="sub-hint">(含未检/弃检'+d.dropN+')</span></span></td>'+
n++;
html+='<tr><td>'+n+'</td><td>'+e.name+'</td><td>'+e.id+'</td><td>'+CUR_ROW.dept+'</td><td>'+CUR_ROW.year+'-'+(6+(i%3))+'-'+(11+i*2)+'</td><td>'+e.hospital+'</td>'+
'<td><span class="link" onclick="showItems('+COST_IDX+','+i+',\'plan\')">'+d.planN+'</span></td>'+
'<td><span class="link" onclick="showItems('+COST_IDX+','+i+',\'checked\')">'+d.checkedCnt+' <span class="sub-hint">(含未检/弃检'+d.dropN+')</span></span></td>'+
'<td>'+(complete?'<span class="tag tag-green">完整</span>':'<span class="tag tag-orange">不完整</span>')+'</td>'+
'<td>'+(complete?0:d.dropN)+'</td>'+
'<td>'+(complete?0:'<span class="link" onclick="showEmpMissing('+COST_IDX+','+i+')">'+d.dropN+'</span>')+'</td>'+
'<td>'+fmtMoney(d.plan)+'</td><td>'+fmtMoney(d.actual)+'</td></tr>';
});
html+='<tr style="font-weight:600;"><td></td><td>合计</td><td></td><td></td><td></td><td></td><td>'+pnSum+'</td><td>'+cnSum+'</td><td></td><td>'+mnSum+'</td><td>'+fmtMoney(pSum)+'</td><td>'+fmtMoney(aSum)+'</td></tr>';
document.getElementById('cost-detail-title').textContent='「'+CUR_ROW.unit+'」'+CUR_ROW.year+'年 员工费用明细';
if(n===0){
html='<tr><td colspan="12" style="text-align:center;color:var(--text-placeholder);">暂无数据</td></tr>';
}else{
html+='<tr style="font-weight:600;"><td></td><td>合计</td><td></td><td></td><td></td><td></td><td>'+pnSum+'</td><td>'+cnSum+'</td><td></td><td>'+mnSum+'</td><td>'+fmtMoney(pSum)+'</td><td>'+fmtMoney(aSum)+'</td></tr>';
}
document.getElementById('cost-detail-body').innerHTML=html;
document.getElementById('cost-detail-modal').style.display='flex';
}
function resetCostFilter(){
document.getElementById('cost-filter-name').value='';
document.getElementById('cost-filter-id').value='';
document.getElementById('cost-filter-status').value='全部';
renderCostDetail();
}
/* 第二层弹窗:应检=全部项目及应检费用;已检=已检项目及费用;两视角均标注体检状态 */
function showItems(rowIdx,empIdx,mode){
@@ -482,7 +551,7 @@ function showStatDetail(rowIdx,mode){
var names=buildPlan(rowIdx,i).items.filter(function(it){return it.status!=='已检';}).map(function(it){return it.name;});
if(names.length===0)return;
cn++;
body+='<tr><td>'+cn+'</td><td>'+e.name+'</td><td>'+e.id+'</td><td>'+CUR_ROW.dept+'</td><td>'+e.hospital+'</td><td>'+names.join('')+'</td><td>'+names.length+'</td></tr>';
body+='<tr><td>'+cn+'</td><td>'+e.name+'</td><td>'+e.id+'</td><td>'+CUR_ROW.dept+'</td><td>'+e.hospital+'</td><td>'+names.join('')+'</td><td><span class="link" onclick="showEmpMissing('+rowIdx+','+i+')">'+names.length+'</span></td></tr>';
});
document.getElementById('stat-modal-count').textContent='共 '+cn+' 人(示例数据)';
} else {
@@ -518,13 +587,14 @@ function itemCode(name){
for(var i=0;i<name.length;i++){h=(h*31+name.charCodeAt(i))%9000;}
return 'XM'+(1000+h);
}
/* 第三层弹窗:点击缺少项目数,查看该员工缺少的具体项目 */
/* 第三层弹窗:点击统计明细的缺少项目数(不完整口径)或缺少项目明细的项目总数,查看该员工缺少的具体项目 */
/* 折扣后费用示例:统一按 8 折计 */
function showEmpMissing(rowIdx,empIdx){
var e=EMP_POOL[empIdx],html='',n=0;
buildPlan(rowIdx,empIdx).items.forEach(function(it){
if(it.status==='已检')return;
n++;
html+='<tr><td>'+n+'</td><td>'+it.name+'</td><td>'+itemCode(it.name)+'</td></tr>';
html+='<tr><td>'+n+'</td><td>'+it.name+'</td><td>'+itemCode(it.name)+'</td><td>'+fmtMoney(it.fee)+'</td><td>'+fmtMoney(it.fee*0.8)+'</td></tr>';
});
document.getElementById('emp-missing-title').textContent='体检报告缺少体检项目信息';
document.getElementById('emp-missing-body').innerHTML=html;
@@ -95,6 +95,7 @@
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:8px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .req{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;transition:border-color .2s;}
.form-input:disabled{background:#F5F7FA;color:var(--text-secondary);cursor:not-allowed;}
.form-input:focus{border-color:var(--primary);}
select.form-input{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 12px center;padding-right:32px;cursor:pointer;}
.form-input:disabled{background:#F5F5F5;color:var(--text-secondary);cursor:not-allowed;}
@@ -442,8 +443,8 @@
</div>
</div>
<!-- 参加人员范围:固定为指定人员,直接展示指定区域 -->
<div class="form-item">
<!-- 参加人员范围:固定为指定人员,直接展示指定区域(新增模式下隐藏) -->
<div class="form-item" id="personScopeItem">
<div class="form-label"><span class="req">*</span> 参加人员范围</div>
<!-- [交互] 指定机构默认为组织单位,可额外勾选其他单位/部门 -->
<div class="person-panel show" id="personPanel" style="margin-top:0;">
@@ -656,6 +657,7 @@ function openPlanModal(){
document.getElementById('plan-modal-title').textContent = '新增冠脉CTA体检计划';
document.getElementById('plan-save-btn').textContent = '确认新增';
resetPlanForm();
setPlanFormMode(true);
document.getElementById('plan-modal-overlay').classList.add('open');
document.getElementById('plan-drawer').classList.add('open');
}
@@ -664,6 +666,13 @@ function closePlanModal(){
document.getElementById('plan-drawer').classList.remove('open');
}
/* 新增模式:体检年份锁定为当年、隐藏参加人员范围;编辑模式恢复可编辑 */
function setPlanFormMode(isAdd){
var yearSel = document.querySelector('#plan-drawer .plan-year');
yearSel.disabled = isAdd;
document.getElementById('personScopeItem').style.display = isAdd ? 'none' : '';
}
/* [交互] 编辑:复用新增抽屉,按已存计划回填全部字段 */
function openPlanEdit(el){
var tr = el.closest('tr');
@@ -674,6 +683,7 @@ function openPlanEdit(el){
document.getElementById('plan-modal-title').textContent = '编辑冠脉CTA体检计划';
document.getElementById('plan-save-btn').textContent = '保存';
resetPlanForm();
setPlanFormMode(false);
document.getElementById('planUnit').value = d.unit;
var yearSel = document.querySelector('#plan-drawer .plan-year');
for(var i=0;i<yearSel.options.length;i++){ if(yearSel.options[i].value === d.year){ yearSel.selectedIndex = i; break; } }
@@ -1114,7 +1124,8 @@ function savePlan(){
if(!dateFrom || !dateTo){ showToast('请选择体检计划日期范围'); return; }
if(dateFrom > dateTo){ showToast('体检计划开始日期不能晚于结束日期'); return; }
var personType = 'specific';
if(selectedInstitutions.length === 0 && selectedEmployees.length === 0){
/* 新增模式隐藏参加人员范围区域,仅编辑时校验 */
if(editingPlanRid && selectedInstitutions.length === 0 && selectedEmployees.length === 0){
showToast('请至少选择指定机构或指定员工'); return;
}
var apptOn = document.getElementById('switchAppt').classList.contains('on');
@@ -95,6 +95,7 @@
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:8px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .req{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;transition:border-color .2s;}
.form-input:disabled{background:#F5F7FA;color:var(--text-secondary);cursor:not-allowed;}
.form-input:focus{border-color:var(--primary);}
select.form-input{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 12px center;padding-right:32px;cursor:pointer;}
.form-input:disabled{background:#F5F5F5;color:var(--text-secondary);cursor:not-allowed;}
@@ -442,8 +443,8 @@
</div>
</div>
<!-- 参加人员范围:固定为指定人员,直接展示指定区域 -->
<div class="form-item">
<!-- 参加人员范围:固定为指定人员,直接展示指定区域(新增模式下隐藏) -->
<div class="form-item" id="personScopeItem">
<div class="form-label"><span class="req">*</span> 参加人员范围</div>
<!-- [交互] 指定机构默认为组织单位,可额外勾选其他单位/部门 -->
<div class="person-panel show" id="personPanel" style="margin-top:0;">
@@ -656,6 +657,7 @@ function openPlanModal(){
document.getElementById('plan-modal-title').textContent = '新增胃肠镜体检计划';
document.getElementById('plan-save-btn').textContent = '确认新增';
resetPlanForm();
setPlanFormMode(true);
document.getElementById('plan-modal-overlay').classList.add('open');
document.getElementById('plan-drawer').classList.add('open');
}
@@ -664,6 +666,13 @@ function closePlanModal(){
document.getElementById('plan-drawer').classList.remove('open');
}
/* 新增模式:体检年份锁定为当年、隐藏参加人员范围;编辑模式恢复可编辑 */
function setPlanFormMode(isAdd){
var yearSel = document.querySelector('#plan-drawer .plan-year');
yearSel.disabled = isAdd;
document.getElementById('personScopeItem').style.display = isAdd ? 'none' : '';
}
/* [交互] 编辑:复用新增抽屉,按已存计划回填全部字段 */
function openPlanEdit(el){
var tr = el.closest('tr');
@@ -674,6 +683,7 @@ function openPlanEdit(el){
document.getElementById('plan-modal-title').textContent = '编辑胃肠镜体检计划';
document.getElementById('plan-save-btn').textContent = '保存';
resetPlanForm();
setPlanFormMode(false);
document.getElementById('planUnit').value = d.unit;
var yearSel = document.querySelector('#plan-drawer .plan-year');
for(var i=0;i<yearSel.options.length;i++){ if(yearSel.options[i].value === d.year){ yearSel.selectedIndex = i; break; } }
@@ -1114,7 +1124,8 @@ function savePlan(){
if(!dateFrom || !dateTo){ showToast('请选择体检计划日期范围'); return; }
if(dateFrom > dateTo){ showToast('体检计划开始日期不能晚于结束日期'); return; }
var personType = 'specific';
if(selectedInstitutions.length === 0 && selectedEmployees.length === 0){
/* 新增模式隐藏参加人员范围区域,仅编辑时校验 */
if(editingPlanRid && selectedInstitutions.length === 0 && selectedEmployees.length === 0){
showToast('请至少选择指定机构或指定员工'); return;
}
var apptOn = document.getElementById('switchAppt').classList.contains('on');
+1 -91
View File
@@ -87,11 +87,6 @@
.form-input:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
.form-select{width:100%;height:40px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;}
.form-select:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.2);}
/* Tab 切换 */
.tabs{display:flex;gap:0;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);padding:0 24px;margin-bottom:0;}
.tab{padding:14px 24px;font-size:var(--font-base);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;user-select:none;}
.tab:hover{color:var(--primary);}
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
.num-link{color:var(--primary);cursor:pointer;font-weight:600;}
.num-link:hover{color:#40a9ff;text-decoration:underline;}
/* 体检年份选择控件 */
@@ -213,14 +208,8 @@
<main class="main">
<div class="tabs">
<div class="tab active" data-tab="dept" onclick="switchTab('dept')">部门</div>
<div class="tab" data-tab="hospital" onclick="switchTab('hospital')">医院</div>
</div>
<!-- 部门tab -->
<div id="panel-dept">
<div class="filter-bar" style="border-radius:0 0 var(--radius-md) var(--radius-md);margin-bottom:16px;">
<div class="filter-bar" style="margin-bottom:16px;">
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
@@ -288,76 +277,6 @@
</div>
</div>
</div>
<!-- 医院tab -->
<div id="panel-hospital" style="display:none;">
<div class="filter-bar" style="border-radius:0 0 var(--radius-md) var(--radius-md);margin-bottom:16px;">
<div class="filter-item">
<label>体检年份:</label>
<div class="year-picker">
<input class="filter-input" type="text" readonly value="2026" style="min-width:110px;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="2.5" width="11" height="10" rx="1.5" stroke="#999" stroke-width="1.2"/><path d="M1.5 5.5h11M4.5 1v3M9.5 1v3" stroke="#999" stroke-width="1.2" stroke-linecap="round"/></svg>
<div class="year-picker__dropdown">
<div class="selected" onclick="pickYear(this)">2026</div>
<div onclick="pickYear(this)">2025</div>
<div onclick="pickYear(this)">2024</div>
</div>
</div>
</div>
<div class="filter-item">
<label>单位:</label>
<select class="filter-select" style="min-width:160px;">
<option>请选择</option>
<option>采油四厂</option>
<option>采油五厂</option>
<option>采油六厂</option>
</select>
</div>
<div class="filter-item">
<label>预选医院:</label>
<select class="filter-select" style="min-width:160px;">
<option>请选择</option>
<option>宁夏宝石花医院</option>
<option>CQ油田医院</option>
<option>长安胃肠镜医院</option>
</select>
</div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap;">
<button class="btn btn-default">导出列表数据</button>
<button class="btn btn-default">查看导出任务</button>
</div>
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">预选信息统计(医院)</div>
</div>
<div class="table-card__content">
<table>
<thead>
<tr>
<th>预选医院</th><th>体检年份</th><th>单位</th><th>预选人数</th>
</tr>
</thead>
<tbody>
<tr><td>宁夏宝石花医院</td><td>2026</td><td>采油四厂</td><td><span class="num-link" onclick="openDetail('宁夏宝石花医院 / 采油四厂')">20</span></td></tr>
<tr><td>宁夏宝石花医院</td><td>2026</td><td>采油五厂</td><td><span class="num-link" onclick="openDetail('宁夏宝石花医院 / 采油五厂')">15</span></td></tr>
<tr><td>CQ油田医院</td><td>2026</td><td>采油五厂</td><td><span class="num-link" onclick="openDetail('CQ油田医院 / 采油五厂')">31</span></td></tr>
<tr><td>长安胃肠镜医院</td><td>2026</td><td>采油六厂</td><td><span class="num-link" onclick="openDetail('长安胃肠镜医院 / 采油六厂')">9</span></td></tr>
</tbody>
</table>
</div>
<div class="pagination">
<span>共 4 条</span>
<button class="page-btn active">1</button>
<span>每页</span>
<select class="filter-select" style="min-width:70px;height:32px;"><option>10条</option><option>50条</option></select>
</div>
</div>
</div>
</main>
<!-- 预选人数明细弹窗 -->
@@ -399,15 +318,6 @@ function toggleSubgroup(header){
while(sub){ sub.classList.add('open'); sub=sub.parentElement.closest('.sidebar-subgroup'); }
})();
/* tab 切换 */
function switchTab(key){
document.querySelectorAll('.tab').forEach(function(t){
t.classList.toggle('active', t.getAttribute('data-tab') === key);
});
document.getElementById('panel-dept').style.display = key === 'dept' ? '' : 'none';
document.getElementById('panel-hospital').style.display = key === 'hospital' ? '' : 'none';
}
/* 预选人数明细 */
function openDetail(title){
document.getElementById('detail-title').textContent = '预选人数明细 - ' + title;
@@ -421,35 +421,35 @@
<div class="tab-panel active" id="panel-required">
<div class="panel-toolbar"><button class="btn btn-default" onclick="addProjectRow('required')">+ 添加项目</button></div>
<table>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>操作</th></tr></thead>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>金额(元)</th><th>操作</th></tr></thead>
<tbody id="tbody-required"></tbody>
</table>
</div>
<div class="tab-panel" id="panel-optional">
<div class="panel-toolbar"><button class="btn btn-default" onclick="addProjectRow('optional')">+ 添加项目</button></div>
<table>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>操作</th></tr></thead>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>金额(元)</th><th>操作</th></tr></thead>
<tbody id="tbody-optional"></tbody>
</table>
</div>
<div class="tab-panel" id="panel-gyn-unmarried">
<div class="panel-toolbar"><button class="btn btn-default" onclick="addProjectRow('gyn-unmarried')">+ 添加项目</button></div>
<table>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>操作</th></tr></thead>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>金额(元)</th><th>操作</th></tr></thead>
<tbody id="tbody-gyn-unmarried"></tbody>
</table>
</div>
<div class="tab-panel" id="panel-gyn-married">
<div class="panel-toolbar"><button class="btn btn-default" onclick="addProjectRow('gyn-married')">+ 添加项目</button></div>
<table>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>操作</th></tr></thead>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>金额(元)</th><th>操作</th></tr></thead>
<tbody id="tbody-gyn-married"></tbody>
</table>
</div>
<div class="tab-panel" id="panel-cancer">
<div class="panel-toolbar"><button class="btn btn-default" onclick="addProjectRow('cancer')">+ 添加项目</button></div>
<table>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>操作</th></tr></thead>
<thead><tr><th>体检项目</th><th>项目编码</th><th>项目标记</th><th>金额(元)</th><th>操作</th></tr></thead>
<tbody id="tbody-cancer"></tbody>
</table>
</div>
@@ -550,19 +550,29 @@ function toggleSubgroup(header){
/* ===== 新增组合模板抽屉 ===== */
var hospitalData = ['泾河医院','庆阳医院','延安医院','兴隆园医院','宁夏宝石花医院'];
var projectData = [
{name:'经颅多普勒', code:'TCD-001'},
{name:'腹部彩超', code:'BUS-002'},
{name:'血常规', code:'BLD-003'},
{name:'心电图', code:'ECG-004'},
{name:'胸部X光', code:'XRY-005'},
{name:'肝功能', code:'LFT-006'},
{name:'肾功能', code:'RFT-007'},
{name:'空腹血糖', code:'GLU-008'},
{name:'血脂四项', code:'LIP-009'},
{name:'尿常规', code:'URI-010'},
{name:'胃镜', code:'GAS-011'},
{name:'肠镜', code:'COL-012'}
{name:'经颅多普勒', code:'TCD-001', price:120},
{name:'腹部彩超', code:'BUS-002', price:160},
{name:'血常规', code:'BLD-003', price:25},
{name:'心电图', code:'ECG-004', price:40},
{name:'胸部X光', code:'XRY-005', price:80},
{name:'肝功能', code:'LFT-006', price:60},
{name:'肾功能', code:'RFT-007', price:60},
{name:'空腹血糖', code:'GLU-008', price:20},
{name:'血脂四项', code:'LIP-009', price:120},
{name:'尿常规', code:'URI-010', price:20},
{name:'胃镜', code:'GAS-011', price:400},
{name:'肠镜', code:'COL-012', price:480}
];
/* 妇科/防癌等不在可选池中的项目金额(编辑回填时按编码取值) */
var EXTRA_PRICES = {
'GYN-013':'30.00', 'GYN-014':'95.00', 'GYN-015':'140.00',
'SEP-016':'199.00', 'CA7-017':'150.00'
};
function projectPrice(code){
var p = projectData.find(function(it){ return it.code === code; });
if(p) return Number(p.price).toFixed(2);
return EXTRA_PRICES[code] || '';
}
var projectTabKeys = ['required','optional','gyn-unmarried','gyn-married','cancer'];
/* 可搜索下拉通用组件 */
@@ -766,6 +776,7 @@ function addProjectRow(tabKey){
var tdItem = document.createElement('td');
var tdCode = document.createElement('td');
var tdMark = document.createElement('td');
var tdAmount = document.createElement('td');
var tdOp = document.createElement('td');
var codeInput = document.createElement('input');
@@ -773,11 +784,19 @@ function addProjectRow(tabKey){
codeInput.readOnly = true;
codeInput.placeholder = '自动填充';
var amountInput = document.createElement('input');
amountInput.className = 'row-input row-amount';
amountInput.readOnly = true;
amountInput.placeholder = '自动填充';
var ss = createSearchSelect(projectData, {
sm: true,
placeholder: '请选择体检项目',
getLabel: function(item){ return item.name + '' + item.code + ''; },
onSelect: function(item){ codeInput.value = item ? item.code : ''; }
onSelect: function(item){
codeInput.value = item ? item.code : '';
amountInput.value = item ? Number(item.price).toFixed(2) : '';
}
});
var markSelect = document.createElement('select');
@@ -797,11 +816,13 @@ function addProjectRow(tabKey){
tdItem.appendChild(ss.container);
tdCode.appendChild(codeInput);
tdMark.appendChild(markSelect);
tdAmount.appendChild(amountInput);
tdOp.appendChild(del);
tr.appendChild(tdItem);
tr.appendChild(tdCode);
tr.appendChild(tdMark);
tr.appendChild(tdAmount);
tr.appendChild(tdOp);
tbody.appendChild(tr);
}
@@ -857,12 +878,18 @@ function addStaticProjectRow(tabKey, p){
var tdItem = document.createElement('td'); tdItem.className = 'row-static';
var tdCode = document.createElement('td');
var tdMark = document.createElement('td');
var tdAmount = document.createElement('td');
var tdOp = document.createElement('td');
tdItem.textContent = p[0];
var codeInput = document.createElement('input');
codeInput.className = 'row-input';
codeInput.readOnly = true;
codeInput.value = p[1];
var amountInput = document.createElement('input');
amountInput.className = 'row-input row-amount';
amountInput.readOnly = true;
amountInput.value = projectPrice(p[1]);
if(!amountInput.value) amountInput.placeholder = '—';
var markSelect = document.createElement('select');
markSelect.className = 'row-select';
['一般项目','重点项目'].forEach(function(m){
@@ -878,10 +905,12 @@ function addStaticProjectRow(tabKey, p){
del.onclick = function(){ tr.remove(); };
tdCode.appendChild(codeInput);
tdMark.appendChild(markSelect);
tdAmount.appendChild(amountInput);
tdOp.appendChild(del);
tr.appendChild(tdItem);
tr.appendChild(tdCode);
tr.appendChild(tdMark);
tr.appendChild(tdAmount);
tr.appendChild(tdOp);
tbody.appendChild(tr);
}
+14 -3
View File
@@ -94,6 +94,7 @@
.form-label{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:8px;display:flex;align-items:center;gap:4px;font-weight:500;}
.form-label .req{color:var(--danger);}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;transition:border-color .2s;}
.form-input:disabled{background:#F5F7FA;color:var(--text-secondary);cursor:not-allowed;}
.form-input:focus{border-color:var(--primary);}
select.form-input{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 12px center;padding-right:32px;cursor:pointer;}
.form-input:disabled{background:#F5F5F5;color:var(--text-secondary);cursor:not-allowed;}
@@ -457,8 +458,8 @@
</div>
</div>
<!-- 参加人员范围:固定为指定人员,直接展示指定区域 -->
<div class="form-item">
<!-- 参加人员范围:固定为指定人员,直接展示指定区域(新增模式下隐藏) -->
<div class="form-item" id="personScopeItem">
<div class="form-label"><span class="req">*</span> 参加人员范围</div>
<!-- [交互] 指定机构默认为组织单位,可额外勾选其他单位/部门 -->
<div class="person-panel show" id="personPanel" style="margin-top:0;">
@@ -678,6 +679,7 @@ function openPlanModal(){
document.getElementById('plan-modal-title').textContent = '新增单位体检计划';
document.getElementById('plan-save-btn').textContent = '确认新增';
resetPlanForm();
setPlanFormMode(true);
document.getElementById('plan-modal-overlay').classList.add('open');
document.getElementById('plan-drawer').classList.add('open');
}
@@ -686,6 +688,13 @@ function closePlanModal(){
document.getElementById('plan-drawer').classList.remove('open');
}
/* 新增模式:体检年份锁定为当年、隐藏参加人员范围;编辑模式恢复可编辑 */
function setPlanFormMode(isAdd){
var yearSel = document.querySelector('#plan-drawer .plan-year');
yearSel.disabled = isAdd;
document.getElementById('personScopeItem').style.display = isAdd ? 'none' : '';
}
/* [交互] 编辑:复用新增抽屉,按已存计划回填全部字段 */
function openPlanEdit(el){
var tr = el.closest('tr');
@@ -696,6 +705,7 @@ function openPlanEdit(el){
document.getElementById('plan-modal-title').textContent = '编辑单位体检计划';
document.getElementById('plan-save-btn').textContent = '保存';
resetPlanForm();
setPlanFormMode(false);
document.getElementById('planUnit').value = d.unit;
var yearSel = document.querySelector('#plan-drawer .plan-year');
for(var i=0;i<yearSel.options.length;i++){ if(yearSel.options[i].value === d.year){ yearSel.selectedIndex = i; break; } }
@@ -1223,7 +1233,8 @@ function savePlan(){
if(!dateFrom || !dateTo){ showToast('请选择体检计划日期范围'); return; }
if(dateFrom > dateTo){ showToast('体检计划开始日期不能晚于结束日期'); return; }
var personType = 'specific';
if(selectedInstitutions.length === 0 && selectedEmployees.length === 0){
/* 新增模式隐藏参加人员范围区域,仅编辑时校验 */
if(editingPlanRid && selectedInstitutions.length === 0 && selectedEmployees.length === 0){
showToast('请至少选择指定机构或指定员工'); return;
}
var apptOn = document.getElementById('switchAppt').classList.contains('on');
@@ -305,6 +305,8 @@
<button class="btn btn-primary" onclick="openAddModal()">+ 添加指定体检医院员工</button>
<button class="btn btn-default">导出列表数据</button>
<button class="btn btn-default">查看导出任务</button>
<button class="btn btn-default">批量导入人员</button>
<button class="btn btn-default">查看导入任务</button>
<button class="btn btn-default">批量删除</button>
</div>