React Native鸿蒙跨平台实现TextInput的keyboardType=“numeric“`属性会适配鸿蒙的数字键盘,从而保证UI渲染和交互逻辑在鸿蒙设备上的一致性
本文介绍了一个基于React Native构建的医保结算应用的技术实现,重点探讨了其在鸿蒙跨平台开发中的应用。该应用采用React Native函数式组件架构,结合TypeScript类型系统和React Hooks,实现了医保结算管理系统。核心技术包括跨端开发框架、类型安全、状态管理、Base64图标资源和响应式布局。数据模型定义了用户、医保政策、医疗费用和报销记录等核心类型,确保跨平台数据一致
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
在医疗健康领域,数字化工具正逐渐成为优化医保结算流程、提高医疗费用透明度的重要手段。本文将深入剖析一个基于 React Native 构建的医保结算应用,探讨其技术实现细节及鸿蒙跨端能力的应用。
技术选型
该应用采用了现代 React Native 函数式组件架构,通过 TypeScript 类型系统和 React Hooks 实现了一个功能完整的医保结算管理系统。核心技术栈包括:
- React Native:作为跨端开发框架,提供了统一的组件 API,确保应用在 iOS、Android 及鸿蒙平台上的一致性体验
- TypeScript:通过严格的类型定义增强代码可维护性,明确了数据结构和组件接口
- React Hooks:使用 useState 管理应用状态,useEffect 处理副作用逻辑,实现了声明式的状态管理
- Base64 图标:采用 Base64 编码的图标资源,避免了不同平台资源格式的差异,提高了跨端兼容性
- 响应式布局:使用 Dimensions API 获取屏幕尺寸,实现适配不同设备的响应式界面
数据模型
应用通过 TypeScript 接口定义了四个核心数据类型,构建了完整的医保结算数据模型体系:
// 用户类型
type User = {
id: string;
name: string;
insuranceCardNumber: string;
balance: number;
};
// 医保政策类型
type InsurancePolicy = {
id: string;
name: string;
coverage: number;
deductible: number;
};
// 医疗费用类型
type MedicalExpense = {
id: string;
userId: string;
date: string;
amount: number;
description: string;
};
// 报销记录类型
type ReimbursementRecord = {
id: string;
userId: string;
expenseId: string;
reimbursedAmount: number;
outOfPocket: number;
status: string;
};
这种强类型设计不仅提高了代码可读性,也为鸿蒙跨端适配提供了清晰的数据契约,确保不同平台间数据传递的一致性。数据模型的设计充分考虑了医保结算的核心要素,包含了用户信息、医保政策、医疗费用和报销记录等关键数据。
状态管理
应用使用 useState Hook 管理多个复杂状态,包括用户列表、医保政策、医疗费用、报销记录等:
const [users] = useState<User[]>([
{
id: '1',
name: '李先生',
insuranceCardNumber: '123456789012345678',
balance: 5000
},
{
id: '2',
name: '王女士',
insuranceCardNumber: '987654321098765432',
balance: 3000
}
]);
// 其他状态定义...
特别值得注意的是,应用通过 useEffect 实现了报销金额的自动计算机制:
// 自动计算报销金额
useEffect(() => {
const interval = setInterval(() => {
const randomUser = users[Math.floor(Math.random() * users.length)];
const randomPolicy = insurancePolicies[Math.floor(Math.random() * insurancePolicies.length)];
const randomExpense = medicalExpenses[Math.floor(Math.random() * medicalExpenses.length)];
const reimbursed = Math.max(0, (randomExpense.amount - randomPolicy.deductible) * randomPolicy.coverage);
const outOfPocket = randomExpense.amount - reimbursed;
const newRecord: ReimbursementRecord = {
id: (reimbursementRecords.length + 1).toString(),
userId: randomUser.id,
expenseId: randomExpense.id,
reimbursedAmount: parseFloat(reimbursed.toFixed(2)),
outOfPocket: parseFloat(outOfPocket.toFixed(2)),
status: '已结算'
};
setReimbursementRecords(prevRecords => [...prevRecords, newRecord]);
}, 30000);
return () => clearInterval(interval);
}, [users, insurancePolicies, medicalExpenses, reimbursementRecords]);
这种基于时间间隔的自动计算机制,模拟了真实场景中医保结算的自动化处理过程,为用户提供了实时的报销金额计算。同时,通过 useEffect 的清理函数,确保了定时器在组件卸载时被正确清除,避免了内存泄漏。
在 React Native 鸿蒙跨端开发中,该应用体现了以下关键技术点:
- 组件兼容性:使用 React Native 核心组件(如 SafeAreaView、View、Text、TouchableOpacity、ScrollView、Modal 等),确保在鸿蒙系统上的兼容性
- 资源管理:通过 Base64 编码的图标资源,避免了不同平台资源格式的差异,提高了跨端部署的一致性
- 尺寸适配:使用 Dimensions API 获取屏幕尺寸,实现响应式布局,适应不同设备屏幕
- 状态管理:采用 React Hooks 进行状态管理,保持跨平台代码一致性
- 类型安全:TypeScript 类型定义确保了数据结构在不同平台间的一致性
- API 调用:使用 React Native 统一的 API 调用方式,如 Alert 组件,确保在鸿蒙平台上的正确显示
- 数学计算:使用 JavaScript 标准数学函数(如 Math.max、Math.floor 等)进行报销金额计算,确保在鸿蒙平台上的兼容性
1. 用户与医保政策管理
应用实现了用户选择和医保政策管理功能,用户可以根据自己的医保类型选择相应的政策:
const handleSelectUser = (userId: string) => {
setSelectedUser(userId);
Alert.alert('选择用户', '您已选择该用户进行医保结算');
};
const handleSelectPolicy = (policyId: string) => {
setSelectedPolicy(policyId);
Alert.alert('选择政策', '您已选择该医保政策');
};
2. 医疗费用管理
应用提供了医疗费用的添加功能,用户可以记录医疗费用的日期、金额和描述:
const handleAddExpense = () => {
if (newExpense.date && newExpense.amount && newExpense.description && selectedUser) {
const newExpenseEntry: MedicalExpense = {
id: (medicalExpenses.length + 1).toString(),
userId: selectedUser,
date: newExpense.date,
amount: parseFloat(newExpense.amount),
description: newExpense.description
};
setMedicalExpenses([...medicalExpenses, newExpenseEntry]);
setNewExpense({ date: '', amount: '', description: '' });
Alert.alert('添加成功', '新的医疗费用已添加');
} else {
Alert.alert('提示', '请选择用户并填写完整的费用信息');
}
};
3. 报销金额计算
应用实现了报销金额的计算功能,根据医保政策的报销比例和起付线,自动计算报销金额和自付金额:
const handleCalculateReimbursement = (expenseId: string) => {
const expense = medicalExpenses.find(e => e.id === expenseId);
const policy = insurancePolicies.find(p => p.id === selectedPolicy);
if (expense && policy && selectedUser) {
const reimbursed = Math.max(0, (expense.amount - policy.deductible) * policy.coverage);
const outOfPocket = expense.amount - reimbursed;
const newRecord: ReimbursementRecord = {
id: (reimbursementRecords.length + 1).toString(),
userId: selectedUser,
expenseId: expense.id,
reimbursedAmount: parseFloat(reimbursed.toFixed(2)),
outOfPocket: parseFloat(outOfPocket.toFixed(2)),
status: '已结算'
};
setReimbursementRecords([...reimbursementRecords, newRecord]);
Alert.alert('计算完成', `报销金额: ${reimbursed.toFixed(2)}元, 自付金额: ${outOfPocket.toFixed(2)}元`);
} else {
Alert.alert('提示', '请选择医保政策并确保费用信息完整');
}
};
4. 报销记录管理
应用实现了报销记录的管理功能,用户可以查看历史报销记录,了解报销状态和金额。
应用的 UI 设计遵循了现代移动应用的设计原则,使用了以下组件和交互模式:
- 安全区域:通过 SafeAreaView 确保内容显示在安全区域内,适应不同设备的屏幕刘海和底部指示条
- 滚动视图:通过 ScrollView 实现内容的垂直滚动,适应不同长度的费用列表和报销记录
- 卡片布局:使用 TouchableOpacity 和 View 组合实现卡片式列表项,提供清晰的视觉层次和交互反馈
- 模态框:通过 Modal 组件展示详细信息,如报销详情、政策说明等
- 交互反馈:使用 Alert 组件提供操作反馈和提示信息
- 响应式设计:根据屏幕尺寸动态调整布局,确保在不同设备上的良好显示效果
- 跨端架构:基于 React Native 构建,实现了一次编码多平台运行的目标,特别关注了鸿蒙平台的适配
- 类型安全:全面使用 TypeScript 类型定义,提高代码质量和可维护性,确保医保数据的准确性
- 自动化结算:实现了报销金额的自动计算,简化了医保结算流程
- 实时数据反馈:通过即时的 Alert 反馈,增强用户操作体验
- 模块化设计:通过清晰的类型定义和函数划分,实现了代码的模块化,提高了可维护性
- 数据精度处理:使用 parseFloat 和 toFixed 方法确保金额计算的精度,符合财务应用的要求
- 多维度数据管理:同时管理用户、医保政策、医疗费用和报销记录,提供全面的医保结算视角
在实际应用中,还可以考虑以下性能优化策略:
- 状态管理优化:对于大型应用,可以考虑使用 Redux 或 Context API 进行全局状态管理,提高状态更新的效率
- 组件拆分:将大型组件拆分为更小的可复用组件,提高渲染性能和代码可维护性
- 数据缓存:对用户数据和报销记录进行本地缓存,减少重复计算和网络请求
- 动画性能:使用 React Native 的 Animated API 实现流畅的过渡动画,提升用户体验
- 内存管理:确保及时清理不再使用的状态和事件监听器,避免内存泄漏
- 网络优化:对于实际应用中的远程数据同步,实现合理的网络请求策略,如批量上传、增量同步等
- 计算优化:对于报销金额计算等频繁操作,可以考虑使用 memoization 技术缓存计算结果
- 列表优化:对于长列表,使用 FlatList 组件替代 ScrollView,提高渲染性能
在开发过程中,可能面临的技术挑战及解决方案:
- 鸿蒙平台适配:通过使用 React Native 核心组件和统一的 API 调用方式,确保应用在鸿蒙平台上的兼容性
- 数据同步:实现本地数据与医保系统数据的实时同步,确保报销信息的准确性和时效性
- 用户隐私保护:实现医保数据的加密存储和传输,保护用户隐私,符合医疗数据安全法规
- 离线功能:实现基本的离线操作能力,确保在网络不稳定情况下的正常使用
- 性能优化:针对不同设备性能差异,实现自适应的性能优化策略,确保在中低端设备上的流畅运行
- 数据精度:确保金额计算的精度,避免舍入误差导致的报销金额错误
- 用户体验一致性:确保在不同平台上的用户体验一致,特别是交互方式和视觉效果
通过对这个医保结算应用的技术解读,我们可以看到 React Native 在跨端开发中的强大能力。该应用不仅实现了完整的医保结算功能,还展示了如何通过 TypeScript、React Hooks 等现代前端技术构建高质量的跨端应用。
医保结算类应用作为民生级医疗数字化产品,对数据精准性、业务逻辑严谨性和跨端兼容性有着极高要求。鸿蒙系统凭借分布式能力和全场景适配特性,成为医保应用跨端部署的核心目标平台。本文将深度拆解基于React Native开发的医保结算应用代码,从医保领域数据模型设计、报销金额自动计算逻辑、支付结算交互到鸿蒙系统跨端兼容的核心技术要点,全方位剖析医保类跨端应用的开发范式与最佳实践。
应用架构设计
这款医保结算应用聚焦于医保报销金额自动计算、电子医保卡支付、异地结算等核心民生功能,涵盖用户医保档案管理、医保政策匹配、医疗费用录入、报销记录追踪等完整业务链路。从架构层面来看,应用完全遵循React Native的组件化设计思想,以TypeScript构建强类型的医保业务逻辑层,这种设计既保证了医保金额计算的精准性,也为鸿蒙系统的跨端兼容提供了核心支撑。
从跨端适配的底层逻辑分析,代码中未引入任何平台专属API(如iOS的PassKit、Android的NFC API或鸿蒙的AbilityKit),所有核心功能均基于React Native的通用核心组件(SafeAreaView、View、Text、TextInput、Modal等)和基础API(Dimensions、Alert、TouchableOpacity)构建。在鸿蒙系统中,这些通用组件会被React Native for HarmonyOS框架转换为ArkUI原生组件——例如TouchableOpacity映射为鸿蒙的Button组件、Modal映射为鸿蒙的Dialog组件,TextInput的keyboardType="numeric"属性会适配鸿蒙的数字键盘,从而保证UI渲染和交互逻辑在鸿蒙设备上的一致性。这种“通用抽象层+平台适配层”的架构,规避了因平台差异导致的代码碎片化,同时满足医保应用对跨端数据一致性和计算准确性的严苛要求。
强类型医保数据模型:
医保结算的核心是金额计算的精准性,代码中通过多层级的TypeScript类型定义构建了完整的医保领域数据模型,从用户医保档案到医保政策、医疗费用、报销记录,形成了闭环的数据体系,这不仅规避了前端开发中的类型错误,更在跨端编译阶段提前拦截了因平台数据格式差异导致的金额计算偏差。
// 核心用户医保档案模型
type User = {
id: string;
name: string;
insuranceCardNumber: string; // 医保卡号(字符串类型保证格式完整)
balance: number; // 医保余额(数值类型保证计算精度)
};
// 医保政策模型(核心计算参数)
type InsurancePolicy = {
id: string;
name: string;
coverage: number; // 报销比例(0-1浮点型,如0.8代表80%)
deductible: number; // 起付线(整数型,保证金额精度)
};
// 医疗费用与报销记录模型
type MedicalExpense = {
id: string;
userId: string;
date: string;
amount: number; // 费用金额(浮点型,支持小数)
description: string;
};
type ReimbursementRecord = {
id: string;
userId: string;
expenseId: string;
reimbursedAmount: number; // 报销金额(浮点型)
outOfPocket: number; // 自付金额(浮点型)
status: string; // 结算状态
};
这些类型定义严格约束了医保数据的格式和类型,所有金额类字段均采用number类型,避免了字符串转数值时的精度丢失问题。在鸿蒙系统中,React Native的TypeScript编译器会对JS层与鸿蒙ArkTS层之间的数据交互进行类型校验——例如报销比例、起付线等核心计算参数在跨端传递时,会被强制校验为number类型,避免了因鸿蒙ArkTS的静态类型特性与JavaScript动态类型特性不兼容导致的计算错误。值得注意的是,报销金额计算时通过toFixed(2)保留两位小数,这种标准化的数值处理逻辑在鸿蒙设备上能够保证金额展示的一致性,符合医保结算的金额精度要求。
应用的核心业务逻辑基于React Hooks(useState、useEffect)实现,这种轻量级的状态管理方式完美适配React Native的跨端生命周期模型,同时与鸿蒙系统的组件生命周期形成深度映射,尤其适配了报销金额自动计算这类周期性任务的需求。
核心状态设计
// 基础医保数据状态(不可变初始化)
const [users] = useState<User[]>([/* 初始化用户医保档案 */]);
const [insurancePolicies] = useState<InsurancePolicy[]>([/* 初始化医保政策 */]);
const [medicalExpenses, setMedicalExpenses] = useState<MedicalExpense[]>([/* 初始化医疗费用 */]);
const [reimbursementRecords, setReimbursementRecords] = useState<ReimbursementRecord[]>([/* 初始化报销记录 */]);
// 交互与表单状态
const [selectedUser, setSelectedUser] = useState<string | null>(null);
const [selectedPolicy, setSelectedPolicy] = useState<string | null>(null);
useState负责管理组件内部的状态数据,其不可变的状态更新机制(如setMedicalExpenses([...medicalExpenses, newExpenseEntry]))保证了医保数据的一致性——每次添加新费用时,都会创建新的数组副本,而非修改原数组,这种设计在鸿蒙系统中能够避免因引用类型数据共享导致的状态混乱。而useEffect则承担了跨端生命周期管理的核心职责,代码中通过useEffect实现的报销金额自动计算逻辑:
useEffect(() => {
const interval = setInterval(() => {
const randomUser = users[Math.floor(Math.random() * users.length)];
const randomPolicy = insurancePolicies[Math.floor(Math.random() * insurancePolicies.length)];
const randomExpense = medicalExpenses[Math.floor(Math.random() * medicalExpenses.length)];
// 核心报销金额计算逻辑(跨端一致)
const reimbursed = Math.max(0, (randomExpense.amount - randomPolicy.deductible) * randomPolicy.coverage);
const outOfPocket = randomExpense.amount - reimbursed;
const newRecord: ReimbursementRecord = {
id: (reimbursementRecords.length + 1).toString(),
userId: randomUser.id,
expenseId: randomExpense.id,
reimbursedAmount: parseFloat(reimbursed.toFixed(2)),
outOfPocket: parseFloat(outOfPocket.toFixed(2)),
status: '已结算'
};
setReimbursementRecords(prevRecords => [...prevRecords, newRecord]);
}, 30000);
return () => clearInterval(interval);
}, [users, insurancePolicies, medicalExpenses, reimbursementRecords]);
这段代码在鸿蒙系统中稳定运行的核心原因在于:setInterval/clearInterval是React Native封装的通用定时器API,已适配鸿蒙的任务调度机制;useEffect的返回清理函数则对应鸿蒙组件的onDestroy生命周期,确保定时器在组件卸载时被正确销毁,避免鸿蒙系统中的内存泄漏问题——这对需要长期运行、实时计算报销金额的医保应用尤为重要。同时,核心报销计算公式Math.max(0, (amount - deductible) * coverage)采用标准化的数学运算逻辑,在鸿蒙设备上能够保证计算结果的一致性,避免了不同平台数学库差异导致的金额偏差。
应用的UI层完全基于React Native的核心组件构建,样式通过StyleSheet.create统一管理,这种设计既保证了UI在鸿蒙系统中的无缝适配,又兼顾了医保应用对数据展示清晰性、操作安全性的特殊要求。
样式
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f9ff', // 政务类应用专属浅蓝主题,适配鸿蒙视觉规范
},
section: {
backgroundColor: '#ffffff',
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
padding: 16,
// 阴影效果跨端适配:elevation适配鸿蒙/Android,shadow系列适配iOS
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
input: {
flex: 1,
backgroundColor: '#f0f9ff',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 8,
fontSize: 14,
color: '#0c4a6e',
marginRight: 8,
},
// 按钮、卡片、弹窗等组件样式定义
});
StyleSheet将CSS样式抽象为跨平台的样式对象,在鸿蒙系统中,flex布局、padding/margin、borderRadius等核心样式属性会被精准转换为ArkUI的布局属性(如flexDirection、paddingLeft、borderRadius);针对金额类输入,TextInput组件设置keyboardType="numeric"属性,在鸿蒙设备上会自动唤起数字键盘,提升医保费用录入的准确性。代码中通过Dimensions.get('window')获取屏幕尺寸:
const { width, height } = Dimensions.get('window');
该API在鸿蒙系统中能够准确获取设备的屏幕像素尺寸,避免了直接使用鸿蒙原生API(如getWindowSize)导致的代码碎片化,保证了不同屏幕尺寸鸿蒙设备(手机、平板、政务终端)上的布局一致性——尤其适配了医保应用在鸿蒙多形态设备上的展示需求。
交互组件
核心交互逻辑基于TouchableOpacity组件实现,该组件在鸿蒙系统中会被渲染为具备点击反馈的原生按钮组件,保证了跨端交互体验的统一,同时其选中态的边框高亮样式(selectedCard)能够清晰标识当前选择的用户/医保政策,符合医保应用的操作指引要求:
<TouchableOpacity
style={[styles.card, selectedUser === user.id && styles.selectedCard]}
onPress={() => handleSelectUser(user.id)}
>
{/* 用户医保档案展示 */}
</TouchableOpacity>
Modal组件通过animationType="slide"实现滑动弹窗效果,在鸿蒙系统中适配为原生的滑动模态框,用于展示报销记录的详细金额信息(报销金额、自付金额),既保证了数据展示的完整性,又符合鸿蒙系统的交互规范。Alert.alert作为跨端通用弹窗API,在鸿蒙设备上调用原生弹窗组件,用于费用添加成功/失败、报销金额计算结果、扫码支付确认等关键操作的反馈,确保医保操作的结果提示及时且清晰。
当前代码已具备基础的鸿蒙兼容能力,在生产环境中可针对鸿蒙系统的特性和医保应用的特殊需求进行以下优化,进一步提升应用体验:
-
高性能列表渲染优化:当前医疗费用、报销记录采用ScrollView + map的方式渲染,在鸿蒙系统中面对大量历史医保数据时可能出现卡顿。可替换为React Native的FlatList组件,该组件在鸿蒙系统中会适配ArkUI的List组件,实现按需渲染和组件复用,同时通过
getItemLayout优化列表滚动性能,尤其适合展示用户长期的医保报销记录。 -
鸿蒙原生能力集成:若需调用鸿蒙的原生能力(如NFC扫码支付、鸿蒙钱包电子医保卡、分布式数据同步),可通过React Native的Native Module机制封装鸿蒙的AbilityKit和NFC相关API,构建JS与ArkTS的通信桥梁——例如将应用中的扫码支付功能对接鸿蒙的NFC原生能力,实现电子医保卡的硬件级扫码验证,在保留跨端代码架构的同时,充分利用鸿蒙系统的原生安全能力。
-
分布式医保数据同步:鸿蒙系统的核心特性是分布式能力,可基于React Native的跨端通信机制,实现医保数据在多鸿蒙设备(手机、政务终端、医院自助机)之间的分布式共享——例如在手机上录入的医疗费用,可实时同步到鸿蒙政务终端的医保结算界面,提升政务办理场景下的用户体验。
这款基于React Native开发的医保结算应用,通过TypeScript强类型约束、React Hooks状态管理、通用UI组件与样式设计,构建了具备完整鸿蒙跨端兼容能力的医保类应用架构。核心技术要点可总结为:
- 采用通用API+组件构建核心医保结算业务逻辑,规避平台专属代码,是实现鸿蒙兼容的基础,同时保证了医保金额计算的跨端一致性;
- TypeScript类型系统不仅提升医保数据的准确性,更在跨端编译阶段拦截数据类型错误,适配鸿蒙ArkTS的静态类型特性,避免金额计算的精度丢失;
- React Hooks的状态管理与鸿蒙组件生命周期深度适配,尤其满足了报销金额自动计算等周期性任务的跨端稳定运行需求;
- 统一的StyleSheet样式系统实现了UI在鸿蒙设备上的一致性渲染,兼顾医保应用的视觉清晰性和操作安全性。
真实演示案例代码:
// App.tsx
import React, { useState, useEffect } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, Modal } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
insurance: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
payment: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
scan: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
settlement: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};
const { width, height } = Dimensions.get('window');
// 用户类型
type User = {
id: string;
name: string;
insuranceCardNumber: string;
balance: number;
};
// 医保政策类型
type InsurancePolicy = {
id: string;
name: string;
coverage: number;
deductible: number;
};
// 医疗费用类型
type MedicalExpense = {
id: string;
userId: string;
date: string;
amount: number;
description: string;
};
// 报销记录类型
type ReimbursementRecord = {
id: string;
userId: string;
expenseId: string;
reimbursedAmount: number;
outOfPocket: number;
status: string;
};
// 医保结算应用组件
const MedicalInsuranceSettlementApp: React.FC = () => {
const [users] = useState<User[]>([
{
id: '1',
name: '李先生',
insuranceCardNumber: '123456789012345678',
balance: 5000
},
{
id: '2',
name: '王女士',
insuranceCardNumber: '987654321098765432',
balance: 3000
}
]);
const [insurancePolicies] = useState<InsurancePolicy[]>([
{
id: '1',
name: '城镇职工医保',
coverage: 0.8,
deductible: 1000
},
{
id: '2',
name: '城乡居民医保',
coverage: 0.7,
deductible: 500
}
]);
const [medicalExpenses, setMedicalExpenses] = useState<MedicalExpense[]>([
{
id: '1',
userId: '1',
date: '2023-12-01',
amount: 2000,
description: '门诊费用'
},
{
id: '2',
userId: '2',
date: '2023-12-02',
amount: 1500,
description: '住院费用'
}
]);
const [reimbursementRecords, setReimbursementRecords] = useState<ReimbursementRecord[]>([
{
id: '1',
userId: '1',
expenseId: '1',
reimbursedAmount: 800,
outOfPocket: 1200,
status: '已结算'
}
]);
const [selectedUser, setSelectedUser] = useState<string | null>(null);
const [selectedPolicy, setSelectedPolicy] = useState<string | null>(null);
const [newExpense, setNewExpense] = useState({
date: '',
amount: '',
description: ''
});
const [isModalVisible, setIsModalVisible] = useState(false);
const [modalContent, setModalContent] = useState('');
// 自动计算报销金额
useEffect(() => {
const interval = setInterval(() => {
const randomUser = users[Math.floor(Math.random() * users.length)];
const randomPolicy = insurancePolicies[Math.floor(Math.random() * insurancePolicies.length)];
const randomExpense = medicalExpenses[Math.floor(Math.random() * medicalExpenses.length)];
const reimbursed = Math.max(0, (randomExpense.amount - randomPolicy.deductible) * randomPolicy.coverage);
const outOfPocket = randomExpense.amount - reimbursed;
const newRecord: ReimbursementRecord = {
id: (reimbursementRecords.length + 1).toString(),
userId: randomUser.id,
expenseId: randomExpense.id,
reimbursedAmount: parseFloat(reimbursed.toFixed(2)),
outOfPocket: parseFloat(outOfPocket.toFixed(2)),
status: '已结算'
};
setReimbursementRecords(prevRecords => [...prevRecords, newRecord]);
}, 30000);
return () => clearInterval(interval);
}, [users, insurancePolicies, medicalExpenses, reimbursementRecords]);
const handleSelectUser = (userId: string) => {
setSelectedUser(userId);
Alert.alert('选择用户', '您已选择该用户进行医保结算');
};
const handleSelectPolicy = (policyId: string) => {
setSelectedPolicy(policyId);
Alert.alert('选择政策', '您已选择该医保政策');
};
const handleAddExpense = () => {
if (newExpense.date && newExpense.amount && newExpense.description && selectedUser) {
const newExpenseEntry: MedicalExpense = {
id: (medicalExpenses.length + 1).toString(),
userId: selectedUser,
date: newExpense.date,
amount: parseFloat(newExpense.amount),
description: newExpense.description
};
setMedicalExpenses([...medicalExpenses, newExpenseEntry]);
setNewExpense({ date: '', amount: '', description: '' });
Alert.alert('添加成功', '新的医疗费用已添加');
} else {
Alert.alert('提示', '请选择用户并填写完整的费用信息');
}
};
const handleCalculateReimbursement = (expenseId: string) => {
const expense = medicalExpenses.find(e => e.id === expenseId);
const policy = insurancePolicies.find(p => p.id === selectedPolicy);
if (expense && policy && selectedUser) {
const reimbursed = Math.max(0, (expense.amount - policy.deductible) * policy.coverage);
const outOfPocket = expense.amount - reimbursed;
const newRecord: ReimbursementRecord = {
id: (reimbursementRecords.length + 1).toString(),
userId: selectedUser,
expenseId: expense.id,
reimbursedAmount: parseFloat(reimbursed.toFixed(2)),
outOfPocket: parseFloat(outOfPocket.toFixed(2)),
status: '已结算'
};
setReimbursementRecords([...reimbursementRecords, newRecord]);
Alert.alert('计算完成', `报销金额: ${reimbursed.toFixed(2)}元, 自付金额: ${outOfPocket.toFixed(2)}元`);
} else {
Alert.alert('提示', '请选择医保政策并确保费用信息完整');
}
};
const handleScanPayment = () => {
if (selectedUser) {
const user = users.find(u => u.id === selectedUser);
if (user) {
Alert.alert('扫码支付', `已扫描${user.name}的电子医保卡,准备支付`);
}
} else {
Alert.alert('提示', '请选择用户');
}
};
const handleRemoteSettlement = () => {
if (selectedUser) {
const user = users.find(u => u.id === selectedUser);
if (user) {
Alert.alert('异地结算', `已为${user.name}办理异地医保结算`);
}
} else {
Alert.alert('提示', '请选择用户');
}
};
const handleViewExpense = (expenseId: string) => {
const expense = medicalExpenses.find(e => e.id === expenseId);
if (expense) {
const user = users.find(u => u.id === expense.userId);
setModalContent(`用户: ${user?.name}\n日期: ${expense.date}\n金额: ${expense.amount}元\n描述: ${expense.description}`);
setIsModalVisible(true);
}
};
const handleViewReimbursement = (recordId: string) => {
const record = reimbursementRecords.find(r => r.id === recordId);
if (record) {
const user = users.find(u => u.id === record.userId);
const expense = medicalExpenses.find(e => e.id === record.expenseId);
setModalContent(`用户: ${user?.name}\n费用: ${expense?.description}\n报销金额: ${record.reimbursedAmount}元\n自付金额: ${record.outOfPocket}元\n状态: ${record.status}`);
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}>自动计算医保报销金额及自付金额,支持电子医保卡扫码支付及异地医保结算</Text>
</View>
<ScrollView style={styles.content}>
{/* 用户列表 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>用户列表</Text>
{users.map(user => (
<TouchableOpacity
key={user.id}
style={[
styles.card,
selectedUser === user.id && styles.selectedCard
]}
onPress={() => handleSelectUser(user.id)}
>
<Text style={styles.icon}>👤</Text>
<View style={styles.cardInfo}>
<Text style={styles.cardTitle}>{user.name}</Text>
<Text style={styles.cardDescription}>医保卡号: {user.insuranceCardNumber}</Text>
<Text style={styles.cardDescription}>余额: {user.balance}元</Text>
</View>
</TouchableOpacity>
))}
</View>
{/* 医保政策 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>医保政策</Text>
{insurancePolicies.map(policy => (
<TouchableOpacity
key={policy.id}
style={[
styles.card,
selectedPolicy === policy.id && styles.selectedCard
]}
onPress={() => handleSelectPolicy(policy.id)}
>
<Text style={styles.icon}>📋</Text>
<View style={styles.cardInfo}>
<Text style={styles.cardTitle}>{policy.name}</Text>
<Text style={styles.cardDescription}>报销比例: {(policy.coverage * 100).toFixed(0)}%</Text>
<Text style={styles.cardDescription}>起付线: {policy.deductible}元</Text>
</View>
</TouchableOpacity>
))}
</View>
{/* 医疗费用录入 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>医疗费用录入</Text>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
placeholder="日期 (YYYY-MM-DD)"
value={newExpense.date}
onChangeText={(text) => setNewExpense({ ...newExpense, date: text })}
/>
<TextInput
style={styles.input}
placeholder="金额 (元)"
value={newExpense.amount}
onChangeText={(text) => setNewExpense({ ...newExpense, amount: text })}
keyboardType="numeric"
/>
</View>
<View style={styles.inputRow}>
<TextInput
style={styles.input}
placeholder="费用描述"
value={newExpense.description}
onChangeText={(text) => setNewExpense({ ...newExpense, description: text })}
/>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={handleAddExpense}
>
<Text style={styles.addText}>添加费用</Text>
</TouchableOpacity>
</View>
{/* 医疗费用列表 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>医疗费用列表</Text>
{medicalExpenses.map(expense => (
<TouchableOpacity
key={expense.id}
style={styles.expenseCard}
onPress={() => handleViewExpense(expense.id)}
>
<Text style={styles.icon}>💰</Text>
<View style={styles.cardInfo}>
<Text style={styles.cardTitle}>费用ID: {expense.id}</Text>
<Text style={styles.cardDescription}>日期: {expense.date}</Text>
<Text style={styles.cardDescription}>金额: {expense.amount}元</Text>
<Text style={styles.cardDescription}>描述: {expense.description}</Text>
</View>
<TouchableOpacity
style={styles.calculateButton}
onPress={() => handleCalculateReimbursement(expense.id)}
>
<Text style={styles.calculateText}>计算报销</Text>
</TouchableOpacity>
</TouchableOpacity>
))}
</View>
{/* 报销记录 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>报销记录</Text>
{reimbursementRecords.map(record => (
<TouchableOpacity
key={record.id}
style={styles.recordCard}
onPress={() => handleViewReimbursement(record.id)}
>
<Text style={styles.icon}>📊</Text>
<View style={styles.cardInfo}>
<Text style={styles.cardTitle}>记录ID: {record.id}</Text>
<Text style={styles.cardDescription}>报销金额: {record.reimbursedAmount}元</Text>
<Text style={styles.cardDescription}>自付金额: {record.outOfPocket}元</Text>
<Text style={styles.cardDescription}>状态: {record.status}</Text>
</View>
</TouchableOpacity>
))}
</View>
{/* 支付与结算 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>支付与结算</Text>
<TouchableOpacity
style={styles.paymentButton}
onPress={handleScanPayment}
>
<Text style={styles.paymentText}>扫码支付</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.settlementButton}
onPress={handleRemoteSettlement}
>
<Text style={styles.settlementText}>异地结算</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>
<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,
},
card: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f0f9ff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
},
selectedCard: {
borderWidth: 2,
borderColor: '#0284c7',
},
expenseCard: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f0f9ff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
},
recordCard: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f0f9ff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
},
icon: {
fontSize: 28,
marginRight: 12,
},
cardInfo: {
flex: 1,
},
cardTitle: {
fontSize: 16,
fontWeight: '500',
color: '#0c4a6e',
marginBottom: 4,
},
cardDescription: {
fontSize: 14,
color: '#0284c7',
marginBottom: 2,
},
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',
},
calculateButton: {
backgroundColor: '#10b981',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
},
calculateText: {
color: '#ffffff',
fontSize: 12,
fontWeight: '500',
},
paymentButton: {
backgroundColor: '#0284c7',
padding: 16,
borderRadius: 12,
alignItems: 'center',
marginBottom: 12,
},
paymentText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '500',
},
settlementButton: {
backgroundColor: '#ef4444',
padding: 16,
borderRadius: 12,
alignItems: 'center',
},
settlementText: {
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 MedicalInsuranceSettlementApp;

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

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

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

本文介绍了一个基于React Native构建的医保结算应用的技术实现,重点探讨了其在鸿蒙跨平台开发中的应用。该应用采用React Native函数式组件架构,结合TypeScript类型系统和React Hooks,实现了医保结算管理系统。核心技术包括跨端开发框架、类型安全、状态管理、Base64图标资源和响应式布局。数据模型定义了用户、医保政策、医疗费用和报销记录等核心类型,确保跨平台数据一致性。应用通过useState管理状态,useEffect实现自动计算报销金额的功能,并提供了用户选择、费用添加和报销计算等核心功能,展示了React Native在医疗健康领域的跨平台开发能力。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐


所有评论(0)