在这里插入图片描述
表单是用户输入数据的主要方式,登录注册、信息编辑、搜索筛选都离不开表单。一个好的表单不仅要收集数据,还要有清晰的布局、友好的校验提示。这篇文章来聊聊在 Electron for OpenHarmony 项目中如何实现 Form 表单组件。

表单的组成

Element Plus 的表单由三个核心组件构成:el-form 是表单容器,负责整体布局和校验管理;el-form-item 是表单项,包裹每个输入控件并显示标签和校验信息;具体的输入控件如 el-inputel-select 等负责数据录入。

这种分层设计让表单的结构很清晰,每一层各司其职。

表单数据管理

表单数据用 reactive 创建响应式对象来管理:

<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
const router = useRouter()
const form = reactive({ name: '', region: '', date: '', delivery: false, type: [], desc: '' })
const options = [{ value: '1', label: '区域一' }, { value: '2', label: '区域二' }]
const onSubmit = () => ElMessage.success('提交成功')
</script>

reactiveref 都能创建响应式数据,区别是 reactive 适合对象,ref 适合基本类型。表单数据通常是一个对象,用 reactive 更方便,不用每次访问都加 .value

表单对象的字段要和表单控件一一对应:name 是文本输入,初始值空字符串;delivery 是开关,初始值 falsetype 是多选框组,初始值空数组。初始值的类型要和控件期望的类型匹配,不然会出问题。

基础表单布局

一个典型的表单长这样:

<el-card class="demo-card">
  <template #header>基础表单</template>
  <el-form :model="form" label-width="80px">
    <el-form-item label="活动名称">
      <el-input v-model="form.name" />
    </el-form-item>
    <el-form-item label="活动区域">
      <el-select v-model="form.region" placeholder="请选择" style="width: 100%">
        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
      </el-select>
    </el-form-item>
    <el-form-item label="即时配送">
      <el-switch v-model="form.delivery" />
    </el-form-item>
    <el-form-item label="活动性质">
      <el-checkbox-group v-model="form.type">
        <el-checkbox value="美食" name="type">美食</el-checkbox>
        <el-checkbox value="地推" name="type">地推</el-checkbox>
        <el-checkbox value="线下" name="type">线下</el-checkbox>
      </el-checkbox-group>
    </el-form-item>
    <el-form-item label="活动描述">
      <el-input v-model="form.desc" type="textarea" />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit">提交</el-button>
      <el-button>取消</el-button>
    </el-form-item>
  </el-form>
</el-card>

:model 绑定表单数据对象,这是表单校验的基础。label-width="80px" 统一设置所有标签的宽度,让表单对齐整齐。

每个 el-form-itemlabel 属性设置标签文字,内部放具体的输入控件。最后一个 form-item 没有 label,用来放提交和取消按钮。

这个表单包含了几种常见的输入控件:文本输入框、下拉选择器、开关、多选框组、文本域。它们都通过 v-model 和 form 对象的对应字段双向绑定。

行内表单

搜索栏这种场景,表单项需要横向排列:

<el-card class="demo-card">
  <template #header>行内表单</template>
  <el-form :model="form" inline>
    <el-form-item label="名称">
      <el-input v-model="form.name" placeholder="名称" />
    </el-form-item>
    <el-form-item label="区域">
      <el-select v-model="form.region" placeholder="区域">
        <el-option label="区域一" value="1" />
        <el-option label="区域二" value="2" />
      </el-select>
    </el-form-item>
    <el-form-item>
      <el-button type="primary">查询</el-button>
    </el-form-item>
  </el-form>
</el-card>

inline 属性让表单项横向排列,适合筛选条件不多的搜索场景。行内表单的标签和控件在同一行,比较紧凑。

表单校验

表单校验是重头戏,Element Plus 内置了基于 async-validator 的校验系统:

<script setup lang="ts">
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'

const formRef = ref<FormInstance>()

const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

const validatePass = (rule: any, value: string, callback: any) => {
  if (value === '') {
    callback(new Error('请输入密码'))
  } else if (value.length < 6) {
    callback(new Error('密码长度不能少于6位'))
  } else {
    callback()
  }
}

