医院叫号大屏自适应HTML5模板源码实战
2026/9/16 4:22:31 网站建设 项目流程

简介:这套基于HTML5的医院叫号大屏模板源码,面向门诊大厅、候诊区等公共场所的信息发布场景,用于实时播报与展示当前叫号、候检队列、各科室排号进度。页面同时支持滚动提示与弹框提醒,功能覆盖叫号场景的完整展示逻辑,且具备自适应布局,适配常见大屏分辨率;代码结构独立,无论信息科直接部署,或是前端开发者在此基础上二次改造,上手门槛都比较低。压缩包共22个文件,大小仅315KB,包含2个HTML入口页面、2套CSS样式、2组JavaScript交互脚本和12张PNG图片素材,另有说明文档与DB数据文件,页面结构、交互逻辑与视觉资源划分清晰,方便替换图片、调整配色或扩展科室模块。当前已有260人学习使用。打开index.html即可直接预览大屏效果,也可参考代码将规则接入实际叫号数据;模板自带多种风格素材,适合作为医院信息化展示、毕业设计或商业项目快速落地的起点。

1. 医院叫号大屏的自适应,不只是把页面“等比例缩一下”

医院大厅里那块叫号屏,实际运行环境往往比开发机上复杂得多:55 英寸横屏、32 英寸竖屏、拼接墙上的 4K 画面,可能同时跑着同一套 HTML5 源码。如果把“自适应”理解成按宽高比缩放字体,那只是第一层;真正要解决的是不同屏体下信息排布不溢出、长时间运行不残影、数据断流时有明确提示。这是笔者在交付医院信息显示屏时最常被问到的问题,也是这类“自适应医院叫号大屏模板源码”最容易在验收阶段翻车的地方。下面从一个可落地的 HTML5 模板出发,把布局、数据接入、渲染帧控制和部署校验讲清楚。

2. 自适应叫号大屏的布局引擎:信息分区与 CSS Grid 容错设计

2.1 一块叫号屏上必然出现的信息分区与优先级

叫号屏是功能屏,不是品牌广告屏。患者要在几秒内完成“找自己的号码、找诊室、判断是否过号”这三个动作,所以屏幕内容至少要切分成五个区块:当前呼号、当前诊室或窗口号、候诊队列、时间日期、底部公告。其中当前呼号和诊室号必须放在视觉焦点区,字号最大;候诊队列放在次焦点的侧边区域;公告在底部滚动展示。

自适应不能先从 CSS 写起,要先把这五个区块的优先级定下来。二级医院门诊的叫号屏通常把“当前呼号”放在左中,诊室号紧随其后;而检验科或药房窗口,取药列表的权重反而更高,呼号区可以缩小。模板里一般会预留两套布局方案:layout-mode: clinic表示门诊模式,layout-mode: window表示窗口模式。这种做法比单纯依赖媒体查询更可靠,因为不同科室不是屏幕尺寸不同,而是信息重心不同。

模板落地时,我一般会把屏幕分为“头部信息区、主呼号区、候诊列表区、公告区”四个容器,再通过 CSS Grid 把不同模式映射成不同的网格位置。网格方案确定后,横竖屏切换就只需要切换一行grid-template-areas

2.2 用 CSS Grid 编排自适应叫号大屏的弹性网格,并配合 clamp() 收住边距

CSS Grid 是目前做自适应大屏布局最顺手的方案。下面是一段可直接复用的栅格配置:

.layout { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); grid-template-rows: auto minmax(0, 1fr) auto; gap: clamp(10px, 1.5vw, 24px); padding: clamp(12px, 2vw, 32px); height: 100vh; } .head { grid-column: 1 / -1; } .queue { grid-row: span 2; } .calling { overflow: hidden; } .notice { padding-top: 0.2em; border-top: 1px solid rgba(255, 255, 255, 0.15); }

