HarmonyOS 校园应用系列之ArkTS 表单工程:自习室选座页的三色优先级与开关配置
HarmonyOS 校园应用系列之ArkTS 表单工程:自习室选座页的三色优先级与开关配置
应用背景:09 自习室预约的选座创建页(Func1Tab.ets,215 行)是用户发起座位申请的核心表单。本文聚焦 FormCard 文本输入、TypeCard 类型四选一、PriorityCard 三色优先级、SettingCard 开关配置、TemplateCard 快捷模板 五大模块。

一、整体架构
选座页采用 "固定头部 + 可滚动表单区" 布局——Header 在 Scroll 之外,天然不随内容滚动:
Column
├── Header(标题"创建"+副标题+📋 图标按钮,白底底部描边)
└── Scroll (.scrollBar Off)
├── FormCard(基本信息:标题输入+描述文本域+附件行)
├── TypeCard(选择类型:四选一单选)
├── PriorityCard(优先级:低/中/高 三档)
├── SettingCard(提醒通知 Toggle + 隐私设置)
├── TemplateCard(快捷模板列表)
└── SubmitBtn(提交按钮+条款说明)
Header 通过 .height(this.safeTop + 60) 适配状态栏,.alignItems(VerticalAlign.Bottom) 让内容沉底。

二、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 })
标题行包含三部分:左侧"创建"大标题+副标题说明(纵向 Column)、中间弹性空白、右侧 📋 图标按钮(36vp 圆角方块)。头部白底 + 底部 1vp 描边,与滚动区形成分层。
三、FormCard —— 受控文本输入
3.1 表单卡片容器
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('标题')... TextInput(...) }
// 描述文本域(带标签)
Column({ space: 6 }) { Text('描述')... TextArea(...) }
// 底部操作行(附件按钮 + 字数统计)
Row() { ... }
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })
FormCard 用 Column 纵向排列所有表单元素,统一 14vp 内边距、card 背景色、rMd 圆角、stroke 描边。每个输入控件上方都有独立的小标签 Text。
3.2 标题输入(TextInput)
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%')
受控模式:text: this.inputTitle 绑定状态值,.onChange() 回写。双向绑定确保 UI 与数据同步。输入框用 cardSoft 底色 + rSm 圆角,占位符 textDim 灰。
3.3 描述文本域(TextArea)
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%')
TextArea 比 TextInput 多行,高度 90vp 适合短描述场景。同样采用受控模式。
3.4 附件按钮 + 字数统计
表单底部一行包含两部分:
左侧:附件按钮组(图片📷 + 链接🔗)
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%')
附件按钮就是带 cardSoft 底色和内边距的 Text 胶囊,无点击事件,预留后续扩展点。
右侧:实时字数统计
Text(`${this.inputTitle.length}/50`).fontSize(10).fontColor(C.textDim)
统计的是 标题(inputTitle)长度而非描述,上限 50 字,固定 textDim 灰色显示。
四、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 })
选中态双标记:图标底 primarySoft 浅青底 + 2vp 主色描边,文字主色加粗。用 Row + ForEach + layoutWeight(1) 四等分(非 Grid)。
四种类型:类型一📝 / 类型二🎯 / 类型三⭐ / 类型四📊。
五、PriorityCard —— 三色优先级选择
优先级卡片用 水平 Row 布局三个选项,priorities 是纯字符串数组 ['低', '中', '高'],颜色由索引三元表达式决定:
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%')
卡片头部右侧还有当前选中档位的回显:Text(this.priorities[this.selectedPriority]) 主色加粗。
三级优先级的颜色语义:
| 级别 | 颜色 | 色值 | 场景 |
|---|---|---|---|
| 低 | 绿色 | #2BB673 | 普通需求 |
| 中 | 橙色 | #FF9F1C | 常规需求(默认选中,selectedPriority 初始 1) |
| 高 | 红色 | #FF5A6E | 紧急需求 |
关键技巧:idx === 2 ? C.danger : idx === 1 ? C.warn : C.ok——选中时白字实底(红/橙/绿),未选中时 textSub 灰字 + cardSoft 浅底。颜色语义遵循国际惯例:绿色安全、橙色注意、红色警告。

六、SettingCard —— Toggle 配置开关
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 开关,.selectedColor(C.primary) 定制选中色,默认开启)与 隐私设置(› 箭头入口),中间用 Divider().color(C.stroke) 分隔。layoutWeight(1) 将开关/箭头推到最右端。

七、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 })
三个模板:快速创建(使用默认模板)/ 高级模式(自定义所有字段)/ 批量导入(从文件导入)。图标底用 primarySoft 浅青色区分于普通设置项,点击 Toast 提示。
八、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%')
防错机制:按钮文案与背景色由 this.inputTitle.length > 0 驱动——未填标题时显示"请填写标题"(cardSoft 灰底),填写后变"✓ 提交创建"(primary 青底)。点击时二次校验,空标题直接 Toast 拦截;成功后清空两个输入框。下方还有 9vp 的条款说明小字。

九、与 08 对比
| 特性 | 08 会议室 | 09 自习室 |
|---|---|---|
| 表单字段 | 标题+描述+类型四选一+优先级三档 | 标题+描述+类型四选一+优先级三档 |
| 选择控件 | Row + ForEach + layoutWeight | Row + ForEach + layoutWeight(同构) |
| 配置项 | 提醒通知 Toggle + 隐私设置入口 | 提醒通知 Toggle + 隐私设置入口(同构) |
| 主题色 | 靛蓝 #6366F1 | 青绿 #14B8A6 |
08 与 09 的创建页 完全同构,仅主题色不同(靛蓝 vs 青绿),属于"同模板换肤"关系。

十、总结
选座页的核心工程实践:
- 受控输入模式:TextInput/TextArea 双向绑定 + onChange 回写
- 字数统计:
${this.inputTitle.length}/50实时显示标题长度 - 优先级语义配色:绿/橙/红三级索引三元 + 选中白字实底/未选中灰字浅底
- 提交防错:按钮文案/背景随 inputTitle 切换 + 点击二次校验 + 成功后清空表单
这套模式可直接复用于任何表单场景。
更多推荐




所有评论(0)