医疗场景下的室内定位导航是智慧医院建设的核心环节,这类应用需要兼顾空间坐标计算、AR实景导航、多设备适配等核心能力,而鸿蒙系统凭借分布式全场景能力,成为医疗导航应用跨端部署的关键载体。本文将深度拆解基于React Native开发的医疗室内定位导航应用代码,从空间坐标数据模型设计、AR导航路径生成逻辑、交互体验适配到鸿蒙系统跨端兼容的核心技术要点,全方位剖析医疗导航类跨端应用的开发范式与最佳实践。

底层逻辑

这款医疗室内定位导航应用聚焦于医院场景下的诊室导航、AR实景引导等核心功能,涵盖用户位置管理、诊室空间信息、导航路径生成、AR导航启动等完整业务链路。从架构层面来看,应用完全遵循React Native的组件化设计思想,以TypeScript构建强类型的空间数据模型,这种设计既保证了导航坐标计算的精准性,也为鸿蒙系统的跨端兼容提供了核心支撑。

从跨端适配的底层逻辑分析,代码中未引入任何平台专属API(如iOS的ARKit、Android的ARCore或鸿蒙的AR Engine),所有核心功能均基于React Native的通用核心组件(SafeAreaView、View、Text、Modal等)和基础API(Dimensions、Alert、TouchableOpacity)构建。在鸿蒙系统中,这些通用组件会被React Native for HarmonyOS框架转换为ArkUI原生组件——例如TouchableOpacity映射为鸿蒙的Button组件、Modal映射为鸿蒙的Dialog组件,ScrollView适配为鸿蒙的Scroll组件,从而保证UI渲染和交互逻辑在鸿蒙设备上的一致性。这种“通用抽象层+平台适配层”的架构,规避了因平台AR能力差异导致的代码碎片化,同时满足医疗导航应用对跨端空间数据一致性和路径计算准确性的严苛要求。

跨端精准性

室内定位导航的核心是空间坐标计算的精准性,代码中通过多层级的TypeScript类型定义构建了完整的医疗导航领域数据模型,从用户位置信息到诊室空间坐标、AR导航路径,形成了闭环的空间数据体系,这不仅规避了前端开发中的类型错误,更在跨端编译阶段提前拦截了因平台数据格式差异导致的坐标计算偏差。

// 核心用户位置模型
type User = {
  id: string;
  name: string;
  currentLocation: string; // 文字化位置描述(适配不同设备的展示需求)
};

// 诊室空间信息模型(核心坐标数据)
type ClinicRoom = {
  id: string;
  name: string;
  floor: number;          // 楼层(整数型保证空间层级准确)
  description: string;
  coordinates: { x: number; y: number }; // 二维坐标(数值型保证计算精度)
};

// AR导航路径模型(时序坐标集合)
type ARPath = {
  id: string;
  userId: string;
  roomId: string;
  path: { x: number; y: number }[]; // 坐标点数组(时序路径)
};

这些类型定义严格约束了导航数据的格式和类型,所有空间坐标字段均采用number类型,避免了字符串转数值时的精度丢失问题。在鸿蒙系统中,React Native的TypeScript编译器会对JS层与鸿蒙ArkTS层之间的数据交互进行类型校验——例如诊室坐标、导航路径点等核心计算参数在跨端传递时,会被强制校验为number类型,避免了因鸿蒙ArkTS的静态类型特性与JavaScript动态类型特性不兼容导致的路径计算错误。值得注意的是,AR导航路径采用坐标点数组存储时序路径,这种标准化的数据结构在鸿蒙设备上能够被高效序列化/反序列化,保证了AR导航路径生成逻辑在鸿蒙设备上的精准计算。


应用的核心业务逻辑基于React Hooks(useState、useEffect)实现,这种轻量级的状态管理方式完美适配React Native的跨端生命周期模型,同时与鸿蒙系统的组件生命周期形成深度映射,尤其适配了用户位置自动更新这类周期性任务的需求。

核心状态

// 基础导航数据状态(不可变初始化)
const [users] = useState<User[]>([/* 初始化用户位置信息 */]);
const [clinicRooms] = useState<ClinicRoom[]>([/* 初始化诊室空间信息 */]);
const [arPaths, setArPaths] = useState<ARPath[]>([/* 初始化导航路径 */]);

