欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。


在医疗服务领域,就诊前的准备工作往往被忽视,但它对提高诊疗效率和质量至关重要。今天我们来深入分析一个基于 React Native 开发的医院就诊准备应用,该应用允许用户在到达医院前通过App完成症状描述、病史填写,让医生能够提前准备,为患者提供更高效、更个性化的医疗服务。

使用 useState 管理应用状态,包括症状数据、病史信息、医生信息和用户输入等

该应用采用了 React Native 现代开发技术栈,主要包括:

  • React Hooks:使用 useState 管理应用状态,包括症状数据、病史信息、医生信息和用户输入等
  • TypeScript:通过类型定义确保数据结构的一致性和代码的可维护性
  • 跨平台组件:使用 SafeAreaViewTouchableOpacityScrollViewModal 等实现跨平台兼容的用户界面
  • 响应式布局:利用 Dimensions API 获取屏幕尺寸,确保在不同设备上的良好显示效果
  • 基础组件:使用 ViewTextTextInput 等构建用户界面

表示症状信息,包含 ID、名称、描述和严重程度

应用通过 TypeScript 定义了三个核心数据类型,构建了完整的医院就诊准备数据模型:

  • Symptom:表示症状信息,包含 ID、名称、描述和严重程度
  • MedicalHistory:表示病史信息,包含 ID、病情、诊断日期和治疗方案
  • Doctor:表示医生信息,包含 ID、姓名、专业、经验、评分和可用性

这种强类型定义不仅提高了代码的可读性和可维护性,也为鸿蒙跨端适配提供了清晰的数据结构映射基础。

应用采用了基于 useState 的轻量级状态管理方案,为不同功能模块分别管理状态:

  • 动态数据(症状列表、病史列表)通过 useState 管理,并支持添加新数据
  • 静态数据(医生列表)通过初始化的 useState 直接存储
  • 表单数据(新症状、新病史)通过独立的 useState 管理
  • 交互状态(选中的症状、选中的病史、选中的医生、模态框可见性)通过独立的 useState 管理

这种模式在中小型应用中非常高效,既避免了过度设计,又保证了状态管理的清晰性。


症状描述

症状描述是应用的核心功能之一,主要包括:

  • 展示症状列表,包括症状名称、描述和严重程度
  • 添加新的症状信息
  • 选择症状进行详细描述
  • 查看症状详细信息

这一功能让患者能够在就诊前详细记录自己的症状,帮助医生更好地了解病情。

病史填写功能为医生提供了患者的历史健康状况:

  • 展示病史列表,包括病情、诊断日期和治疗方案
  • 添加新的病史信息
  • 选择病史进行详细填写
  • 查看病史详细信息

完整的病史记录对医生的诊断和治疗方案制定具有重要参考价值。

医生选择

应用内置了医生信息列表,包含医生的姓名、专业、经验、评分和可用性状态。用户可以点击选择医生进行就诊准备,确保医生能够提前了解患者情况。

就诊准备功能是应用的最终目标,当用户选择了症状、病史和医生后,可以提交就诊准备信息,医生会收到这些信息并提前为就诊做准备。这一功能大大提高了诊疗效率,减少了患者在医院的等待时间。

应用通过卡片式布局展示症状信息、病史信息和医生信息,使用 TouchableOpacity 组件实现交互功能,点击卡片后可以查看详细信息或执行相关操作。此外,应用使用 Modal 组件展示详细信息,提升了用户体验。


组件映射

在鸿蒙 OS 适配过程中,需要注意以下组件映射:

  • SafeAreaView:在鸿蒙上需要使用 SafeArea 组件或自定义适配
  • TouchableOpacity:对应鸿蒙的 ButtonText 组件配合点击事件
  • ScrollView:对应鸿蒙的 ListContainerScroll 组件
  • Modal:对应鸿蒙的 Dialog 组件
  • Alert:对应鸿蒙的 ToastDialogAlertDialog 组件
  • TextInput:对应鸿蒙的 TextField 组件

布局

鸿蒙 OS 对 Flexbox 布局的支持与 React Native 基本一致,但在细节上仍需注意:

  • 确保样式命名符合鸿蒙规范
  • 调整间距和尺寸以适应鸿蒙设备的显示特性
  • 注意字体大小和行高的适配
  • 对于表单输入控件,需要适配鸿蒙的输入框样式和交互方式

在鸿蒙跨端开发中,性能优化是一个重要考虑因素:

  • 使用 memo 优化组件渲染,特别是对于症状列表、病史列表等重复渲染的场景
  • 合理使用 useCallbackuseMemo 减少不必要的计算
  • 优化图片资源,考虑使用鸿蒙的资源加载机制
  • 对于表单输入,考虑使用防抖处理减少不必要的状态更新
  • 对于列表数据的渲染,考虑使用虚拟化技术减少内存占用

预就诊准备流程

应用实现了完整的预就诊准备流程:

  • 症状描述:详细记录患者的症状
  • 病史填写:提供患者的历史健康状况
  • 医生选择:选择合适的医生
  • 信息提交:将准备信息发送给医生

这种预就诊准备流程不仅提高了诊疗效率,也改善了患者的就医体验。

应用鼓励患者主动参与到就诊准备过程中:

  • 患者可以详细描述自己的症状
  • 患者可以完整填写自己的病史
  • 患者可以选择自己信任的医生

