img

一、案例介绍

本案例将展示如何使用SpanStepper组件实现一个多步骤表单验证系统,包括表单数据验证、错误提示和状态管理等功能。

二、代码实现

@Entry
@Component
struct FormValidationExample {
  @State currentStep: number = 0
  @State formData: {
    username: string,
    password: string,
    email: string,
    phone: string,
    address: string,
    company: string
  } = {
    username: '',
    password: '',
    email: '',
    phone: '',
    address: '',
    company: ''
  }

  @State errors: {
    username: string,
    password: string,
    email: string,
    phone: string,
    address: string,
    company: string
  } = {
    username: '',
    password: '',
    email: '',
    phone: '',
    address: '',
    company: ''
  }

  // 验证用户名
  validateUsername(username: string): boolean {
    if (username.length < 3) {
      this.errors.username = '用户名至少需要3个字符'
      return false
    }
    this.errors.username = ''
    return true
  }

  // 验证密码
  validatePassword(password: string): boolean {
    const regex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/
    if (!regex.test(password)) {
      this.errors.password = '密码至少8位,包含字母和数字'
      return false
    }
    this.errors.password = ''
    return true
  }

  // 验证邮箱
  validateEmail(email: string): boolean {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    if (!regex.test(email)) {
      this.errors.email = '请输入有效的邮箱地址'
      return false
    }
    this.errors.email = ''
    return true
  }

  // 验证手机号
  validatePhone(phone: string): boolean {
    const regex = /^1[3-9]\d{9}$/
    if (!regex.test(phone)) {
      this.errors.phone = '请输入有效的手机号'
      return false
    }
    this.errors.phone = ''
    return true
  }

  // 验证当前步骤
  validateCurrentStep(): boolean {
    if (this.currentStep === 0) {
      return this.validateUsername(this.formData.username) &&
             this.validatePassword(this.formData.password)
    } else if (this.currentStep === 1) {
      return this.validateEmail(this.formData.email) &&
             this.validatePhone(this.formData.phone)
    }
    return true
  }