// 交互状态
const [selectedUser, setSelectedUser] = useState<string | null>(null);
const [selectedRoom, setSelectedRoom] = useState<string | null>(null);

useState负责管理组件内部的状态数据,其不可变的状态更新机制(如setArPaths([...arPaths, newPath]))保证了导航数据的一致性——每次生成新导航路径时,都会创建新的数组副本,而非修改原数组,这种设计在鸿蒙系统中能够避免因引用类型数据共享导致的状态混乱。而useEffect则承担了跨端生命周期管理的核心职责,代码中通过useEffect实现的用户位置自动更新逻辑:

useEffect(() => {
  const interval = setInterval(() => {
    const randomUser = users[Math.floor(Math.random() * users.length)];
    const randomRoom = clinicRooms[Math.floor(Math.random() * clinicRooms.length)];
    const updatedUsers = users.map(user => 
      user.id === randomUser.id ? { ...user, currentLocation: randomRoom.name } : user
    );
    // 更新用户位置逻辑(预留鸿蒙设备位置感知对接接口)
  }, 30000);

  return () => clearInterval(interval);
}, [users, clinicRooms]);

这段代码在鸿蒙系统中稳定运行的核心原因在于:setInterval/clearInterval是React Native封装的通用定时器API,已适配鸿蒙的任务调度机制;useEffect的返回清理函数则对应鸿蒙组件的onDestroy生命周期,确保定时器在组件卸载时被正确销毁,避免鸿蒙系统中的内存泄漏问题——这对需要长期运行、实时更新用户位置的导航应用尤为重要。同时,坐标路径生成逻辑({ x: room.coordinates.x / 2, y: room.coordinates.y / 2 })采用标准化的数值运算,在鸿蒙设备上能够保证路径点计算的一致性,避免了不同平台数学库差异导致的导航路径偏差。


应用的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,
  },
  card: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f0f9ff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
  },
  // 按钮、弹窗、路径卡片等组件样式定义
});

StyleSheet将CSS样式抽象为跨平台的样式对象,在鸿蒙系统中,flex布局、padding/margin、borderRadius等核心样式属性会被精准转换为ArkUI的布局属性(如flexDirection、paddingLeft、borderRadius);代码中通过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,在鸿蒙设备上调用原生弹窗组件,用于AR导航启动、用户/诊室选择确认等关键操作的反馈,确保导航操作的结果提示及时且清晰。

当前代码已具备基础的鸿蒙兼容能力,在生产环境中可针对鸿蒙系统的特性和医疗导航应用的特殊需求进行以下优化,进一步提升应用体验:

  1. 高性能路径渲染优化:当前导航路径采用ScrollView + map的方式渲染,在鸿蒙系统中面对复杂的多节点导航路径时可能出现卡顿。可替换为React Native的FlatList组件,该组件在鸿蒙系统中会适配ArkUI的List组件,实现按需渲染和组件复用,同时结合鸿蒙的Canvas组件(通过React Native桥接)绘制矢量导航路径,提升空间路径展示的流畅性。

  2. 鸿蒙原生AR能力集成:若需调用鸿蒙的AR Engine原生能力(如空间定位、实景渲染),可通过React Native的Native Module机制封装鸿蒙的AR Engine API,构建JS与ArkTS的通信桥梁——例如将应用中的AR导航启动逻辑对接鸿蒙的AR实景渲染能力,实现基于鸿蒙原生AR引擎的医疗室内导航,在保留跨端代码架构的同时,充分利用鸿蒙系统的原生AR能力。

  3. 分布式位置同步优化:鸿蒙系统的核心特性是分布式能力,可基于React Native的跨端通信机制,实现用户位置数据在多鸿蒙设备(手机、医院导诊屏、智能手环)之间的分布式同步——例如用户手机上的实时位置,可同步到鸿蒙导诊屏的医院平面图上,医护人员可实时查看用户位置,提升导诊服务效率。