这种患者主动参与的模式有助于提高患者对医疗过程的理解和配合度。

应用为医生提供了提前了解患者情况的机会:

  • 医生可以提前了解患者的症状
  • 医生可以提前查看患者的病史
  • 医生可以提前制定诊疗计划

这种医生提前准备的模式有助于提高诊疗质量和效率。

应用采用了清晰的模块化设计:

  • 功能按模块划分(症状描述、病史填写、医生选择、就诊准备)
  • 组件职责单一,便于维护和扩展
  • 状态管理逻辑与 UI 渲染分离

Base64 图标库

应用使用 Base64 编码的图标,这种方式有几个优点:

  • 减少网络请求,提高加载速度
  • 避免图标资源的跨平台适配问题
  • 减小应用包体积

虽然示例中使用的是占位 Base64 编码,但实际应用中可以使用真实的图标编码。


文件结构

示例代码集中在 App.tsx 文件中,适合小型应用。对于大型应用,建议按功能模块拆分文件:

  • /components:存放可复用组件,如症状卡片、病史卡片、医生卡片等

  • /types:存放类型定义,如 Symptom、MedicalHistory、Doctor 等

  • /hooks:存放自定义 hooks,如症状管理逻辑、病史管理逻辑等

  • /services:存放 API 调用和业务逻辑,如数据存储、医生信息服务等

  • /utils:存放工具函数,如日期处理、数据格式化等

  • 命名规范:变量和函数命名清晰,符合语义化要求

  • 类型安全:使用 TypeScript 确保类型安全

  • 错误处理:通过条件判断处理可能的异常情况,如表单验证

  • 注释:代码结构清晰,关键部分有适当注释

  • 性能考虑:合理使用 React Hooks,避免不必要的渲染和计算

应用架构具有良好的扩展性:

  • 可以轻松添加新的症状类型和病史记录
  • 可以集成真实的后端 API,实现数据的持久化存储
  • 可以扩展支持更多的就诊准备功能,如检查预约、药品准备等
  • 可以集成推送通知服务,实现医生确认和就诊提醒
  • 可以添加数据可视化功能,如症状发展趋势图表等

这个医院就诊准备应用展示了如何使用 React Native 构建功能完备的预就诊准备工具,特别是在患者主动参与和医生提前准备方面的实践具有重要参考价值。通过跨端开发技术,可以在不同平台为患者提供一致的服务体验。

随着医疗服务模式的不断创新,这类应用的需求将不断增长。未来可以考虑:

  • 与医院的挂号系统对接,实现就诊准备与挂号的一体化
  • 利用 AI 技术根据患者的症状和病史提供初步的健康建议
  • 支持更多的就诊准备功能,如检查前准备、用药指导等
  • 开发配套的医生端应用,实现更高效的就诊准备流程
  • 集成电子病历功能,为患者提供完整的健康记录管理

React Native 鸿蒙跨端开发代表了移动应用开发的未来趋势,通过一套代码库覆盖多个平台,不仅可以降低开发成本,还可以确保用户体验的一致性。在医疗应用领域,这种开发模式尤为重要,因为它可以让开发者更专注于核心功能的实现,而不是平台差异的处理。


医院就诊准备是智慧医疗在院前服务场景的核心落地形态,聚焦“症状描述-病史填写-医生选择-就诊准备”全流程的院前服务逻辑,既要保证症状、病史等医疗信息录入的完整性与准确性,又需兼顾医生资源匹配的合理性与就诊准备流程的闭环性,同时实现多端服务体验的一致性。本文基于这套 React Native 医院就诊准备应用代码,从架构设计、核心业务逻辑、鸿蒙跨端适配三个维度,系统解读院前服务场景的跨端开发逻辑与技术要点,重点剖析 React Native 与鸿蒙系统的适配底层逻辑和落地实践方案,尤其针对症状/病史新增、医生资源筛选、就诊准备提交等核心交互的跨端实现进行深度拆解。

一、保证症状/病史新增、医生选择、就诊准备提交

该医院就诊准备应用基于 React Native 函数式组件 + TypeScript 强类型架构构建,核心依赖 React Native 原生基础组件(SafeAreaView、ScrollView、TouchableOpacity、TextInput、Modal 等)与 useState 核心 Hook,未引入第三方 UI 框架或复杂状态管理库。这种极简架构是医院就诊准备这类“强表单录入、轻实时交互”场景实现鸿蒙跨端的核心优势——轻量意味着适配成本更低,且能最大程度保证多端院前服务流程逻辑的一致性,尤其适合症状描述、病史填写、医生选择等核心逻辑的跨端复用。

从跨端技术底层逻辑来看,React Native 以“JS 桥接层(JS Bridge)”为核心实现跨端能力:前端编写的 JSX 组件与医院就诊准备业务逻辑,通过桥接层映射为不同平台的原生组件,iOS 端映射为 UIKit 体系、Android 端映射为 View 体系,而鸿蒙(HarmonyOS)端则通过 React Native for HarmonyOS 适配层,完成 React Native 组件/API 与鸿蒙 ArkUI 组件/API 的双向映射。该应用的代码结构完全遵循跨端开发规范:无平台专属硬编码、状态管理基于 React 原生 Hooks、样式采用跨端通用的 Flex 布局,从根源上消除了鸿蒙适配的技术壁垒,同时保证症状/病史新增、医生选择、就诊准备提交等核心院前服务管理流程逻辑在多端的一致性。

