HarmonyOS 校园应用系列之ArkTS 表单工程:打印文件创建页的受控组件与分组卡片实践

发布时间:2026/9/3 5:22:26
HarmonyOS 校园应用系列之ArkTS 表单工程:打印文件创建页的受控组件与分组卡片实践 HarmonyOS 校园应用系列之ArkTS 表单工程打印文件创建页的受控组件与分组卡片实践一、页面定位与功能概述文件创建页Func1Tab是校园打印 App 的数据录入中心——用户在此创建打印任务填写标题、描述、选择打印类型和优先级。页面标题为创建副标题填写信息快速创建明确了单一职责。本页是纯粹的数据录入型页面——包含 TextInput、TextArea、Toggle 等多种受控表单组件是学习 ArkUI 表单开发的最佳实战案例。页面采用Section 分组卡片布局每个功能区用白色圆角卡片包裹卡片间距 14vpColumn({ space: 14 })。1.1 页面状态定义State inputTitle: string // 文档标题 State inputDesc: string // 详细描述 State selectedType: number 0 // 选中的类型索引 State selectedPriority: number 1 // 优先级0低, 1中, 2高 State remindOn: boolean true // 提醒通知开关5 个State变量覆盖所有输入维度。selectedType和selectedPriority使用 number 类型作为数组索引比 string 类型更语义化。项目源码开源https://gitee.com/codenestFlow/HarmonyOSHub二、页面头部 —— 标题 操作按钮Builder Header() { Row({ space: 12 }) { Column({ space: 2 }) { Text(创建).fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) Text(填写信息快速创建).fontSize(10).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start) Blank() Row() { Text().fontSize(18) } .width(36).height(36).backgroundColor(C.cardSoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center) } .width(100%).height(this.safeTop 60) .padding({ top: this.safeTop, left: D.pad, right: D.pad }) .backgroundColor(C.card).alignItems(VerticalAlign.Bottom) .border({ width: { bottom: 1 }, color: C.stroke }) }头部是主标题副标题双行结构左侧 20vp 加粗创建与 10vp 灰色副标题填写信息快速创建纵向排列明确页面单一职责。右侧是 emoji 图标放在C.cardSoft底、D.rSm圆角的 36×36 方块中暗示可从剪贴板粘贴文档信息这是降低输入成本的典型策略。头部高度safeTop 60配合alignItems(VerticalAlign.Bottom)让内容沉底对齐底部 1vpC.stroke分隔线与白底C.card构成通栏吸顶效果。三、FormCard —— 基本信息录入区Builder FormCard() { Column({ space: 12 }) { Row() { Text(基本信息).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text) Blank() Text(✕).fontSize(16).fontColor(C.textDim) }.width(100%) Column({ space: 6 }) { Text(标题).fontSize(12).fontColor(C.textSub) TextInput({ placeholder: 请输入标题, text: this.inputTitle }) .onChange((v: string) { this.inputTitle v; }) .height(44).backgroundColor(C.cardSoft).borderRadius(D.rSm).placeholderColor(C.textDim) }.alignItems(HorizontalAlign.Start).width(100%) Column({ space: 6 }) { Text(描述).fontSize(12).fontColor(C.textSub) TextArea({ placeholder: 请输入详细描述..., text: this.inputDesc }) .onChange((v: string) { this.inputDesc v; }) .height(90).backgroundColor(C.cardSoft).borderRadius(D.rSm).placeholderColor(C.textDim) }.alignItems(HorizontalAlign.Start).width(100%) Row({ space: 8 }) { Text( 图片).fontSize(12).fontColor(C.textSub) .padding({ left: 10, right: 10, top: 6, bottom: 6 }) .backgroundColor(C.cardSoft).borderRadius(D.rSm) Text( 链接).fontSize(12).fontColor(C.textSub) .padding({ left: 10, right: 10, top: 6, bottom: 6 }) .backgroundColor(C.cardSoft).borderRadius(D.rSm) Blank() Text(${this.inputTitle.length}/50).fontSize(10).fontColor(C.textDim) }.width(100%) } .width(100%).padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }3.1 TextInput vs TextArea 对比特性TextInputTextArea适用场景单行短文本标题多行长文本描述默认行为Enter 不换行Enter 换行高度固定 44vp固定 90vp两者共享相同样式体系D.rSm圆角 10vp、C.cardSoft浅灰背景保持视觉一致性。3.2 受控组件双向绑定模式TextInput({ placeholder: 请输入标题, text: this.inputTitle }) // ① 外部传入当前值 .onChange((v: string) { // ② 用户输入时回调 this.inputTitle v // ③ 更新状态变量触发重渲染 })这是 ArkUI 标准的受控组件模式—— UI 显示完全由状态决定不存在显示值和存储值不一致的问题。在表单校验、重置、回填场景中尤为关键。3.3 实时字数统计${this.inputTitle.length}/50展示标题输入框的实时字数——即时反馈让用户每输入一个字符就看到变化分母 50 隐式传达上限。接近或超限时可将数字变为橙色/红色警告。3.4 附件按钮设计图片和链接按钮使用emoji 图标文字色块组合 图片、 链接浅灰背景C.cardSoft像可点击的标签Tag Button比纯文字或纯图标更醒目。四、TypeCard —— 图标化四选一类型选择器Builder TypeCard() { Column({ space: 12 }) { Text(选择类型).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) Row() { ForEach(this.types, (item: OptionItem, idx: number) { Column({ space: 6 }) { Row() { Text(item.icon).fontSize(24) } .width(48).height(48).borderRadius(D.rMd).justifyContent(FlexAlign.Center) .backgroundColor(this.selectedType idx ? C.primarySoft : C.cardSoft) .border({ width: this.selectedType idx ? 2 : 0, color: C.primary }) Text(item.name).fontSize(10) .fontColor(this.selectedType idx ? C.primary : C.textDim) .fontWeight(this.selectedType idx ? FontWeight.Bold : FontWeight.Normal) }.layoutWeight(1) .onClick(() { this.selectedType idx; }) }, (item: OptionItem) item.name) }.width(100%) } .width(100%).padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }4.1 选中态的四重同步变化选中某类型时同时发生4 个视觉属性变化属性未选中选中图标背景色浅灰C.cardSoft浅绿C.primarySoft色块强调图标边框0vp2vp 绿色描边文字颜色灰C.textDim绿C.primary文字高亮文字字重NormalBold加粗强化图标是 emojiText 类型一 / 类型二 / ⭐ 类型三 / 类型四放在 48×48、D.rMd圆角的方块中。多重信号叠加的效果远超单一属性切换——即使不看文字仅凭色块边框就能识别选中项。4.2 为什么不用 Radio 组件标准 Radio 只能显示圆形选择器无法展示业务图标需要横向一行四列排列而非垂直列表选中态需要使用主题绿色。当标准组件无法满足需求时用基础组件组合自定义是合理的选择。五、PriorityCard —— 三档优先级Builder PriorityCard() { Column({ space: 12 }) { Row() { Text(优先级).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text) Blank() Text(this.priorities[this.selectedPriority]).fontSize(13).fontColor(C.primary).fontWeight(FontWeight.Bold) }.width(100%) Row({ space: 6 }) { ForEach(this.priorities, (p: string, idx: number) { Text(p).fontSize(13) .fontColor(this.selectedPriority idx ? #FFFFFF : C.textSub) .backgroundColor(this.selectedPriority idx ? (idx 2 ? C.danger : idx 1 ? C.warn : C.ok) : C.cardSoft) .borderRadius(D.rSm).padding({ left: 16, right: 16, top: 8, bottom: 8 }) .onClick(() { this.selectedPriority idx; }) }, (p: string) p) }.width(100%) } .width(100%).padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }5.1 当前值回显卡片标题行右侧用Text(this.priorities[this.selectedPriority])实时回显当前选中的优先级绿色加粗用户无需扫视下方按钮即可确认当前状态。5.2 交通灯色彩语义优先级色值含义低C.ok#2BB673绿色冷静、不紧急中C.warn#FF9F1C琥珀注意、默认推荐高C.danger#FF5A6E红色紧急、立即关注选中项填充对应语义色 白字未选中项统一为C.cardSoft浅灰底 C.textSub灰字。默认值selectedPriority 1即中档大多数用户会接受默认选项以减少决策成本。六、SettingCard —— Toggle 开关与设置项Builder SettingCard() { Column({ space: 0 }) { Row({ space: 12 }) { Row() { Text().fontSize(16) } .width(32).height(32).backgroundColor(C.cardSoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center) Column({ space: 2 }) { Text(提醒通知).fontSize(14).fontColor(C.text) Text(开启后将推送提醒).fontSize(11).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start).layoutWeight(1) Toggle({ type: ToggleType.Switch, isOn: this.remindOn }) .selectedColor(C.primary) .onChange((on: boolean) { this.remindOn on; }) }.width(100%).padding({ top: 12, bottom: 12 }) Divider().color(C.stroke) Row({ space: 12 }) { Row() { Text().fontSize(16) } .width(32).height(32).backgroundColor(C.cardSoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center) Column({ space: 2 }) { Text(隐私设置).fontSize(14).fontColor(C.text) Text(仅自己可见).fontSize(11).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start).layoutWeight(1) Text(›).fontSize(22).fontColor(C.textDim) }.width(100%).padding({ top: 12, bottom: 12 }) } .width(100%).padding({ left: 14, right: 14 }).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }提醒开关使用系统Toggle组件ToggleType.Switch滑动开关isOn绑定remindOn状态.selectedColor(C.primary)让开启态轨道为主题绿。图标是 emojiText()放在 32×32 浅灰方块中。卡片下半部分是隐私设置跳转项 图标 ›箭头中间用Divider().color(C.stroke)分隔——开关项与跳转项合并在同一张卡片是设置页的经典分组手法。HarmonyOS 提供三种 Toggle 类型SwitchiOS 风格滑动开关、Checkbox复选框、Button独立切换按钮。开/关二态用 Toggle 更直观多选用 Checkbox 更语义准确。提醒通知明显是二态开关。七、TemplateCard 与 SubmitBtn —— 快捷模板与提交按钮Builder TemplateCard() { Column({ space: 10 }) { Text(快捷模板).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) ForEach(this.templates, (item: QuickTemplate) { Row({ space: 10 }) { Row() { Text().fontSize(18) } .width(32).height(32).backgroundColor(C.primarySoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center) Column({ space: 2 }) { Text(item.title).fontSize(13).fontWeight(FontWeight.Medium).fontColor(C.text) Text(item.desc).fontSize(10).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start).layoutWeight(1) Text(›).fontSize(18).fontColor(C.textDim) }.width(100%) .onClick(() { promptAction.showToast({ message: item.title }); }) }, (item: QuickTemplate) item.title) } .width(100%).padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) } Builder SubmitBtn() { Column({ space: 8 }) { Button(this.inputTitle.length 0 ? ✓ 提交创建 : 请填写标题) .width(100%).height(48) .backgroundColor(this.inputTitle.length 0 ? C.primary : C.cardSoft).fontColor(#FFFFFF) .fontSize(16).fontWeight(FontWeight.Bold).borderRadius(D.rMd) .onClick(() { if (this.inputTitle.length 0) { promptAction.showToast({ message: 请输入标题 }); return; } promptAction.showToast({ message: 创建成功 }); this.inputTitle ; this.inputDesc ; }) Text(提交即表示同意相关条款).fontSize(9).fontColor(C.textDim).width(100%).textAlign(TextAlign.Center) }.width(100%) }快捷模板提供三个预设快速创建/高级模式/批量导入点击弹出 toast。提交按钮是状态自适应按钮标题为空时显示请填写标题C.cardSoft灰底有输入时变为✓ 提交创建C.primary绿底——按钮文案与背景色随inputTitle.length联动无需额外校验提示。点击时若标题为空弹 toast 请输入标题并 return成功则弹创建成功并清空inputTitle、inputDesc两个状态完成表单重置。按钮下方 9vp 灰字提交即表示同意相关条款是合规提示。八、表单设计六大最佳实践视觉分组每个功能区用独立白色圆角卡片包裹降低认知负荷渐进式 disclosure核心信息 → 分类 → 优先级 → 辅助设置 → 模板 → 提交符合自然思维流程即时反馈字数实时更新、选中态即时切换、Toggle 即时响应、按钮文案随输入变化合理默认值类型默认第一项、优先级默认中、提醒默认开启清晰标签体系输入字段上方小号灰色标签 placeholder 补充上下文操作引导快捷模板降低输入成本、提交按钮文案引导补全标题九、表单校验与提交流程源码中的提交校验逻辑非常轻量——只校验标题.onClick(() { if (this.inputTitle.length 0) { promptAction.showToast({ message: 请输入标题 }); return; } promptAction.showToast({ message: 创建成功 }); this.inputTitle ; this.inputDesc ; })若要扩展为完整的生产级流程可在此基础上增加四步// 第一步前端校验 const canSubmit this.inputTitle.trim().length 0 this.inputDesc.trim().length 0; if (!canSubmit) { showToast(请填写完整信息); return; } // 第二步数据组装 const payload { title: this.inputTitle, content: this.inputDesc, type: this.types[this.selectedType].name, priority: [低,中,高][this.selectedPriority], reminder: this.remindOn }; // 第三步异步请求http 模块 POST // 第四步状态重置或路由跳转十、键盘适配方案真机上键盘弹出可能遮挡输入框。三种解决方案.expandSafeArea([SafeAreaType.KEYBOARD])— 自动避让Scroll 包裹输入区域推荐本页已采用— 最简单框架自动处理监听window.on(keyboardHeightChange)动态调整 padding本页主内容区已经包裹在Scroll中.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)键盘弹出时用户可滚动定位被遮挡的输入框。十一、总结文件创建页展示了 ArkUI 表单开发的完整模式Section 卡片分组布局保持视觉清晰受控组件双向绑定确保数据一致性图标化选择器提供超越标准组件的表现力状态自适应提交按钮引导用户补全关键信息。这些模式可直接复用到任何需要数据录入的场景。