这里的几个参数是叫号屏自适应的关键。grid-template-columns使用minmax(0, 3fr)而不是裸的3fr 2fr,是为了防止候诊列表里超长姓名把列撑破。fr单位在内容超出时会自动扩张,导致另一列被挤得看不到;加minmax(0, ...)后,列的弹性范围被限制在网格剩余空间内。clamp(10px, 1.5vw, 24px)同时约束了间距和内边距,小屏不挤、大屏不失衡。height: 100vh直接锁高度,避免滚动条破坏大屏观感。

在 21:9 比较宽的带鱼屏上,这个网格会显得中央呼号区太宽,可以引入媒体查询:@media (min-aspect-ratio: 8 / 3) { .layout { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) minmax(0, 2fr); } },把候诊列表挪到最右侧,呼号保持在黄金分割位置。竖屏广告机则把grid-template-columns改成单列,候诊列表移到呼号下方。

2.3 候诊表格自适应宽度:table-layout 与 overflow 的双保险设定

候诊列表是最容易破坏布局的地方。姓名长度、科室简称、排队序号混在一行里,有的屏幕会在第 18 个字符处出现横向滚动条。给表格设置响应式宽度是最经济的做法:

.queue-table { width: 100%; table-layout: fixed; } .queue-table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

table-layout: fixed让列宽不再由内容决定,而是由表格宽度和首行单元格宽度决定。加上text-overflow: ellipsis后,超长姓名的部分会显示为省略号,鼠标悬停也无法在大屏上看到完整内容,所以实现时还应该在td上保留title属性,内容为完整姓名,方便管理人员在调试模式下查看。

不同屏体下,表格的行数和字号可以参考以下参数。行列数不是越多越好,候诊列表显示 6 到 8 行最为清晰,超出部分交给滚动动画。

屏宽比常见屏体候诊列表建议列数呼号字号参考
16:955 英寸横挂3 列clamp(80px, 13vmin, 180px)
21:958 英寸带鱼屏4 列clamp(70px, 12vmin, 170px)
9:1632 英寸竖屏2 列clamp(56px, 9vmin, 120px)

竖屏场景下,表格列数减少是因为可用宽度有限,两列已经能容纳“序号 + 姓名”,第三列“状态”适合用背景色块代替文字。

3. 源码组织与渲染逻辑:用一份 JSON 撬动整块大屏

3.1 模板目录的最小结构:五类文件各管一件事

叫号屏模板的源码组织不需要引入 Vue 或 React。它本质上是单页应用,用原生 JavaScript 反而更容易在老旧安卓盒子上跑稳。一套最精简的目录结构如下:

hospital-board/ ├── index.html ├── config.json ├── css/ │ ├── base.css # 主题色、字体、reset │ └── layout.css # 自适应栅格与媒体查询 ├── js/ │ ├── config.js # 读取 config.json 的入口 │ ├── api.js # WebSocket 与 fetch 封装 │ └── render.js # DOM 渲染与动画入口 └── assets/ └── audio/ └── ding.mp3 # 呼号提示音

这类模板不依赖 node_modules,直接把文件夹拷到内网任意静态服务器就能运行,适合做源码建站式的快速交付。config.json放的是屏幕级配置,比如screenId、科室名称、是否启用语音播报;config.js负责在页面加载时异步读取它。这样现场实施人员只需要改 JSON,不需要碰 JavaScript 代码。

index.html内只需要保留根节点和静态资源引用:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/layout.css"> </head> <body> <main id="board" class="layout">{ "screenId": "entrance_3f", "target": "tick", "payload": { "room": "消化内科3诊室", "current": "A028", "queue": ["A029", "A030", "A031", "A032"], "overdue": ["A021", "A025"], "notice": "过号患者请前往导诊台登记" }, "ts": 1700000000 }

screenId用于标识数据目标是哪块屏;target表示消息类型,当前主要是tick,即叫号事件;payload.room是诊室名称,payload.current是当前呼号的号码,queue是候诊数组,overdue是过号数组,notice是底部公告。把公告也放进同样的 payload,是因为很多医院希望临时通知能直接推到指定屏上,而不是让实施人员跑到现场改页面。

