Electron for OpenHarmony 实战:Form 表单组件实现 PC适配

表单是用户输入数据的主要方式,登录注册、信息编辑、搜索筛选都离不开表单。一个好的表单不仅要收集数据,还要有清晰的布局、友好的校验提示。这篇文章来聊聊在 Electron for OpenHarmony 项目中如何实现 Form 表单组件。
表单的组成
Element Plus 的表单由三个核心组件构成:el-form 是表单容器,负责整体布局和校验管理;el-form-item 是表单项,包裹每个输入控件并显示标签和校验信息;具体的输入控件如 el-input、el-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>
reactive 和 ref 都能创建响应式数据,区别是 reactive 适合对象,ref 适合基本类型。表单数据通常是一个对象,用 reactive 更方便,不用每次访问都加 .value。
表单对象的字段要和表单控件一一对应:name 是文本输入,初始值空字符串;delivery 是开关,初始值 false;type 是多选框组,初始值空数组。初始值的类型要和控件期望的类型匹配,不然会出问题。
基础表单布局
一个典型的表单长这样:
<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-item 的 label 属性设置标签文字,内部放具体的输入控件。最后一个 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: 内置类型校验,如email、url、numbervalidator: 自定义校验函数
el-form-item 的 prop 属性要和 rules 的 key 对应,这样校验系统才知道这个表单项用哪条规则。
formRef 是表单实例的引用,通过它可以调用 validate 方法触发校验,resetFields 方法重置表单。
自定义校验函数 validatePass 和 validateConfirmPass 展示了如何实现复杂的校验逻辑,比如密码长度检查和两次密码一致性检查。校验通过调用 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-row 和 el-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/
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)