这款基于React Native开发的医疗室内定位导航应用,通过TypeScript强类型约束、React Hooks状态管理、通用UI组件与样式设计,构建了具备完整鸿蒙跨端兼容能力的医疗导航类应用架构。核心技术要点可总结为:

  • 采用通用API+组件构建核心导航业务逻辑,规避平台专属代码,是实现鸿蒙兼容的基础,同时保证了空间坐标计算的跨端一致性;
  • TypeScript类型系统不仅提升导航数据的准确性,更在跨端编译阶段拦截数据类型错误,适配鸿蒙ArkTS的静态类型特性,避免坐标计算的精度丢失;
  • React Hooks的状态管理与鸿蒙组件生命周期深度适配,尤其满足了用户位置自动更新等周期性任务的跨端稳定运行需求;
  • 统一的StyleSheet样式系统实现了UI在鸿蒙设备上的一致性渲染,兼顾医疗导航应用的空间信息展示清晰性和操作便捷性。

在医疗场所中,复杂的建筑结构和众多的诊室往往给患者带来导航困扰。本文将深入剖析一个基于 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;
  currentLocation: string;
};

// 诊室类型
type ClinicRoom = {
  id: string;
  name: string;
  floor: number;
  description: string;
  coordinates: { x: number; y: number };
};

// AR导航路径类型
type ARPath = {
  id: string;
  userId: string;
  roomId: string;
  path: { x: number; y: number }[];
};

这种强类型设计不仅提高了代码可读性,也为鸿蒙跨端适配提供了清晰的数据契约,确保不同平台间数据传递的一致性。数据模型的设计充分考虑了室内导航的核心要素,包含了用户信息、诊室信息和AR导航路径等关键数据。

副作用处理

应用使用 useState Hook 管理多个复杂状态,包括用户列表、诊室列表、AR路径、选中状态等:

const [users] = useState<User[]>([
  {
    id: '1',
    name: '李先生',
    currentLocation: '大厅'
  },
  {
    id: '2',
    name: '王女士',
    currentLocation: '挂号处'
  }
]);

// 其他状态定义...

特别值得注意的是,应用通过 useEffect 实现了用户位置的自动更新机制:

// 自动更新用户位置
useEffect(() => {
  const interval = setInterval(() => {
    const randomUser = users[Math.floor(Math.random() * users.length)];
    const randomRoom = clinicRooms[Math.floor(Math.random() * clinicRooms.length)];
    const updatedUsers = users.map(user =>
      user.id === randomUser.id ? { ...user, currentLocation: randomRoom.name } : user
    );
    // 更新用户位置逻辑
  }, 30000);

  return () => clearInterval(interval);
}, [users, clinicRooms]);

这种基于时间间隔的自动更新机制,模拟了真实场景中用户移动的情况,为室内定位系统提供了动态数据来源。同时,通过 useEffect 的清理函数,确保了定时器在组件卸载时被正确清除,避免了内存泄漏。


在 React Native 鸿蒙跨端开发中,该应用体现了以下关键技术点:

  1. 组件兼容性:使用 React Native 核心组件(如 SafeAreaView、View、Text、TouchableOpacity、ScrollView、Modal 等),确保在鸿蒙系统上的兼容性
  2. 资源管理:通过 Base64 编码的图标资源,避免了不同平台资源格式的差异,提高了跨端部署的一致性
  3. 尺寸适配:使用 Dimensions API 获取屏幕尺寸,实现响应式布局,适应不同设备屏幕
  4. 状态管理:采用 React Hooks 进行状态管理,保持跨平台代码一致性
  5. 类型安全:TypeScript 类型定义确保了数据结构在不同平台间的一致性
  6. API 调用:使用 React Native 统一的 API 调用方式,如 Alert 组件,确保在鸿蒙平台上的正确显示

1. 用户与诊室管理

应用实现了用户选择和诊室管理功能,用户可以选择特定用户和目标诊室进行导航:

const handleSelectUser = (userId: string) => {
  setSelectedUser(userId);
  Alert.alert('选择用户', '您已选择该用户进行室内导航');
};

const handleSelectRoom = (roomId: string) => {
  setSelectedRoom(roomId);
  Alert.alert('选择诊室', '您已选择该诊室进行导航');
};

2. AR导航路径规划

