HarmonyOS 校园应用系列之ArkTS 表单工程:会议室预约页的六卡纵向拆分与快捷模板
HarmonyOS 校园应用系列之ArkTS 表单工程:会议室预约页的六卡纵向拆分与快捷模板
一、页面定位与功能概述
预约页(Func1Tab)是会议室预约 App 的 "录入中心"——用户创建记录,填写标题与描述,选择类型和优先级,设置提醒通知,还可套用快捷模板。标题 "创建",副标题 "填写信息快速创建"。
页面由六个卡片纵向组成:FormCard(基本信息)→ TypeCard(选择类型)→ PriorityCard(优先级)→ SettingCard(提醒/隐私)→ TemplateCard(快捷模板)→ SubmitBtn(提交)。
@State inputTitle: string = '' // 标题
@State inputDesc: string = '' // 描述
@State selectedType: number = 0 // 类型(图标四选一)
@State selectedPriority: number = 1 // 优先级(默认"中")
@State remindOn: boolean = true // 提醒通知开关
selectedPriority 默认值 1——初始即选中"中"档;索引单选的默认态完全由初始值决定。



二、FormCard —— 标题与描述录入
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%')
// 描述用 TextArea,height(90)
TextInput({ placeholder, text: this.inputTitle }).onChange(...) 标准受控组件,输入框高 44vp。表单分区用 14vp 内边距的边框卡片包裹,卡片标题行右侧有 ✕ 关闭符,底部还有 📷 图片 / 🔗 链接 快捷标签与 ${this.inputTitle.length}/50 字数统计。
三、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 })
}
3.1 选中态的三重反馈
选中项同时变化三处:图标底色 C.cardSoft → C.primarySoft、描边 0 → 2vp C.primary、文字变主题色加粗。"底色+描边+字色"三重反馈 比单一反色更立体。
3.2 ForEach + layoutWeight 等分布局
四个选项用 ForEach(this.types, ...) 渲染,每项 .layoutWeight(1) 等分整行宽度——无需计算宽度,自动适配屏幕。键值生成器用 item.name 保证唯一。
四、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 })
}
4.1 语义化条件配色
选中色随档位变化:idx === 2 ? C.danger : idx === 1 ? C.warn : C.ok——高=红、中=橙、低=绿。颜色即语义,用户未读文字也能感知轻重。未选中项统一 C.cardSoft 底、C.textSub 字。
4.2 标题行右侧的当前值回显
Text(this.priorities[this.selectedPriority]) 在标题行右侧用主题色加粗回显当前档位——选中状态不止一处可见,扫一眼标题行即可确认。

五、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 })
}
5.1 一卡两行:开关行 + 跳转行
SettingCard 用 Column({ space: 0 }) 装两行设置项,中间 Divider().color(C.stroke) 分隔——"设置组"卡片 的标准结构。第一行是 Toggle 开关(提醒通知),第二行是 › 箭头跳转项(隐私设置)。
5.2 Toggle 受控绑定
Toggle({ type: ToggleType.Switch, isOn: this.remindOn })
.selectedColor(C.primary)
.onChange((on: boolean) => { this.remindOn = on; })
Toggle 的 isOn 直接绑定 this.remindOn,onChange 回写。表单场景最简单的受控组件——仅一个布尔状态。注意卡片外层只设左右 14vp 内边距,行内各自用 padding({ top: 12, bottom: 12 }) 控制高度,Divider 才能通栏。
六、TemplateCard —— 快捷模板列表
@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 })
}
三个模板(快速创建/高级模式/批量导入)渲染为图标+标题+描述+箭头的行列表,点击 Toast 提示。模板图标底色用 C.primarySoft(区别于设置项的 C.cardSoft),暗示"可点选的预设"。
七、SubmitBtn —— 动态文案 + 点击校验
@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%')
}
7.1 动态文案 + 点击时校验
按钮文案随标题输入实时切换:未填时显示 "请填写标题"(灰底 C.cardSoft),填好后变 "✓ 提交创建"(主题色底)。注意源码 没有用 .enabled() 禁用按钮——按钮始终可点,校验放在 onClick 内:标题为空时 Toast 报"请输入标题"并 return,成功则 Toast"创建成功!"并清空 inputTitle/inputDesc 两个状态。这是 "动态文案引导 + 点击时校验" 模式,与 .enabled() 防错禁用是两种不同取舍。

八、表单设计模式总结
| 组件 | 模式 | 状态类型 |
|---|---|---|
| TextInput/TextArea | 受控输入 | string |
| TypeCard | 图标四选一(单选) | number |
| PriorityCard | 三档胶囊(单选) | number |
| Toggle | 布尔开关 | boolean |
| SubmitBtn | 动态文案+点击校验 | — |
关键经验:①单选索引默认值即初始选中态(如 selectedPriority=1 默认“中”)②语义化配色让档位轻重一目了然 ③提交按钮动态文案引导、onClick 内校验兜底 ④成功后清空表单状态复位。

九、与 07 表单对比
有趣的事实:07 校园打印与 08 会议室预约的 Func1Tab 结构完全相同——同样的 FormCard/TypeCard/PriorityCard/SettingCard/TemplateCard/SubmitBtn 六卡片,同样的状态变量与交互逻辑,是同一套模板在不同业务域的复用。
| 特性 | 07 校园打印 | 08 会议室预约 |
|---|---|---|
| 页面结构 | 六卡片表单 | 完全相同 |
| 状态变量 | inputTitle/inputDesc/selectedType/selectedPriority/remindOn | 完全相同 |
| 主题色 | 绿色系 #10B981 | 靛蓝系 #6366F1 |
| 提交逻辑 | 动态文案+onClick 校验 | 完全相同 |
差异仅在 Theme.ets 的主题色定义——换肤不改结构 正是 C/D 双类主题体系的价值:业务模板沉淀后,新项目只需替换色板即可快速产出。
更多推荐




所有评论(0)