img

一、场景介绍

在实际开发中,表单验证是一个常见的需求场景。本文将介绍如何使用StepperItem组件来构建一个分步骤的表单验证流程,包括个人信息填写、联系方式验证和账户设置三个步骤。

二、功能实现

1. 页面结构设计

@Entry
@Component
struct FormValidationExample {
  @State currentStep: number = 0
  @State formData: {[key: string]: string} = {
    name: '',
    age: '',
    phone: '',
    email: '',
    username: '',
    password: ''
  }
  @State errors: {[key: string]: string} = {}

  build() {
    Column() {
      // 步骤指示器
      Stepper() {
        StepperItem() {
          Text('个人信息')
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
        }.status(this.currentStep > 0 ? ItemState.COMPLETED :
                this.currentStep === 0 ? ItemState.CURRENT : ItemState.WAITING)

        StepperItem() {
          Text('联系方式')
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
        }.status(this.currentStep > 1 ? ItemState.COMPLETED :
                this.currentStep === 1 ? ItemState.CURRENT : ItemState.WAITING)

        StepperItem() {
          Text('账户设置')
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
        }.status(this.currentStep === 2 ? ItemState.CURRENT : ItemState.WAITING)
      }.margin({ top: 20, bottom: 40 })

      // 表单内容区域
      this.renderFormContent()

      // 操作按钮
      Row() {
        if (this.currentStep > 0) {
          Button('上一步')
            .onClick(() => this.previousStep())
            .margin({ right: 20 })
        }
        Button(this.currentStep === 2 ? '提交' : '下一步')
          .onClick(() => this.nextStep())
      }.margin({ top: 40 })
    }.padding(20)
  }

  @Builder
  renderFormContent() {
    Column() {
      if (this.currentStep === 0) {
        this.renderPersonalInfo()
      } else if (this.currentStep === 1) {
        this.renderContactInfo()
      } else {
        this.renderAccountInfo()
      }
    }
  }

  @Builder
  renderPersonalInfo() {
    Column() {
      TextInput({ placeholder: '请输入姓名' })
        .onChange((value: string) => {
          this.formData.name = value
          this.validateField('name', value)
        })
      if (this.errors.name) {
        Text(this.errors.name)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }

      TextInput({ placeholder: '请输入年龄' })
        .type(InputType.Number)
        .margin({ top: 20 })
        .onChange((value: string) => {
          this.formData.age = value
          this.validateField('age', value)
        })
      if (this.errors.age) {
        Text(this.errors.age)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }
    }.width('100%')
  }

  @Builder
  renderContactInfo() {
    Column() {
      TextInput({ placeholder: '请输入手机号' })
        .type(InputType.Number)
        .onChange((value: string) => {
          this.formData.phone = value
          this.validateField('phone', value)
        })
      if (this.errors.phone) {
        Text(this.errors.phone)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }

      TextInput({ placeholder: '请输入邮箱' })
        .margin({ top: 20 })
        .onChange((value: string) => {
          this.formData.email = value
          this.validateField('email', value)
        })
      if (this.errors.email) {
        Text(this.errors.email)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }
    }.width('100%')
  }

  @Builder
  renderAccountInfo() {
    Column() {
      TextInput({ placeholder: '请输入用户名' })
        .onChange((value: string) => {
          this.formData.username = value
          this.validateField('username', value)
        })
      if (this.errors.username) {
        Text(this.errors.username)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }

      TextInput({ placeholder: '请输入密码' })
        .type(InputType.Password)
        .margin({ top: 20 })
        .onChange((value: string) => {
          this.formData.password = value
          this.validateField('password', value)
        })
      if (this.errors.password) {
        Text(this.errors.password)
          .fontSize(12)
          .fontColor(Color.Red)
          .margin({ top: 4 })
      }
    }.width('100%')
  }

  validateField(field: string, value: string) {
    switch (field) {
      case 'name':
        if (!value) {
          this.errors.name = '姓名不能为空'
        } else if (value.length < 2) {
          this.errors.name = '姓名长度不能少于2个字符'
        } else {
          delete this.errors.name
        }
        break
      case 'age':
        if (!value) {
          this.errors.age = '年龄不能为空'
        } else if (parseInt(value) < 18 || parseInt(value) > 100) {
          this.errors.age = '年龄必须在18-100岁之间'
        } else {
          delete this.errors.age
        }
        break
      case 'phone':
        if (!value) {
          this.errors.phone = '手机号不能为空'
        } else if (!/^1\d{10}$/.test(value)) {
          this.errors.phone = '请输入正确的手机号'
        } else {
          delete this.errors.phone
        }
        break
      case 'email':
        if (!value) {
          this.errors.email = '邮箱不能为空'
        } else if (!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
          this.errors.email = '请输入正确的邮箱地址'
        } else {
          delete this.errors.email
        }
        break
      case 'username':
        if (!value) {
          this.errors.username = '用户名不能为空'
        } else if (value.length < 4) {
          this.errors.username = '用户名长度不能少于4个字符'
        } else {
          delete this.errors.username
        }
        break
      case 'password':
        if (!value) {
          this.errors.password = '密码不能为空'
        } else if (value.length < 6) {
          this.errors.password = '密码长度不能少于6个字符'
        } else {
          delete this.errors.password
        }
        break
    }
  }

  validateCurrentStep(): boolean {
    const fieldsToValidate = {
      0: ['name', 'age'],
      1: ['phone', 'email'],
      2: ['username', 'password']
    }

    const currentFields = fieldsToValidate[this.currentStep]
    currentFields.forEach(field => {
      this.validateField(field, this.formData[field])
    })

    return !currentFields.some(field => this.errors[field])
  }

  nextStep() {
    if (this.validateCurrentStep()) {
      if (this.currentStep === 2) {
        this.submitForm()
      } else {
        this.currentStep++
      }
    }
  }

  previousStep() {
    if (this.currentStep > 0) {
      this.currentStep--
    }
  }

  submitForm() {
    // 在实际应用中,这里会发送请求到服务器
    AlertDialog.show({
      title: '提交成功',
      message: '表单数据已成功提交',
      confirm: {
        value: '确定',
        action: () => {
          // 重置表单
          this.currentStep = 0
          this.formData = {
            name: '',
            age: '',
            phone: '',
            email: '',
            username: '',
            password: ''
          }
          this.errors = {}
        }
      }
    })
  }
}

三、实现要点

1. 状态管理

  • 使用@State装饰器管理表单数据和当前步骤
  • 通过errors对象存储表单验证错误信息
  • 实现了表单数据的双向绑定

2. 表单验证

  • 为每个字段实现了实时验证
  • 在进入下一步之前进行当前步骤的表单验证
  • 提供了清晰的错误提示

3. 步骤控制

  • 实现了前进、后退功能
  • 根据当前步骤动态显示不同的表单内容
  • 最后一步完成后显示提交成功提示

四、总结

本文通过使用HarmonyOS的StepperItem组件,实现了一个分步骤的表单验证流程。通过状态管理、表单验证和步骤控制等功能,展示了如何构建一个结构清晰、用户体验良好的表单验证系统。这种分步骤的表单设计不仅提高了用户填写表单的效率,还增强了表单验证的准确性和易用性。

Logo

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

更多推荐