字段解析建议按“先整体、后局部”的方式做。整体是指ts时间戳是否比本地已有数据新;如果新消息的ts小于等于当前值,直接丢弃,这能避免后端重复推送导致页面抖动。以下是一个能直接用的接收函数骨架:

function applyMessage(raw) { let msg; try { msg = JSON.parse(raw); } catch (err) { return; } if (msg.ts <= state.updatedAt) return; state.room = msg.payload?.room || state.room; state.current = msg.payload?.current || state.current; state.queue = msg.payload?.queue || state.queue; state.overdue = msg.payload?.overdue || state.overdue; state.notice = msg.payload?.notice || state.notice; state.updatedAt = msg.ts; }

state.updatedAt是整份状态的版本号,也是后面“自适应频率控制”的基础。

3.3 一次 WebSocket 消息如何变成屏幕上的呼号动画

数据接收到之后不直接改 DOM,而是先合并进状态对象,再挂一个渲染任务。这样做的原因是大屏可能在一秒内连续收到多次推送,如果每次都触发 DOM 操作,低性能设备会出现明显的卡顿。模板里常见的做法如下:

let renderQueued = false; function scheduleRender() { if (renderQueued) return; renderQueued = true; requestAnimationFrame(() => { renderQueued = false; renderDOM(state); }); } function renderDOM(s) { document.getElementById('call-num').textContent = s.current; document.getElementById('room-name').textContent = s.room; renderQueueList(s.queue); }

requestAnimationFrame保证同一帧内的多次数据更新只触发一次 DOM 写入,这就是“自适应频率控制”在 HTML5 大屏里的典型应用:更新频率跟随浏览器刷新帧率,而不是跟随后端推送频率。

state.current发生变化时,模板会为呼号区补一个弹跳动画,也就是 HTML5 动画里最常见的pop效果:

@keyframes pop-in { 0% { transform: scale(0.8); opacity: 0.4; } 80% { transform: scale(1.04); opacity: 1; } 100% { transform: scale(1); opacity: 1; } } .pop { animation: pop-in 0.35s ease-out; }

重触发动画的技巧是“先重置再添加类”:

const el = document.getElementById('call-num'); el.classList.remove('pop'); void el.offsetWidth; el.classList.add('pop');

offsetWidth的读取会强制浏览器进行一次样式重算,确保动画能重新播放。

4. 实时叫号接入、断线重连与自适应频率刷新的落地细节

4.1 短轮询、SSE 与 WebSocket:叫号屏实时通道的选型对比

医院内网环境比较特殊,老旧的呼叫系统往往只提供一个 HTTP 查询接口,并没有独立的推送服务。接入方式需要根据后端能力来选:

传输方式实现成本典型延迟适合场景
短轮询很低5~15 秒后端只开放 HTTP 查询接口
SSE秒内后端可独立部署,单向推送足够
WebSocket中高毫秒级有独立推送服务,或存在过号即时提醒需求

模板一般会封装数据源接口,启动时读取config.json里的transport字段来决定走哪条通道。如果选短轮询,最简单的做法是setInterval(fetchData, 5000),并保证上一次请求结束后再计时,避免请求堆积。

如果后端可以直接建 WebSocket,推荐在消息里带上screenId,让服务端精确推送,而不是广播到所有屏。否则同一网段几十块屏同时收到无关消息,渲染层的过滤逻辑写得再好,网络带宽也会被浪费。

4.2 心跳、指数退避重连与陈旧数据提示

大屏长期运行,Wi-Fi 或网线端口都可能出现短暂断开。WebSocket 断线时浏览器会自动触发onclose,但网络处于“半开状态”时,onclose可能延迟很久,所以必须有应用层心跳。下面这段完整代码可以直接放进js/api.js