值得注意的是,应用核心的症状新增(handleAddSymptom)、病史新增(handleAddHistory)、就诊准备提交(handlePrepareVisit)逻辑均为纯 JS 状态操作与数组关联查询实现,无任何平台相关依赖,这是跨端复用的关键——鸿蒙端可通过 JS 引擎直接执行该逻辑,无需适配任何原生能力,保证院前服务管理规则在多端的完全一致,避免因平台差异导致的症状/病史新增失败、就诊准备提交异常等核心问题。


1. 经验(experience)为标准化字符串

应用通过 TypeScript 接口定义了 Symptom(症状)、MedicalHistory(病史)、Doctor(医生)三类核心数据类型,字段设计精准匹配院前服务全流程数据需求,且所有字段均为 JS 基础数据类型(string/number/boolean),为跨端适配奠定基础:

  • Symptom 作为核心症状数据模型,涵盖症状ID、名称、描述、严重程度字段,严重程度(severity: string)采用标准化枚举式字符串(轻度/中度/重度),在鸿蒙端适配层可直接映射为 ArkTS 的 string 类型,避免多端数据类型解析差异导致的症状严重程度展示错误,尤其在“头痛 持续性头痛,伴有恶心 中度”这类核心症状信息的传递上,保证了跨端的数据准确性;
  • MedicalHistory 模型涵盖病史ID、病情、诊断日期、治疗方案字段,诊断日期(diagnosisDate: string)采用标准化日期格式字符串,治疗方案(treatment: string)为标准化描述字符串,鸿蒙端适配层可直接解析,保证病史信息展示的跨端一致性;
  • Doctor 模型延续了在线状态(available: boolean)、评分(rating: number)等核心字段设计,布尔值与数值类型为 JS/ArkTS 通用基础类型,医生在线状态、评分的展示逻辑跨端统一,专科(specialty)、经验(experience)为标准化字符串,保证医生资源信息展示的准确性。

这种强类型+场景化的数据模型设计,在跨端场景下保证了数据结构的一致性——鸿蒙端适配层可直接解析 TypeScript 类型定义,与 ArkTS 中的数据模型形成精准映射,避免多端数据格式不一致导致的症状/病史信息展示异常、医生在线状态错误等核心问题,是医院就诊准备场景跨端落地的基础保障。

