目录
一、引言
在医疗领域,时间就是生命,尤其是在紧急就医场景下,快速、准确地获取关键信息对于患者的救治和康复至关重要。医院紧急就医大屏展示系统作为一种高效的信息展示方式,能够实时呈现患者的病情、医护人员的排班、医疗资源的分布等关键信息,为医疗团队提供全面、直观的决策支持,从而显著提升医疗效率,保障患者的生命安全。
Vue.js 作为一款流行的 JavaScript 前端框架,以其简洁的语法、高效的性能和灵活的组件化开发模式,在构建用户界面方面表现出色。将 Vue.js 应用于医院紧急就医大屏展示系统的开发,能够充分发挥其优势,实现界面的快速渲染、动态更新以及与后端数据的无缝对接,为医护人员提供流畅、便捷的操作体验。
通过 Vue.js,我们可以轻松实现数据的可视化展示,将复杂的医疗数据转化为直观的图表、图形和动态信息,使医护人员能够在第一时间获取关键信息,做出准确的判断和决策。同时,Vue.js 的响应式原理和虚拟 DOM 技术能够确保界面在数据变化时快速更新,保持信息的实时性和准确性。
此外,Vue.js 丰富的插件生态系统和组件库,如 Element - UI、Vuetify 等,为我们提供了大量现成的 UI 组件和工具,能够大大加快开发速度,提高代码的可维护性和复用性。在医院复杂场景下,这些组件可以帮助我们快速构建出美观、易用的大屏界面,满足不同科室和业务场景的需求。
综上所述,Vue.js 在医院紧急就医大屏展示系统中具有重要的应用价值,能够为提升医疗效率和患者安全提供有力的技术支持。接下来,本文将详细介绍基于 Vue.js 的医院紧急就医大屏展示方案的设计与实现。
二、医院复杂场景分析
(一)紧急就医流程
- 患者入院:患者通过救护车转运、自行前来或其他医疗机构转诊等方式到达医院急诊科。此时,医护人员会在接诊台询问患者基本信息,包括姓名、年龄、职业等,同时了解患者既往病史、现病史及主要症状 ,并根据病情轻重缓急进行分诊,指引患者前往相应的诊室或抢救区。例如,对于心跳呼吸骤停的患者,会立即被送往抢救室进行心肺复苏;而病情相对较轻的患者则会被安排到普通诊室就诊。
- 初步评估与检查:医护人员对患者进行初步评估,运用观察法观察患者面色、呼吸、步态等,通过问诊法询问患者病史、症状等,还会进行必要的体格检查,如测量血压、心率等。之后,根据患者情况安排一系列检查,常规检查项目有生命体征监测、常规血液检测、体格检查等;影像学检查包括 X 线检查、CT 检查、MRI 检查等;实验室检查包含生化检测、微生物学检测、免疫学检测等;特殊检查有心电图检查、内镜检查、穿刺活检等。
- 诊断与治疗方案制定:医生根据检查结果做出诊断,并制定相应的治疗方案。对于急性心肌梗死患者,会立即进行心电图检查,确诊后给予阿司匹林、氯吡格雷等药物治疗,必要时进行急诊 PCI 手术;对于缺血性脑卒中患者给予溶栓治疗,出血性脑卒中患者则控制血压、降低颅内压等;对于严重创伤患者,先对伤口进行止血、包扎、固定等初步处理,评估患者生命体征,必要时进行紧急手术治疗。
- 治疗执行与病情监测:护士及相关医护人员按照治疗方案执行治疗,如进行药物治疗、手术治疗、康复治疗等。在治疗过程中,持续监测患者生命体征、病情变化等,及时调整治疗方案。同时,将患者的治疗进展、病情变化等信息记录在病历中,并在医院信息系统中更新。
- 康复与出院:患者病情稳定后,进入康复阶段,接受康复治疗和护理。医生根据患者康复情况,决定是否可以出院。出院时,为患者提供出院指导和康复建议。
在这个流程中,信息传递和展示的关键节点包括患者入院时的信息登记与分诊、检查结果的获取与展示、治疗方案的制定与传达、治疗过程中的病情监测数据展示以及出院时的康复指导信息提供等。准确、及时的信息传递和展示对于医护人员做出正确决策、患者得到有效治疗至关重要。
(二)数据特点
- 患者信息:包括基本信息(姓名、性别、年龄、联系方式等)、病史(既往疾病史、过敏史等)、诊断信息等,数据类型主要为文本和数值。这类数据在患者入院时采集,后续可能会根据治疗过程中的新发现进行补充和更新,更新频率相对较低,但要求准确性高,因为它是医生了解患者整体情况、制定治疗方案的基础。
- 生命体征数据:如心率、血压、体温、呼吸频率、血氧饱和度等,数据类型为数值。这些数据需要实时监测,更新频率高,通常每秒或每分钟都会有新的数据产生。生命体征数据的实时性要求极高,因为它能直接反映患者的即时身体状况,一旦出现异常,医护人员需要立即做出反应。
- 救治进度数据:涵盖检查项目的完成情况、治疗方案的执行进度、药物使用情况等,数据类型多样,有文本(如检查报告描述)、数值(如用药剂量)、时间(如治疗开始时间、结束时间)等。随着救治过程的推进不断更新,更新频率根据救治阶段和具体操作而定。例如,在手术过程中,手术进度信息会实时更新;而在药物治疗阶段,药物使用记录会按用药时间间隔进行更新。
(三)展示需求
- 数据实时更新:由于患者病情变化迅速,大屏展示的数据必须能够实时更新,确保医护人员随时获取最新信息。例如,生命体征数据要以秒或分钟为单位实时刷新显示,让医护人员及时掌握患者身体状况的变化。
- 突出重点信息:在众多医疗数据中,要突出显示关键信息,如患者的危急生命体征、紧急治疗措施等。可以通过颜色、大小、闪烁等视觉效果来区分重点和非重点信息。比如,当患者心率超过正常范围时,将心率数值用红色字体显示并闪烁提醒。
- 可视化呈现:将复杂的数据转化为直观的图表、图形等可视化形式,便于医护人员快速理解和分析。例如,用折线图展示患者一段时间内的体温变化趋势,用柱状图对比不同患者的各项生命体征指标等。这样可以使医护人员更直观地把握患者病情的发展态势和整体情况。
- 多维度数据展示:能够同时展示患者的基本信息、生命体征、救治进度等多维度数据,提供全面的患者病情视图。例如,在大屏上划分不同区域,分别展示患者基本信息、实时生命体征数据、检查检验结果、治疗方案及执行进度等,方便医护人员综合分析。
- 可交互性:支持医护人员对大屏展示的数据进行交互操作,如查询特定患者的详细信息、放大查看某一数据指标的具体变化等。通过交互操作,医护人员可以更深入地了解数据背后的信息,为医疗决策提供更有力的支持 。
三、Vue.js 技术优势
(一)响应式原理
Vue.js 的响应式原理基于数据劫持和发布 - 订阅模式。在数据劫持方面,Vue.js 利用Object.defineProperty()方法来劫持对象属性的getter和setter操作。当一个 Vue 实例创建时,它会遍历data选项中的所有属性,并使用Object.defineProperty()将这些属性转换为响应式数据。例如:
let data = { message: 'Hello, Vue!' }; let vm = {}; Object.keys(data).forEach((key) => { Object.defineProperty(vm, key, { enumerable: true, configurable: true, get: function () { console.log(`获取属性 ${key}: ${data[key]}`); return data[key]; }, set: function (newValue) { if (newValue!== data[key]) { console.log(`设置属性 ${key}: ${data[key]} -> ${newValue}`); data[key] = newValue; // 这里可以触发更新视图的操作 } } }); }); console.log(vm.message); // 获取属性 message: Hello, Vue! vm.message = 'New message'; // 设置属性 message: Hello, Vue! -> New message
在这个例子中,通过Object.defineProperty()对vm对象的message属性进行了劫持,当获取或设置该属性时,会执行相应的getter和setter函数。
结合发布 - 订阅模式,Vue.js 在数据变化时通知相关的订阅者(通常是视图)进行更新。每个响应式数据都对应一个Dep对象(依赖收集器),它负责收集依赖(即订阅者)。当数据的setter被触发时,Dep会通知所有订阅者执行更新操作。例如,在一个 Vue 组件中:
<template> <div> <p>{ { message }}</p> <button @click="updateMessage">更新消息</button> </div> </template> <script> export default { data() { return { message: '初始消息' }; }, methods: { updateMessage() { this.message = '更新后的消息'; } } }; </script>
当点击按钮调用updateMessage方法更新message数据时,由于数据劫持和发布 - 订阅模式的作用,页面上显示message的<p>元素会自动更新为最新的值。在医院紧急就医大屏中,实时数据更新可以通过这种方式轻松实现。例如,对于患者的生命体征数据,当后台传来新的数据时,只需更新 Vue 实例中对应的响应式数据,页面上展示生命体征的区域就会自动更新,无需手动操作 DOM 来更新数据展示,大大提高了数据更新的效率和实时性。
(二)组件化开发
组件化开发是 Vue.js 的核心特性之一,它将一个复杂的界面拆分成多个独立、可复用的组件,每个组件都有自己的模板、逻辑和样式,通过 props 进行父子组件之间的通信,通过事件来触发组件之间的交互。这种方式极大地提高了代码的复用性和可维护性。
以医院紧急就医大屏展示中的不同模块为例,患者列表可以定义为一个组件,该组件负责获取患者的基本信息(如姓名、年龄、病情等)并进行展示。组件的模板可以定义如下:
<template> <ul> <li v-for="patient in patients" :key="patient.id"> { { patient.name }} - { { patient.age }}岁 - { { patient.condition }} </li> </ul> </template> <script> export default { data() { return { patients: [] }; }, mounted() { // 模拟从后端获取患者数据 this.patients = [ { id: 1, name: '张三', age: 35, condition: '心脏病' }, { id: 2, name: '李四', age: 42, condition: '骨折' } ]; } }; </script>
在其他需要展示患者列表的地方,只需引入并使用这个组件即可,无需重复编写获取和展示患者数据的代码。
生命体征图表也可以定义为一个组件,它接收患者的生命体征数据(如心率、血压、体温等),并使用图表库(如 Echarts)将数据可视化展示。组件的模板和逻辑如下:
<template> <div ref="chart" style="width: 400px; height: 300px;"></div> </template> <script> import echarts from 'echarts'; export default { props: { vitalSigns: { type: Array, default: () => [] } }, mounted() { const chart = echarts.init(this.$refs.chart); const option = { xAxis: { type: 'category', data: ['心率', '血压', '体温'] }, yAxis: { type: 'value' }, series: [ { data: this.vitalSigns, type: 'bar' } ] }; chart.setOption(option); }, watch: { vitalSigns(newValue) { const chart = echarts.init(this.$refs.chart); const option = { xAxis: { type: 'category', data: ['心率', '血压', '体温'] }, yAxis: { type: 'value' }, series: [ { data: newValue, type: 'bar' } ] }; chart.setOption(option); } } }; </script>
当有新的患者生命体征数据时,通过 props 传入该组件,组件会自动更新图表展示。通过组件化开发,每个模块的功能职责明确,代码结构清晰,便于维护和扩展。如果需要修改患者列表的展示样式或生命体征图表的展示逻辑,只需在对应的组件中进行修改,不会影响其他部分的代码。
(三)虚拟 DOM
虚拟 DOM 是 Vue.js 中实现高效更新真实 DOM 的关键技术。它是真实 DOM 的一个轻量级的 JavaScript 对象表示,包含了标签名、属性、子节点等信息,就像是真实 DOM 的一个副本。
在 Vue.js 中,当组件的数据发生变化时,会重新生成一个新的虚拟 DOM 树,然后使用 Diff 算法来比较新的虚拟 DOM 树和旧的虚拟 DOM 树之间的差异。Diff 算法采用同层比较的原则,先比较两个虚拟 DOM 树的根节点,如果根节点相同,再比较它们的属性和子节点;如果根节点不同,就直接替换整个节点。在比较子节点时,会尽量复用已有的节点,对于列表类型的节点,Vue.js 会给每个列表项添加一个唯一的key属性,通过key来准确地识别节点的移动、添加或者删除,以提高 Diff 效率。例如,有以下模板:
<template> <div> <ul> <li v-for="(item, index) in list" :key="index">{ { item }}</li> </ul> <button @click="addItem">添加项目</button> </div> </template> <script> export default { data() { return { list: ['苹果', '香蕉', '橘子'] }; }, methods: { addItem() { this.list.push('草莓'); } } }; </script>
当点击按钮添加项目时,数据list发生变化,Vue.js 会生成新的虚拟 DOM 树,通过 Diff 算法比较新旧虚拟 DOM 树,发现list数组新增了一个元素,然后只更新真实 DOM 中新增的<li>元素,而不是重新渲染整个<ul>。
通过虚拟 DOM 和 Diff 算法,Vue.js 能够将多次 DOM 操作合并,只更新真实 DOM 中真正需要更新的部分,减少了浏览器的重排(reflow)和重绘(repaint)次数,从而大大提高了页面性能,使得在医院紧急就医大屏展示中,即使数据频繁更新,也能保持页面的流畅性和响应速度。
四、技术选型与准备
(一)开发环境搭建
- 开发工具:选用 VSCode 作为主要的开发工具,它是一款轻量级但功能强大的代码编辑器,拥有丰富的插件生态系统,能够为 Vue.js 开发提供良好的支持,如 Vetur 插件,它可以为 Vue 文件提供语法高亮、智能代码补全、错误检查等功能 。
- Node.js 版本:Node.js 是 Vue.js 开发的基础运行环境,建议使用 Node.js 14.x 及以上本,以确保对最新 JavaScript 特性的支持和更好的性能表现。可以通过Node.js 官网下载对应系统的安装包进行安装,安装完成后,在命令行中输入node -v和npm -v,若能正确输出版本号,则说明安装成功。
- 创建 Vue 项目:在确保 Node.js 和 npm 安装成功后,通过以下步骤创建 Vue 项目:
打开命令行工具,进入你希望创建项目的目录,例如cd D:/projects。
使用 Vue CLI 创建项目,命令为vue create emergency - medical - screen,其中emergency - medical - screen为项目名称,可根据实际情况修改。
执行命令后,会出现一系列选项,可选择默认配置快速创建项目,也可自定义配置,如选择是否使用 TypeScript、是否安装路由、是否使用 CSS 预处理器等。对于本项目,选择默认配置即可,等待项目初始化完成。
4. 初始化配置:项目创建完成后,进入项目目录,即cd emergency - medical - screen。在项目的package.json文件中,可以看到项目的基本信息、依赖项以及一些脚本命令。例如,"scripts"字段中定义了项目的启动、打包、代码检查等命令。默认情况下,"serve"命令用于启动开发服务器,"build"命令用于打包项目。可以根据项目需求对这些命令进行修改或添加新的命令。同时,在项目根目录下的.env文件中,可以配置一些环境变量,如后端接口地址等,这些变量在项目运行时会被加载,方便在不同环境下进行配置管理。
(二)引入必要插件
- Echarts:用于数据可视化展示,它提供了丰富的图表类型,如折线图、柱状图、饼图等,能够满足医院紧急就医大屏展示中各种数据可视化的需求。
安装 Echarts:在项目根目录下的命令行中执行npm install echarts --save,等待安装完成。
引入 Echarts:在需要使用 Echarts 的组件中,可以通过以下两种方式引入。
全局引入:在main.js文件中添加以下代码:
import * as echarts from 'echarts';
Vue.prototype.$echarts = echarts;
这样在所有组件中都可以通过this.$echarts来访问 Echarts。
局部引入:在单个组件中引入,例如在src/components/VitalSignsChart.vue组件中:
<template>
<div ref="chart" style="width: 400px; height: 300px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
data() {
return {};
},
mounted() {
const chart = echarts.init(this.$refs.chart);
const option = {
// 配置图表的相关选项,如标题、坐标轴、系列等
title: {
text: '生命体征图表'
},
xAxis: {
type: 'category',
data: ['心率', '血压', '体温']
},
yAxis: {
type: 'value'
},
series: [
{
data: [70, 120, 36.5],
type: 'bar'
}
]
};
chart.setOption(option);
}
};
</script>
2. axios:用于与后端进行数据请求和交互,它是一个基于 Promise 的 HTTP 库,支持浏览器和 Node.js 环境,能够方便地发送 GET、POST、PUT、DELETE 等各种类型的 HTTP 请求。
安装 axios:在项目根目录下的命令行中执行npm install axios --save。
引入 axios:在main.js文件中添加以下代码:
import axios from 'axios';
Vue.prototype.$axios = axios;
这样在所有组件中都可以通过this.$axios来发送 HTTP 请求。例如,在获取患者列表数据时,可以在组件的mounted钩子函数中使用:
mounted() {
this.$axios.get('/api/patients').then((response) => {
this.patients = response.data;
}).catch((error) => {
console.error('获取患者列表失败', error);
});
}
其中/api/patients是后端提供的获取患者列表的接口地址,实际使用时需要根据后端接口进行调整。
五、具体实现方案
(一)数据获取与处理
- 接口设计:与后端的数据接口采用 RESTful 风格,以确保接口的简洁性和可维护性。例如,获取患者列表数据的接口为GET /api/patients,无需传递额外参数,后端将返回所有当前在紧急就医流程中的患者基本信息。获取单个患者详细信息的接口为GET /api/patients/{patientId},其中{patientId}为患者的唯一标识,通过该参数可以获取特定患者的详细病情描述、就诊状态、过往病史等信息。获取患者生命体征数据的接口为GET /api/patients/{patientId}/vital - signs,同样以患者 ID 作为参数,后端返回该患者的实时生命体征数据,如心率、血压、体温、呼吸频率等。
后端返回的数据格式统一采用 JSON 格式。对于患者列表数据,返回示例如下:
[
{
"id": 1,
"name": "张三",
"age": 35,
"gender": "男",
"status": "正在抢救"
},
{
"id": 2,
"name": "李四",
"age": 42,
"gender": "女",
"status": "等待检查"
}
]
对于单个患者详细信息,返回示例如下:
{
"id": 1,
"name": "张三",
"age": 35,
"gender": "男",
"status": "正在抢救",
"medicalHistory": "高血压病史5年",
"symptoms": "胸痛、呼吸困难",
"diagnosis": "急性心肌梗死"
}
对于生命体征数据,返回示例如下:
{
"patientId": 1,
"heartRate": 120,
"bloodPressure": "160/90",
"temperature": 38.5,
"respiratoryRate": 25
}
2. 数据解析与预处理:在前端接收到后端返回的数据后,需要进行解析和预处理,以适应大屏展示的需求。例如,对于时间格式的处理,假设后端返回的时间格式为YYYY - MM - DD HH:MM:SS,而在大屏展示中可能需要将其转换为更友好的格式,如MM月DD日 HH:MM。可以使用moment.js库来进行时间格式的转换。首先安装moment.js,在项目根目录下执行npm install moment --save。在组件中引入并使用moment.js:
<template>
<div>
<p>就诊时间:{
{ formattedTime }}</p>
</div>
</template>
<script>
import moment from'moment';
export default {
data() {
return {
rawTime: '2024 - 10 - 15 14:30:00', // 假设从后端获取的原始时间
formattedTime: ''
};
},
mounted() {
this.formattedTime = moment(this.rawTime).format('MM月DD日 HH:mm');
}
};
</script>
对于数据单位的统一,以血压数据为例,后端返回的可能是字符串形式的160/90,而在展示时可能需要将其拆分为收缩压和舒张压两个数值,并添加单位mmHg。可以这样处理:
const bloodPressureStr = '160/90';
const [systolic, diastolic] = bloodPressureStr.split('/');
const formattedBloodPressure = `${systolic}mmHg / ${diastolic}mmHg`;
通过这些数据解析和预处理操作,能够将后端返回的数据转化为更适合前端展示的格式,提高数据的可读性和可视化效果。
(二)组件设计与搭建
- 患者信息展示组件:该组件用于展示患者的基本信息、病情描述、就诊状态等。以下是一个简单的组件示例:
<template> <div class="patient - info - container"> <h2>{ { patient.name }} - { { patient.age }}岁 - { { patient.gender }}</h2> <p>病情描述:{ { patient.symptoms }}</p> <p>就诊状态:{ { patient.status }}</p> </div> </template> <script> export default { props: { patient: { type: Object, default: () => ({}) } }, data() { return {}; }, mounted() { }, methods: { } }; </script> <style scoped> .patient - info - container { border: 1px solid #ccc; border - radius: 5px; padding: 10px; margin: 10px; background - color: #f9f9f9; } </style>
在使用该组件时,通过 props 传入患者信息对象,即可在页面上展示患者的相关信息。例如:
<template> <div> <patient - info :patient="patientData"></patient - info> </div> </template> <script> import PatientInfo from '@/components/PatientInfo.vue'; export default { components: { PatientInfo }, data() { return { patientData: { name: '张三', age: 35, gender: '男', symptoms: '胸痛、呼吸困难', status: '正在抢救' } }; } }; </script>
- 生命体征监测组件:利用 Echarts 实现折线图、柱状图等图表展示患者生命体征的实时变化。以心率、血压、体温的柱状图展示为例,配置图表的代码如下:
<template>
<div ref="vitalSignsChart" style="width: 400px; height: 300px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
props: {
vitalSignsData: {
type: Array,
default: () => []
}
},
data() {
return {};
},
mounted() {
this.renderChart();
},
methods: {
renderChart() {
const chart = echarts.init(this.$refs.vitalSignsChart);
const option = {
title: {
text: '生命体征监测'
},
xAxis: {
type: 'category',
data: ['心率', '血压', '体温']
},
yAxis: {
type: 'value'
},
series: [
{
data: this.vitalSignsData,
type: 'bar'
}
]
};
chart.setOption(option);
}
},
watch: {
vitalSignsData(newValue) {
const chart = echarts.init(this.$refs.vitalSignsChart);
const option = {
title: {
text: '生命体征监测'
},
xAxis: {
type: 'category',
data: ['心率', '血压', '体温']
},
yAxis: {
type: 'value'
},
series: [
{
data: newValue,
type: 'bar'
}
]
};
chart.setOption(option);
}
}
};
</script>
在使用该组件时,通过 props 传入生命体征数据数组,如[120, '160/90', 38.5],即可实时更新图表展示。
3. 急救进度跟踪组件:该组件以进度条、状态图标等形式展示急救流程的进展。通过数据驱动组件的状态变化,例如,假设急救流程分为接诊、检查、诊断、治疗、康复五个阶段,用数字 0 - 4 表示不同阶段的进度。组件代码如下:
<template>
<div class="progress - container">
<el - progress :percentage="progress * 20" :status="getStatus(progress)"></el - progress>
<div class="status - icons">
<span v - for="(stage, index) in stages" :key="index" :class="{ 'active': index <= progress }">{
{ stage }}</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
progress: 2, // 假设当前处于诊断阶段,进度为40%
stages: ['接诊', '检查', '诊断', '治疗', '康复']
};
},
methods: {
getStatus(progress) {
if (progress === 4) {
return'success';
} else if (progress >= 0 && progress < 4) {
return 'active';
} else {
return 'exception';
}
}
}
};
</script>
<style scoped>
.progress - container {
margin: 10px;
}
.status - icons span {
display: inline - block;
margin: 0 5px;
padding: 5px 10px;
border: 1px solid #ccc;
border - radius: 3px;
}
.status - icons span.active {
background - color: #1890ff;
color: white;
}
</style>
在这个组件中,通过progress数据来控制进度条的百分比和状态图标,当progress数据发生变化时,组件会自动更新展示,直观地反映急救流程的进展情况。
(三)实时更新机制
- 轮询与 WebSocket 对比:轮询是指前端定时向后端发送请求获取最新数据,实现简单,兼容性好,几乎所有浏览器都支持,基于 HTTP 协议,无需额外配置。但缺点也很明显,大量耗费服务器内存和带宽资源,因为是不停的请求服务器,很多时候并没有新的数据更新,因此绝大部分请求都是无效请求;数据不一定是实时更新,要看设置的请求间隔,基本会带点延迟。
WebSocket 是一种基于 HTTP 协议的全双工通信协议,可以实现客户端和服务器之间的实时数据交互。其优点是实时性更强,能够实现实时数据传输,数据更新速度更快;节省网络资源,采用长连接,减少了 HTTP 协议中频繁建立和断开连接的开销;可以实现双向通信,客户端和服务器可以同时发送和接收数据。缺点是在一些老旧浏览器中兼容性不如轮询,需要进行额外的兼容处理;实现相对复杂,需要服务器端和客户端都进行相应的配置和编程。
在本项目中,由于医院紧急就医场景对数据实时性要求极高,需要医护人员能够在第一时间获取患者的最新生命体征、救治进度等信息,所以选择 WebSocket 作为实时数据更新方案,以确保数据的及时传递和展示。
2. 具体实现代码:首先,在项目中引入ws库(如果是基于 Node.js 的后端服务器),用于创建 WebSocket 服务器。在前端,使用浏览器原生的 WebSocket 对象。以下是前端使用 WebSocket 实现实时数据推送和更新的代码示例:
<template>
<div>
<h2>实时生命体征数据</h2>
<p>心率:{
{ vitalSigns.heartRate }}</p>
<p>血压:{
{ vitalSigns.bloodPressure }}</p>
<p>体温:{
{ vitalSigns.temperature }}</p>
</div>
</template>
<script>
export default {
data() {
return {
vitalSigns: {
heartRate: 0,
bloodPressure: '0/0',
temperature: 0
},
socket: null
};
},
mounted() {
this.connectWebSocket();
},
methods: {
connectWebSocket() {
this.socket = new WebSocket('ws://localhost:8080'); // 替换为实际的WebSocket服务器地址
this.socket.onopen = () => {
console.log('WebSocket连接已建立');
};
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.vitalSigns = data;
};
this.socket.onerror = (error) => {
console.error('WebSocket连接错误', error);
};
this.socket.onclose = () => {
console.log('WebSocket连接已关闭');
};
},
disconnectWebSocket() {
if (this.socket) {
this.socket.close();
}
}
},
beforeDestroy() {
this.disconnectWebSocket();
}
};
</script>
在上述代码中,创建了一个 WebSocket 连接,当连接建立时,在控制台输出连接成功信息;当接收到服务器发送的消息时,将消息解析为 JSON 格式,并更新vitalSigns数据,从而实现页面上生命体征数据的实时更新;当连接发生错误或关闭时,在控制台输出相应信息。在组件销毁前,关闭 WebSocket 连接,以释放资源。通过这种方式,实现了基于 WebSocket 的实时数据更新机制,满足医院紧急就医大屏对数据实时性的要求。
六、代码示例解析
(一)完整组件代码展示
以下是一个更完整的患者信息展示组件代码,包含模板、脚本和样式部分:
<template>
<div class="patient - info - container">
<h2>{
{ patient.name }} - {
{ patient.age }}岁 - {
{ patient.gender }}</h2>
<p>病情描述:{
{ patient.symptoms }}</p>
<p>就诊状态:{
{ patient.status }}</p>
<p>过往病史:{
{ patient.medicalHistory }}</p>
<p>最近一次检查时间:{
{ formattedCheckTime }}</p>
<button @click="showPatientDetails">查看详细信息</button>
<el-dialog :visible.sync="dialogVisible" width="40%">
<h3>患者详细信息</h3>
<p>姓名:{
{ patient.name }}</p>
<p>年龄:{
{ patient.age }}</p>
<p>性别:{
{ patient.gender }}</p>
<p>病情描述:{
{ patient.symptoms }}</p>
<p>就诊状态:{
{ patient.status }}</p>
<p>过往病史:{
{ patient.medicalHistory }}</p>
<p>过敏史:{
{ patient.allergyHistory }}</p>
<p>最近一次检查时间:{
{ formattedCheckTime }}</p>
<p>检查结果:{
{ patient.checkResults }}</p>
<p>治疗方案:{
{ patient.treatmentPlan }}</p>
<p>主治医生:{
{ patient.doctor }}</p>
<p>病房号:{
{ patient.roomNumber }}</p>
<p>联系方式:{
{ patient.contactNumber }}</p>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import moment from'moment';
export default {
props: {
patient: {
type: Object,
default: () => ({})
}
},
data() {
return {
dialogVisible: false
};
},
computed: {
formattedCheckTime() {
if (this.patient.checkTime) {
return moment(this.patient.checkTime).format('YYYY-MM-DD HH:mm');
}
return '';
}
},
methods: {
showPatientDetails() {
this.dialogVisible = true;
}
}
};
</script>
<style scoped>
.patient - info - container {
border: 1px solid #ccc;
border - radius: 5px;
padding: 10px;
margin: 10px;
background - color: #f9f9f9;
}
</style>
(二)关键代码注释
- 模板部分:
<h2>{ { patient.name }} - { { patient.age }}岁 - { { patient.gender }}</h2>:展示患者的姓名、年龄和性别。 <p>病情描述:{ { patient.symptoms }}</p>:显示患者的病情描述。 <p>就诊状态:{ { patient.status }}</p>:展示患者当前的就诊状态,如 “正在抢救”“等待检查” 等。 <p>过往病史:{ { patient.medicalHistory }}</p>:显示患者的过往病史。 <p>最近一次检查时间:{ { formattedCheckTime }}</p>:通过计算属性formattedCheckTime展示格式化后的最近一次检查时间。 <button @click="showPatientDetails">查看详细信息</button>:点击按钮时,调用showPatientDetails方法,用于显示患者详细信息的弹窗。 <el-dialog :visible.sync="dialogVisible" width="40%">:使用 Element - UI 的对话框组件,visible.sync双向绑定控制对话框的显示与隐藏,width设置对话框宽度为 40%。在对话框中展示患者的详细信息,包括过敏史、检查结果、治疗方案、主治医生等。
-
脚本部分:
props: { patient: { type: Object, default: () => ({}) } }:接收父组件传递的患者信息对象,类型为 Object,默认值为空对象。
data() { return { dialogVisible: false }; }:定义一个数据属性dialogVisible,用于控制患者详细信息对话框的显示与隐藏,初始值为false。
computed: { formattedCheckTime() { if (this.patient.checkTime) { return moment(this.patient.checkTime).format('YYYY - MM - DD HH:mm'); } return ''; } }:计算属性formattedCheckTime,如果患者有检查时间,则使用moment.js将其格式化为YYYY - MM - DD HH:mm的形式,否则返回空字符串。
methods: { showPatientDetails() { this.dialogVisible = true; } }:showPatientDetails方法,当点击 “查看详细信息” 按钮时,将dialogVisible设置为true,显示对话框。
(三)代码优化建议
- 减少不必要的重新渲染:
对于一些不需要响应式的数据,如患者的静态描述信息(如固定的病情说明等),可以使用Object.freeze()方法冻结对象,避免 Vue 对其进行响应式处理,从而减少响应式系统的开销。例如:
// 在获取患者数据后,假设patient为获取到的患者数据对象
const frozenPatient = Object.freeze(patient);
// 然后将frozenPatient传递给组件
使用v - once指令,对于一些只需要渲染一次,且数据不会改变的元素,如患者的基本信息展示(假设不会再修改),可以添加v - once指令,使数据只渲染一次,减少不必要的渲染。例如 :
<div v - once>
<h2>{
{ patient.name }} - {
{ patient.age }}岁 - {
{ patient.gender }}</h2>
</div>
合理使用计算属性和侦听器:
- 对于一些复杂的逻辑计算,尽量使用计算属性而不是在模板中直接编写复杂表达式。计算属性具有缓存机制,只有在其依赖的数据发生变化时才会重新计算,而模板中的表达式每次渲染都会重新计算,会增加性能开销。
- 侦听器适用于需要在数据变化时执行异步操作或复杂逻辑的场景。如果只是简单的数据转换,应优先使用计算属性。例如,在上述代码中,如果需要在患者的就诊状态发生变化时发送通知给相关医护人员,就可以使用侦听器:
watch: {
'patient.status': {
immediate: true,
handler(newValue, oldValue) {
if (newValue === '抢救成功') {
// 发送通知的逻辑,比如调用后端接口发送通知
this.$axios.post('/api/send - notice', {
message: '患者' + this.patient.name + '抢救成功',
receivers: ['doctor1@hospital.com', 'nurse1@hospital.com']
});
}
}
}
}
- 组件拆分:将复杂的组件拆分成更小的子组件,提高组件的复用性和可维护性。例如,可以将患者详细信息对话框中的内容拆分成一个单独的子组件,在主组件中通过引入该子组件来展示详细信息,这样如果需要修改详细信息的展示逻辑,只需在子组件中进行修改,不会影响其他部分的代码。
七、测试与优化
(一)功能测试
- 数据准确性测试:编写测试用例,模拟从后端获取不同类型的患者数据,包括正常数据、边界数据和异常数据。对于正常数据,如患者的基本信息(姓名、年龄、性别等)、生命体征数据(心率、血压、体温等)、救治进度数据(检查项目完成情况、治疗方案执行进度等),检查前端展示的数据是否与后端返回的数据完全一致。例如,使用 Jest 测试框架,编写如下测试用例:
import { mount } from '@vue/test - utils'; import PatientInfo from '@/components/PatientInfo.vue'; describe('PatientInfo.vue', () => { it('should display correct patient name', () => { const patient = { name: '张三', age: 35, gender: '男' }; const wrapper = mount(PatientInfo, { propsData: { patient } }); expect(wrapper.find('h2').text()).toContain('张三'); }); }); 对于边界数据,如患者年龄为 0 或最大允许年龄、生命体征数据达到临界值等,验证前端是否能正确处理和展示。对于异常数据,如患者姓名为空、生命体征数据为非法值等,检查前端是否有合理的错误提示或数据处理机制。 2. 实时更新效果测试:利用模拟数据生成工具,模拟后端实时推送数据的场景,观察前端数据的更新情况。可以使用 Mock.js 来生成模拟数据,例如: import Mock from'mockjs'; Mock.mock('/api/patients/1/vital - signs', 'get', () => { return { patientId: 1, heartRate: Mock.Random.integer(60, 100), bloodPressure: Mock.Random.string('number', 3) + '/' + Mock.Random.string('number', 2), temperature: Mock.Random.float(36, 37, 1, 1) }; });
在测试过程中,记录数据更新的时间间隔,检查是否满足医院紧急就医场景对实时性的要求(通常要求秒级更新)。同时,观察数据更新时,页面的闪烁、卡顿等异常情况,确保用户体验不受影响。
3. 组件交互测试:针对各个组件的交互功能,如患者信息展示组件的详细信息查看按钮、生命体征监测组件的图表缩放功能、急救进度跟踪组件的进度条点击交互等,编写测试用例。使用测试工具模拟用户的交互操作,检查组件的响应是否符合预期。例如,测试患者信息展示组件的详细信息查看按钮:
describe('PatientInfo.vue', () => {
it('should show patient details dialog when button is clicked', () => {
const patient = { name: '张三', age: 35, gender: '男' };
const wrapper = mount(PatientInfo, {
propsData: { patient }
});
wrapper.find('button').trigger('click');
expect(wrapper.vm.dialogVisible).toBe(true);
});
});
(二)性能优化
- 加载速度优化:
代码拆分:使用 Webpack 的代码拆分功能,将项目代码拆分成多个小块(chunk),根据需要进行加载。例如,在路由配置中使用动态导入,实现路由组件的懒加载:
const routes = [
{
path: '/patient - list',
name: 'PatientList',
component: () => import('@/views/PatientList.vue')
},
{
path: '/patient - details/:id',
name: 'PatientDetails',
component: () => import('@/views/PatientDetails.vue')
}
];
这样,在初始页面加载时,只加载必要的代码,减少初始加载时间。当用户导航到特定路由时,才会加载对应的组件代码。
懒加载:对于一些非关键的组件,如一些辅助信息展示组件、图表的详细说明组件等,使用 Vue 的异步组件进行懒加载。例如:
const AuxiliaryInfoComponent = () => ({
component: import('@/components/AuxiliaryInfo.vue'),
loading: () => import('@/components/Loading.vue'),
error: () => import('@/components/Error.vue'),
delay: 200,
timeout: 3000
});
在模板中使用该异步组件:
<template>
<div>
<auxiliary - info - component v - if="showAuxiliaryInfo"></auxiliary - info - component>
</div>
</template>
只有当showAuxiliaryInfo为true时,才会加载AuxiliaryInfo.vue组件,并且在加载过程中会显示Loading.vue组件,加载失败时显示Error.vue组件。
2. 内存管理:
避免内存泄漏:在组件销毁时,及时清理不再使用的资源。例如,对于使用setInterval创建的定时器,在beforeDestroy钩子函数中使用clearInterval清除定时器:
export default {
data() {
return {
timer: null
};
},
mounted() {
this.timer = setInterval(() => {
// 执行一些定时任务
}, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
}
};
对于添加的事件监听器,如在mounted钩子函数中为window对象添加resize事件监听器,在beforeDestroy钩子函数中移除该监听器:
export default {
mounted() {
this.handleResize = this.onResize.bind(this);
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
onResize() {
// 处理窗口大小变化的逻辑
}
}
};
对于一些全局变量或对象的引用,在组件销毁时将其设置为null,以便垃圾回收机制能够回收相关内存。例如,如果在组件中引用了一个全局的图表对象this.chart,在beforeDestroy钩子函数中设置this.chart = null。
(三)兼容性测试
- 不同浏览器测试:在 Chrome、Firefox、Edge 等主流浏览器上进行兼容性测试。使用 BrowserStack、Sauce Labs 等在线测试平台,或者在本地安装不同版本的浏览器进行测试。在测试过程中,检查页面的布局是否正常,组件的样式是否正确显示,功能是否能够正常使用。例如,在某些浏览器中,可能会出现 CSS 样式不兼容的问题,如 Flexbox 布局在旧版本的 IE 浏览器中不支持。可以使用 Autoprefixer 工具,自动为 CSS 属性添加浏览器前缀,以确保样式在不同浏览器中的兼容性。在项目中安装 Autoprefixer,然后在 Webpack 配置中进行如下配置:
module.exports = { module: { rules: [ { test: /\.css$/, use: [ 'vue - style - loader', 'css - loader', { loader: 'postcss - loader', options: { postcssOptions: { plugins: [ require('autoprefixer') ] } } } ] } ] } };
对于 JavaScript 代码中的 ES6 + 语法,在旧版本浏览器中可能不支持。可以使用 Babel 将 ES6 + 代码编译为 ES5 代码,以确保在不同浏览器中都能正常运行。在项目中安装 Babel 相关依赖,如@babel/core、@babel/preset - env等,然后在.babelrc文件中进行如下配置:
{
"presets": [
[
"@babel/preset - env",
{
"targets": {
"browsers": ["ie >= 11"]
}
}
]
]
}
- 不同屏幕分辨率测试:在不同屏幕分辨率下进行测试,包括常见的桌面分辨率(如 1920x1080、1366x768 等)和移动设备分辨率(如 375x667、414x896 等)。可以使用响应式设计,通过 CSS 媒体查询来适配不同的屏幕尺寸。例如:
@media (max - width: 768px) { .patient - info - container { width: 100%; margin: 5px 0; } }
在上述代码中,当屏幕宽度小于等于 768px 时,修改patient - info - container组件的宽度和外边距,以适应小屏幕设备的显示。同时,在测试过程中,检查页面元素的布局是否合理,是否出现元素重叠、文字溢出等问题。对于一些需要适配高分辨率屏幕的元素,如图片,可以使用srcset属性,根据屏幕像素密度加载不同分辨率的图片,以提高图片的显示质量和加载速度。例如:
<img :srcset="['small.jpg 1x', 'large.jpg 2x']" :src="small.jpg" alt="患者图片">
在上述代码中,当屏幕像素密度为 1x 时,加载small.jpg图片;当屏幕像素密度为 2x 时,加载large.jpg图片。
喜欢作者的可以关注微信公众号,一起开启开发之旅吧!!!(写作不易,记得关注作者呀)