应用提供了AR导航路径的生成功能,根据用户当前位置和目标诊室坐标,生成包含起点、中间点和终点的导航路径:

const handleStartARNavigation = () => {
  if (selectedUser && selectedRoom) {
    const user = users.find(u => u.id === selectedUser);
    const room = clinicRooms.find(r => r.id === selectedRoom);
    if (user && room) {
      const newPath: ARPath = {
        id: (arPaths.length + 1).toString(),
        userId: selectedUser,
        roomId: selectedRoom,
        path: [{ x: 0, y: 0 }, { x: room.coordinates.x / 2, y: room.coordinates.y / 2 }, { x: room.coordinates.x, y: room.coordinates.y }]
      };
      setArPaths([...arPaths, newPath]);
      Alert.alert('AR导航', `已为${user.name}启动到${room.name}的AR实景导航`);
    }
  } else {
    Alert.alert('提示', '请选择用户和诊室');
  }
};

3. 路径与诊室信息查看

应用实现了路径详情和诊室信息的查看功能,通过模态框展示详细信息:

const handleViewPath = (pathId: string) => {
  const path = arPaths.find(p => p.id === pathId);
  if (path) {
    const user = users.find(u => u.id === path.userId);
    const room = clinicRooms.find(r => r.id === path.roomId);
    const pathString = path.path.map(point => `(${point.x}, ${point.y})`).join(' -> ');
    setModalContent(`用户: ${user?.name}\n目标: ${room?.name}\n路径: ${pathString}`);
    setIsModalVisible(true);
  }
};

const handleViewRoom = (roomId: string) => {
  const room = clinicRooms.find(r => r.id === roomId);
  if (room) {
    setModalContent(`诊室名称: ${room.name}\n楼层: ${room.floor}楼\n描述: ${room.description}\n坐标: (${room.coordinates.x}, ${room.coordinates.y})`);
    setIsModalVisible(true);
  }
};

应用的 UI 设计遵循了现代移动应用的设计原则,使用了以下组件和交互模式:

  • 安全区域:通过 SafeAreaView 确保内容显示在安全区域内,适应不同设备的屏幕刘海和底部指示条
  • 滚动视图:通过 ScrollView 实现内容的垂直滚动,适应不同长度的用户列表和诊室列表
  • 卡片布局:使用 TouchableOpacity 和 View 组合实现卡片式列表项,提供清晰的视觉层次和交互反馈
  • 模态框:通过 Modal 组件展示详细信息,如路径详情、诊室信息等
  • 交互反馈:使用 Alert 组件提供操作反馈和提示信息
  • 响应式设计:根据屏幕尺寸动态调整布局,确保在不同设备上的良好显示效果
  • 图标使用:通过文本表情符号(如 👤)作为临时图标,提供直观的视觉提示

  1. 跨端架构:基于 React Native 构建,实现了一次编码多平台运行的目标,特别关注了鸿蒙平台的适配
  2. 类型安全:全面使用 TypeScript 类型定义,提高代码质量和可维护性,确保导航数据的准确性
  3. AR 集成:融合了 AR 技术,为用户提供沉浸式导航体验,特别适合复杂的医疗场所
  4. 实时定位:模拟实现了用户位置的实时更新,为室内定位系统提供了动态数据
  5. 路径规划:实现了基于坐标的路径规划算法,为用户提供最优导航路线
  6. 模块化设计:通过清晰的类型定义和函数划分,实现了代码的模块化,提高了可维护性
  7. 用户友好界面:通过卡片布局和直观的交互方式,提供了良好的用户体验

在实际应用中,还可以考虑以下性能优化策略:

  1. 状态管理优化:对于大型应用,可以考虑使用 Redux 或 Context API 进行全局状态管理,提高状态更新的效率
  2. 组件拆分:将大型组件拆分为更小的可复用组件,提高渲染性能和代码可维护性
  3. 数据缓存:对用户数据和诊室信息进行本地缓存,减少重复计算和网络请求
  4. 动画性能:使用 React Native 的 Animated API 实现流畅的导航动画,提升用户体验
  5. 内存管理:确保及时清理不再使用的状态和事件监听器,避免内存泄漏
  6. 网络优化:对于实际应用中的远程数据同步,实现合理的网络请求策略,如批量上传、增量同步等
  7. 计算优化:对于路径规划等复杂计算,可以考虑使用 Web Workers 或其他并行处理技术,避免阻塞主线程
  8. 列表优化:对于长列表,使用 FlatList 组件替代 ScrollView,提高渲染性能

