前言

为一篇讲解 HarmonyOS 7 中 ArkUI/ArkTS 复杂表单状态机管理的技术文章绘制一张

复杂表单真正难的不是输入框多,而是流程状态多。草稿、校验失败、提交中、已提交、被驳回如果都靠布尔值拼,很容易出现非法组合。

这篇单独聊 报销申请 这个场景。重点不是堆 API,而是用状态机约束复杂表单的草稿、校验失败、提交中和已提交流程。

为什么复杂表单总是越改越乱

因为表单一开始看上去只是几个输入框,很多人自然会先用最顺手的方式往前写:加一个 isSubmitting,再加一个 hasError,不够再补一个 isSuccess

问题是这些布尔值前期很好加,后期却很难收。只要流程一复杂,你就会慢慢写出“既提交成功又还能编辑”“正在提交但还显示校验错误”这种别扭状态。

所以这篇文章想解决的核心,不是怎么声明一个枚举,而是怎么把表单流程真正收成一条清楚的状态线。

复杂表单别靠布尔值硬撑

报销申请这类表单会经历草稿、校验失败、提交中、已提交、被驳回。用一堆 isSubmit/isError/isEdit 很快会互相冲突,状态机更稳。

你可以先把它想成一个最简单的问题:这张表单现在到底处在哪一个阶段。只要这个问题回答不清,按钮文案、输入框可编辑性、错误提示就会开始互相打架。

为一篇讨论复杂表单状态管理的技术文章绘制一张手绘对比图,主题是‘布尔值硬撑 vs 状态机约束’。左侧

实操步骤
  1. 先列出表单状态,而不是先写输入框。
  2. 每个动作只允许从指定状态进入下一个状态。
  3. UI 按状态决定按钮文案和可编辑性。
  4. 错误信息单独保存,不塞进状态枚举。
状态可编辑主按钮
Draft提交
Invalid重新提交
Submitting提交中
Submitted已提交

状态机不是复杂化,而是把“什么时候能做什么”写清楚。

enum FormStage {
  Draft,
  Invalid,
  Submitting,
  Submitted
}

@Entry
@Component
struct ExpenseFormPage {
  @State stage: FormStage = FormStage.Draft
  @State amount: string = ''
  @State reason: string = ''
  @State errorText: string = ''

  private editable(): boolean {
    return this.stage === FormStage.Draft || this.stage === FormStage.Invalid
  }

  private submit() {
    if (!this.editable()) {
      return
    }
    if (this.amount.trim().length === 0 || this.reason.trim().length < 4) {
      this.errorText = '请填写金额,并补充至少 4 个字的报销原因'
      this.stage = FormStage.Invalid
      return
    }
    this.stage = FormStage.Submitting
    this.errorText = ''
    this.stage = FormStage.Submitted
  }

  private buttonText(): string {
    if (this.stage === FormStage.Submitting) {
      return '提交中'
    }
    if (this.stage === FormStage.Submitted) {
      return '已提交'
    }
    return this.stage === FormStage.Invalid ? '重新提交' : '提交'
  }

  build() {
    Column({ space: 14 }) {
      Text('报销申请').fontSize(22).fontWeight(FontWeight.Bold)
      TextInput({ placeholder: '报销金额', text: this.amount }).enabled(this.editable()).onChange((value: string) => this.amount = value)
      TextArea({ placeholder: '报销原因', text: this.reason }).enabled(this.editable()).height(90).onChange((value: string) => this.reason = value)
      if (this.errorText.length > 0) {
        Text(this.errorText).fontSize(13).fontColor('#D92D20')
      }
      Button(this.buttonText()).enabled(this.editable()).width('100%').onClick(() => this.submit())
    }.padding(16)
  }
}

为一篇讲解 HarmonyOS 7 ArkUI/ArkTS 状态机提交逻辑的文章绘制一张手绘流程图,

把关键代码一段段拆开
  • FormStage 是唯一流程状态,避免多个布尔字段组合出非法状态。
  • editable() 统一控制输入框和按钮是否可操作。
  • submit() 只负责状态迁移,校验失败进入 Invalid,成功进入 Submitted
  • errorText 单独保存,状态枚举只表达流程,不承载文案。
表单状态机要限制非法组合

复杂表单最怕多个布尔值互相打架。isSubmittingisErrorisSubmitted 同时存在时,很容易组合出“已经提交但还能编辑”“正在提交但又显示错误”的状态。状态机的价值,就是把流程收敛成一个明确阶段。

示例里的 FormStage 只表达流程,不承载错误文案。校验失败进入 Invalid,用户仍然可以编辑;提交中进入 Submitting,输入框和按钮都不可操作;提交成功进入 Submitted,页面不再允许重复提交。

放进真实项目还要补什么

真实项目里的表单,通常还会接异步提交、草稿保存、驳回重提、附件上传这些流程。只要流程一多,状态机的价值就会越来越明显,因为你终于可以明确地说出:当前是哪个阶段,允许做什么,不允许做什么。

如果后面要接接口,最好连“请求发起中”“请求失败后回到哪个状态”“成功后是否还能编辑”这些规则一起先补上。别等需求叠起来再补,到时候最容易重新滑回一堆布尔值。

写在最后

后续如果增加“被驳回”“草稿已保存”“等待审批”,也应该继续扩展状态和迁移动作,而不是再随手加布尔值。流程越复杂,越要先问清楚:当前状态下允许用户做什么。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