ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

鸿蒙医院陪诊系统源码拆包:三步跑通与避坑指南

鸿蒙医院陪诊系统源码拆包:三步跑通与避坑指南

简介:这是一份面向HarmonyOS初学者与ArkTS开发者的医院陪诊系统轻应用源码,基于HarmonyOS SDK 6.0.0、Stage模型与ArkTS语言实现,适配手机和平板设备。系统覆盖用户注册、登录、病情选择与陪诊确认等核心流程,采用纯文字交互,并借助页面路由、状态管理和本地数据模拟完成无后端的数据暂存,适合作为鸿蒙应用入门练手或课程设计参考。资源包共12个文件,约10KB,以json、json5配置文件为主,配合ts业务代码、html页面、png图标及md说明文档,结构清晰,涵盖用户数据模型、应用入口管理、登录注册页、病情选择页与陪诊确认页等模块。目前已有141人学习。读者可从中获取完整的项目目录组织方式、编译运行指南与常见问题解决方案,并了解持久化存储、网络请求对接和样式优化等扩展方向,便于在此基础上二次开发与功能迭代。

1. 鸿蒙医院陪诊系统源码拆包:从跑不起来到跑通只要三步

拿到一份鸿蒙医院陪诊系统的代码包,最怕的不是代码多,而是打开 DevEco Studio 之后一片红。我见过太多人下载完源码,导入、Sync、报错、关掉,然后再也没打开过。这份陪诊系统源码解决的就是这个尴尬——它把患者端、陪诊员端、管理端三套角色的核心流程都写完了,挂号预约、订单派单、实时位置、服务评价这些模块一个不少,基于 ArkTS + ArkUI 声明式开发,后端接口用 mock 数据兜底,不依赖真实服务器就能跑起来看效果。适合正在做鸿蒙课程设计的学生、想快速搭医疗类 App 原型的独立开发者,以及需要一套完整业务流参考的鸿蒙应用工程师。下面我按实际拆包顺序,把环境、结构、跑通、踩坑、进阶五件事讲透。

2. 环境与工程结构:先搞清楚这套代码依赖什么

2.1 版本对齐是第一步,别急着点 Sync

鸿蒙项目的版本敏感度比 Android 高得多。这份源码用的是 API 11(HarmonyOS 4.0)的 Stage 模型,build-profile.json5里compileSdkVersion和compatibleSdkVersion都锁在 11。如果你本地装的是 API 9 或者 API 12,Sync 阶段就会报The SDK version does not match或者一堆@ohos.xxx找不到的错。常见做法是打开 DevEco Studio 的 SDK Manager,确认HarmonyOS SDK下 API 11 的 Full SDK 已经下载,然后在File → Project Structure → Project里把编译版本切到 11。

另一个容易忽略的是 hvigor 版本。源码根目录的hvigor/hvigor-config.json5里指定了hvigorVersion,如果本地缓存的 hvigor 版本不一致,Sync 会卡在Installing hvigor然后超时。我一般会先看这个文件,再去ohpm仓库确认依赖能拉到。网络环境不通畅的话,ohpm install会反复重试,这时候别硬等,先检查ohpmrc里的 registry 配置。

// build-profile.json5 关键字段 { "app": { "compileSdkVersion": 11, // 编译用的 SDK 版本,必须本地已安装 "compatibleSdkVersion": 11, // 最低兼容版本,低于此版本的设备装不上 "products": [ { "name": "default", "signingConfig": "default" // 签名配置,真机调试必须配 } ] } }

compileSdkVersion决定编译时用哪套 API,compatibleSdkVersion决定应用能装在哪些设备上。两个都设 11 意味着你至少需要一台 HarmonyOS 4.0 以上的设备或模拟器。如果只是想看 UI 效果,用 DevEco 自带的 Previewer 就够了,但 Previewer 对@ohos.geoLocationManager这类系统能力支持有限,定位相关的页面会显示空白。

2.2 目录结构:三端角色怎么分的

解压之后,entry/src/main/ets/下面是主要业务代码。这套源码没有用复杂的模块化拆分,而是按角色分了三个目录:

entry/src/main/ets/ ├── pages/ │ ├── patient/ # 患者端页面 │ │ ├── AppointmentPage.ets # 预约挂号 │ │ ├── OrderListPage.ets # 订单列表 │ │ └── EvaluatePage.ets # 服务评价 │ ├── companion/ # 陪诊员端页面 │ │ ├── OrderHallPage.ets # 接单大厅 │ │ ├── NavigationPage.ets # 陪诊导航 │ │ └── IncomePage.ets # 收入统计 │ └── admin/ # 管理端页面 │ ├── UserManagePage.ets # 用户管理 │ └── OrderAuditPage.ets # 订单审核 ├── model/ │ ├── OrderModel.ets # 订单数据模型 │ ├── UserModel.ets # 用户数据模型 │ └── HospitalModel.ets # 医院/科室数据模型 ├── common/ │ ├── constants.ets # 常量定义 │ ├── utils.ets # 工具函数 │ └── mockData.ets # 模拟数据 └── entryability/ └── EntryAbility.ets # 应用入口