  build() {
    Column() {
      // 步骤指示器
      SpanStepper() {
        StepperItem() {
          Column() {
            Text('基本信息').fontSize(16).fontWeight(FontWeight.Bold)
            Text('账号和密码').fontSize(14).opacity(0.6)
          }
        }
        .status(this.currentStep === 0 ? ItemState.CURRENT :
               this.currentStep > 0 ? ItemState.COMPLETED : ItemState.WAITING)

        StepperItem() {
          Column() {
            Text('详细信息').fontSize(16).fontWeight(FontWeight.Bold)
            Text('联系方式').fontSize(14).opacity(0.6)
          }
        }
        .status(this.currentStep === 1 ? ItemState.CURRENT :
               this.currentStep > 1 ? ItemState.COMPLETED : ItemState.WAITING)

        StepperItem() {
          Column() {
            Text('确认提交').fontSize(16).fontWeight(FontWeight.Bold)
            Text('信息确认').fontSize(14).opacity(0.6)
          }
        }
        .status(this.currentStep === 2 ? ItemState.CURRENT : ItemState.WAITING)
      }
      .width('100%')
      .padding(16)

      // 表单内容
      Column() {
        if (this.currentStep === 0) {
          // 基本信息表单
          TextInput({ placeholder: '请输入用户名' })
            .width('100%')
            .height(48)
            .margin({ top: 16 })
            .onChange((value: string) => {
              this.formData.username = value
              this.validateUsername(value)
            })
          if (this.errors.username) {
            Text(this.errors.username)
              .fontSize(12)
              .fontColor(Color.Red)
              .margin({ top: 4 })
          }

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

        } else if (this.currentStep === 1) {
          // 详细信息表单
          TextInput({ placeholder: '请输入邮箱' })
            .width('100%')
            .height(48)
            .margin({ top: 16 })
            .onChange((value: string) => {
              this.formData.email = value
              this.validateEmail(value)
            })
          if (this.errors.email) {
            Text(this.errors.email)
              .fontSize(12)
              .fontColor(Color.Red)
              .margin({ top: 4 })
          }

          TextInput({ placeholder: '请输入手机号' })
            .width('100%')
            .height(48)
            .margin({ top: 16 })
            .onChange((value: string) => {
              this.formData.phone = value
              this.validatePhone(value)
            })
          if (this.errors.phone) {
            Text(this.errors.phone)
              .fontSize(12)
              .fontColor(Color.Red)
              .margin({ top: 4 })
          }

          TextInput({ placeholder: '请输入公司名称' })
            .width('100%')
            .height(48)
            .margin({ top: 16 })
            .onChange((value: string) => {
              this.formData.company = value
            })

          TextArea({ placeholder: '请输入详细地址' })
            .width('100%')
            .height(80)
            .margin({ top: 16 })
            .onChange((value: string) => {
              this.formData.address = value
            })

        } else {
          // 信息确认
          Column() {
            Text('信息确认').fontSize(20).fontWeight(FontWeight.Bold)
            
            Text(`用户名:${this.formData.username}`)
              .fontSize(16)
              .margin({ top: 16 })
            
            Text(`邮箱:${this.formData.email}`)
              .fontSize(14)
              .margin({ top: 8 })
            
            Text(`手机号:${this.formData.phone}`)
              .fontSize(14)
              .margin({ top: 8 })
            
            Text(`公司:${this.formData.company}`)
              .fontSize(14)
              .margin({ top: 8 })
            
            Text(`地址:${this.formData.address}`)
              .fontSize(14)
              .margin({ top: 8 })
          }
          .width('100%')
          .padding(16)
        }
      }.width('100%').padding({ left: 16, right: 16 })

      // 操作按钮
      Row() {
        Button('上一步')
          .enabled(this.currentStep > 0)
          .opacity(this.currentStep > 0 ? 1 : 0.5)
          .onClick(() => {
            if (this.currentStep > 0) {
              this.currentStep--
            }
          })

        Button(this.currentStep === 2 ? '提交' : '下一步')
          .enabled(this.validateCurrentStep())
          .onClick(() => {
            if (this.currentStep < 2) {
              this.currentStep++
            } else {
              // 在实际应用中,这里可以处理表单提交逻辑
              console.info('表单提交成功')
            }
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
      .padding(16)
      .margin({ top: 20 })
    }
  }
}

三、代码解析

1. 状态定义

@State formData: {  // 表单数据对象
  username: string,
  password: string,
  email: string,
  phone: string,
  address: string,
  company: string
}

@State errors: {  // 错误信息对象
  username: string,
  password: string,
  email: string,
  phone: string,
  address: string,
  company: string
}
  • 使用@State装饰器定义表单数据和错误信息
  • 分别存储每个字段的值和对应的错误提示

2. 表单验证

validateUsername(username: string): boolean {
  if (username.length < 3) {
    this.errors.username = '用户名至少需要3个字符'
    return false
  }
  this.errors.username = ''
  return true
}

validateCurrentStep(): boolean {
  if (this.currentStep === 0) {
    return this.validateUsername(this.formData.username) &&
           this.validatePassword(this.formData.password)
  }
  // ...
}
  • 为每个字段实现单独的验证方法
  • 使用正则表达式进行格式验证
  • 实现步骤级别的验证方法

3. 错误提示

TextInput({ placeholder: '请输入用户名' })
  .onChange((value: string) => {
    this.formData.username = value
    this.validateUsername(value)
  })
if (this.errors.username) {
  Text(this.errors.username)
    .fontSize(12)
    .fontColor(Color.Red)
}
  • 在输入框下方显示错误提示
  • 使用红色文字突出显示错误信息
  • 实时验证并更新错误状态

4. 按钮状态控制

Button(this.currentStep === 2 ? '提交' : '下一步')
  .enabled(this.validateCurrentStep())
  .onClick(() => {
    if (this.currentStep < 2) {
      this.currentStep++
    } else {
      console.info('表单提交成功')
    }
  })
  • 根据验证结果控制按钮状态
  • 只有当前步骤验证通过才能继续
  • 最后一步显示提交按钮

通过本案例,我们学习了:

  1. SpanStepper组件在表单验证中的应用
  2. 如何实现表单数据的验证和错误提示
  3. 使用正则表达式进行数据格式验证
  4. 表单状态的管理和控制

这些知识点将帮助你在实际项目中实现更严谨的表单验证功能。

Logo

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

更多推荐