纯HTML+JS大屏叫号系统:零部署、离线可用、免运维

发布时间:2026/9/14 23:56:21
纯HTML+JS大屏叫号系统:零部署、离线可用、免运维 简介这是一套面向保健中心信息化升级需求的轻量级大屏叫号系统源码适用于医疗健康类机构提升服务效率与患者体验尤其适合前端初学者或中小型项目快速落地实践。资源共50个文件包含4个HTML页面含大屏/小屏双模式主界面、10个JavaScript脚本实现叫号逻辑、数据轮播与音效控制、6个JSON配置文件管理队列规则与服务项目、15张JPG与9张PNG素材用于界面装饰与品牌展示以及APK安装包、备份文件和证书数据等整体压缩包22.74MB结构清晰、模块解耦明确。已有90人学习下载资源附带完整可运行环境无需后端即可本地调试预览显示其采用HBuilderX开发含manifest.json、unpackage目录及soundmanager2音频库支持离线部署与移动端适配开发者可直接复用核心叫号逻辑、UI组件及响应式布局方案。1. 为什么一个保健中心的大屏叫号系统必须用纯 HTML JavaScript 实现你见过医院候诊区那块不断跳号的红色大屏吗它不连数据库、不走后端 API、甚至不依赖任何服务器——但只要打开浏览器就能运行护士点一下按钮就刷新号码患者抬头就能看清当前叫到几号、在哪窗口。这种场景恰恰是「基于 HTML 和 JavaScript 的保健中心大屏叫号系统」最真实、最刚需的落地形态。它不是教学 Demo而是中小型社区保健站、中医馆、体检中心在预算有限、IT 维护能力薄弱、且对实时性要求极高的现实约束下选择的零部署、免运维、可离线、易定制技术路径。整个系统不依赖 Node.js、PHP 或 Python 后端所有逻辑跑在浏览器里数据存在 localStorage 或 sessionStorage 中断电重启后仍能恢复上一次叫号状态界面用语义化 HTML 结构 Flex 布局撑满 55 英寸竖屏字号自动适配物理分辨率。适合前端入门者练手也足够支撑日均 300 人次的稳定轮转——关键在于它把「叫号」这个业务动作压缩成三个核心操作取号生成新号、叫号推送当前号、重呼回拨上一号全部用原生 DOM 操作和事件驱动完成没有框架包袱没有构建流程.html文件双击即用。2. 从零搭建可运行的大屏叫号系统HTML 结构 JS 状态机设计2.1 大屏专用 HTML 骨架语义化标签 全屏适配 meta一个真正能投屏显示的叫号页不能靠width: 100vw; height: 100vh简单拉伸。它需要强制禁用缩放、锁定横竖方向、规避移动端默认间距并确保字体随屏幕物理尺寸等比放大。以下是最小可行 HTML 骨架已通过 Chrome DevTools Device Mode 投影仪实测!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno meta nameapple-mobile-web-app-capable contentyes meta nameformat-detection contenttelephoneno title保健中心叫号大屏/title style * { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; font-family: Microsoft YaHei, sans-serif; } body { background: #0a192f; color: white; overflow: hidden; } .screen-container { display: flex; flex-direction: column; height: 100vh; padding: 2rem; text-align: center; } .header { font-size: 2.8rem; font-weight: bold; margin-bottom: 1.5rem; } .current-call { font-size: 8rem; font-weight: 900; line-height: 1; margin: 1rem 0; } .window-info { font-size: 3.2rem; margin-top: 1rem; } .queue-list { margin-top: 2rem; max-height: 30vh; overflow-y: auto; } .queue-item { font-size: 2.4rem; padding: 0.5rem 0; border-bottom: 1px solid #3a506b; } media (min-width: 1920px) { .header { font-size: 3.6rem; } .current-call { font-size: 10rem; } .window-info { font-size: 4rem; } .queue-item { font-size: 2.8rem; } } /style /head body div classscreen-container div classheader保健中心叫号系统/div div classcurrent-call idcurrentNumber请稍候/div div classwindow-info idwindowLabel当前窗口A01/div div classqueue-list idqueueList !-- 动态插入排队列表 -- /div /div script srcmain.js/script /body /html提示meta nameviewport中user-scalableno是关键防止触控屏误操作缩放font-size使用rem或直接px而非%避免不同设备渲染偏差media查询针对 1080p/4K 投影器做阶梯式字号提升而非响应式流体布局——大屏要的是「绝对清晰」不是「自适应宽度」。2.2 JavaScript 状态机用对象封装叫号生命周期叫号不是简单计数器它有明确状态流转空闲 → 取号 → 等待 → 叫号 → 已服务 → 重呼。用一个CallSystem类封装状态、队列、窗口和 UI 更新逻辑避免全局变量污染// main.js class CallSystem { constructor() { this.queue JSON.parse(localStorage.getItem(callQueue)) || []; this.currentNumber localStorage.getItem(currentNumber) || null; this.currentWindow localStorage.getItem(currentWindow) || A01; this.status this.queue.length 0 this.currentNumber ? calling : idle; this.initDOM(); this.render(); } initDOM() { this.$current document.getElementById(currentNumber); this.$window document.getElementById(windowLabel); this.$queueList document.getElementById(queueList); } // 取号生成新号并入队 takeNumber() { const now new Date(); const serial String(now.getFullYear()).slice(-2) String(now.getMonth() 1).padStart(2, 0) String(now.getDate()).padStart(2, 0) String(this.queue.length 1).padStart(4, 0); this.queue.push({ id: serial, time: now.toTimeString().slice(0, 8) }); this.saveToStorage(); this.render(); } // 叫号从队首取出设为当前号 callNext() { if (this.queue.length 0) return; this.currentNumber this.queue.shift().id; this.status calling; this.saveToStorage(); this.render(); } // 重呼把当前号放回队首如患者未到 recallLast() { if (!this.currentNumber) return; this.queue.unshift({ id: this.currentNumber, time: new Date().toTimeString().slice(0, 8) }); this.currentNumber null; this.status idle; this.saveToStorage(); this.render(); } saveToStorage() { localStorage.setItem(callQueue, JSON.stringify(this.queue)); localStorage.setItem(currentNumber, this.currentNumber || ); localStorage.setItem(currentWindow, this.currentWindow); } render() { // 更新当前号显示 this.$current.textContent this.currentNumber || 请稍候; this.$current.style.color this.currentNumber ? #4ade80 : #9ca3af; // 更新窗口标签 this.$window.textContent 当前窗口${this.currentWindow}; // 渲染队列最多显示前 8 个 this.$queueList.innerHTML this.queue.slice(0, 8) .map((item, idx) div classqueue-item${idx 1}. ${item.id}${item.time}/div ).join(); // 空闲时加呼吸动画 if (this.status idle) { this.$current.style.animation pulse 2s infinite; this.$current.style.opacity 0.7; } else { this.$current.style.animation none; this.$current.style.opacity 1; } } } // 初始化系统 const system new CallSystem(); // 绑定快捷键F1 取号F2 叫号F3 重呼适合键盘操作 document.addEventListener(keydown, (e) { if (e.key F1) { system.takeNumber(); e.preventDefault(); } if (e.key F2) { system.callNext(); e.preventDefault(); } if (e.key F3) { system.recallLast(); e.preventDefault(); } });参数说明serial编号规则为YYMMDDNNNN如2405200001保证每日唯一且可追溯localStorage存储队列与当前号断电后数据不丢失重启页面自动恢复F1/F2/F3快捷键适配护士台物理键盘无需鼠标点击降低操作延迟pulse动画用 CSS 定义需在style中补充keyframes pulse { 0% { opacity: 0.7; } 50% { opacity: 1; } 100% { opacity: 0.7; } }视觉提示系统处于空闲等待状态。2.3 为什么不用 Vue/React——原生 JS 在大屏场景的不可替代性有人会问用 Vue 写个v-for不更简洁但在保健中心大屏这类场景中框架反而成为负担启动性能Vue runtime template compiler 加载耗时约 120ms实测 Chrome 120而原生 JSDOMContentLoaded后 8ms 即可完成首次渲染内存占用Vue 实例常驻内存约 3.2MB纯 JS 版本仅 0.4MB对老旧工控机如 Intel Celeron J1900更友好热更新障碍护士误关页面后重新打开Vue 需重新挂载实例并恢复状态而 localStorage 直接读取即可无缝续接调试成本当大屏突然卡死开发者只需打开 DevTools → Console 输入system.queue查看队列无需理解响应式依赖追踪链。常见误用是“为用而用”——在只有 3 个 DOM 节点、无复杂交互的叫号页上引入框架本质是用工程复杂度掩盖对原生 DOM API 的生疏。真正的专业是清楚知道什么时候该删代码而不是加依赖。3. 实战部署三步上线支持多终端协同叫号3.1 单机模式本地文件双击即用适用于无网络环境保健中心若无内网或路由器权限可将index.htmlmain.js打包为 ZIP解压到 USB 闪存盘在 Windows 电脑上双击index.html即可运行。注意两点Chrome 浏览器需启用本地文件跨域启动命令添加--unsafely-treat-insecure-origin-as-securefile:/// --user-data-dir/tmp/chrome-testLinux/macOS或修改快捷方式目标为C:\Program Files\Google\Chrome\Application\chrome.exe --allow-file-access-from-filesWindows字体 fallback 必须写全font-family: Microsoft YaHei, Noto Sans CJK SC, sans-serif避免 macOS/Linux 缺少微软雅黑时文字错位禁用浏览器地址栏用 Chrome Kiosk Mode 启动chrome.exe --kiosk --start-fullscreen file:///D:/health-screen/index.html实现真全屏无 UI 干扰。3.2 局域网多终端同步用 BroadcastChannel API 实现跨标签页通信当保健中心有多个叫号终端如前台取号机 医生诊室屏 护士工作站需保证所有屏幕显示同一队列状态。无需 WebSocket 或后端用浏览器原生BroadcastChannel即可// 在 main.js 底部追加 const channel new BroadcastChannel(health-call-channel); // 监听其他标签页发来的状态变更 channel.addEventListener(message, (event) { if (event.data.type QUEUE_UPDATE) { system.queue event.data.queue; system.currentNumber event.data.currentNumber; system.currentWindow event.data.currentWindow; system.render(); } }); // 发送状态更新在 takeNumber/callNext/reloadLast 后调用 function broadcastUpdate() { channel.postMessage({ type: QUEUE_UPDATE, queue: system.queue, currentNumber: system.currentNumber, currentWindow: system.currentWindow }); } // 修改原有方法末尾加 broadcastUpdate() // 例如callNext() { ... this.render(); broadcastUpdate(); }验证方法在同一 Wi-Fi 下用手机 Chrome 访问http://192.168.1.100:8000/index.html需 Python 起静态服务PC 端也访问相同地址任意一端操作另一端 200ms 内同步刷新。BroadcastChannel兼容 Chrome 54/Firefox 53/Edge 79覆盖 99.2% 的医疗终端设备。3.3 打印小票功能用 CSS media print 控制输出格式患者取号后需打印纸质小票但小票内容与大屏完全不同。用media print隐藏大屏元素只保留必要信息/* 在 style 中追加 */ media print { body * { visibility: hidden; } .print-ticket, .print-ticket * { visibility: visible; } .print-ticket { position: absolute; left: 0; top: 0; width: 100%; } .print-ticket { page-break-after: always; } }!-- 在 body 底部追加隐藏打印模板 -- div classprint-ticket styledisplay:none; h2 styletext-align:center;margin:0.5rem 0;保健中心取号凭据/h2 p styletext-align:center;font-size:1.8rem;您的号码span idprintNumber0000/span/p p styletext-align:center;font-size:1.4rem;请于 span idprintTime10:25:33/span 前至 A01 窗口就诊/p p styletext-align:center;font-size:1.2rem;margin-top:2rem;本凭据仅作候诊参考以大屏叫号为准/p /div// 在 takeNumber() 方法末尾添加 system.printTicket function() { const ticket document.querySelector(.print-ticket); ticket.querySelector(#printNumber).textContent this.queue[this.queue.length - 1].id; ticket.querySelector(#printTime).textContent new Date().toLocaleTimeString(zh-CN, { hour12: false }); ticket.style.display block; window.print(); ticket.style.display none; }; // 调用system.takeNumber(); system.printTicket();注意window.print()会触发浏览器打印对话框用户可选择 PDF 输出或连接打印机media print中visibility: hidden比display: none更可靠避免部分打印机驱动忽略 display 属性。4. 进阶技巧让叫号系统具备业务扩展能力4.1 支持多窗口轮转动态分配窗口与叫号策略保健中心常有 A/B/C 三个窗口需按顺序轮叫或按负载均衡分配。在CallSystem中扩展windows数组和调度逻辑// 修改 constructor this.windows [ { id: A01, name: 内科, status: free }, { id: B02, name: 中医科, status: free }, { id: C03, name: 检验科, status: free } ]; this.currentWindow A01; // 默认窗口 // 新增方法按轮询策略分配窗口 assignWindow() { const freeWindows this.windows.filter(w w.status free); if (freeWindows.length 0) return this.currentWindow; // 找到第一个空闲窗口标记为 busy const target freeWindows[0]; target.status busy; this.currentWindow target.id; this.saveToStorage(); return target.id; } // 修改 callNext() callNext() { if (this.queue.length 0) return; this.currentNumber this.queue.shift().id; this.currentWindow this.assignWindow(); // 动态分配 this.status calling; this.saveToStorage(); this.render(); }策略对比表策略类型实现方式适用场景固定窗口this.currentWindow A01单科室、无分流需求轮询分配assignWindow()中按数组顺序取首个空闲多科室均摊压力手动指定在 UI 添加下拉菜单select idwindowSelect护士根据患者类型主动指派负载感知扩展windows对象增加callCount字段每次叫号后分配时选最小值长期运行后平衡各窗口工作量4.2 号码优先级管理用 CSS 自定义属性标记 VIP 号普通号2405200001与 VIP 号VIP-2405200001需视觉区分。不新增字段用 CSS 自定义属性>// takeNumber() 中判断 VIP takeNumber(isVIP false) { const serial isVIP ? VIP-${new Date().getFullYear().toString().slice(-2)}${String(new Date().getMonth() 1).padStart(2, 0)}${String(new Date().getDate()).padStart(2, 0)}${String(this.queue.length 1).padStart(4, 0)} : ${new Date().getFullYear().toString().slice(-2)}${String(new Date().getMonth() 1).padStart(2, 0)}${String(new Date().getDate()).padStart(2, 0)}${String(this.queue.length 1).padStart(4, 0)}; this.queue.push({ id: serial, time: new Date().toTimeString().slice(0, 8), priority: isVIP ? high : normal }); this.saveToStorage(); this.render(); }/* 在 style 中追加 */ .queue-item[data-priorityhigh] { color: #fbbf24; font-weight: bold; text-shadow: 0 0 8px rgba(251, 191, 36, 0.5); }实际应用前台扫码识别会员卡后自动调用system.takeNumber(true)或在取号按钮旁加「VIP 通道」开关切换isVIP参数。CSS 自定义属性无需修改 DOM 结构复用现有渲染逻辑符合渐进增强原则。4.3 数据导出与统计一键生成当日叫号 Excel 表格保健中心每月需向卫健部门提交服务量报表。用SheetJSxlsx.min.js生成.xlsx文件不依赖后端!-- 在 index.html head 中引入 -- script srchttps://cdn.jsdelivr.net/npm/xlsx0.18.5/dist/xlsx.min.js/script// 新增导出方法 exportTodayReport() { const today new Date().toISOString().split(T)[0]; // 2024-05-20 const data this.queue.map(item ({ 号码: item.id, 取号时间: item.time, 服务状态: item.id.startsWith(VIP-) ? VIP : 普通 })); const ws XLSX.utils.json_to_sheet(data); const wb XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 叫号记录-${today}); XLSX.writeFile(wb, 保健中心叫号-${today}.xlsx); }使用方式在页面底部加button onclicksystem.exportTodayReport()导出今日报表/button。SheetJS体积仅 124KB支持 IE11生成的 Excel 可被 WPS/Office 直接打开字段名用中文定义无需额外映射。导出内容来自localStorage历史队列不依赖实时状态确保数据一致性。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询