const validateConfirmPass = (rule: any, value: string, callback: any) => {
  if (value === '') {
    callback(new Error('请再次输入密码'))
  } else if (value !== form.password) {
    callback(new Error('两次输入的密码不一致'))
  } else {
    callback()
  }
}

const rules = reactive<FormRules>({
  name: [
    { required: true, message: '请输入活动名称', trigger: 'blur' },
    { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
  ],
  email: [
    { required: true, message: '请输入邮箱', trigger: 'blur' },
    { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
  ],
  password: [
    { required: true, validator: validatePass, trigger: 'blur' }
  ],
  confirmPassword: [
    { required: true, validator: validateConfirmPass, trigger: 'blur' }
  ]
})

const onSubmit = async () => {
  if (!formRef.value) return
  await formRef.value.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('请检查表单')
    }
  })
}

const onReset = () => {
  formRef.value?.resetFields()
}
</script>

<template>
  <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
    <el-form-item label="活动名称" prop="name">
      <el-input v-model="form.name" />
    </el-form-item>
    <el-form-item label="邮箱" prop="email">
      <el-input v-model="form.email" />
    </el-form-item>
    <el-form-item label="密码" prop="password">
      <el-input v-model="form.password" type="password" show-password />
    </el-form-item>
    <el-form-item label="确认密码" prop="confirmPassword">
      <el-input v-model="form.confirmPassword" type="password" show-password />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit">提交</el-button>
      <el-button @click="onReset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

校验规则通过 :rules 绑定,是一个对象,key 是字段名,value 是规则数组。每条规则可以设置:

  • required: 是否必填
  • message: 校验失败的提示信息
  • trigger: 触发校验的时机,blur 是失焦时,change 是值变化时
  • min/max: 长度限制
  • type: 内置类型校验,如 emailurlnumber
  • validator: 自定义校验函数

el-form-itemprop 属性要和 rules 的 key 对应,这样校验系统才知道这个表单项用哪条规则。

formRef 是表单实例的引用,通过它可以调用 validate 方法触发校验,resetFields 方法重置表单。

自定义校验函数 validatePassvalidateConfirmPass 展示了如何实现复杂的校验逻辑,比如密码长度检查和两次密码一致性检查。校验通过调用 callback(),失败调用 callback(new Error('错误信息'))

动态表单项

有些场景需要动态增减表单项,比如添加多个联系人:

<script setup lang="ts">
import { reactive } from 'vue'

const form = reactive({
  contacts: [{ name: '', phone: '' }]
})

const addContact = () => {
  form.contacts.push({ name: '', phone: '' })
}

const removeContact = (index: number) => {
  if (form.contacts.length > 1) {
    form.contacts.splice(index, 1)
  }
}
</script>

<template>
  <el-form :model="form" label-width="80px">
    <div v-for="(contact, index) in form.contacts" :key="index" style="margin-bottom: 16px; padding: 16px; background: #f5f7fa; border-radius: 4px;">
      <el-form-item :label="'联系人' + (index + 1)" :prop="'contacts.' + index + '.name'" :rules="{ required: true, message: '请输入姓名', trigger: 'blur' }">
        <el-input v-model="contact.name" placeholder="姓名" />
      </el-form-item>
      <el-form-item label="电话" :prop="'contacts.' + index + '.phone'" :rules="{ required: true, message: '请输入电话', trigger: 'blur' }">
        <el-input v-model="contact.phone" placeholder="电话" />
      </el-form-item>
      <el-button type="danger" size="small" @click="removeContact(index)" :disabled="form.contacts.length === 1">删除</el-button>
    </div>
    <el-form-item>
      <el-button type="primary" @click="addContact">添加联系人</el-button>
    </el-form-item>
  </el-form>
</template>

动态表单项的关键是 prop 的写法:'contacts.' + index + '.name' 这种点号分隔的路径格式,让校验系统能正确定位到数组中的具体字段。

表单布局控制

