HarmonyOS ArkTS API 24+ 实战:搭建调机记录编辑表单

📅 发布时间:2026/8/1 10:08:13
HarmonyOS ArkTS API 24+ 实战:搭建调机记录编辑表单 前言表单的难点在数据回路不在输入框数量上一篇 已经把DebugRecord的字段职责确定下来。现在要把这些字段放进一个可以操作的页面用户进入“新增调机记录”时看到默认参数打开已有记录时看到原值点击“暂存草稿”可以保存未完成内容点击“提交班组复核”则要经过更严格的校验。如果只把多个TextInput摆在页面上视觉上可能已经像一个表单但数据回路仍然可能断开。输入框的字符串要进入页面状态页面状态要转换为业务输入对象保存时要生成DebugRecordRepository 保存后还要反馈状态变化。任何一环遗漏页面就会出现“输入看得见但列表没有变化”的问题。本篇围绕真实的DebugRecordForm.ets讲清楚这条链路重点放在新增和编辑共用一套表单、ArkUI 状态与输入值同步以及保存动作如何区分草稿和提交。一、表单文件和入口关系本篇涉及entry/src/main/ets/pages/Index.ets entry/src/main/ets/features/debug/DebugRecordList.ets entry/src/main/ets/features/debug/DebugRecordForm.ets entry/src/main/ets/models/DebugRecord.ets entry/src/main/ets/repositories/DemoBusinessRepository.ets entry/src/main/ets/utils/Validation.etsIndex.ets决定什么时候显示表单DebugRecordList.ets决定传入新建标识还是已有记录 IDDebugRecordForm.ets管理页面输入和按钮事件DebugRecord.ets表示最终保存的对象Validation.ets负责提交前检查。表单不应该自己查询当前底部页签也不应该自己修改顶级detailKind。它接收recordId和onBack完成“读取、编辑、保存、反馈、返回”这一组局部任务。二、用 recordId 区分新增和编辑组件入口是Componentexportstruct DebugRecordForm{recordId:stringnew;onBack:()void(){};recordId默认值为new表示用户从“新增记录”入口进入。编辑已有记录时入口页传入DBG-241等真实演示 ID。组件不需要再增加一个isEdit布尔值因为 ID 本身已经表达了对象身份。入口页的调用方式如下}elseif(this.detailKinddebug){DebugRecordForm({recordId:this.detailId,onBack:()this.closeDetail()}).layoutWeight(1);}列表页则分别处理两种点击DebugRecordList({onOpenRecord:(recordId:string)this.openDetail(debug,recordId),onCreate:()this.openDetail(debug,new)})新建和编辑共用一个表单组件可以避免两套页面在字段、样式和保存规则上的漂移。差异集中在“是否能通过 ID 查询到已有记录”这一点上。三、页面状态使用字符串承接 TextInput表单字段声明为State字符串StateholdingPressure:string54;StatebackPressure:string9;StatebarrelZone3:string210;StatesampleCount:string;StateobservationMinutes:string;StatesampleResult:string;StateerrorText:string;StatesavedText:string;这里故意让输入层使用字符串。TextInput的onChange回调拿到的是文本值用户可能正在输入空字符串、部分数字或还没有完成编辑。先把输入原样保存在页面状态中能够避免每次键入都强制构造完整的业务对象。真正保存时再由input()把字符串转换成DebugRecordInput。这样“输入状态”和“业务对象”边界清楚输入框负责可编辑文本校验函数负责判断转换后的值是否能提交模型负责保存后的对象结构。四、aboutToAppear 负责编辑回填编辑已有记录时组件出现后执行aboutToAppear():void{constrecord:DebugRecord|undefineddemoBusinessRepository.debugById(this.recordId);if(record!undefined){this.holdingPressurerecord.holdingPressure.toString();this.backPressurerecord.backPressure.toString();this.barrelZone3record.barrelZone3.toString();this.sampleCountrecord.sampleCount.toString();this.observationMinutesrecord.observationMinutes.toString();this.sampleResultrecord.sampleResult;}}aboutToAppear()是组件即将显示时执行的生命周期入口。它在这里承担一次性的编辑回填通过recordId查询记录把数值转回字符串再交给TextInput显示。新建时recordId是newRepository 查不到对象默认参数保持为保压 54、背压 9、料筒三区温度 210试样数量、观察时长和结论则留空。这个默认值来自当前演示表单不应写成现场工艺标准。如果编辑页面没有回填先确认recordId是否传入了记录 ID再确认debugById()查询的是DebugRecord.id而不是exceptionId、machineId或列表显示文本。最后检查数值字段是否调用了toString()因为TextInput需要字符串输入。五、把输入转换成 DebugRecordInput表单通过一个小方法集中读取当前状态privateinput():DebugRecordInput{constinput:DebugRecordInputnewDebugRecordInput();input.holdingPressureNumber(this.holdingPressure);input.backPressureNumber(this.backPressure);input.barrelZone3Number(this.barrelZone3);input.sampleCountNumber(this.sampleCount);input.observationMinutesNumber(this.observationMinutes);input.sampleResultthis.sampleResult;returninput;}把转换集中在input()中有两个好处。第一保存逻辑不会在多个地方重复Number()第二校验函数收到的是结构化输入对象而不是一组页面状态字段。需要注意Number()的结果是0。这正好让空的试样数量和观察时长进入“必须大于 0”的校验分支但也说明Number()不是完整的输入验证。输入abc会得到NaN生产代码还需要增加有限数判断和更明确的提示。六、baseline 用于判断参数是否真正改变提交前需要知道当前输入是否与基线一致privatebaseline():DebugRecordInput{constbaseline:DebugRecordInputnewDebugRecordInput();constrecord:DebugRecord|undefineddemoBusinessRepository.debugById(this.recordId);baseline.holdingPressurerecordundefined?54:record.holdingPressure;baseline.backPressurerecordundefined?9:record.backPressure;baseline.barrelZone3recordundefined?210:record.barrelZone3;returnbaseline;}新建记录使用页面默认参数作为基线编辑记录使用原记录中的三个工艺参数作为基线。这样校验函数可以判断用户是否至少改变了一项参数而不是只判断输入框有没有内容。这个判断很重要。调机记录的目的不是重复提交原参数而是记录一次真实调整。如果所有工艺参数都和基线相同即使试样数量和观察结果已经填写也应该先提示用户确认是否真的发生了调机变化。当前baseline()只比较保压、背压和料筒三区温度。它没有把试样数量和试样结论纳入“参数是否变化”的比较因为那些字段是调整后的验证结果不是调机参数本身。七、表单布局如何与状态字段对应当前表单依次展示参数、试样数量、观察时长和结论Text(调整后保压MPa)TextInput({text:this.holdingPressure,placeholder:例如 54})Text(调整后背压MPa)TextInput({text:this.backPressure,placeholder:例如 9})Text(料筒三区温度℃)TextInput({text:this.barrelZone3,placeholder:例如 210})Text(试样数量)TextInput({text:this.sampleCount,placeholder:大于 0})Text(观察时长分钟)TextInput({text:this.observationMinutes,placeholder:大于 0})Text(试样结论)TextInput({text:this.sampleResult,placeholder:填写连续试模结果})前三项是调整后的工艺参数后面三项是验证结果。页面顺序先放参数再放试样和观察结论是为了让填写顺序符合现场操作先记录改了什么再记录结果如何。每个输入控件都要回写对应的StateTextInput({text:this.holdingPressure,placeholder:例如 54}).type(InputType.Number).onChange((value:string)this.holdingPressurevalue)如果漏掉onChange初始文本仍然会显示但用户输入不会进入保存链路。排查时可以先观察页面状态是否变化再检查input()是否读取了同一个字段。八、暂存草稿和提交复核使用两个按钮页面底部提供两个动作Row({space:10}){Button(暂存草稿).onClick(()this.save(false))Button(提交班组复核).onClick(()this.save(true))}save(false)表示暂存save(true)表示提交。动作参数把两者的差异显式传入统一保存流程两者都要组装记录并调用 Repository但只有提交动作需要阻止不完整输入。这也是为什么代码没有在输入框失焦时就强制报错。草稿允许用户先保存部分内容等现场观察完成后再回来补齐提交复核才是流程门禁点。九、save 方法的完整数据流保存方法首先调用校验函数privatesave(submit:boolean):void{constvalidation:stringvalidateDebugRecord(this.input(),this.baseline());if(submitvalidation.length0){this.errorTextvalidation;this.savedText;return;}如果是提交且校验返回错误页面只更新错误提示不创建新对象也不调用 Repository。这样失败输入不会污染记录列表。通过校验或执行草稿保存后方法根据recordId确定保存对象的 IDconstid:stringthis.recordIdnew?DBG-NEW-001:this.recordId;constprevious:DebugRecord|undefineddemoBusinessRepository.debugById(id);当前演示实现用固定的DBG-NEW-001作为新建示例 ID方便重复演示和编辑回填。生产实现应由服务端、数据库或统一 ID 生成器保证唯一性不能直接照搬固定编号。接着创建新的DebugRecordconstrecord:DebugRecordnewDebugRecord(id,previousundefined?EX-241:previous.exceptionId,machine-001,product-c08,20260719-A07,this.input().holdingPressure,this.input().backPressure,this.input().barrelZone3,this.input().sampleCount,this.input().observationMinutes,this.input().sampleResult,submit?submitted:draft,2026-07-23 17:00);这段构造代码把页面输入和业务关联组合成完整对象。当前演示表单固定关联EX-241、machine-001、product-c08和20260719-A07这是为了把 M3 表单串到已有脱敏业务链路中不代表表单已经支持任意机台、产品和批次选择。最后交给 RepositorydemoBusinessRepository.saveDebugRecord(record);this.errorText;this.savedTextsubmit?已提交班组复核异常状态已更新为待复核。:已暂存草稿。;Repository 会按 ID 更新已有记录或追加新记录当状态是submitted时还会把关联异常推进到verifying。页面本身只显示反馈不直接修改异常对象这能避免状态更新逻辑散落在表单中。十、状态与 UI 的变化顺序一次成功提交可以按下面的顺序复述用户修改输入框onChange更新对应的State字符串。用户点击“提交班组复核”。input()将字符串转换为DebugRecordInput。baseline()读取原始参数或新建默认基线。validateDebugRecord()返回空字符串表示允许提交。save()创建状态为submitted的DebugRecord。Repository 更新记录并将关联异常状态推进到verifying。表单清空错误提示显示提交成功反馈。如果失败链路会在第 5 步停止记录和异常都不会变化。排查时可以把问题定位到“输入没有回写”“转换结果不对”“基线不对”“校验阻断”或“Repository 保存”中的一个环节。十一、验证和排错当前项目的可复核路径包括打开“调机”页签进入新增表单保持默认参数直接提交预期会被“请至少调整一项工艺参数后再提交”阻止调整参数后如果试样数量为空预期会看到试样数量校验填写完整后提交列表中应出现待复核记录。编辑DBG-241时表单应该回填保压 54、背压 9 和料筒三区温度 210。若回填后直接提交应该先提示参数没有改变修改任意一个参数并补齐试样信息后才进入 Repository 保存。如果点击“暂存草稿”后页面显示成功但返回列表没有记录检查save(false)是否调用了saveDebugRecord()以及列表是否每次通过loadDebugRecords()读取同一个 Repository 实例。当前页面和入口使用的是导出的单例demoBusinessRepository。当前运行事实是 API 26 Beta SDK 编译并在 API 24 模拟器上观察到 M3 表单路径。此结论用于说明本工程的测试环境不等于 API 24 SDK 编译也不覆盖所有设备和输入法场景。十二、常见误区1. 直接把 TextInput 的字符串存进 number 字段这样会破坏模型的字段类型。输入层使用字符串是为了支持编辑过程保存前必须完成转换。2. 草稿和提交都使用同样的强校验这会导致用户无法保存未完成现场记录。当前实现只在submit true时阻止校验错误草稿仍然可以保存。3. 表单直接修改异常状态异常状态更新应由 Repository 的保存方法统一处理。表单只提交记录避免多个页面各自推进业务状态。4. 把固定演示 ID 当成生产方案DBG-NEW-001、固定机台和固定批次只是演示链路。接入真实数据后必须改为由业务上下文和唯一 ID 生成策略提供。十三、总结本篇完成了调机表单的核心回路通过recordId复用新增和编辑页面。用State字符串承接输入过程。通过input()转换为结构化业务输入。通过baseline()判断参数是否真正发生变化。将暂存草稿和提交复核拆成两个用户动作。由 Repository 统一保存记录并推进关联异常状态。下一篇继续处理Validation.ets把参数改变、试样数量、观察时长和试样结论的提交门禁拆开验证让错误反馈与现场填写顺序一致。附录工程配置与版本说明为了便于复现本文中的代码片段和运行现象这里把当前文章系列对应的工程基线单独列出。本文所说的“当前工程”指e_notebook项目的 HarmonyOS ArkTS 客户端应用名称为“注塑工程师助手”主要用于脱敏演示机台档案、产品档案、调机记录、参数模板、异常闭环、生产批次和看板报表等业务路径。1. 应用与模块配置应用包名com.atan.enotebook。应用版本versionName为1.0.0versionCode为1000000。工程模型ArkTS / ArkUI Stage 模型。主模块entry模块类型为entry。入口 AbilityEntryAbility入口文件为entry/src/main/ets/entryability/EntryAbility.ets。主页面配置模块通过pages: $profile:main_pages读取页面列表。设备类型当前模块声明支持phone、tablet和2in1。安装方式deliveryWithInstall为trueinstallationFree为false属于随应用安装的普通 entry 模块。2. SDK 与 API 版本DevEco Studio 版本DevEco Studio Beta26.0.0.461。编译 SDKHarmonyOS SDK API 26 Beta1SDK 包版本为26.0.0.23。SDK 平台信息apiVersion为26platformVersion为26.0.0releaseType/stage为Beta1。targetSdkVersion26.0.0。compatibleSdkVersion6.1.1(24)。API 口径说明文章系列以 API 24 作为兼容目标进行表述当前工程实际由 API 26 Beta SDK 编译并在 API 24 模拟器上做过安装、启动和交互观察。因此文中的“API 24 运行观察”表示兼容目标环境下的模拟器验证结果不等同于使用 API 24 SDK 重新完成编译验证。3. 构建与运行工具开发工具 IDEDevEco Studio Beta安装目录指向D:/Program Files/Huawei/DevEco Studio Beta。SDK 路径D:/Program Files/Huawei/DevEco Studio Beta/sdk。构建系统Hvigor工程入口hvigorfile.ts使用ohos/hvigor-ohos-plugin的appTasks。Hvigor 执行配置开启 daemon、incremental、parallel 和 typeCheck日志级别为info。构建脚本本地build.ps1优先使用 DevEco Studio 自带的 JBR、Node.js、SDK 与 Hvigor避免系统环境变量中的 Java 或 Node.js 版本干扰构建结果。调试产物未配置签名时本地构建生成entry/build/default/outputs/default/entry-default-unsigned.hap。这类 unsigned HAP 只用于本地调试和模拟器验证正式发布前需要在 DevEco Studio 中补充签名配置。4. 本系列文章的验证边界本系列代码以脱敏演示数据为主Repository、Store、页面状态和组件边界都围绕本地演示闭环展开。已观察过的运行现象以文中对应截图、布局树和人工核对记录为准没有重新核对的页面不在单篇文章中扩大为完整结论。如果读者使用更新的 DevEco Studio、HarmonyOS SDK 或真机系统版本复现API 差异、控件行为和签名流程可能会发生变化。遇到差异时建议优先核对build-profile.json5、module.json5、SDK Manager 中安装的 API 版本以及当前设备或模拟器的系统 API 等级。附录 2项目目录结构与设计意图下面这份目录说明对应当前 DevEco Studio 中打开的harmonyos-app工程。截图里能看到的目录并不只是文件摆放习惯它反映了一个 ArkTS Stage 工程的分层方式应用级配置、业务模块、页面源码、资源文件、构建配置和过程归档分别放在不同位置方便后续排查问题时先判断“问题属于配置、页面、数据、状态、资源还是构建产物”。harmonyos-app/ ├── AppScope/ # 应用级配置与全局资源入口 │ ├── app.json5 # bundleName、版本号、图标、应用标签等应用级元信息 │ └── resources/ # 应用级图标、字符串和基础资源 ├── entry/ # 主业务模块当前 App 的主要页面和业务代码都在这里 │ ├── src/main/ets/ # ArkTS 源码根目录 │ │ ├── components/ # 可复用 ArkUI 组件如底部导航、数据状态面板 │ │ ├── entryability/ # Stage 模型入口 Ability负责应用启动入口 │ │ ├── features/ # 按业务域拆分的功能页面 │ │ │ ├── debug/ # 调机记录相关页面 │ │ │ ├── exceptions/ # 异常处置与闭环相关页面 │ │ │ ├── home/ # 首页看板与概览入口 │ │ │ ├── machines/ # 机台档案列表、详情和机台相关交互 │ │ │ ├── production/ # 生产批次、报工和结案门禁相关页面 │ │ │ ├── products/ # 产品档案、产品详情和关联信息 │ │ │ ├── reports/ # 周报、月报、班次报表和下钻入口 │ │ │ └── templates/ # 参数模板列表与详情 │ │ ├── models/ # 业务对象的数据结构如 Machine、Product、DebugRecord │ │ ├── pages/ # 页面容器与导航装配如 Index.ets │ │ ├── repositories/ # 脱敏演示数据、查询方法、快照持久化和数据重置边界 │ │ ├── stores/ # 页面路由、导航选择和共享状态规则 │ │ └── utils/ # 主题令牌、校验函数等通用工具 │ ├── src/main/resources/base/ # 模块级资源目录 │ │ ├── element/ # 字符串、颜色等基础资源声明 │ │ ├── media/ # 图标、启动图等媒体资源 │ │ └── profile/ # 页面 profile 配置如 main_pages.json │ ├── src/main/module.json5 # entry 模块配置声明 EntryAbility、设备类型和页面入口 │ ├── build-profile.json5 # 模块级构建目标、混淆和 target 配置 │ └── oh-package.json5 # entry 模块包信息与依赖声明 ├── hvigor/ # Hvigor 构建系统配置 │ └── hvigor-config.json5 # 构建执行参数如增量、并行和类型检查 ├── build-profile.json5 # 工程级 SDK、targetSdkVersion、compatibleSdkVersion 配置 ├── hvigorfile.ts # 工程级构建任务入口接入 appTasks ├── local.properties # 本机 SDK 路径配置 ├── oh-package.json5 # 工程级包信息与依赖声明 ├── build.ps1 # 本地构建脚本固定使用 DevEco Studio 自带工具链 ├── document_claude/ # 开发过程归档、测试记录和验证材料 ├── .hvigor/ # Hvigor 生成的缓存和构建记录不作为手写源码维护 ├── .idea/ # DevEco Studio / IntelliJ 工程配置不承载业务逻辑 └── entry/build/ # 构建输出目录HAP 和中间产物由构建流程生成1. 为什么应用级配置放在AppScopeAppScope负责应用整体身份而不是某个页面的业务逻辑。app.json5中的bundleName、versionName、versionCode、应用图标和应用标签会影响安装包身份、桌面展示和版本识别。把这类配置放在应用级目录可以避免业务页面为了改一个标题或图标而混入应用发布配置。在当前工程中AppScope更像“应用身份证”。它回答的是“这个 App 是谁、版本是多少、展示什么图标”而不是“机台列表怎么筛选、详情页怎么返回”。2. 为什么业务代码集中在entry/src/main/etsentry是当前工程的主业务模块src/main/ets是 ArkTS 源码根目录。截图里打开的MachineDetail.ets就位于features/machines下面说明机台详情页被归入“机台业务域”而不是随意放在全局页面目录中。这种组织方式的好处是定位明确机台问题优先看features/machines产品问题优先看features/products生产批次问题优先看features/production。当文章里讨论某个业务链路时读者也能从目录直接反推代码位置。3.components、features和pages的边界components放的是可复用组件例如底部导航、加载/空态/失败态面板。它们不应该直接知道“当前打开的是哪台机台”而是通过参数和回调服务于不同页面。features放的是业务域页面。每个子目录都围绕一个业务主题组织例如machines负责机台档案templates负责参数模板exceptions负责异常闭环。业务页面可以组合组件也可以读取模型和仓储但应尽量把本业务域的显示和交互留在本目录内。pages更偏页面容器和入口装配。当前Index.ets承担主页面状态切换、底部导航和详情路径分发等职责。它不应该塞满所有业务细节而是负责把用户当前所在位置、打开对象和页面分支组织起来。4.models、repositories和stores分别解决什么问题models定义数据形状例如机台、产品、调机记录、生产批次等对象有哪些字段。它让页面和仓储使用同一套类型语言避免每个页面临时拼对象。repositories定义数据来源和查询边界。当前工程使用脱敏演示数据和本地持久化快照因此仓储层负责“从哪里取数据、按什么 ID 查询、怎样重置演示数据”。页面不直接关心数据是内置数组、Preferences 快照还是后续真实接口。stores定义页面级或应用级状态规则例如当前导航项、路由分支、打开详情的类型和 ID。把状态规则从具体组件中抽出来可以减少“列表、详情、导航互相覆盖状态”的问题。5. 为什么资源放在resources/baseresources/base/element管字符串、颜色等声明resources/base/media管图标和图片resources/base/profile管页面 profile。它们和 ArkTS 页面代码分开是为了让“界面逻辑”和“静态资源”各自清晰。如果页面显示异常先判断是布局代码问题还是资源引用问题。比如图标不显示应优先检查media和资源引用页面无法进入应检查profile/main_pages.json和module.json5的页面声明颜色或字符串不符合预期则回到element下核对。6. 构建目录和生成目录不要手工维护.hvigor、entry/build和部分中间产物目录由构建系统生成主要用于缓存、编译记录、HAP 输出和临时文件。它们可以帮助排查构建结果但不应该作为手写业务代码维护。当前调试 HAP 位于entry/build/default/outputs/default/entry-default-unsigned.hap。这个路径说明构建已经产出安装包但它仍是 unsigned 调试产物正式发布前应回到 DevEco Studio 的签名配置和发布流程而不是直接修改build目录里的文件。