let ws = null; let failCount = 0; let staleTimeout = null; function connect() { ws = new WebSocket(`ws://${location.host}/ws?screenId=${SCREEN_ID}`); ws.onopen = () => { failCount = 0; hideStaleBanner(); }; ws.onmessage = (ev) => { applyMessage(ev.data); scheduleRender(); }; ws.onclose = () => { ws = null; const delay = Math.min(30000, 3000 * Math.pow(2, failCount++)); setTimeout(connect, delay); }; ws.onerror = () => ws.close(); } setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "ping", ts: Date.now() })); } if (Date.now() - state.updatedAt > 10000) { showStaleBanner(true); } }, 5000);

failCount控制重连间隔:第一次失败 3 秒后重试,第二次 6 秒,依次翻倍,最多 30 秒。这样在网络抖动时恢复快,长时间断网时也不会频繁握手。陈旧数据提示则是根据state.updatedAt判断,超过 10 秒没收到新数据就显示“数据连接中断”,而不是让屏上留着旧号码造成叫号延误。这里的 10 秒和 5 秒要按实际推送频率调,如果该系统每分钟只推送一次叫号消息,阈值应放大到 70 秒。

4.3 用自适应频率控制把 HTML5 动画的帧消耗压下去

大屏设备性能差异极大,从 i5 工控机到几百元的安卓盒子都有。同一条呼号动画在高端设备上流畅,在低端盒子上就可能掉帧。几个有效的降载手段集中在渲染函数里:

第一,所有列表更新用增量渲染,不要重建整张表格。候诊队列更新时,先对比新旧数组,只对新插入的节点调用insertAdjacentHTML,其余节点只改文本。

第二,动画属性只使用transformopacity,不要用topleftheight做动画。CSS 动画触发重排时,大屏上数千个像素点的代价是明显的。

第三,多加一个简单比较,在刷新前过滤重复数据:

function shouldUpdate(prev, next) { return prev.current !== next.current || prev.room !== next.room || prev.queue.join(',') !== next.queue.join(','); }

queue.join(',')在队列长度只有几十条时开销可忽略,却能挡住一半以上重复推送。装在这些细节上,低端盒子的 CPU 占用能下降 30% 以上。

5. 收尾的最后一公里:防烧屏、kiosk 部署和同屏校验

5.1 交付前的稳定性检查表

叫号屏交付给医院后,实施人员往往不在现场,所以安装前必须把运行环境参数固定下来。常用的检查项如下:

检查项推荐设置说明
屏保与休眠系统层面关闭任何电源休眠都会中断 WebSocket
浏览器启动全屏模式或 kiosk 模式Chrome 可用--kiosk参数
分辨率缩放4K 屏设 200% 或 300%避免vmin计算出现偏差
开机自启写脚本加入启动项掉电恢复后能自动拉起浏览器
夜间刷新凌晨 3 点强制 reload清掉长期驻留的页面内存

夜间刷新可以通过一段setInterval判断时间实现,注意在模板里加开关。如果院方不允许页面定时刷新,就要把数据异常自动重启的逻辑做得更保守一些。

5.2 用状态自检页与内屏切换保证 7×24 可观测

最后推荐一个轻量做法:在模板里增加debug.html,它不渲染叫号界面,只显示连接状态和数据版本号。现场排查时,维护人员访问http://<屏的IP>/debug.html?screenId=entrance_3f,就能立刻看到这块屏有没有收到数据。

<script> const screenId = location.search.split('screenId=')[1]; fetch(`/api/status?screenId=${screenId}`) .then(r => r.json()) .then(d => { document.body.textContent = (Date.now() - d.ts < 10000) ? `在线:${d.screenId},最后更新 ${d.ts}` : `失联:${d.screenId}`; }); </script>

自检页只需要说明“在线还是失联”,不用做复杂报表。把自检逻辑与主模板分离,是为了让维护入口不受叫号页面的动画和脚本影响,即便主页面渲染崩溃,自检页依然能工作。剩下的部署细节,比如把自检页加入收藏夹、把 IP 绑定到屏身标签上,都是现场实施时顺手做的事,但能省掉后续大量沟通成本。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询