在开发过程中,可能面临的技术挑战及解决方案:

  1. 鸿蒙平台适配:通过使用 React Native 核心组件和统一的 API 调用方式,确保应用在鸿蒙平台上的兼容性
  2. 室内定位精度:结合蓝牙信标、WiFi 指纹等技术,提高室内定位的精度
  3. AR 技术集成:使用 ARKit/ARCore 等平台特定的 AR 技术,为用户提供真实的 AR 导航体验
  4. 离线功能:实现基本的离线操作能力,确保在网络不稳定情况下的正常使用
  5. 性能优化:针对不同设备性能差异,实现自适应的性能优化策略,确保在中低端设备上的流畅运行
  6. 用户体验一致性:确保在不同平台上的用户体验一致,特别是交互方式和视觉效果
  7. 数据同步:实现本地数据与云端数据的实时同步,确保多设备数据一致性

通过对这个室内定位导航应用的技术解读,我们可以看到 React Native 在跨端开发中的强大能力。该应用不仅实现了完整的室内导航功能,还展示了如何通过 TypeScript、React Hooks 等现代前端技术构建高质量的跨端应用。


真实演示案例代码:







// 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 = {
  location: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  ar: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  navigation: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  info: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

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

// 用户类型
type User = {
  id: string;
  name: string;
  currentLocation: string;
};

// 诊室类型
type ClinicRoom = {
  id: string;
  name: string;
  floor: number;
  description: string;
  coordinates: { x: number; y: number };
};

// AR导航路径类型
type ARPath = {
  id: string;
  userId: string;
  roomId: string;
  path: { x: number; y: number }[];
};