model目录下的数据模型是理解整个业务流的钥匙。OrderModel里定义了订单状态机:待接单 → 已接单 → 服务中 → 待评价 → 已完成,每个状态对应不同的页面跳转和按钮显隐。mockData.ets里预置了十几条医院和科室数据,以及模拟的陪诊员列表,这样不连后端也能把流程走完。

提示:如果你要接真实后端,改common/utils.ets里的request方法就行,所有网络请求都走这一个出口,替换 baseUrl 和鉴权逻辑即可,不用逐个页面改。

3. 跑通主流程:从挂号到评价的完整链路

3.1 启动配置与路由跳转

鸿蒙 Stage 模型的页面路由靠router模块,这套源码在EntryAbility.ets的onWindowStageCreate里加载了首页,然后在各页面用router.pushUrl跳转。先看入口配置:

// EntryAbility.ets 关键片段 onWindowStageCreate(windowStage: window.WindowStage) { windowStage.loadContent('pages/patient/AppointmentPage', (err) => { if (err.code) { console.error('加载首页失败: ' + JSON.stringify(err)); return; } console.info('首页加载成功'); }); }

loadContent的参数是页面路径,不带.ets后缀。如果你改了首页文件位置,这里必须同步改,否则启动就是白屏。我见过有人把首页挪到pages/Index.ets但忘了改这里,排查了半天以为是 SDK 问题。

路由跳转统一用router.pushUrl({ url: 'pages/xxx' }),参数通过params字段传:

// 从订单列表跳转到评价页,带订单 ID router.pushUrl({ url: 'pages/patient/EvaluatePage', params: { orderId: this.orderId, companionName: this.companionName } });

接收端在aboutToAppear里用router.getParams()取:

aboutToAppear() { const params = router.getParams() as Record<string, string>; this.orderId = params.orderId; this.companionName = params.companionName; }

这里有个类型坑:getParams()返回的是Object,直接取属性 TS 会报错,必须断言成具体类型。源码里用的是Record<string, string>,如果你的参数里有数字或布尔值,断言成Record<string, Object>再逐个转。

3.2 订单状态流转与 mock 数据替换

整个陪诊业务的核心是订单状态机。OrderModel.ets里用枚举定义了状态:

export enum OrderStatus { PENDING = 0, // 待接单 ACCEPTED = 1, // 已接单 IN_SERVICE = 2, // 服务中 TO_EVALUATE = 3,// 待评价 COMPLETED = 4 // 已完成 }

患者端下单后状态是PENDING,陪诊员端接单大厅只展示PENDING的订单,接单后变ACCEPTED,陪诊员点击「开始服务」变IN_SERVICE,服务结束变TO_EVALUATE,患者评价后变COMPLETED。每个状态变化都对应mockData.ets里的一次数据更新。

要把 mock 换成真实接口,改utils.ets里的请求封装:

// common/utils.ets 请求封装 const BASE_URL = 'http://your-server.com/api'; // 替换成你的后端地址 export async function request<T>(url: string, method: http.RequestMethod, data?: Object): Promise<T> { const httpRequest = http.createHttp(); try { const response = await httpRequest.request(BASE_URL + url, { method: method, header: { 'Content-Type': 'application/json' }, extraData: data ? JSON.stringify(data) : undefined, connectTimeout: 10000, // 连接超时 10 秒 readTimeout: 10000 // 读取超时 10 秒 }); if (response.responseCode === 200) { return JSON.parse(response.result as string) as T; } throw new Error(`请求失败: ${response.responseCode}`); } finally { httpRequest.destroy(); // 必须销毁,否则连接泄漏 } }

connectTimeout和readTimeout单位是毫秒,默认分别是 60 秒和 60 秒,实际项目里建议设短一点,避免用户等太久。httpRequest.destroy()一定要放在finally里,鸿蒙的 http 对象不销毁会导致后续请求复用连接时出问题,这个坑我在真机上遇到过,表现是第二次请求必超时。

3.3 定位与地图:陪诊导航页的实现边界

NavigationPage.ets是陪诊员端的导航页,用了@ohos.geoLocationManager获取当前位置,然后在地图上标注医院和患者位置。源码里地图用的是MapComponent,需要你在module.json5里声明权限:

// module.json5 权限声明 "requestPermissions": [ { "name": "ohos.permission.LOCATION", "reason": "$string:location_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } }, { "name": "ohos.permission.APPROXIMATELY_LOCATION", "reason": "$string:location_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } ]

LOCATION是精确位置,APPROXIMATELY_LOCATION是模糊位置,鸿蒙要求两个都声明才能拿到定位。when字段填inuse表示仅前台使用,always是后台也允许,陪诊场景用inuse就够了,上架审核对后台定位卡得很严。

地图组件在模拟器上可能显示不出来,这是模拟器没有真实地图服务的缘故,真机调试正常。如果你只需要展示文字地址,可以把MapComponent换成Text组件,源码里已经做了条件渲染的判断,改一个布尔值就能切换。

注意:定位权限的弹窗只在第一次请求时出现,用户拒绝后不会再弹。源码里在NavigationPage的aboutToAppear里做了权限检查,如果被拒会跳转到设置页引导用户手动开启,这个逻辑在common/utils.ets的checkLocationPermission里。

4. 避坑与排查:五个让我加班到凌晨的问题

4.1 现象:Sync 成功但 Previewer 白屏

原因:Previewer 默认加载pages/Index.ets,而这份源码的首页是pages/patient/AppointmentPage.ets,入口不匹配。解决:在 Previewer 右上角的下拉框里手动选择AppointmentPage,或者临时把EntryAbility的loadContent改成pages/Index并新建一个 Index 页做重定向。

4.2 现象:真机安装报「签名验证失败」

原因:build-profile.json5里的signingConfig是空的或者用了别人的签名证书。解决:在 DevEco Studio 的File → Project Structure → Signing Configs里勾选「Automatically generate signature」,用你自己的华为开发者账号生成调试签名。没有账号的话,模拟器可以免签名运行,但真机必须签。

4.3 现象:订单列表滑动卡顿,列表项多了之后掉帧

原因:OrderListPage.ets里用的是Column+ForEach渲染订单,没有用LazyForEach。订单超过 20 条后,所有节点一次性创建,内存和渲染压力都大。解决:把ForEach换成LazyForEach,并实现IDataSource接口。源码里model/OrderModel.ets已经预留了OrderDataSource类,只是页面里没用上,改一行引入就行。

4.4 现象:评价页提交后返回列表,状态没更新

原因:router.back()返回时不会触发上一页的aboutToAppear,列表数据还是旧的。解决:在评价页提交成功后用router.back({ url: 'pages/patient/OrderListPage', params: { refresh: true } }),列表页在aboutToAppear里检查refresh参数,为 true 时重新拉数据。或者用EventHub发一个事件通知列表页刷新,源码里common/utils.ets封装了eventHub的用法,直接调就行。

4.5 现象:mock 数据里的医院列表在真机上不显示

原因:mockData.ets里的图片路径用的是本地资源$r('app.media.hospital_icon'),但resources/base/media目录下没有对应的图片文件,Previewer 会显示占位图,真机上直接空白。解决:在resources/base/media下放一张同名图片,或者把mockData里的image字段改成网络图片 URL。源码的README里提了需要自行补充图片资源,但很多人不看。

5. 进阶技巧:把 mock 数据层换成可切换的双模式

这套源码最值钱的地方不是页面写得多漂亮,而是mockData.ets和utils.ets之间的那层抽象。我一般会在这个基础上加一个开关,让项目支持「mock 模式」和「真实接口模式」一键切换,方便演示和联调。

// common/config.ets 新增配置文件 export const USE_MOCK = true; // 改成 false 就走真实接口 // common/utils.ets 里改造 request 方法 import { USE_MOCK } from './config'; import { getMockData } from './mockData'; export async function request<T>(url: string, method: http.RequestMethod, data?: Object): Promise<T> { if (USE_MOCK) { // mock 模式下直接返回模拟数据,加 300ms 延迟模拟网络 return new Promise((resolve) => { setTimeout(() => { resolve(getMockData(url, data) as T); }, 300); }); } // 真实接口逻辑保持不变 const httpRequest = http.createHttp(); // ... 省略 }

getMockData根据 url 路径匹配返回对应的模拟数据,这样切换开关不用改任何页面代码。演示的时候开 mock,联调的时候关 mock,两不误。

另一个实用技巧是给订单状态机加一个「后悔药」——在管理端页面加一个重置按钮,把指定订单的状态强制改回PENDING,方便反复测试接单流程。源码的admin/OrderAuditPage.ets里已经有订单列表,加一个Button调OrderModel.updateStatus(orderId, OrderStatus.PENDING)就行。这个功能在演示时特别有用,不用每次重新下单。

从那以后我每次拆鸿蒙项目,都先把build-profile.json5、module.json5和mockData.ets这三个文件过一遍,确认版本、权限和数据源,再动手跑。这三步走完,90% 的「跑不起来」问题都能提前排掉。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进