2. 数组扩展运算符([...symptoms, newSymptomEntry]

应用采用 useState 实现多维度状态管理,核心状态均具备跨端复用的特性,且针对医院就诊准备场景做了适配性设计:

  • 核心业务数据状态(symptoms/medicalHistories/doctors)中,doctors 为只读设计,适配层自动映射为鸿蒙的 @State 响应式状态,医生列表的展示逻辑跨端统一;symptoms/medicalHistories 支持新增操作(setSymptoms/setMedicalHistories),数组扩展运算符([...symptoms, newSymptomEntry])为 ES6+ 标准语法,鸿蒙端直接执行,症状/病史新增的规则跨端一致,保证多端数据的同步性;
  • 表单录入状态(newSymptom/newHistory)采用对象式状态管理,其属性更新逻辑(setNewSymptom({ ...newSymptom, name: text }))为纯 JS 对象解构赋值,鸿蒙端直接执行,症状/病史表单录入的交互逻辑跨端统一;
  • 交互状态(selectedSymptom/selectedHistory/selectedDoctor)维护选中症状、病史、医生,均为字符串/空值赋值,鸿蒙端直接执行,选择操作的状态管理逻辑跨端统一;
  • 弹窗状态(isModalVisible/modalContent)维护弹窗显隐与内容,其更新逻辑为基础状态操作,鸿蒙端适配层会将 Modal 的显示状态映射为 ArkUI 弹窗的显隐状态,症状/病史详情展示的逻辑跨端统一。

1. 医院就诊准备专属样式的跨端适配

应用在基础样式之上新增症状/病史表单录入区域、症状/病史卡片、医生卡片、就诊准备按钮专属样式,核心样式设计既遵循跨端兼容原则,又针对院前服务场景的操作习惯做了特殊优化,适配鸿蒙系统无明显改造成本:

  • Flex 布局的跨端统一:从症状/病史表单的“多行输入框+添加按钮”、症状/病史卡片的“图标+信息+查看按钮”,到医生卡片的“图标+信息+在线状态”、就诊准备按钮的独立布局,全量采用 Flex 布局体系——症状/病史表单使用 flexDirection: 'row' + justifyContent: 'space-between' 横向布局,输入框均分宽度,保证表单录入的整洁性;症状/病史卡片使用 flexDirection: 'row' + alignItems: 'center' 横向布局,“查看详情”按钮独立排布,方便快速查看详情。Flex 作为 W3C 标准布局方案,在鸿蒙端可被适配层直接解析为 ArkUI 的 Flex 布局,无需重构任何布局逻辑,仅需保证样式属性命名与 React Native 规范一致,尤其在症状/病史录入、医生选择、就诊准备提交等核心院前服务交互区域的布局上,Flex 布局的跨端一致性表现突出;
  • 医院就诊准备专属样式的跨端适配
    • 选中症状/病史/医生卡片样式(selectedCardborderWidth: 2 + borderColor: '#0284c7')为通用样式属性,鸿蒙端适配层会将边框属性转换为 ArkUI 的 border 相关属性,选中项的高亮边框视觉效果跨端统一,帮助用户快速识别选择状态;
    • 表单输入框样式(input)设置 flex: 1 + marginRight: 8,为通用样式属性,鸿蒙端适配层直接解析,多输入框的均分布局逻辑跨端统一,符合用户表单录入的操作习惯;
    • 添加按钮/就诊准备按钮样式(addButton/prepareButton)分别采用蓝/绿背景色区分功能,为通用样式属性,鸿蒙端适配层直接解析,按钮的视觉辨识度与点击区域跨端统一,方便用户完成新增/提交操作;
    • 医生在线状态样式(availability)采用 emoji 图标+文字组合,为纯文本展示逻辑,鸿蒙端直接渲染,在线/离线状态的视觉区分跨端统一;
  • 屏幕适配与层级兼容Dimensions.get('window') 获取设备宽高的 API 在鸿蒙端已完成原生映射,为不同尺寸鸿蒙设备(手机、平板)的自适应布局预留基础,尤其适配平板设备的表单大屏录入场景;shadow + elevation 的双层阴影设计,鸿蒙系统对 elevation 属性的支持与 Android 端完全兼容,保证各类卡片、信息卡片的视觉层级跨端统一,同时阴影透明度(0.1)的低饱和度设计,避免视觉干扰,符合医疗场景专业、清晰的视觉需求;
  • 安全区域适配SafeAreaView 组件在鸿蒙端已适配为 ArkUI 的 SafeArea 组件,保证头部医院就诊准备标题区域在不同鸿蒙设备上的展示完整性,避免标题被刘海屏、全面屏遮挡,尤其适配患者/医护人员的视觉聚焦习惯。

(1)症状/病史管理组件

症状/病史管理是医院就诊准备的基础功能,核心适配逻辑如下:

  • 症状/病史列表渲染采用 map 方法遍历 symptoms/medicalHistories 数组,该逻辑为纯 JS 数组操作,鸿蒙端通过 JS 引擎直接执行,列表渲染的顺序与信息展示的完整性跨端一致,保证症状/病史信息的准确展示;
  • 症状/病史选择逻辑(handleSelectSymptom/handleSelectHistory)包含状态更新与弹窗反馈,状态更新为基础字符串赋值,弹窗反馈转换为鸿蒙 AlertDialog 组件,选择操作的反馈逻辑跨端一致;
  • 症状/病史新增逻辑(handleAddSymptom/handleAddHistory)包含表单校验(newSymptom.name && newSymptom.description && newSymptom.severity)、数据构造、状态更新、表单重置、弹窗反馈,表单校验为纯 JS 布尔运算,数据构造为对象字面量赋值,状态更新使用数组扩展运算符,表单重置为对象属性清空,弹窗反馈转换为鸿蒙 AlertDialog 组件,新增操作的逻辑跨端一致;
  • 症状/病史详情查看逻辑(handleViewSymptom/handleViewHistory)包含数据查找(symptoms.find/medicalHistories.find)、弹窗内容拼接、弹窗显隐控制,数据查找为 JS 原生数组方法,弹窗内容拼接为纯 JS 模板字符串操作,弹窗显隐控制为基础状态赋值,鸿蒙端直接执行,详情展示的逻辑跨端统一。
(2)医生资源匹配组件

医生资源匹配是医院就诊准备的核心资源管理功能,核心适配逻辑如下:

  • 医生列表渲染采用 map 方法遍历 doctors 数组,在线状态展示(doctor.available ? '🟢 在线' : '🔴 离线')为纯 JS 条件渲染,鸿蒙端直接执行,在线状态的展示规则跨端一致;
  • 医生选择逻辑(handleSelectDoctor)包含状态更新与弹窗反馈,状态更新为基础字符串赋值,弹窗反馈转换为鸿蒙 AlertDialog 组件,选择操作的反馈逻辑跨端一致。
(3)就诊准备提交组件

就诊准备提交是医院就诊准备的核心交互功能,核心适配逻辑如下:

  • 就诊准备提交逻辑(handlePrepareVisit)包含表单校验(selectedSymptom && selectedHistory && selectedDoctor)、弹窗反馈,表单校验为纯 JS 布尔运算,弹窗反馈转换为鸿蒙 AlertDialog 组件,提交操作的逻辑跨端一致;
  • 提交按钮采用 TouchableOpacity 组件,onPress 回调绑定提交逻辑,该组件在鸿蒙端已适配为 ArkUI 的 Button 组件,点击交互逻辑跨端统一。

1. 症状/病史新增全流程逻辑

handleAddSymptom/handleAddHistory 是医院就诊准备的核心数据录入机制,实现了“表单校验-数据构造-状态更新-表单重置-反馈提示”的全流程数据录入处理,核心适配逻辑如下:

  • 表单校验逻辑(newSymptom.name && newSymptom.description && newSymptom.severity)为纯 JS 布尔运算,鸿蒙端直接执行,校验规则跨端一致,保证症状/病史信息录入的完整性;
  • 数据构造逻辑(newSymptomEntry/newHistoryEntry)采用对象字面量赋值,ID 生成((symptoms.length + 1).toString())为纯 JS 数值运算与类型转换,鸿蒙端直接执行,数据构造的规则跨端一致;
  • 状态更新逻辑(setSymptoms([...symptoms, newSymptomEntry]))使用数组扩展运算符,鸿蒙端直接执行,症状/病史新增的规则跨端一致,保证多端数据的同步性;
  • 表单重置逻辑(setNewSymptom({ name: '', description: '', severity: '' }))为对象属性清空,鸿蒙端直接执行,输入框重置的规则跨端一致;
  • 反馈提示逻辑(Alert.alert)转换为鸿蒙 AlertDialog 组件,提示文案为纯 JS 字符串,鸿蒙端直接执行,提示展示的逻辑跨端一致。

应用使用的核心 API 均为 React Native 跨端兼容 API,在鸿蒙端可无缝适配,且针对医院就诊准备场景做了适配性验证:

  • 数组 API:map/find/扩展运算符等数组方法为 JS 原生 API,鸿蒙端通过 JS 引擎直接执行,无需适配,保证症状/病史/医生列表的渲染、查找、新增等核心逻辑的跨端一致性;
  • 字符串 API:字符串拼接、模板字符串等操作为 JS 原生 API,鸿蒙端直接执行,保证弹窗内容拼接、表单校验等核心逻辑的跨端一致性;
  • 交互 API:TouchableOpacityonPress 回调、TextInputonChangeText/placeholder 属性、Alert.alert 弹窗、Modal 组件的核心属性,在鸿蒙端均已完成适配,点击交互、输入交互、弹窗反馈、模态框展示的逻辑跨端一致,符合患者/医护人员的操作习惯;
  • 样式 API:StyleSheet.create 封装的样式规则,适配层转换为 ArkUI 的样式对象,尤其选中症状/病史/医生卡片的条件样式绑定逻辑,鸿蒙端可直接解析,保证选择状态的视觉适配跨端统一;
  • 对象 API:对象解构赋值(setNewSymptom({ ...newSymptom, name: text }))为 ES6+ 标准语法,鸿蒙端直接执行,表单录入的状态更新逻辑跨端统一。

该医院就诊准备应用作为智慧医疗院前服务场景核心模块,适配鸿蒙系统的成本极低,核心适配思路与技术要点如下:

应用核心的症状/病史新增、医生选择、就诊准备提交等逻辑均为纯 JS 实现,无任何平台相关依赖,这是跨端复用的最大优势——鸿蒙端可通过 JS 引擎直接执行该逻辑,无需适配任何原生能力。在生产环境中扩展症状分类、病史模板、医生预约等逻辑时,新增规则仍为纯 JS 逻辑(医生预约可通过原生模块封装鸿蒙日历 API,核心业务逻辑复用),鸿蒙端可直接复用,仅需保证规则逻辑的通用性,无需考虑平台差异,这也是医院就诊准备场景跨端开发的核心优势。

该应用当前的列表渲染采用基础 map 方法,在生产环境中若症状/病史/医生数据量较大(如超过30条症状、20条病史、20位医生),可替换为 React Native 的 FlatList 高性能列表组件——FlatList 在鸿蒙端已完成深度适配,支持虚拟化列表渲染,其核心属性(data/renderItem/keyExtractor)与 React Native 端完全一致,且 FlatListrenderItem 中可直接复用现有卡片样式与选中条件样式绑定逻辑,仅需少量调整即可适配鸿蒙端的性能优化策略,保证列表的滚动性能,尤其适合多症状、多病史的院前服务场景。

鸿蒙系统有自身的医疗院前服务场景友好型设计规范,在适配时可通过条件编译实现差异化样式,既保证遵循鸿蒙设计规范,又能保留现有代码的完整性与场景友好特性:

// 鸿蒙端医院就诊准备样式差异化适配示例
import { Platform } from 'react-native';
const isHarmonyOS = Platform.OS === 'harmony';

const adaptiveStyles = {
  card: {
    ...styles.card,
    backgroundColor: isHarmonyOS ? '#e0f7fa' : '#f0f9ff',
    borderRadius: isHarmonyOS ? 14 : 12,
    padding: isHarmonyOS ? 20 : 16, // 鸿蒙端增大内边距,更适合查看医疗信息
  },
  selectedCard: {
    ...styles.selectedCard,
    borderWidth: isHarmonyOS ? 3 : 2, // 鸿蒙端增大选中边框宽度
    borderColor: isHarmonyOS ? '#0369a1' : '#0284c7',
  },
  input: {
    ...styles.input,
    fontSize: isHarmonyOS ? 16 : 14, // 鸿蒙端增大输入框字体
    paddingHorizontal: isHarmonyOS ? 16 : 12,
    paddingVertical: isHarmonyOS ? 10 : 8,
  },
  prepareButton: {
    ...styles.prepareButton,
    padding: isHarmonyOS ? 18 : 16, // 鸿蒙端增大提交按钮点击区域
    borderRadius: isHarmonyOS ? 14 : 12,
  }
};

这种轻量级的差异化适配,既能保证符合鸿蒙的医疗院前服务场景友好设计规范,又能保留现有代码的完整性,尤其在症状/病史卡片、表单输入框、就诊准备按钮等核心院前服务交互组件的样式适配中,效果显著,同时维持了医院就诊准备场景专业、清晰的视觉调性。

该 React Native 医院就诊准备应用实现了症状描述、病史填写、医生选择、就诊准备提交等核心智慧医疗服务功能,代码结构符合跨端开发规范,可低成本适配鸿蒙系统。针对生产环境落地,可做以下优化:

  1. 症状分类与模板功能:新增症状分类数据模型与模板库,实现症状快速选择录入,该逻辑为纯 JS 数组过滤与对象赋值,鸿蒙端直接执行,无需适配原生能力,提升症状录入的便捷性;
  2. 病史模板与快速填充:新增病史模板数据模型,支持常见病史(高血压、糖尿病等)的快速填充,该逻辑为纯 JS 对象赋值,鸿蒙端直接执行,提升病史录入的效率;
  3. 医生预约与时间选择:通过 React Native 原生模块封装鸿蒙的日历/时间选择 API,实现医生预约时间的选择,核心医生选择逻辑可复用,仅需扩展时间选择能力,提升院前服务的精细化程度;
  4. 就诊信息提交与医院系统对接:对接医院 HIS 系统接口,实现症状/病史/医生选择信息的自动提交,数据提交逻辑为纯 JS 数组/对象序列化,鸿蒙端直接执行,保证院前信息与医院系统的同步性;
  5. 离线数据存储:通过 React Native 原生模块封装鸿蒙的本地存储 API,实现症状/病史信息的离线存储,核心数据管理逻辑可复用,仅需扩展离线存储能力,提升无网络场景下的院前服务体验。

真实演示案例代码:




// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, Modal } from 'react-native';

// Base64 图标库
const ICONS_BASE64 = {
  symptom: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  history: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  doctor: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  preparation: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

const { width, height } = Dimensions.get('window');

// 症状类型
type Symptom = {
  id: string;
  name: string;
  description: string;
  severity: string;
};

// 病史类型
type MedicalHistory = {
  id: string;
  condition: string;
  diagnosisDate: string;
  treatment: string;
};

// 医生类型
type Doctor = {
  id: string;
  name: string;
  specialty: string;
  experience: string;
  rating: number;
  available: boolean;
};

// 医院就诊准备应用组件
const HospitalVisitPreparationApp: React.FC = () => {
  const [symptoms, setSymptoms] = useState<Symptom[]>([
    {
      id: '1',
      name: '头痛',
      description: '持续性头痛,伴有恶心',
      severity: '中度'
    },
    {
      id: '2',
      name: '发热',
      description: '体温升高,伴有寒战',
      severity: '轻度'
    }
  ]);

  const [medicalHistories, setMedicalHistories] = useState<MedicalHistory[]>([
    {
      id: '1',
      condition: '高血压',
      diagnosisDate: '2020-05-15',
      treatment: '服用降压药'
    },
    {
      id: '2',
      condition: '糖尿病',
      diagnosisDate: '2019-08-22',
      treatment: '胰岛素注射'
    }
  ]);

  const [doctors] = useState<Doctor[]>([
    {
      id: '1',
      name: '张医生',
      specialty: '内科',
      experience: '10年经验',
      rating: 4.8,
      available: true
    },
    {
      id: '2',
      name: '李医生',
      specialty: '儿科',
      experience: '8年经验',
      rating: 4.9,
      available: false
    }
  ]);

  const [selectedSymptom, setSelectedSymptom] = useState<string | null>(null);
  const [selectedHistory, setSelectedHistory] = useState<string | null>(null);
  const [selectedDoctor, setSelectedDoctor] = useState<string | null>(null);
  const [newSymptom, setNewSymptom] = useState({
    name: '',
    description: '',
    severity: ''
  });
  const [newHistory, setNewHistory] = useState({
    condition: '',
    diagnosisDate: '',
    treatment: ''
  });
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalContent, setModalContent] = useState('');

  const handleSelectSymptom = (symptomId: string) => {
    setSelectedSymptom(symptomId);
    Alert.alert('选择症状', '您已选择该症状进行描述');
  };

  const handleSelectHistory = (historyId: string) => {
    setSelectedHistory(historyId);
    Alert.alert('选择病史', '您已选择该病史进行填写');
  };

  const handleSelectDoctor = (doctorId: string) => {
    setSelectedDoctor(doctorId);
    Alert.alert('选择医生', '您已选择该医生进行准备');
  };

  const handleAddSymptom = () => {
    if (newSymptom.name && newSymptom.description && newSymptom.severity) {
      const newSymptomEntry: Symptom = {
        id: (symptoms.length + 1).toString(),
        name: newSymptom.name,
        description: newSymptom.description,
        severity: newSymptom.severity
      };
      setSymptoms([...symptoms, newSymptomEntry]);
      setNewSymptom({ name: '', description: '', severity: '' });
      Alert.alert('添加成功', '症状已添加到描述中');
    } else {
      Alert.alert('提示', '请填写完整的症状信息');
    }
  };

  const handleAddHistory = () => {
    if (newHistory.condition && newHistory.diagnosisDate && newHistory.treatment) {
      const newHistoryEntry: MedicalHistory = {
        id: (medicalHistories.length + 1).toString(),
        condition: newHistory.condition,
        diagnosisDate: newHistory.diagnosisDate,
        treatment: newHistory.treatment
      };
      setMedicalHistories([...medicalHistories, newHistoryEntry]);
      setNewHistory({ condition: '', diagnosisDate: '', treatment: '' });
      Alert.alert('添加成功', '病史已添加到记录中');
    } else {
      Alert.alert('提示', '请填写完整的病史信息');
    }
  };

  const handlePrepareVisit = () => {
    if (selectedSymptom && selectedHistory && selectedDoctor) {
      Alert.alert('准备完成', '医生已收到您的信息,将提前为您准备');
    } else {
      Alert.alert('提示', '请选择症状、病史和医生');
    }
  };

  const handleViewSymptom = (symptomId: string) => {
    const symptom = symptoms.find(s => s.id === symptomId);
    if (symptom) {
      setModalContent(`症状名称: ${symptom.name}\n描述: ${symptom.description}\n严重程度: ${symptom.severity}`);
      setIsModalVisible(true);
    }
  };

  const handleViewHistory = (historyId: string) => {
    const history = medicalHistories.find(h => h.id === historyId);
    if (history) {
      setModalContent(`病情: ${history.condition}\n诊断日期: ${history.diagnosisDate}\n治疗方案: ${history.treatment}`);
      setIsModalVisible(true);
    }
  };

  const openModal = (content: string) => {
    setModalContent(content);
    setIsModalVisible(true);
  };

  const closeModal = () => {
    setIsModalVisible(false);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>医院就诊准备</Text>
        <Text style={styles.subtitle}>用户到达医院前,通过App完成症状描述、病史填写,医生可提前准备</Text>
      </View>

      <ScrollView style={styles.content}>
        {/* 症状描述 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>症状描述</Text>
          <View style={styles.inputRow}>
            <TextInput
              style={styles.input}
              placeholder="症状名称"
              value={newSymptom.name}
              onChangeText={(text) => setNewSymptom({ ...newSymptom, name: text })}
            />
            <TextInput
              style={styles.input}
              placeholder="症状描述"
              value={newSymptom.description}
              onChangeText={(text) => setNewSymptom({ ...newSymptom, description: text })}
            />
          </View>
          <View style={styles.inputRow}>
            <TextInput
              style={styles.input}
              placeholder="严重程度"
              value={newSymptom.severity}
              onChangeText={(text) => setNewSymptom({ ...newSymptom, severity: text })}
            />
          </View>
          <TouchableOpacity 
            style={styles.addButton}
            onPress={handleAddSymptom}
          >
            <Text style={styles.addText}>添加症状</Text>
          </TouchableOpacity>
          {symptoms.map(symptom => (
            <TouchableOpacity 
              key={symptom.id}
              style={[
                styles.card,
                selectedSymptom === symptom.id && styles.selectedCard
              ]}
              onPress={() => handleSelectSymptom(symptom.id)}
            >
              <Text style={styles.icon}>🤒</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{symptom.name}</Text>
                <Text style={styles.cardDescription}>描述: {symptom.description}</Text>
                <Text style={styles.cardDescription}>严重程度: {symptom.severity}</Text>
              </View>
              <TouchableOpacity 
                style={styles.viewButton}
                onPress={() => handleViewSymptom(symptom.id)}
              >
                <Text style={styles.viewText}>查看详情</Text>
              </TouchableOpacity>
            </TouchableOpacity>
          ))}
        </View>

        {/* 病史填写 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>病史填写</Text>
          <View style={styles.inputRow}>
            <TextInput
              style={styles.input}
              placeholder="病情"
              value={newHistory.condition}
              onChangeText={(text) => setNewHistory({ ...newHistory, condition: text })}
            />
            <TextInput
              style={styles.input}
              placeholder="诊断日期 (YYYY-MM-DD)"
              value={newHistory.diagnosisDate}
              onChangeText={(text) => setNewHistory({ ...newHistory, diagnosisDate: text })}
            />
          </View>
          <View style={styles.inputRow}>
            <TextInput
              style={styles.input}
              placeholder="治疗方案"
              value={newHistory.treatment}
              onChangeText={(text) => setNewHistory({ ...newHistory, treatment: text })}
            />
          </View>
          <TouchableOpacity 
            style={styles.addButton}
            onPress={handleAddHistory}
          >
            <Text style={styles.addText}>添加病史</Text>
          </TouchableOpacity>
          {medicalHistories.map(history => (
            <TouchableOpacity 
              key={history.id}
              style={[
                styles.card,
                selectedHistory === history.id && styles.selectedCard
              ]}
              onPress={() => handleSelectHistory(history.id)}
            >
              <Text style={styles.icon}>📜</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{history.condition}</Text>
                <Text style={styles.cardDescription}>诊断日期: {history.diagnosisDate}</Text>
                <Text style={styles.cardDescription}>治疗方案: {history.treatment}</Text>
              </View>
              <TouchableOpacity 
                style={styles.viewButton}
                onPress={() => handleViewHistory(history.id)}
              >
                <Text style={styles.viewText}>查看详情</Text>
              </TouchableOpacity>
            </TouchableOpacity>
          ))}
        </View>

        {/* 医生选择 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>医生选择</Text>
          {doctors.map(doctor => (
            <TouchableOpacity 
              key={doctor.id}
              style={[
                styles.card,
                selectedDoctor === doctor.id && styles.selectedCard
              ]}
              onPress={() => handleSelectDoctor(doctor.id)}
            >
              <Text style={styles.icon}>👨‍⚕️</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{doctor.name}</Text>
                <Text style={styles.cardDescription}>专科: {doctor.specialty}</Text>
                <Text style={styles.cardDescription}>经验: {doctor.experience}</Text>
                <Text style={styles.cardDescription}>评分: {doctor.rating}</Text>
              </View>
              <Text style={styles.availability}>
                {doctor.available ? '🟢 在线' : '🔴 离线'}
              </Text>
            </TouchableOpacity>
          ))}
        </View>

        {/* 就诊准备 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>就诊准备</Text>
          <TouchableOpacity 
            style={styles.prepareButton}
            onPress={handlePrepareVisit}
          >
            <Text style={styles.prepareText}>准备就诊</Text>
          </TouchableOpacity>
        </View>

        {/* 使用说明 */}
        <View style={styles.infoCard}>
          <Text style={styles.sectionTitle}>📘 使用说明</Text>
          <Text style={styles.infoText}>• 描述当前症状并添加到记录中</Text>
          <Text style={styles.infoText}>• 填写既往病史信息</Text>
          <Text style={styles.infoText}>• 选择合适的医生进行就诊</Text>
          <Text style={styles.infoText}>• 医生将提前收到信息并做好准备</Text>
        </View>

        {/* 弹框内容 */}
        <Modal
          animationType="slide"
          transparent={true}
          visible={isModalVisible}
          onRequestClose={closeModal}
        >
          <View style={styles.modalContainer}>
            <View style={styles.modalContent}>
              <Text style={styles.modalTitle}>详细信息</Text>
              <Text style={styles.modalText}>{modalContent}</Text>
              <TouchableOpacity
                style={styles.closeButton}
                onPress={closeModal}
              >
                <Text style={styles.closeButtonText}>关闭</Text>
              </TouchableOpacity>
            </View>
          </View>
        </Modal>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f9ff',
  },
  header: {
    flexDirection: 'column',
    padding: 16,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#bae6fd',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0c4a6e',
    marginBottom: 4,
  },
  subtitle: {
    fontSize: 14,
    color: '#0284c7',
  },
  content: {
    flex: 1,
    marginTop: 12,
  },
  section: {
    backgroundColor: '#ffffff',
    marginHorizontal: 16,
    marginBottom: 12,
    borderRadius: 12,
    padding: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#0c4a6e',
    marginBottom: 12,
  },
  inputRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 12,
  },
  input: {
    flex: 1,
    backgroundColor: '#f0f9ff',
    borderRadius: 8,
    paddingHorizontal: 12,
    paddingVertical: 8,
    fontSize: 14,
    color: '#0c4a6e',
    marginRight: 8,
  },
  addButton: {
    backgroundColor: '#0284c7',
    padding: 12,
    borderRadius: 8,
    alignItems: 'center',
  },
  addText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
  card: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f0f9ff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
  },
  selectedCard: {
    borderWidth: 2,
    borderColor: '#0284c7',
  },
  icon: {
    fontSize: 28,
    marginRight: 12,
  },
  cardInfo: {
    flex: 1,
  },
  cardTitle: {
    fontSize: 16,
    fontWeight: '500',
    color: '#0c4a6e',
    marginBottom: 4,
  },
  cardDescription: {
    fontSize: 14,
    color: '#0284c7',
    marginBottom: 2,
  },
  availability: {
    fontSize: 14,
    color: '#0c4a6e',
    fontWeight: '500',
  },
  viewButton: {
    backgroundColor: '#0284c7',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  viewText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  prepareButton: {
    backgroundColor: '#10b981',
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  prepareText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '500',
  },
  infoCard: {
    backgroundColor: '#ffffff',
    marginHorizontal: 16,
    marginBottom: 80,
    borderRadius: 12,
    padding: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  infoText: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 20,
    marginBottom: 4,
  },
  modalContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
  },
  modalContent: {
    width: '80%',
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 20,
    elevation: 5,
  },
  modalTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#0c4a6e',
    marginBottom: 12,
    textAlign: 'center',
  },
  modalText: {
    fontSize: 14,
    color: '#0c4a6e',
    lineHeight: 20,
    marginBottom: 20,
  },
  closeButton: {
    backgroundColor: '#0284c7',
    padding: 10,
    borderRadius: 8,
    alignItems: 'center',
  },
  closeButtonText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
});

export default HospitalVisitPreparationApp;



请添加图片描述


打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

在这里插入图片描述

最后运行效果图如下显示:

请添加图片描述
本文介绍了一个基于React Native开发的医院就诊准备应用,该应用通过症状描述、病史填写和医生选择功能,实现高效的预就诊流程。应用采用React Hooks进行状态管理,使用TypeScript确保类型安全,并利用跨平台组件实现响应式布局。文章重点分析了该应用在鸿蒙系统的适配方案,包括组件映射、性能优化和模块化设计。这种轻量级架构既保证了多端一致性,又降低了适配成本,为智慧医疗的院前服务提供了技术参考。未来可考虑与医院系统对接、AI辅助等功能扩展。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