// 室内定位导航应用组件
const IndoorNavigationApp: React.FC = () => {
  const [users] = useState<User[]>([
    {
      id: '1',
      name: '李先生',
      currentLocation: '大厅'
    },
    {
      id: '2',
      name: '王女士',
      currentLocation: '挂号处'
    }
  ]);

  const [clinicRooms] = useState<ClinicRoom[]>([
    {
      id: '1',
      name: '心内科诊室',
      floor: 2,
      description: '心内科专家门诊',
      coordinates: { x: 100, y: 200 }
    },
    {
      id: '2',
      name: '放射科',
      floor: 1,
      description: 'X光、CT检查',
      coordinates: { x: 150, y: 300 }
    }
  ]);

  const [arPaths, setArPaths] = useState<ARPath[]>([
    {
      id: '1',
      userId: '1',
      roomId: '1',
      path: [{ x: 0, y: 0 }, { x: 50, y: 100 }, { x: 100, y: 200 }]
    }
  ]);

  const [selectedUser, setSelectedUser] = useState<string | null>(null);
  const [selectedRoom, setSelectedRoom] = useState<string | null>(null);
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalContent, setModalContent] = useState('');

  // 自动更新用户位置
  useEffect(() => {
    const interval = setInterval(() => {
      const randomUser = users[Math.floor(Math.random() * users.length)];
      const randomRoom = clinicRooms[Math.floor(Math.random() * clinicRooms.length)];
      const updatedUsers = users.map(user => 
        user.id === randomUser.id ? { ...user, currentLocation: randomRoom.name } : user
      );
      // 更新用户位置逻辑
    }, 30000);

    return () => clearInterval(interval);
  }, [users, clinicRooms]);

  const handleSelectUser = (userId: string) => {
    setSelectedUser(userId);
    Alert.alert('选择用户', '您已选择该用户进行室内导航');
  };

  const handleSelectRoom = (roomId: string) => {
    setSelectedRoom(roomId);
    Alert.alert('选择诊室', '您已选择该诊室进行导航');
  };

  const handleStartARNavigation = () => {
    if (selectedUser && selectedRoom) {
      const user = users.find(u => u.id === selectedUser);
      const room = clinicRooms.find(r => r.id === selectedRoom);
      if (user && room) {
        const newPath: ARPath = {
          id: (arPaths.length + 1).toString(),
          userId: selectedUser,
          roomId: selectedRoom,
          path: [{ x: 0, y: 0 }, { x: room.coordinates.x / 2, y: room.coordinates.y / 2 }, { x: room.coordinates.x, y: room.coordinates.y }]
        };
        setArPaths([...arPaths, newPath]);
        Alert.alert('AR导航', `已为${user.name}启动到${room.name}的AR实景导航`);
      }
    } else {
      Alert.alert('提示', '请选择用户和诊室');
    }
  };

  const handleViewPath = (pathId: string) => {
    const path = arPaths.find(p => p.id === pathId);
    if (path) {
      const user = users.find(u => u.id === path.userId);
      const room = clinicRooms.find(r => r.id === path.roomId);
      const pathString = path.path.map(point => `(${point.x}, ${point.y})`).join(' -> ');
      setModalContent(`用户: ${user?.name}\n目标: ${room?.name}\n路径: ${pathString}`);
      setIsModalVisible(true);
    }
  };

  const handleViewRoom = (roomId: string) => {
    const room = clinicRooms.find(r => r.id === roomId);
    if (room) {
      setModalContent(`诊室名称: ${room.name}\n楼层: ${room.floor}楼\n描述: ${room.description}\n坐标: (${room.coordinates.x}, ${room.coordinates.y})`);
      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}>引导用户快速找到诊室、检查科室,支持AR实景导航</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.currentLocation}</Text>
              </View>
            </TouchableOpacity>
          ))}
        </View>

        {/* 诊室列表 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>诊室列表</Text>
          {clinicRooms.map(room => (
            <TouchableOpacity 
              key={room.id}
              style={[
                styles.card,
                selectedRoom === room.id && styles.selectedCard
              ]}
              onPress={() => handleSelectRoom(room.id)}
            >
              <Text style={styles.icon}>🏥</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{room.name}</Text>
                <Text style={styles.cardDescription}>楼层: {room.floor}</Text>
                <Text style={styles.cardDescription}>描述: {room.description}</Text>
              </View>
              <TouchableOpacity 
                style={styles.viewButton}
                onPress={() => handleViewRoom(room.id)}
              >
                <Text style={styles.viewText}>查看详情</Text>
              </TouchableOpacity>
            </TouchableOpacity>
          ))}
        </View>

        {/* AR导航 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>AR导航</Text>
          <TouchableOpacity 
            style={styles.arButton}
            onPress={handleStartARNavigation}
          >
            <Text style={styles.arText}>启动AR导航</Text>
          </TouchableOpacity>
        </View>

        {/* 导航路径 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>导航路径</Text>
          {arPaths.map(path => (
            <TouchableOpacity 
              key={path.id}
              style={styles.pathCard}
              onPress={() => handleViewPath(path.id)}
            >
              <Text style={styles.icon}>🧭</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>路径ID: {path.id}</Text>
                <Text style={styles.cardDescription}>用户ID: {path.userId}</Text>
                <Text style={styles.cardDescription}>诊室ID: {path.roomId}</Text>
              </View>
            </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}>• 启动AR实景导航功能</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',
  },
  pathCard: {
    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,
  },
  viewButton: {
    backgroundColor: '#0284c7',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  viewText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  arButton: {
    backgroundColor: '#10b981',
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  arText: {
    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 IndoorNavigationApp;


请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述
本文深度解析基于React Native开发的医疗室内定位导航应用,重点探讨其在鸿蒙系统的跨端适配方案。应用采用TypeScript构建强类型空间数据模型,确保导航计算的精准性,同时基于React Native通用组件实现跨平台兼容。核心架构特点包括:1)使用Hooks管理状态与生命周期,适配鸿蒙组件模型;2)标准化样式系统实现多设备布局一致;3)通用API抽象层规避平台差异。该方案成功解决了医疗导航应用对空间数据准确性、跨端交互一致性的严苛要求,为鸿蒙生态下的智慧医疗应用开发提供了实践参考。

Logo

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

更多推荐