# HO2409 **Repository Path**: xinyuefei/ho2409 ## Basic Information - **Project Name**: HO2409 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-05-17 - **Last Updated**: 2025-06-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 鸿蒙系统 - iOS苹果、Android安卓、HarmonyOS鸿蒙 - HarmonyOS 3.1/4.0版本对应的API能力级别为 API 9 Release - HarmonyOS 5.0.0 Release该版本API能力级别为API 12 Release ## 将要学的 - 养老护工端 - Bundle(App包) - 多Module(模块)- 一个AbilityStage(组件容器) - 多UIAbility(应用组件) - 一个WindowStage(窗口) - 多页面 - 多组件 - Context / Want - rcp请求 - ArkWeb / webview - Emitter / EventHub事件通信 - lazyForEach - ⼀次开发,多端部署 / ⾃适应布局 ## 模块概念的简化版本 - Module模块,两种大类型 - Ability类型(实现应用功能),两种子类型 - entry类型(设备入口,包含图标) - feature类型(特性) - Library类型(复用、需要被引用/调用),两种子类型 - 静态 - 动态 ## Module模块的配置文件 - src/main/module.json5 - name模块名称、type模块类型、mainElement默认的UIAbility名称、deviceTypes模块支持的设备类型、pages是router路由配置、routerMap是Navigation路由配置、requestPermissions权限、abilities是UIAbility ## 打开一个UIAbility - 在页面/组件中打开一个UIAbility ``` import { common } from '@kit.AbilityKit' // 获取组件所处UIAbility的上下文对象 const context = getContext() as common.UIAbilityContext // 调用context对象上的startAbility方法,启动UIAbility时,参数是Want对象 context.startAbility({ deviceId: '', // 设备,留空 bundleName: 'com.example.ho2409', // 应用名称 moduleName: 'entry', // 模块名称 abilityName: 'EntryAbility1' // UIAbility parameters: { info: '传参1' } }) ``` - 被打开的UIAbility中,在onCreate生命周期中获取参数Want对象 - singleton单实例模式,复⽤/热启动时⾛onNewWant回调,再次打开单实例模式的UIAbility时,Want参数在onNewWant中获取 ## 关闭一个UIAbility - 调用UIAbilityContext上的terminateSelf方法 - 调⽤terminateSelf⽅法停⽌当前UIAbility实例时,在最近任务列表中仍然能查看到该实例对应的任务,如不需要保留快照,可在module.json5配置⽂件中将abilities标签的removeMissionAfterTerminate字段配置为true ## 启动应⽤内的UIAbility并获取返回结果 - 用startAbilityForResult打开并获取返回结果,通过.then回调获取信息 ``` const context = getContext() as common.UIAbilityContext context.startAbilityForResult({ deviceId: '', bundleName: 'com.example.ho2409', moduleName: 'feature', abilityName: 'FeatureAbility' }).then(res => { console.log('获取到的信息', JSON.stringify(res)) }) ``` - 用terminateSelfWithResult关闭并携带信息返回 ``` const context = getContext() as common.UIAbilityContext // 返回信息时有格式要求 const result: common.AbilityResult = { resultCode: 0, want: { parameters: { info: '返回的信息' } } } context.terminateSelfWithResult(result) ``` ## AbilityStage - AbilityStage是⼀个Module模块级别的组件容器,模块在⾸次加载时会创建⼀个AbilityStage实例,可以对该Module进⾏初始化等操作 - AbilityStage与Module⼀⼀对应,即⼀个Module拥有⼀个AbilityStage - AbilityStage拥有onCreate⽣命周期回调和onAcceptWant等事件回调 - DevEco Studio默认项目中未⾃动⽣成AbilityStage,需要在module.json5中通过配置module的srcEntry参数来指定模块对应的代码路径(如果需要,请手动创建) - 启动UIAbility时,如果使用指定实例模式,需要给这个UIAbility所属的模块创建AbilityStage并使用 ## UIAbility的启动模式 - 在module.json5配置⽂件中设置abilities标签的launchType字段 - singleton单实例模式(默认值),调⽤startAbility⽅法时,会复⽤应⽤进程中已经存在的UIAbility实例,复⽤时⾛onNewWant回调 - multiton多实例模式,每次调⽤startAbility⽅法,都会在应⽤进程中创建⼀个新UIAbility实例,在最近任务列表中可以看到有多个该类型的UIAbility实例 - specified指定实例模式(自己指定/设置打开模式是单实例还是多实例),针对⼀些特殊场景使⽤(例如⽂档应⽤中每次新建⽂档希望都能新建⼀个⽂档实例,重复打开⼀个已保存的⽂档希望打开的都是同⼀个⽂档实例) ## 指定实例模式(难) - 指定实例模式的UIAbility启动前,会进⼊对应模块Module的AbilityStage的onAcceptWant⽣命周期回调,使⽤onAcceptWant回调返回的字符串Key进⾏实例匹配,如果存在匹配项,则会启动与之绑定的UIAbility实例,并进⼊该UIAbility实例的onNewWant回调;否则会创建⼀个新的UIAbility实例 ## Context和Want - Context上下文对象,有基类Context、ApplicationContext、AbilityStageContext、 UIAbilityContext等 - ApplicationContext:在AbilityStageContext或者UIAbilityContext上调用,`this.context.getApplicationContext()` - AbilityStageContext:在AbilityStage中通过this.context获取 - UIAbilityContext:在UIAbility中通过this.context获取,在组件中用getContext()获取 - Want是UIAbility之间传递信息的载体 ## 养老院护工端项目 - 护工测试账号/密码hulilaozhao,手机号13812345678 - 接口文档:https://app.apifox.com/project/4445300 - 请求域名前缀:http://123.57.237.81:8080/caresystem/api - 已上传文件的域名前缀:http://123.57.237.81:8080/caresystem/ - 登录页 / 账号 / 权限 - 在App启动时根据token是否存在判断,已登录就进首页,未登录进登录页 - Tab切换:首页、任务、消息(无)、我的 - 首页:通知、banner轮播图、6项服务、当前登录护工负责的老人 - 夜巡管理:地址(扫码获取) - 任务交接:功能和任务tab页一样 - 错衣登记:类似于失物招领 - 外出登记:外出登记表单(老人选择,日期范围)、返回状态、返回登记 - 医生查房:批量操作(老人多选、批量提交)、基本情况和生命体征多项状态切换 - 护士测量:血压/血糖/体温切换、批量操作 - 任务:护工帮老人做的事情、完成状态(勾选切换)、某个老人的任务列表 - 我的:展示用户信息 ## 登录 - 请求头携带token:authorization - 安装加密三方库:`ohpm i @yyz116/sm-crypto` - 国密sm2非对称加密对密码字段进行加密 ## 任务相关接口 - 首页“服务老人”和任务Tab页:/elderlyTaskList/list(老人ID/elderlyId传参) - 列表跳转到老人任务列表页:/elderlyTaskList/taskForElderlyId(老人ID作参数) - 批量修改一个老人要做的多条任务的状态:/elderlyTaskList/updateState(任务ID数组参数) - 老人相关字段:elderlyId、elderlyName、elderlyPhoto - 住宿相关字段:buildingName楼栋、houseName房间、begName床位 ## 错衣缺衣相关接口 - 添加:/clothes/add,elderlyId老人ID ## 外出登记 - 列表:/goOut/list,状态筛选state(不传就是全部、0未返回、1已返回、2逾期未返回) - 详情:/goOut/get/1314(参数是路径一部分),goBack是返回信息(为空表示未返回) - 登记:/goOut/add,表单(老人选择,日期范围) - 返回:/goBack/add,返回状态、返回登记(日期时间选择) ## 医生查房 - 批量列表,分组列表:/checkRoom/listGroup,按房间批量添加,护工和添加时间;在列表中点击一个条目时,打开详情查看页(借用添加的页面样式) - 查房记录:/checkRoom/list,根据批量列表条目中的添加时间和护工ID来在查房记录中筛选,获取到多个老人记录详情 - 查房登记,批量添加:/checkRoom/addList,按房间批量添加(一次给多个老人同时添加),登录接口会返回护工/医生信息;选择老人复用之前老人列表UIAbility;每个老人需要有的字段(老人ID、护工ID、添加时间、指标正常/异常) ## Navigation组件导航 - Navigation组件包含​导航页(NavBar,就是Navigation默认的子组件)和子页(根标签NavDestination,要跳转的别的页面) - Navigation的参数是NavPathStack的路由栈实例对象,NavPathStack对象用来控制跳转 - 两种方式定义/配置路由(名称name和子页面组件的对应关系) 1. Navigation的属性navDestination传入@Builder构建函数,用来设置名称name和页面组件的对应关系 ``` @Builder function pageMap(name: string) { if (name === 'NurseMeasureList') NurseMeasureList() } ``` 2. 系统路由表,页面声明在entry/src/main/resources/base/profile/route_map.json文件中,在module.json5中配置`{ "routerMap": "$profile:route_map" }`,JSON文件中name子页跳转使用名称、pageSourceFile子页文件路径、buildFunction子页中的构建函数(使用系统路由表时,每个子页面中需要定义一个builder) ``` { "routerMap": [{ "name": "NurseMeasureList", "pageSourceFile": "src/main/ets/pages/NurseMeasureList.ets", "buildFunction": "nurseMeasureListBuilder" }] } ``` ## ohpm - 鸿蒙三方库的包管理工具 - 下载Command Line Tools For HarmonyOS,解压,将其bin目录设置在电脑的环境变量中 - https://ohpm.openharmony.cn/ ## 页面和组件生命周期 - @Entry装饰的页面 - onPageShow ⻚⾯每次显示时触发⼀次,应⽤进⼊前台 - onPageHide ⻚⾯每次隐藏时触发⼀次,应⽤进⼊后台 - onBackPress ⽤户点击返回按钮时触发 - @Component或者@CustomDialog装饰的自定义组件 - aboutToAppear 组件即将出现时回调该接⼝,在执⾏build函数之前执⾏(调用接口) - onDidBuild 组件build函数执⾏完成之后回调该接⼝ - aboutToDisappear 在⾃定义组件销毁之前执⾏ ## UIAbility和WindowStage生命周期 - UIAbility是包含UI的应⽤组件,是系统调度的基本单元,为应⽤提供绘制界⾯的窗⼝,⼀个应⽤可以包含⼀个或多个UIAbility组件 - 每个UIAbility实例都会与⼀个WindowStage类实例绑定,该类⽤来管理进程内的窗⼝,它包含⼀个主窗⼝,为ArkUI提供了绘制区域 - UIAbility的⽣命周期包括Create、Foreground、Background、Destroy四个状态 - UIAbility实例对应的WindowStage的状态包含WindowStageCreate和WindowStageDestroy等 ## 应⽤多Module设计机制 - 给每个Module标注所⽀持的设备类型,在应⽤市场分发应⽤包时,能够根据设备类型做精准的筛选和匹配,从⽽将不同的包合理地组合和部署到对应的设备上 - ⼀个应⽤通常会包含多种功能,可以将每个功能模块作为⼀个独⽴的Module进⾏开发,Module中包含源代码、资源⽂件、第三⽅库、配置⽂件等,每个Module可以独⽴编译,实现特定的功能 ## Module模块有两种类型 - Ability类型(创建模块时选择第一个Empty Ability),⽤于实现应⽤的功能和特性,每个Ability类型的Module编译后会⽣成⼀个以.hap为后缀的⽂件,HAP包可以独⽴安装和运⾏,是应⽤安装的基本单位,⼀个应⽤中可以包含⼀个或多个HAP包,Ability类型的模块有两种子分类(创建模块时第二步选择Module Type) - entry类型的Module是应⽤的主模块("type": "entry"),包含应⽤的⼊⼝界⾯、⼊⼝图标和主功能特性,每⼀个应⽤分发到同⼀类型的设备上的应⽤程序包,只能包含唯⼀⼀个entry类型的HAP - feature类型的Module是应⽤的动态特性模块("type": "feature"),⼀个应⽤中可以包含⼀个或多个 feature类型的HAP,也可以不包含 - Library类型(创建模块时选择第三个或第四个),⽤于实现代码和资源的共享,同⼀个Library类型的Module可以被其他的Module多次引⽤ - Static Library静态共享库("type": "har"),编译后会⽣成⼀个以.har为后缀的⽂件,即静态共享包HAR - Shared Library动态共享库("type": "shared"),编译后会⽣成⼀个以.hsp为后缀的⽂件,即动态共享包HSP ## Static Library静态共享库 - 实现多个模块或多个⼯程共享ArkUI组件、资源等相关代码,⽀持发布到OHPM私仓或者中⼼仓,供其他应⽤使⽤ - 不⽀持在设备上单独安装/运⾏,只能作为应⽤模块的依赖项被引⽤ - 不⽀持在配置⽂件中声明UIAbility组件与ExtensionAbility组件 - 不⽀持在配置⽂件中声明pages⻚⾯,但是可以包含pages⻚⾯,并通过命名路由的⽅式进⾏跳转 - 多包(HAP/HSP)引⽤相同的HAR时,会造成多包间代码和资源的重复拷⻉,从⽽导致应⽤包膨⼤ ## Shared Library动态共享库 - 多个HAP/HSP共⽤的代码和资源放在同⼀个HSP中,可以提⾼可重⽤性和可维护性,编译打包时也只保留⼀份HSP代码和资源,能够有效控制应⽤包⼤⼩ - HSP不⽀持独⽴发布,⽽是跟随其宿主应⽤的APP包⼀起发布,与宿主应⽤同进程,具有相同的包名和⽣命周期 - 不⽀持在设备上单独安装/运⾏,需要与依赖该HSP的HAP⼀起安装/运⾏ - 不⽀持在配置⽂件中声明UIAbility组件与ExtensionAbility组件 - ⽀持在配置⽂件中声明pages⻚⾯,通过@bundle:包名(bundleName)/模块名(moduleName)/路径/⻚⾯所在的⽂件名(不加.ets后缀)的url进⾏跳转 - 多包(HAP/HSP)引⽤同⼀个共享包时,采⽤HSP替代HAR,可以避免HAR造成的多包间代码和资源的重复拷⻉,从⽽减⼩应⽤包⼤⼩ ## 静态共享库和动态共享库对比、简化版 - 都不支持单独安装/运⾏;都不⽀持声明UIAbility - pages页面:静态库不⽀持声明(但是可以包含页面文件);动态库⽀持声明pages⻚⾯ - 多包引用:静态库是重复拷⻉;动态库是引⽤同⼀个共享 - OHPM私仓或者中⼼仓:静态库⽀持发布;动态库不支持发布 ## 引⽤本地共享库模块代码 - 在一个Ability类型的模块的oh-package.json5中配置 ``` "dependencies": { "static_library": "file:../static_library", "shared_library": "file:../shared_library" } ``` - 然后在命令⾏中cd到项目根路径,执⾏ohpm install ## 访问/打开共享库中的pages页面 - 静态和动态共享库中的页面都可以通过命名路由的方式打开 ``` // 在共享库中,定义页面时给页面/路由命名 @Entry({ routeName: 'MyPage' }) @Component struct Page1 { build() { Column() { Text('静态/动态共享库中的页面') } } } // 在Ability模块中,导入页面,然后跳转 import { router } from '@kit.ArkUI' import 'shared_library/src/main/ets/pages/Page' router.pushNamedRoute({ name: 'MyPage' }) ``` - 动态共享库中的页面,可以通过特殊格式的路径进行跳转 ``` router.pushUrl({ url: `@bundle:${bundleName应用名称}/${moduleName模块名称}/ets/pages/${pageName页面文件名不包含文件后缀}` }) ``` ## 下拉刷新和滚动加载 - 下拉刷新:取最新的数据,新闻、邮件,页码设置为1调用列表接口将数组完全替换 - 滚动/触底加载更多:翻页,页面加1调用列表接口将获取到的数据追加到数组的后面 ## ArkWeb,在鸿蒙应⽤程序中嵌⼊Web⻚⾯ - 使用Web组件,结合WebviewController控制器对象 ``` import { webview } from '@kit.ArkWeb' wc = new webview.WebviewController() Web({ src: $rawfile('web/1.html'), controller: this.wc, renderMode: RenderMode.SYNC_RENDER // 同步渲染 }).layoutMode(WebLayoutMode.FIT_CONTENT) // 让Web组件的尺寸适配他嵌入的网页的尺寸 ``` - 在应用侧调用网页中全局方法,使用WebviewController控制器对象上的runJavaScript来调用,参数是字符串 ``` list: number[] = [1,2,3] this.wc.runJavaScript(`fn(${JSON.stringify(this.list)})`) ``` - 在网页中调用应用侧方法,使用Web组件的javaScriptProxy给网页全局挂载/注入对象 ``` // 鸿蒙/应用侧 myObject: ObjectType = { clear: () => { console.log('clear执行了') }, confirm: (val: string) => { console.log('confirm执行了', val) } } Web(...).javaScriptProxy({ name: 'obj', // 在网页window对象中挂的对象名称 object: this.myObject, // 在网页window对象中挂的对象的值 / 传给网页的对象 methodList: ['clear', 'confirm'], // 对象中暴露给网页的属性方法 controller: this.wc }) // 网页/web侧 obj.clear() obj.confirm('参数') ``` ## LazyForEach数据懒加载 - 超大数据列表渲染,页面会卡顿 - 虚拟化表格、虚拟列表,框架会根据滚动容器可视区域按需创建组件/DOM,当组件/DOM滑/滚动出可视区域外时,框架会销毁并回收组件/DOM以降低内存占用 - List、Grid、Swiper、WaterFlow组件中可以使用 - 给LazyForEach提供的数据源,是实现了IDataSource接口的类的实例对象 ## 单位 - 某台手机2796x1290物理像素分辨率,网页像素932x430,用3个物理像素点表示1个网页像素 - vp默认单位,不写单位就相当于vp,屏幕密度相关像素,类似于网页像素px - px像素单位,屏幕物理像素单位 - lpx响应式单位,默认将屏幕宽分成720份,类似于rpx,在entry/src/main/resources/base/profile/main_pages.json中的window属性的designWidth设置设计稿宽度 - '50%' 百分比 - 系统内置了单位转换方法:px2vp、vp2px等 ## ⼀次开发,多端部署 / 一多 - 编写一套代码,让应用工作在多个不同设备中 - 1.自适应布局,当容器/屏幕⼤⼩发⽣变化时,元素可以根据相对关系⾃动变化以适应外部容器变化。相对关系如占⽐、固定宽⾼⽐、显示优先级等,实现界⾯显示随容器/屏幕⼤⼩**连续**变化 - 2.响应式布局,当容器/屏幕⼤⼩发⽣变化时,元素可以根据断点、栅格或特定的特征(如屏幕⽅向、窗⼝宽⾼等)⾃动变化以适应外部容器变化,实现界⾯随外部容器/屏幕⼤⼩有**不连续(版式/排版)**变化 - 断点,以应⽤窗⼝宽度为切⼊点,将应⽤窗⼝在宽度维度上分成了⼏个 不同的区间,超窄屏/手表xs[0, 320),窄屏/手机sm[320, 600),中屏/平板md[600, 840),宽屏/PC电脑lg[840, +∞),可以根据实际需要在lg断点后⾯新增xl、xxl等断点 - GridRow和GridCol配合使用实现响应式布局,GridRow参数columns设置栅格数量,GridCol参数span设置占用栅格数量、order设置排列顺序 ## 字体图标 - 支持调整颜色、大小,只一次请求字体包 - 图标源文件格式要求为SVG,需要UI设计师提供 ``` import { font } from '@kit.ArkUI' font.registerFont({ familyName: 'myFont', familySrc: $rawfile('fonts/iconfont.ttf') }) Text('\ue631').fontFamily('myFont') ``` ## 拖拽/拖放/drag drop - 拖动的元素.draggable(true).onDragStart(...).onDragEnd(...),必须有draggable 和onDragStart - 放置的元素.onDragEnter(...).onDragMove(...).onDragLeave(...).onDrop(...),必须有onDrop ## 非鸿蒙知识 ### fetch - 一种原生的请求,和XMLHttpRequest类似 ### 约束promise成功回调函数的参数类型 - 使用泛型参数Promise ``` function p(): Promise { return new Promise(resolve => { resolve(1); }); } p().then(res => console.log(res)); ``` ### 几个TS类型 - Function表示任意函数 - Object表示除去null、undefined外的所有类型 - object表示复杂/复合类型 - 任意对象:`Record` ### 对象的遍历 - for/in、Object.keys/values/entries ### 同步/异步 - async异步、sync同步 - **Promise实例的.then方法返回的还是个Promise实例,前一个.then回调的返回值会作为下一个.then的回调参数** - **async函数执行后返回的还是个Promise实例,async函数的返回值会作为其.then的回调参数** ### 数组方法 - splice - indexOf、includes - filter、map ### ES6文档 - https://es6.ruanyifeng.com/#docs/intro ### Echarts 1. 导入/引入echarts对象 2. 准备一个有尺寸的容器 3. 调用echarts.init方法进行初始化,参数是DOM,返回图表实例对象 4. 调用图表实例对象上的setOption方法画图,参数是图表的配置项 5. 窗口缩放时,调用图表实例对象上的resize方法重新画图 ### 可选链运算符 .? ### 空值合并运算符 ?? ### Grid布局 - 网格 - 父容器grid-template-columns、grid-template-rows