当前位置: 首页 > news >正文

Vue3封装动态Form表单

在使用Vue3的时候,有的时候,经常需要写很多的表单,使用UI组件库的时候,经常需要一大堆的Form和Form-Item;写的多,拷贝的也多,看到网上大佬的优雅代码,醍醐灌顶,改造了如下代码

UI库: @arco-design/web-vue ^2.56.3

vue: ^3.5.12,

  1. 父组件
<template><div class="container"><!-- 第一种用法,单文件.vue的用法,所有的属性通过props传递过去 --><!-- <FormBuilder ref="formRef" v-model="formData" :fields="fields" :label-col-props="{ span: 2 }":wrapper-col-props="{ span: 20 }" :rules="rules"></FormBuilder> --><!-- 第二种用法,hooks的方式,通过渲染函数进行渲染,h --><FormBuilder2></FormBuilder2><a-button @click="handleSubmit">提交</a-button></div>
</template><script setup lang="ts">
import FormBuilder from '@/components/FormBuilder.vue';
import { Message } from '@arco-design/web-vue';
import useFormBuilder from '@/hooks/useFormBuilder';const formRef = useTemplateRef<InstanceType<typeof FormBuilder>>('formRef')const formData = reactive({name: '张',age: 18,gender: '1',
})const fields = computed(() => ([{componentType: 'input',formItemProps: {label: '姓名',field: 'name',required: true,},componentProps: {placeholder: '请输入姓名',}},{componentType: 'number',isHidden: formData.name === "张三", // ",formItemProps: {label: '年龄',field: 'age',},componentProps: {placeholder: '请输入年龄',}},{componentType: 'select',formItemProps: {label: '性别',field: 'gender',},componentProps: {placeholder: '请选择性别',options: [{ label: '男', value: '1' },{ label: '女', value: '2' }]}}
]))const rules = {name: [{ required: true, message: '请输入姓名' }],age: [{ required: true, message: '请输入年龄' }],gender: [{ required: true, message: '请选择性别' }],
}const handleSubmit = async () => {// const valid = await formRef.value?.validate()const valid = await getFormBuilderRef().value?.validate()if (valid) return Message.error('提交失败')console.log(formData);Message.success('提交成功')
}
const { FormBuilder: FormBuilder2, getRef: getFormBuilderRef } = useFormBuilder({modelValue: formData,fields: fields.value,rules,wrapperColProps: {span: 20},labelColProps: {span: 2}
})
</script><style lang="scss" scoped></style>
  1. FormBuilder.vue 子组件
<template><a-form ref="formRef" :model="formData" v-bind="$attrs"><a-row><a-col :span="getSpan(item)" v-for="item in filterFields" :key="item.formItemProps.field"><a-form-item v-bind="item.formItemProps"><slot :name="item.formItemProps.field"><component v-model="formData[item.formItemProps.field]" :is="getComponent(item)"v-bind="item.componentProps"></component></slot></a-form-item></a-col></a-row></a-form>
</template><script setup lang="ts" name="FormBuilder">
import { Input, InputNumber, Select } from '@arco-design/web-vue'
import type { ValidatedError } from '@arco-design/web-vue/es/form/interface'
const props = defineProps({fields: {type: Array as PropType<Record<string, any>[]>,default: () => []}
})const formRef = useTemplateRef('formRef')
const formData = defineModel<Record<string, any>>({ required: true })const componentMap = {input: Input,number: InputNumber,select: Select,
}const getComponent = (item: Record<string, any>) => {return componentMap[item.componentType as keyof typeof componentMap]
}// 1. isHidden 为函数每次更新表单时都会执行
// 2. 对fields使用 computed 缓存
// const filterFields = computed(() => {
//     return props.fields.filter((item) => {
//         return !(typeof item.isHidden === 'function' ? item.isHidden() : item.isHidden)
//     })
// })const filterFields = computed(() => {return props.fields.filter((item) => !item.isHidden)
})const getSpan = (item: Record<string, any>) => {return item.span || 12
}type ValidateFieldFnReturn = Promise<undefined | Record<string, ValidatedError>>
defineExpose({validate: () => formRef.value?.validate() as ValidateFieldFnReturn,validateField: ((field: string | string[]) => formRef.value?.validateField(field) as ValidateFieldFnReturn)
})
</script><style lang="scss" scoped></style>
  1. useFormBuilder.ts hooks文件
import FormBuilder from "@/components/FormBuilder.vue";
import type { SetupContext } from "vue";
type FormBuilderProps = InstanceType<typeof FormBuilder>["$props"];export default function useFormBuilder(props: Record<string, any>) {const formRef = ref<InstanceType<typeof FormBuilder>>();const Component = (_: Record<string, any>, { slots }: SetupContext) => {return h(FormBuilder, { ...(props as FormBuilderProps), ref: formRef }, slots);};return {FormBuilder: Component,getRef: () => formRef,};
}
  1. 最终的演示效果
    在这里插入图片描述
http://www.dtcms.com/a/266214.html

相关文章:

  • 第二章-AIGC入门-开启AIGC音频探索之旅:从入门到实践(6/36)
  • 【学术写作+AI实战】株洲高校科研写作研修班全纪实:核心期刊编辑与AI专家的联合授课笔记
  • Web前端数据可视化:ECharts高效数据展示完全指南
  • 【JavaEE】计算机工作原理
  • JavaEE初阶第七期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(五)
  • 运维打铁:企业云服务解决方案
  • openEuler 24.03 全流程实战:用 Ansible 5 分钟部署分布式 MinIO 高可用集群
  • Django+DRF 实战:从异常捕获到自定义错误信息
  • 深度分析:Microsoft .NET Framework System.Random 的 C++ 复刻实现
  • 切出idea窗口自动编译,关闭idea自动编译
  • WPF+HelixToolkit打造炫酷自定义3D贴图立方体盒子模型
  • 机器学习在智能供应链中的应用:需求预测与物流优化
  • Java技术深潜:从并发陷阱到云原生突围
  • web网页,在线%电商,茶叶,商城,网上商城系统%分析系统demo,于vscode,vue,java,jdk,springboot,mysql数据库
  • 警惕 Rust 字符串的性能陷阱:`chars().nth()` 的深坑与高效之道
  • 「AI产业」| 《中国信通院华为:智能体技术和应用研究报告》
  • P1202 [USACO1.1] 黑色星期五Friday the Thirteenth
  • Ubuntu Linux Cursor 安装与使用一
  • 成功解决运行:Django框架提示:no such table: django_session
  • 基于探索C++特殊容器类型:容器适配器+底层实现原理
  • 如何通过注解(@Component 等)声明一个 Bean?Spring 是如何找到这些注解的?
  • java微服务(Springboot篇)——————IDEA搭建第一个Springboot入门项目
  • 【基础算法】贪心 (二) :推公式
  • 封装一个png的编码解码操作
  • 译码器Multisim电路仿真汇总——硬件工程师笔记
  • 嵌入式系统中实现串口重定向
  • 【模糊集合】示例
  • 【MySQL\Oracle\PostgreSQL】迁移到openGauss数据出现的问题解决方案
  • Qt Creator自定义控件开发流程
  • redis缓存三大问题分析与解决方案