除了行内布局,还可以控制标签的位置:

<!-- 标签在上方 -->
<el-form :model="form" label-position="top">
  <el-form-item label="活动名称">
    <el-input v-model="form.name" />
  </el-form-item>
</el-form>

<!-- 标签右对齐 -->
<el-form :model="form" label-position="right" label-width="100px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name" />
  </el-form-item>
</el-form>

<!-- 标签左对齐 -->
<el-form :model="form" label-position="left" label-width="100px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name" />
  </el-form-item>
</el-form>

label-position 控制标签位置,top 适合移动端或者标签文字很长的情况,right 是默认值,left 让标签左对齐。

还可以用栅格系统实现多列布局:

<el-form :model="form" label-width="80px">
  <el-row :gutter="20">
    <el-col :span="12">
      <el-form-item label="姓名">
        <el-input v-model="form.name" />
      </el-form-item>
    </el-col>
    <el-col :span="12">
      <el-form-item label="电话">
        <el-input v-model="form.phone" />
      </el-form-item>
    </el-col>
  </el-row>
  <el-row :gutter="20">
    <el-col :span="24">
      <el-form-item label="地址">
        <el-input v-model="form.address" />
      </el-form-item>
    </el-col>
  </el-row>
</el-form>

el-rowel-col 是 Element Plus 的栅格组件,:span 设置列宽(总共 24 份),:gutter 设置列间距。

与鸿蒙原生能力结合

在 Electron for OpenHarmony 项目中,表单提交可能需要调用原生能力:

<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useOhos } from '@/composables/useOhos'
import { ElMessage } from 'element-plus'
import type { FormInstance } from 'element-plus'

const { showNotification, openFile } = useOhos()
const formRef = ref<FormInstance>()

const form = reactive({
  title: '',
  content: '',
  attachment: ''
})

const selectAttachment = async () => {
  const files = await openFile({
    title: '选择附件',
    filters: [{ name: '所有文件', extensions: ['*'] }]
  })
  if (files && files.length > 0) {
    form.attachment = files[0]
  }
}

const onSubmit = async () => {
  if (!formRef.value) return
  await formRef.value.validate(async (valid) => {
    if (valid) {
      // 调用原生通知
      await showNotification('提交成功', `标题: ${form.title}`)
      ElMessage.success('表单已提交')
    }
  })
}
</script>

<template>
  <el-form ref="formRef" :model="form" label-width="80px">
    <el-form-item label="标题" prop="title" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
      <el-input v-model="form.title" />
    </el-form-item>
    <el-form-item label="内容" prop="content">
      <el-input v-model="form.content" type="textarea" :rows="4" />
    </el-form-item>
    <el-form-item label="附件">
      <el-input v-model="form.attachment" readonly placeholder="点击选择文件">
        <template #append>
          <el-button @click="selectAttachment">选择</el-button>
        </template>
      </el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="onSubmit">提交</el-button>
    </el-form-item>
  </el-form>
</template>

这个例子展示了两个原生能力的使用:openFile 打开文件选择器让用户选择附件,showNotification 在提交成功后发送系统通知。表单和原生能力的结合让应用体验更接近原生应用。

表单禁用

整个表单需要禁用时,不用给每个控件都加 disabled:

<el-form :model="form" :disabled="isSubmitting" label-width="80px">
  <el-form-item label="名称">
    <el-input v-model="form.name" />
  </el-form-item>
  <el-form-item label="区域">
    <el-select v-model="form.region">
      <el-option label="区域一" value="1" />
    </el-select>
  </el-form-item>
</el-form>

:disabled 加在 el-form 上会禁用所有子控件,适合提交过程中防止用户修改数据。

小结

Form 表单是数据录入的核心组件,这篇文章介绍了它的各种用法:基础布局、行内表单、表单校验、动态表单项、布局控制,以及与鸿蒙原生能力的结合。表单的核心是数据绑定和校验,v-model 实现双向绑定,rules 定义校验规则,validate 方法触发校验。掌握这些,就能应对大部分表单场景了。


欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