☰
HIS系统源码拆解:从AJAX+JSON到fetch的前端改造实战
2026/10/14 4:06:39 网站建设 项目流程

简介:这是一份面向医疗信息化前端开发者的HIS系统前端界面源代码包,围绕登录注册、挂号、我的病历、我的药方等核心业务页面,完整展示了医院信息系统前端交互的实现方式。资源共42个文件,压缩包大小736KB,其中9个HTML页面构成主要功能界面,8个JavaScript脚本(含jQuery库)用于DOM操作、事件监听与异步通信,6个CSS文件负责页面布局与样式,另有2个JSON文件用于模拟或传输挂号、病历、药方等医疗数据,搭配JPG、PNG、GIF图片资源完善界面展示。Ajax技术的应用可在不刷新页面的情况下请求并更新局部数据,JSON作为轻量级数据交换格式,与后端数据库信息对接;JS目录和registry目录进一步展示了独立脚本与注册逻辑的组织方式。已有2808人学习下载。通过研究这份源码,可以掌握医疗信息系统前端的页面规划、jQuery交互写法、Ajax异步数据刷新和CSS样式设计,理解真实HIS业务模块从界面到数据流转的完整链路,适合作为医疗信息化或Web前端项目的实践参考。

1. 这套 HIS 源码包:先想清楚它解决什么问题再动手

“HIS系统源代码及说明.rar”解压之后,你看到的是一整套传统 Web 业务系统的完整骨架。它的前端技术栈非常朴素:AJAX 负责异步请求,JSON 负责数据交换,JavaScript 负责页面交互,没有打包器、没有前端框架,甚至可能连依赖都没做版本管理。这恰恰是它最大的价值——你能在一套真实的挂号、收费、药房、门诊业务流程里,看清楚老式业务系统是如何组织代码的。适合想接手传统医院信息系统的开发者,也适合想从纯前端过渡到业务系统的从业者。下载之后我先翻“说明文档”,再打开页面源码看一条请求链路,别急着盲目跑起来。

2. AJAX + JSON + JavaScript:这套源码的前端交互骨架

2.1 请求入口:页面级代码里共用的 ajax 封装

打开这套源码里的任意一个 JSP 页面,你会发现很少有页面直接写原生 XMLHttpRequest。绝大部分请求都收敛到公共 JS 文件里某个封装函数,比如js/common.js里的sendRequest。别小看这个封装,它决定了整个系统的请求风格:统一处理 URL 拼接、POST 表单序列化、超时、异常提示。我之前拆过一套类似的老 HIS,这个函数差不多长这样:

// 公共 AJAX 封装(示例来自某医院 HIS 前端公共 js) function sendRequest(url, params, callback) { var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); var body = ''; if (params) { var arr = []; for (var key in params) { arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); } body = arr.join('&'); } xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { var data = JSON.parse(xhr.responseText); if (data.success) { callback(data); } else { alert(data.msg || '操作失败'); } } else { alert('请求失败,状态码:' + xhr.status); } } }; xhr.send(body); }

这段代码里的重点是三处:第一,请求头固定为表单格式,后端按request.getParameter取值,你收到源码后不要试图改成 JSON 请求头,否则后端取值全变 null;第二,回调函数统一收一个data对象,页面层并不直接操作xhr.responseText,这个约定能让所有页面保持同一套状态码判断;第三,所有 POST 参数都做了encodeURIComponent编码,这是为了避免中文姓名、药品名称在传输中出现乱码。你在改造前端时,只要保住这三个约定,其余逻辑都容易迁。

2.2 JSON 结构:后端返回的统一数据契约

老式 HIS 里前后端不分离,但 JSON 结构还是很有规律。后端无论返回列表还是详情,都会套一层固定外壳,我称它为“业务外壳 + 数据体”。典型结构如下:

{ "success": true, "msg": "操作成功", "code": "0", "data": { "patientId": "P20240001", "name": "张三", "cardNo": "100200300", "deptName": "内科", "doctorName": "李四" } }

这套源码里的每个接口返回值基本都是这个模式。success控制页面层是否进入回调,msg用于弹窗展示,code保留给特殊情况区分,data才是你要渲染的业务数据。你在维护时经常会遇到后端偷偷把data从对象改成数组,前端拿到data.length判断就失效了。所以我的习惯是拿到资源后先把所有接口返回的 JSON 样本整理成一张字段表,再对照页面里的引用点,这个动作能避免后面很多玄学问题。

2.3 渲染层:直接操作 DOM 而不是用模板引擎

这套源码的前端渲染方式非常“原始”:拿到 JSON 之后手工创建元素拼进表格。没有 Vue 的v-for,没有 jQuery 的模板插件,全部是循环加createElement。我看过其中收费页面的源码,渲染患者列表的逻辑可以简化成下面这样:

function renderPatientList(list) { var tbody = document.getElementById('patientTbody'); tbody.innerHTML = ''; for (var i = 0; i < list.length; i++) { var tr = document.createElement('tr'); var tdPatient = document.createElement('td'); tdPatient.textContent = list[i].patientId; tr.appendChild(tdPatient); var tdName = document.createElement('td'); tdName.textContent = list[i].name; tr.appendChild(tdName); var tdDept = document.createElement('td'); tdDept.textContent = list[i].deptName; tr.appendChild(tdDept); tbody.appendChild(tr); } }

页面多起来之后,这种写法会产生大量重复代码,但好处是调试非常直接。你在浏览器控制台打断点,能一步一步看到数据从 JSON 的data字段变成表格行的全过程。我建议你读源码时不要嫌它啰嗦,把渲染函数看懂,就理解了这套系统大半个前端。参数上要注意的是textContent和innerHTML混用:源码里有些老旧页面用innerHTML拼字符串,遇到患者姓名里带<或>时会出现渲染异常,这是历史包袱,不是需求。

3. 落地运行:解压、配库、把登录流程完整走通

3.1 环境准备:确认 JDK 版本、数据库脚本和中间件位置

这份源码是传统 Web 项目结构,不是 Maven 工程,是一个 Eclipse/MyEclipse 可以直接导入的 Web 项目。第一步要装的是 JDK 1.7 或 1.8,Tomcat 7 或 8.5。JDK 版本如果高于 1.8,部分老框架在启动时会在 XML 解析或 JSP 编译阶段报错。数据库脚本一般放在sql目录,打开说明文档确认数据库版本。以 MySQL 5.7 为例,初始化命令如下:

# 进入 sql 脚本所在目录 mysql -uroot -p < his_schema.sql mysql -uroot -p < his_init_data.sql

执行完这两条命令之后,确认库里出现了his_user、his_patient、his_register等核心表。我遇到过一种情况:his_schema.sql建表成功,但his_init_data.sql因为包含中文字段且文件编码不是 UTF-8,导入后所有中文变成乱码。解决办法是在导入前先执行:

# 强制以 utf8 读取脚本文件,避免中文乱码 mysql -uroot -p --default-character-set=utf8 < his_init_data.sql

之后把项目放进 Tomcat 的webapps目录,或者在 IDE 里关联 Tomcat 后启动。初次启动要重点看日志里的INFO和WARN,只要没有Exception或Error级别的报错,说明基础环境就绪。数据库连接配置通常在WEB-INF/classes下的jdbc.properties,里面会有jdbc.url、jdbc.username、jdbc.password三个值,记得改成你自己的本地数据库账号。

3.2 登录请求:从按钮点击到 session 写入的完整链路

登录功能是理解这套系统前后端联动最好的入口。在源码里找到登录页面,比如login.jsp,页面底部往往有一段初始化脚本。点击登录按钮后,前端先做本地校验,再调用上一章说的sendRequest。我简化了这段源码的关键部分:

function doLogin() { var username = document.getElementById('username').value; var password = document.getElementById('password').value; if (username === '' || password === '') { alert('请输入用户名和密码'); return; } // 密码字段在源码里一般是明文传输,生产环境会做改造 sendRequest('login.action', { username: username, password: password }, function (data) { var user = data.data; if (user && user.userId) { window.location.href = 'main.jsp?userId=' + user.userId; } else { alert(data.msg); } }); }

这段逻辑有两个坑。第一,“login.action” 这个 URL 对应后端一个 Servlet,整个工程的 action 后缀通常通过 web.xml 里的 servlet-mapping 来统一拦截,你改动前台时不要随意改 URL 后缀。第二,登录成功跳转到main.jsp后,主页面会再发起一次请求获取菜单权限树,那个请求依赖 session 里保存的用户信息。如果你直接绕过登录页面访问业务页面,会看到菜单加载失败,因为 session 里根本没有用户实体。这也是这套系统考验你对 HTTP 无状态理解的地方。

3.3 增删改查走通:以一个收费页面的结算动作为例

收费窗口页面通常是整套 HIS 里交互最复杂的部分。它的核心操作是:选择患者 → 获取待收费项目 → 确认结算 → 刷新费用列表。这里每条操作都对应一个 AJAX 请求,我建议按下面的顺序逐条走通:

第一步,在页面下拉框里切换患者,触发getPendingFees.action,后端返回待收费列表。第二步,点击“结算”按钮,前端收集所选条目 ID 数组,POST 给settleFees.action。第三步,结算成功后,前端重新调用getPendingFees.action刷新列表,同时调用getTodayIncome.action更新今日收入总计。下面是我从源码里整理出的结算按钮逻辑片段:

function settleFees(patientId) { var feeIds = []; var checkboxes = document.querySelectorAll('input[name="feeCheck"]:checked'); for (var i = 0; i < checkboxes.length; i++) { feeIds.push(checkboxes[i].value); } if (feeIds.length === 0) { alert('请选择需要结算的费用项目'); return; } sendRequest('settleFees.action', { patientId: patientId, feeIds: feeIds.join(',') }, function (data) { if (data.success) { loadPendingFees(patientId); // 刷新待收费列表 loadTodayIncome(); // 刷新今日收入统计 } else { alert(data.msg); } }); }

这里最关键的参数是feeIds:后端拿到的可能是字符串,也可能是数组,取决于源码里的拆分方式。如果后端用String.split(','))解析,前端传feeIds: feeIds.join(',')没问题;如果后端直接request.getParameterValues('feeIds'),你传一个逗号拼接字符串反而会得到 null。所以你在二次开发时,一定要先翻开后端 Servlet 或 Action 的取值代码,再决定前端传参格式。

4. 前端改造实战:把老式 AJAX 换成 fetch 的完整路径

4.1 改造前的代码体检:先定位请求集中层

当你拿到这份源码准备做现代化改造,我建议不要打开页面就改,先花一小时做一次“请求集中度体检”。打开工程里的 JS 文件,统计一下sendRequest被调用的次数。如果所有页面都调同一个公共函数,说明请求层是收敛的,你只需要改一个文件。如果页面各自为政,散落着大量原生 XHR 写法和 jQuery$.ajax,那改造范围会大得多。

这套资源里的情况通常是前者:公共js/common.js文件是唯一的请求入口。我的改造策略是保留sendRequest的函数签名,不新造轮子,让所有业务页面无感切换。具体做法是把sendRequest的函数体替换成 fetch 实现,对外暴露的参数和回调行为完全不变。

4.2 fetch 替换 XHR 的兼容写法与错误处理

下面是我在改造时会直接采用的写法,它保持参数签名不变,代码风格改为 Promise 配合 async。因为我只想动一个公共文件,不想牵连几十个页面:

// 改造后的 sendRequest:内部用 fetch,对外行为与旧版一致 async function sendRequest(url, params, callback) { var body = ''; if (params) { var arr = []; for (var key in params) { arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); } body = arr.join('&'); } var resp; try { resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: body, credentials: 'same-origin' }); } catch (e) { alert('网络异常,请检查连接'); return; } var data = await resp.json(); if (data.success) { callback(data); } else { alert(data.msg || '操作失败'); } }

这段代码引入了一个关键变化:credentials: 'same-origin'。老 HIS 的登录状态靠的是 session cookie,fetch 默认携带同源 cookie 没问题,但如果你把前端页面拆到另一个端口部署,fetch 的默认凭证策略会导致 cookie 不带,登录状态全部失效。另一个变化是错误处理从 XHR 的status判断变成了 fetch 的异常捕获,网络断开、跨域拦截会进入 catch 分支。这里要注意 fetch 在 HTTP 4xx 状态下并不会抛异常,所以你在调用方不能依赖 catch 判断业务错误,必须统一走data.success。

4.3 JSON 字段映射:改造时最容易翻车的隐藏角落

把 XHR 换成 fetch 本身不复杂,真正容易翻车的是后续的 JSON 处理差异。老系统的后端可能输出带 BOM 头的 JSON 字符串,resp.json()有时候解析失败。另一个常见问题是后端返回的 JSON 里包含 Java 日期格式,比如"2024-01-15 14:30:00",前端拿到后如果不处理直接拼进表格,显示没问题;但如果你做了二次格式化,就容易踩时区坑。

我处理 JSON 字段映射的习惯是加一层数据清洗函数,统一处理data里的 null 值和日期字符串:

function normalizePatient(patient) { return { id: patient.patientId || '', name: patient.name || '未知', cardNo: patient.cardNo || '无卡号', deptName: patient.deptName || '未分配科室', visitTime: patient.visitTime ? patient.visitTime.replace('T', ' ') : '' }; }

所有页面调用渲染函数之前,先把原始对象过一遍normalizePatient。这样做的收益是,后端改名时不会让前端所有页面一起炸掉。你在改造这套源码时,务必把字段清洗作为独立模块加入改造计划。

5. HIS 系统改造避坑:五个高频问题的排查记录

5.1 现象:AJAX 请求永远命中缓存,改的后端代码不生效

排查时发现浏览器控制台里每次请求都返回 200,但响应内容一直是旧数据。关掉浏览器缓存后立刻正常。原因在 Tomcat 的响应头没有禁止缓存,老式doGet或doPost里没有写Cache-Control,浏览器在某些条件下缓存了 POST 响应。解决方式是在后端响应处加一行response.setHeader("Cache-Control", "no-cache")。我一般更彻底,直接在工程的过滤器里给所有响应统一加禁用缓存头,避免你一个个 Servlet 去补。

5.2 现象:中文参数传到后端变成问号

提交患者姓名“张三”,后端日志看到的是??。这个问题的根源是前端拼接application/x-www-form-urlencoded时只对参数体做了编码,但 JSP 页面本身的 charset 不是 UTF-8。老项目里 JSP 顶部经常写pageEncoding="GBK"。解决方法是统一三个位置:JSP 的pageEncoding、Content-Type里的 charset、数据库连接 URL 里的characterEncoding=utf8。三个编码不一致,中文必乱。这是我拆老 HIS 时遇到最频繁的坑,几乎每次改动涉及新增页面都会踩一轮。

5.3 现象:登录成功后跳转 main.jsp,又被弹回登录页

明明登录成功了,地址栏也到了main.jsp,但页面刚渲染完就跳回login.jsp。打开 Network 面板发现main.jsp加载后连续发起多个 AJAX 请求,其中一个返回 302。原因是菜单权限请求的 session 校验失败,常见是 Tomcat 的 session cookie 名在新旧版本不兼容,或者前端写死了跳转地址。解决方式是在主页面加载时先调用一次/getCurrentUser.action,把返回结果存进前端变量,后续菜单请求都带这个用户上下文,而不是每次依赖服务端 session。

5.4 现象:日期列显示 “NaN-NaN-NaN”

页面上某列日期显示成NaN-NaN-NaN,其他列正常。原因是后端返回的日期格式是"2024-01-15 14:30:00",前端有人用new Date("2024-01-15 14:30:00")去解析。老版本浏览器对这个格式解析不稳定。解决方式是避免用new Date直接解析,改用字符串拆分成new Date(year, month-1, day)之后再格式化。我在这套源码里也见到过专门处理日期的工具函数,如果里面用了split('-')之后忘记减 1,月份就会偏大一个月,这是 JS 日期默认的月份 0-11 机制导致的,排查时第一反应先看月份减没减。

5.5 现象:多个异步请求并发时,表格数据显示错位

用户快速点击不同的科室按钮后,费用列表偶尔显示成上一个科室的数据。原因每个按钮触发一个独立的 AJAX,请求返回顺序不一致,后点按钮先返回,先点按钮后返回,页面最后一次渲染的可能是旧请求的数据。解决方式是用“请求序号”做守卫。改造时我先声明一个全局变量let requestSeq = 0,每次发起请求前let curSeq = ++requestSeq,在回调里判断if (curSeq === requestSeq)才执行渲染。这个方法不改变请求本身,只是确保旧响应不覆盖新数据。我把这段逻辑写进公共请求封装后,问题再没出现过。

6. 把一个页面读透:从源码反推整套 HIS 的框架约定

6.1 用请求 URL 反推后端路由表

拿到源码包后,最快熟悉整套系统的方式不是去读十几个 Servlet,而是打开任意一个业务页面,把页面里所有 AJAX 请求的 URL 摘出来。比如收费页面里有getDeptList.action、getDoctorList.action、queryRegisterInfo.action。看 GET 参数和返回 JSON,你就能反推每个接口的表结构和业务状态。我习惯建一张映射表,第一列写 URL,第二列写参数,第三列写返回 data 结构,第四列写对应页面文件。这张表不需要完整,只需要覆盖核心流程,就能在头脑中搭出整系统的模块边界。

6.2 从返回结构推断前端的状态维护方式

看 JSON 的data字段数量,能判断页面是重交互还是轻交互。拿就诊卡绑定页面举例,data返回患者基本信息加余额,页面每次操作后前端都会重新请求全部数据,这说明系统设计是“无状态前端”。改造时你不需要引入状态管理,继续保持每次请求全量刷新即可。千万不要在既有代码里强行加入“仅更新单个字段”的优化,一旦与原有刷新逻辑冲突,会出现界面跟数据库不一致的诡异现象。

6.3 一个我坚持到现在的习惯

以前我接手一套老 HIS 时,因为没做“请求集中层体检”,直接拿着一个页面就开改,做到一半发现三个页面用了三套不同的请求写法,导致改了公共封装后部分页面报错,本来两天的活硬生生拖了一周。从那以后,我每次拆解这类源码包,第一晚必定只做一件事:把所有 JS 文件里的网络请求调用点全部列出来,统计公共函数的覆盖比例,低于百分之八十就放弃全局改造,改用页面级渐进替换。这一步听起来慢,实际是最快的后悔药。希望帮到你。

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

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

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

立即咨询